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
This commit is contained in:
Kristiyan Kostadinov 2025-05-19 09:47:03 +02:00 committed by Jessica Janiuk
parent 049fe82de6
commit 6783fb7eae
10 changed files with 59 additions and 60 deletions

View file

@ -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<T> extends AbstractComponentFactory<T> {
createDirectivesInstances(rootTView, rootLView, hostTNode);
executeContentQueries(rootTView, hostTNode, rootLView);
elementEndFirstCreatePass(rootTView, hostTNode);
elementLikeEndFirstCreatePass(rootTView, hostTNode);
if (projectableNodes !== undefined) {
projectNodes(hostTNode, this.ngContentSelectors, projectableNodes);

View file

@ -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;
}

View file

@ -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 (<ng-container>) 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;
}

View file

@ -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
///////////////////////////////

View file

@ -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)) {

View file

@ -273,7 +273,7 @@
"detectChangesInternal",
"diPublicInInjector",
"documentElement",
"elementEndFirstCreatePass",
"elementLikeEndFirstCreatePass",
"elementLikeStartFirstCreatePass",
"enterDI",
"enterView",

View file

@ -275,7 +275,7 @@
"detectChangesInViewIfAttached",
"detectChangesInternal",
"diPublicInInjector",
"elementEndFirstCreatePass",
"elementLikeEndFirstCreatePass",
"elementLikeStartFirstCreatePass",
"enterDI",
"enterView",

View file

@ -326,7 +326,7 @@
"detectChangesInViewIfAttached",
"detectChangesInternal",
"diPublicInInjector",
"elementEndFirstCreatePass",
"elementLikeEndFirstCreatePass",
"elementLikeStartFirstCreatePass",
"enterDI",
"enterView",

View file

@ -317,7 +317,7 @@
"detectChangesInViewIfAttached",
"detectChangesInternal",
"diPublicInInjector",
"elementEndFirstCreatePass",
"elementLikeEndFirstCreatePass",
"elementLikeStartFirstCreatePass",
"enterDI",
"enterView",

View file

@ -384,7 +384,7 @@
"detectChangesInViewIfAttached",
"detectChangesInternal",
"diPublicInInjector",
"elementEndFirstCreatePass",
"elementLikeEndFirstCreatePass",
"elementLikeStartFirstCreatePass",
"emptyPathMatch",
"encodeUriQuery",