@angular/core 20.0.1 → 20.1.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{api.d-B0vztftH.d.ts → api.d-Dwpmmn5j.d.ts} +2 -2
- package/{chrome_dev_tools_performance.d-DvzAxqBc.d.ts → chrome_dev_tools_performance.d-Dk_7kdX9.d.ts} +5 -1
- package/{discovery.d-CB2iJta5.d.ts → discovery.d-BAZTj_rM.d.ts} +2 -2
- package/event_dispatcher.d-BReQpZfC.d.ts +1 -1
- package/fesm2022/attribute-BWp59EjE.mjs +1 -1
- package/fesm2022/attribute-BWp59EjE.mjs.map +1 -1
- package/fesm2022/core.mjs +12 -11
- package/fesm2022/core.mjs.map +1 -1
- package/fesm2022/{debug_node-B9JawCEy.mjs → debug_node-CjNGi9N3.mjs} +1463 -1437
- package/fesm2022/debug_node-CjNGi9N3.mjs.map +1 -0
- package/fesm2022/primitives/di.mjs +1 -1
- package/fesm2022/primitives/di.mjs.map +1 -1
- package/fesm2022/primitives/event-dispatch.mjs +93 -2
- package/fesm2022/primitives/event-dispatch.mjs.map +1 -1
- package/fesm2022/primitives/signals.mjs +3 -3
- package/fesm2022/primitives/signals.mjs.map +1 -1
- package/fesm2022/{resource-W6LObBPP.mjs → resource-1o9xbJml.mjs} +5 -5
- package/fesm2022/{resource-W6LObBPP.mjs.map → resource-1o9xbJml.mjs.map} +1 -1
- package/fesm2022/{root_effect_scheduler-C4AUixQF.mjs → root_effect_scheduler-Ds-Wmkv_.mjs} +16 -9
- package/fesm2022/root_effect_scheduler-Ds-Wmkv_.mjs.map +1 -0
- package/fesm2022/rxjs-interop.mjs +4 -4
- package/fesm2022/rxjs-interop.mjs.map +1 -1
- package/fesm2022/signal-BZ1SD--i.mjs +1 -1
- package/fesm2022/signal-BZ1SD--i.mjs.map +1 -1
- package/fesm2022/testing.mjs +22 -32
- package/fesm2022/testing.mjs.map +1 -1
- package/fesm2022/{untracked-RA6XPQ1Z.mjs → untracked-C72kieeB.mjs} +3 -3
- package/fesm2022/{untracked-RA6XPQ1Z.mjs.map → untracked-C72kieeB.mjs.map} +1 -1
- package/fesm2022/weak_ref-BaIq-pgY.mjs +1 -1
- package/fesm2022/weak_ref-BaIq-pgY.mjs.map +1 -1
- package/graph.d-BcIOep_B.d.ts +1 -1
- package/index.d.ts +10 -7
- package/package.json +2 -2
- package/primitives/di/index.d.ts +1 -1
- package/primitives/event-dispatch/index.d.ts +1 -1
- package/primitives/signals/index.d.ts +1 -1
- package/rxjs-interop/index.d.ts +3 -3
- package/schematics/bundles/{apply_import_manager-DT15wSJs.cjs → apply_import_manager-CSEu0fby.cjs} +3 -3
- package/schematics/bundles/{checker-Bu1Wu4f7.cjs → checker-CuQvkMhs.cjs} +680 -528
- package/schematics/bundles/cleanup-unused-imports.cjs +5 -5
- package/schematics/bundles/{compiler_host-C_4Iw5UD.cjs → compiler_host-Biezhzch.cjs} +2 -2
- package/schematics/bundles/control-flow-migration.cjs +3 -3
- package/schematics/bundles/document-core.cjs +5 -5
- package/schematics/bundles/imports-CIX-JgAN.cjs +1 -1
- package/schematics/bundles/{index-CCX_cTPD.cjs → index-C5wL4qaq.cjs} +1001 -585
- package/schematics/bundles/{index-CAM7Xiu7.cjs → index-vkqofxID.cjs} +17 -16
- package/schematics/bundles/inject-flags.cjs +5 -5
- package/schematics/bundles/inject-migration.cjs +3 -3
- package/schematics/bundles/leading_space-D9nQ8UQC.cjs +1 -1
- package/schematics/bundles/{migrate_ts_type_references-DSqmdRpG.cjs → migrate_ts_type_references-BYgEQg0O.cjs} +26 -77
- package/schematics/bundles/ng_decorators-B5HCqr20.cjs +1 -1
- package/schematics/bundles/nodes-B16H9JUd.cjs +1 -1
- package/schematics/bundles/output-migration.cjs +6 -6
- package/schematics/bundles/{project_paths-BjQra9mv.cjs → project_paths-KPCsvRfl.cjs} +3 -3
- package/schematics/bundles/project_tsconfig_paths-CDVxT6Ov.cjs +1 -1
- package/schematics/bundles/property_name-BBwFuqMe.cjs +1 -1
- package/schematics/bundles/route-lazy-loading.cjs +3 -3
- package/schematics/bundles/self-closing-tags-migration.cjs +7 -19
- package/schematics/bundles/signal-input-migration.cjs +28 -12
- package/schematics/bundles/signal-queries-migration.cjs +7 -7
- package/schematics/bundles/signals.cjs +7 -7
- package/schematics/bundles/standalone-migration.cjs +4 -4
- package/schematics/bundles/symbol-VPWguRxr.cjs +1 -1
- package/schematics/bundles/test-bed-get.cjs +4 -4
- package/signal.d-fOdF0h0o.d.ts +1 -1
- package/testing/index.d.ts +3 -3
- package/weak_ref.d-eGOEP9S1.d.ts +1 -1
- package/fesm2022/debug_node-B9JawCEy.mjs.map +0 -1
- package/fesm2022/root_effect_scheduler-C4AUixQF.mjs.map +0 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/**
|
|
3
|
-
* @license Angular v20.0.
|
|
3
|
+
* @license Angular v20.1.0-next.0
|
|
4
4
|
* (c) 2010-2025 Google LLC. https://angular.io/
|
|
5
5
|
* License: MIT
|
|
6
6
|
*/
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
-
var checker = require('./checker-
|
|
9
|
+
var checker = require('./checker-CuQvkMhs.cjs');
|
|
10
10
|
var ts = require('typescript');
|
|
11
11
|
var p = require('path');
|
|
12
12
|
require('os');
|
|
@@ -891,7 +891,7 @@ const MINIMUM_PARTIAL_LINKER_DEFER_SUPPORT_VERSION = '18.0.0';
|
|
|
891
891
|
function compileDeclareClassMetadata(metadata) {
|
|
892
892
|
const definitionMap = new checker.DefinitionMap();
|
|
893
893
|
definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
|
|
894
|
-
definitionMap.set('version', checker.literal('20.0.
|
|
894
|
+
definitionMap.set('version', checker.literal('20.1.0-next.0'));
|
|
895
895
|
definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
|
|
896
896
|
definitionMap.set('type', metadata.type);
|
|
897
897
|
definitionMap.set('decorators', metadata.decorators);
|
|
@@ -909,7 +909,7 @@ function compileComponentDeclareClassMetadata(metadata, dependencies) {
|
|
|
909
909
|
callbackReturnDefinitionMap.set('ctorParameters', metadata.ctorParameters ?? checker.literal(null));
|
|
910
910
|
callbackReturnDefinitionMap.set('propDecorators', metadata.propDecorators ?? checker.literal(null));
|
|
911
911
|
definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_DEFER_SUPPORT_VERSION));
|
|
912
|
-
definitionMap.set('version', checker.literal('20.0.
|
|
912
|
+
definitionMap.set('version', checker.literal('20.1.0-next.0'));
|
|
913
913
|
definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
|
|
914
914
|
definitionMap.set('type', metadata.type);
|
|
915
915
|
definitionMap.set('resolveDeferredDeps', compileComponentMetadataAsyncResolver(dependencies));
|
|
@@ -1004,7 +1004,7 @@ function createDirectiveDefinitionMap(meta) {
|
|
|
1004
1004
|
const definitionMap = new checker.DefinitionMap();
|
|
1005
1005
|
const minVersion = getMinimumVersionForPartialOutput(meta);
|
|
1006
1006
|
definitionMap.set('minVersion', checker.literal(minVersion));
|
|
1007
|
-
definitionMap.set('version', checker.literal('20.0.
|
|
1007
|
+
definitionMap.set('version', checker.literal('20.1.0-next.0'));
|
|
1008
1008
|
// e.g. `type: MyDirective`
|
|
1009
1009
|
definitionMap.set('type', meta.type.value);
|
|
1010
1010
|
if (meta.isStandalone !== undefined) {
|
|
@@ -1420,7 +1420,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
|
|
|
1420
1420
|
function compileDeclareFactoryFunction(meta) {
|
|
1421
1421
|
const definitionMap = new checker.DefinitionMap();
|
|
1422
1422
|
definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
|
|
1423
|
-
definitionMap.set('version', checker.literal('20.0.
|
|
1423
|
+
definitionMap.set('version', checker.literal('20.1.0-next.0'));
|
|
1424
1424
|
definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
|
|
1425
1425
|
definitionMap.set('type', meta.type.value);
|
|
1426
1426
|
definitionMap.set('deps', compileDependencies(meta.deps));
|
|
@@ -1455,7 +1455,7 @@ function compileDeclareInjectableFromMetadata(meta) {
|
|
|
1455
1455
|
function createInjectableDefinitionMap(meta) {
|
|
1456
1456
|
const definitionMap = new checker.DefinitionMap();
|
|
1457
1457
|
definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
|
|
1458
|
-
definitionMap.set('version', checker.literal('20.0.
|
|
1458
|
+
definitionMap.set('version', checker.literal('20.1.0-next.0'));
|
|
1459
1459
|
definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
|
|
1460
1460
|
definitionMap.set('type', meta.type.value);
|
|
1461
1461
|
// Only generate providedIn property if it has a non-null value
|
|
@@ -1506,7 +1506,7 @@ function compileDeclareInjectorFromMetadata(meta) {
|
|
|
1506
1506
|
function createInjectorDefinitionMap(meta) {
|
|
1507
1507
|
const definitionMap = new checker.DefinitionMap();
|
|
1508
1508
|
definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
|
|
1509
|
-
definitionMap.set('version', checker.literal('20.0.
|
|
1509
|
+
definitionMap.set('version', checker.literal('20.1.0-next.0'));
|
|
1510
1510
|
definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
|
|
1511
1511
|
definitionMap.set('type', meta.type.value);
|
|
1512
1512
|
definitionMap.set('providers', meta.providers);
|
|
@@ -1539,7 +1539,7 @@ function createNgModuleDefinitionMap(meta) {
|
|
|
1539
1539
|
throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
|
|
1540
1540
|
}
|
|
1541
1541
|
definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
|
|
1542
|
-
definitionMap.set('version', checker.literal('20.0.
|
|
1542
|
+
definitionMap.set('version', checker.literal('20.1.0-next.0'));
|
|
1543
1543
|
definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
|
|
1544
1544
|
definitionMap.set('type', meta.type.value);
|
|
1545
1545
|
// We only generate the keys in the metadata if the arrays contain values.
|
|
@@ -1590,7 +1590,7 @@ function compileDeclarePipeFromMetadata(meta) {
|
|
|
1590
1590
|
function createPipeDefinitionMap(meta) {
|
|
1591
1591
|
const definitionMap = new checker.DefinitionMap();
|
|
1592
1592
|
definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_VERSION));
|
|
1593
|
-
definitionMap.set('version', checker.literal('20.0.
|
|
1593
|
+
definitionMap.set('version', checker.literal('20.1.0-next.0'));
|
|
1594
1594
|
definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
|
|
1595
1595
|
// e.g. `type: MyPipe`
|
|
1596
1596
|
definitionMap.set('type', meta.type.value);
|
|
@@ -1598,7 +1598,7 @@ function createPipeDefinitionMap(meta) {
|
|
|
1598
1598
|
definitionMap.set('isStandalone', checker.literal(meta.isStandalone));
|
|
1599
1599
|
}
|
|
1600
1600
|
// e.g. `name: "myPipe"`
|
|
1601
|
-
definitionMap.set('name', checker.literal(meta.pipeName));
|
|
1601
|
+
definitionMap.set('name', checker.literal(meta.pipeName ?? meta.name));
|
|
1602
1602
|
if (meta.pure === false) {
|
|
1603
1603
|
// e.g. `pure: false`
|
|
1604
1604
|
definitionMap.set('pure', checker.literal(meta.pure));
|
|
@@ -2546,6 +2546,10 @@ class DtsMetadataReader {
|
|
|
2546
2546
|
// used to increase the accuracy of a diagnostic.
|
|
2547
2547
|
preserveWhitespaces: false,
|
|
2548
2548
|
isExplicitlyDeferred: false,
|
|
2549
|
+
// We don't need to know if imported components from .d.ts
|
|
2550
|
+
// files are selectorless for type-checking purposes.
|
|
2551
|
+
selectorlessEnabled: false,
|
|
2552
|
+
localReferencedSymbols: null,
|
|
2549
2553
|
};
|
|
2550
2554
|
}
|
|
2551
2555
|
/**
|
|
@@ -2567,11 +2571,12 @@ class DtsMetadataReader {
|
|
|
2567
2571
|
return null;
|
|
2568
2572
|
}
|
|
2569
2573
|
const type = def.type.typeArguments[1];
|
|
2570
|
-
if (!ts.isLiteralTypeNode(type) ||
|
|
2574
|
+
if (!ts.isLiteralTypeNode(type) ||
|
|
2575
|
+
(!ts.isStringLiteral(type.literal) && type.literal.kind !== ts.SyntaxKind.NullKeyword)) {
|
|
2571
2576
|
// The type metadata was the wrong type.
|
|
2572
2577
|
return null;
|
|
2573
2578
|
}
|
|
2574
|
-
const name = type.literal.text;
|
|
2579
|
+
const name = ts.isStringLiteral(type.literal) ? type.literal.text : null;
|
|
2575
2580
|
const isStandalone = def.type.typeArguments.length > 2 && (checker.readBooleanType(def.type.typeArguments[2]) ?? false);
|
|
2576
2581
|
return {
|
|
2577
2582
|
kind: checker.MetaKind.Pipe,
|
|
@@ -3187,6 +3192,7 @@ class TraitCompiler {
|
|
|
3187
3192
|
dtsTransforms;
|
|
3188
3193
|
semanticDepGraphUpdater;
|
|
3189
3194
|
sourceFileTypeIdentifier;
|
|
3195
|
+
emitDeclarationOnly;
|
|
3190
3196
|
/**
|
|
3191
3197
|
* Maps class declarations to their `ClassRecord`, which tracks the Ivy traits being applied to
|
|
3192
3198
|
* those classes.
|
|
@@ -3204,7 +3210,7 @@ class TraitCompiler {
|
|
|
3204
3210
|
filesWithoutTraits = new Set();
|
|
3205
3211
|
reexportMap = new Map();
|
|
3206
3212
|
handlersByName = new Map();
|
|
3207
|
-
constructor(handlers, reflector, perf, incrementalBuild, compileNonExportedClasses, compilationMode, dtsTransforms, semanticDepGraphUpdater, sourceFileTypeIdentifier) {
|
|
3213
|
+
constructor(handlers, reflector, perf, incrementalBuild, compileNonExportedClasses, compilationMode, dtsTransforms, semanticDepGraphUpdater, sourceFileTypeIdentifier, emitDeclarationOnly) {
|
|
3208
3214
|
this.handlers = handlers;
|
|
3209
3215
|
this.reflector = reflector;
|
|
3210
3216
|
this.perf = perf;
|
|
@@ -3214,6 +3220,7 @@ class TraitCompiler {
|
|
|
3214
3220
|
this.dtsTransforms = dtsTransforms;
|
|
3215
3221
|
this.semanticDepGraphUpdater = semanticDepGraphUpdater;
|
|
3216
3222
|
this.sourceFileTypeIdentifier = sourceFileTypeIdentifier;
|
|
3223
|
+
this.emitDeclarationOnly = emitDeclarationOnly;
|
|
3217
3224
|
for (const handler of handlers) {
|
|
3218
3225
|
this.handlersByName.set(handler.name, handler);
|
|
3219
3226
|
}
|
|
@@ -3422,13 +3429,16 @@ class TraitCompiler {
|
|
|
3422
3429
|
record.metaDiagnostics === null) {
|
|
3423
3430
|
// Custom decorators found in local compilation mode! In this mode we don't support custom
|
|
3424
3431
|
// decorators yet. But will eventually do (b/320536434). For now a temporary error is thrown.
|
|
3432
|
+
const compilationModeName = this.emitDeclarationOnly
|
|
3433
|
+
? 'experimental declaration-only emission'
|
|
3434
|
+
: 'local compilation';
|
|
3425
3435
|
record.metaDiagnostics = [...nonNgDecoratorsInLocalMode].map((decorator) => ({
|
|
3426
3436
|
category: ts.DiagnosticCategory.Error,
|
|
3427
3437
|
code: Number('-99' + checker.ErrorCode.DECORATOR_UNEXPECTED),
|
|
3428
3438
|
file: checker.getSourceFile(clazz),
|
|
3429
3439
|
start: decorator.node.getStart(),
|
|
3430
3440
|
length: decorator.node.getWidth(),
|
|
3431
|
-
messageText:
|
|
3441
|
+
messageText: `In ${compilationModeName} mode, Angular does not support custom decorators. Ensure all class decorators are from Angular.`,
|
|
3432
3442
|
}));
|
|
3433
3443
|
record.traits = foundTraits = [];
|
|
3434
3444
|
}
|
|
@@ -3848,9 +3858,6 @@ class DtsTransformer {
|
|
|
3848
3858
|
if (ts.isClassDeclaration(node)) {
|
|
3849
3859
|
return this.transformClassDeclaration(node, transforms, imports);
|
|
3850
3860
|
}
|
|
3851
|
-
else if (ts.isFunctionDeclaration(node)) {
|
|
3852
|
-
return this.transformFunctionDeclaration(node, transforms, imports);
|
|
3853
|
-
}
|
|
3854
3861
|
else {
|
|
3855
3862
|
// Otherwise return node as is.
|
|
3856
3863
|
return ts.visitEachChild(node, visitor, this.ctx);
|
|
@@ -3862,52 +3869,13 @@ class DtsTransformer {
|
|
|
3862
3869
|
return imports.transformTsFile(this.ctx, sf);
|
|
3863
3870
|
}
|
|
3864
3871
|
transformClassDeclaration(clazz, transforms, imports) {
|
|
3865
|
-
let elements = clazz.members;
|
|
3866
|
-
let elementsChanged = false;
|
|
3867
|
-
for (const transform of transforms) {
|
|
3868
|
-
if (transform.transformClassElement !== undefined) {
|
|
3869
|
-
for (let i = 0; i < elements.length; i++) {
|
|
3870
|
-
const res = transform.transformClassElement(elements[i], imports);
|
|
3871
|
-
if (res !== elements[i]) {
|
|
3872
|
-
if (!elementsChanged) {
|
|
3873
|
-
elements = [...elements];
|
|
3874
|
-
elementsChanged = true;
|
|
3875
|
-
}
|
|
3876
|
-
elements[i] = res;
|
|
3877
|
-
}
|
|
3878
|
-
}
|
|
3879
|
-
}
|
|
3880
|
-
}
|
|
3881
3872
|
let newClazz = clazz;
|
|
3882
3873
|
for (const transform of transforms) {
|
|
3883
3874
|
if (transform.transformClass !== undefined) {
|
|
3884
|
-
|
|
3885
|
-
// not yet been incorporated. Otherwise, `newClazz.members` holds the latest class members.
|
|
3886
|
-
const inputMembers = clazz === newClazz ? elements : newClazz.members;
|
|
3887
|
-
newClazz = transform.transformClass(newClazz, inputMembers, this.reflector, this.refEmitter, imports);
|
|
3888
|
-
}
|
|
3889
|
-
}
|
|
3890
|
-
// If some elements have been transformed but the class itself has not been transformed, create
|
|
3891
|
-
// an updated class declaration with the updated elements.
|
|
3892
|
-
if (elementsChanged && clazz === newClazz) {
|
|
3893
|
-
newClazz = ts.factory.updateClassDeclaration(
|
|
3894
|
-
/* node */ clazz,
|
|
3895
|
-
/* modifiers */ clazz.modifiers,
|
|
3896
|
-
/* name */ clazz.name,
|
|
3897
|
-
/* typeParameters */ clazz.typeParameters,
|
|
3898
|
-
/* heritageClauses */ clazz.heritageClauses,
|
|
3899
|
-
/* members */ elements);
|
|
3900
|
-
}
|
|
3901
|
-
return newClazz;
|
|
3902
|
-
}
|
|
3903
|
-
transformFunctionDeclaration(declaration, transforms, imports) {
|
|
3904
|
-
let newDecl = declaration;
|
|
3905
|
-
for (const transform of transforms) {
|
|
3906
|
-
if (transform.transformFunctionDeclaration !== undefined) {
|
|
3907
|
-
newDecl = transform.transformFunctionDeclaration(newDecl, imports);
|
|
3875
|
+
newClazz = transform.transformClass(newClazz, newClazz.members, this.reflector, this.refEmitter, imports);
|
|
3908
3876
|
}
|
|
3909
3877
|
}
|
|
3910
|
-
return
|
|
3878
|
+
return newClazz;
|
|
3911
3879
|
}
|
|
3912
3880
|
}
|
|
3913
3881
|
class IvyDeclarationDtsTransform {
|
|
@@ -4036,11 +4004,11 @@ class Visitor {
|
|
|
4036
4004
|
|
|
4037
4005
|
const NO_DECORATORS = new Set();
|
|
4038
4006
|
const CLOSURE_FILE_OVERVIEW_REGEXP = /\s+@fileoverview\s+/i;
|
|
4039
|
-
function ivyTransformFactory(compilation, reflector, importRewriter, defaultImportTracker, localCompilationExtraImportsTracker, perf, isCore, isClosureCompilerEnabled) {
|
|
4007
|
+
function ivyTransformFactory(compilation, reflector, importRewriter, defaultImportTracker, localCompilationExtraImportsTracker, perf, isCore, isClosureCompilerEnabled, emitDeclarationOnly) {
|
|
4040
4008
|
const recordWrappedNode = createRecorderFn(defaultImportTracker);
|
|
4041
4009
|
return (context) => {
|
|
4042
4010
|
return (file) => {
|
|
4043
|
-
return perf.inPhase(checker.PerfPhase.Compile, () => transformIvySourceFile(compilation, context, reflector, importRewriter, localCompilationExtraImportsTracker, file, isCore, isClosureCompilerEnabled, recordWrappedNode));
|
|
4011
|
+
return perf.inPhase(checker.PerfPhase.Compile, () => transformIvySourceFile(compilation, context, reflector, importRewriter, localCompilationExtraImportsTracker, file, isCore, isClosureCompilerEnabled, emitDeclarationOnly, recordWrappedNode));
|
|
4044
4012
|
};
|
|
4045
4013
|
};
|
|
4046
4014
|
}
|
|
@@ -4250,7 +4218,7 @@ class IvyTransformationVisitor extends Visitor {
|
|
|
4250
4218
|
/**
|
|
4251
4219
|
* A transformer which operates on ts.SourceFiles and applies changes from an `IvyCompilation`.
|
|
4252
4220
|
*/
|
|
4253
|
-
function transformIvySourceFile(compilation, context, reflector, importRewriter, localCompilationExtraImportsTracker, file, isCore, isClosureCompilerEnabled, recordWrappedNode) {
|
|
4221
|
+
function transformIvySourceFile(compilation, context, reflector, importRewriter, localCompilationExtraImportsTracker, file, isCore, isClosureCompilerEnabled, emitDeclarationOnly, recordWrappedNode) {
|
|
4254
4222
|
const constantPool = new checker.ConstantPool(isClosureCompilerEnabled);
|
|
4255
4223
|
const importManager = new checker.ImportManager({
|
|
4256
4224
|
...checker.presetImportManagerForceNamespaceImports,
|
|
@@ -4268,6 +4236,10 @@ function transformIvySourceFile(compilation, context, reflector, importRewriter,
|
|
|
4268
4236
|
// Step 1. Go though all classes in AST, perform compilation and collect the results.
|
|
4269
4237
|
const compilationVisitor = new IvyCompilationVisitor(compilation, constantPool);
|
|
4270
4238
|
visit(file, compilationVisitor, context);
|
|
4239
|
+
// If we are emitting declarations only, we can skip the script transforms.
|
|
4240
|
+
if (emitDeclarationOnly) {
|
|
4241
|
+
return file;
|
|
4242
|
+
}
|
|
4271
4243
|
// Step 2. Scan through the AST again and perform transformations based on Ivy compilation
|
|
4272
4244
|
// results obtained at Step 1.
|
|
4273
4245
|
const transformationVisitor = new IvyTransformationVisitor(compilation, compilationVisitor.classCompilationMap, reflector, importManager, recordWrappedNode, isClosureCompilerEnabled, isCore, compilationVisitor.deferrableImports);
|
|
@@ -4380,6 +4352,369 @@ function nodeArrayFromDecoratorsArray(decorators) {
|
|
|
4380
4352
|
return array;
|
|
4381
4353
|
}
|
|
4382
4354
|
|
|
4355
|
+
function insertDebugNameIntoCallExpression(callExpression, debugName) {
|
|
4356
|
+
const signalExpressionHasNoArguments = callExpression.arguments.length === 0;
|
|
4357
|
+
const signalExpressionIsRequired = isRequiredSignalFunction(callExpression.expression);
|
|
4358
|
+
let configPosition = signalExpressionIsRequired ? 0 : 1;
|
|
4359
|
+
// If the call expression has no arguments, we pretend that the config object is at position 0.
|
|
4360
|
+
// We do this so that we can insert a spread element at the start of the args list in a way where
|
|
4361
|
+
// undefined can be the first argument but still get tree-shaken out in production builds.
|
|
4362
|
+
if (signalExpressionHasNoArguments) {
|
|
4363
|
+
configPosition = 0;
|
|
4364
|
+
}
|
|
4365
|
+
const nodeArgs = Array.from(callExpression.arguments);
|
|
4366
|
+
let existingArgument = nodeArgs[configPosition];
|
|
4367
|
+
if (existingArgument === undefined) {
|
|
4368
|
+
existingArgument = ts.factory.createObjectLiteralExpression([]);
|
|
4369
|
+
}
|
|
4370
|
+
// Do nothing if an identifier is used as the config object
|
|
4371
|
+
// Ex -
|
|
4372
|
+
// const defaultObject = { equals: () => false };
|
|
4373
|
+
// signal(123, defaultObject)
|
|
4374
|
+
if (ts.isIdentifier(existingArgument)) {
|
|
4375
|
+
return callExpression;
|
|
4376
|
+
}
|
|
4377
|
+
if (!ts.isObjectLiteralExpression(existingArgument)) {
|
|
4378
|
+
return callExpression;
|
|
4379
|
+
}
|
|
4380
|
+
// insert debugName into the existing config object
|
|
4381
|
+
const properties = Array.from(existingArgument.properties);
|
|
4382
|
+
const debugNameExists = properties.some((prop) => ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'debugName');
|
|
4383
|
+
if (debugNameExists) {
|
|
4384
|
+
return callExpression;
|
|
4385
|
+
}
|
|
4386
|
+
// We prepend instead of appending so that we don't overwrite an existing debugName Property
|
|
4387
|
+
// `{ foo: 'bar' }` -> `{ debugName: 'myDebugName', foo: 'bar' }`
|
|
4388
|
+
properties.unshift(ts.factory.createPropertyAssignment('debugName', ts.factory.createStringLiteral(debugName)));
|
|
4389
|
+
const transformedConfigProperties = ts.factory.createObjectLiteralExpression(properties);
|
|
4390
|
+
const ngDevModeIdentifier = ts.factory.createIdentifier('ngDevMode');
|
|
4391
|
+
let devModeCase;
|
|
4392
|
+
// if the signal expression has no arguments and the config object is not required,
|
|
4393
|
+
// we need to add an undefined identifier to the start of the args list so that we can spread the
|
|
4394
|
+
// config object in the right place.
|
|
4395
|
+
if (signalExpressionHasNoArguments && !signalExpressionIsRequired) {
|
|
4396
|
+
devModeCase = ts.factory.createArrayLiteralExpression([
|
|
4397
|
+
ts.factory.createIdentifier('undefined'),
|
|
4398
|
+
transformedConfigProperties,
|
|
4399
|
+
]);
|
|
4400
|
+
}
|
|
4401
|
+
else {
|
|
4402
|
+
devModeCase = ts.factory.createArrayLiteralExpression([
|
|
4403
|
+
transformedConfigProperties,
|
|
4404
|
+
...nodeArgs.slice(configPosition + 1),
|
|
4405
|
+
]);
|
|
4406
|
+
}
|
|
4407
|
+
const nonDevModeCase = signalExpressionIsRequired
|
|
4408
|
+
? ts.factory.createArrayLiteralExpression(nodeArgs)
|
|
4409
|
+
: ts.factory.createArrayLiteralExpression(nodeArgs.slice(configPosition));
|
|
4410
|
+
const spreadElementContainingUpdatedOptions = ts.factory.createSpreadElement(ts.factory.createParenthesizedExpression(ts.factory.createConditionalExpression(ngDevModeIdentifier,
|
|
4411
|
+
/* question token */ undefined, devModeCase,
|
|
4412
|
+
/* colon token */ undefined, nonDevModeCase)));
|
|
4413
|
+
let transformedSignalArgs;
|
|
4414
|
+
if (signalExpressionIsRequired || signalExpressionHasNoArguments) {
|
|
4415
|
+
// 1. If the call expression is a required signal function, there is no args other than the config object.
|
|
4416
|
+
// So we just use the spread element as the only argument.
|
|
4417
|
+
// or
|
|
4418
|
+
// 2. If the call expression has no arguments (ex. input(), model(), etc), we already added the undefined
|
|
4419
|
+
// identifier in the spread element above. So we use that spread Element as is.
|
|
4420
|
+
transformedSignalArgs = ts.factory.createNodeArray([spreadElementContainingUpdatedOptions]);
|
|
4421
|
+
}
|
|
4422
|
+
else {
|
|
4423
|
+
// 3. Signal expression is not required and has arguments.
|
|
4424
|
+
// Here we leave the first argument as is and spread the rest.
|
|
4425
|
+
transformedSignalArgs = ts.factory.createNodeArray([
|
|
4426
|
+
nodeArgs[0],
|
|
4427
|
+
spreadElementContainingUpdatedOptions,
|
|
4428
|
+
]);
|
|
4429
|
+
}
|
|
4430
|
+
return ts.factory.updateCallExpression(callExpression, callExpression.expression, callExpression.typeArguments, transformedSignalArgs);
|
|
4431
|
+
}
|
|
4432
|
+
/**
|
|
4433
|
+
*
|
|
4434
|
+
* Determines if the node is a variable declaration with a call expression initializer.
|
|
4435
|
+
* Ex:
|
|
4436
|
+
* ```ts
|
|
4437
|
+
* const mySignal = signal(123);
|
|
4438
|
+
* ```
|
|
4439
|
+
*/
|
|
4440
|
+
function isVariableDeclarationCase(node) {
|
|
4441
|
+
if (!ts.isVariableDeclaration(node)) {
|
|
4442
|
+
return false;
|
|
4443
|
+
}
|
|
4444
|
+
if (!node.initializer || !ts.isCallExpression(node.initializer)) {
|
|
4445
|
+
return false;
|
|
4446
|
+
}
|
|
4447
|
+
let expression = node.initializer.expression;
|
|
4448
|
+
if (ts.isPropertyAccessExpression(expression)) {
|
|
4449
|
+
expression = expression.expression;
|
|
4450
|
+
}
|
|
4451
|
+
return ts.isIdentifier(expression) && isSignalFunction(expression);
|
|
4452
|
+
}
|
|
4453
|
+
/**
|
|
4454
|
+
*
|
|
4455
|
+
* Determines if the node is a property assignment with a call expression initializer.
|
|
4456
|
+
*
|
|
4457
|
+
* Ex:
|
|
4458
|
+
* ```ts
|
|
4459
|
+
* class MyClass {
|
|
4460
|
+
* mySignal: Signal<number>;
|
|
4461
|
+
* constructor() {
|
|
4462
|
+
* this.mySignal = signal(123);
|
|
4463
|
+
* }
|
|
4464
|
+
* }
|
|
4465
|
+
* ```
|
|
4466
|
+
*/
|
|
4467
|
+
function isPropertyAssignmentCase(node) {
|
|
4468
|
+
if (!ts.isExpressionStatement(node)) {
|
|
4469
|
+
return false;
|
|
4470
|
+
}
|
|
4471
|
+
if (!ts.isBinaryExpression(node.expression)) {
|
|
4472
|
+
return false;
|
|
4473
|
+
}
|
|
4474
|
+
const binaryExpression = node.expression;
|
|
4475
|
+
if (binaryExpression.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
|
|
4476
|
+
return false;
|
|
4477
|
+
}
|
|
4478
|
+
if (!ts.isCallExpression(binaryExpression.right)) {
|
|
4479
|
+
return false;
|
|
4480
|
+
}
|
|
4481
|
+
if (!ts.isPropertyAccessExpression(binaryExpression.left)) {
|
|
4482
|
+
return false;
|
|
4483
|
+
}
|
|
4484
|
+
let expression = binaryExpression.right.expression;
|
|
4485
|
+
if (ts.isPropertyAccessExpression(expression)) {
|
|
4486
|
+
expression = expression.expression;
|
|
4487
|
+
}
|
|
4488
|
+
return ts.isIdentifier(expression) && isSignalFunction(expression);
|
|
4489
|
+
}
|
|
4490
|
+
/**
|
|
4491
|
+
*
|
|
4492
|
+
* Determines if the node is a property declaration with a call expression initializer.
|
|
4493
|
+
*
|
|
4494
|
+
* Ex:
|
|
4495
|
+
* ```ts
|
|
4496
|
+
* class MyClass {
|
|
4497
|
+
* mySignal: Signal<number> = signal(123);
|
|
4498
|
+
* }
|
|
4499
|
+
* ```
|
|
4500
|
+
*/
|
|
4501
|
+
function isPropertyDeclarationCase(node) {
|
|
4502
|
+
if (!ts.isPropertyDeclaration(node)) {
|
|
4503
|
+
return false;
|
|
4504
|
+
}
|
|
4505
|
+
if (!(node.initializer && ts.isCallExpression(node.initializer))) {
|
|
4506
|
+
return false;
|
|
4507
|
+
}
|
|
4508
|
+
let expression = node.initializer.expression;
|
|
4509
|
+
if (ts.isPropertyAccessExpression(expression)) {
|
|
4510
|
+
expression = expression.expression;
|
|
4511
|
+
}
|
|
4512
|
+
return ts.isIdentifier(expression) && isSignalFunction(expression);
|
|
4513
|
+
}
|
|
4514
|
+
/**
|
|
4515
|
+
*
|
|
4516
|
+
* Determines if a node is an expression that references an @angular/core imported symbol.
|
|
4517
|
+
* Ex:
|
|
4518
|
+
* ```ts
|
|
4519
|
+
* import { signal } from '@angular/core';
|
|
4520
|
+
* const mySignal = signal(123); // expressionIsUsingAngularImportedSymbol === true
|
|
4521
|
+
* ```
|
|
4522
|
+
*/
|
|
4523
|
+
function expressionIsUsingAngularCoreImportedSymbol(program, expression) {
|
|
4524
|
+
const symbol = program.getTypeChecker().getSymbolAtLocation(expression);
|
|
4525
|
+
if (symbol === undefined) {
|
|
4526
|
+
return false;
|
|
4527
|
+
}
|
|
4528
|
+
const declarations = symbol.declarations;
|
|
4529
|
+
if (declarations === undefined || declarations.length === 0) {
|
|
4530
|
+
return false;
|
|
4531
|
+
}
|
|
4532
|
+
// climb up the tree from the import specifier to the import declaration
|
|
4533
|
+
const importSpecifier = declarations[0];
|
|
4534
|
+
if (!ts.isImportSpecifier(importSpecifier)) {
|
|
4535
|
+
return false;
|
|
4536
|
+
}
|
|
4537
|
+
const namedImports = importSpecifier.parent;
|
|
4538
|
+
if (!ts.isNamedImports(namedImports)) {
|
|
4539
|
+
return false;
|
|
4540
|
+
}
|
|
4541
|
+
const importsClause = namedImports.parent;
|
|
4542
|
+
if (!ts.isImportClause(importsClause)) {
|
|
4543
|
+
return false;
|
|
4544
|
+
}
|
|
4545
|
+
const importDeclaration = importsClause.parent;
|
|
4546
|
+
if (!ts.isImportDeclaration(importDeclaration) ||
|
|
4547
|
+
!ts.isStringLiteral(importDeclaration.moduleSpecifier)) {
|
|
4548
|
+
return false;
|
|
4549
|
+
}
|
|
4550
|
+
const specifier = importDeclaration.moduleSpecifier.text;
|
|
4551
|
+
return (specifier !== undefined &&
|
|
4552
|
+
(specifier === '@angular/core' || specifier.startsWith('@angular/core/')));
|
|
4553
|
+
}
|
|
4554
|
+
const signalFunctions = new Set([
|
|
4555
|
+
'signal',
|
|
4556
|
+
'computed',
|
|
4557
|
+
'input',
|
|
4558
|
+
'model',
|
|
4559
|
+
'viewChild',
|
|
4560
|
+
'viewChildren',
|
|
4561
|
+
'contentChild',
|
|
4562
|
+
'contentChildren',
|
|
4563
|
+
'effect',
|
|
4564
|
+
]);
|
|
4565
|
+
function isSignalFunction(expression) {
|
|
4566
|
+
const text = expression.text;
|
|
4567
|
+
return signalFunctions.has(text);
|
|
4568
|
+
}
|
|
4569
|
+
function isRequiredSignalFunction(expression) {
|
|
4570
|
+
// Check for a property access expression that uses the 'required' property
|
|
4571
|
+
if (ts.isPropertyAccessExpression(expression) &&
|
|
4572
|
+
ts.isIdentifier(expression.name) &&
|
|
4573
|
+
ts.isIdentifier(expression.expression)) {
|
|
4574
|
+
const accessName = expression.name.text;
|
|
4575
|
+
if (accessName === 'required') {
|
|
4576
|
+
return true;
|
|
4577
|
+
}
|
|
4578
|
+
}
|
|
4579
|
+
return false;
|
|
4580
|
+
}
|
|
4581
|
+
function transformVariableDeclaration(program, node) {
|
|
4582
|
+
if (!node.initializer || !ts.isCallExpression(node.initializer))
|
|
4583
|
+
return node;
|
|
4584
|
+
const expression = node.initializer.expression;
|
|
4585
|
+
if (ts.isPropertyAccessExpression(expression)) {
|
|
4586
|
+
if (!expressionIsUsingAngularCoreImportedSymbol(program, expression.expression)) {
|
|
4587
|
+
return node;
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
else if (!expressionIsUsingAngularCoreImportedSymbol(program, expression)) {
|
|
4591
|
+
return node;
|
|
4592
|
+
}
|
|
4593
|
+
try {
|
|
4594
|
+
// may throw if the node does not have a source file. Ignore this case for now
|
|
4595
|
+
const nodeText = node.name.getText();
|
|
4596
|
+
return ts.factory.updateVariableDeclaration(node, node.name, node.exclamationToken, node.type, insertDebugNameIntoCallExpression(node.initializer, nodeText));
|
|
4597
|
+
}
|
|
4598
|
+
catch {
|
|
4599
|
+
return node;
|
|
4600
|
+
}
|
|
4601
|
+
}
|
|
4602
|
+
function transformPropertyAssignment(program, node) {
|
|
4603
|
+
const expression = node.expression.right.expression;
|
|
4604
|
+
if (ts.isPropertyAccessExpression(expression)) {
|
|
4605
|
+
if (!expressionIsUsingAngularCoreImportedSymbol(program, expression.expression)) {
|
|
4606
|
+
return node;
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4609
|
+
else if (!expressionIsUsingAngularCoreImportedSymbol(program, expression)) {
|
|
4610
|
+
return node;
|
|
4611
|
+
}
|
|
4612
|
+
return ts.factory.updateExpressionStatement(node, ts.factory.createBinaryExpression(node.expression.left, node.expression.operatorToken, insertDebugNameIntoCallExpression(node.expression.right, node.expression.left.name.text)));
|
|
4613
|
+
}
|
|
4614
|
+
function transformPropertyDeclaration(program, node) {
|
|
4615
|
+
if (!node.initializer || !ts.isCallExpression(node.initializer))
|
|
4616
|
+
return node;
|
|
4617
|
+
const expression = node.initializer.expression;
|
|
4618
|
+
if (ts.isPropertyAccessExpression(expression)) {
|
|
4619
|
+
if (!expressionIsUsingAngularCoreImportedSymbol(program, expression.expression)) {
|
|
4620
|
+
return node;
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
else if (!expressionIsUsingAngularCoreImportedSymbol(program, expression)) {
|
|
4624
|
+
return node;
|
|
4625
|
+
}
|
|
4626
|
+
try {
|
|
4627
|
+
// may throw if the node does not have a source file. Ignore this case for now.
|
|
4628
|
+
const nodeText = node.name.getText();
|
|
4629
|
+
return ts.factory.updatePropertyDeclaration(node, node.modifiers, node.name, node.questionToken, node.type, insertDebugNameIntoCallExpression(node.initializer, nodeText));
|
|
4630
|
+
}
|
|
4631
|
+
catch {
|
|
4632
|
+
return node;
|
|
4633
|
+
}
|
|
4634
|
+
}
|
|
4635
|
+
/**
|
|
4636
|
+
*
|
|
4637
|
+
* This transformer adds a debugName property to the config object of signal functions like
|
|
4638
|
+
* signal, computed, effect, etc.
|
|
4639
|
+
*
|
|
4640
|
+
* The debugName property is added conditionally based on the value of ngDevMode. This is done
|
|
4641
|
+
* to avoid adding the debugName property in production builds.
|
|
4642
|
+
*
|
|
4643
|
+
* Ex:
|
|
4644
|
+
* ```ts
|
|
4645
|
+
* import {signal} from '@angular/core';
|
|
4646
|
+
* const mySignal = signal('Hello World');
|
|
4647
|
+
* ```
|
|
4648
|
+
*
|
|
4649
|
+
* is transformed to:
|
|
4650
|
+
* ```ts
|
|
4651
|
+
* import {signal} from '@angular/core';
|
|
4652
|
+
* const mySignal = signal('Hello World', ...(ngDevMode ? [{ debugName: "mySignal" }] : []));
|
|
4653
|
+
* ```
|
|
4654
|
+
*
|
|
4655
|
+
* The transformer supports the following cases:
|
|
4656
|
+
*
|
|
4657
|
+
* # Variable declaration
|
|
4658
|
+
* ```ts
|
|
4659
|
+
* const mySignal = signal('Hello World');
|
|
4660
|
+
* ```
|
|
4661
|
+
*
|
|
4662
|
+
* becomes
|
|
4663
|
+
* ```
|
|
4664
|
+
* const mySignal = signal('Hello World', ...(ngDevMode ? [{ debugName: "mySignal" }] : []));
|
|
4665
|
+
* ```
|
|
4666
|
+
*
|
|
4667
|
+
* # Property assignment
|
|
4668
|
+
* ```ts
|
|
4669
|
+
* class MyClass {
|
|
4670
|
+
* mySignal: Signal<string>;
|
|
4671
|
+
* constructor() {
|
|
4672
|
+
* this.mySignal = signal('Hello World');
|
|
4673
|
+
* }
|
|
4674
|
+
* }
|
|
4675
|
+
* ```
|
|
4676
|
+
* becomes
|
|
4677
|
+
* ```ts
|
|
4678
|
+
* class MyClass {
|
|
4679
|
+
* mySignal: Signal<string>;
|
|
4680
|
+
* constructor() {
|
|
4681
|
+
* this.mySignal = signal(...(ngDevMode ? ['Hello World', { debugName: "mySignal" }] : ['Hello World']));
|
|
4682
|
+
* }
|
|
4683
|
+
* }
|
|
4684
|
+
* ```
|
|
4685
|
+
*
|
|
4686
|
+
* # Property declaration
|
|
4687
|
+
* ```ts
|
|
4688
|
+
* class MyClass {
|
|
4689
|
+
* mySignal = signal('Hello World');
|
|
4690
|
+
* }
|
|
4691
|
+
* ```
|
|
4692
|
+
* becomes
|
|
4693
|
+
* ```ts
|
|
4694
|
+
* class MyClass {
|
|
4695
|
+
* mySignal = signal(...(ngDevMode ? ['Hello World', { debugName: "mySignal" }] : ['Hello World']));
|
|
4696
|
+
* }
|
|
4697
|
+
* ```
|
|
4698
|
+
*
|
|
4699
|
+
*/
|
|
4700
|
+
function signalMetadataTransform(program) {
|
|
4701
|
+
return (context) => (rootNode) => {
|
|
4702
|
+
const visit = (node) => {
|
|
4703
|
+
if (isVariableDeclarationCase(node)) {
|
|
4704
|
+
return transformVariableDeclaration(program, node);
|
|
4705
|
+
}
|
|
4706
|
+
if (isPropertyAssignmentCase(node)) {
|
|
4707
|
+
return transformPropertyAssignment(program, node);
|
|
4708
|
+
}
|
|
4709
|
+
if (isPropertyDeclarationCase(node)) {
|
|
4710
|
+
return transformPropertyDeclaration(program, node);
|
|
4711
|
+
}
|
|
4712
|
+
return ts.visitEachChild(node, visit, context);
|
|
4713
|
+
};
|
|
4714
|
+
return ts.visitNode(rootNode, visit);
|
|
4715
|
+
};
|
|
4716
|
+
}
|
|
4717
|
+
|
|
4383
4718
|
function resolveEnumValue(evaluator, metadata, field, enumSymbolName, isCore) {
|
|
4384
4719
|
let resolved = null;
|
|
4385
4720
|
if (metadata.has(field)) {
|
|
@@ -5880,6 +6215,111 @@ function reexportCollision(module, refA, refB) {
|
|
|
5880
6215
|
]);
|
|
5881
6216
|
}
|
|
5882
6217
|
|
|
6218
|
+
/**
|
|
6219
|
+
* Computes the scope for a selectorless component by looking at imports within the same
|
|
6220
|
+
* file and resolving them to metadata.
|
|
6221
|
+
*/
|
|
6222
|
+
class SelectorlessComponentScopeReader {
|
|
6223
|
+
metaReader;
|
|
6224
|
+
reflector;
|
|
6225
|
+
cache = new Map();
|
|
6226
|
+
constructor(metaReader, reflector) {
|
|
6227
|
+
this.metaReader = metaReader;
|
|
6228
|
+
this.reflector = reflector;
|
|
6229
|
+
}
|
|
6230
|
+
getScopeForComponent(node) {
|
|
6231
|
+
if (this.cache.has(node)) {
|
|
6232
|
+
return this.cache.get(node);
|
|
6233
|
+
}
|
|
6234
|
+
const clazzRef = new checker.Reference(node);
|
|
6235
|
+
const meta = this.metaReader.getDirectiveMetadata(clazzRef);
|
|
6236
|
+
if (meta === null || !meta.isComponent || !meta.isStandalone || !meta.selectorlessEnabled) {
|
|
6237
|
+
this.cache.set(node, null);
|
|
6238
|
+
return null;
|
|
6239
|
+
}
|
|
6240
|
+
const eligibleIdentifiers = this.getAvailableIdentifiers(node);
|
|
6241
|
+
const dependencies = new Map();
|
|
6242
|
+
const dependencyIdentifiers = [];
|
|
6243
|
+
let isPoisoned = meta.isPoisoned;
|
|
6244
|
+
for (const [name, identifier] of eligibleIdentifiers) {
|
|
6245
|
+
if (dependencies.has(name)) {
|
|
6246
|
+
continue;
|
|
6247
|
+
}
|
|
6248
|
+
const dep = this.getMetaFromIdentifier(meta, name, identifier);
|
|
6249
|
+
if (dep !== null) {
|
|
6250
|
+
dependencies.set(name, dep);
|
|
6251
|
+
dependencyIdentifiers.push(identifier);
|
|
6252
|
+
if (dep.kind === checker.MetaKind.Directive && dep.isPoisoned) {
|
|
6253
|
+
isPoisoned = true;
|
|
6254
|
+
}
|
|
6255
|
+
}
|
|
6256
|
+
}
|
|
6257
|
+
const scope = {
|
|
6258
|
+
kind: checker.ComponentScopeKind.Selectorless,
|
|
6259
|
+
component: node,
|
|
6260
|
+
dependencies,
|
|
6261
|
+
dependencyIdentifiers,
|
|
6262
|
+
isPoisoned,
|
|
6263
|
+
schemas: meta.schemas ?? [],
|
|
6264
|
+
};
|
|
6265
|
+
this.cache.set(node, scope);
|
|
6266
|
+
return scope;
|
|
6267
|
+
}
|
|
6268
|
+
getRemoteScope() {
|
|
6269
|
+
return null;
|
|
6270
|
+
}
|
|
6271
|
+
/** Determines which identifiers a class has access to. */
|
|
6272
|
+
getAvailableIdentifiers(node) {
|
|
6273
|
+
const result = new Map();
|
|
6274
|
+
let current = ts.getOriginalNode(node).parent;
|
|
6275
|
+
while (current) {
|
|
6276
|
+
// Note: doesn't account for some cases like function parameters,
|
|
6277
|
+
// but we likely don't want to support those anyways.
|
|
6278
|
+
if (!ts.isSourceFile(current) && !ts.isBlock(current)) {
|
|
6279
|
+
current = current.parent;
|
|
6280
|
+
continue;
|
|
6281
|
+
}
|
|
6282
|
+
for (const stmt of current.statements) {
|
|
6283
|
+
if (this.reflector.isClass(stmt)) {
|
|
6284
|
+
result.set(stmt.name.text, stmt.name);
|
|
6285
|
+
continue;
|
|
6286
|
+
}
|
|
6287
|
+
if (ts.isImportDeclaration(stmt) &&
|
|
6288
|
+
stmt.importClause !== undefined &&
|
|
6289
|
+
!stmt.importClause.isTypeOnly) {
|
|
6290
|
+
const clause = stmt.importClause;
|
|
6291
|
+
if (clause.namedBindings !== undefined && ts.isNamedImports(clause.namedBindings)) {
|
|
6292
|
+
for (const element of clause.namedBindings.elements) {
|
|
6293
|
+
if (!element.isTypeOnly) {
|
|
6294
|
+
result.set(element.name.text, element.name);
|
|
6295
|
+
}
|
|
6296
|
+
}
|
|
6297
|
+
}
|
|
6298
|
+
if (clause.name !== undefined) {
|
|
6299
|
+
result.set(clause.name.text, clause.name);
|
|
6300
|
+
}
|
|
6301
|
+
continue;
|
|
6302
|
+
}
|
|
6303
|
+
}
|
|
6304
|
+
current = current.parent;
|
|
6305
|
+
}
|
|
6306
|
+
return result;
|
|
6307
|
+
}
|
|
6308
|
+
getMetaFromIdentifier(meta, localName, node) {
|
|
6309
|
+
// Consult the set of used names in the template so we don't hit the type checker for every
|
|
6310
|
+
// import in the file. Most likely a subset of imports in the file will be used in the template.
|
|
6311
|
+
if (meta.localReferencedSymbols === null || !meta.localReferencedSymbols.has(localName)) {
|
|
6312
|
+
return null;
|
|
6313
|
+
}
|
|
6314
|
+
const declaration = this.reflector.getDeclarationOfIdentifier(node);
|
|
6315
|
+
if (declaration === null || !this.reflector.isClass(declaration.node)) {
|
|
6316
|
+
return null;
|
|
6317
|
+
}
|
|
6318
|
+
const ref = new checker.Reference(declaration.node);
|
|
6319
|
+
return this.metaReader.getDirectiveMetadata(ref) ?? this.metaReader.getPipeMetadata(ref);
|
|
6320
|
+
}
|
|
6321
|
+
}
|
|
6322
|
+
|
|
5883
6323
|
/**
|
|
5884
6324
|
* Computes scope information to be used in template type checking.
|
|
5885
6325
|
*/
|
|
@@ -5907,13 +6347,12 @@ class TypeCheckScopeRegistry {
|
|
|
5907
6347
|
* an empty type-check scope is returned.
|
|
5908
6348
|
*/
|
|
5909
6349
|
getTypeCheckScope(node) {
|
|
5910
|
-
const matcher = new checker.SelectorMatcher();
|
|
5911
6350
|
const directives = [];
|
|
5912
6351
|
const pipes = new Map();
|
|
5913
6352
|
const scope = this.scopeReader.getScopeForComponent(node);
|
|
5914
6353
|
if (scope === null) {
|
|
5915
6354
|
return {
|
|
5916
|
-
matcher,
|
|
6355
|
+
matcher: null,
|
|
5917
6356
|
directives,
|
|
5918
6357
|
pipes,
|
|
5919
6358
|
schemas: [],
|
|
@@ -5921,36 +6360,40 @@ class TypeCheckScopeRegistry {
|
|
|
5921
6360
|
};
|
|
5922
6361
|
}
|
|
5923
6362
|
const isNgModuleScope = scope.kind === checker.ComponentScopeKind.NgModule;
|
|
6363
|
+
const isSelectorlessScope = scope.kind === checker.ComponentScopeKind.Selectorless;
|
|
5924
6364
|
const cacheKey = isNgModuleScope ? scope.ngModule : scope.component;
|
|
5925
|
-
const dependencies = isNgModuleScope ? scope.compilation.dependencies : scope.dependencies;
|
|
5926
6365
|
if (this.scopeCache.has(cacheKey)) {
|
|
5927
6366
|
return this.scopeCache.get(cacheKey);
|
|
5928
6367
|
}
|
|
5929
|
-
let
|
|
5930
|
-
if (
|
|
5931
|
-
|
|
5932
|
-
scope.
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
continue;
|
|
6368
|
+
let matcher;
|
|
6369
|
+
if (isSelectorlessScope) {
|
|
6370
|
+
matcher = this.getSelectorlessMatcher(scope);
|
|
6371
|
+
for (const [name, dep] of scope.dependencies) {
|
|
6372
|
+
if (dep.kind === checker.MetaKind.Directive) {
|
|
6373
|
+
directives.push(dep);
|
|
6374
|
+
}
|
|
6375
|
+
else {
|
|
6376
|
+
// Pipes should be available under the imported name in selectorless.
|
|
6377
|
+
pipes.set(name, dep);
|
|
5940
6378
|
}
|
|
5941
|
-
// Carry over the `isExplicitlyDeferred` flag from the dependency info.
|
|
5942
|
-
const directiveMeta = this.applyExplicitlyDeferredFlag(extMeta, meta.isExplicitlyDeferred);
|
|
5943
|
-
matcher.addSelectables(checker.CssSelector.parse(meta.selector), [
|
|
5944
|
-
...this.hostDirectivesResolver.resolve(directiveMeta),
|
|
5945
|
-
directiveMeta,
|
|
5946
|
-
]);
|
|
5947
|
-
directives.push(directiveMeta);
|
|
5948
6379
|
}
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
6380
|
+
}
|
|
6381
|
+
else {
|
|
6382
|
+
const dependencies = isNgModuleScope ? scope.compilation.dependencies : scope.dependencies;
|
|
6383
|
+
let allDependencies = dependencies;
|
|
6384
|
+
if (!isNgModuleScope &&
|
|
6385
|
+
Array.isArray(scope.deferredDependencies) &&
|
|
6386
|
+
scope.deferredDependencies.length > 0) {
|
|
6387
|
+
allDependencies = [...allDependencies, ...scope.deferredDependencies];
|
|
6388
|
+
}
|
|
6389
|
+
matcher = this.getSelectorMatcher(allDependencies);
|
|
6390
|
+
for (const dep of allDependencies) {
|
|
6391
|
+
if (dep.kind === checker.MetaKind.Directive) {
|
|
6392
|
+
directives.push(dep);
|
|
6393
|
+
}
|
|
6394
|
+
else if (dep.kind === checker.MetaKind.Pipe && dep.name !== null) {
|
|
6395
|
+
pipes.set(dep.name, dep);
|
|
5952
6396
|
}
|
|
5953
|
-
pipes.set(meta.name, meta);
|
|
5954
6397
|
}
|
|
5955
6398
|
}
|
|
5956
6399
|
const typeCheckScope = {
|
|
@@ -5980,6 +6423,34 @@ class TypeCheckScopeRegistry {
|
|
|
5980
6423
|
applyExplicitlyDeferredFlag(meta, isExplicitlyDeferred) {
|
|
5981
6424
|
return isExplicitlyDeferred === true ? { ...meta, isExplicitlyDeferred } : meta;
|
|
5982
6425
|
}
|
|
6426
|
+
getSelectorMatcher(allDependencies) {
|
|
6427
|
+
const matcher = new checker.SelectorMatcher();
|
|
6428
|
+
for (const meta of allDependencies) {
|
|
6429
|
+
if (meta.kind === checker.MetaKind.Directive && meta.selector !== null) {
|
|
6430
|
+
const extMeta = this.getTypeCheckDirectiveMetadata(meta.ref);
|
|
6431
|
+
if (extMeta === null) {
|
|
6432
|
+
continue;
|
|
6433
|
+
}
|
|
6434
|
+
// Carry over the `isExplicitlyDeferred` flag from the dependency info.
|
|
6435
|
+
const directiveMeta = this.applyExplicitlyDeferredFlag(extMeta, meta.isExplicitlyDeferred);
|
|
6436
|
+
matcher.addSelectables(checker.CssSelector.parse(meta.selector), [
|
|
6437
|
+
...this.hostDirectivesResolver.resolve(directiveMeta),
|
|
6438
|
+
directiveMeta,
|
|
6439
|
+
]);
|
|
6440
|
+
}
|
|
6441
|
+
}
|
|
6442
|
+
return matcher;
|
|
6443
|
+
}
|
|
6444
|
+
getSelectorlessMatcher(scope) {
|
|
6445
|
+
const registry = new Map();
|
|
6446
|
+
for (const [name, dep] of scope.dependencies) {
|
|
6447
|
+
const extMeta = dep.kind === checker.MetaKind.Directive ? this.getTypeCheckDirectiveMetadata(dep.ref) : null;
|
|
6448
|
+
if (extMeta !== null) {
|
|
6449
|
+
registry.set(name, [extMeta, ...this.hostDirectivesResolver.resolve(extMeta)]);
|
|
6450
|
+
}
|
|
6451
|
+
}
|
|
6452
|
+
return new checker.SelectorlessMatcher(registry);
|
|
6453
|
+
}
|
|
5983
6454
|
}
|
|
5984
6455
|
|
|
5985
6456
|
/**
|
|
@@ -6599,7 +7070,8 @@ class DirectiveDecoratorHandler {
|
|
|
6599
7070
|
implicitStandaloneValue;
|
|
6600
7071
|
usePoisonedData;
|
|
6601
7072
|
typeCheckHostBindings;
|
|
6602
|
-
|
|
7073
|
+
emitDeclarationOnly;
|
|
7074
|
+
constructor(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, injectableRegistry, refEmitter, referencesRegistry, isCore, strictCtorDeps, semanticDepGraphUpdater, annotateForClosureCompiler, perf, importTracker, includeClassMetadata, typeCheckScopeRegistry, compilationMode, jitDeclarationRegistry, resourceRegistry, strictStandalone, implicitStandaloneValue, usePoisonedData, typeCheckHostBindings, emitDeclarationOnly) {
|
|
6603
7075
|
this.reflector = reflector;
|
|
6604
7076
|
this.evaluator = evaluator;
|
|
6605
7077
|
this.metaRegistry = metaRegistry;
|
|
@@ -6623,6 +7095,7 @@ class DirectiveDecoratorHandler {
|
|
|
6623
7095
|
this.implicitStandaloneValue = implicitStandaloneValue;
|
|
6624
7096
|
this.usePoisonedData = usePoisonedData;
|
|
6625
7097
|
this.typeCheckHostBindings = typeCheckHostBindings;
|
|
7098
|
+
this.emitDeclarationOnly = emitDeclarationOnly;
|
|
6626
7099
|
}
|
|
6627
7100
|
precedence = checker.HandlerPrecedence.PRIMARY;
|
|
6628
7101
|
name = 'DirectiveDecoratorHandler';
|
|
@@ -6656,7 +7129,7 @@ class DirectiveDecoratorHandler {
|
|
|
6656
7129
|
}
|
|
6657
7130
|
this.perf.eventCount(checker.PerfEvent.AnalyzeDirective);
|
|
6658
7131
|
const directiveResult = checker.extractDirectiveMetadata(node, decorator, this.reflector, this.importTracker, this.evaluator, this.refEmitter, this.referencesRegistry, this.isCore, this.annotateForClosureCompiler, this.compilationMode,
|
|
6659
|
-
/* defaultSelector */ null, this.strictStandalone, this.implicitStandaloneValue);
|
|
7132
|
+
/* defaultSelector */ null, this.strictStandalone, this.implicitStandaloneValue, this.emitDeclarationOnly);
|
|
6660
7133
|
// `extractDirectiveMetadata` returns `jitForced = true` when the `@Directive` has
|
|
6661
7134
|
// set `jit: true`. In this case, compilation of the decorator is skipped. Returning
|
|
6662
7135
|
// an empty object signifies that no analysis was produced.
|
|
@@ -6734,6 +7207,8 @@ class DirectiveDecoratorHandler {
|
|
|
6734
7207
|
// Instead, we statically analyze their imports to make a direct determination.
|
|
6735
7208
|
assumedToExportProviders: false,
|
|
6736
7209
|
isExplicitlyDeferred: false,
|
|
7210
|
+
selectorlessEnabled: false,
|
|
7211
|
+
localReferencedSymbols: null,
|
|
6737
7212
|
});
|
|
6738
7213
|
this.resourceRegistry.registerResources(analysis.resources, node);
|
|
6739
7214
|
this.injectableRegistry.registerInjectable(node, {
|
|
@@ -7063,7 +7538,8 @@ class NgModuleDecoratorHandler {
|
|
|
7063
7538
|
compilationMode;
|
|
7064
7539
|
localCompilationExtraImportsTracker;
|
|
7065
7540
|
jitDeclarationRegistry;
|
|
7066
|
-
|
|
7541
|
+
emitDeclarationOnly;
|
|
7542
|
+
constructor(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, exportedProviderStatusResolver, semanticDepGraphUpdater, isCore, refEmitter, annotateForClosureCompiler, onlyPublishPublicTypings, injectableRegistry, perf, includeClassMetadata, includeSelectorScope, compilationMode, localCompilationExtraImportsTracker, jitDeclarationRegistry, emitDeclarationOnly) {
|
|
7067
7543
|
this.reflector = reflector;
|
|
7068
7544
|
this.evaluator = evaluator;
|
|
7069
7545
|
this.metaReader = metaReader;
|
|
@@ -7083,6 +7559,7 @@ class NgModuleDecoratorHandler {
|
|
|
7083
7559
|
this.compilationMode = compilationMode;
|
|
7084
7560
|
this.localCompilationExtraImportsTracker = localCompilationExtraImportsTracker;
|
|
7085
7561
|
this.jitDeclarationRegistry = jitDeclarationRegistry;
|
|
7562
|
+
this.emitDeclarationOnly = emitDeclarationOnly;
|
|
7086
7563
|
}
|
|
7087
7564
|
precedence = checker.HandlerPrecedence.PRIMARY;
|
|
7088
7565
|
name = 'NgModuleDecoratorHandler';
|
|
@@ -7127,13 +7604,14 @@ class NgModuleDecoratorHandler {
|
|
|
7127
7604
|
createModuleWithProvidersResolver(this.reflector, this.isCore),
|
|
7128
7605
|
forwardRefResolver,
|
|
7129
7606
|
]);
|
|
7607
|
+
const allowUnresolvedReferences = this.compilationMode === checker.CompilationMode.LOCAL && !this.emitDeclarationOnly;
|
|
7130
7608
|
const diagnostics = [];
|
|
7131
7609
|
// Resolving declarations
|
|
7132
7610
|
let declarationRefs = [];
|
|
7133
7611
|
const rawDeclarations = ngModule.get('declarations') ?? null;
|
|
7134
7612
|
if (rawDeclarations !== null) {
|
|
7135
7613
|
const declarationMeta = this.evaluator.evaluate(rawDeclarations, forwardRefResolver);
|
|
7136
|
-
declarationRefs = this.resolveTypeList(rawDeclarations, declarationMeta, name, 'declarations', 0,
|
|
7614
|
+
declarationRefs = this.resolveTypeList(rawDeclarations, declarationMeta, name, 'declarations', 0, allowUnresolvedReferences).references;
|
|
7137
7615
|
// Look through the declarations to make sure they're all a part of the current compilation.
|
|
7138
7616
|
for (const ref of declarationRefs) {
|
|
7139
7617
|
if (ref.node.getSourceFile().isDeclarationFile) {
|
|
@@ -7150,7 +7628,7 @@ class NgModuleDecoratorHandler {
|
|
|
7150
7628
|
let rawImports = ngModule.get('imports') ?? null;
|
|
7151
7629
|
if (rawImports !== null) {
|
|
7152
7630
|
const importsMeta = this.evaluator.evaluate(rawImports, moduleResolvers);
|
|
7153
|
-
const result = this.resolveTypeList(rawImports, importsMeta, name, 'imports', 0,
|
|
7631
|
+
const result = this.resolveTypeList(rawImports, importsMeta, name, 'imports', 0, allowUnresolvedReferences);
|
|
7154
7632
|
if (this.compilationMode === checker.CompilationMode.LOCAL &&
|
|
7155
7633
|
this.localCompilationExtraImportsTracker !== null) {
|
|
7156
7634
|
// For generating extra imports in local mode, the NgModule imports that are from external
|
|
@@ -7173,13 +7651,13 @@ class NgModuleDecoratorHandler {
|
|
|
7173
7651
|
const rawExports = ngModule.get('exports') ?? null;
|
|
7174
7652
|
if (rawExports !== null) {
|
|
7175
7653
|
const exportsMeta = this.evaluator.evaluate(rawExports, moduleResolvers);
|
|
7176
|
-
exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports', 0,
|
|
7654
|
+
exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports', 0, allowUnresolvedReferences).references;
|
|
7177
7655
|
this.referencesRegistry.add(node, ...exportRefs);
|
|
7178
7656
|
}
|
|
7179
7657
|
// Resolving bootstrap
|
|
7180
7658
|
let bootstrapRefs = [];
|
|
7181
7659
|
const rawBootstrap = ngModule.get('bootstrap') ?? null;
|
|
7182
|
-
if (
|
|
7660
|
+
if (!allowUnresolvedReferences && rawBootstrap !== null) {
|
|
7183
7661
|
const bootstrapMeta = this.evaluator.evaluate(rawBootstrap, forwardRefResolver);
|
|
7184
7662
|
bootstrapRefs = this.resolveTypeList(rawBootstrap, bootstrapMeta, name, 'bootstrap', 0,
|
|
7185
7663
|
/* allowUnresolvedReferences */ false).references;
|
|
@@ -7245,7 +7723,7 @@ class NgModuleDecoratorHandler {
|
|
|
7245
7723
|
exports.some(isForwardReference);
|
|
7246
7724
|
const type = checker.wrapTypeReference(this.reflector, node);
|
|
7247
7725
|
let ngModuleMetadata;
|
|
7248
|
-
if (
|
|
7726
|
+
if (allowUnresolvedReferences) {
|
|
7249
7727
|
ngModuleMetadata = {
|
|
7250
7728
|
kind: checker.R3NgModuleMetadataKind.Local,
|
|
7251
7729
|
type,
|
|
@@ -7295,7 +7773,7 @@ class NgModuleDecoratorHandler {
|
|
|
7295
7773
|
: rawProviders);
|
|
7296
7774
|
}
|
|
7297
7775
|
const topLevelImports = [];
|
|
7298
|
-
if (
|
|
7776
|
+
if (!allowUnresolvedReferences && ngModule.has('imports')) {
|
|
7299
7777
|
const rawImports = checker.unwrapExpression(ngModule.get('imports'));
|
|
7300
7778
|
let topLevelExpressions = [];
|
|
7301
7779
|
if (ts.isArrayLiteralExpression(rawImports)) {
|
|
@@ -7332,7 +7810,7 @@ class NgModuleDecoratorHandler {
|
|
|
7332
7810
|
providers: wrappedProviders,
|
|
7333
7811
|
imports: [],
|
|
7334
7812
|
};
|
|
7335
|
-
if (
|
|
7813
|
+
if (allowUnresolvedReferences) {
|
|
7336
7814
|
// Adding NgModule's raw imports/exports to the injector's imports field in local compilation
|
|
7337
7815
|
// mode.
|
|
7338
7816
|
for (const exp of [rawImports, rawExports]) {
|
|
@@ -7719,6 +8197,12 @@ class NgModuleDecoratorHandler {
|
|
|
7719
8197
|
dynamicValueSet.add(entry);
|
|
7720
8198
|
continue;
|
|
7721
8199
|
}
|
|
8200
|
+
else if (this.emitDeclarationOnly &&
|
|
8201
|
+
entry instanceof checker.DynamicValue &&
|
|
8202
|
+
entry.isFromUnknownIdentifier()) {
|
|
8203
|
+
throw checker.createValueHasWrongTypeError(entry.node, entry, `Value at position ${absoluteIndex} in the NgModule.${arrayName} of ${className} is an external reference. ` +
|
|
8204
|
+
'External references in @NgModule declarations are not supported in experimental declaration-only emission mode');
|
|
8205
|
+
}
|
|
7722
8206
|
else {
|
|
7723
8207
|
// TODO(alxhub): Produce a better diagnostic here - the array index may be an inner array.
|
|
7724
8208
|
throw checker.createValueHasWrongTypeError(expr, entry, `Value at position ${absoluteIndex} in the NgModule.${arrayName} of ${className} is not a reference`);
|
|
@@ -8847,6 +9331,52 @@ class HmrModuleImportRewriter {
|
|
|
8847
9331
|
}
|
|
8848
9332
|
}
|
|
8849
9333
|
|
|
9334
|
+
/*!
|
|
9335
|
+
* @license
|
|
9336
|
+
* Copyright Google LLC All Rights Reserved.
|
|
9337
|
+
*
|
|
9338
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
9339
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
9340
|
+
*/
|
|
9341
|
+
/**
|
|
9342
|
+
* Analyzes a component's template to determine if it's using selectorless syntax
|
|
9343
|
+
* and to extract the names of the selectorless symbols that are referenced.
|
|
9344
|
+
*/
|
|
9345
|
+
function analyzeTemplateForSelectorless(template) {
|
|
9346
|
+
const analyzer = new SelectorlessDirectivesAnalyzer();
|
|
9347
|
+
checker.visitAll(analyzer, template);
|
|
9348
|
+
const isSelectorless = analyzer.symbols !== null && analyzer.symbols.size > 0;
|
|
9349
|
+
const localReferencedSymbols = analyzer.symbols;
|
|
9350
|
+
// The template is considered selectorless only if there
|
|
9351
|
+
// are direct references to directives or pipes.
|
|
9352
|
+
return { isSelectorless, localReferencedSymbols };
|
|
9353
|
+
}
|
|
9354
|
+
/**
|
|
9355
|
+
* Visitor that traverses all the template nodes and
|
|
9356
|
+
* expressions to look for selectorless references.
|
|
9357
|
+
*/
|
|
9358
|
+
class SelectorlessDirectivesAnalyzer extends checker.CombinedRecursiveAstVisitor {
|
|
9359
|
+
symbols = null;
|
|
9360
|
+
visit(node) {
|
|
9361
|
+
if (node instanceof checker.BindingPipe && node.type === checker.BindingPipeType.ReferencedDirectly) {
|
|
9362
|
+
this.trackSymbol(node.name);
|
|
9363
|
+
}
|
|
9364
|
+
super.visit(node);
|
|
9365
|
+
}
|
|
9366
|
+
visitComponent(component) {
|
|
9367
|
+
this.trackSymbol(component.componentName);
|
|
9368
|
+
super.visitComponent(component);
|
|
9369
|
+
}
|
|
9370
|
+
visitDirective(directive) {
|
|
9371
|
+
this.trackSymbol(directive.name);
|
|
9372
|
+
super.visitDirective(directive);
|
|
9373
|
+
}
|
|
9374
|
+
trackSymbol(name) {
|
|
9375
|
+
this.symbols ??= new Set();
|
|
9376
|
+
this.symbols.add(name);
|
|
9377
|
+
}
|
|
9378
|
+
}
|
|
9379
|
+
|
|
8850
9380
|
const EMPTY_ARRAY = [];
|
|
8851
9381
|
const isUsedDirective = (decl) => decl.kind === checker.R3TemplateDependencyKind.Directive;
|
|
8852
9382
|
const isUsedPipe = (decl) => decl.kind === checker.R3TemplateDependencyKind.Pipe;
|
|
@@ -8899,7 +9429,8 @@ class ComponentDecoratorHandler {
|
|
|
8899
9429
|
implicitStandaloneValue;
|
|
8900
9430
|
typeCheckHostBindings;
|
|
8901
9431
|
enableSelectorless;
|
|
8902
|
-
|
|
9432
|
+
emitDeclarationOnly;
|
|
9433
|
+
constructor(reflector, evaluator, metaRegistry, metaReader, scopeReader, compilerHost, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, strictCtorDeps, resourceLoader, rootDirs, defaultPreserveWhitespaces, i18nUseExternalIds, enableI18nLegacyMessageIdFormat, usePoisonedData, i18nNormalizeLineEndingsInICUs, moduleResolver, cycleAnalyzer, cycleHandlingStrategy, refEmitter, referencesRegistry, depTracker, injectableRegistry, semanticDepGraphUpdater, annotateForClosureCompiler, perf, hostDirectivesResolver, importTracker, includeClassMetadata, compilationMode, deferredSymbolTracker, forbidOrphanRendering, enableBlockSyntax, enableLetSyntax, externalRuntimeStyles, localCompilationExtraImportsTracker, jitDeclarationRegistry, i18nPreserveSignificantWhitespace, strictStandalone, enableHmr, implicitStandaloneValue, typeCheckHostBindings, enableSelectorless, emitDeclarationOnly) {
|
|
8903
9434
|
this.reflector = reflector;
|
|
8904
9435
|
this.evaluator = evaluator;
|
|
8905
9436
|
this.metaRegistry = metaRegistry;
|
|
@@ -8945,6 +9476,7 @@ class ComponentDecoratorHandler {
|
|
|
8945
9476
|
this.implicitStandaloneValue = implicitStandaloneValue;
|
|
8946
9477
|
this.typeCheckHostBindings = typeCheckHostBindings;
|
|
8947
9478
|
this.enableSelectorless = enableSelectorless;
|
|
9479
|
+
this.emitDeclarationOnly = emitDeclarationOnly;
|
|
8948
9480
|
this.extractTemplateOptions = {
|
|
8949
9481
|
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
|
|
8950
9482
|
i18nNormalizeLineEndingsInICUs: this.i18nNormalizeLineEndingsInICUs,
|
|
@@ -9082,7 +9614,7 @@ class ComponentDecoratorHandler {
|
|
|
9082
9614
|
let isPoisoned = false;
|
|
9083
9615
|
// @Component inherits @Directive, so begin by extracting the @Directive metadata and building
|
|
9084
9616
|
// on it.
|
|
9085
|
-
const directiveResult = checker.extractDirectiveMetadata(node, decorator, this.reflector, this.importTracker, this.evaluator, this.refEmitter, this.referencesRegistry, this.isCore, this.annotateForClosureCompiler, this.compilationMode, this.elementSchemaRegistry.getDefaultComponentElementName(), this.strictStandalone, this.implicitStandaloneValue);
|
|
9617
|
+
const directiveResult = checker.extractDirectiveMetadata(node, decorator, this.reflector, this.importTracker, this.evaluator, this.refEmitter, this.referencesRegistry, this.isCore, this.annotateForClosureCompiler, this.compilationMode, this.elementSchemaRegistry.getDefaultComponentElementName(), this.strictStandalone, this.implicitStandaloneValue, this.emitDeclarationOnly);
|
|
9086
9618
|
// `extractDirectiveMetadata` returns `jitForced = true` when the `@Component` has
|
|
9087
9619
|
// set `jit: true`. In this case, compilation of the decorator is skipped. Returning
|
|
9088
9620
|
// an empty object signifies that no analysis was produced.
|
|
@@ -9262,6 +9794,25 @@ class ComponentDecoratorHandler {
|
|
|
9262
9794
|
node: template.sourceMapping.node,
|
|
9263
9795
|
};
|
|
9264
9796
|
const relativeTemplatePath = getProjectRelativePath(templateResource.path ?? ts.getOriginalNode(node).getSourceFile().fileName, this.rootDirs, this.compilerHost);
|
|
9797
|
+
let selectorlessEnabled = false;
|
|
9798
|
+
let localReferencedSymbols = null;
|
|
9799
|
+
if (this.enableSelectorless) {
|
|
9800
|
+
const templateAnalysis = analyzeTemplateForSelectorless(template.nodes);
|
|
9801
|
+
selectorlessEnabled = templateAnalysis.isSelectorless;
|
|
9802
|
+
localReferencedSymbols = templateAnalysis.localReferencedSymbols;
|
|
9803
|
+
}
|
|
9804
|
+
if (selectorlessEnabled) {
|
|
9805
|
+
if (!metadata.isStandalone) {
|
|
9806
|
+
isPoisoned = true;
|
|
9807
|
+
diagnostics ??= [];
|
|
9808
|
+
diagnostics.push(checker.makeDiagnostic(checker.ErrorCode.COMPONENT_NOT_STANDALONE, component.get('standalone') || node.name, `Cannot use selectorless with a component that is not standalone`));
|
|
9809
|
+
}
|
|
9810
|
+
else if (rawImports || rawDeferredImports) {
|
|
9811
|
+
isPoisoned = true;
|
|
9812
|
+
diagnostics ??= [];
|
|
9813
|
+
diagnostics.push(checker.makeDiagnostic(checker.ErrorCode.UNSUPPORTED_SELECTORLESS_COMPONENT_FIELD, (rawImports || rawDeferredImports), `Cannot use the "${rawImports === null ? 'deferredImports' : 'imports'}" field in a selectorless component`));
|
|
9814
|
+
}
|
|
9815
|
+
}
|
|
9265
9816
|
// Figure out the set of styles. The ordering here is important: external resources (styleUrls)
|
|
9266
9817
|
// precede inline styles, and styles defined in the template override styles defined in the
|
|
9267
9818
|
// component.
|
|
@@ -9379,6 +9930,8 @@ class ComponentDecoratorHandler {
|
|
|
9379
9930
|
outputs,
|
|
9380
9931
|
hostDirectives,
|
|
9381
9932
|
rawHostDirectives,
|
|
9933
|
+
selectorlessEnabled,
|
|
9934
|
+
localReferencedSymbols,
|
|
9382
9935
|
meta: {
|
|
9383
9936
|
...metadata,
|
|
9384
9937
|
template,
|
|
@@ -9464,6 +10017,8 @@ class ComponentDecoratorHandler {
|
|
|
9464
10017
|
ngContentSelectors: analysis.template.ngContentSelectors,
|
|
9465
10018
|
preserveWhitespaces: analysis.template.preserveWhitespaces ?? false,
|
|
9466
10019
|
isExplicitlyDeferred: false,
|
|
10020
|
+
selectorlessEnabled: analysis.selectorlessEnabled,
|
|
10021
|
+
localReferencedSymbols: analysis.localReferencedSymbols,
|
|
9467
10022
|
});
|
|
9468
10023
|
this.resourceRegistry.registerResources(analysis.resources, node);
|
|
9469
10024
|
this.injectableRegistry.registerInjectable(node, {
|
|
@@ -9476,25 +10031,19 @@ class ComponentDecoratorHandler {
|
|
|
9476
10031
|
}
|
|
9477
10032
|
const scope = this.scopeReader.getScopeForComponent(node);
|
|
9478
10033
|
const selector = analysis.meta.selector;
|
|
9479
|
-
|
|
10034
|
+
let matcher = null;
|
|
9480
10035
|
if (scope !== null) {
|
|
9481
|
-
|
|
10036
|
+
const isPoisoned = scope.kind === checker.ComponentScopeKind.NgModule
|
|
10037
|
+
? scope.compilation.isPoisoned
|
|
10038
|
+
: scope.isPoisoned;
|
|
9482
10039
|
if ((isPoisoned || (scope.kind === checker.ComponentScopeKind.NgModule && scope.exported.isPoisoned)) &&
|
|
9483
10040
|
!this.usePoisonedData) {
|
|
9484
10041
|
// Don't bother indexing components which had erroneous scopes, unless specifically
|
|
9485
10042
|
// requested.
|
|
9486
10043
|
return null;
|
|
9487
10044
|
}
|
|
9488
|
-
|
|
9489
|
-
if (dep.kind === checker.MetaKind.Directive && dep.selector !== null) {
|
|
9490
|
-
matcher.addSelectables(checker.CssSelector.parse(dep.selector), [
|
|
9491
|
-
...this.hostDirectivesResolver.resolve(dep),
|
|
9492
|
-
dep,
|
|
9493
|
-
]);
|
|
9494
|
-
}
|
|
9495
|
-
}
|
|
10045
|
+
matcher = createMatcherFromScope(scope, this.hostDirectivesResolver);
|
|
9496
10046
|
}
|
|
9497
|
-
// TODO(crisbeto): implement for selectorless.
|
|
9498
10047
|
const binder = new checker.R3TargetBinder(matcher);
|
|
9499
10048
|
const boundTemplate = binder.bind({ template: analysis.template.diagNodes });
|
|
9500
10049
|
context.addComponent({
|
|
@@ -9517,7 +10066,6 @@ class ComponentDecoratorHandler {
|
|
|
9517
10066
|
// Don't type-check components that had errors in their scopes, unless requested.
|
|
9518
10067
|
return;
|
|
9519
10068
|
}
|
|
9520
|
-
// TODO(crisbeto): implement for selectorless.
|
|
9521
10069
|
const binder = new checker.R3TargetBinder(scope.matcher);
|
|
9522
10070
|
const templateContext = {
|
|
9523
10071
|
nodes: meta.template.diagNodes,
|
|
@@ -9608,8 +10156,8 @@ class ComponentDecoratorHandler {
|
|
|
9608
10156
|
data.deferPerBlockDependencies = this.locateDeferBlocksWithoutScope(metadata.template);
|
|
9609
10157
|
}
|
|
9610
10158
|
else {
|
|
9611
|
-
const { eagerlyUsed, deferBlocks, allDependencies, wholeTemplateUsed } = this.resolveComponentDependencies(node, context, analysis, scope, metadata, diagnostics);
|
|
9612
|
-
const declarations = this.componentDependenciesToDeclarations(node, context, allDependencies, wholeTemplateUsed);
|
|
10159
|
+
const { eagerlyUsed, deferBlocks, allDependencies, wholeTemplateUsed, pipes } = this.resolveComponentDependencies(node, context, analysis, scope, metadata, diagnostics);
|
|
10160
|
+
const declarations = this.componentDependenciesToDeclarations(node, context, allDependencies, wholeTemplateUsed, pipes);
|
|
9613
10161
|
if (this.semanticDepGraphUpdater !== null) {
|
|
9614
10162
|
const getSemanticReference = (decl) => this.semanticDepGraphUpdater.getSemanticReference(decl.ref.node, decl.type);
|
|
9615
10163
|
symbol.usedDirectives = Array.from(declarations.values())
|
|
@@ -9621,7 +10169,7 @@ class ComponentDecoratorHandler {
|
|
|
9621
10169
|
}
|
|
9622
10170
|
// Process information related to defer blocks
|
|
9623
10171
|
if (this.compilationMode !== checker.CompilationMode.LOCAL) {
|
|
9624
|
-
this.resolveDeferBlocks(node, deferBlocks, declarations, data, analysis, eagerlyUsed);
|
|
10172
|
+
this.resolveDeferBlocks(node, scope, deferBlocks, declarations, data, analysis, eagerlyUsed);
|
|
9625
10173
|
}
|
|
9626
10174
|
this.handleDependencyCycles(node, context, scope, data, analysis, metadata, declarations, eagerlyUsed, symbol);
|
|
9627
10175
|
}
|
|
@@ -9691,7 +10239,7 @@ class ComponentDecoratorHandler {
|
|
|
9691
10239
|
if (perComponentDeferredDeps !== null) {
|
|
9692
10240
|
removeDeferrableTypesFromComponentDecorator(analysis, perComponentDeferredDeps);
|
|
9693
10241
|
}
|
|
9694
|
-
const def = checker.compileComponentFromMetadata(meta, pool,
|
|
10242
|
+
const def = checker.compileComponentFromMetadata(meta, pool, this.getNewBindingParser());
|
|
9695
10243
|
const inputTransformFields = compileInputTransformFields(analysis.inputs);
|
|
9696
10244
|
const classMetadata = analysis.classMetadata !== null
|
|
9697
10245
|
? compileComponentClassMetadata(analysis.classMetadata, perComponentDeferredDeps).toStmt()
|
|
@@ -9759,7 +10307,7 @@ class ComponentDecoratorHandler {
|
|
|
9759
10307
|
removeDeferrableTypesFromComponentDecorator(analysis, deferrableTypes);
|
|
9760
10308
|
}
|
|
9761
10309
|
const fac = compileNgFactoryDefField(checker.toFactoryMetadata(meta, checker.FactoryTarget.Component));
|
|
9762
|
-
const def = checker.compileComponentFromMetadata(meta, pool,
|
|
10310
|
+
const def = checker.compileComponentFromMetadata(meta, pool, this.getNewBindingParser());
|
|
9763
10311
|
const inputTransformFields = compileInputTransformFields(analysis.inputs);
|
|
9764
10312
|
const classMetadata = analysis.classMetadata !== null
|
|
9765
10313
|
? compileComponentClassMetadata(analysis.classMetadata, deferrableTypes).toStmt()
|
|
@@ -9789,7 +10337,7 @@ class ComponentDecoratorHandler {
|
|
|
9789
10337
|
defer,
|
|
9790
10338
|
};
|
|
9791
10339
|
const fac = compileNgFactoryDefField(checker.toFactoryMetadata(meta, checker.FactoryTarget.Component));
|
|
9792
|
-
const def = checker.compileComponentFromMetadata(meta, pool,
|
|
10340
|
+
const def = checker.compileComponentFromMetadata(meta, pool, this.getNewBindingParser());
|
|
9793
10341
|
const classMetadata = analysis.classMetadata !== null
|
|
9794
10342
|
? compileComponentClassMetadata(analysis.classMetadata, null).toStmt()
|
|
9795
10343
|
: null;
|
|
@@ -9829,10 +10377,30 @@ class ComponentDecoratorHandler {
|
|
|
9829
10377
|
// is an alternative implementation of template matching which is used for template
|
|
9830
10378
|
// type-checking and will eventually replace matching in the TemplateDefinitionBuilder.
|
|
9831
10379
|
const isModuleScope = scope.kind === checker.ComponentScopeKind.NgModule;
|
|
9832
|
-
|
|
9833
|
-
const
|
|
10380
|
+
const isSelectorlessScope = scope.kind === checker.ComponentScopeKind.Selectorless;
|
|
10381
|
+
const pipes = new Map();
|
|
9834
10382
|
// Dependencies from the `@Component.deferredImports` field.
|
|
9835
|
-
const explicitlyDeferredDependencies =
|
|
10383
|
+
const explicitlyDeferredDependencies = scope.kind === checker.ComponentScopeKind.Standalone ? scope.deferredDependencies : null;
|
|
10384
|
+
const dependencies = [];
|
|
10385
|
+
if (isSelectorlessScope) {
|
|
10386
|
+
for (const [localName, dep] of scope.dependencies) {
|
|
10387
|
+
// In selectorless the pipes are referred to by their local name.
|
|
10388
|
+
if (dep.kind === checker.MetaKind.Pipe) {
|
|
10389
|
+
pipes.set(localName, dep);
|
|
10390
|
+
}
|
|
10391
|
+
dependencies.push(dep);
|
|
10392
|
+
}
|
|
10393
|
+
}
|
|
10394
|
+
else {
|
|
10395
|
+
const scopeDeps = isModuleScope ? scope.compilation.dependencies : scope.dependencies;
|
|
10396
|
+
for (const dep of scopeDeps) {
|
|
10397
|
+
// Outside of selectorless the pipes are referred to by their defined name.
|
|
10398
|
+
if (dep.kind === checker.MetaKind.Pipe && dep.name !== null) {
|
|
10399
|
+
pipes.set(dep.name, dep);
|
|
10400
|
+
}
|
|
10401
|
+
dependencies.push(dep);
|
|
10402
|
+
}
|
|
10403
|
+
}
|
|
9836
10404
|
// Mark the component is an NgModule-based component with its NgModule in a different file
|
|
9837
10405
|
// then mark this file for extra import generation
|
|
9838
10406
|
if (isModuleScope && context.fileName !== checker.getSourceFile(scope.ngModule).fileName) {
|
|
@@ -9840,8 +10408,10 @@ class ComponentDecoratorHandler {
|
|
|
9840
10408
|
}
|
|
9841
10409
|
// Make sure that `@Component.imports` and `@Component.deferredImports` do not have
|
|
9842
10410
|
// the same dependencies.
|
|
9843
|
-
if (
|
|
10411
|
+
if (!isSelectorlessScope &&
|
|
10412
|
+
metadata.isStandalone &&
|
|
9844
10413
|
analysis.rawDeferredImports !== null &&
|
|
10414
|
+
explicitlyDeferredDependencies !== null &&
|
|
9845
10415
|
explicitlyDeferredDependencies.length > 0) {
|
|
9846
10416
|
const diagnostic = validateNoImportOverlap(dependencies, explicitlyDeferredDependencies, analysis.rawDeferredImports);
|
|
9847
10417
|
if (diagnostic !== null) {
|
|
@@ -9849,7 +10419,7 @@ class ComponentDecoratorHandler {
|
|
|
9849
10419
|
}
|
|
9850
10420
|
}
|
|
9851
10421
|
// Set up the R3TargetBinder.
|
|
9852
|
-
const binder =
|
|
10422
|
+
const binder = new checker.R3TargetBinder(createMatcherFromScope(scope, this.hostDirectivesResolver));
|
|
9853
10423
|
let allDependencies = dependencies;
|
|
9854
10424
|
let deferBlockBinder = binder;
|
|
9855
10425
|
// If there are any explicitly deferred dependencies (via `@Component.deferredImports`),
|
|
@@ -9858,13 +10428,19 @@ class ComponentDecoratorHandler {
|
|
|
9858
10428
|
// and need to be referenced later when determining what dependencies need to be in a
|
|
9859
10429
|
// defer function / instruction call. Otherwise they end up treated as a standard
|
|
9860
10430
|
// import, which is wrong.
|
|
9861
|
-
if (explicitlyDeferredDependencies.length > 0) {
|
|
10431
|
+
if (explicitlyDeferredDependencies !== null && explicitlyDeferredDependencies.length > 0) {
|
|
9862
10432
|
allDependencies = [...explicitlyDeferredDependencies, ...dependencies];
|
|
9863
|
-
|
|
10433
|
+
const deferBlockMatcher = new checker.SelectorMatcher();
|
|
10434
|
+
for (const dep of allDependencies) {
|
|
10435
|
+
if (dep.kind === checker.MetaKind.Pipe && dep.name !== null) {
|
|
10436
|
+
pipes.set(dep.name, dep);
|
|
10437
|
+
}
|
|
10438
|
+
else if (dep.kind === checker.MetaKind.Directive && dep.selector !== null) {
|
|
10439
|
+
deferBlockMatcher.addSelectables(checker.CssSelector.parse(dep.selector), [dep]);
|
|
10440
|
+
}
|
|
10441
|
+
}
|
|
10442
|
+
deferBlockBinder = new checker.R3TargetBinder(deferBlockMatcher);
|
|
9864
10443
|
}
|
|
9865
|
-
// Set up the pipes map that is later used to determine which dependencies are used in
|
|
9866
|
-
// the template.
|
|
9867
|
-
const pipes = extractPipes(allDependencies);
|
|
9868
10444
|
// Next, the component template AST is bound using the R3TargetBinder. This produces a
|
|
9869
10445
|
// BoundTarget, which is similar to a ts.TypeChecker.
|
|
9870
10446
|
const bound = binder.bind({ template: metadata.template.nodes });
|
|
@@ -9916,13 +10492,13 @@ class ComponentDecoratorHandler {
|
|
|
9916
10492
|
wholeTemplateUsed.add(pipes.get(name).ref.node);
|
|
9917
10493
|
}
|
|
9918
10494
|
}
|
|
9919
|
-
return { allDependencies, eagerlyUsed, wholeTemplateUsed, deferBlocks };
|
|
10495
|
+
return { allDependencies, eagerlyUsed, wholeTemplateUsed, deferBlocks, pipes };
|
|
9920
10496
|
}
|
|
9921
10497
|
/**
|
|
9922
10498
|
* Converts component dependencies into declarations by
|
|
9923
10499
|
* resolving their metadata and deduplicating them.
|
|
9924
10500
|
*/
|
|
9925
|
-
componentDependenciesToDeclarations(node, context, allDependencies, wholeTemplateUsed) {
|
|
10501
|
+
componentDependenciesToDeclarations(node, context, allDependencies, wholeTemplateUsed, pipes) {
|
|
9926
10502
|
const declarations = new Map();
|
|
9927
10503
|
// Transform the dependencies list, filtering out unused dependencies.
|
|
9928
10504
|
for (const dep of allDependencies) {
|
|
@@ -9949,20 +10525,6 @@ class ComponentDecoratorHandler {
|
|
|
9949
10525
|
isComponent: dep.isComponent,
|
|
9950
10526
|
});
|
|
9951
10527
|
break;
|
|
9952
|
-
case checker.MetaKind.Pipe:
|
|
9953
|
-
if (!wholeTemplateUsed.has(dep.ref.node)) {
|
|
9954
|
-
continue;
|
|
9955
|
-
}
|
|
9956
|
-
const pipeType = this.refEmitter.emit(dep.ref, context);
|
|
9957
|
-
checker.assertSuccessfulReferenceEmit(pipeType, node.name, 'pipe');
|
|
9958
|
-
declarations.set(dep.ref.node, {
|
|
9959
|
-
kind: checker.R3TemplateDependencyKind.Pipe,
|
|
9960
|
-
type: pipeType.expression,
|
|
9961
|
-
name: dep.name,
|
|
9962
|
-
ref: dep.ref,
|
|
9963
|
-
importedFile: pipeType.importedFile,
|
|
9964
|
-
});
|
|
9965
|
-
break;
|
|
9966
10528
|
case checker.MetaKind.NgModule:
|
|
9967
10529
|
const ngModuleType = this.refEmitter.emit(dep.ref, context);
|
|
9968
10530
|
checker.assertSuccessfulReferenceEmit(ngModuleType, node.name, 'NgModule');
|
|
@@ -9974,6 +10536,21 @@ class ComponentDecoratorHandler {
|
|
|
9974
10536
|
break;
|
|
9975
10537
|
}
|
|
9976
10538
|
}
|
|
10539
|
+
for (const [localName, dep] of pipes) {
|
|
10540
|
+
if (!wholeTemplateUsed.has(dep.ref.node)) {
|
|
10541
|
+
continue;
|
|
10542
|
+
}
|
|
10543
|
+
const pipeType = this.refEmitter.emit(dep.ref, context);
|
|
10544
|
+
checker.assertSuccessfulReferenceEmit(pipeType, node.name, 'pipe');
|
|
10545
|
+
declarations.set(dep.ref.node, {
|
|
10546
|
+
kind: checker.R3TemplateDependencyKind.Pipe,
|
|
10547
|
+
type: pipeType.expression,
|
|
10548
|
+
// Use the local name for pipes to account for selectorless.
|
|
10549
|
+
name: localName,
|
|
10550
|
+
ref: dep.ref,
|
|
10551
|
+
importedFile: pipeType.importedFile,
|
|
10552
|
+
});
|
|
10553
|
+
}
|
|
9977
10554
|
return declarations;
|
|
9978
10555
|
}
|
|
9979
10556
|
/** Handles any cycles in the dependencies of a component. */
|
|
@@ -10211,7 +10788,7 @@ class ComponentDecoratorHandler {
|
|
|
10211
10788
|
* Resolves information about defer blocks dependencies to make it
|
|
10212
10789
|
* available for the final `compile` step.
|
|
10213
10790
|
*/
|
|
10214
|
-
resolveDeferBlocks(componentClassDecl, deferBlocks, deferrableDecls, resolutionData, analysisData, eagerlyUsedDecls) {
|
|
10791
|
+
resolveDeferBlocks(componentClassDecl, scope, deferBlocks, deferrableDecls, resolutionData, analysisData, eagerlyUsedDecls) {
|
|
10215
10792
|
// Collect all deferred decls from all defer blocks from the entire template
|
|
10216
10793
|
// to intersect with the information from the `imports` field of a particular
|
|
10217
10794
|
// Component.
|
|
@@ -10252,15 +10829,27 @@ class ComponentDecoratorHandler {
|
|
|
10252
10829
|
allDeferredDecls.add(decl.ref.node);
|
|
10253
10830
|
}
|
|
10254
10831
|
}
|
|
10255
|
-
// For standalone components with the `imports` and `deferredImports` fields -
|
|
10256
|
-
// inspect the list of referenced symbols and mark the ones used in defer blocks
|
|
10257
|
-
// as potential candidates for defer loading.
|
|
10258
10832
|
if (analysisData.meta.isStandalone) {
|
|
10259
|
-
|
|
10260
|
-
|
|
10833
|
+
// For standalone components with the `imports` and `deferredImports` fields -
|
|
10834
|
+
// inspect the list of referenced symbols and mark the ones used in defer blocks
|
|
10835
|
+
// as potential candidates for defer loading.
|
|
10836
|
+
if (analysisData.rawImports !== null &&
|
|
10837
|
+
ts.isArrayLiteralExpression(analysisData.rawImports)) {
|
|
10838
|
+
for (const element of analysisData.rawImports.elements) {
|
|
10839
|
+
this.registerDeferrableCandidate(componentClassDecl, element, false /* isDeferredImport */, allDeferredDecls, eagerlyUsedDecls, resolutionData);
|
|
10840
|
+
}
|
|
10841
|
+
}
|
|
10842
|
+
if (analysisData.rawDeferredImports !== null &&
|
|
10843
|
+
ts.isArrayLiteralExpression(analysisData.rawDeferredImports)) {
|
|
10844
|
+
for (const element of analysisData.rawDeferredImports.elements) {
|
|
10845
|
+
this.registerDeferrableCandidate(componentClassDecl, element, false /* isDeferredImport */, allDeferredDecls, eagerlyUsedDecls, resolutionData);
|
|
10846
|
+
}
|
|
10261
10847
|
}
|
|
10262
|
-
|
|
10263
|
-
|
|
10848
|
+
// Selectorless references dependencies directly so we register through the identifiers.
|
|
10849
|
+
if (scope.kind === checker.ComponentScopeKind.Selectorless) {
|
|
10850
|
+
for (const identifier of scope.dependencyIdentifiers) {
|
|
10851
|
+
this.registerDeferrableCandidate(componentClassDecl, identifier, false /* isDeferredImport */, allDeferredDecls, eagerlyUsedDecls, resolutionData);
|
|
10852
|
+
}
|
|
10264
10853
|
}
|
|
10265
10854
|
}
|
|
10266
10855
|
}
|
|
@@ -10269,58 +10858,53 @@ class ComponentDecoratorHandler {
|
|
|
10269
10858
|
* `@Component.deferredImports`) and registers imported types as deferrable
|
|
10270
10859
|
* candidates.
|
|
10271
10860
|
*/
|
|
10272
|
-
|
|
10273
|
-
|
|
10861
|
+
registerDeferrableCandidate(componentClassDecl, element, isDeferredImport, allDeferredDecls, eagerlyUsedDecls, resolutionData) {
|
|
10862
|
+
const node = checker.tryUnwrapForwardRef(element, this.reflector) || element;
|
|
10863
|
+
if (!ts.isIdentifier(node)) {
|
|
10864
|
+
// Can't defer-load non-literal references.
|
|
10274
10865
|
return;
|
|
10275
10866
|
}
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10279
|
-
|
|
10280
|
-
|
|
10281
|
-
|
|
10282
|
-
|
|
10283
|
-
|
|
10284
|
-
|
|
10285
|
-
|
|
10286
|
-
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
//
|
|
10297
|
-
|
|
10298
|
-
continue;
|
|
10299
|
-
}
|
|
10300
|
-
if (eagerlyUsedDecls.has(decl.node)) {
|
|
10301
|
-
// Can't defer-load symbols that are eagerly referenced as a dependency
|
|
10302
|
-
// in a template outside of a defer block.
|
|
10303
|
-
continue;
|
|
10304
|
-
}
|
|
10305
|
-
// Is it a standalone directive/component?
|
|
10306
|
-
const dirMeta = this.metaReader.getDirectiveMetadata(new checker.Reference(decl.node));
|
|
10307
|
-
if (dirMeta !== null && !dirMeta.isStandalone) {
|
|
10308
|
-
continue;
|
|
10309
|
-
}
|
|
10310
|
-
// Is it a standalone pipe?
|
|
10311
|
-
const pipeMeta = this.metaReader.getPipeMetadata(new checker.Reference(decl.node));
|
|
10312
|
-
if (pipeMeta !== null && !pipeMeta.isStandalone) {
|
|
10313
|
-
continue;
|
|
10314
|
-
}
|
|
10315
|
-
if (dirMeta === null && pipeMeta === null) {
|
|
10316
|
-
// This is not a directive or a pipe.
|
|
10317
|
-
continue;
|
|
10318
|
-
}
|
|
10319
|
-
// Keep track of how this class made it into the current source file
|
|
10320
|
-
// (which ts.ImportDeclaration was used for this symbol).
|
|
10321
|
-
resolutionData.deferrableDeclToImportDecl.set(decl.node, imp.node);
|
|
10322
|
-
this.deferredSymbolTracker.markAsDeferrableCandidate(node, imp.node, componentClassDecl, isDeferredImport);
|
|
10867
|
+
const imp = this.reflector.getImportOfIdentifier(node);
|
|
10868
|
+
if (imp === null) {
|
|
10869
|
+
// Can't defer-load symbols which aren't imported.
|
|
10870
|
+
return;
|
|
10871
|
+
}
|
|
10872
|
+
const decl = this.reflector.getDeclarationOfIdentifier(node);
|
|
10873
|
+
if (decl === null) {
|
|
10874
|
+
// Can't defer-load symbols which don't exist.
|
|
10875
|
+
return;
|
|
10876
|
+
}
|
|
10877
|
+
if (!checker.isNamedClassDeclaration(decl.node)) {
|
|
10878
|
+
// Can't defer-load symbols which aren't classes.
|
|
10879
|
+
return;
|
|
10880
|
+
}
|
|
10881
|
+
// Are we even trying to defer-load this symbol?
|
|
10882
|
+
if (!allDeferredDecls.has(decl.node)) {
|
|
10883
|
+
return;
|
|
10884
|
+
}
|
|
10885
|
+
if (eagerlyUsedDecls.has(decl.node)) {
|
|
10886
|
+
// Can't defer-load symbols that are eagerly referenced as a dependency
|
|
10887
|
+
// in a template outside of a defer block.
|
|
10888
|
+
return;
|
|
10323
10889
|
}
|
|
10890
|
+
// Is it a standalone directive/component?
|
|
10891
|
+
const dirMeta = this.metaReader.getDirectiveMetadata(new checker.Reference(decl.node));
|
|
10892
|
+
if (dirMeta !== null && !dirMeta.isStandalone) {
|
|
10893
|
+
return;
|
|
10894
|
+
}
|
|
10895
|
+
// Is it a standalone pipe?
|
|
10896
|
+
const pipeMeta = this.metaReader.getPipeMetadata(new checker.Reference(decl.node));
|
|
10897
|
+
if (pipeMeta !== null && !pipeMeta.isStandalone) {
|
|
10898
|
+
return;
|
|
10899
|
+
}
|
|
10900
|
+
if (dirMeta === null && pipeMeta === null) {
|
|
10901
|
+
// This is not a directive or a pipe.
|
|
10902
|
+
return;
|
|
10903
|
+
}
|
|
10904
|
+
// Keep track of how this class made it into the current source file
|
|
10905
|
+
// (which ts.ImportDeclaration was used for this symbol).
|
|
10906
|
+
resolutionData.deferrableDeclToImportDecl.set(decl.node, imp.node);
|
|
10907
|
+
this.deferredSymbolTracker.markAsDeferrableCandidate(node, imp.node, componentClassDecl, isDeferredImport);
|
|
10324
10908
|
}
|
|
10325
10909
|
compileDeferBlocks(resolution) {
|
|
10326
10910
|
const { deferBlockDepsEmitMode: mode, deferPerBlockDependencies: perBlockDeps, deferPerComponentDependencies: perComponentDeps, } = resolution;
|
|
@@ -10347,36 +10931,31 @@ class ComponentDecoratorHandler {
|
|
|
10347
10931
|
}
|
|
10348
10932
|
throw new Error(`Invalid deferBlockDepsEmitMode. Cannot compile deferred block metadata.`);
|
|
10349
10933
|
}
|
|
10934
|
+
/** Creates a new binding parser. */
|
|
10935
|
+
getNewBindingParser() {
|
|
10936
|
+
return checker.makeBindingParser(undefined, this.enableSelectorless);
|
|
10937
|
+
}
|
|
10350
10938
|
}
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10939
|
+
function createMatcherFromScope(scope, hostDirectivesResolver) {
|
|
10940
|
+
if (scope.kind === checker.ComponentScopeKind.Selectorless) {
|
|
10941
|
+
const registry = new Map();
|
|
10942
|
+
for (const [name, dep] of scope.dependencies) {
|
|
10943
|
+
if (dep.kind === checker.MetaKind.Directive) {
|
|
10944
|
+
registry.set(name, [dep, ...hostDirectivesResolver.resolve(dep)]);
|
|
10945
|
+
}
|
|
10946
|
+
}
|
|
10947
|
+
return new checker.SelectorlessMatcher(registry);
|
|
10948
|
+
}
|
|
10355
10949
|
const matcher = new checker.SelectorMatcher();
|
|
10950
|
+
const dependencies = scope.kind === checker.ComponentScopeKind.NgModule
|
|
10951
|
+
? scope.compilation.dependencies
|
|
10952
|
+
: scope.dependencies;
|
|
10356
10953
|
for (const dep of dependencies) {
|
|
10357
10954
|
if (dep.kind === checker.MetaKind.Directive && dep.selector !== null) {
|
|
10358
10955
|
matcher.addSelectables(checker.CssSelector.parse(dep.selector), [dep]);
|
|
10359
10956
|
}
|
|
10360
10957
|
}
|
|
10361
|
-
|
|
10362
|
-
return new checker.R3TargetBinder(matcher);
|
|
10363
|
-
}
|
|
10364
|
-
/**
|
|
10365
|
-
* Returns the list of dependencies from `@Component.deferredImports` if provided.
|
|
10366
|
-
*/
|
|
10367
|
-
function getExplicitlyDeferredDeps(scope) {
|
|
10368
|
-
return scope.kind === checker.ComponentScopeKind.NgModule
|
|
10369
|
-
? []
|
|
10370
|
-
: scope.deferredDependencies;
|
|
10371
|
-
}
|
|
10372
|
-
function extractPipes(dependencies) {
|
|
10373
|
-
const pipes = new Map();
|
|
10374
|
-
for (const dep of dependencies) {
|
|
10375
|
-
if (dep.kind === checker.MetaKind.Pipe) {
|
|
10376
|
-
pipes.set(dep.name, dep);
|
|
10377
|
-
}
|
|
10378
|
-
}
|
|
10379
|
-
return pipes;
|
|
10958
|
+
return matcher;
|
|
10380
10959
|
}
|
|
10381
10960
|
/**
|
|
10382
10961
|
* Drop references to existing imports for deferrable symbols that should be present
|
|
@@ -10816,41 +11395,49 @@ class PipeDecoratorHandler {
|
|
|
10816
11395
|
if (decorator.args === null) {
|
|
10817
11396
|
throw new checker.FatalDiagnosticError(checker.ErrorCode.DECORATOR_NOT_CALLED, decorator.node, `@Pipe must be called`);
|
|
10818
11397
|
}
|
|
10819
|
-
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
10824
|
-
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
throw new checker.FatalDiagnosticError(checker.ErrorCode.PIPE_MISSING_NAME, meta, `@Pipe decorator is missing name field`);
|
|
10829
|
-
}
|
|
10830
|
-
const pipeNameExpr = pipe.get('name');
|
|
10831
|
-
const pipeName = this.evaluator.evaluate(pipeNameExpr);
|
|
10832
|
-
if (typeof pipeName !== 'string') {
|
|
10833
|
-
throw checker.createValueHasWrongTypeError(pipeNameExpr, pipeName, `@Pipe.name must be a string`);
|
|
10834
|
-
}
|
|
11398
|
+
const meta = decorator.args.length === 0 ||
|
|
11399
|
+
// TODO(crisbeto): temporary for testing until we've changed
|
|
11400
|
+
// the pipe public API not to require a name.
|
|
11401
|
+
(ts.isNonNullExpression(decorator.args[0]) &&
|
|
11402
|
+
decorator.args[0].expression.kind === ts.SyntaxKind.NullKeyword)
|
|
11403
|
+
? null
|
|
11404
|
+
: checker.unwrapExpression(decorator.args[0]);
|
|
11405
|
+
let pipeName = null;
|
|
11406
|
+
let pipeNameExpr = null;
|
|
10835
11407
|
let pure = true;
|
|
10836
|
-
if (pipe.has('pure')) {
|
|
10837
|
-
const expr = pipe.get('pure');
|
|
10838
|
-
const pureValue = this.evaluator.evaluate(expr);
|
|
10839
|
-
if (typeof pureValue !== 'boolean') {
|
|
10840
|
-
throw checker.createValueHasWrongTypeError(expr, pureValue, `@Pipe.pure must be a boolean`);
|
|
10841
|
-
}
|
|
10842
|
-
pure = pureValue;
|
|
10843
|
-
}
|
|
10844
11408
|
let isStandalone = this.implicitStandaloneValue;
|
|
10845
|
-
if (
|
|
10846
|
-
|
|
10847
|
-
|
|
10848
|
-
|
|
10849
|
-
|
|
11409
|
+
if (meta !== null) {
|
|
11410
|
+
if (!ts.isObjectLiteralExpression(meta)) {
|
|
11411
|
+
throw new checker.FatalDiagnosticError(checker.ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, '@Pipe must have a literal argument');
|
|
11412
|
+
}
|
|
11413
|
+
const pipe = checker.reflectObjectLiteral(meta);
|
|
11414
|
+
if (!pipe.has('name')) {
|
|
11415
|
+
throw new checker.FatalDiagnosticError(checker.ErrorCode.PIPE_MISSING_NAME, meta, `@Pipe decorator is missing name field`);
|
|
11416
|
+
}
|
|
11417
|
+
pipeNameExpr = pipe.get('name');
|
|
11418
|
+
const evaluatedName = this.evaluator.evaluate(pipeNameExpr);
|
|
11419
|
+
if (typeof evaluatedName !== 'string') {
|
|
11420
|
+
throw checker.createValueHasWrongTypeError(pipeNameExpr, evaluatedName, `@Pipe.name must be a string`);
|
|
11421
|
+
}
|
|
11422
|
+
pipeName = evaluatedName;
|
|
11423
|
+
if (pipe.has('pure')) {
|
|
11424
|
+
const expr = pipe.get('pure');
|
|
11425
|
+
const pureValue = this.evaluator.evaluate(expr);
|
|
11426
|
+
if (typeof pureValue !== 'boolean') {
|
|
11427
|
+
throw checker.createValueHasWrongTypeError(expr, pureValue, `@Pipe.pure must be a boolean`);
|
|
11428
|
+
}
|
|
11429
|
+
pure = pureValue;
|
|
10850
11430
|
}
|
|
10851
|
-
|
|
10852
|
-
|
|
10853
|
-
|
|
11431
|
+
if (pipe.has('standalone')) {
|
|
11432
|
+
const expr = pipe.get('standalone');
|
|
11433
|
+
const resolved = this.evaluator.evaluate(expr);
|
|
11434
|
+
if (typeof resolved !== 'boolean') {
|
|
11435
|
+
throw checker.createValueHasWrongTypeError(expr, resolved, `standalone flag must be a boolean`);
|
|
11436
|
+
}
|
|
11437
|
+
isStandalone = resolved;
|
|
11438
|
+
if (!isStandalone && this.strictStandalone) {
|
|
11439
|
+
throw new checker.FatalDiagnosticError(checker.ErrorCode.NON_STANDALONE_NOT_ALLOWED, expr, `Only standalone pipes are allowed when 'strictStandalone' is enabled.`);
|
|
11440
|
+
}
|
|
10854
11441
|
}
|
|
10855
11442
|
}
|
|
10856
11443
|
return {
|
|
@@ -10873,7 +11460,7 @@ class PipeDecoratorHandler {
|
|
|
10873
11460
|
};
|
|
10874
11461
|
}
|
|
10875
11462
|
symbol(node, analysis) {
|
|
10876
|
-
return new PipeSymbol(node, analysis.meta.pipeName);
|
|
11463
|
+
return new PipeSymbol(node, analysis.meta.pipeName ?? analysis.meta.name);
|
|
10877
11464
|
}
|
|
10878
11465
|
register(node, analysis) {
|
|
10879
11466
|
const ref = new checker.Reference(node);
|
|
@@ -10935,7 +11522,7 @@ class PipeDecoratorHandler {
|
|
|
10935
11522
|
* @description
|
|
10936
11523
|
* Entry point for all public APIs of the compiler-cli package.
|
|
10937
11524
|
*/
|
|
10938
|
-
new checker.Version('20.0.
|
|
11525
|
+
new checker.Version('20.1.0-next.0');
|
|
10939
11526
|
|
|
10940
11527
|
/**
|
|
10941
11528
|
* Whether a given decorator should be treated as an Angular decorator.
|
|
@@ -14000,6 +14587,8 @@ var IdentifierKind;
|
|
|
14000
14587
|
IdentifierKind[IdentifierKind["Reference"] = 5] = "Reference";
|
|
14001
14588
|
IdentifierKind[IdentifierKind["Variable"] = 6] = "Variable";
|
|
14002
14589
|
IdentifierKind[IdentifierKind["LetDeclaration"] = 7] = "LetDeclaration";
|
|
14590
|
+
IdentifierKind[IdentifierKind["Component"] = 8] = "Component";
|
|
14591
|
+
IdentifierKind[IdentifierKind["Directive"] = 9] = "Directive";
|
|
14003
14592
|
})(IdentifierKind || (IdentifierKind = {}));
|
|
14004
14593
|
/**
|
|
14005
14594
|
* Describes the absolute byte offsets of a text anchor in a source code.
|
|
@@ -14029,109 +14618,20 @@ class IndexingContext {
|
|
|
14029
14618
|
}
|
|
14030
14619
|
}
|
|
14031
14620
|
|
|
14032
|
-
/**
|
|
14033
|
-
* Visits the AST of an Angular template syntax expression, finding interesting
|
|
14034
|
-
* entities (variable references, etc.). Creates an array of Entities found in
|
|
14035
|
-
* the expression, with the location of the Entities being relative to the
|
|
14036
|
-
* expression.
|
|
14037
|
-
*
|
|
14038
|
-
* Visiting `text {{prop}}` will return
|
|
14039
|
-
* `[TopLevelIdentifier {name: 'prop', span: {start: 7, end: 11}}]`.
|
|
14040
|
-
*/
|
|
14041
|
-
class ExpressionVisitor extends checker.RecursiveAstVisitor {
|
|
14042
|
-
expressionStr;
|
|
14043
|
-
absoluteOffset;
|
|
14044
|
-
boundTemplate;
|
|
14045
|
-
targetToIdentifier;
|
|
14046
|
-
identifiers = [];
|
|
14047
|
-
errors = [];
|
|
14048
|
-
constructor(expressionStr, absoluteOffset, boundTemplate, targetToIdentifier) {
|
|
14049
|
-
super();
|
|
14050
|
-
this.expressionStr = expressionStr;
|
|
14051
|
-
this.absoluteOffset = absoluteOffset;
|
|
14052
|
-
this.boundTemplate = boundTemplate;
|
|
14053
|
-
this.targetToIdentifier = targetToIdentifier;
|
|
14054
|
-
}
|
|
14055
|
-
/**
|
|
14056
|
-
* Returns identifiers discovered in an expression.
|
|
14057
|
-
*
|
|
14058
|
-
* @param ast expression AST to visit
|
|
14059
|
-
* @param source expression AST source code
|
|
14060
|
-
* @param absoluteOffset absolute byte offset from start of the file to the start of the AST
|
|
14061
|
-
* source code.
|
|
14062
|
-
* @param boundTemplate bound target of the entire template, which can be used to query for the
|
|
14063
|
-
* entities expressions target.
|
|
14064
|
-
* @param targetToIdentifier closure converting a template target node to its identifier.
|
|
14065
|
-
*/
|
|
14066
|
-
static getIdentifiers(ast, source, absoluteOffset, boundTemplate, targetToIdentifier) {
|
|
14067
|
-
const visitor = new ExpressionVisitor(source, absoluteOffset, boundTemplate, targetToIdentifier);
|
|
14068
|
-
visitor.visit(ast);
|
|
14069
|
-
return { identifiers: visitor.identifiers, errors: visitor.errors };
|
|
14070
|
-
}
|
|
14071
|
-
visit(ast) {
|
|
14072
|
-
ast.visit(this);
|
|
14073
|
-
}
|
|
14074
|
-
visitPropertyRead(ast, context) {
|
|
14075
|
-
this.visitIdentifier(ast, IdentifierKind.Property);
|
|
14076
|
-
super.visitPropertyRead(ast, context);
|
|
14077
|
-
}
|
|
14078
|
-
visitPropertyWrite(ast, context) {
|
|
14079
|
-
this.visitIdentifier(ast, IdentifierKind.Property);
|
|
14080
|
-
super.visitPropertyWrite(ast, context);
|
|
14081
|
-
}
|
|
14082
|
-
/**
|
|
14083
|
-
* Visits an identifier, adding it to the identifier store if it is useful for indexing.
|
|
14084
|
-
*
|
|
14085
|
-
* @param ast expression AST the identifier is in
|
|
14086
|
-
* @param kind identifier kind
|
|
14087
|
-
*/
|
|
14088
|
-
visitIdentifier(ast, kind) {
|
|
14089
|
-
// The definition of a non-top-level property such as `bar` in `{{foo.bar}}` is currently
|
|
14090
|
-
// impossible to determine by an indexer and unsupported by the indexing module.
|
|
14091
|
-
// The indexing module also does not currently support references to identifiers declared in the
|
|
14092
|
-
// template itself, which have a non-null expression target.
|
|
14093
|
-
if (!(ast.receiver instanceof checker.ImplicitReceiver)) {
|
|
14094
|
-
return;
|
|
14095
|
-
}
|
|
14096
|
-
// The source span of the requested AST starts at a location that is offset from the expression.
|
|
14097
|
-
let identifierStart = ast.sourceSpan.start - this.absoluteOffset;
|
|
14098
|
-
if (ast instanceof checker.PropertyRead || ast instanceof checker.PropertyWrite) {
|
|
14099
|
-
// For `PropertyRead` and `PropertyWrite`, the identifier starts at the `nameSpan`, not
|
|
14100
|
-
// necessarily the `sourceSpan`.
|
|
14101
|
-
identifierStart = ast.nameSpan.start - this.absoluteOffset;
|
|
14102
|
-
}
|
|
14103
|
-
if (!this.expressionStr.substring(identifierStart).startsWith(ast.name)) {
|
|
14104
|
-
this.errors.push(new Error(`Impossible state: "${ast.name}" not found in "${this.expressionStr}" at location ${identifierStart}`));
|
|
14105
|
-
return;
|
|
14106
|
-
}
|
|
14107
|
-
// Join the relative position of the expression within a node with the absolute position
|
|
14108
|
-
// of the node to get the absolute position of the expression in the source code.
|
|
14109
|
-
const absoluteStart = this.absoluteOffset + identifierStart;
|
|
14110
|
-
const span = new AbsoluteSourceSpan(absoluteStart, absoluteStart + ast.name.length);
|
|
14111
|
-
const targetAst = this.boundTemplate.getExpressionTarget(ast);
|
|
14112
|
-
const target = targetAst ? this.targetToIdentifier(targetAst) : null;
|
|
14113
|
-
const identifier = {
|
|
14114
|
-
name: ast.name,
|
|
14115
|
-
span,
|
|
14116
|
-
kind,
|
|
14117
|
-
target,
|
|
14118
|
-
};
|
|
14119
|
-
this.identifiers.push(identifier);
|
|
14120
|
-
}
|
|
14121
|
-
}
|
|
14122
14621
|
/**
|
|
14123
14622
|
* Visits the AST of a parsed Angular template. Discovers and stores
|
|
14124
14623
|
* identifiers of interest, deferring to an `ExpressionVisitor` as needed.
|
|
14125
14624
|
*/
|
|
14126
|
-
let TemplateVisitor$1 = class TemplateVisitor extends checker.
|
|
14625
|
+
let TemplateVisitor$1 = class TemplateVisitor extends checker.CombinedRecursiveAstVisitor {
|
|
14127
14626
|
boundTemplate;
|
|
14128
14627
|
// Identifiers of interest found in the template.
|
|
14129
14628
|
identifiers = new Set();
|
|
14130
14629
|
errors = [];
|
|
14630
|
+
currentAstWithSource = null;
|
|
14131
14631
|
// Map of targets in a template to their identifiers.
|
|
14132
14632
|
targetIdentifierCache = new Map();
|
|
14133
14633
|
// Map of elements and templates to their identifiers.
|
|
14134
|
-
|
|
14634
|
+
directiveHostIdentifierCache = new Map();
|
|
14135
14635
|
/**
|
|
14136
14636
|
* Creates a template visitor for a bound template target. The bound target can be used when
|
|
14137
14637
|
* deferred to the expression visitor to get information about the target of an expression.
|
|
@@ -14142,139 +14642,78 @@ let TemplateVisitor$1 = class TemplateVisitor extends checker.RecursiveVisitor {
|
|
|
14142
14642
|
super();
|
|
14143
14643
|
this.boundTemplate = boundTemplate;
|
|
14144
14644
|
}
|
|
14145
|
-
/**
|
|
14146
|
-
* Visits a node in the template.
|
|
14147
|
-
*
|
|
14148
|
-
* @param node node to visit
|
|
14149
|
-
*/
|
|
14150
|
-
visit(node) {
|
|
14151
|
-
node.visit(this);
|
|
14152
|
-
}
|
|
14153
|
-
visitAll(nodes) {
|
|
14154
|
-
nodes.forEach((node) => this.visit(node));
|
|
14155
|
-
}
|
|
14156
14645
|
/**
|
|
14157
14646
|
* Add an identifier for an HTML element and visit its children recursively.
|
|
14158
14647
|
*
|
|
14159
14648
|
* @param element
|
|
14160
14649
|
*/
|
|
14161
14650
|
visitElement(element) {
|
|
14162
|
-
const elementIdentifier = this.
|
|
14651
|
+
const elementIdentifier = this.directiveHostToIdentifier(element);
|
|
14163
14652
|
if (elementIdentifier !== null) {
|
|
14164
14653
|
this.identifiers.add(elementIdentifier);
|
|
14165
14654
|
}
|
|
14166
|
-
|
|
14167
|
-
this.visitAll(element.inputs);
|
|
14168
|
-
this.visitAll(element.attributes);
|
|
14169
|
-
this.visitAll(element.directives);
|
|
14170
|
-
this.visitAll(element.children);
|
|
14171
|
-
this.visitAll(element.outputs);
|
|
14655
|
+
super.visitElement(element);
|
|
14172
14656
|
}
|
|
14173
14657
|
visitTemplate(template) {
|
|
14174
|
-
const templateIdentifier = this.
|
|
14658
|
+
const templateIdentifier = this.directiveHostToIdentifier(template);
|
|
14175
14659
|
if (templateIdentifier !== null) {
|
|
14176
14660
|
this.identifiers.add(templateIdentifier);
|
|
14177
14661
|
}
|
|
14178
|
-
|
|
14179
|
-
this.visitAll(template.variables);
|
|
14180
|
-
this.visitAll(template.attributes);
|
|
14181
|
-
this.visitAll(template.templateAttrs);
|
|
14182
|
-
this.visitAll(template.children);
|
|
14183
|
-
this.visitAll(template.references);
|
|
14184
|
-
}
|
|
14185
|
-
visitBoundAttribute(attribute) {
|
|
14186
|
-
// If the bound attribute has no value, it cannot have any identifiers in the value expression.
|
|
14187
|
-
if (attribute.valueSpan === undefined) {
|
|
14188
|
-
return;
|
|
14189
|
-
}
|
|
14190
|
-
const { identifiers, errors } = ExpressionVisitor.getIdentifiers(attribute.value, attribute.valueSpan.toString(), attribute.valueSpan.start.offset, this.boundTemplate, this.targetToIdentifier.bind(this));
|
|
14191
|
-
identifiers.forEach((id) => this.identifiers.add(id));
|
|
14192
|
-
this.errors.push(...errors);
|
|
14193
|
-
}
|
|
14194
|
-
visitBoundEvent(attribute) {
|
|
14195
|
-
this.visitExpression(attribute.handler);
|
|
14196
|
-
}
|
|
14197
|
-
visitBoundText(text) {
|
|
14198
|
-
this.visitExpression(text.value);
|
|
14662
|
+
super.visitTemplate(template);
|
|
14199
14663
|
}
|
|
14200
14664
|
visitReference(reference) {
|
|
14201
14665
|
const referenceIdentifier = this.targetToIdentifier(reference);
|
|
14202
|
-
if (referenceIdentifier
|
|
14203
|
-
|
|
14666
|
+
if (referenceIdentifier !== null) {
|
|
14667
|
+
this.identifiers.add(referenceIdentifier);
|
|
14204
14668
|
}
|
|
14205
|
-
|
|
14669
|
+
super.visitReference(reference);
|
|
14206
14670
|
}
|
|
14207
14671
|
visitVariable(variable) {
|
|
14208
14672
|
const variableIdentifier = this.targetToIdentifier(variable);
|
|
14209
|
-
if (variableIdentifier
|
|
14210
|
-
|
|
14211
|
-
}
|
|
14212
|
-
this.identifiers.add(variableIdentifier);
|
|
14213
|
-
}
|
|
14214
|
-
visitDeferredBlock(deferred) {
|
|
14215
|
-
deferred.visitAll(this);
|
|
14216
|
-
}
|
|
14217
|
-
visitDeferredBlockPlaceholder(block) {
|
|
14218
|
-
this.visitAll(block.children);
|
|
14219
|
-
}
|
|
14220
|
-
visitDeferredBlockError(block) {
|
|
14221
|
-
this.visitAll(block.children);
|
|
14222
|
-
}
|
|
14223
|
-
visitDeferredBlockLoading(block) {
|
|
14224
|
-
this.visitAll(block.children);
|
|
14225
|
-
}
|
|
14226
|
-
visitDeferredTrigger(trigger) {
|
|
14227
|
-
if (trigger instanceof checker.BoundDeferredTrigger) {
|
|
14228
|
-
this.visitExpression(trigger.value);
|
|
14673
|
+
if (variableIdentifier !== null) {
|
|
14674
|
+
this.identifiers.add(variableIdentifier);
|
|
14229
14675
|
}
|
|
14230
|
-
|
|
14231
|
-
visitSwitchBlock(block) {
|
|
14232
|
-
this.visitExpression(block.expression);
|
|
14233
|
-
this.visitAll(block.cases);
|
|
14234
|
-
}
|
|
14235
|
-
visitSwitchBlockCase(block) {
|
|
14236
|
-
block.expression && this.visitExpression(block.expression);
|
|
14237
|
-
this.visitAll(block.children);
|
|
14238
|
-
}
|
|
14239
|
-
visitForLoopBlock(block) {
|
|
14240
|
-
block.item.visit(this);
|
|
14241
|
-
this.visitAll(block.contextVariables);
|
|
14242
|
-
this.visitExpression(block.expression);
|
|
14243
|
-
this.visitAll(block.children);
|
|
14244
|
-
block.empty?.visit(this);
|
|
14245
|
-
}
|
|
14246
|
-
visitForLoopBlockEmpty(block) {
|
|
14247
|
-
this.visitAll(block.children);
|
|
14248
|
-
}
|
|
14249
|
-
visitIfBlock(block) {
|
|
14250
|
-
this.visitAll(block.branches);
|
|
14251
|
-
}
|
|
14252
|
-
visitIfBlockBranch(block) {
|
|
14253
|
-
block.expression && this.visitExpression(block.expression);
|
|
14254
|
-
block.expressionAlias?.visit(this);
|
|
14255
|
-
this.visitAll(block.children);
|
|
14676
|
+
super.visitVariable(variable);
|
|
14256
14677
|
}
|
|
14257
14678
|
visitLetDeclaration(decl) {
|
|
14258
14679
|
const identifier = this.targetToIdentifier(decl);
|
|
14259
14680
|
if (identifier !== null) {
|
|
14260
14681
|
this.identifiers.add(identifier);
|
|
14261
14682
|
}
|
|
14262
|
-
|
|
14683
|
+
super.visitLetDeclaration(decl);
|
|
14263
14684
|
}
|
|
14264
14685
|
visitComponent(component) {
|
|
14265
|
-
|
|
14686
|
+
const identifier = this.directiveHostToIdentifier(component);
|
|
14687
|
+
if (identifier !== null) {
|
|
14688
|
+
this.identifiers.add(identifier);
|
|
14689
|
+
}
|
|
14690
|
+
super.visitComponent(component);
|
|
14266
14691
|
}
|
|
14267
14692
|
visitDirective(directive) {
|
|
14268
|
-
|
|
14693
|
+
const identifier = this.directiveHostToIdentifier(directive);
|
|
14694
|
+
if (identifier !== null) {
|
|
14695
|
+
this.identifiers.add(identifier);
|
|
14696
|
+
}
|
|
14697
|
+
super.visitDirective(directive);
|
|
14698
|
+
}
|
|
14699
|
+
visitPropertyRead(ast) {
|
|
14700
|
+
this.visitIdentifier(ast, IdentifierKind.Property);
|
|
14701
|
+
super.visitPropertyRead(ast, null);
|
|
14702
|
+
}
|
|
14703
|
+
visitBoundAttribute(attribute) {
|
|
14704
|
+
const previous = this.currentAstWithSource;
|
|
14705
|
+
this.currentAstWithSource = {
|
|
14706
|
+
source: attribute.valueSpan?.toString() || null,
|
|
14707
|
+
absoluteOffset: attribute.valueSpan ? attribute.valueSpan.start.offset : -1,
|
|
14708
|
+
};
|
|
14709
|
+
this.visit(attribute.value instanceof checker.ASTWithSource ? attribute.value.ast : attribute.value);
|
|
14710
|
+
this.currentAstWithSource = previous;
|
|
14269
14711
|
}
|
|
14270
14712
|
/** Creates an identifier for a template element or template node. */
|
|
14271
|
-
|
|
14272
|
-
if (node instanceof checker.Component || node instanceof checker.Directive) {
|
|
14273
|
-
throw new Error('TODO');
|
|
14274
|
-
}
|
|
14713
|
+
directiveHostToIdentifier(node) {
|
|
14275
14714
|
// If this node has already been seen, return the cached result.
|
|
14276
|
-
if (this.
|
|
14277
|
-
return this.
|
|
14715
|
+
if (this.directiveHostIdentifierCache.has(node)) {
|
|
14716
|
+
return this.directiveHostIdentifierCache.get(node);
|
|
14278
14717
|
}
|
|
14279
14718
|
let name;
|
|
14280
14719
|
let kind;
|
|
@@ -14282,14 +14721,23 @@ let TemplateVisitor$1 = class TemplateVisitor extends checker.RecursiveVisitor {
|
|
|
14282
14721
|
name = node.tagName ?? 'ng-template';
|
|
14283
14722
|
kind = IdentifierKind.Template;
|
|
14284
14723
|
}
|
|
14285
|
-
else {
|
|
14724
|
+
else if (node instanceof checker.Element) {
|
|
14286
14725
|
name = node.name;
|
|
14287
14726
|
kind = IdentifierKind.Element;
|
|
14288
14727
|
}
|
|
14728
|
+
else if (node instanceof checker.Component) {
|
|
14729
|
+
name = node.fullName;
|
|
14730
|
+
kind = IdentifierKind.Component;
|
|
14731
|
+
}
|
|
14732
|
+
else {
|
|
14733
|
+
name = node.name;
|
|
14734
|
+
kind = IdentifierKind.Directive;
|
|
14735
|
+
}
|
|
14289
14736
|
// Namespaced elements have a particular format for `node.name` that needs to be handled.
|
|
14290
14737
|
// For example, an `<svg>` element has a `node.name` of `':svg:svg'`.
|
|
14291
14738
|
// TODO(alxhub): properly handle namespaced elements
|
|
14292
|
-
if (
|
|
14739
|
+
if ((node instanceof checker.Template || node instanceof checker.Element) &&
|
|
14740
|
+
name.startsWith(':')) {
|
|
14293
14741
|
name = name.split(':').pop();
|
|
14294
14742
|
}
|
|
14295
14743
|
const sourceSpan = node.startSourceSpan;
|
|
@@ -14324,7 +14772,7 @@ let TemplateVisitor$1 = class TemplateVisitor extends checker.RecursiveVisitor {
|
|
|
14324
14772
|
})),
|
|
14325
14773
|
// cast b/c pre-TypeScript 3.5 unions aren't well discriminated
|
|
14326
14774
|
};
|
|
14327
|
-
this.
|
|
14775
|
+
this.directiveHostIdentifierCache.set(node, identifier);
|
|
14328
14776
|
return identifier;
|
|
14329
14777
|
}
|
|
14330
14778
|
/** Creates an identifier for a template reference or template variable target. */
|
|
@@ -14353,10 +14801,10 @@ let TemplateVisitor$1 = class TemplateVisitor extends checker.RecursiveVisitor {
|
|
|
14353
14801
|
refTarget instanceof checker.Template ||
|
|
14354
14802
|
refTarget instanceof checker.Component ||
|
|
14355
14803
|
refTarget instanceof checker.Directive) {
|
|
14356
|
-
node = this.
|
|
14804
|
+
node = this.directiveHostToIdentifier(refTarget);
|
|
14357
14805
|
}
|
|
14358
14806
|
else {
|
|
14359
|
-
node = this.
|
|
14807
|
+
node = this.directiveHostToIdentifier(refTarget.node);
|
|
14360
14808
|
directive = refTarget.directive.ref.node;
|
|
14361
14809
|
}
|
|
14362
14810
|
if (node === null) {
|
|
@@ -14406,16 +14854,60 @@ let TemplateVisitor$1 = class TemplateVisitor extends checker.RecursiveVisitor {
|
|
|
14406
14854
|
*
|
|
14407
14855
|
* @param node node whose expression to visit
|
|
14408
14856
|
*/
|
|
14409
|
-
|
|
14410
|
-
|
|
14411
|
-
|
|
14412
|
-
|
|
14413
|
-
|
|
14414
|
-
|
|
14415
|
-
|
|
14416
|
-
|
|
14417
|
-
|
|
14857
|
+
visit(node) {
|
|
14858
|
+
if (node instanceof checker.ASTWithSource) {
|
|
14859
|
+
const previous = this.currentAstWithSource;
|
|
14860
|
+
this.currentAstWithSource = { source: node.source, absoluteOffset: node.sourceSpan.start };
|
|
14861
|
+
super.visit(node.ast);
|
|
14862
|
+
this.currentAstWithSource = previous;
|
|
14863
|
+
}
|
|
14864
|
+
else {
|
|
14865
|
+
super.visit(node);
|
|
14866
|
+
}
|
|
14867
|
+
}
|
|
14868
|
+
/**
|
|
14869
|
+
* Visits an identifier, adding it to the identifier store if it is useful for indexing.
|
|
14870
|
+
*
|
|
14871
|
+
* @param ast expression AST the identifier is in
|
|
14872
|
+
* @param kind identifier kind
|
|
14873
|
+
*/
|
|
14874
|
+
visitIdentifier(ast, kind) {
|
|
14875
|
+
// Only handle identifiers in expressions that have a source location.
|
|
14876
|
+
if (this.currentAstWithSource === null || this.currentAstWithSource.source === null) {
|
|
14877
|
+
return;
|
|
14878
|
+
}
|
|
14879
|
+
// The definition of a non-top-level property such as `bar` in `{{foo.bar}}` is currently
|
|
14880
|
+
// impossible to determine by an indexer and unsupported by the indexing module.
|
|
14881
|
+
// The indexing module also does not currently support references to identifiers declared in the
|
|
14882
|
+
// template itself, which have a non-null expression target.
|
|
14883
|
+
if (!(ast.receiver instanceof checker.ImplicitReceiver)) {
|
|
14884
|
+
return;
|
|
14885
|
+
}
|
|
14886
|
+
const { absoluteOffset, source: expressionStr } = this.currentAstWithSource;
|
|
14887
|
+
// The source span of the requested AST starts at a location that is offset from the expression.
|
|
14888
|
+
let identifierStart = ast.sourceSpan.start - absoluteOffset;
|
|
14889
|
+
if (ast instanceof checker.PropertyRead) {
|
|
14890
|
+
// For `PropertyRead` and the identifier starts at the `nameSpan`,
|
|
14891
|
+
// not necessarily the `sourceSpan`.
|
|
14892
|
+
identifierStart = ast.nameSpan.start - absoluteOffset;
|
|
14893
|
+
}
|
|
14894
|
+
if (!expressionStr.substring(identifierStart).startsWith(ast.name)) {
|
|
14895
|
+
this.errors.push(new Error(`Impossible state: "${ast.name}" not found in "${expressionStr}" at location ${identifierStart}`));
|
|
14896
|
+
return;
|
|
14418
14897
|
}
|
|
14898
|
+
// Join the relative position of the expression within a node with the absolute position
|
|
14899
|
+
// of the node to get the absolute position of the expression in the source code.
|
|
14900
|
+
const absoluteStart = absoluteOffset + identifierStart;
|
|
14901
|
+
const span = new AbsoluteSourceSpan(absoluteStart, absoluteStart + ast.name.length);
|
|
14902
|
+
const targetAst = this.boundTemplate.getExpressionTarget(ast);
|
|
14903
|
+
const target = targetAst ? this.targetToIdentifier(targetAst) : null;
|
|
14904
|
+
const identifier = {
|
|
14905
|
+
name: ast.name,
|
|
14906
|
+
span,
|
|
14907
|
+
kind,
|
|
14908
|
+
target,
|
|
14909
|
+
};
|
|
14910
|
+
this.identifiers.add(identifier);
|
|
14419
14911
|
}
|
|
14420
14912
|
};
|
|
14421
14913
|
/**
|
|
@@ -14427,7 +14919,7 @@ let TemplateVisitor$1 = class TemplateVisitor extends checker.RecursiveVisitor {
|
|
|
14427
14919
|
function getTemplateIdentifiers(boundTemplate) {
|
|
14428
14920
|
const visitor = new TemplateVisitor$1(boundTemplate);
|
|
14429
14921
|
if (boundTemplate.target.template !== undefined) {
|
|
14430
|
-
|
|
14922
|
+
checker.visitAll(visitor, boundTemplate.target.template);
|
|
14431
14923
|
}
|
|
14432
14924
|
return { identifiers: visitor.identifiers, errors: visitor.errors };
|
|
14433
14925
|
}
|
|
@@ -14839,7 +15331,10 @@ class StandaloneComponentScopeReader {
|
|
|
14839
15331
|
if (!this.cache.has(clazz)) {
|
|
14840
15332
|
const clazzRef = new checker.Reference(clazz);
|
|
14841
15333
|
const clazzMeta = this.metaReader.getDirectiveMetadata(clazzRef);
|
|
14842
|
-
if (clazzMeta === null ||
|
|
15334
|
+
if (clazzMeta === null ||
|
|
15335
|
+
!clazzMeta.isComponent ||
|
|
15336
|
+
!clazzMeta.isStandalone ||
|
|
15337
|
+
clazzMeta.selectorlessEnabled) {
|
|
14843
15338
|
this.cache.set(clazz, null);
|
|
14844
15339
|
return null;
|
|
14845
15340
|
}
|
|
@@ -14994,7 +15489,7 @@ class TemplateCheckWithVisitor {
|
|
|
14994
15489
|
/**
|
|
14995
15490
|
* Visits all nodes in a template (TmplAstNode and AST) and calls `visitNode` for each one.
|
|
14996
15491
|
*/
|
|
14997
|
-
class TemplateVisitor extends checker.
|
|
15492
|
+
class TemplateVisitor extends checker.CombinedRecursiveAstVisitor {
|
|
14998
15493
|
ctx;
|
|
14999
15494
|
component;
|
|
15000
15495
|
check;
|
|
@@ -15005,133 +15500,33 @@ class TemplateVisitor extends checker.RecursiveAstVisitor {
|
|
|
15005
15500
|
this.component = component;
|
|
15006
15501
|
this.check = check;
|
|
15007
15502
|
}
|
|
15008
|
-
visit(node
|
|
15503
|
+
visit(node) {
|
|
15009
15504
|
this.diagnostics.push(...this.check.visitNode(this.ctx, this.component, node));
|
|
15010
|
-
|
|
15011
|
-
}
|
|
15012
|
-
visitAllNodes(nodes) {
|
|
15013
|
-
for (const node of nodes) {
|
|
15014
|
-
this.visit(node);
|
|
15015
|
-
}
|
|
15016
|
-
}
|
|
15017
|
-
visitAst(ast) {
|
|
15018
|
-
if (ast instanceof checker.ASTWithSource) {
|
|
15019
|
-
ast = ast.ast;
|
|
15020
|
-
}
|
|
15021
|
-
this.visit(ast);
|
|
15022
|
-
}
|
|
15023
|
-
visitElement(element) {
|
|
15024
|
-
this.visitAllNodes(element.attributes);
|
|
15025
|
-
this.visitAllNodes(element.inputs);
|
|
15026
|
-
this.visitAllNodes(element.outputs);
|
|
15027
|
-
this.visitAllNodes(element.directives);
|
|
15028
|
-
this.visitAllNodes(element.references);
|
|
15029
|
-
this.visitAllNodes(element.children);
|
|
15505
|
+
super.visit(node);
|
|
15030
15506
|
}
|
|
15031
15507
|
visitTemplate(template) {
|
|
15032
15508
|
const isInlineTemplate = template.tagName === 'ng-template';
|
|
15033
|
-
this.
|
|
15509
|
+
this.visitAllTemplateNodes(template.attributes);
|
|
15034
15510
|
if (isInlineTemplate) {
|
|
15035
15511
|
// Only visit input/outputs if this isn't an inline template node generated for a structural
|
|
15036
15512
|
// directive (like `<div *ngIf></div>`). These nodes would be visited when the underlying
|
|
15037
15513
|
// element of an inline template node is processed.
|
|
15038
|
-
this.
|
|
15039
|
-
this.
|
|
15514
|
+
this.visitAllTemplateNodes(template.inputs);
|
|
15515
|
+
this.visitAllTemplateNodes(template.outputs);
|
|
15040
15516
|
}
|
|
15041
|
-
this.
|
|
15517
|
+
this.visitAllTemplateNodes(template.directives);
|
|
15042
15518
|
// TODO(crisbeto): remove this condition when deleting `canVisitStructuralAttributes`.
|
|
15043
15519
|
if (this.check.canVisitStructuralAttributes || isInlineTemplate) {
|
|
15044
15520
|
// `templateAttrs` aren't transferred over to the inner element so we always have to visit them.
|
|
15045
|
-
this.
|
|
15046
|
-
}
|
|
15047
|
-
this.visitAllNodes(template.variables);
|
|
15048
|
-
this.visitAllNodes(template.references);
|
|
15049
|
-
this.visitAllNodes(template.children);
|
|
15050
|
-
}
|
|
15051
|
-
visitContent(content) {
|
|
15052
|
-
this.visitAllNodes(content.children);
|
|
15053
|
-
}
|
|
15054
|
-
visitVariable(variable) { }
|
|
15055
|
-
visitReference(reference) { }
|
|
15056
|
-
visitTextAttribute(attribute) { }
|
|
15057
|
-
visitUnknownBlock(block) { }
|
|
15058
|
-
visitBoundAttribute(attribute) {
|
|
15059
|
-
this.visitAst(attribute.value);
|
|
15060
|
-
}
|
|
15061
|
-
visitBoundEvent(attribute) {
|
|
15062
|
-
this.visitAst(attribute.handler);
|
|
15063
|
-
}
|
|
15064
|
-
visitText(text) { }
|
|
15065
|
-
visitBoundText(text) {
|
|
15066
|
-
this.visitAst(text.value);
|
|
15067
|
-
}
|
|
15068
|
-
visitIcu(icu) {
|
|
15069
|
-
Object.keys(icu.vars).forEach((key) => this.visit(icu.vars[key]));
|
|
15070
|
-
Object.keys(icu.placeholders).forEach((key) => this.visit(icu.placeholders[key]));
|
|
15071
|
-
}
|
|
15072
|
-
visitDeferredBlock(deferred) {
|
|
15073
|
-
deferred.visitAll(this);
|
|
15074
|
-
}
|
|
15075
|
-
visitDeferredTrigger(trigger) {
|
|
15076
|
-
if (trigger instanceof checker.BoundDeferredTrigger) {
|
|
15077
|
-
this.visitAst(trigger.value);
|
|
15521
|
+
this.visitAllTemplateNodes(template.templateAttrs);
|
|
15078
15522
|
}
|
|
15079
|
-
|
|
15080
|
-
|
|
15081
|
-
this.
|
|
15082
|
-
}
|
|
15083
|
-
visitDeferredBlockError(block) {
|
|
15084
|
-
this.visitAllNodes(block.children);
|
|
15085
|
-
}
|
|
15086
|
-
visitDeferredBlockLoading(block) {
|
|
15087
|
-
this.visitAllNodes(block.children);
|
|
15088
|
-
}
|
|
15089
|
-
visitSwitchBlock(block) {
|
|
15090
|
-
this.visitAst(block.expression);
|
|
15091
|
-
this.visitAllNodes(block.cases);
|
|
15092
|
-
}
|
|
15093
|
-
visitSwitchBlockCase(block) {
|
|
15094
|
-
block.expression && this.visitAst(block.expression);
|
|
15095
|
-
this.visitAllNodes(block.children);
|
|
15096
|
-
}
|
|
15097
|
-
visitForLoopBlock(block) {
|
|
15098
|
-
block.item.visit(this);
|
|
15099
|
-
this.visitAllNodes(block.contextVariables);
|
|
15100
|
-
this.visitAst(block.expression);
|
|
15101
|
-
this.visitAllNodes(block.children);
|
|
15102
|
-
block.empty?.visit(this);
|
|
15103
|
-
}
|
|
15104
|
-
visitForLoopBlockEmpty(block) {
|
|
15105
|
-
this.visitAllNodes(block.children);
|
|
15106
|
-
}
|
|
15107
|
-
visitIfBlock(block) {
|
|
15108
|
-
this.visitAllNodes(block.branches);
|
|
15109
|
-
}
|
|
15110
|
-
visitIfBlockBranch(block) {
|
|
15111
|
-
block.expression && this.visitAst(block.expression);
|
|
15112
|
-
block.expressionAlias?.visit(this);
|
|
15113
|
-
this.visitAllNodes(block.children);
|
|
15114
|
-
}
|
|
15115
|
-
visitLetDeclaration(decl) {
|
|
15116
|
-
this.visitAst(decl.value);
|
|
15117
|
-
}
|
|
15118
|
-
visitComponent(component) {
|
|
15119
|
-
this.visitAllNodes(component.attributes);
|
|
15120
|
-
this.visitAllNodes(component.inputs);
|
|
15121
|
-
this.visitAllNodes(component.outputs);
|
|
15122
|
-
this.visitAllNodes(component.directives);
|
|
15123
|
-
this.visitAllNodes(component.references);
|
|
15124
|
-
this.visitAllNodes(component.children);
|
|
15125
|
-
}
|
|
15126
|
-
visitDirective(directive) {
|
|
15127
|
-
this.visitAllNodes(directive.attributes);
|
|
15128
|
-
this.visitAllNodes(directive.inputs);
|
|
15129
|
-
this.visitAllNodes(directive.outputs);
|
|
15130
|
-
this.visitAllNodes(directive.references);
|
|
15523
|
+
this.visitAllTemplateNodes(template.variables);
|
|
15524
|
+
this.visitAllTemplateNodes(template.references);
|
|
15525
|
+
this.visitAllTemplateNodes(template.children);
|
|
15131
15526
|
}
|
|
15132
15527
|
getDiagnostics(template) {
|
|
15133
15528
|
this.diagnostics = [];
|
|
15134
|
-
this.
|
|
15529
|
+
this.visitAllTemplateNodes(template);
|
|
15135
15530
|
return this.diagnostics;
|
|
15136
15531
|
}
|
|
15137
15532
|
}
|
|
@@ -15932,9 +16327,13 @@ class ExpressionsSemanticsVisitor extends checker.RecursiveAstVisitor {
|
|
|
15932
16327
|
this.component = component;
|
|
15933
16328
|
this.diagnostics = diagnostics;
|
|
15934
16329
|
}
|
|
15935
|
-
|
|
15936
|
-
|
|
15937
|
-
|
|
16330
|
+
visitBinary(ast, context) {
|
|
16331
|
+
if (ast.operation === '=' && ast.left instanceof checker.PropertyRead) {
|
|
16332
|
+
this.checkForIllegalWriteInEventBinding(ast.left, context);
|
|
16333
|
+
}
|
|
16334
|
+
else {
|
|
16335
|
+
super.visitBinary(ast, context);
|
|
16336
|
+
}
|
|
15938
16337
|
}
|
|
15939
16338
|
visitPropertyRead(ast, context) {
|
|
15940
16339
|
super.visitPropertyRead(ast, context);
|
|
@@ -16146,6 +16545,7 @@ class UnusedStandaloneImportsRule {
|
|
|
16146
16545
|
const pipeMeta = this.templateTypeChecker.getPipeMetadata(currentNode);
|
|
16147
16546
|
if (pipeMeta !== null &&
|
|
16148
16547
|
pipeMeta.isStandalone &&
|
|
16548
|
+
pipeMeta.name !== null &&
|
|
16149
16549
|
!usedPipes.has(pipeMeta.name) &&
|
|
16150
16550
|
!this.isPotentialSharedReference(current, rawImports)) {
|
|
16151
16551
|
unused ??= [];
|
|
@@ -18974,7 +19374,7 @@ var semver = /*@__PURE__*/getDefaultExportFromCjs(semverExports);
|
|
|
18974
19374
|
* @param minVersion Minimum required version for the feature.
|
|
18975
19375
|
*/
|
|
18976
19376
|
function coreVersionSupportsFeature(coreVersion, minVersion) {
|
|
18977
|
-
// A version of `20.0.
|
|
19377
|
+
// A version of `20.1.0-next.0` usually means that core is at head so it supports
|
|
18978
19378
|
// all features. Use string interpolation prevent the placeholder from being replaced
|
|
18979
19379
|
// with the current version during build time.
|
|
18980
19380
|
if (coreVersion === `0.0.0-${'PLACEHOLDER'}`) {
|
|
@@ -19090,6 +19490,7 @@ class NgCompiler {
|
|
|
19090
19490
|
enableHmr;
|
|
19091
19491
|
implicitStandaloneValue;
|
|
19092
19492
|
enableSelectorless;
|
|
19493
|
+
emitDeclarationOnly;
|
|
19093
19494
|
/**
|
|
19094
19495
|
* `NgCompiler` can be reused for multiple compilations (for resource-only changes), and each
|
|
19095
19496
|
* new compilation uses a fresh `PerfRecorder`. Thus, classes created with a lifespan of the
|
|
@@ -19135,6 +19536,8 @@ class NgCompiler {
|
|
|
19135
19536
|
this.enableBlockSyntax = options['_enableBlockSyntax'] ?? true;
|
|
19136
19537
|
this.enableLetSyntax = options['_enableLetSyntax'] ?? true;
|
|
19137
19538
|
this.enableSelectorless = options['_enableSelectorless'] ?? false;
|
|
19539
|
+
this.emitDeclarationOnly =
|
|
19540
|
+
!!options.emitDeclarationOnly && !!options._experimentalAllowEmitDeclarationOnly;
|
|
19138
19541
|
// Standalone by default is enabled since v19. We need to toggle it here,
|
|
19139
19542
|
// because the language service extension may be running with the latest
|
|
19140
19543
|
// version of the compiler against an older version of Angular.
|
|
@@ -19421,7 +19824,7 @@ class NgCompiler {
|
|
|
19421
19824
|
}
|
|
19422
19825
|
const defaultImportTracker = new checker.DefaultImportTracker();
|
|
19423
19826
|
const before = [
|
|
19424
|
-
ivyTransformFactory(compilation.traitCompiler, compilation.reflector, importRewriter, defaultImportTracker, compilation.localCompilationExtraImportsTracker, this.delegatingPerfRecorder, compilation.isCore, this.closureCompilerEnabled),
|
|
19827
|
+
ivyTransformFactory(compilation.traitCompiler, compilation.reflector, importRewriter, defaultImportTracker, compilation.localCompilationExtraImportsTracker, this.delegatingPerfRecorder, compilation.isCore, this.closureCompilerEnabled, this.emitDeclarationOnly),
|
|
19425
19828
|
aliasTransformFactory(compilation.traitCompiler.exportStatements),
|
|
19426
19829
|
defaultImportTracker.importPreservingTransformer(),
|
|
19427
19830
|
];
|
|
@@ -19447,11 +19850,19 @@ class NgCompiler {
|
|
|
19447
19850
|
};
|
|
19448
19851
|
});
|
|
19449
19852
|
}
|
|
19853
|
+
// Typescript transformer to add debugName metadata to signal functions.
|
|
19854
|
+
before.push(signalMetadataTransform(this.inputProgram));
|
|
19450
19855
|
const afterDeclarations = [];
|
|
19451
19856
|
// In local compilation mode we don't make use of .d.ts files for Angular compilation, so their
|
|
19452
19857
|
// transformation can be ditched.
|
|
19453
|
-
if (this.options.compilationMode !== 'experimental-local' &&
|
|
19858
|
+
if ((this.options.compilationMode !== 'experimental-local' || this.emitDeclarationOnly) &&
|
|
19454
19859
|
compilation.dtsTransforms !== null) {
|
|
19860
|
+
// If we are emitting declarations only, the script transformations are skipped by the TS
|
|
19861
|
+
// compiler, so we have to add them to the afterDeclarations transforms to run their analysis
|
|
19862
|
+
// because the declaration transform depends on their metadata output.
|
|
19863
|
+
if (this.emitDeclarationOnly) {
|
|
19864
|
+
afterDeclarations.push(...before);
|
|
19865
|
+
}
|
|
19455
19866
|
afterDeclarations.push(declarationTransformFactory(compilation.dtsTransforms, compilation.reflector, compilation.refEmitter, importRewriter));
|
|
19456
19867
|
}
|
|
19457
19868
|
// Only add aliasing re-exports to the .d.ts output if the `AliasingHost` requests it.
|
|
@@ -19789,6 +20200,9 @@ class NgCompiler {
|
|
|
19789
20200
|
break;
|
|
19790
20201
|
}
|
|
19791
20202
|
}
|
|
20203
|
+
if (this.emitDeclarationOnly) {
|
|
20204
|
+
compilationMode = checker.CompilationMode.LOCAL;
|
|
20205
|
+
}
|
|
19792
20206
|
const checker$1 = this.inputProgram.getTypeChecker();
|
|
19793
20207
|
const reflector = new checker.TypeScriptReflectionHost(checker$1, compilationMode === checker.CompilationMode.LOCAL);
|
|
19794
20208
|
// Construct the ReferenceEmitter.
|
|
@@ -19856,8 +20270,10 @@ class NgCompiler {
|
|
|
19856
20270
|
const ngModuleIndex = new NgModuleIndexImpl(metaReader, localMetaReader);
|
|
19857
20271
|
const ngModuleScopeRegistry = new LocalModuleScopeRegistry(localMetaReader, metaReader, depScopeReader, refEmitter, aliasingHost);
|
|
19858
20272
|
const standaloneScopeReader = new StandaloneComponentScopeReader(metaReader, ngModuleScopeRegistry, depScopeReader);
|
|
20273
|
+
const selectorlessScopeReader = new SelectorlessComponentScopeReader(metaReader, reflector);
|
|
19859
20274
|
const scopeReader = new CompoundComponentScopeReader([
|
|
19860
20275
|
ngModuleScopeRegistry,
|
|
20276
|
+
selectorlessScopeReader,
|
|
19861
20277
|
standaloneScopeReader,
|
|
19862
20278
|
]);
|
|
19863
20279
|
const semanticDepGraphUpdater = this.incrementalCompilation.semanticDepGraphUpdater;
|
|
@@ -19915,17 +20331,17 @@ class NgCompiler {
|
|
|
19915
20331
|
const jitDeclarationRegistry = new JitDeclarationRegistry();
|
|
19916
20332
|
// Set up the IvyCompilation, which manages state for the Ivy transformer.
|
|
19917
20333
|
const handlers = [
|
|
19918
|
-
new ComponentDecoratorHandler(reflector, evaluator, metaRegistry, metaReader, scopeReader, this.adapter, ngModuleScopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, strictCtorDeps, this.resourceManager, this.adapter.rootDirs, this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false, this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData, this.options.i18nNormalizeLineEndingsInICUs === true, this.moduleResolver, this.cycleAnalyzer, cycleHandlingStrategy, refEmitter, referencesRegistry, this.incrementalCompilation.depGraph, injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder, hostDirectivesResolver, importTracker, supportTestBed, compilationMode, deferredSymbolsTracker, !!this.options.forbidOrphanComponents, this.enableBlockSyntax, this.enableLetSyntax, externalRuntimeStyles, localCompilationExtraImportsTracker, jitDeclarationRegistry, this.options.i18nPreserveWhitespaceForLegacyExtraction ?? true, !!this.options.strictStandalone, this.enableHmr, this.implicitStandaloneValue, typeCheckHostBindings, this.enableSelectorless),
|
|
20334
|
+
new ComponentDecoratorHandler(reflector, evaluator, metaRegistry, metaReader, scopeReader, this.adapter, ngModuleScopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, strictCtorDeps, this.resourceManager, this.adapter.rootDirs, this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false, this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData, this.options.i18nNormalizeLineEndingsInICUs === true, this.moduleResolver, this.cycleAnalyzer, cycleHandlingStrategy, refEmitter, referencesRegistry, this.incrementalCompilation.depGraph, injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder, hostDirectivesResolver, importTracker, supportTestBed, compilationMode, deferredSymbolsTracker, !!this.options.forbidOrphanComponents, this.enableBlockSyntax, this.enableLetSyntax, externalRuntimeStyles, localCompilationExtraImportsTracker, jitDeclarationRegistry, this.options.i18nPreserveWhitespaceForLegacyExtraction ?? true, !!this.options.strictStandalone, this.enableHmr, this.implicitStandaloneValue, typeCheckHostBindings, this.enableSelectorless, this.emitDeclarationOnly),
|
|
19919
20335
|
// TODO(alxhub): understand why the cast here is necessary (something to do with `null`
|
|
19920
20336
|
// not being assignable to `unknown` when wrapped in `Readonly`).
|
|
19921
|
-
new DirectiveDecoratorHandler(reflector, evaluator, metaRegistry, ngModuleScopeRegistry, metaReader, injectableRegistry, refEmitter, referencesRegistry, isCore, strictCtorDeps, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder, importTracker, supportTestBed, typeCheckScopeRegistry, compilationMode, jitDeclarationRegistry, resourceRegistry, !!this.options.strictStandalone, this.implicitStandaloneValue, this.usePoisonedData, typeCheckHostBindings),
|
|
20337
|
+
new DirectiveDecoratorHandler(reflector, evaluator, metaRegistry, ngModuleScopeRegistry, metaReader, injectableRegistry, refEmitter, referencesRegistry, isCore, strictCtorDeps, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder, importTracker, supportTestBed, typeCheckScopeRegistry, compilationMode, jitDeclarationRegistry, resourceRegistry, !!this.options.strictStandalone, this.implicitStandaloneValue, this.usePoisonedData, typeCheckHostBindings, this.emitDeclarationOnly),
|
|
19922
20338
|
// Pipe handler must be before injectable handler in list so pipe factories are printed
|
|
19923
20339
|
// before injectable factories (so injectable factories can delegate to them)
|
|
19924
20340
|
new PipeDecoratorHandler(reflector, evaluator, metaRegistry, ngModuleScopeRegistry, injectableRegistry, isCore, this.delegatingPerfRecorder, supportTestBed, compilationMode, !!this.options.generateExtraImportsInLocalMode, !!this.options.strictStandalone, this.implicitStandaloneValue),
|
|
19925
20341
|
new InjectableDecoratorHandler(reflector, evaluator, isCore, strictCtorDeps, injectableRegistry, this.delegatingPerfRecorder, supportTestBed, compilationMode),
|
|
19926
|
-
new NgModuleDecoratorHandler(reflector, evaluator, metaReader, metaRegistry, ngModuleScopeRegistry, referencesRegistry, exportedProviderStatusResolver, semanticDepGraphUpdater, isCore, refEmitter, this.closureCompilerEnabled, this.options.onlyPublishPublicTypingsForNgModules ?? false, injectableRegistry, this.delegatingPerfRecorder, supportTestBed, supportJitMode, compilationMode, localCompilationExtraImportsTracker, jitDeclarationRegistry),
|
|
20342
|
+
new NgModuleDecoratorHandler(reflector, evaluator, metaReader, metaRegistry, ngModuleScopeRegistry, referencesRegistry, exportedProviderStatusResolver, semanticDepGraphUpdater, isCore, refEmitter, this.closureCompilerEnabled, this.options.onlyPublishPublicTypingsForNgModules ?? false, injectableRegistry, this.delegatingPerfRecorder, supportTestBed, supportJitMode, compilationMode, localCompilationExtraImportsTracker, jitDeclarationRegistry, this.emitDeclarationOnly),
|
|
19927
20343
|
];
|
|
19928
|
-
const traitCompiler = new TraitCompiler(handlers, reflector, this.delegatingPerfRecorder, this.incrementalCompilation, this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms, semanticDepGraphUpdater, this.adapter);
|
|
20344
|
+
const traitCompiler = new TraitCompiler(handlers, reflector, this.delegatingPerfRecorder, this.incrementalCompilation, this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms, semanticDepGraphUpdater, this.adapter, this.emitDeclarationOnly);
|
|
19929
20345
|
// Template type-checking may use the `ProgramDriver` to produce new `ts.Program`(s). If this
|
|
19930
20346
|
// happens, they need to be tracked by the `NgCompiler`.
|
|
19931
20347
|
const notifyingDriver = new NotifyingProgramDriverWrapper(this.programDriver, (program) => {
|