Skip to content

Commit fad0138

Browse files
committed
Create compile context
1 parent 9bfefcb commit fad0138

File tree

3 files changed

+55
-145
lines changed

3 files changed

+55
-145
lines changed

@types/core/compile/compile.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,13 +176,18 @@ export type CompileFn = (
176176
) => PublicLinkFn;
177177
export type LinkFnMapping = {
178178
index: number;
179-
nodeLinkFn?: NodeLinkFn;
179+
nodeLinkFnCtx?: NodeLinkFnCtx;
180180
childLinkFn?: CompositeLinkFn;
181181
};
182182
export type CompileNodesFn = () => CompositeLinkFn;
183183
export type NodeLinkFn = () => Node | Element | NodeList;
184184
export type NodeLinkFnCtx = {
185185
nodeLinkFn: NodeLinkFn;
186+
terminal: boolean;
187+
transclude: TranscludeFn;
188+
transcludeOnThisElement: boolean;
189+
templateOnThisElement: boolean;
190+
newScope: boolean;
186191
};
187192
export type ApplyDirectivesToNodeFn = () => NodeLinkFn;
188193
export type CompositeLinkFn = (

src/core/compile/compile.js

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ import { ngObserveDirective } from "../../directive/observe/observe.js";
9393
/**
9494
* @typedef {Object} LinkFnMapping
9595
* @property {number} index
96-
* @property {NodeLinkFn} [nodeLinkFn]
96+
* @property {NodeLinkFnCtx} [nodeLinkFnCtx]
9797
* @property {CompositeLinkFn} [childLinkFn]
9898
*/
9999

@@ -109,6 +109,11 @@ import { ngObserveDirective } from "../../directive/observe/observe.js";
109109
/**
110110
* @typedef {Object} NodeLinkFnCtx
111111
* @property {NodeLinkFn} nodeLinkFn
112+
* @property {boolean} terminal
113+
* @property {TranscludeFn} transclude
114+
* @property {boolean} transcludeOnThisElement
115+
* @property {boolean} templateOnThisElement
116+
* @property {boolean} newScope
112117
*/
113118

114119
/**
@@ -846,12 +851,11 @@ export class CompileProvider {
846851
ignoreDirective,
847852
);
848853

849-
// /** @type {NodeLinkFnCtx} */
850-
// let nodeLinkFnCtx;
851-
/** @type {NodeLinkFn} */
852-
let nodeLinkFn;
854+
/** @type {NodeLinkFnCtx} */
855+
let nodeLinkFnCtx;
856+
853857
if (directives.length) {
854-
nodeLinkFn = applyDirectivesToNode(
858+
nodeLinkFnCtx = applyDirectivesToNode(
855859
directives,
856860
nodeRefList.getIndex(i),
857861
attrs,
@@ -865,24 +869,22 @@ export class CompileProvider {
865869
ctxNodeRef: nodeRefList,
866870
}),
867871
);
868-
} else {
869-
nodeLinkFn = null;
870872
}
871873

872874
let childLinkFn;
873875
let childNodes;
874-
876+
let nodeLinkFn = nodeLinkFnCtx?.nodeLinkFn;
875877
if (
876-
(nodeLinkFn && nodeLinkFn["terminal"]) ||
878+
(nodeLinkFn && nodeLinkFnCtx.terminal) ||
877879
!(childNodes = nodeRefList.getIndex(i).childNodes) ||
878880
!childNodes.length
879881
) {
880882
childLinkFn = null;
881883
} else {
882884
let transcluded = nodeLinkFn
883-
? (nodeLinkFn["transcludeOnThisElement"] ||
884-
!nodeLinkFn["templateOnThisElement"]) &&
885-
nodeLinkFn["transclude"]
885+
? (nodeLinkFnCtx.transcludeOnThisElement ||
886+
!nodeLinkFnCtx.templateOnThisElement) &&
887+
nodeLinkFnCtx.transclude
886888
: transcludeFn;
887889
// recursive call
888890
const childNodeRef = new NodeRef(childNodes);
@@ -892,7 +894,7 @@ export class CompileProvider {
892894
if (nodeLinkFn || childLinkFn) {
893895
linkFnsList.push({
894896
index: i,
895-
nodeLinkFn: nodeLinkFn,
897+
nodeLinkFnCtx: nodeLinkFnCtx,
896898
childLinkFn: childLinkFn,
897899
});
898900
linkFnFound = true;
@@ -941,24 +943,24 @@ export class CompileProvider {
941943
}
942944
}
943945

944-
linkFnsList.forEach(({ index, nodeLinkFn, childLinkFn }) => {
946+
linkFnsList.forEach(({ index, nodeLinkFnCtx, childLinkFn }) => {
945947
const node = stableNodeList[index];
946948
node.stable = true;
947949
let childScope;
948950
let childBoundTranscludeFn;
949951

950-
if (nodeLinkFn) {
951-
childScope = nodeLinkFn["scope"] ? scope.$new() : scope;
952+
if (nodeLinkFnCtx?.nodeLinkFn) {
953+
childScope = nodeLinkFnCtx.newScope ? scope.$new() : scope;
952954

953-
if (nodeLinkFn["transcludeOnThisElement"]) {
955+
if (nodeLinkFnCtx.transcludeOnThisElement) {
954956
// bind proper scope for the translusion function
955957
childBoundTranscludeFn = createBoundTranscludeFn(
956958
scope,
957-
nodeLinkFn["transclude"],
959+
nodeLinkFnCtx.transclude,
958960
parentBoundTranscludeFn,
959961
);
960962
} else if (
961-
!nodeLinkFn["templateOnThisElement"] &&
963+
!nodeLinkFnCtx.templateOnThisElement &&
962964
parentBoundTranscludeFn
963965
) {
964966
childBoundTranscludeFn = parentBoundTranscludeFn;
@@ -972,11 +974,11 @@ export class CompileProvider {
972974
}
973975

974976
// attach new scope to element
975-
if (nodeLinkFn["scope"]) {
977+
if (nodeLinkFnCtx?.newScope) {
976978
setScope(node, childScope);
977979
}
978980
// @ts-ignore
979-
nodeLinkFn(
981+
nodeLinkFnCtx.nodeLinkFn(
980982
// @ts-ignore
981983
childLinkFn,
982984
childScope,
@@ -1235,7 +1237,7 @@ export class CompileProvider {
12351237
* @param {Array.<Function>} [postLinkFns]
12361238
* @param {Object} [previousCompileContext] Context used for previous compilation of the current
12371239
* node
1238-
* @returns {NodeLinkFn} node link function
1240+
* @returns {NodeLinkFnCtx} node link function
12391241
*/
12401242
function applyDirectivesToNode(
12411243
directives,
@@ -1250,6 +1252,7 @@ export class CompileProvider {
12501252
previousCompileContext = previousCompileContext || {};
12511253

12521254
let terminalPriority = -Number.MAX_VALUE;
1255+
let terminal = false;
12531256
let {
12541257
newScopeDirective,
12551258
controllerDirectives,
@@ -1982,24 +1985,23 @@ export class CompileProvider {
19821985
}
19831986

19841987
if (directive.terminal) {
1985-
nodeLinkFn["terminal"] = true;
1988+
terminal = true;
19861989
terminalPriority = Math.max(terminalPriority, directive.priority);
19871990
}
19881991
}
19891992

1990-
nodeLinkFn["scope"] =
1991-
newScopeDirective && newScopeDirective.scope === true;
1992-
1993-
// track trancluded scope
1994-
nodeLinkFn["transcludeOnThisElement"] = hasTranscludeDirective;
1995-
nodeLinkFn["templateOnThisElement"] = hasTemplate;
1996-
nodeLinkFn["transclude"] = childTranscludeFn;
1997-
19981993
previousCompileContext.hasElementTranscludeDirective =
19991994
hasElementTranscludeDirective;
20001995

20011996
// might be normal or delayed nodeLinkFn depending on if templateUrl is present
2002-
return nodeLinkFn;
1997+
return {
1998+
nodeLinkFn,
1999+
terminal,
2000+
transclude: childTranscludeFn,
2001+
transcludeOnThisElement: hasTranscludeDirective,
2002+
templateOnThisElement: hasTemplate,
2003+
newScope: newScopeDirective && newScopeDirective.scope === true,
2004+
};
20032005

20042006
/// /////////////////
20052007
function addLinkFns(pre, post) {
@@ -2303,6 +2305,7 @@ export class CompileProvider {
23032305
/** @type {any} */
23042306
let afterTemplateNodeLinkFn;
23052307
let afterTemplateChildLinkFn;
2308+
let afterTemplateNodeLinkFnCtx;
23062309
const beforeTemplateCompileNode = $compileNode.getAny();
23072310
const origAsyncDirective = directives.shift();
23082311
const derivedSyncDirective = inherit(origAsyncDirective, {
@@ -2388,7 +2391,7 @@ export class CompileProvider {
23882391
}
23892392

23902393
directives.unshift(derivedSyncDirective);
2391-
afterTemplateNodeLinkFn = applyDirectivesToNode(
2394+
afterTemplateNodeLinkFnCtx = applyDirectivesToNode(
23922395
directives,
23932396
compileNode,
23942397
tAttrs,
@@ -2398,6 +2401,8 @@ export class CompileProvider {
23982401
postLinkFns,
23992402
{ ...previousCompileContext, ctxNodeRef: $compileNode },
24002403
);
2404+
2405+
afterTemplateNodeLinkFn = afterTemplateNodeLinkFnCtx?.nodeLinkFn;
24012406
if ($rootElement) {
24022407
Object.entries($rootElement).forEach(([i, node]) => {
24032408
if (node === compileNode) {
@@ -2445,10 +2450,10 @@ export class CompileProvider {
24452450
// SVG element, where class name is read-only.
24462451
}
24472452
}
2448-
if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
2453+
if (afterTemplateNodeLinkFnCtx.transcludeOnThisElement) {
24492454
childBoundTranscludeFn = createBoundTranscludeFn(
24502455
scope,
2451-
afterTemplateNodeLinkFn.transclude,
2456+
afterTemplateNodeLinkFnCtx.transclude,
24522457
boundTranscludeFn,
24532458
);
24542459
} else {

src/core/compile/compile.spec.js

Lines changed: 8 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -8546,80 +8546,6 @@ describe("$compile", () => {
85468546
]);
85478547
});
85488548

8549-
// LEAK
8550-
xit("should throw an error if `$onChanges()` hooks are not stable", async () => {
8551-
function TestController() {}
8552-
TestController.prototype.$onChanges = function (change) {
8553-
this.onChange();
8554-
};
8555-
8556-
module
8557-
.decorator("$exceptionHandler", () => {
8558-
return (exception) => {
8559-
throw new Error(exception.message);
8560-
};
8561-
})
8562-
.component("c1", {
8563-
controller: TestController,
8564-
bindings: { prop: "<", onChange: "&" },
8565-
});
8566-
8567-
createInjector(["test1"]).invoke((_$compile_, _$rootScope_) => {
8568-
$compile = _$compile_;
8569-
$rootScope = _$rootScope_;
8570-
});
8571-
// Setup the directive with bindings that will keep updating the bound value forever
8572-
element = $compile('<c1 prop="a" on-change="a = -a"></c1>')(
8573-
$rootScope,
8574-
);
8575-
await wait();
8576-
// Update val to trigger the unstable onChanges, which will result in an error
8577-
expect(async () => {
8578-
$rootScope.$apply("a = 42");
8579-
await wait();
8580-
}).toThrowError(/infchng/);
8581-
8582-
dealoc(element);
8583-
element = $compile('<c1 prop="b" on-change=""></c1>')($rootScope);
8584-
await wait();
8585-
$rootScope.$apply("b = 24");
8586-
$rootScope.$apply("b = 48");
8587-
});
8588-
8589-
// LEAK
8590-
xit("should log an error if `$onChanges()` hooks are not stable", async () => {
8591-
function TestController() {}
8592-
TestController.prototype.$onChanges = function (change) {
8593-
this.onChange();
8594-
};
8595-
8596-
module
8597-
.decorator("$exceptionHandler", () => {
8598-
return (exception) => {
8599-
log.push(exception.message);
8600-
};
8601-
})
8602-
.component("c1", {
8603-
controller: TestController,
8604-
bindings: { prop: "<", onChange: "&" },
8605-
});
8606-
8607-
createInjector(["test1"]).invoke((_$compile_, _$rootScope_) => {
8608-
$compile = _$compile_;
8609-
$rootScope = _$rootScope_;
8610-
});
8611-
// Setup the directive with bindings that will keep updating the bound value forever
8612-
element = $compile('<c1 prop="a" on-change="a = -a"></c1>')(
8613-
$rootScope,
8614-
);
8615-
await wait();
8616-
// Update val to trigger the unstable onChanges, which will result in an error
8617-
$rootScope.$apply("a = 42");
8618-
await wait();
8619-
expect(log.length).toEqual(1);
8620-
expect(log[0].match(/infchng/)).toBeTruthy();
8621-
});
8622-
86238549
it("should continue to trigger other `$onChanges` hooks if one throws an error", async () => {
86248550
function ThrowingController() {
86258551
this.$onChanges = function (change) {
@@ -9463,64 +9389,38 @@ describe("$compile", () => {
94639389
]);
94649390
});
94659391

9466-
xit("should update isolate again after $onInit if outer object reference changes even if equal", () => {
9392+
it("should update isolate again after $onInit if outer object reference changes even if equal", async () => {
94679393
$rootScope.name = ["outer"];
94689394
$compile('<ow-component input="name"></ow-component>')($rootScope);
9469-
9395+
await wait();
94709396
expect($rootScope.name).toEqual(["outer"]);
94719397
expect(component.input).toEqual("$onInit");
94729398

94739399
$rootScope.name = ["outer"];
9400+
await wait();
94749401
expect($rootScope.name).toEqual(["outer"]);
94759402
expect(component.input).toEqual(["outer"]);
9476-
94779403
expect(log).toEqual([
94789404
"constructor",
9479-
[
9480-
"$onChanges",
9481-
jasmine.objectContaining({ currentValue: ["outer"] }),
9482-
],
9405+
["$onChanges", "outer"],
94839406
"$onInit",
9484-
[
9485-
"$onChanges",
9486-
jasmine.objectContaining({
9487-
previousValue: ["outer"],
9488-
currentValue: ["outer"],
9489-
}),
9490-
],
9407+
["$onChanges", "outer"],
94919408
]);
94929409
});
94939410

9494-
xit("should not update isolate again after $onInit if outer is a literal", () => {
9411+
it("should not update isolate again after $onInit if outer is a literal", async () => {
94959412
$rootScope.name = "outer";
94969413
$compile('<ow-component input="[name]"></ow-component>')(
94979414
$rootScope,
94989415
);
94999416

95009417
expect(component.input).toEqual("$onInit");
95019418

9502-
// No outer change
9503-
$rootScope.$apply('name = "outer"');
9504-
expect(component.input).toEqual("$onInit");
9505-
95069419
// Outer change
95079420
$rootScope.$apply('name = "re-outer"');
9421+
await wait();
95089422
expect(component.input).toEqual(["re-outer"]);
9509-
9510-
expect(log).toEqual([
9511-
"constructor",
9512-
[
9513-
"$onChanges",
9514-
jasmine.objectContaining({ currentValue: ["outer"] }),
9515-
],
9516-
"$onInit",
9517-
[
9518-
"$onChanges",
9519-
jasmine.objectContaining({
9520-
currentValue: ["re-outer"],
9521-
}),
9522-
],
9523-
]);
9423+
expect(log).toEqual(["constructor", "$onInit"]);
95249424
});
95259425

95269426
xit("should update isolate again after $onInit if outer has changed (before initial watchAction call)", () => {

0 commit comments

Comments
 (0)