From 6783fb7eaebf341e2d7fababcf6042cfa7d19dca Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 19 May 2025 09:47:03 +0200 Subject: [PATCH] refactor(core): consolidate element end instruction logic (#61409) There was some identical logic between the `elementEnd` and `elementContainerEnd` instructions. These changes consolidate it. PR Close #61409 --- packages/core/src/render3/component_ref.ts | 4 +- .../core/src/render3/instructions/element.ts | 48 +++++++++---------- .../render3/instructions/element_container.ts | 29 ++--------- .../core/src/render3/instructions/shared.ts | 26 +++++++++- packages/core/src/render3/view/elements.ts | 2 +- .../bundle.golden_symbols.json | 2 +- .../bundling/defer/bundle.golden_symbols.json | 2 +- .../forms_reactive/bundle.golden_symbols.json | 2 +- .../bundle.golden_symbols.json | 2 +- .../router/bundle.golden_symbols.json | 2 +- 10 files changed, 59 insertions(+), 60 deletions(-) diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index a52ce8e96da..df8564ddf08 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -77,7 +77,7 @@ import {executeContentQueries} from './queries/query_execution'; import {enterView, leaveView} from './state'; import {debugStringifyTypeForError, stringifyForError} from './util/stringify_utils'; import {getComponentLViewByIndex, getTNode} from './util/view_utils'; -import {elementEndFirstCreatePass, elementLikeStartFirstCreatePass} from './view/elements'; +import {elementLikeEndFirstCreatePass, elementLikeStartFirstCreatePass} from './view/elements'; import {ViewRef} from './view_ref'; import {createLView, createTView, getInitialLViewFlagsFromDef} from './view/construction'; import {BINDING, Binding, DirectiveWithBindings} from './dynamic_bindings'; @@ -328,7 +328,7 @@ export class ComponentFactory extends AbstractComponentFactory { createDirectivesInstances(rootTView, rootLView, hostTNode); executeContentQueries(rootTView, hostTNode, rootLView); - elementEndFirstCreatePass(rootTView, hostTNode); + elementLikeEndFirstCreatePass(rootTView, hostTNode); if (projectableNodes !== undefined) { projectNodes(hostTNode, this.ngContentSelectors, projectableNodes); diff --git a/packages/core/src/render3/instructions/element.ts b/packages/core/src/render3/instructions/element.ts index 28b3c1c3670..e0cbb5672da 100644 --- a/packages/core/src/render3/instructions/element.ts +++ b/packages/core/src/render3/instructions/element.ts @@ -25,7 +25,6 @@ import { } from '../../hydration/utils'; import {isDetachedByI18n} from '../../i18n/utils'; import {assertDefined, assertEqual} from '../../util/assert'; -import {assertHasParent} from '../assert'; import {clearElementContents, createElementNode} from '../dom_node_manipulation'; import {hasClassInput, hasStyleInput, TNode, TNodeType} from '../interfaces/node'; import {RElement} from '../interfaces/renderer_dom'; @@ -41,19 +40,15 @@ import { getLView, getNamespace, getTView, - isCurrentTNodeParent, isInSkipHydrationBlock, isSkipHydrationRootTNode, lastNodeWasCreated, leaveSkipHydrationBlock, - setCurrentTNode, - setCurrentTNodeAsNotParent, } from '../state'; -import {elementEndFirstCreatePass} from '../view/elements'; import {validateElementIsKnown} from './element_validation'; import {setDirectiveInputsWhichShadowsStyling} from './property'; -import {elementLikeStartShared} from './shared'; +import {elementLikeEndShared, elementLikeStartShared} from './shared'; /** * Create DOM element. The instruction must later be followed by `elementEnd()` call. @@ -118,36 +113,37 @@ export function ɵɵelementStart( * @codeGenApi */ export function ɵɵelementEnd(): typeof ɵɵelementEnd { - let currentTNode = getCurrentTNode()!; const tView = getTView(); - ngDevMode && assertDefined(currentTNode, 'No parent node to close.'); - if (isCurrentTNodeParent()) { - setCurrentTNodeAsNotParent(); - } else { - ngDevMode && assertHasParent(getCurrentTNode()); - currentTNode = currentTNode.parent!; - setCurrentTNode(currentTNode, false); - } + const initialTNode = getCurrentTNode()!; + ngDevMode && assertDefined(initialTNode, 'No parent node to close.'); - const tNode = currentTNode; - ngDevMode && assertTNodeType(tNode, TNodeType.AnyRNode); + const currentTNode = elementLikeEndShared(tView, initialTNode); + ngDevMode && assertTNodeType(currentTNode, TNodeType.AnyRNode); - if (isSkipHydrationRootTNode(tNode)) { + if (isSkipHydrationRootTNode(currentTNode)) { leaveSkipHydrationBlock(); } decreaseElementDepthCount(); - if (tView.firstCreatePass) { - elementEndFirstCreatePass(tView, tNode); + if (currentTNode.classesWithoutHost != null && hasClassInput(currentTNode)) { + setDirectiveInputsWhichShadowsStyling( + tView, + currentTNode, + getLView(), + currentTNode.classesWithoutHost, + true, + ); } - if (tNode.classesWithoutHost != null && hasClassInput(tNode)) { - setDirectiveInputsWhichShadowsStyling(tView, tNode, getLView(), tNode.classesWithoutHost, true); - } - - if (tNode.stylesWithoutHost != null && hasStyleInput(tNode)) { - setDirectiveInputsWhichShadowsStyling(tView, tNode, getLView(), tNode.stylesWithoutHost, false); + if (currentTNode.stylesWithoutHost != null && hasStyleInput(currentTNode)) { + setDirectiveInputsWhichShadowsStyling( + tView, + currentTNode, + getLView(), + currentTNode.stylesWithoutHost, + false, + ); } return ɵɵelementEnd; } diff --git a/packages/core/src/render3/instructions/element_container.ts b/packages/core/src/render3/instructions/element_container.ts index 72d6208c5ee..43563b9aee3 100644 --- a/packages/core/src/render3/instructions/element_container.ts +++ b/packages/core/src/render3/instructions/element_container.ts @@ -14,13 +14,10 @@ import { setSegmentHead, } from '../../hydration/utils'; import {isDetachedByI18n} from '../../i18n/utils'; -import {assertEqual, assertNumber} from '../../util/assert'; -import {assertHasParent} from '../assert'; +import {assertDefined, assertEqual, assertNumber} from '../../util/assert'; import {createCommentNode} from '../dom_node_manipulation'; -import {registerPostOrderHooks} from '../hooks'; import {TNode, TNodeType} from '../interfaces/node'; import {RComment} from '../interfaces/renderer_dom'; -import {isContentQueryHost} from '../interfaces/type_checks'; import {HYDRATION, LView, RENDERER, TView} from '../interfaces/view'; import {assertTNodeType} from '../node_assert'; import { @@ -29,13 +26,10 @@ import { getCurrentTNode, getLView, getTView, - isCurrentTNodeParent, isInSkipHydrationBlock, lastNodeWasCreated, - setCurrentTNode, - setCurrentTNodeAsNotParent, } from '../state'; -import {elementLikeStartShared} from './shared'; +import {elementLikeEndShared, elementLikeStartShared} from './shared'; /** * Creates a logical container for other nodes () backed by a comment node in the DOM. @@ -87,24 +81,11 @@ export function ɵɵelementContainerStart( * @codeGenApi */ export function ɵɵelementContainerEnd(): typeof ɵɵelementContainerEnd { - let currentTNode = getCurrentTNode()!; const tView = getTView(); - if (isCurrentTNodeParent()) { - setCurrentTNodeAsNotParent(); - } else { - ngDevMode && assertHasParent(currentTNode); - currentTNode = currentTNode.parent!; - setCurrentTNode(currentTNode, false); - } - + const initialTNode = getCurrentTNode()!; + ngDevMode && assertDefined(initialTNode, 'No parent node to close.'); + const currentTNode = elementLikeEndShared(tView, initialTNode); ngDevMode && assertTNodeType(currentTNode, TNodeType.ElementContainer); - - if (tView.firstCreatePass) { - registerPostOrderHooks(tView, currentTNode); - if (isContentQueryHost(currentTNode)) { - tView.queries!.elementEnd(currentTNode); - } - } return ɵɵelementContainerEnd; } diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 3ce6162ff12..2a354a3d70a 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -20,7 +20,7 @@ import {assertIndexInRange, assertNotSame} from '../../util/assert'; import {escapeCommentText} from '../../util/dom'; import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../ng_reflect'; import {stringify} from '../../util/stringify'; -import {assertFirstCreatePass, assertLView} from '../assert'; +import {assertFirstCreatePass, assertHasParent, assertLView} from '../assert'; import {attachPatchData} from '../context_discovery'; import {getNodeInjectable, getOrCreateNodeInjectorForNode} from '../di'; import {throwMultipleComponentError} from '../errors'; @@ -60,12 +60,15 @@ import {profiler} from '../profiler'; import {ProfilerEvent} from '../profiler_types'; import { getCurrentDirectiveIndex, + getCurrentTNode, getElementDepthCount, getSelectedIndex, increaseElementDepthCount, + isCurrentTNodeParent, isInCheckNoChangesMode, setCurrentDirectiveIndex, setCurrentTNode, + setCurrentTNodeAsNotParent, setSelectedIndex, wasLastNodeCreated, } from '../state'; @@ -79,7 +82,7 @@ import {createComponentLView} from '../view/construction'; import {selectIndexInternal} from './advance'; import {handleUnknownPropertyError, isPropertyValid, matchingSchemas} from './element_validation'; import {writeToDirectiveInput} from './write_to_directive_input'; -import {elementLikeStartFirstCreatePass} from '../view/elements'; +import {elementLikeEndFirstCreatePass, elementLikeStartFirstCreatePass} from '../view/elements'; import {isDetachedByI18n} from '../../i18n/utils'; import {appendChild} from '../node_manipulation'; import {executeContentQueries} from '../queries/query_execution'; @@ -635,6 +638,25 @@ export function elementLikeStartShared( return tNode; } +/** Shared code between instructions that indicate the end of an element. */ +export function elementLikeEndShared(tView: TView, tNode: TNode): TNode { + let currentTNode = tNode; + + if (isCurrentTNodeParent()) { + setCurrentTNodeAsNotParent(); + } else { + ngDevMode && assertHasParent(getCurrentTNode()); + currentTNode = currentTNode.parent!; + setCurrentTNode(currentTNode, false); + } + + if (tView.firstCreatePass) { + elementLikeEndFirstCreatePass(tView, currentTNode); + } + + return currentTNode; +} + /////////////////////////////// //// Bindings & interpolations /////////////////////////////// diff --git a/packages/core/src/render3/view/elements.ts b/packages/core/src/render3/view/elements.ts index e6d0ee267d4..49d7e71d804 100644 --- a/packages/core/src/render3/view/elements.ts +++ b/packages/core/src/render3/view/elements.ts @@ -69,7 +69,7 @@ export function elementLikeStartFirstCreatePass( return tNode; } -export function elementEndFirstCreatePass(tView: TView, tNode: TNode) { +export function elementLikeEndFirstCreatePass(tView: TView, tNode: TNode) { ngDevMode && assertFirstCreatePass(tView); registerPostOrderHooks(tView, tNode); if (isContentQueryHost(tNode)) { diff --git a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json index f557b6c3afd..e244b5c39b8 100644 --- a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json @@ -273,7 +273,7 @@ "detectChangesInternal", "diPublicInInjector", "documentElement", - "elementEndFirstCreatePass", + "elementLikeEndFirstCreatePass", "elementLikeStartFirstCreatePass", "enterDI", "enterView", diff --git a/packages/core/test/bundling/defer/bundle.golden_symbols.json b/packages/core/test/bundling/defer/bundle.golden_symbols.json index b88a82b8e9e..54cce388505 100644 --- a/packages/core/test/bundling/defer/bundle.golden_symbols.json +++ b/packages/core/test/bundling/defer/bundle.golden_symbols.json @@ -275,7 +275,7 @@ "detectChangesInViewIfAttached", "detectChangesInternal", "diPublicInInjector", - "elementEndFirstCreatePass", + "elementLikeEndFirstCreatePass", "elementLikeStartFirstCreatePass", "enterDI", "enterView", diff --git a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json index 37a136d0a87..6cd3a1d9b47 100644 --- a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json @@ -326,7 +326,7 @@ "detectChangesInViewIfAttached", "detectChangesInternal", "diPublicInInjector", - "elementEndFirstCreatePass", + "elementLikeEndFirstCreatePass", "elementLikeStartFirstCreatePass", "enterDI", "enterView", diff --git a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json index 4d2df872be0..56f5b413a3e 100644 --- a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json @@ -317,7 +317,7 @@ "detectChangesInViewIfAttached", "detectChangesInternal", "diPublicInInjector", - "elementEndFirstCreatePass", + "elementLikeEndFirstCreatePass", "elementLikeStartFirstCreatePass", "enterDI", "enterView", diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 005fc943a80..f411d5f7d45 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -384,7 +384,7 @@ "detectChangesInViewIfAttached", "detectChangesInternal", "diPublicInInjector", - "elementEndFirstCreatePass", + "elementLikeEndFirstCreatePass", "elementLikeStartFirstCreatePass", "emptyPathMatch", "encodeUriQuery",