@angular/core 20.0.0-next.2 → 20.0.0-next.3
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/fesm2022/core.mjs +413 -365
- package/fesm2022/core.mjs.map +1 -1
- package/fesm2022/primitives/di.mjs +3 -2
- package/fesm2022/primitives/di.mjs.map +1 -1
- package/fesm2022/primitives/event-dispatch.mjs +1 -1
- package/fesm2022/primitives/signals.mjs +28 -7
- package/fesm2022/primitives/signals.mjs.map +1 -1
- package/fesm2022/rxjs-interop.mjs +1 -8
- package/fesm2022/rxjs-interop.mjs.map +1 -1
- package/fesm2022/testing.mjs +2 -10
- package/fesm2022/testing.mjs.map +1 -1
- package/fesm2022/weak_ref-DrMdAIDh.mjs +1 -1
- package/index.d.ts +7523 -7489
- package/navigation_types.d-u4EOrrdZ.d.ts +1 -1
- package/package.json +2 -2
- package/primitives/di/index.d.ts +25 -10
- package/primitives/event-dispatch/index.d.ts +1 -1
- package/primitives/signals/index.d.ts +9 -6
- package/rxjs-interop/index.d.ts +1 -10
- package/schematics/bundles/{apply_import_manager-CyRT0UvU.js → apply_import_manager-BXQEjo09.js} +6 -6
- package/schematics/bundles/{checker-DF8ZaFW5.js → checker-BHb19MHt.js} +629 -71
- package/schematics/bundles/cleanup-unused-imports.js +42 -69
- package/schematics/bundles/{compiler_host-Da636uJ8.js → compiler_host-Bk3repE2.js} +2 -2
- package/schematics/bundles/control-flow-migration.js +3 -3
- package/schematics/bundles/imports-CIX-JgAN.js +1 -1
- package/schematics/bundles/{index-DnkWgagp.js → index-BL9kAIe5.js} +11 -11
- package/schematics/bundles/{program-BZk27Ndu.js → index-I8VbxQcO.js} +2234 -2097
- package/schematics/bundles/inject-flags.js +18 -52
- package/schematics/bundles/inject-migration.js +3 -3
- package/schematics/bundles/leading_space-D9nQ8UQC.js +1 -1
- package/schematics/bundles/{migrate_ts_type_references-DtkOnnv0.js → migrate_ts_type_references-KlOTWeDl.js} +20 -20
- package/schematics/bundles/ng_decorators-DznZ5jMl.js +1 -1
- package/schematics/bundles/nodes-B16H9JUd.js +1 -1
- package/schematics/bundles/output-migration.js +62 -90
- package/schematics/bundles/project_tsconfig_paths-CDVxT6Ov.js +1 -1
- package/schematics/bundles/property_name-BBwFuqMe.js +1 -1
- package/schematics/bundles/route-lazy-loading.js +3 -3
- package/schematics/bundles/{project_paths-Jtbi76Bs.js → run_in_devkit-C0JPtK2u.js} +262 -197
- package/schematics/bundles/self-closing-tags-migration.js +41 -71
- package/schematics/bundles/signal-input-migration.js +69 -97
- package/schematics/bundles/signal-queries-migration.js +80 -108
- package/schematics/bundles/signals.js +11 -11
- package/schematics/bundles/standalone-migration.js +8 -22
- package/schematics/bundles/symbol-VPWguRxr.js +25 -0
- package/schematics/bundles/test-bed-get.js +98 -0
- package/schematics/migrations.json +5 -0
- package/testing/index.d.ts +1 -3
- package/weak_ref.d-ttyj86RV.d.ts +1 -1
- package/schematics/bundles/index-vGJcp5M7.js +0 -30
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/**
|
|
3
|
-
* @license Angular v20.0.0-next.
|
|
3
|
+
* @license Angular v20.0.0-next.3
|
|
4
4
|
* (c) 2010-2025 Google LLC. https://angular.io/
|
|
5
5
|
* License: MIT
|
|
6
6
|
*/
|
|
@@ -5350,6 +5350,28 @@ let Icu$1 = class Icu {
|
|
|
5350
5350
|
return visitor.visitIcu(this);
|
|
5351
5351
|
}
|
|
5352
5352
|
};
|
|
5353
|
+
/**
|
|
5354
|
+
* AST node that represents the host element of a directive.
|
|
5355
|
+
* This node is used only for type checking purposes and cannot be produced from a user's template.
|
|
5356
|
+
*/
|
|
5357
|
+
class HostElement {
|
|
5358
|
+
tagNames;
|
|
5359
|
+
bindings;
|
|
5360
|
+
listeners;
|
|
5361
|
+
sourceSpan;
|
|
5362
|
+
constructor(tagNames, bindings, listeners, sourceSpan) {
|
|
5363
|
+
this.tagNames = tagNames;
|
|
5364
|
+
this.bindings = bindings;
|
|
5365
|
+
this.listeners = listeners;
|
|
5366
|
+
this.sourceSpan = sourceSpan;
|
|
5367
|
+
if (tagNames.length === 0) {
|
|
5368
|
+
throw new Error('HostElement must have at least one tag name.');
|
|
5369
|
+
}
|
|
5370
|
+
}
|
|
5371
|
+
visit() {
|
|
5372
|
+
throw new Error(`HostElement cannot be visited`);
|
|
5373
|
+
}
|
|
5374
|
+
}
|
|
5353
5375
|
let RecursiveVisitor$1 = class RecursiveVisitor {
|
|
5354
5376
|
visitElement(element) {
|
|
5355
5377
|
visitAll$1(this, element.attributes);
|
|
@@ -22548,7 +22570,6 @@ function enableBindings() {
|
|
|
22548
22570
|
function listener(name, handlerFn, eventTargetResolver, syntheticHost, sourceSpan) {
|
|
22549
22571
|
const args = [literal$1(name), handlerFn];
|
|
22550
22572
|
if (eventTargetResolver !== null) {
|
|
22551
|
-
args.push(literal$1(false)); // `useCapture` flag, defaults to `false`
|
|
22552
22573
|
args.push(importExpr(eventTargetResolver));
|
|
22553
22574
|
}
|
|
22554
22575
|
return call(syntheticHost ? Identifiers.syntheticHostListener : Identifiers.listener, args, sourceSpan);
|
|
@@ -29463,7 +29484,7 @@ class R3TargetBinder {
|
|
|
29463
29484
|
* metadata about the types referenced in the template.
|
|
29464
29485
|
*/
|
|
29465
29486
|
bind(target) {
|
|
29466
|
-
if (!target.template) {
|
|
29487
|
+
if (!target.template && !target.host) {
|
|
29467
29488
|
throw new Error('Empty bound targets are not supported');
|
|
29468
29489
|
}
|
|
29469
29490
|
const directives = new Map();
|
|
@@ -29493,6 +29514,11 @@ class R3TargetBinder {
|
|
|
29493
29514
|
// template. This extracts all the metadata that doesn't depend on directive matching.
|
|
29494
29515
|
TemplateBinder.applyWithScope(target.template, scope, expressions, symbols, nestingLevel, usedPipes, eagerPipes, deferBlocks);
|
|
29495
29516
|
}
|
|
29517
|
+
// Bind the host element in a separate scope. Note that it only uses the
|
|
29518
|
+
// `TemplateBinder` since directives don't apply inside a host context.
|
|
29519
|
+
if (target.host) {
|
|
29520
|
+
TemplateBinder.applyWithScope(target.host, Scope$1.apply(target.host), expressions, symbols, nestingLevel, usedPipes, eagerPipes, deferBlocks);
|
|
29521
|
+
}
|
|
29496
29522
|
return new R3BoundTarget(target, directives, eagerDirectives, bindings, references, expressions, symbols, nestingLevel, scopedNodeEntities, usedPipes, eagerPipes, deferBlocks);
|
|
29497
29523
|
}
|
|
29498
29524
|
}
|
|
@@ -29568,7 +29594,7 @@ let Scope$1 = class Scope {
|
|
|
29568
29594
|
nodeOrNodes instanceof Content) {
|
|
29569
29595
|
nodeOrNodes.children.forEach((node) => node.visit(this));
|
|
29570
29596
|
}
|
|
29571
|
-
else {
|
|
29597
|
+
else if (!(nodeOrNodes instanceof HostElement)) {
|
|
29572
29598
|
// No overarching `Template` instance, so process the nodes directly.
|
|
29573
29599
|
nodeOrNodes.forEach((node) => node.visit(this));
|
|
29574
29600
|
}
|
|
@@ -29897,7 +29923,7 @@ class TemplateBinder extends RecursiveAstVisitor {
|
|
|
29897
29923
|
/**
|
|
29898
29924
|
* Process a template and extract metadata about expressions and symbols within.
|
|
29899
29925
|
*
|
|
29900
|
-
* @param
|
|
29926
|
+
* @param nodeOrNodes the nodes of the template to process
|
|
29901
29927
|
* @param scope the `Scope` of the template being processed.
|
|
29902
29928
|
* @returns three maps which contain metadata about the template: `expressions` which interprets
|
|
29903
29929
|
* special `AST` nodes in expressions as pointing to references or variables declared within the
|
|
@@ -29906,11 +29932,11 @@ class TemplateBinder extends RecursiveAstVisitor {
|
|
|
29906
29932
|
* nesting level (how many levels deep within the template structure the `Template` is), starting
|
|
29907
29933
|
* at 1.
|
|
29908
29934
|
*/
|
|
29909
|
-
static applyWithScope(
|
|
29910
|
-
const template =
|
|
29935
|
+
static applyWithScope(nodeOrNodes, scope, expressions, symbols, nestingLevel, usedPipes, eagerPipes, deferBlocks) {
|
|
29936
|
+
const template = nodeOrNodes instanceof Template ? nodeOrNodes : null;
|
|
29911
29937
|
// The top-level template has nesting level 0.
|
|
29912
29938
|
const binder = new TemplateBinder(expressions, symbols, usedPipes, eagerPipes, deferBlocks, nestingLevel, scope, template, 0);
|
|
29913
|
-
binder.ingest(
|
|
29939
|
+
binder.ingest(nodeOrNodes);
|
|
29914
29940
|
}
|
|
29915
29941
|
ingest(nodeOrNodes) {
|
|
29916
29942
|
if (nodeOrNodes instanceof Template) {
|
|
@@ -29952,6 +29978,10 @@ class TemplateBinder extends RecursiveAstVisitor {
|
|
|
29952
29978
|
nodeOrNodes.children.forEach((node) => node.visit(this));
|
|
29953
29979
|
this.nestingLevel.set(nodeOrNodes, this.level);
|
|
29954
29980
|
}
|
|
29981
|
+
else if (nodeOrNodes instanceof HostElement) {
|
|
29982
|
+
// Host elements are always at the top level.
|
|
29983
|
+
this.nestingLevel.set(nodeOrNodes, 0);
|
|
29984
|
+
}
|
|
29955
29985
|
else {
|
|
29956
29986
|
// Visit each node from the top-level template.
|
|
29957
29987
|
nodeOrNodes.forEach(this.visitNode);
|
|
@@ -31437,7 +31467,7 @@ var FactoryTarget;
|
|
|
31437
31467
|
* @description
|
|
31438
31468
|
* Entry point for all public APIs of the compiler package.
|
|
31439
31469
|
*/
|
|
31440
|
-
new Version('20.0.0-next.
|
|
31470
|
+
new Version('20.0.0-next.3');
|
|
31441
31471
|
|
|
31442
31472
|
//////////////////////////////////////
|
|
31443
31473
|
// THIS FILE HAS GLOBAL SIDE EFFECT //
|
|
@@ -31903,6 +31933,10 @@ exports.ErrorCode = void 0;
|
|
|
31903
31933
|
* A symbol referenced in `@Component.imports` isn't being used within the template.
|
|
31904
31934
|
*/
|
|
31905
31935
|
ErrorCode[ErrorCode["UNUSED_STANDALONE_IMPORTS"] = 8113] = "UNUSED_STANDALONE_IMPORTS";
|
|
31936
|
+
/**
|
|
31937
|
+
* An expression mixes nullish coalescing and logical and/or without parentheses.
|
|
31938
|
+
*/
|
|
31939
|
+
ErrorCode[ErrorCode["UNPARENTHESIZED_NULLISH_COALESCING"] = 8114] = "UNPARENTHESIZED_NULLISH_COALESCING";
|
|
31906
31940
|
/**
|
|
31907
31941
|
* The template type-checking engine would need to generate an inline type check block for a
|
|
31908
31942
|
* component, but the current type-checking environment doesn't support it.
|
|
@@ -32061,6 +32095,7 @@ exports.ExtendedTemplateDiagnosticName = void 0;
|
|
|
32061
32095
|
ExtendedTemplateDiagnosticName["CONTROL_FLOW_PREVENTING_CONTENT_PROJECTION"] = "controlFlowPreventingContentProjection";
|
|
32062
32096
|
ExtendedTemplateDiagnosticName["UNUSED_LET_DECLARATION"] = "unusedLetDeclaration";
|
|
32063
32097
|
ExtendedTemplateDiagnosticName["UNUSED_STANDALONE_IMPORTS"] = "unusedStandaloneImports";
|
|
32098
|
+
ExtendedTemplateDiagnosticName["UNPARENTHESIZED_NULLISH_COALESCING"] = "unparenthesizedNullishCoalescing";
|
|
32064
32099
|
})(exports.ExtendedTemplateDiagnosticName || (exports.ExtendedTemplateDiagnosticName = {}));
|
|
32065
32100
|
|
|
32066
32101
|
/**
|
|
@@ -32387,7 +32422,7 @@ class NodeJSPathManipulation {
|
|
|
32387
32422
|
// G3-ESM-MARKER: G3 uses CommonJS, but externally everything in ESM.
|
|
32388
32423
|
// CommonJS/ESM interop for determining the current file name and containing dir.
|
|
32389
32424
|
const isCommonJS = typeof __filename !== 'undefined';
|
|
32390
|
-
const currentFileUrl = isCommonJS ? null : (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('checker-
|
|
32425
|
+
const currentFileUrl = isCommonJS ? null : (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('checker-BHb19MHt.js', document.baseURI).href));
|
|
32391
32426
|
const currentFileName = isCommonJS ? __filename : url.fileURLToPath(currentFileUrl);
|
|
32392
32427
|
/**
|
|
32393
32428
|
* A wrapper around the Node.js file-system that supports readonly operations and path manipulation.
|
|
@@ -39209,7 +39244,12 @@ function extractDirectiveMetadata(clazz, decorator, reflector, importTracker, ev
|
|
|
39209
39244
|
throw new FatalDiagnosticError(exports.ErrorCode.DIRECTIVE_MISSING_SELECTOR, expr, `Directive ${clazz.name.text} has no selector, please add it!`);
|
|
39210
39245
|
}
|
|
39211
39246
|
}
|
|
39212
|
-
const
|
|
39247
|
+
const hostBindingNodes = {
|
|
39248
|
+
literal: null,
|
|
39249
|
+
bindingDecorators: new Set(),
|
|
39250
|
+
listenerDecorators: new Set(),
|
|
39251
|
+
};
|
|
39252
|
+
const host = extractHostBindings(decoratedElements, evaluator, coreModule, compilationMode, hostBindingNodes, directive);
|
|
39213
39253
|
const providers = directive.has('providers')
|
|
39214
39254
|
? new WrappedNodeExpr(annotateForClosureCompiler
|
|
39215
39255
|
? wrapFunctionExpressionsInParens(directive.get('providers'))
|
|
@@ -39321,6 +39361,7 @@ function extractDirectiveMetadata(clazz, decorator, reflector, importTracker, ev
|
|
|
39321
39361
|
isStructural,
|
|
39322
39362
|
hostDirectives,
|
|
39323
39363
|
rawHostDirectives,
|
|
39364
|
+
hostBindingNodes,
|
|
39324
39365
|
// Track inputs from class metadata. This is useful for migration efforts.
|
|
39325
39366
|
inputFieldNamesFromMetadataArray: new Set(Object.values(inputsFromMeta).map((i) => i.classPropertyName)),
|
|
39326
39367
|
};
|
|
@@ -39403,10 +39444,14 @@ function extractDecoratorQueryMetadata(exprNode, name, args, propertyName, refle
|
|
|
39403
39444
|
emitDistinctChangesOnly,
|
|
39404
39445
|
};
|
|
39405
39446
|
}
|
|
39406
|
-
function extractHostBindings(members, evaluator, coreModule, compilationMode, metadata) {
|
|
39447
|
+
function extractHostBindings(members, evaluator, coreModule, compilationMode, hostBindingNodes, metadata) {
|
|
39407
39448
|
let bindings;
|
|
39408
39449
|
if (metadata && metadata.has('host')) {
|
|
39409
|
-
|
|
39450
|
+
const hostExpression = metadata.get('host');
|
|
39451
|
+
bindings = evaluateHostExpressionBindings(hostExpression, evaluator);
|
|
39452
|
+
if (ts.isObjectLiteralExpression(hostExpression)) {
|
|
39453
|
+
hostBindingNodes.literal = hostExpression;
|
|
39454
|
+
}
|
|
39410
39455
|
}
|
|
39411
39456
|
else {
|
|
39412
39457
|
bindings = parseHostBindings({});
|
|
@@ -39430,6 +39475,9 @@ function extractHostBindings(members, evaluator, coreModule, compilationMode, me
|
|
|
39430
39475
|
}
|
|
39431
39476
|
hostPropertyName = resolved;
|
|
39432
39477
|
}
|
|
39478
|
+
if (ts.isDecorator(decorator.node)) {
|
|
39479
|
+
hostBindingNodes.bindingDecorators.add(decorator.node);
|
|
39480
|
+
}
|
|
39433
39481
|
// Since this is a decorator, we know that the value is a class member. Always access it
|
|
39434
39482
|
// through `this` so that further down the line it can't be confused for a literal value
|
|
39435
39483
|
// (e.g. if there's a property called `true`). There is no size penalty, because all
|
|
@@ -39465,6 +39513,9 @@ function extractHostBindings(members, evaluator, coreModule, compilationMode, me
|
|
|
39465
39513
|
args = resolvedArgs;
|
|
39466
39514
|
}
|
|
39467
39515
|
}
|
|
39516
|
+
if (ts.isDecorator(decorator.node)) {
|
|
39517
|
+
hostBindingNodes.listenerDecorators.add(decorator.node);
|
|
39518
|
+
}
|
|
39468
39519
|
bindings.listeners[eventName] = `${member.name}(${args.join(',')})`;
|
|
39469
39520
|
});
|
|
39470
39521
|
});
|
|
@@ -40134,6 +40185,19 @@ function toR3InputMetadata(mapping) {
|
|
|
40134
40185
|
isSignal: mapping.isSignal,
|
|
40135
40186
|
};
|
|
40136
40187
|
}
|
|
40188
|
+
function extractHostBindingResources(nodes) {
|
|
40189
|
+
const result = new Set();
|
|
40190
|
+
if (nodes.literal !== null) {
|
|
40191
|
+
result.add({ path: null, node: nodes.literal });
|
|
40192
|
+
}
|
|
40193
|
+
for (const current of nodes.bindingDecorators) {
|
|
40194
|
+
result.add({ path: null, node: current.expression });
|
|
40195
|
+
}
|
|
40196
|
+
for (const current of nodes.listenerDecorators) {
|
|
40197
|
+
result.add({ path: null, node: current.expression });
|
|
40198
|
+
}
|
|
40199
|
+
return result;
|
|
40200
|
+
}
|
|
40137
40201
|
|
|
40138
40202
|
const NgOriginalFile = Symbol('NgOriginalFile');
|
|
40139
40203
|
exports.UpdateMode = void 0;
|
|
@@ -40441,7 +40505,7 @@ function parseTemplateAsSourceFile(fileName, template) {
|
|
|
40441
40505
|
}
|
|
40442
40506
|
|
|
40443
40507
|
const TYPE_CHECK_ID_MAP = Symbol('TypeCheckId');
|
|
40444
|
-
function getTypeCheckId(clazz) {
|
|
40508
|
+
function getTypeCheckId$1(clazz) {
|
|
40445
40509
|
const sf = clazz.getSourceFile();
|
|
40446
40510
|
if (sf[TYPE_CHECK_ID_MAP] === undefined) {
|
|
40447
40511
|
sf[TYPE_CHECK_ID_MAP] = new Map();
|
|
@@ -42212,7 +42276,7 @@ class RegistryDomSchemaChecker {
|
|
|
42212
42276
|
this._diagnostics.push(diag);
|
|
42213
42277
|
}
|
|
42214
42278
|
}
|
|
42215
|
-
|
|
42279
|
+
checkTemplateElementProperty(id, element, name, span, schemas, hostIsStandalone) {
|
|
42216
42280
|
if (!REGISTRY$1.hasProperty(element.name, name, schemas)) {
|
|
42217
42281
|
const mapping = this.resolver.getTemplateSourceMapping(id);
|
|
42218
42282
|
const decorator = hostIsStandalone ? '@Component' : '@NgModule';
|
|
@@ -42235,6 +42299,18 @@ class RegistryDomSchemaChecker {
|
|
|
42235
42299
|
this._diagnostics.push(diag);
|
|
42236
42300
|
}
|
|
42237
42301
|
}
|
|
42302
|
+
checkHostElementProperty(id, element, name, span, schemas) {
|
|
42303
|
+
for (const tagName of element.tagNames) {
|
|
42304
|
+
if (REGISTRY$1.hasProperty(tagName, name, schemas)) {
|
|
42305
|
+
continue;
|
|
42306
|
+
}
|
|
42307
|
+
const errorMessage = `Can't bind to '${name}' since it isn't a known property of '${tagName}'.`;
|
|
42308
|
+
const mapping = this.resolver.getHostBindingsMapping(id);
|
|
42309
|
+
const diag = makeTemplateDiagnostic(id, mapping, span, ts.DiagnosticCategory.Error, ngErrorCode(exports.ErrorCode.SCHEMA_INVALID_ATTRIBUTE), errorMessage);
|
|
42310
|
+
this._diagnostics.push(diag);
|
|
42311
|
+
break;
|
|
42312
|
+
}
|
|
42313
|
+
}
|
|
42238
42314
|
}
|
|
42239
42315
|
|
|
42240
42316
|
/**
|
|
@@ -42346,13 +42422,26 @@ function tsCastToAny(expr) {
|
|
|
42346
42422
|
* Thanks to narrowing of `document.createElement()`, this expression will have its type inferred
|
|
42347
42423
|
* based on the tag name, including for custom elements that have appropriate .d.ts definitions.
|
|
42348
42424
|
*/
|
|
42349
|
-
function tsCreateElement(
|
|
42425
|
+
function tsCreateElement(...tagNames) {
|
|
42350
42426
|
const createElement = ts.factory.createPropertyAccessExpression(
|
|
42351
42427
|
/* expression */ ts.factory.createIdentifier('document'), 'createElement');
|
|
42428
|
+
let arg;
|
|
42429
|
+
if (tagNames.length === 1) {
|
|
42430
|
+
// If there's only one tag name, we can pass it in directly.
|
|
42431
|
+
arg = ts.factory.createStringLiteral(tagNames[0]);
|
|
42432
|
+
}
|
|
42433
|
+
else {
|
|
42434
|
+
// If there's more than one name, we have to generate a union of all the tag names. To do so,
|
|
42435
|
+
// create an expression in the form of `null! as 'tag-1' | 'tag-2' | 'tag-3'`. This allows
|
|
42436
|
+
// TypeScript to infer the type as a union of the differnet tags.
|
|
42437
|
+
const assertedNullExpression = ts.factory.createNonNullExpression(ts.factory.createNull());
|
|
42438
|
+
const type = ts.factory.createUnionTypeNode(tagNames.map((tag) => ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(tag))));
|
|
42439
|
+
arg = ts.factory.createAsExpression(assertedNullExpression, type);
|
|
42440
|
+
}
|
|
42352
42441
|
return ts.factory.createCallExpression(
|
|
42353
42442
|
/* expression */ createElement,
|
|
42354
42443
|
/* typeArguments */ undefined,
|
|
42355
|
-
/* argumentsArray */ [
|
|
42444
|
+
/* argumentsArray */ [arg]);
|
|
42356
42445
|
}
|
|
42357
42446
|
/**
|
|
42358
42447
|
* Create a `ts.VariableStatement` which declares a variable without explicit initialization.
|
|
@@ -42528,6 +42617,353 @@ class TypeParameterEmitter {
|
|
|
42528
42617
|
}
|
|
42529
42618
|
}
|
|
42530
42619
|
|
|
42620
|
+
/*!
|
|
42621
|
+
* @license
|
|
42622
|
+
* Copyright Google LLC All Rights Reserved.
|
|
42623
|
+
*
|
|
42624
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
42625
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
42626
|
+
*/
|
|
42627
|
+
/**
|
|
42628
|
+
* Comment attached to an AST node that serves as a guard to distinguish nodes
|
|
42629
|
+
* used for type checking host bindings from ones used for templates.
|
|
42630
|
+
*/
|
|
42631
|
+
const GUARD_COMMENT_TEXT = 'hostBindingsBlockGuard';
|
|
42632
|
+
/**
|
|
42633
|
+
* Creates an AST node that represents the host element of a directive.
|
|
42634
|
+
* Can return null if there are no valid bindings to be checked.
|
|
42635
|
+
* @param type Whether the host element is for a directive or a component.
|
|
42636
|
+
* @param selector Selector of the directive.
|
|
42637
|
+
* @param sourceNode Class declaration for the directive.
|
|
42638
|
+
* @param literal `host` object literal from the decorator.
|
|
42639
|
+
* @param bindingDecorators `HostBinding` decorators discovered on the node.
|
|
42640
|
+
* @param listenerDecorators `HostListener` decorators discovered on the node.
|
|
42641
|
+
*/
|
|
42642
|
+
function createHostElement(type, selector, sourceNode, literal, bindingDecorators, listenerDecorators) {
|
|
42643
|
+
const bindings = [];
|
|
42644
|
+
const listeners = [];
|
|
42645
|
+
let parser = null;
|
|
42646
|
+
if (literal !== null) {
|
|
42647
|
+
for (const prop of literal.properties) {
|
|
42648
|
+
// We only support type checking of static bindings.
|
|
42649
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
42650
|
+
ts.isStringLiteralLike(prop.initializer) &&
|
|
42651
|
+
isStaticName(prop.name)) {
|
|
42652
|
+
parser ??= makeBindingParser();
|
|
42653
|
+
createNodeFromHostLiteralProperty(prop, parser, bindings, listeners);
|
|
42654
|
+
}
|
|
42655
|
+
}
|
|
42656
|
+
}
|
|
42657
|
+
for (const decorator of bindingDecorators) {
|
|
42658
|
+
createNodeFromBindingDecorator(decorator, bindings);
|
|
42659
|
+
}
|
|
42660
|
+
for (const decorator of listenerDecorators) {
|
|
42661
|
+
parser ??= makeBindingParser();
|
|
42662
|
+
createNodeFromListenerDecorator(decorator, parser, listeners);
|
|
42663
|
+
}
|
|
42664
|
+
// The element will be a no-op if there are no bindings.
|
|
42665
|
+
if (bindings.length === 0 && listeners.length === 0) {
|
|
42666
|
+
return null;
|
|
42667
|
+
}
|
|
42668
|
+
const tagNames = [];
|
|
42669
|
+
if (selector !== null) {
|
|
42670
|
+
const parts = CssSelector.parse(selector);
|
|
42671
|
+
for (const part of parts) {
|
|
42672
|
+
if (part.element !== null) {
|
|
42673
|
+
tagNames.push(part.element);
|
|
42674
|
+
}
|
|
42675
|
+
}
|
|
42676
|
+
}
|
|
42677
|
+
// If none of the selectors have a tag name, fall back to `ng-component`/`ng-directive`.
|
|
42678
|
+
// This is how the runtime handles components without tag names as well.
|
|
42679
|
+
if (tagNames.length === 0) {
|
|
42680
|
+
tagNames.push(`ng-${type}`);
|
|
42681
|
+
}
|
|
42682
|
+
return new HostElement(tagNames, bindings, listeners, createSourceSpan(sourceNode.name));
|
|
42683
|
+
}
|
|
42684
|
+
/**
|
|
42685
|
+
* Creates an AST node that can be used as a guard in `if` statements to distinguish TypeScript
|
|
42686
|
+
* nodes used for checking host bindings from ones used for checking templates.
|
|
42687
|
+
*/
|
|
42688
|
+
function createHostBindingsBlockGuard() {
|
|
42689
|
+
// Note that the comment text is quite generic. This doesn't really matter, because it is
|
|
42690
|
+
// used only inside a TCB and there's no way for users to produce a comment there.
|
|
42691
|
+
// `true /*hostBindings*/`.
|
|
42692
|
+
const trueExpr = ts.addSyntheticTrailingComment(ts.factory.createTrue(), ts.SyntaxKind.MultiLineCommentTrivia, GUARD_COMMENT_TEXT);
|
|
42693
|
+
// Wrap the expression in parentheses to ensure that the comment is attached to the correct node.
|
|
42694
|
+
return ts.factory.createParenthesizedExpression(trueExpr);
|
|
42695
|
+
}
|
|
42696
|
+
/**
|
|
42697
|
+
* Determines if a given node is a guard that indicates that descendant nodes are used to check
|
|
42698
|
+
* host bindings.
|
|
42699
|
+
*/
|
|
42700
|
+
function isHostBindingsBlockGuard(node) {
|
|
42701
|
+
if (!ts.isIfStatement(node)) {
|
|
42702
|
+
return false;
|
|
42703
|
+
}
|
|
42704
|
+
// Needs to be kept in sync with `createHostBindingsMarker`.
|
|
42705
|
+
const expr = node.expression;
|
|
42706
|
+
if (!ts.isParenthesizedExpression(expr) || expr.expression.kind !== ts.SyntaxKind.TrueKeyword) {
|
|
42707
|
+
return false;
|
|
42708
|
+
}
|
|
42709
|
+
const text = expr.getSourceFile().text;
|
|
42710
|
+
return (ts.forEachTrailingCommentRange(text, expr.expression.getEnd(), (pos, end, kind) => kind === ts.SyntaxKind.MultiLineCommentTrivia &&
|
|
42711
|
+
text.substring(pos + 2, end - 2) === GUARD_COMMENT_TEXT) || false);
|
|
42712
|
+
}
|
|
42713
|
+
/**
|
|
42714
|
+
* If possible, creates and tracks the relevant AST node for a binding declared
|
|
42715
|
+
* through a property on the `host` literal.
|
|
42716
|
+
* @param prop Property to parse.
|
|
42717
|
+
* @param parser Binding parser used to parse the expressions.
|
|
42718
|
+
* @param bindings Array tracking the bound attributes of the host element.
|
|
42719
|
+
* @param listeners Array tracking the event listeners of the host element.
|
|
42720
|
+
*/
|
|
42721
|
+
function createNodeFromHostLiteralProperty(property, parser, bindings, listeners) {
|
|
42722
|
+
// TODO(crisbeto): surface parsing errors here, because currently they just get ignored.
|
|
42723
|
+
// They'll still get reported when the handler tries to parse the bindings, but here we
|
|
42724
|
+
// can highlight the nodes more accurately.
|
|
42725
|
+
const { name, initializer } = property;
|
|
42726
|
+
if (name.text.startsWith('[') && name.text.endsWith(']')) {
|
|
42727
|
+
const { attrName, type } = inferBoundAttribute(name.text.slice(1, -1));
|
|
42728
|
+
const valueSpan = createStaticExpressionSpan(initializer);
|
|
42729
|
+
const ast = parser.parseBinding(initializer.text, true, valueSpan, valueSpan.start.offset);
|
|
42730
|
+
if (ast.errors.length > 0) {
|
|
42731
|
+
return; // See TODO above.
|
|
42732
|
+
}
|
|
42733
|
+
fixupSpans(ast, initializer);
|
|
42734
|
+
bindings.push(new BoundAttribute(attrName, type, 0, ast, null, createSourceSpan(property), createStaticExpressionSpan(name), valueSpan, undefined));
|
|
42735
|
+
}
|
|
42736
|
+
else if (name.text.startsWith('(') && name.text.endsWith(')')) {
|
|
42737
|
+
const events = [];
|
|
42738
|
+
parser.parseEvent(name.text.slice(1, -1), initializer.text, false, createSourceSpan(property), createStaticExpressionSpan(initializer), [], events, createStaticExpressionSpan(name));
|
|
42739
|
+
if (events.length === 0 || events[0].handler.errors.length > 0) {
|
|
42740
|
+
return; // See TODO above.
|
|
42741
|
+
}
|
|
42742
|
+
fixupSpans(events[0].handler, initializer);
|
|
42743
|
+
listeners.push(BoundEvent.fromParsedEvent(events[0]));
|
|
42744
|
+
}
|
|
42745
|
+
}
|
|
42746
|
+
/**
|
|
42747
|
+
* If possible, creates and tracks a bound attribute node from a `HostBinding` decorator.
|
|
42748
|
+
* @param decorator Decorator from which to create the node.
|
|
42749
|
+
* @param bindings Array tracking the bound attributes of the host element.
|
|
42750
|
+
*/
|
|
42751
|
+
function createNodeFromBindingDecorator(decorator, bindings) {
|
|
42752
|
+
// We only support decorators that are being called.
|
|
42753
|
+
if (!ts.isCallExpression(decorator.expression)) {
|
|
42754
|
+
return;
|
|
42755
|
+
}
|
|
42756
|
+
const args = decorator.expression.arguments;
|
|
42757
|
+
const property = decorator.parent;
|
|
42758
|
+
let nameNode = null;
|
|
42759
|
+
let propertyName = null;
|
|
42760
|
+
if (property && ts.isPropertyDeclaration(property) && isStaticName(property.name)) {
|
|
42761
|
+
propertyName = property.name;
|
|
42762
|
+
}
|
|
42763
|
+
// The first parameter is optional. If omitted, the name
|
|
42764
|
+
// of the class member is used as the property.
|
|
42765
|
+
if (args.length === 0) {
|
|
42766
|
+
nameNode = propertyName;
|
|
42767
|
+
}
|
|
42768
|
+
else if (ts.isStringLiteralLike(args[0])) {
|
|
42769
|
+
nameNode = args[0];
|
|
42770
|
+
}
|
|
42771
|
+
else {
|
|
42772
|
+
return;
|
|
42773
|
+
}
|
|
42774
|
+
if (nameNode === null || propertyName === null) {
|
|
42775
|
+
return;
|
|
42776
|
+
}
|
|
42777
|
+
// We can't synthesize a fake expression here and pass it through the binding parser, because
|
|
42778
|
+
// it constructs all the spans based on the source code origin and they aren't easily mappable
|
|
42779
|
+
// back to the source. E.g. `@HostBinding('foo') id = '123'` in source code would look
|
|
42780
|
+
// something like `[foo]="this.id"` in the AST. Instead we construct the expressions
|
|
42781
|
+
// manually here. Note that we use a dummy span with -1/-1 as offsets, because it isn't
|
|
42782
|
+
// used for type checking and constructing it accurately would take some effort.
|
|
42783
|
+
const span = new ParseSpan(-1, -1);
|
|
42784
|
+
const propertyStart = property.getStart();
|
|
42785
|
+
const receiver = new ThisReceiver(span, new AbsoluteSourceSpan(propertyStart, propertyStart));
|
|
42786
|
+
const nameSpan = new AbsoluteSourceSpan(propertyName.getStart(), propertyName.getEnd());
|
|
42787
|
+
const read = ts.isIdentifier(propertyName)
|
|
42788
|
+
? new PropertyRead(span, nameSpan, nameSpan, receiver, propertyName.text)
|
|
42789
|
+
: new KeyedRead(span, nameSpan, receiver, new LiteralPrimitive(span, nameSpan, propertyName.text));
|
|
42790
|
+
const { attrName, type } = inferBoundAttribute(nameNode.text);
|
|
42791
|
+
bindings.push(new BoundAttribute(attrName, type, 0, read, null, createSourceSpan(decorator), createStaticExpressionSpan(nameNode), createSourceSpan(decorator), undefined));
|
|
42792
|
+
}
|
|
42793
|
+
/**
|
|
42794
|
+
* If possible, creates and tracks a bound event node from a `HostBinding` decorator.
|
|
42795
|
+
* @param decorator Decorator from which to create the node.
|
|
42796
|
+
* @param parser Binding parser used to parse the expressions.
|
|
42797
|
+
* @param bindings Array tracking the bound events of the host element.
|
|
42798
|
+
*/
|
|
42799
|
+
function createNodeFromListenerDecorator(decorator, parser, listeners) {
|
|
42800
|
+
// We only support decorators that are being called with at least one argument.
|
|
42801
|
+
if (!ts.isCallExpression(decorator.expression) || decorator.expression.arguments.length === 0) {
|
|
42802
|
+
return;
|
|
42803
|
+
}
|
|
42804
|
+
const args = decorator.expression.arguments;
|
|
42805
|
+
const method = decorator.parent;
|
|
42806
|
+
// Only handle decorators that are statically analyzable.
|
|
42807
|
+
if (!method ||
|
|
42808
|
+
!ts.isMethodDeclaration(method) ||
|
|
42809
|
+
!isStaticName(method.name) ||
|
|
42810
|
+
!ts.isStringLiteralLike(args[0])) {
|
|
42811
|
+
return;
|
|
42812
|
+
}
|
|
42813
|
+
// We can't synthesize a fake expression here and pass it through the binding parser, because
|
|
42814
|
+
// it constructs all the spans based on the source code origin and they aren't easily mappable
|
|
42815
|
+
// back to the source. E.g. `@HostListener('foo') handleFoo() {}` in source code would look
|
|
42816
|
+
// something like `(foo)="handleFoo()"` in the AST. Instead we construct the expressions
|
|
42817
|
+
// manually here. Note that we use a dummy span with -1/-1 as offsets, because it isn't
|
|
42818
|
+
// used for type checking and constructing it accurately would take some effort.
|
|
42819
|
+
const span = new ParseSpan(-1, -1);
|
|
42820
|
+
const argNodes = [];
|
|
42821
|
+
const methodStart = method.getStart();
|
|
42822
|
+
const methodReceiver = new ThisReceiver(span, new AbsoluteSourceSpan(methodStart, methodStart));
|
|
42823
|
+
const nameSpan = new AbsoluteSourceSpan(method.name.getStart(), method.name.getEnd());
|
|
42824
|
+
const receiver = ts.isIdentifier(method.name)
|
|
42825
|
+
? new PropertyRead(span, nameSpan, nameSpan, methodReceiver, method.name.text)
|
|
42826
|
+
: new KeyedRead(span, nameSpan, methodReceiver, new LiteralPrimitive(span, nameSpan, method.name.text));
|
|
42827
|
+
if (args.length > 1 && ts.isArrayLiteralExpression(args[1])) {
|
|
42828
|
+
for (const expr of args[1].elements) {
|
|
42829
|
+
// If the parameter is a static string, parse it using the binding parser since it can be any
|
|
42830
|
+
// expression, otherwise treat it as `any` so the rest of the parameters can be checked.
|
|
42831
|
+
if (ts.isStringLiteralLike(expr)) {
|
|
42832
|
+
const span = createStaticExpressionSpan(expr);
|
|
42833
|
+
const ast = parser.parseBinding(expr.text, true, span, span.start.offset);
|
|
42834
|
+
fixupSpans(ast, expr);
|
|
42835
|
+
argNodes.push(ast);
|
|
42836
|
+
}
|
|
42837
|
+
else {
|
|
42838
|
+
// Represents `$any(0)`. We need to construct it manually in order to set the right spans.
|
|
42839
|
+
const expressionSpan = new AbsoluteSourceSpan(expr.getStart(), expr.getEnd());
|
|
42840
|
+
const anyRead = new PropertyRead(span, expressionSpan, expressionSpan, new ImplicitReceiver(span, expressionSpan), '$any');
|
|
42841
|
+
const anyCall = new Call(span, expressionSpan, anyRead, [new LiteralPrimitive(span, expressionSpan, 0)], expressionSpan);
|
|
42842
|
+
argNodes.push(anyCall);
|
|
42843
|
+
}
|
|
42844
|
+
}
|
|
42845
|
+
}
|
|
42846
|
+
const callNode = new Call(span, nameSpan, receiver, argNodes, span);
|
|
42847
|
+
const eventNameNode = args[0];
|
|
42848
|
+
const [eventName, phase] = eventNameNode.text.split('.');
|
|
42849
|
+
listeners.push(new BoundEvent(eventName, eventName.startsWith('@') ? exports.ParsedEventType.Animation : exports.ParsedEventType.Regular, callNode, null, phase, createSourceSpan(decorator), createSourceSpan(decorator), createStaticExpressionSpan(eventNameNode)));
|
|
42850
|
+
}
|
|
42851
|
+
/**
|
|
42852
|
+
* Infers the attribute name and binding type of a bound attribute based on its raw name.
|
|
42853
|
+
* @param name Name from which to infer the information.
|
|
42854
|
+
*/
|
|
42855
|
+
function inferBoundAttribute(name) {
|
|
42856
|
+
const attrPrefix = 'attr.';
|
|
42857
|
+
const classPrefix = 'class.';
|
|
42858
|
+
const stylePrefix = 'style.';
|
|
42859
|
+
const animationPrefix = '@';
|
|
42860
|
+
let attrName;
|
|
42861
|
+
let type;
|
|
42862
|
+
// Infer the binding type based on the prefix.
|
|
42863
|
+
if (name.startsWith(attrPrefix)) {
|
|
42864
|
+
attrName = name.slice(attrPrefix.length);
|
|
42865
|
+
type = exports.BindingType.Attribute;
|
|
42866
|
+
}
|
|
42867
|
+
else if (name.startsWith(classPrefix)) {
|
|
42868
|
+
attrName = name.slice(classPrefix.length);
|
|
42869
|
+
type = exports.BindingType.Class;
|
|
42870
|
+
}
|
|
42871
|
+
else if (name.startsWith(stylePrefix)) {
|
|
42872
|
+
attrName = name.slice(stylePrefix.length);
|
|
42873
|
+
type = exports.BindingType.Style;
|
|
42874
|
+
}
|
|
42875
|
+
else if (name.startsWith(animationPrefix)) {
|
|
42876
|
+
attrName = name.slice(animationPrefix.length);
|
|
42877
|
+
type = exports.BindingType.Animation;
|
|
42878
|
+
}
|
|
42879
|
+
else {
|
|
42880
|
+
attrName = name;
|
|
42881
|
+
type = exports.BindingType.Property;
|
|
42882
|
+
}
|
|
42883
|
+
return { attrName, type };
|
|
42884
|
+
}
|
|
42885
|
+
/** Checks whether the specified node is a static name node. */
|
|
42886
|
+
function isStaticName(node) {
|
|
42887
|
+
return ts.isIdentifier(node) || ts.isStringLiteralLike(node);
|
|
42888
|
+
}
|
|
42889
|
+
/** Creates a `ParseSourceSpan` pointing to a static expression AST node's source. */
|
|
42890
|
+
function createStaticExpressionSpan(node) {
|
|
42891
|
+
const span = createSourceSpan(node);
|
|
42892
|
+
// Offset by one on both sides to skip over the quotes.
|
|
42893
|
+
if (ts.isStringLiteralLike(node)) {
|
|
42894
|
+
span.fullStart = span.fullStart.moveBy(1);
|
|
42895
|
+
span.start = span.start.moveBy(1);
|
|
42896
|
+
span.end = span.end.moveBy(-1);
|
|
42897
|
+
}
|
|
42898
|
+
return span;
|
|
42899
|
+
}
|
|
42900
|
+
/**
|
|
42901
|
+
* Adjusts the spans of a parsed AST so that they're appropriate for a host bindings context.
|
|
42902
|
+
* @param ast The parsed AST that may need to be adjusted.
|
|
42903
|
+
* @param initializer TypeScript node from which the source of the AST was extracted.
|
|
42904
|
+
*/
|
|
42905
|
+
function fixupSpans(ast, initializer) {
|
|
42906
|
+
// When parsing the initializer as a property/event binding, we use `.text` which excludes escaped
|
|
42907
|
+
// quotes and is generally what we want, because preserving them would result in a parser error,
|
|
42908
|
+
// however it has the downside in that the spans of the expressions following the escaped
|
|
42909
|
+
// characters are no longer accurate relative to the source code. The more escaped characters
|
|
42910
|
+
// there are before a node, the more inaccurate its span will be. If we detect cases like that,
|
|
42911
|
+
// we override the spans of all nodes following the escaped string to point to the entire
|
|
42912
|
+
// initializer string so that we don't surface diagnostics with mangled spans. This isn't ideal,
|
|
42913
|
+
// but is likely rare in user code. Some workarounds that were attempted and ultimately discarded:
|
|
42914
|
+
// 1. Counting the number of escaped strings before a node and adjusting its span accordingly -
|
|
42915
|
+
// There's a prototype of this approach in https://github.com/crisbeto/angular/commit/1eb92353784a609f6be7e6653ae5a9faef549e6a
|
|
42916
|
+
// It works for the most part, but is complex and somewhat brittle, because it's not just the
|
|
42917
|
+
// escaped literals that need to be updated, but also any nodes _after_ them and any nodes
|
|
42918
|
+
// _containing_ them which gets increasingly complex with more complicated ASTs.
|
|
42919
|
+
// 2. Replacing escape characters with whitespaces, for example `\'foo\' + 123` would become
|
|
42920
|
+
// ` 'foo ' + 123` - this appears to produce accurate ASTs for some simpler use cases, but has
|
|
42921
|
+
// the potential of either changing the user's code into something that's no longer parseable or
|
|
42922
|
+
// causing type checking errors (e.g. the typings might require the exact string 'foo').
|
|
42923
|
+
// 3. Passing the raw text (e.g. `initializer.getText().slice(1, -1)`) into the binding parser -
|
|
42924
|
+
// This will preserve the right mappings, but can lead to parsing errors, because some of the
|
|
42925
|
+
// strings won't have to be escaped anymore. We could add a mode to the parser that allows it to
|
|
42926
|
+
// recover from such cases, but it'll introduce more complexity that we may not want to take on.
|
|
42927
|
+
// 4. Constructing some sort of string like `<host ${name.getText()}=${initializer.getText()}/>`,
|
|
42928
|
+
// passing it through the HTML parser and extracting the first attribute from it - wasn't explored
|
|
42929
|
+
// much, but likely has the same issues as approach #3.
|
|
42930
|
+
const escapeIndex = initializer.getText().indexOf('\\', 1);
|
|
42931
|
+
if (escapeIndex > -1) {
|
|
42932
|
+
const newSpan = new ParseSpan(0, initializer.getWidth());
|
|
42933
|
+
const newSourceSpan = new AbsoluteSourceSpan(initializer.getStart(), initializer.getEnd());
|
|
42934
|
+
ast.visit(new ReplaceSpanVisitor(escapeIndex, newSpan, newSourceSpan));
|
|
42935
|
+
}
|
|
42936
|
+
}
|
|
42937
|
+
/**
|
|
42938
|
+
* Visitor that replaces the spans of all nodes with new ones,
|
|
42939
|
+
* if they're defined after a specific index.
|
|
42940
|
+
*/
|
|
42941
|
+
class ReplaceSpanVisitor extends RecursiveAstVisitor {
|
|
42942
|
+
afterIndex;
|
|
42943
|
+
overrideSpan;
|
|
42944
|
+
overrideSourceSpan;
|
|
42945
|
+
constructor(afterIndex, overrideSpan, overrideSourceSpan) {
|
|
42946
|
+
super();
|
|
42947
|
+
this.afterIndex = afterIndex;
|
|
42948
|
+
this.overrideSpan = overrideSpan;
|
|
42949
|
+
this.overrideSourceSpan = overrideSourceSpan;
|
|
42950
|
+
}
|
|
42951
|
+
visit(ast) {
|
|
42952
|
+
// Only nodes after the index need to be adjusted, but all nodes should be visited.
|
|
42953
|
+
if (ast.span.start >= this.afterIndex || ast.span.end >= this.afterIndex) {
|
|
42954
|
+
ast.span = this.overrideSpan;
|
|
42955
|
+
ast.sourceSpan = this.overrideSourceSpan;
|
|
42956
|
+
if (ast instanceof ASTWithName) {
|
|
42957
|
+
ast.nameSpan = this.overrideSourceSpan;
|
|
42958
|
+
}
|
|
42959
|
+
if (ast instanceof Call || ast instanceof SafeCall) {
|
|
42960
|
+
ast.argumentSpan = this.overrideSourceSpan;
|
|
42961
|
+
}
|
|
42962
|
+
}
|
|
42963
|
+
super.visit(ast);
|
|
42964
|
+
}
|
|
42965
|
+
}
|
|
42966
|
+
|
|
42531
42967
|
/**
|
|
42532
42968
|
* External modules/identifiers that always should exist for type check
|
|
42533
42969
|
* block files.
|
|
@@ -42596,18 +43032,39 @@ function getSourceMapping(shimSf, position, resolver, isDiagnosticRequest) {
|
|
|
42596
43032
|
if (sourceLocation === null) {
|
|
42597
43033
|
return null;
|
|
42598
43034
|
}
|
|
42599
|
-
|
|
43035
|
+
if (isInHostBindingTcb(node)) {
|
|
43036
|
+
const hostSourceMapping = resolver.getHostBindingsMapping(sourceLocation.id);
|
|
43037
|
+
const span = resolver.toHostParseSourceSpan(sourceLocation.id, sourceLocation.span);
|
|
43038
|
+
if (span === null) {
|
|
43039
|
+
return null;
|
|
43040
|
+
}
|
|
43041
|
+
return { sourceLocation, sourceMapping: hostSourceMapping, span };
|
|
43042
|
+
}
|
|
42600
43043
|
const span = resolver.toTemplateParseSourceSpan(sourceLocation.id, sourceLocation.span);
|
|
42601
43044
|
if (span === null) {
|
|
42602
43045
|
return null;
|
|
42603
43046
|
}
|
|
42604
43047
|
// TODO(atscott): Consider adding a context span by walking up from `node` until we get a
|
|
42605
43048
|
// different span.
|
|
42606
|
-
return {
|
|
43049
|
+
return {
|
|
43050
|
+
sourceLocation,
|
|
43051
|
+
sourceMapping: resolver.getTemplateSourceMapping(sourceLocation.id),
|
|
43052
|
+
span,
|
|
43053
|
+
};
|
|
43054
|
+
}
|
|
43055
|
+
function isInHostBindingTcb(node) {
|
|
43056
|
+
let current = node;
|
|
43057
|
+
while (current && !ts.isFunctionDeclaration(current)) {
|
|
43058
|
+
if (isHostBindingsBlockGuard(current)) {
|
|
43059
|
+
return true;
|
|
43060
|
+
}
|
|
43061
|
+
current = current.parent;
|
|
43062
|
+
}
|
|
43063
|
+
return false;
|
|
42607
43064
|
}
|
|
42608
43065
|
function findTypeCheckBlock(file, id, isDiagnosticRequest) {
|
|
42609
43066
|
for (const stmt of file.statements) {
|
|
42610
|
-
if (ts.isFunctionDeclaration(stmt) &&
|
|
43067
|
+
if (ts.isFunctionDeclaration(stmt) && getTypeCheckId(stmt, file, isDiagnosticRequest) === id) {
|
|
42611
43068
|
return stmt;
|
|
42612
43069
|
}
|
|
42613
43070
|
}
|
|
@@ -42630,7 +43087,7 @@ function findSourceLocation(node, sourceFile, isDiagnosticsRequest) {
|
|
|
42630
43087
|
if (span !== null) {
|
|
42631
43088
|
// Once the positional information has been extracted, search further up the TCB to extract
|
|
42632
43089
|
// the unique id that is attached with the TCB's function declaration.
|
|
42633
|
-
const id =
|
|
43090
|
+
const id = getTypeCheckId(node, sourceFile, isDiagnosticsRequest);
|
|
42634
43091
|
if (id === null) {
|
|
42635
43092
|
return null;
|
|
42636
43093
|
}
|
|
@@ -42640,7 +43097,7 @@ function findSourceLocation(node, sourceFile, isDiagnosticsRequest) {
|
|
|
42640
43097
|
}
|
|
42641
43098
|
return null;
|
|
42642
43099
|
}
|
|
42643
|
-
function
|
|
43100
|
+
function getTypeCheckId(node, sourceFile, isDiagnosticRequest) {
|
|
42644
43101
|
// Walk up to the function declaration of the TCB, the file information is attached there.
|
|
42645
43102
|
while (!ts.isFunctionDeclaration(node)) {
|
|
42646
43103
|
if (hasIgnoreForDiagnosticsMarker(node, sourceFile) && isDiagnosticRequest) {
|
|
@@ -43896,7 +44353,6 @@ var TcbGenericContextBehavior;
|
|
|
43896
44353
|
*/
|
|
43897
44354
|
function generateTypeCheckBlock(env, ref, name, meta, domSchemaChecker, oobRecorder, genericContextBehavior) {
|
|
43898
44355
|
const tcb = new Context(env, domSchemaChecker, oobRecorder, meta.id, meta.boundTarget, meta.pipes, meta.schemas, meta.isStandalone, meta.preserveWhitespaces);
|
|
43899
|
-
const scope = Scope.forNodes(tcb, null, null, tcb.boundTarget.target.template, /* guard */ null);
|
|
43900
44356
|
const ctxRawType = env.referenceType(ref);
|
|
43901
44357
|
if (!ts.isTypeReferenceNode(ctxRawType)) {
|
|
43902
44358
|
throw new Error(`Expected TypeReferenceNode when referencing the ctx param for ${ref.debugName}`);
|
|
@@ -43923,13 +44379,19 @@ function generateTypeCheckBlock(env, ref, name, meta, domSchemaChecker, oobRecor
|
|
|
43923
44379
|
}
|
|
43924
44380
|
}
|
|
43925
44381
|
const paramList = [tcbThisParam(ctxRawType.typeName, typeArguments)];
|
|
43926
|
-
const
|
|
43927
|
-
|
|
43928
|
-
|
|
43929
|
-
|
|
43930
|
-
|
|
43931
|
-
|
|
43932
|
-
|
|
44382
|
+
const statements = [];
|
|
44383
|
+
// Add the template type checking code.
|
|
44384
|
+
if (tcb.boundTarget.target.template !== undefined) {
|
|
44385
|
+
const templateScope = Scope.forNodes(tcb, null, null, tcb.boundTarget.target.template,
|
|
44386
|
+
/* guard */ null);
|
|
44387
|
+
statements.push(renderBlockStatements(env, templateScope, ts.factory.createTrue()));
|
|
44388
|
+
}
|
|
44389
|
+
// Add the host bindings type checking code.
|
|
44390
|
+
if (tcb.boundTarget.target.host !== undefined) {
|
|
44391
|
+
const hostScope = Scope.forNodes(tcb, null, tcb.boundTarget.target.host, null, null);
|
|
44392
|
+
statements.push(renderBlockStatements(env, hostScope, createHostBindingsBlockGuard()));
|
|
44393
|
+
}
|
|
44394
|
+
const body = ts.factory.createBlock(statements);
|
|
43933
44395
|
const fnDecl = ts.factory.createFunctionDeclaration(
|
|
43934
44396
|
/* modifiers */ undefined,
|
|
43935
44397
|
/* asteriskToken */ undefined,
|
|
@@ -43941,6 +44403,14 @@ function generateTypeCheckBlock(env, ref, name, meta, domSchemaChecker, oobRecor
|
|
|
43941
44403
|
addTypeCheckId(fnDecl, meta.id);
|
|
43942
44404
|
return fnDecl;
|
|
43943
44405
|
}
|
|
44406
|
+
function renderBlockStatements(env, scope, wrapperExpression) {
|
|
44407
|
+
const scopeStatements = scope.render();
|
|
44408
|
+
const innerBody = ts.factory.createBlock([...env.getPreludeStatements(), ...scopeStatements]);
|
|
44409
|
+
// Wrap the body in an if statement. This serves two purposes:
|
|
44410
|
+
// 1. It allows us to distinguish between the sections of the block (e.g. host or template).
|
|
44411
|
+
// 2. It allows the `ts.Printer` to produce better-looking output.
|
|
44412
|
+
return ts.factory.createIfStatement(wrapperExpression, innerBody);
|
|
44413
|
+
}
|
|
43944
44414
|
/**
|
|
43945
44415
|
* A code generation operation that's involved in the construction of a Type Check Block.
|
|
43946
44416
|
*
|
|
@@ -44693,20 +45163,28 @@ class TcbDomSchemaCheckerOp extends TcbOp {
|
|
|
44693
45163
|
return false;
|
|
44694
45164
|
}
|
|
44695
45165
|
execute() {
|
|
44696
|
-
|
|
44697
|
-
|
|
45166
|
+
const element = this.element;
|
|
45167
|
+
const isTemplateElement = element instanceof Element$1;
|
|
45168
|
+
const bindings = isTemplateElement ? element.inputs : element.bindings;
|
|
45169
|
+
if (this.checkElement && isTemplateElement) {
|
|
45170
|
+
this.tcb.domSchemaChecker.checkElement(this.tcb.id, element, this.tcb.schemas, this.tcb.hostIsStandalone);
|
|
44698
45171
|
}
|
|
44699
45172
|
// TODO(alxhub): this could be more efficient.
|
|
44700
|
-
for (const binding of
|
|
45173
|
+
for (const binding of bindings) {
|
|
44701
45174
|
const isPropertyBinding = binding.type === exports.BindingType.Property || binding.type === exports.BindingType.TwoWay;
|
|
44702
|
-
if (isPropertyBinding && this.claimedInputs
|
|
45175
|
+
if (isPropertyBinding && this.claimedInputs?.has(binding.name)) {
|
|
44703
45176
|
// Skip this binding as it was claimed by a directive.
|
|
44704
45177
|
continue;
|
|
44705
45178
|
}
|
|
44706
45179
|
if (isPropertyBinding && binding.name !== 'style' && binding.name !== 'class') {
|
|
44707
45180
|
// A direct binding to a property.
|
|
44708
45181
|
const propertyName = ATTR_TO_PROP.get(binding.name) ?? binding.name;
|
|
44709
|
-
|
|
45182
|
+
if (isTemplateElement) {
|
|
45183
|
+
this.tcb.domSchemaChecker.checkTemplateElementProperty(this.tcb.id, element, propertyName, binding.sourceSpan, this.tcb.schemas, this.tcb.hostIsStandalone);
|
|
45184
|
+
}
|
|
45185
|
+
else {
|
|
45186
|
+
this.tcb.domSchemaChecker.checkHostElementProperty(this.tcb.id, element, propertyName, binding.keySpan, this.tcb.schemas);
|
|
45187
|
+
}
|
|
44710
45188
|
}
|
|
44711
45189
|
}
|
|
44712
45190
|
return null;
|
|
@@ -44821,6 +45299,30 @@ class TcbControlFlowContentProjectionOp extends TcbOp {
|
|
|
44821
45299
|
return false;
|
|
44822
45300
|
}
|
|
44823
45301
|
}
|
|
45302
|
+
/**
|
|
45303
|
+
* A `TcbOp` which creates an expression for a the host element of a directive.
|
|
45304
|
+
*
|
|
45305
|
+
* Executing this operation returns a reference to the element variable.
|
|
45306
|
+
*/
|
|
45307
|
+
class TcbHostElementOp extends TcbOp {
|
|
45308
|
+
tcb;
|
|
45309
|
+
scope;
|
|
45310
|
+
element;
|
|
45311
|
+
optional = true;
|
|
45312
|
+
constructor(tcb, scope, element) {
|
|
45313
|
+
super();
|
|
45314
|
+
this.tcb = tcb;
|
|
45315
|
+
this.scope = scope;
|
|
45316
|
+
this.element = element;
|
|
45317
|
+
}
|
|
45318
|
+
execute() {
|
|
45319
|
+
const id = this.tcb.allocateId();
|
|
45320
|
+
const initializer = tsCreateElement(...this.element.tagNames);
|
|
45321
|
+
addParseSpanInfo(initializer, this.element.sourceSpan);
|
|
45322
|
+
this.scope.addStatement(tsCreateVariable(id, initializer));
|
|
45323
|
+
return id;
|
|
45324
|
+
}
|
|
45325
|
+
}
|
|
44824
45326
|
/**
|
|
44825
45327
|
* Mapping between attributes names that don't correspond to their element property names.
|
|
44826
45328
|
* Note: this mapping has to be kept in sync with the equally named mapping in the runtime.
|
|
@@ -44846,13 +45348,15 @@ const ATTR_TO_PROP = new Map(Object.entries({
|
|
|
44846
45348
|
class TcbUnclaimedInputsOp extends TcbOp {
|
|
44847
45349
|
tcb;
|
|
44848
45350
|
scope;
|
|
44849
|
-
|
|
45351
|
+
inputs;
|
|
45352
|
+
target;
|
|
44850
45353
|
claimedInputs;
|
|
44851
|
-
constructor(tcb, scope,
|
|
45354
|
+
constructor(tcb, scope, inputs, target, claimedInputs) {
|
|
44852
45355
|
super();
|
|
44853
45356
|
this.tcb = tcb;
|
|
44854
45357
|
this.scope = scope;
|
|
44855
|
-
this.
|
|
45358
|
+
this.inputs = inputs;
|
|
45359
|
+
this.target = target;
|
|
44856
45360
|
this.claimedInputs = claimedInputs;
|
|
44857
45361
|
}
|
|
44858
45362
|
get optional() {
|
|
@@ -44863,9 +45367,9 @@ class TcbUnclaimedInputsOp extends TcbOp {
|
|
|
44863
45367
|
// the element itself.
|
|
44864
45368
|
let elId = null;
|
|
44865
45369
|
// TODO(alxhub): this could be more efficient.
|
|
44866
|
-
for (const binding of this.
|
|
45370
|
+
for (const binding of this.inputs) {
|
|
44867
45371
|
const isPropertyBinding = binding.type === exports.BindingType.Property || binding.type === exports.BindingType.TwoWay;
|
|
44868
|
-
if (isPropertyBinding && this.claimedInputs
|
|
45372
|
+
if (isPropertyBinding && this.claimedInputs?.has(binding.name)) {
|
|
44869
45373
|
// Skip this binding as it was claimed by a directive.
|
|
44870
45374
|
continue;
|
|
44871
45375
|
}
|
|
@@ -44873,7 +45377,7 @@ class TcbUnclaimedInputsOp extends TcbOp {
|
|
|
44873
45377
|
if (this.tcb.env.config.checkTypeOfDomBindings && isPropertyBinding) {
|
|
44874
45378
|
if (binding.name !== 'style' && binding.name !== 'class') {
|
|
44875
45379
|
if (elId === null) {
|
|
44876
|
-
elId = this.scope.resolve(this.
|
|
45380
|
+
elId = this.scope.resolve(this.target);
|
|
44877
45381
|
}
|
|
44878
45382
|
// A direct binding to a property.
|
|
44879
45383
|
const propertyName = ATTR_TO_PROP.get(binding.name) ?? binding.name;
|
|
@@ -44973,13 +45477,17 @@ class TcbDirectiveOutputsOp extends TcbOp {
|
|
|
44973
45477
|
class TcbUnclaimedOutputsOp extends TcbOp {
|
|
44974
45478
|
tcb;
|
|
44975
45479
|
scope;
|
|
44976
|
-
|
|
45480
|
+
target;
|
|
45481
|
+
outputs;
|
|
45482
|
+
inputs;
|
|
44977
45483
|
claimedOutputs;
|
|
44978
|
-
constructor(tcb, scope,
|
|
45484
|
+
constructor(tcb, scope, target, outputs, inputs, claimedOutputs) {
|
|
44979
45485
|
super();
|
|
44980
45486
|
this.tcb = tcb;
|
|
44981
45487
|
this.scope = scope;
|
|
44982
|
-
this.
|
|
45488
|
+
this.target = target;
|
|
45489
|
+
this.outputs = outputs;
|
|
45490
|
+
this.inputs = inputs;
|
|
44983
45491
|
this.claimedOutputs = claimedOutputs;
|
|
44984
45492
|
}
|
|
44985
45493
|
get optional() {
|
|
@@ -44988,14 +45496,16 @@ class TcbUnclaimedOutputsOp extends TcbOp {
|
|
|
44988
45496
|
execute() {
|
|
44989
45497
|
let elId = null;
|
|
44990
45498
|
// TODO(alxhub): this could be more efficient.
|
|
44991
|
-
for (const output of this.
|
|
44992
|
-
if (this.claimedOutputs
|
|
45499
|
+
for (const output of this.outputs) {
|
|
45500
|
+
if (this.claimedOutputs?.has(output.name)) {
|
|
44993
45501
|
// Skip this event handler as it was claimed by a directive.
|
|
44994
45502
|
continue;
|
|
44995
45503
|
}
|
|
44996
|
-
if (this.tcb.env.config.checkTypeOfOutputEvents &&
|
|
45504
|
+
if (this.tcb.env.config.checkTypeOfOutputEvents &&
|
|
45505
|
+
this.inputs !== null &&
|
|
45506
|
+
output.name.endsWith('Change')) {
|
|
44997
45507
|
const inputName = output.name.slice(0, -6);
|
|
44998
|
-
if (checkSplitTwoWayBinding(inputName, output, this.
|
|
45508
|
+
if (checkSplitTwoWayBinding(inputName, output, this.inputs, this.tcb)) {
|
|
44999
45509
|
// Skip this event handler as the error was already handled.
|
|
45000
45510
|
continue;
|
|
45001
45511
|
}
|
|
@@ -45016,7 +45526,7 @@ class TcbUnclaimedOutputsOp extends TcbOp {
|
|
|
45016
45526
|
// base `Event` type.
|
|
45017
45527
|
const handler = tcbCreateEventHandler(output, this.tcb, this.scope, 0 /* EventParamType.Infer */);
|
|
45018
45528
|
if (elId === null) {
|
|
45019
|
-
elId = this.scope.resolve(this.
|
|
45529
|
+
elId = this.scope.resolve(this.target);
|
|
45020
45530
|
}
|
|
45021
45531
|
const propertyAccess = ts.factory.createPropertyAccessExpression(elId, 'addEventListener');
|
|
45022
45532
|
addParseSpanInfo(propertyAccess, output.keySpan);
|
|
@@ -45406,6 +45916,10 @@ class Scope {
|
|
|
45406
45916
|
* A map of `TmplAstElement`s to the index of their `TcbElementOp` in the `opQueue`
|
|
45407
45917
|
*/
|
|
45408
45918
|
elementOpMap = new Map();
|
|
45919
|
+
/**
|
|
45920
|
+
* A map of `TmplAstHostElement`s to the index of their `TcbHostElementOp` in the `opQueue`
|
|
45921
|
+
*/
|
|
45922
|
+
hostElementOpMap = new Map();
|
|
45409
45923
|
/**
|
|
45410
45924
|
* A map of maps which tracks the index of `TcbDirectiveCtorOp`s in the `opQueue` for each
|
|
45411
45925
|
* directive on a `TmplAstElement` or `TmplAstTemplate` node.
|
|
@@ -45508,8 +46022,13 @@ class Scope {
|
|
|
45508
46022
|
this.registerVariable(scope, variable, new TcbBlockImplicitVariableOp(tcb, scope, type, variable));
|
|
45509
46023
|
}
|
|
45510
46024
|
}
|
|
45511
|
-
|
|
45512
|
-
scope.appendNode(
|
|
46025
|
+
else if (scopedNode instanceof HostElement) {
|
|
46026
|
+
scope.appendNode(scopedNode);
|
|
46027
|
+
}
|
|
46028
|
+
if (children !== null) {
|
|
46029
|
+
for (const node of children) {
|
|
46030
|
+
scope.appendNode(node);
|
|
46031
|
+
}
|
|
45513
46032
|
}
|
|
45514
46033
|
// Once everything is registered, we need to check if there are `@let`
|
|
45515
46034
|
// declarations that conflict with other local symbols defined after them.
|
|
@@ -45669,6 +46188,9 @@ class Scope {
|
|
|
45669
46188
|
// Resolving the DOM node of an element in this template.
|
|
45670
46189
|
return this.resolveOp(this.elementOpMap.get(ref));
|
|
45671
46190
|
}
|
|
46191
|
+
else if (ref instanceof HostElement && this.hostElementOpMap.has(ref)) {
|
|
46192
|
+
return this.resolveOp(this.hostElementOpMap.get(ref));
|
|
46193
|
+
}
|
|
45672
46194
|
else {
|
|
45673
46195
|
return null;
|
|
45674
46196
|
}
|
|
@@ -45764,6 +46286,11 @@ class Scope {
|
|
|
45764
46286
|
this.letDeclOpMap.set(node.name, { opIndex, node });
|
|
45765
46287
|
}
|
|
45766
46288
|
}
|
|
46289
|
+
else if (node instanceof HostElement) {
|
|
46290
|
+
const opIndex = this.opQueue.push(new TcbHostElementOp(this.tcb, this, node)) - 1;
|
|
46291
|
+
this.hostElementOpMap.set(node, opIndex);
|
|
46292
|
+
this.opQueue.push(new TcbUnclaimedInputsOp(this.tcb, this, node.bindings, node, null), new TcbUnclaimedOutputsOp(this.tcb, this, node, node.listeners, null, null), new TcbDomSchemaCheckerOp(this.tcb, node, false, null));
|
|
46293
|
+
}
|
|
45767
46294
|
}
|
|
45768
46295
|
appendChildren(node) {
|
|
45769
46296
|
for (const child of node.children) {
|
|
@@ -45798,8 +46325,7 @@ class Scope {
|
|
|
45798
46325
|
// If there are no directives, then all inputs are unclaimed inputs, so queue an operation
|
|
45799
46326
|
// to add them if needed.
|
|
45800
46327
|
if (node instanceof Element$1) {
|
|
45801
|
-
this.opQueue.push(new TcbUnclaimedInputsOp(this.tcb, this, node, claimedInputs));
|
|
45802
|
-
this.opQueue.push(new TcbDomSchemaCheckerOp(this.tcb, node, /* checkElement */ true, claimedInputs));
|
|
46328
|
+
this.opQueue.push(new TcbUnclaimedInputsOp(this.tcb, this, node.inputs, node, claimedInputs), new TcbDomSchemaCheckerOp(this.tcb, node, /* checkElement */ true, claimedInputs));
|
|
45803
46329
|
}
|
|
45804
46330
|
return;
|
|
45805
46331
|
}
|
|
@@ -45850,7 +46376,7 @@ class Scope {
|
|
|
45850
46376
|
claimedInputs.add(propertyName);
|
|
45851
46377
|
}
|
|
45852
46378
|
}
|
|
45853
|
-
this.opQueue.push(new TcbUnclaimedInputsOp(this.tcb, this, node, claimedInputs));
|
|
46379
|
+
this.opQueue.push(new TcbUnclaimedInputsOp(this.tcb, this, node.inputs, node, claimedInputs));
|
|
45854
46380
|
// If there are no directives which match this element, then it's a "plain" DOM element (or a
|
|
45855
46381
|
// web component), and should be checked against the DOM schema. If any directives match,
|
|
45856
46382
|
// we must assume that the element could be custom (either a component, or a directive like
|
|
@@ -45867,7 +46393,7 @@ class Scope {
|
|
|
45867
46393
|
// If there are no directives, then all outputs are unclaimed outputs, so queue an operation
|
|
45868
46394
|
// to add them if needed.
|
|
45869
46395
|
if (node instanceof Element$1) {
|
|
45870
|
-
this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, claimedOutputs));
|
|
46396
|
+
this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, node.outputs, node.inputs, claimedOutputs));
|
|
45871
46397
|
}
|
|
45872
46398
|
return;
|
|
45873
46399
|
}
|
|
@@ -45884,7 +46410,7 @@ class Scope {
|
|
|
45884
46410
|
claimedOutputs.add(outputProperty);
|
|
45885
46411
|
}
|
|
45886
46412
|
}
|
|
45887
|
-
this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, claimedOutputs));
|
|
46413
|
+
this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, node.outputs, node.inputs, claimedOutputs));
|
|
45888
46414
|
}
|
|
45889
46415
|
}
|
|
45890
46416
|
appendDeepSchemaChecks(nodes) {
|
|
@@ -46535,18 +47061,22 @@ class TypeCheckContextImpl {
|
|
|
46535
47061
|
*
|
|
46536
47062
|
* Implements `TypeCheckContext.addTemplate`.
|
|
46537
47063
|
*/
|
|
46538
|
-
addDirective(ref, binder, schemas, templateContext, isStandalone) {
|
|
47064
|
+
addDirective(ref, binder, schemas, templateContext, hostBindingContext, isStandalone) {
|
|
46539
47065
|
if (!this.host.shouldCheckClass(ref.node)) {
|
|
46540
47066
|
return;
|
|
46541
47067
|
}
|
|
46542
|
-
const
|
|
47068
|
+
const sourceFile = ref.node.getSourceFile();
|
|
47069
|
+
const fileData = this.dataForFile(sourceFile);
|
|
46543
47070
|
const shimData = this.pendingShimForClass(ref.node);
|
|
46544
47071
|
const id = fileData.sourceManager.getTypeCheckId(ref.node);
|
|
46545
47072
|
const templateParsingDiagnostics = [];
|
|
46546
47073
|
if (templateContext !== null && templateContext.parseErrors !== null) {
|
|
46547
47074
|
templateParsingDiagnostics.push(...getTemplateDiagnostics(templateContext.parseErrors, id, templateContext.sourceMapping));
|
|
46548
47075
|
}
|
|
46549
|
-
const boundTarget = binder.bind({
|
|
47076
|
+
const boundTarget = binder.bind({
|
|
47077
|
+
template: templateContext?.nodes,
|
|
47078
|
+
host: hostBindingContext?.node,
|
|
47079
|
+
});
|
|
46550
47080
|
if (this.inlining === InliningMode.InlineOps) {
|
|
46551
47081
|
// Get all of the directives used in the template and record inline type constructors when
|
|
46552
47082
|
// required.
|
|
@@ -46576,6 +47106,7 @@ class TypeCheckContextImpl {
|
|
|
46576
47106
|
template: templateContext?.nodes || null,
|
|
46577
47107
|
boundTarget,
|
|
46578
47108
|
templateParsingDiagnostics,
|
|
47109
|
+
hostElement: hostBindingContext?.node ?? null,
|
|
46579
47110
|
});
|
|
46580
47111
|
const usedPipes = [];
|
|
46581
47112
|
if (templateContext !== null) {
|
|
@@ -46601,6 +47132,12 @@ class TypeCheckContextImpl {
|
|
|
46601
47132
|
if (templateContext !== null) {
|
|
46602
47133
|
fileData.sourceManager.captureTemplateSource(id, templateContext.sourceMapping, templateContext.file);
|
|
46603
47134
|
}
|
|
47135
|
+
if (hostBindingContext !== null) {
|
|
47136
|
+
fileData.sourceManager.captureHostBindingsMapping(id, hostBindingContext.sourceMapping,
|
|
47137
|
+
// We only support host bindings in the same file as the directive
|
|
47138
|
+
// so we can get the source file from here.
|
|
47139
|
+
new ParseSourceFile(sourceFile.text, sourceFile.fileName));
|
|
47140
|
+
}
|
|
46604
47141
|
const meta = {
|
|
46605
47142
|
id,
|
|
46606
47143
|
boundTarget,
|
|
@@ -46904,10 +47441,10 @@ function findClosestLineStartPosition(linesMap, position, low = 0, high = linesM
|
|
|
46904
47441
|
}
|
|
46905
47442
|
|
|
46906
47443
|
/**
|
|
46907
|
-
* Represents the source of
|
|
46908
|
-
*
|
|
47444
|
+
* Represents the source of code processed during type-checking. This information is used when
|
|
47445
|
+
* translating parse offsets in diagnostics back to their original line/column location.
|
|
46909
47446
|
*/
|
|
46910
|
-
class
|
|
47447
|
+
class Source {
|
|
46911
47448
|
mapping;
|
|
46912
47449
|
file;
|
|
46913
47450
|
lineStarts = null;
|
|
@@ -46944,11 +47481,16 @@ class DirectiveSourceManager {
|
|
|
46944
47481
|
* diagnostics produced for TCB code to their source location in the template.
|
|
46945
47482
|
*/
|
|
46946
47483
|
templateSources = new Map();
|
|
47484
|
+
/** Keeps track of type check IDs and the source location of their host bindings. */
|
|
47485
|
+
hostBindingSources = new Map();
|
|
46947
47486
|
getTypeCheckId(node) {
|
|
46948
|
-
return getTypeCheckId(node);
|
|
47487
|
+
return getTypeCheckId$1(node);
|
|
46949
47488
|
}
|
|
46950
47489
|
captureTemplateSource(id, mapping, file) {
|
|
46951
|
-
this.templateSources.set(id, new
|
|
47490
|
+
this.templateSources.set(id, new Source(mapping, file));
|
|
47491
|
+
}
|
|
47492
|
+
captureHostBindingsMapping(id, mapping, file) {
|
|
47493
|
+
this.hostBindingSources.set(id, new Source(mapping, file));
|
|
46952
47494
|
}
|
|
46953
47495
|
getTemplateSourceMapping(id) {
|
|
46954
47496
|
if (!this.templateSources.has(id)) {
|
|
@@ -46956,6 +47498,12 @@ class DirectiveSourceManager {
|
|
|
46956
47498
|
}
|
|
46957
47499
|
return this.templateSources.get(id).mapping;
|
|
46958
47500
|
}
|
|
47501
|
+
getHostBindingsMapping(id) {
|
|
47502
|
+
if (!this.hostBindingSources.has(id)) {
|
|
47503
|
+
throw new Error(`Unexpected unknown type check ID: ${id}`);
|
|
47504
|
+
}
|
|
47505
|
+
return this.hostBindingSources.get(id).mapping;
|
|
47506
|
+
}
|
|
46959
47507
|
toTemplateParseSourceSpan(id, span) {
|
|
46960
47508
|
if (!this.templateSources.has(id)) {
|
|
46961
47509
|
return null;
|
|
@@ -46963,6 +47511,13 @@ class DirectiveSourceManager {
|
|
|
46963
47511
|
const templateSource = this.templateSources.get(id);
|
|
46964
47512
|
return templateSource.toParseSourceSpan(span.start, span.end);
|
|
46965
47513
|
}
|
|
47514
|
+
toHostParseSourceSpan(id, span) {
|
|
47515
|
+
if (!this.hostBindingSources.has(id)) {
|
|
47516
|
+
return null;
|
|
47517
|
+
}
|
|
47518
|
+
const source = this.hostBindingSources.get(id);
|
|
47519
|
+
return source.toParseSourceSpan(span.start, span.end);
|
|
47520
|
+
}
|
|
46966
47521
|
}
|
|
46967
47522
|
|
|
46968
47523
|
/**
|
|
@@ -47727,16 +48282,17 @@ class TemplateTypeCheckerImpl {
|
|
|
47727
48282
|
}
|
|
47728
48283
|
getTemplate(component, optimizeFor) {
|
|
47729
48284
|
const { data } = this.getLatestComponentState(component, optimizeFor);
|
|
47730
|
-
|
|
47731
|
-
|
|
47732
|
-
|
|
47733
|
-
|
|
48285
|
+
return data?.template ?? null;
|
|
48286
|
+
}
|
|
48287
|
+
getHostElement(directive, optimizeFor) {
|
|
48288
|
+
const { data } = this.getLatestComponentState(directive, optimizeFor);
|
|
48289
|
+
return data?.hostElement ?? null;
|
|
47734
48290
|
}
|
|
47735
48291
|
getUsedDirectives(component) {
|
|
47736
|
-
return this.getLatestComponentState(component).data?.boundTarget.getUsedDirectives()
|
|
48292
|
+
return this.getLatestComponentState(component).data?.boundTarget.getUsedDirectives() ?? null;
|
|
47737
48293
|
}
|
|
47738
48294
|
getUsedPipes(component) {
|
|
47739
|
-
return this.getLatestComponentState(component).data?.boundTarget.getUsedPipes()
|
|
48295
|
+
return this.getLatestComponentState(component).data?.boundTarget.getUsedPipes() ?? null;
|
|
47740
48296
|
}
|
|
47741
48297
|
getLatestComponentState(component, optimizeFor = exports.OptimizeFor.SingleFile) {
|
|
47742
48298
|
switch (optimizeFor) {
|
|
@@ -47931,7 +48487,7 @@ class TemplateTypeCheckerImpl {
|
|
|
47931
48487
|
this.isComplete = false;
|
|
47932
48488
|
}
|
|
47933
48489
|
getExpressionTarget(expression, clazz) {
|
|
47934
|
-
return (this.getLatestComponentState(clazz).data?.boundTarget.getExpressionTarget(expression)
|
|
48490
|
+
return (this.getLatestComponentState(clazz).data?.boundTarget.getExpressionTarget(expression) ?? null);
|
|
47935
48491
|
}
|
|
47936
48492
|
makeTemplateDiagnostic(clazz, sourceSpan, category, errorCode, message, relatedInformation) {
|
|
47937
48493
|
const sfPath = absoluteFromSourceFile(clazz.getSourceFile());
|
|
@@ -48607,6 +49163,7 @@ exports.createDirectiveType = createDirectiveType;
|
|
|
48607
49163
|
exports.createFactoryType = createFactoryType;
|
|
48608
49164
|
exports.createForwardRefResolver = createForwardRefResolver;
|
|
48609
49165
|
exports.createHostDirectivesMappingArray = createHostDirectivesMappingArray;
|
|
49166
|
+
exports.createHostElement = createHostElement;
|
|
48610
49167
|
exports.createInjectableType = createInjectableType;
|
|
48611
49168
|
exports.createInjectorType = createInjectorType;
|
|
48612
49169
|
exports.createMayBeForwardRefExpression = createMayBeForwardRefExpression;
|
|
@@ -48622,6 +49179,7 @@ exports.extraReferenceFromTypeQuery = extraReferenceFromTypeQuery;
|
|
|
48622
49179
|
exports.extractDecoratorQueryMetadata = extractDecoratorQueryMetadata;
|
|
48623
49180
|
exports.extractDirectiveMetadata = extractDirectiveMetadata;
|
|
48624
49181
|
exports.extractDirectiveTypeCheckMeta = extractDirectiveTypeCheckMeta;
|
|
49182
|
+
exports.extractHostBindingResources = extractHostBindingResources;
|
|
48625
49183
|
exports.extractMessages = extractMessages;
|
|
48626
49184
|
exports.extractReferencesFromType = extractReferencesFromType;
|
|
48627
49185
|
exports.findAngularDecorator = findAngularDecorator;
|