@angular/core 19.0.0-next.10 → 19.0.0-next.11

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.
Files changed (39) hide show
  1. package/fesm2022/core.mjs +13205 -11798
  2. package/fesm2022/core.mjs.map +1 -1
  3. package/fesm2022/primitives/event-dispatch.mjs +1 -1
  4. package/fesm2022/primitives/signals.mjs +8 -6
  5. package/fesm2022/primitives/signals.mjs.map +1 -1
  6. package/fesm2022/rxjs-interop.mjs +72 -4
  7. package/fesm2022/rxjs-interop.mjs.map +1 -1
  8. package/fesm2022/testing.mjs +4 -4
  9. package/index.d.ts +527 -51
  10. package/package.json +1 -1
  11. package/primitives/event-dispatch/index.d.ts +1 -1
  12. package/primitives/signals/index.d.ts +3 -1
  13. package/rxjs-interop/index.d.ts +32 -1
  14. package/schematics/bundles/{checker-77660732.js → checker-51c08a1b.js} +112 -97
  15. package/schematics/bundles/{compiler_host-81f430d9.js → compiler_host-d7f120f0.js} +2 -2
  16. package/schematics/bundles/control-flow-migration.js +3 -3
  17. package/schematics/bundles/explicit-standalone-flag.js +6 -4
  18. package/schematics/bundles/imports-4ac08251.js +1 -1
  19. package/schematics/bundles/{group_replacements-1f48eff7.js → index-f7b283e6.js} +247 -1649
  20. package/schematics/bundles/inject-migration.js +7 -6
  21. package/schematics/bundles/leading_space-d190b83b.js +1 -1
  22. package/schematics/bundles/migrate_ts_type_references-b2b55f62.js +1448 -0
  23. package/schematics/bundles/{nodes-0e7d45ca.js → ng_decorators-4579dec6.js} +1 -14
  24. package/schematics/bundles/nodes-a535b2be.js +27 -0
  25. package/schematics/bundles/output-migration.js +7295 -0
  26. package/schematics/bundles/pending-tasks.js +3 -3
  27. package/schematics/bundles/{program-1413936a.js → program-6e6520d8.js} +40 -18
  28. package/schematics/bundles/project_tsconfig_paths-e9ccccbf.js +1 -1
  29. package/schematics/bundles/provide-initializer.js +190 -0
  30. package/schematics/bundles/route-lazy-loading.js +3 -3
  31. package/schematics/bundles/signal-input-migration.js +73 -60
  32. package/schematics/bundles/signal-queries-migration.js +119 -90
  33. package/schematics/bundles/signals.js +54 -0
  34. package/schematics/bundles/standalone-migration.js +12 -11
  35. package/schematics/collection.json +11 -0
  36. package/schematics/migrations.json +7 -1
  37. package/schematics/ng-generate/output-migration/schema.json +19 -0
  38. package/schematics/ng-generate/signals/schema.json +65 -0
  39. package/testing/index.d.ts +1 -1
@@ -1,20 +1,18 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v19.0.0-next.10
3
+ * @license Angular v19.0.0-next.11
4
4
  * (c) 2010-2024 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
7
7
  'use strict';
8
8
 
9
+ var core = require('@angular-devkit/core');
10
+ var posixPath = require('node:path/posix');
9
11
  var os = require('os');
10
12
  var ts = require('typescript');
11
- var checker = require('./checker-77660732.js');
12
- var program = require('./program-1413936a.js');
13
+ var checker = require('./checker-51c08a1b.js');
14
+ var program = require('./program-6e6520d8.js');
13
15
  require('path');
14
- var assert = require('assert');
15
- var leading_space = require('./leading_space-d190b83b.js');
16
- var core = require('@angular-devkit/core');
17
- var posixPath = require('node:path/posix');
18
16
 
19
17
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
20
18
 
@@ -36,10 +34,9 @@ function _interopNamespace(e) {
36
34
  return Object.freeze(n);
37
35
  }
38
36
 
37
+ var posixPath__namespace = /*#__PURE__*/_interopNamespace(posixPath);
39
38
  var os__namespace = /*#__PURE__*/_interopNamespace(os);
40
39
  var ts__default = /*#__PURE__*/_interopDefaultLegacy(ts);
41
- var assert__default = /*#__PURE__*/_interopDefaultLegacy(assert);
42
- var posixPath__namespace = /*#__PURE__*/_interopNamespace(posixPath);
43
40
 
44
41
  /// <reference types="node" />
45
42
  class NgtscCompilerHost {
@@ -253,82 +250,161 @@ function getExtendedConfigPathWorker(configFile, extendsValue, host, fs) {
253
250
  }
254
251
 
255
252
  /**
256
- * Reasons why a field cannot be migrated.
257
- *
258
- * Higher values of incompatibility reasons indicate a more significant
259
- * incompatibility reason. Lower ones may be overridden by higher ones.
260
- * */
261
- exports.FieldIncompatibilityReason = void 0;
262
- (function (FieldIncompatibilityReason) {
263
- FieldIncompatibilityReason[FieldIncompatibilityReason["OverriddenByDerivedClass"] = 1] = "OverriddenByDerivedClass";
264
- FieldIncompatibilityReason[FieldIncompatibilityReason["RedeclaredViaDerivedClassInputsArray"] = 2] = "RedeclaredViaDerivedClassInputsArray";
265
- FieldIncompatibilityReason[FieldIncompatibilityReason["TypeConflictWithBaseClass"] = 3] = "TypeConflictWithBaseClass";
266
- FieldIncompatibilityReason[FieldIncompatibilityReason["ParentIsIncompatible"] = 4] = "ParentIsIncompatible";
267
- FieldIncompatibilityReason[FieldIncompatibilityReason["DerivedIsIncompatible"] = 5] = "DerivedIsIncompatible";
268
- FieldIncompatibilityReason[FieldIncompatibilityReason["SpyOnThatOverwritesField"] = 6] = "SpyOnThatOverwritesField";
269
- FieldIncompatibilityReason[FieldIncompatibilityReason["PotentiallyNarrowedInTemplateButNoSupportYet"] = 7] = "PotentiallyNarrowedInTemplateButNoSupportYet";
270
- FieldIncompatibilityReason[FieldIncompatibilityReason["SignalInput__RequiredButNoGoodExplicitTypeExtractable"] = 8] = "SignalInput__RequiredButNoGoodExplicitTypeExtractable";
271
- FieldIncompatibilityReason[FieldIncompatibilityReason["SignalInput__QuestionMarkButNoGoodExplicitTypeExtractable"] = 9] = "SignalInput__QuestionMarkButNoGoodExplicitTypeExtractable";
272
- FieldIncompatibilityReason[FieldIncompatibilityReason["SignalQueries__QueryListProblematicFieldAccessed"] = 10] = "SignalQueries__QueryListProblematicFieldAccessed";
273
- FieldIncompatibilityReason[FieldIncompatibilityReason["SignalQueries__IncompatibleMultiUnionType"] = 11] = "SignalQueries__IncompatibleMultiUnionType";
274
- FieldIncompatibilityReason[FieldIncompatibilityReason["WriteAssignment"] = 12] = "WriteAssignment";
275
- FieldIncompatibilityReason[FieldIncompatibilityReason["Accessor"] = 13] = "Accessor";
276
- FieldIncompatibilityReason[FieldIncompatibilityReason["OutsideOfMigrationScope"] = 14] = "OutsideOfMigrationScope";
277
- FieldIncompatibilityReason[FieldIncompatibilityReason["SkippedViaConfigFilter"] = 15] = "SkippedViaConfigFilter";
278
- })(exports.FieldIncompatibilityReason || (exports.FieldIncompatibilityReason = {}));
279
- /** Field reasons that cannot be ignored. */
280
- const nonIgnorableFieldIncompatibilities = [
281
- // Outside of scope fields should not be migrated. E.g. references to inputs in `node_modules/`.
282
- exports.FieldIncompatibilityReason.OutsideOfMigrationScope,
283
- // Explicitly filtered fields cannot be skipped via best effort mode.
284
- exports.FieldIncompatibilityReason.SkippedViaConfigFilter,
285
- // There is no good output for accessor fields.
286
- exports.FieldIncompatibilityReason.Accessor,
287
- // There is no good output for such inputs. We can't perform "conversion".
288
- exports.FieldIncompatibilityReason.SignalInput__RequiredButNoGoodExplicitTypeExtractable,
289
- exports.FieldIncompatibilityReason.SignalInput__QuestionMarkButNoGoodExplicitTypeExtractable,
290
- ];
291
- /** Reasons why a whole class and its fields cannot be migrated. */
292
- exports.ClassIncompatibilityReason = void 0;
293
- (function (ClassIncompatibilityReason) {
294
- ClassIncompatibilityReason[ClassIncompatibilityReason["ClassManuallyInstantiated"] = 0] = "ClassManuallyInstantiated";
295
- ClassIncompatibilityReason[ClassIncompatibilityReason["OwningClassReferencedInClassProperty"] = 1] = "OwningClassReferencedInClassProperty";
296
- })(exports.ClassIncompatibilityReason || (exports.ClassIncompatibilityReason = {}));
297
- /** Whether the given value refers to an field incompatibility. */
298
- function isFieldIncompatibility(value) {
299
- return (value.reason !== undefined &&
300
- value.context !== undefined &&
301
- exports.FieldIncompatibilityReason.hasOwnProperty(value.reason));
302
- }
303
- /** Picks the more significant field compatibility. */
304
- function pickFieldIncompatibility(a, b) {
305
- if (b === null) {
306
- return a;
253
+ * Angular compiler file system implementation that leverages an
254
+ * CLI schematic virtual file tree.
255
+ */
256
+ class DevkitMigrationFilesystem {
257
+ constructor(tree) {
258
+ this.tree = tree;
259
+ }
260
+ extname(path) {
261
+ return core.extname(path);
262
+ }
263
+ isRoot(path) {
264
+ return path === core.normalize('/');
265
+ }
266
+ isRooted(path) {
267
+ return this.normalize(path).startsWith('/');
268
+ }
269
+ dirname(file) {
270
+ return this.normalize(core.dirname(file));
271
+ }
272
+ join(basePath, ...paths) {
273
+ return this.normalize(core.join(basePath, ...paths));
274
+ }
275
+ relative(from, to) {
276
+ return this.normalize(core.relative(from, to));
277
+ }
278
+ basename(filePath, extension) {
279
+ return posixPath__namespace.basename(filePath, extension);
280
+ }
281
+ normalize(path) {
282
+ return core.normalize(path);
283
+ }
284
+ resolve(...paths) {
285
+ const normalizedPaths = paths.map((p) => core.normalize(p));
286
+ // In dev-kit, the NodeJS working directory should never be
287
+ // considered, so `/` is the last resort over `cwd`.
288
+ return this.normalize(posixPath__namespace.resolve(core.normalize('/'), ...normalizedPaths));
289
+ }
290
+ pwd() {
291
+ return '/';
292
+ }
293
+ isCaseSensitive() {
294
+ return true;
295
+ }
296
+ exists(path) {
297
+ return statPath(this.tree, path) !== null;
298
+ }
299
+ readFile(path) {
300
+ return this.tree.readText(path);
301
+ }
302
+ readFileBuffer(path) {
303
+ const buffer = this.tree.read(path);
304
+ if (buffer === null) {
305
+ throw new Error(`File does not exist: ${path}`);
306
+ }
307
+ return buffer;
307
308
  }
308
- if (a.reason < b.reason) {
309
- return b;
309
+ readdir(path) {
310
+ const dir = this.tree.getDir(path);
311
+ return [
312
+ ...dir.subdirs,
313
+ ...dir.subfiles,
314
+ ];
315
+ }
316
+ lstat(path) {
317
+ const stat = statPath(this.tree, path);
318
+ if (stat === null) {
319
+ throw new Error(`File does not exist for "lstat": ${path}`);
320
+ }
321
+ return stat;
322
+ }
323
+ stat(path) {
324
+ const stat = statPath(this.tree, path);
325
+ if (stat === null) {
326
+ throw new Error(`File does not exist for "stat": ${path}`);
327
+ }
328
+ return stat;
329
+ }
330
+ realpath(filePath) {
331
+ return filePath;
332
+ }
333
+ getDefaultLibLocation() {
334
+ return 'node_modules/typescript/lib';
335
+ }
336
+ ensureDir(path) {
337
+ // Migrations should compute replacements and not write directly.
338
+ throw new Error('DevkitFilesystem#ensureDir is not supported.');
339
+ }
340
+ writeFile(path, data) {
341
+ // Migrations should compute replacements and not write directly.
342
+ throw new Error('DevkitFilesystem#writeFile is not supported.');
343
+ }
344
+ removeFile(path) {
345
+ // Migrations should compute replacements and not write directly.
346
+ throw new Error('DevkitFilesystem#removeFile is not supported.');
347
+ }
348
+ copyFile(from, to) {
349
+ // Migrations should compute replacements and not write directly.
350
+ throw new Error('DevkitFilesystem#copyFile is not supported.');
351
+ }
352
+ moveFile(from, to) {
353
+ // Migrations should compute replacements and not write directly.
354
+ throw new Error('DevkitFilesystem#moveFile is not supported.');
355
+ }
356
+ removeDeep(path) {
357
+ // Migrations should compute replacements and not write directly.
358
+ throw new Error('DevkitFilesystem#removeDeep is not supported.');
359
+ }
360
+ chdir(_path) {
361
+ throw new Error('FileSystem#chdir is not supported.');
362
+ }
363
+ symlink() {
364
+ throw new Error('FileSystem#symlink is not supported.');
310
365
  }
311
- return a;
312
366
  }
313
-
314
- function getMemberName(member) {
315
- if (member.name === undefined) {
316
- return null;
367
+ /** Stats the given path in the virtual tree. */
368
+ function statPath(tree, path) {
369
+ let fileInfo = null;
370
+ let dirInfo = null;
371
+ try {
372
+ fileInfo = tree.get(path);
317
373
  }
318
- if (ts__default["default"].isIdentifier(member.name) || ts__default["default"].isStringLiteralLike(member.name)) {
319
- return member.name.text;
374
+ catch (e) {
375
+ if (e.constructor.name === 'PathIsDirectoryException') {
376
+ dirInfo = tree.getDir(path);
377
+ }
378
+ else {
379
+ throw e;
380
+ }
320
381
  }
321
- if (ts__default["default"].isPrivateIdentifier(member.name)) {
322
- return `#${member.name.text}`;
382
+ if (fileInfo !== null || dirInfo !== null) {
383
+ return {
384
+ isDirectory: () => dirInfo !== null,
385
+ isFile: () => fileInfo !== null,
386
+ isSymbolicLink: () => false,
387
+ };
323
388
  }
324
389
  return null;
325
390
  }
326
391
 
327
- /** Checks whether the given node can be an `@Input()` declaration node. */
328
- function isInputContainerNode(node) {
329
- return (((ts__default["default"].isAccessor(node) && ts__default["default"].isClassDeclaration(node.parent)) ||
330
- ts__default["default"].isPropertyDeclaration(node)) &&
331
- getMemberName(node) !== null);
392
+ /**
393
+ * Groups the given replacements per project relative
394
+ * file path.
395
+ *
396
+ * This allows for simple execution of the replacements
397
+ * against a given file. E.g. via {@link applyTextUpdates}.
398
+ */
399
+ function groupReplacementsByFile(replacements) {
400
+ const result = new Map();
401
+ for (const { projectFile, update } of replacements) {
402
+ if (!result.has(projectFile.rootRelativePath)) {
403
+ result.set(projectFile.rootRelativePath, []);
404
+ }
405
+ result.get(projectFile.rootRelativePath).push(update);
406
+ }
407
+ return result;
332
408
  }
333
409
 
334
410
  /** Code of the error raised by TypeScript when a tsconfig doesn't match any files. */
@@ -404,6 +480,20 @@ class TsurgeBaseMigration {
404
480
  }
405
481
  }
406
482
 
483
+ /**
484
+ * A simpler variant of a {@link TsurgeComplexMigration} that does not
485
+ * fan-out into multiple workers per compilation unit to compute
486
+ * the final migration replacements.
487
+ *
488
+ * This is faster and less resource intensive as workers and TS programs
489
+ * are only ever created once.
490
+ *
491
+ * This is commonly the case when migrations are refactored to eagerly
492
+ * compute replacements in the analyze stage, and then leverage the
493
+ * global unit data to filter replacements that turned out to be "invalid".
494
+ */
495
+ class TsurgeFunnelMigration extends TsurgeBaseMigration {
496
+ }
407
497
  /**
408
498
  * Complex variant of a `Tsurge` migration.
409
499
  *
@@ -475,103 +565,76 @@ function isWithinBasePath(fs, base, path) {
475
565
  }
476
566
 
477
567
  /**
478
- * Detects `spyOn(dirInstance, 'myInput')` calls that likely modify
479
- * the input signal. There is no way to change the value inside the input signal,
480
- * and hence observing is not possible.
568
+ * Applies import manager changes, and writes them as replacements the
569
+ * given result array.
481
570
  */
482
- class SpyOnFieldPattern {
483
- constructor(checker, fields) {
484
- this.checker = checker;
485
- this.fields = fields;
486
- }
487
- detect(node) {
488
- if (ts__default["default"].isCallExpression(node) &&
489
- ts__default["default"].isIdentifier(node.expression) &&
490
- node.expression.text === 'spyOn' &&
491
- node.arguments.length === 2 &&
492
- ts__default["default"].isStringLiteralLike(node.arguments[1])) {
493
- const spyTargetType = this.checker.getTypeAtLocation(node.arguments[0]);
494
- const spyProperty = spyTargetType.getProperty(node.arguments[1].text);
495
- if (spyProperty === undefined) {
496
- return;
497
- }
498
- const fieldTarget = this.fields.attemptRetrieveDescriptorFromSymbol(spyProperty);
499
- if (fieldTarget === null) {
500
- return;
501
- }
502
- this.fields.markFieldIncompatible(fieldTarget, {
503
- reason: exports.FieldIncompatibilityReason.SpyOnThatOverwritesField,
504
- context: node,
505
- });
571
+ function applyImportManagerChanges(importManager, replacements, sourceFiles, info) {
572
+ const { newImports, updatedImports, deletedImports } = importManager.finalize();
573
+ const printer = ts__default["default"].createPrinter({});
574
+ const pathToFile = new Map(sourceFiles.map((s) => [s.fileName, s]));
575
+ // Capture new imports
576
+ newImports.forEach((newImports, fileName) => {
577
+ newImports.forEach((newImport) => {
578
+ const printedImport = printer.printNode(ts__default["default"].EmitHint.Unspecified, newImport, pathToFile.get(fileName));
579
+ replacements.push(new Replacement(projectFile(checker.absoluteFrom(fileName), info), new TextUpdate({ position: 0, end: 0, toInsert: `${printedImport}\n` })));
580
+ });
581
+ });
582
+ // Capture updated imports
583
+ for (const [oldBindings, newBindings] of updatedImports.entries()) {
584
+ // The import will be generated as multi-line if it already is multi-line,
585
+ // or if the number of elements significantly increased and it previously
586
+ // consisted of very few specifiers.
587
+ const isMultiline = oldBindings.getText().includes('\n') ||
588
+ (newBindings.elements.length >= 6 && oldBindings.elements.length <= 3);
589
+ const hasSpaceBetweenBraces = oldBindings.getText().startsWith('{ ');
590
+ let formatFlags = ts__default["default"].ListFormat.NamedImportsOrExportsElements |
591
+ ts__default["default"].ListFormat.Indented |
592
+ ts__default["default"].ListFormat.Braces |
593
+ ts__default["default"].ListFormat.PreserveLines |
594
+ (isMultiline ? ts__default["default"].ListFormat.MultiLine : ts__default["default"].ListFormat.SingleLine);
595
+ if (hasSpaceBetweenBraces) {
596
+ formatFlags |= ts__default["default"].ListFormat.SpaceBetweenBraces;
597
+ }
598
+ else {
599
+ formatFlags &= ~ts__default["default"].ListFormat.SpaceBetweenBraces;
506
600
  }
601
+ const printedBindings = printer.printList(formatFlags, newBindings.elements, oldBindings.getSourceFile());
602
+ replacements.push(new Replacement(projectFile(oldBindings.getSourceFile(), info), new TextUpdate({
603
+ position: oldBindings.getStart(),
604
+ end: oldBindings.getEnd(),
605
+ // TS uses four spaces as indent. We migrate to two spaces as we
606
+ // assume this to be more common.
607
+ toInsert: printedBindings.replace(/^ {4}/gm, ' '),
608
+ })));
609
+ }
610
+ // Update removed imports
611
+ for (const removedImport of deletedImports) {
612
+ replacements.push(new Replacement(projectFile(removedImport.getSourceFile(), info), new TextUpdate({
613
+ position: removedImport.getStart(),
614
+ end: removedImport.getEnd(),
615
+ toInsert: '',
616
+ })));
507
617
  }
508
618
  }
509
619
 
510
- /**
511
- * Phase where problematic patterns are detected and advise
512
- * the migration to skip certain inputs.
513
- *
514
- * For example, detects classes that are instantiated manually. Those
515
- * cannot be migrated as `input()` requires an injection context.
516
- *
517
- * In addition, spying onto an input may be problematic- so we skip migrating
518
- * such.
519
- */
520
- function checkIncompatiblePatterns(inheritanceGraph, checker$1, groupedTsAstVisitor, fields, getAllClassesWithKnownFields) {
521
- const inputClassSymbolsToClass = new Map();
522
- for (const knownFieldClass of getAllClassesWithKnownFields()) {
523
- const classSymbol = checker$1.getTypeAtLocation(knownFieldClass).symbol;
524
- assert__default["default"](classSymbol != null, 'Expected a symbol to exist for the container of known field class.');
525
- assert__default["default"](classSymbol.valueDeclaration !== undefined, 'Expected declaration to exist for known field class.');
526
- assert__default["default"](ts__default["default"].isClassDeclaration(classSymbol.valueDeclaration), 'Expected declaration to be a class.');
527
- // track class symbol for derived class checks.
528
- inputClassSymbolsToClass.set(classSymbol, classSymbol.valueDeclaration);
529
- }
530
- const spyOnPattern = new SpyOnFieldPattern(checker$1, fields);
531
- const visitor = (node) => {
532
- // Check for manual class instantiations.
533
- if (ts__default["default"].isNewExpression(node) && ts__default["default"].isIdentifier(checker.unwrapExpression(node.expression))) {
534
- let newTarget = checker$1.getSymbolAtLocation(checker.unwrapExpression(node.expression));
535
- // Plain identifier references can point to alias symbols (e.g. imports).
536
- if (newTarget !== undefined && newTarget.flags & ts__default["default"].SymbolFlags.Alias) {
537
- newTarget = checker$1.getAliasedSymbol(newTarget);
538
- }
539
- if (newTarget && inputClassSymbolsToClass.has(newTarget)) {
540
- fields.markClassIncompatible(inputClassSymbolsToClass.get(newTarget), exports.ClassIncompatibilityReason.ClassManuallyInstantiated);
541
- }
542
- }
543
- // Detect `spyOn` problematic usages and record them.
544
- spyOnPattern.detect(node);
545
- const insidePropertyDeclaration = groupedTsAstVisitor.state.insidePropertyDeclaration;
546
- // Check for problematic class references inside property declarations.
547
- // These are likely problematic, causing type conflicts, if the containing
548
- // class inherits a non-input member with the same name.
549
- // Suddenly the derived class changes its signature, but the base class may not.
550
- problematicReferencesCheck: if (insidePropertyDeclaration !== null &&
551
- ts__default["default"].isIdentifier(node) &&
552
- insidePropertyDeclaration.parent.heritageClauses !== undefined) {
553
- let newTarget = checker$1.getSymbolAtLocation(checker.unwrapExpression(node));
554
- // Plain identifier references can point to alias symbols (e.g. imports).
555
- if (newTarget !== undefined && newTarget.flags & ts__default["default"].SymbolFlags.Alias) {
556
- newTarget = checker$1.getAliasedSymbol(newTarget);
557
- }
558
- if (newTarget && inputClassSymbolsToClass.has(newTarget)) {
559
- const memberName = getMemberName(insidePropertyDeclaration);
560
- if (memberName === null) {
561
- break problematicReferencesCheck;
562
- }
563
- const { derivedMembers, inherited } = inheritanceGraph.checkOverlappingMembers(insidePropertyDeclaration.parent, insidePropertyDeclaration, memberName);
564
- // Member is not inherited, or derived.
565
- // Hence the reference is unproblematic and is expected to not
566
- // cause any type conflicts.
567
- if (derivedMembers.length === 0 && inherited === undefined) {
568
- break problematicReferencesCheck;
569
- }
570
- fields.markClassIncompatible(inputClassSymbolsToClass.get(newTarget), exports.ClassIncompatibilityReason.OwningClassReferencedInClassProperty);
571
- }
572
- }
573
- };
574
- groupedTsAstVisitor.register(visitor);
620
+ function getMemberName(member) {
621
+ if (member.name === undefined) {
622
+ return null;
623
+ }
624
+ if (ts__default["default"].isIdentifier(member.name) || ts__default["default"].isStringLiteralLike(member.name)) {
625
+ return member.name.text;
626
+ }
627
+ if (ts__default["default"].isPrivateIdentifier(member.name)) {
628
+ return `#${member.name.text}`;
629
+ }
630
+ return null;
631
+ }
632
+
633
+ /** Checks whether the given node can be an `@Input()` declaration node. */
634
+ function isInputContainerNode(node) {
635
+ return (((ts__default["default"].isAccessor(node) && ts__default["default"].isClassDeclaration(node.parent)) ||
636
+ ts__default["default"].isPropertyDeclaration(node)) &&
637
+ getMemberName(node) !== null);
575
638
  }
576
639
 
577
640
  /**
@@ -29625,7 +29688,7 @@ function publishFacade(global) {
29625
29688
  * @description
29626
29689
  * Entry point for all public APIs of the compiler package.
29627
29690
  */
29628
- new Version('19.0.0-next.10');
29691
+ new Version('19.0.0-next.11');
29629
29692
 
29630
29693
  var _VisitorMode;
29631
29694
  (function (_VisitorMode) {
@@ -29987,31 +30050,31 @@ function isTwoWayBindingNode(node) {
29987
30050
  }
29988
30051
 
29989
30052
  /** Possible types of references to known fields detected. */
29990
- var ReferenceKind;
30053
+ exports.ReferenceKind = void 0;
29991
30054
  (function (ReferenceKind) {
29992
30055
  ReferenceKind[ReferenceKind["InTemplate"] = 0] = "InTemplate";
29993
30056
  ReferenceKind[ReferenceKind["InHostBinding"] = 1] = "InHostBinding";
29994
30057
  ReferenceKind[ReferenceKind["TsReference"] = 2] = "TsReference";
29995
30058
  ReferenceKind[ReferenceKind["TsClassTypeReference"] = 3] = "TsClassTypeReference";
29996
- })(ReferenceKind || (ReferenceKind = {}));
30059
+ })(exports.ReferenceKind || (exports.ReferenceKind = {}));
29997
30060
  /** Whether the given reference is a TypeScript reference. */
29998
30061
  function isTsReference(ref) {
29999
- return ref.kind === ReferenceKind.TsReference;
30062
+ return ref.kind === exports.ReferenceKind.TsReference;
30000
30063
  }
30001
30064
  /** Whether the given reference is a template reference. */
30002
30065
  function isTemplateReference(ref) {
30003
- return ref.kind === ReferenceKind.InTemplate;
30066
+ return ref.kind === exports.ReferenceKind.InTemplate;
30004
30067
  }
30005
30068
  /** Whether the given reference is a host binding reference. */
30006
30069
  function isHostBindingReference(ref) {
30007
- return ref.kind === ReferenceKind.InHostBinding;
30070
+ return ref.kind === exports.ReferenceKind.InHostBinding;
30008
30071
  }
30009
30072
  /**
30010
30073
  * Whether the given reference is a TypeScript `ts.Type` reference
30011
30074
  * to a class containing known fields.
30012
30075
  */
30013
30076
  function isTsClassTypeReference(ref) {
30014
- return ref.kind === ReferenceKind.TsClassTypeReference;
30077
+ return ref.kind === exports.ReferenceKind.TsClassTypeReference;
30015
30078
  }
30016
30079
 
30017
30080
  /**
@@ -30099,7 +30162,7 @@ function identifyHostBindingReferences(node, programInfo, checker$1, reflector,
30099
30162
  }
30100
30163
  for (const ref of expressionResult) {
30101
30164
  result.references.push({
30102
- kind: ReferenceKind.InHostBinding,
30165
+ kind: exports.ReferenceKind.InHostBinding,
30103
30166
  from: {
30104
30167
  read: ref.read,
30105
30168
  readAstPath: ref.readAstPath,
@@ -30199,7 +30262,7 @@ function identifyTemplateReferences(programInfo, node, reflector, checker$1, eva
30199
30262
  continue;
30200
30263
  }
30201
30264
  result.references.push({
30202
- kind: ReferenceKind.InTemplate,
30265
+ kind: exports.ReferenceKind.InTemplate,
30203
30266
  from: {
30204
30267
  read: res.read,
30205
30268
  readAstPath: res.readAstPath,
@@ -30385,7 +30448,7 @@ function identifyPotentialTypeScriptReference(node, programInfo, checker, knownF
30385
30448
  writeBinaryOperators.includes(accessParent.operatorToken.kind);
30386
30449
  // track accesses from source files to known fields.
30387
30450
  result.references.push({
30388
- kind: ReferenceKind.TsReference,
30451
+ kind: exports.ReferenceKind.TsReference,
30389
30452
  from: {
30390
30453
  node,
30391
30454
  file: projectFile(node.getSourceFile(), programInfo),
@@ -30448,7 +30511,7 @@ function createFindAllSourceFileReferencesVisitor(programInfo, checker, reflecto
30448
30511
  const partialDirectiveInCatalyst = partialDirectiveCatalystTracker.detect(node);
30449
30512
  if (partialDirectiveInCatalyst !== null) {
30450
30513
  result.references.push({
30451
- kind: ReferenceKind.TsClassTypeReference,
30514
+ kind: exports.ReferenceKind.TsClassTypeReference,
30452
30515
  from: {
30453
30516
  file: projectFile(partialDirectiveInCatalyst.referenceNode.getSourceFile(), programInfo),
30454
30517
  node: partialDirectiveInCatalyst.referenceNode,
@@ -30468,1488 +30531,23 @@ function createFindAllSourceFileReferencesVisitor(programInfo, checker, reflecto
30468
30531
  };
30469
30532
  }
30470
30533
 
30471
- /** Gets all types that are inherited (implemented or extended). */
30472
- function getInheritedTypes(node, checker) {
30473
- if (node.heritageClauses === undefined) {
30474
- return [];
30475
- }
30476
- const heritageTypes = [];
30477
- for (const heritageClause of node.heritageClauses) {
30478
- for (const typeNode of heritageClause.types) {
30479
- heritageTypes.push(checker.getTypeFromTypeNode(typeNode));
30480
- }
30481
- }
30482
- return heritageTypes;
30483
- }
30484
-
30485
- /**
30486
- * Inheritance graph tracks edges between classes that describe
30487
- * heritage.
30488
- *
30489
- * This graph is helpful for efficient lookups whether e.g. an input
30490
- * is overridden, or inherited etc. This is helpful when detecting
30491
- * and propagating input incompatibility statuses.
30492
- */
30493
- class InheritanceGraph {
30494
- constructor(checker) {
30495
- this.checker = checker;
30496
- /** Maps nodes to their parent nodes. */
30497
- this.classToParents = new Map();
30498
- /** Maps nodes to their derived nodes. */
30499
- this.parentToChildren = new Map();
30500
- /** All classes seen participating in inheritance chains. */
30501
- this.allClassesInInheritance = new Set();
30502
- }
30503
- /** Registers a given class in the graph. */
30504
- registerClass(clazz, parents) {
30505
- this.classToParents.set(clazz, parents);
30506
- this.allClassesInInheritance.add(clazz);
30507
- for (const parent of parents) {
30508
- this.allClassesInInheritance.add(parent);
30509
- if (!this.parentToChildren.has(parent)) {
30510
- this.parentToChildren.set(parent, []);
30511
- }
30512
- this.parentToChildren.get(parent).push(clazz);
30513
- }
30514
- }
30515
- /**
30516
- * Checks if the given class has overlapping members, either
30517
- * inherited or derived.
30518
- *
30519
- * @returns Symbols of the inherited or derived members, if they exist.
30520
- */
30521
- checkOverlappingMembers(clazz, member, memberName) {
30522
- const inheritedTypes = (this.classToParents.get(clazz) ?? []).map((c) => this.checker.getTypeAtLocation(c));
30523
- const derivedLeafs = this._traceDerivedChainToLeafs(clazz).map((c) => this.checker.getTypeAtLocation(c));
30524
- const inheritedMember = inheritedTypes
30525
- .map((t) => t.getProperty(memberName))
30526
- .find((m) => m !== undefined);
30527
- const derivedMembers = derivedLeafs
30528
- .map((t) => t.getProperty(memberName))
30529
- // Skip members that point back to the current class element. The derived type
30530
- // might look up back to our starting point— which we ignore.
30531
- .filter((m) => m !== undefined && m.valueDeclaration !== member);
30532
- return { inherited: inheritedMember, derivedMembers };
30533
- }
30534
- /** Gets all leaf derived classes that extend from the given class. */
30535
- _traceDerivedChainToLeafs(clazz) {
30536
- const queue = [clazz];
30537
- const leafs = [];
30538
- while (queue.length) {
30539
- const node = queue.shift();
30540
- if (!this.parentToChildren.has(node)) {
30541
- if (node !== clazz) {
30542
- leafs.push(node);
30543
- }
30544
- continue;
30545
- }
30546
- queue.push(...this.parentToChildren.get(node));
30547
- }
30548
- return leafs;
30549
- }
30550
- /** Gets all derived classes of the given node. */
30551
- traceDerivedClasses(clazz) {
30552
- const queue = [clazz];
30553
- const derived = [];
30554
- while (queue.length) {
30555
- const node = queue.shift();
30556
- if (node !== clazz) {
30557
- derived.push(node);
30558
- }
30559
- if (!this.parentToChildren.has(node)) {
30560
- continue;
30561
- }
30562
- queue.push(...this.parentToChildren.get(node));
30563
- }
30564
- return derived;
30565
- }
30566
- /**
30567
- * Populates the graph.
30568
- *
30569
- * NOTE: This is expensive and should be called with caution.
30570
- */
30571
- expensivePopulate(files) {
30572
- for (const file of files) {
30573
- const visitor = (node) => {
30574
- if ((ts__default["default"].isClassLike(node) || ts__default["default"].isInterfaceDeclaration(node)) &&
30575
- node.heritageClauses !== undefined) {
30576
- const heritageTypes = getInheritedTypes(node, this.checker);
30577
- const parents = heritageTypes
30578
- // Interfaces participate in the graph and are not "value declarations".
30579
- // Also, symbol may be undefined for unresolvable nodes.
30580
- .map((t) => (t.symbol ? t.symbol.declarations?.[0] : undefined))
30581
- .filter((d) => d !== undefined && (ts__default["default"].isClassLike(d) || ts__default["default"].isInterfaceDeclaration(d)));
30582
- this.registerClass(node, parents);
30583
- }
30584
- ts__default["default"].forEachChild(node, visitor);
30585
- };
30586
- ts__default["default"].forEachChild(file, visitor);
30587
- }
30588
- return this;
30589
- }
30590
- }
30591
-
30592
- /**
30593
- * Class that allows for efficient grouping of TypeScript node AST
30594
- * traversal.
30595
- *
30596
- * Allows visitors to execute in a single pass when visiting all
30597
- * children of source files.
30598
- */
30599
- class GroupedTsAstVisitor {
30600
- constructor(files) {
30601
- this.files = files;
30602
- this.visitors = [];
30603
- this.doneFns = [];
30604
- this.state = {
30605
- insidePropertyDeclaration: null,
30606
- };
30607
- }
30608
- register(visitor, done) {
30609
- this.visitors.push(visitor);
30610
- if (done !== undefined) {
30611
- this.doneFns.push(done);
30612
- }
30613
- }
30614
- execute() {
30615
- const visitor = (node) => {
30616
- for (const v of this.visitors) {
30617
- v(node);
30618
- }
30619
- if (ts__default["default"].isPropertyDeclaration(node)) {
30620
- this.state.insidePropertyDeclaration = node;
30621
- ts__default["default"].forEachChild(node, visitor);
30622
- this.state.insidePropertyDeclaration = null;
30623
- }
30624
- else {
30625
- ts__default["default"].forEachChild(node, visitor);
30626
- }
30627
- };
30628
- for (const file of this.files) {
30629
- ts__default["default"].forEachChild(file, visitor);
30630
- }
30631
- for (const doneFn of this.doneFns) {
30632
- doneFn();
30633
- }
30634
- this.visitors = [];
30635
- }
30636
- }
30637
-
30638
- /**
30639
- * Phase that propagates incompatibilities to derived classes or
30640
- * base classes. For example, consider:
30641
- *
30642
- * ```
30643
- * class Base {
30644
- * bla = true;
30645
- * }
30646
- *
30647
- * class Derived extends Base {
30648
- * @Input() bla = false;
30649
- * }
30650
- * ```
30651
- *
30652
- * Whenever we migrate `Derived`, the inheritance would fail
30653
- * and result in a build breakage because `Base#bla` is not an Angular input.
30654
- *
30655
- * The logic here detects such cases and marks `bla` as incompatible. If `Derived`
30656
- * would then have other derived classes as well, it would propagate the status.
30657
- */
30658
- function checkInheritanceOfKnownFields(inheritanceGraph, metaRegistry, fields, opts) {
30659
- const allInputClasses = Array.from(inheritanceGraph.allClassesInInheritance).filter((t) => ts__default["default"].isClassDeclaration(t) && opts.isClassWithKnownFields(t));
30660
- for (const inputClass of allInputClasses) {
30661
- // Note: Class parents of `inputClass` were already checked by
30662
- // the previous iterations (given the reverse topological sort)—
30663
- // hence it's safe to assume that incompatibility of parent classes will
30664
- // not change again, at a later time.
30665
- assert__default["default"](ts__default["default"].isClassDeclaration(inputClass), 'Expected input graph node to be always a class.');
30666
- const classFields = opts.getFieldsForClass(inputClass);
30667
- // Iterate through derived class chains and determine all inputs that are overridden
30668
- // via class metadata fields. e.g `@Component#inputs`. This is later used to mark a
30669
- // potential similar class input as incompatible— because those cannot be migrated.
30670
- const inputFieldNamesFromMetadataArray = new Set();
30671
- for (const derivedClasses of inheritanceGraph.traceDerivedClasses(inputClass)) {
30672
- const derivedMeta = ts__default["default"].isClassDeclaration(derivedClasses) && derivedClasses.name !== undefined
30673
- ? metaRegistry.getDirectiveMetadata(new checker.Reference(derivedClasses))
30674
- : null;
30675
- if (derivedMeta !== null && derivedMeta.inputFieldNamesFromMetadataArray !== null) {
30676
- derivedMeta.inputFieldNamesFromMetadataArray.forEach((b) => inputFieldNamesFromMetadataArray.add(b));
30677
- }
30678
- }
30679
- // Check inheritance of every input in the given "directive class".
30680
- inputCheck: for (const fieldDescr of classFields) {
30681
- const inputNode = fieldDescr.node;
30682
- const { derivedMembers, inherited } = inheritanceGraph.checkOverlappingMembers(inputClass, inputNode, getMemberName(inputNode));
30683
- // If we discover a derived, input re-declared via class metadata, then it
30684
- // will cause conflicts as we cannot migrate it/ nor mark it as signal-based.
30685
- if (fieldDescr.node.name !== undefined &&
30686
- (ts__default["default"].isIdentifier(fieldDescr.node.name) || ts__default["default"].isStringLiteralLike(fieldDescr.node.name)) &&
30687
- inputFieldNamesFromMetadataArray.has(fieldDescr.node.name.text)) {
30688
- fields.captureUnknownDerivedField(fieldDescr);
30689
- }
30690
- for (const derived of derivedMembers) {
30691
- const derivedInput = fields.attemptRetrieveDescriptorFromSymbol(derived);
30692
- if (derivedInput !== null) {
30693
- // Note: We always track dependencies from the child to the parent,
30694
- // so skip here for now.
30695
- continue;
30696
- }
30697
- // If we discover a derived, non-input member, then it will cause
30698
- // conflicts, and we mark the current input as incompatible.
30699
- fields.captureUnknownDerivedField(fieldDescr);
30700
- continue inputCheck;
30701
- }
30702
- // If there is no parent, we are done. Otherwise, check the parent
30703
- // to either inherit or check the incompatibility with the inheritance.
30704
- if (inherited === undefined) {
30705
- continue;
30706
- }
30707
- const inheritedMemberInput = fields.attemptRetrieveDescriptorFromSymbol(inherited);
30708
- // Parent is not an input, and hence will conflict..
30709
- if (inheritedMemberInput === null) {
30710
- fields.captureUnknownParentField(fieldDescr);
30711
- continue;
30712
- }
30713
- fields.captureKnownFieldInheritanceRelationship(fieldDescr, inheritedMemberInput);
30714
- }
30715
- }
30716
- }
30717
-
30718
- function removeFromUnionIfPossible(union, filter) {
30719
- const filtered = union.types.filter(filter);
30720
- if (filtered.length === union.types.length) {
30721
- return union;
30722
- }
30723
- // If there is only item at this point, avoid the union structure.
30724
- if (filtered.length === 1) {
30725
- return filtered[0];
30726
- }
30727
- return ts__default["default"].factory.updateUnionTypeNode(union, ts__default["default"].factory.createNodeArray(filtered));
30728
- }
30729
-
30730
- /**
30731
- * Inserts a leading string for the given node, respecting
30732
- * indentation of the given anchor node.
30733
- *
30734
- * Useful for inserting TODOs.
30735
- */
30736
- function insertPrecedingLine(node, info, text) {
30737
- const leadingSpace = leading_space.getLeadingLineWhitespaceOfNode(node);
30738
- return new Replacement(projectFile(node.getSourceFile(), info), new TextUpdate({
30739
- position: node.getStart(),
30740
- end: node.getStart(),
30741
- toInsert: `${text}\n${leadingSpace}`,
30742
- }));
30743
- }
30744
-
30745
- /**
30746
- * Cuts the given string into lines basing around the specified
30747
- * line length limit. This function breaks the string on a per-word basis.
30748
- */
30749
- function cutStringToLineLimit(str, limit) {
30750
- const words = str.split(' ');
30751
- const chunks = [];
30752
- let chunkIdx = 0;
30753
- while (words.length) {
30754
- // New line if we exceed limit.
30755
- if (chunks[chunkIdx] !== undefined && chunks[chunkIdx].length > limit) {
30756
- chunkIdx++;
30757
- }
30758
- // Ensure line is initialized for the given index.
30759
- if (chunks[chunkIdx] === undefined) {
30760
- chunks[chunkIdx] = '';
30761
- }
30762
- const word = words.shift();
30763
- const needsSpace = chunks[chunkIdx].length > 0;
30764
- // Insert word. Add space before, if the line already contains text.
30765
- chunks[chunkIdx] += `${needsSpace ? ' ' : ''}${word}`;
30766
- }
30767
- return chunks;
30768
- }
30769
-
30770
- /**
30771
- * Gets human-readable message information for the given field incompatibility.
30772
- * This text will be used by the language service, or CLI-based migration.
30773
- */
30774
- function getMessageForFieldIncompatibility(reason, fieldName) {
30775
- switch (reason) {
30776
- case exports.FieldIncompatibilityReason.Accessor:
30777
- return {
30778
- short: `Accessor ${fieldName.plural} cannot be migrated as they are too complex.`,
30779
- extra: 'The migration potentially requires usage of `effect` or `computed`, but ' +
30780
- 'the intent is unclear. The migration cannot safely migrate.',
30781
- };
30782
- case exports.FieldIncompatibilityReason.OverriddenByDerivedClass:
30783
- return {
30784
- short: `The ${fieldName.single} cannot be migrated because the field is overridden by a subclass.`,
30785
- extra: 'The field in the subclass is not a signal, so migrating would break your build.',
30786
- };
30787
- case exports.FieldIncompatibilityReason.ParentIsIncompatible:
30788
- return {
30789
- short: `This ${fieldName.single} is inherited from a superclass, but the parent cannot be migrated.`,
30790
- extra: 'Migrating this field would cause your build to fail.',
30791
- };
30792
- case exports.FieldIncompatibilityReason.DerivedIsIncompatible:
30793
- return {
30794
- short: `This ${fieldName.single} cannot be migrated because the field is overridden by a subclass.`,
30795
- extra: 'The field in the subclass is incompatible for migration, so migrating this field would ' +
30796
- 'break your build.',
30797
- };
30798
- case exports.FieldIncompatibilityReason.PotentiallyNarrowedInTemplateButNoSupportYet:
30799
- return {
30800
- short: `This ${fieldName.single} is used in a control flow expression (e.g. \`@if\` or \`*ngIf\`) and ` +
30801
- 'migrating would break narrowing currently.',
30802
- extra: `In the future, Angular intends to support narrowing of signals.`,
30803
- };
30804
- case exports.FieldIncompatibilityReason.RedeclaredViaDerivedClassInputsArray:
30805
- return {
30806
- short: `The ${fieldName.single} is overridden by a subclass that cannot be migrated.`,
30807
- extra: `The subclass overrides this ${fieldName.single} via the \`inputs\` array in @Directive/@Component. ` +
30808
- 'Migrating the field would break your build because the subclass field cannot be a signal.',
30809
- };
30810
- case exports.FieldIncompatibilityReason.SignalInput__RequiredButNoGoodExplicitTypeExtractable:
30811
- return {
30812
- short: `Input is required, but the migration cannot determine a good type for the input.`,
30813
- extra: 'Consider adding an explicit type to make the migration possible.',
30814
- };
30815
- case exports.FieldIncompatibilityReason.SignalInput__QuestionMarkButNoGoodExplicitTypeExtractable:
30816
- return {
30817
- short: `Input is marked with a question mark. Migration could not determine a good type for the input.`,
30818
- extra: 'The migration needs to be able to resolve a type, so that it can include `undefined` in your type. ' +
30819
- 'Consider adding an explicit type to make the migration possible.',
30820
- };
30821
- case exports.FieldIncompatibilityReason.SignalQueries__QueryListProblematicFieldAccessed:
30822
- return {
30823
- short: `There are references to this query that cannot be migrated automatically.`,
30824
- extra: "For example, it's not possible to migrate `.changes` or `.dirty` trivially.",
30825
- };
30826
- case exports.FieldIncompatibilityReason.SignalQueries__IncompatibleMultiUnionType:
30827
- return {
30828
- short: `Query type is too complex to automatically migrate.`,
30829
- extra: "The new query API doesn't allow us to migrate safely without breaking your app.",
30830
- };
30831
- case exports.FieldIncompatibilityReason.SkippedViaConfigFilter:
30832
- return {
30833
- short: `This ${fieldName.single} is not part of the current migration scope.`,
30834
- extra: 'Skipped via migration config.',
30835
- };
30836
- case exports.FieldIncompatibilityReason.SpyOnThatOverwritesField:
30837
- return {
30838
- short: 'A jasmine `spyOn` call spies on this field. This breaks with signals.',
30839
- extra: `Migration cannot safely migrate as "spyOn" writes to the ${fieldName.single}. ` +
30840
- `Signal ${fieldName.plural} are readonly.`,
30841
- };
30842
- case exports.FieldIncompatibilityReason.TypeConflictWithBaseClass:
30843
- return {
30844
- short: `This ${fieldName.single} overrides a field from a superclass, while the superclass ` +
30845
- `field is not migrated.`,
30846
- extra: 'Migrating the field would break your build because of a type conflict.',
30847
- };
30848
- case exports.FieldIncompatibilityReason.WriteAssignment:
30849
- return {
30850
- short: `Your application code writes to the ${fieldName.single}. This prevents migration.`,
30851
- extra: `Signal ${fieldName.plural} are readonly, so migrating would break your build.`,
30852
- };
30853
- case exports.FieldIncompatibilityReason.OutsideOfMigrationScope:
30854
- return {
30855
- short: `This ${fieldName.single} is not part of any source files in your project.`,
30856
- extra: `The migration excludes ${fieldName.plural} if no source file declaring them was seen.`,
30857
- };
30858
- }
30859
- }
30860
- /**
30861
- * Gets human-readable message information for the given class incompatibility.
30862
- * This text will be used by the language service, or CLI-based migration.
30863
- */
30864
- function getMessageForClassIncompatibility(reason, fieldName) {
30865
- switch (reason) {
30866
- case exports.ClassIncompatibilityReason.OwningClassReferencedInClassProperty:
30867
- return {
30868
- short: `Class of this ${fieldName.single} is referenced in the signature of another class.`,
30869
- extra: 'The other class is likely typed to expect a non-migrated field, so ' +
30870
- 'migration is skipped to not break your build.',
30871
- };
30872
- case exports.ClassIncompatibilityReason.ClassManuallyInstantiated:
30873
- return {
30874
- short: `Class of this ${fieldName.single} is manually instantiated. ` +
30875
- 'This is discouraged and prevents migration',
30876
- extra: `Signal ${fieldName.plural} require a DI injection context. Manually instantiating ` +
30877
- 'breaks this requirement in some cases, so the migration is skipped.',
30878
- };
30879
- }
30880
- }
30881
-
30882
- /**
30883
- * Inserts a TODO for the incompatibility blocking the given node
30884
- * from being migrated.
30885
- */
30886
- function insertTodoForIncompatibility(node, programInfo, incompatibility, fieldName) {
30887
- // If a field is skipped via config filter or outside migration scope, do not
30888
- // insert TODOs, as this could results in lots of unnecessary comments.
30889
- if (isFieldIncompatibility(incompatibility) &&
30890
- (incompatibility.reason === exports.FieldIncompatibilityReason.SkippedViaConfigFilter ||
30891
- incompatibility.reason === exports.FieldIncompatibilityReason.OutsideOfMigrationScope)) {
30892
- return [];
30893
- }
30894
- const message = isFieldIncompatibility(incompatibility)
30895
- ? getMessageForFieldIncompatibility(incompatibility.reason, fieldName).short
30896
- : getMessageForClassIncompatibility(incompatibility, fieldName).short;
30897
- const lines = cutStringToLineLimit(message, 70);
30898
- return [
30899
- insertPrecedingLine(node, programInfo, `// TODO: Skipped for migration because:`),
30900
- ...lines.map((line) => insertPrecedingLine(node, programInfo, `// ${line}`)),
30901
- ];
30902
- }
30903
-
30904
- /**
30905
- * Applies import manager changes, and writes them as replacements the
30906
- * given result array.
30907
- */
30908
- function applyImportManagerChanges(importManager, replacements, sourceFiles, info) {
30909
- const { newImports, updatedImports, deletedImports } = importManager.finalize();
30910
- const printer = ts__default["default"].createPrinter({});
30911
- const pathToFile = new Map(sourceFiles.map((s) => [s.fileName, s]));
30912
- // Capture new imports
30913
- newImports.forEach((newImports, fileName) => {
30914
- newImports.forEach((newImport) => {
30915
- const printedImport = printer.printNode(ts__default["default"].EmitHint.Unspecified, newImport, pathToFile.get(fileName));
30916
- replacements.push(new Replacement(projectFile(checker.absoluteFrom(fileName), info), new TextUpdate({ position: 0, end: 0, toInsert: `${printedImport}\n` })));
30917
- });
30918
- });
30919
- // Capture updated imports
30920
- for (const [oldBindings, newBindings] of updatedImports.entries()) {
30921
- // The import will be generated as multi-line if it already is multi-line,
30922
- // or if the number of elements significantly increased and it previously
30923
- // consisted of very few specifiers.
30924
- const isMultiline = oldBindings.getText().includes('\n') ||
30925
- (newBindings.elements.length >= 6 && oldBindings.elements.length <= 3);
30926
- const hasSpaceBetweenBraces = oldBindings.getText().startsWith('{ ');
30927
- let formatFlags = ts__default["default"].ListFormat.NamedImportsOrExportsElements |
30928
- ts__default["default"].ListFormat.Indented |
30929
- ts__default["default"].ListFormat.Braces |
30930
- ts__default["default"].ListFormat.PreserveLines |
30931
- (isMultiline ? ts__default["default"].ListFormat.MultiLine : ts__default["default"].ListFormat.SingleLine);
30932
- if (hasSpaceBetweenBraces) {
30933
- formatFlags |= ts__default["default"].ListFormat.SpaceBetweenBraces;
30934
- }
30935
- else {
30936
- formatFlags &= ~ts__default["default"].ListFormat.SpaceBetweenBraces;
30937
- }
30938
- const printedBindings = printer.printList(formatFlags, newBindings.elements, oldBindings.getSourceFile());
30939
- replacements.push(new Replacement(projectFile(oldBindings.getSourceFile(), info), new TextUpdate({
30940
- position: oldBindings.getStart(),
30941
- end: oldBindings.getEnd(),
30942
- // TS uses four spaces as indent. We migrate to two spaces as we
30943
- // assume this to be more common.
30944
- toInsert: printedBindings.replace(/^ {4}/gm, ' '),
30945
- })));
30946
- }
30947
- // Update removed imports
30948
- for (const removedImport of deletedImports) {
30949
- replacements.push(new Replacement(projectFile(removedImport.getSourceFile(), info), new TextUpdate({
30950
- position: removedImport.getStart(),
30951
- end: removedImport.getEnd(),
30952
- toInsert: '',
30953
- })));
30954
- }
30955
- }
30956
-
30957
- /** Whether the given node is a descendant of the given ancestor. */
30958
- function isNodeDescendantOf(node, ancestor) {
30959
- while (node) {
30960
- if (node === ancestor)
30961
- return true;
30962
- node = node.parent;
30963
- }
30964
- return false;
30965
- }
30966
-
30967
- /** Symbol that can be used to mark a variable as reserved, synthetically. */
30968
- const ReservedMarker = Symbol();
30969
- /**
30970
- * Gets whether the given identifier name is free for use in the
30971
- * given location, avoiding shadowed variable names.
30972
- *
30973
- */
30974
- function isIdentifierFreeInScope(name, location) {
30975
- const startContainer = findClosestParentLocalsContainer(location);
30976
- assert__default["default"](startContainer !== undefined, 'Expecting a locals container.');
30977
- // Traverse up and check for potential collisions.
30978
- let container = startContainer;
30979
- let firstNextContainer = undefined;
30980
- while (container !== undefined) {
30981
- if (!isIdentifierFreeInContainer(name, container)) {
30982
- return null;
30983
- }
30984
- if (firstNextContainer === undefined && container.nextContainer !== undefined) {
30985
- firstNextContainer = container.nextContainer;
30986
- }
30987
- container = findClosestParentLocalsContainer(container.parent);
30988
- }
30989
- // Check descendent local containers to avoid shadowing variables.
30990
- // Note that this is not strictly needed, but it's helping avoid
30991
- // some lint errors, like TSLint's no shadowed variables.
30992
- container = firstNextContainer;
30993
- while (container && isNodeDescendantOf(container, startContainer)) {
30994
- if (!isIdentifierFreeInContainer(name, container)) {
30995
- return null;
30996
- }
30997
- container = container.nextContainer;
30998
- }
30999
- return { container: startContainer };
31000
- }
31001
- /** Finds the closest parent locals container. */
31002
- function findClosestParentLocalsContainer(node) {
31003
- return ts__default["default"].findAncestor(node, isLocalsContainer);
31004
- }
31005
- /** Whether the given identifier is free in the given locals container. */
31006
- function isIdentifierFreeInContainer(name, container) {
31007
- if (container.locals === undefined || !container.locals.has(name)) {
31008
- return true;
31009
- }
31010
- // We consider alias symbols as locals conservatively.
31011
- // Note: This check is similar to the check by the TypeScript emitter.
31012
- // typescript/stable/src/compiler/emitter.ts;l=5436;rcl=651008033
31013
- const local = container.locals.get(name);
31014
- return (local !== ReservedMarker &&
31015
- !(local.flags & (ts__default["default"].SymbolFlags.Value | ts__default["default"].SymbolFlags.ExportValue | ts__default["default"].SymbolFlags.Alias)));
31016
- }
31017
- /**
31018
- * Whether the given node can contain local variables.
31019
- *
31020
- * Note: This is similar to TypeScript's `canHaveLocals` internal helper.
31021
- * typescript/stable/src/compiler/utilitiesPublic.ts;l=2265;rcl=651008033
31022
- */
31023
- function isLocalsContainer(node) {
31024
- switch (node.kind) {
31025
- case ts__default["default"].SyntaxKind.ArrowFunction:
31026
- case ts__default["default"].SyntaxKind.Block:
31027
- case ts__default["default"].SyntaxKind.CallSignature:
31028
- case ts__default["default"].SyntaxKind.CaseBlock:
31029
- case ts__default["default"].SyntaxKind.CatchClause:
31030
- case ts__default["default"].SyntaxKind.ClassStaticBlockDeclaration:
31031
- case ts__default["default"].SyntaxKind.ConditionalType:
31032
- case ts__default["default"].SyntaxKind.Constructor:
31033
- case ts__default["default"].SyntaxKind.ConstructorType:
31034
- case ts__default["default"].SyntaxKind.ConstructSignature:
31035
- case ts__default["default"].SyntaxKind.ForStatement:
31036
- case ts__default["default"].SyntaxKind.ForInStatement:
31037
- case ts__default["default"].SyntaxKind.ForOfStatement:
31038
- case ts__default["default"].SyntaxKind.FunctionDeclaration:
31039
- case ts__default["default"].SyntaxKind.FunctionExpression:
31040
- case ts__default["default"].SyntaxKind.FunctionType:
31041
- case ts__default["default"].SyntaxKind.GetAccessor:
31042
- case ts__default["default"].SyntaxKind.IndexSignature:
31043
- case ts__default["default"].SyntaxKind.JSDocCallbackTag:
31044
- case ts__default["default"].SyntaxKind.JSDocEnumTag:
31045
- case ts__default["default"].SyntaxKind.JSDocFunctionType:
31046
- case ts__default["default"].SyntaxKind.JSDocSignature:
31047
- case ts__default["default"].SyntaxKind.JSDocTypedefTag:
31048
- case ts__default["default"].SyntaxKind.MappedType:
31049
- case ts__default["default"].SyntaxKind.MethodDeclaration:
31050
- case ts__default["default"].SyntaxKind.MethodSignature:
31051
- case ts__default["default"].SyntaxKind.ModuleDeclaration:
31052
- case ts__default["default"].SyntaxKind.SetAccessor:
31053
- case ts__default["default"].SyntaxKind.SourceFile:
31054
- case ts__default["default"].SyntaxKind.TypeAliasDeclaration:
31055
- return true;
31056
- default:
31057
- return false;
31058
- }
31059
- }
31060
-
31061
- /**
31062
- * Helper that can generate unique identifier names at a
31063
- * given location.
31064
- *
31065
- * Used for generating unique names to extract input reads
31066
- * to support narrowing.
31067
- */
31068
- class UniqueNamesGenerator {
31069
- constructor(fallbackSuffixes) {
31070
- this.fallbackSuffixes = fallbackSuffixes;
31071
- }
31072
- generate(base, location) {
31073
- const checkNameAndClaimIfAvailable = (name) => {
31074
- const freeInfo = isIdentifierFreeInScope(name, location);
31075
- if (freeInfo === null) {
31076
- return false;
31077
- }
31078
- // Claim the locals to avoid conflicts with future generations.
31079
- freeInfo.container.locals ??= new Map();
31080
- freeInfo.container.locals.set(name, ReservedMarker);
31081
- return true;
31082
- };
31083
- // Check the base name. Ideally, we'd use this one.
31084
- if (checkNameAndClaimIfAvailable(base)) {
31085
- return base;
31086
- }
31087
- // Try any of the possible suffixes.
31088
- for (const suffix of this.fallbackSuffixes) {
31089
- const name = `${base}${suffix}`;
31090
- if (checkNameAndClaimIfAvailable(name)) {
31091
- return name;
31092
- }
31093
- }
31094
- // Worst case, suffix the base name with a unique number until
31095
- // we find an available name.
31096
- let name = null;
31097
- let counter = 1;
31098
- do {
31099
- name = `${base}_${counter++}`;
31100
- } while (!checkNameAndClaimIfAvailable(name));
31101
- return name;
31102
- }
31103
- }
31104
-
31105
- /**
31106
- * Creates replacements to insert the given statement as
31107
- * first statement into the arrow function.
31108
- *
31109
- * The arrow function is converted to a block-based arrow function
31110
- * that can hold multiple statements. The original expression is
31111
- * simply returned like before.
31112
- */
31113
- function createNewBlockToInsertVariable(node, file, toInsert) {
31114
- const sf = node.getSourceFile();
31115
- // For indentation, we traverse up and find the earliest statement.
31116
- // This node is most of the time a good candidate for acceptable
31117
- // indentation of a new block.
31118
- const spacingNode = ts__default["default"].findAncestor(node, ts__default["default"].isStatement) ?? node.parent;
31119
- const { character } = ts__default["default"].getLineAndCharacterOfPosition(sf, spacingNode.getStart());
31120
- const blockSpace = ' '.repeat(character);
31121
- const contentSpace = ' '.repeat(character + 2);
31122
- return [
31123
- // Delete leading whitespace of the concise body.
31124
- new Replacement(file, new TextUpdate({
31125
- position: node.body.getFullStart(),
31126
- end: node.body.getStart(),
31127
- toInsert: '',
31128
- })),
31129
- // Insert leading block braces, and `toInsert` content.
31130
- // Wrap the previous expression in a return now.
31131
- new Replacement(file, new TextUpdate({
31132
- position: node.body.getStart(),
31133
- end: node.body.getStart(),
31134
- toInsert: ` {\n${contentSpace}${toInsert}\n${contentSpace}return `,
31135
- })),
31136
- // Add trailing brace.
31137
- new Replacement(file, new TextUpdate({
31138
- position: node.body.getEnd(),
31139
- end: node.body.getEnd(),
31140
- toInsert: `;\n${blockSpace}}`,
31141
- })),
31142
- ];
31143
- }
31144
-
31145
- /**
31146
- * Migrates a binding element that refers to an Angular input.
31147
- *
31148
- * E.g. `const {myInput} = this`.
31149
- *
31150
- * For references in binding elements, we extract the element into a variable
31151
- * where we unwrap the input. This ensures narrowing naturally works in subsequent
31152
- * places, and we also don't need to detect potential aliases.
31153
- *
31154
- * ```ts
31155
- * const {myInput} = this;
31156
- * // turns into
31157
- * const {myInput: myInputValue} = this;
31158
- * const myInput = myInputValue();
31159
- * ```
31160
- */
31161
- function migrateBindingElementInputReference(tsReferencesInBindingElements, info, replacements, printer) {
31162
- const nameGenerator = new UniqueNamesGenerator(['Input', 'Signal', 'Ref']);
31163
- for (const reference of tsReferencesInBindingElements) {
31164
- const bindingElement = reference.parent;
31165
- const bindingDecl = getBindingElementDeclaration(bindingElement);
31166
- const sourceFile = bindingElement.getSourceFile();
31167
- const file = projectFile(sourceFile, info);
31168
- const inputFieldName = bindingElement.propertyName ?? bindingElement.name;
31169
- assert__default["default"](!ts__default["default"].isObjectBindingPattern(inputFieldName) && !ts__default["default"].isArrayBindingPattern(inputFieldName), 'Property of binding element cannot be another pattern.');
31170
- const tmpName = nameGenerator.generate(reference.text, bindingElement);
31171
- // Only use the temporary name, if really needed. A temporary name is needed if
31172
- // the input field simply aliased via the binding element, or if the exposed identifier
31173
- // is a string-literal like.
31174
- const useTmpNameForInputField = !ts__default["default"].isObjectBindingPattern(bindingElement.name) || !ts__default["default"].isIdentifier(inputFieldName);
31175
- const propertyName = useTmpNameForInputField ? inputFieldName : undefined;
31176
- const exposedName = useTmpNameForInputField
31177
- ? ts__default["default"].factory.createIdentifier(tmpName)
31178
- : inputFieldName;
31179
- const newBindingToAccessInputField = ts__default["default"].factory.updateBindingElement(bindingElement, bindingElement.dotDotDotToken, propertyName, exposedName, bindingElement.initializer);
31180
- const temporaryVariableReplacements = insertTemporaryVariableForBindingElement(bindingDecl, file, `const ${bindingElement.name.getText()} = ${exposedName.text}();`);
31181
- if (temporaryVariableReplacements === null) {
31182
- console.error(`Could not migrate reference ${reference.text} in ${file.rootRelativePath}`);
31183
- continue;
31184
- }
31185
- replacements.push(new Replacement(file, new TextUpdate({
31186
- position: bindingElement.getStart(),
31187
- end: bindingElement.getEnd(),
31188
- toInsert: printer.printNode(ts__default["default"].EmitHint.Unspecified, newBindingToAccessInputField, sourceFile),
31189
- })), ...temporaryVariableReplacements);
31190
- }
31191
- }
31192
- /**
31193
- * Inserts the given code snippet after the given variable or
31194
- * parameter declaration.
31195
- *
31196
- * If this is a parameter of an arrow function, a block may be
31197
- * added automatically.
31198
- */
31199
- function insertTemporaryVariableForBindingElement(expansionDecl, file, toInsert) {
31200
- const sf = expansionDecl.getSourceFile();
31201
- const parent = expansionDecl.parent;
31202
- // The snippet is simply inserted after the variable declaration.
31203
- // The other case of a variable declaration inside a catch clause is handled
31204
- // below.
31205
- if (ts__default["default"].isVariableDeclaration(expansionDecl) && ts__default["default"].isVariableDeclarationList(parent)) {
31206
- const leadingSpaceCount = ts__default["default"].getLineAndCharacterOfPosition(sf, parent.getStart()).character;
31207
- const leadingSpace = ' '.repeat(leadingSpaceCount);
31208
- const statement = parent.parent;
31209
- return [
31210
- new Replacement(file, new TextUpdate({
31211
- position: statement.getEnd(),
31212
- end: statement.getEnd(),
31213
- toInsert: `\n${leadingSpace}${toInsert}`,
31214
- })),
31215
- ];
31216
- }
31217
- // If we are dealing with a object expansion inside a parameter of
31218
- // a function-like declaration w/ block, add the variable as the first
31219
- // node inside the block.
31220
- const bodyBlock = getBodyBlockOfNode(parent);
31221
- if (bodyBlock !== null) {
31222
- const firstElementInBlock = bodyBlock.statements[0];
31223
- const spaceReferenceNode = firstElementInBlock ?? bodyBlock;
31224
- const spaceOffset = firstElementInBlock !== undefined ? 0 : 2;
31225
- const leadingSpaceCount = ts__default["default"].getLineAndCharacterOfPosition(sf, spaceReferenceNode.getStart()).character + spaceOffset;
31226
- const leadingSpace = ' '.repeat(leadingSpaceCount);
31227
- return [
31228
- new Replacement(file, new TextUpdate({
31229
- position: bodyBlock.getStart() + 1,
31230
- end: bodyBlock.getStart() + 1,
31231
- toInsert: `\n${leadingSpace}${toInsert}`,
31232
- })),
31233
- ];
31234
- }
31235
- // Other cases where we see an arrow function without a block.
31236
- // We need to create one now.
31237
- if (ts__default["default"].isArrowFunction(parent) && !ts__default["default"].isBlock(parent.body)) {
31238
- return createNewBlockToInsertVariable(parent, file, toInsert);
31239
- }
31240
- return null;
31241
- }
31242
- /** Gets the body block of a given node, if available. */
31243
- function getBodyBlockOfNode(node) {
31244
- if ((ts__default["default"].isMethodDeclaration(node) ||
31245
- ts__default["default"].isFunctionDeclaration(node) ||
31246
- ts__default["default"].isGetAccessorDeclaration(node) ||
31247
- ts__default["default"].isConstructorDeclaration(node) ||
31248
- ts__default["default"].isArrowFunction(node)) &&
31249
- node.body !== undefined &&
31250
- ts__default["default"].isBlock(node.body)) {
31251
- return node.body;
31252
- }
31253
- if (ts__default["default"].isCatchClause(node.parent)) {
31254
- return node.parent.block;
31255
- }
31256
- return null;
31257
- }
31258
-
31259
- /**
31260
- * Whether the given node represents a control flow container boundary.
31261
- * E.g. variables cannot be narrowed when descending into children of `node`.
31262
- */
31263
- function isControlFlowBoundary(node) {
31264
- return ((ts__default["default"].isFunctionLike(node) && !getImmediatelyInvokedFunctionExpression(node)) ||
31265
- node.kind === ts__default["default"].SyntaxKind.ModuleBlock ||
31266
- node.kind === ts__default["default"].SyntaxKind.SourceFile ||
31267
- node.kind === ts__default["default"].SyntaxKind.PropertyDeclaration);
31268
- }
31269
- /** Determines the current flow container of a given node. */
31270
- function getControlFlowContainer(node) {
31271
- return ts__default["default"].findAncestor(node.parent, (node) => isControlFlowBoundary(node));
31272
- }
31273
- /** Checks whether the given node refers to an IIFE declaration. */
31274
- function getImmediatelyInvokedFunctionExpression(func) {
31275
- if (func.kind === ts__default["default"].SyntaxKind.FunctionExpression || func.kind === ts__default["default"].SyntaxKind.ArrowFunction) {
31276
- let prev = func;
31277
- let parent = func.parent;
31278
- while (parent.kind === ts__default["default"].SyntaxKind.ParenthesizedExpression) {
31279
- prev = parent;
31280
- parent = parent.parent;
31281
- }
31282
- if (parent.kind === ts__default["default"].SyntaxKind.CallExpression &&
31283
- parent.expression === prev) {
31284
- return parent;
31285
- }
31286
- }
31287
- return undefined;
31288
- }
31289
-
31290
- /** @internal */
31291
- var FlowFlags;
31292
- (function (FlowFlags) {
31293
- FlowFlags[FlowFlags["Unreachable"] = 1] = "Unreachable";
31294
- FlowFlags[FlowFlags["Start"] = 2] = "Start";
31295
- FlowFlags[FlowFlags["BranchLabel"] = 4] = "BranchLabel";
31296
- FlowFlags[FlowFlags["LoopLabel"] = 8] = "LoopLabel";
31297
- FlowFlags[FlowFlags["Assignment"] = 16] = "Assignment";
31298
- FlowFlags[FlowFlags["TrueCondition"] = 32] = "TrueCondition";
31299
- FlowFlags[FlowFlags["FalseCondition"] = 64] = "FalseCondition";
31300
- FlowFlags[FlowFlags["SwitchClause"] = 128] = "SwitchClause";
31301
- FlowFlags[FlowFlags["ArrayMutation"] = 256] = "ArrayMutation";
31302
- FlowFlags[FlowFlags["Call"] = 512] = "Call";
31303
- FlowFlags[FlowFlags["ReduceLabel"] = 1024] = "ReduceLabel";
31304
- FlowFlags[FlowFlags["Referenced"] = 2048] = "Referenced";
31305
- FlowFlags[FlowFlags["Shared"] = 4096] = "Shared";
31306
- FlowFlags[FlowFlags["Label"] = 12] = "Label";
31307
- FlowFlags[FlowFlags["Condition"] = 96] = "Condition";
31308
- })(FlowFlags || (FlowFlags = {}));
31309
-
31310
- /**
31311
- * Traverses the graph of the TypeScript flow nodes, exploring all possible branches
31312
- * and keeps track of interesting nodes that may contribute to "narrowing".
31313
- *
31314
- * This allows us to figure out which nodes may be narrowed or not, and need
31315
- * temporary variables in the migration to allowing narrowing to continue working.
31316
- *
31317
- * Some resources on flow nodes by TypeScript:
31318
- * https://effectivetypescript.com/2024/03/24/flownodes/.
31319
- */
31320
- function traverseFlowForInterestingNodes(flow) {
31321
- let flowDepth = 0;
31322
- let interestingNodes = [];
31323
- const queue = new Set([flow]);
31324
- // Queue is evolved during iteration, and new items will be added
31325
- // to the end of the iteration. Effectively implementing a queue
31326
- // with deduping out of the box.
31327
- for (const flow of queue) {
31328
- if (++flowDepth === 2000) {
31329
- // We have made 2000 recursive invocations. To avoid overflowing the call stack we report an
31330
- // error and disable further control flow analysis in the containing function or module body.
31331
- return interestingNodes;
31332
- }
31333
- const flags = flow.flags;
31334
- if (flags & FlowFlags.Assignment) {
31335
- const assignment = flow;
31336
- queue.add(assignment.antecedent);
31337
- if (ts__default["default"].isVariableDeclaration(assignment.node)) {
31338
- interestingNodes.push(assignment.node.name);
31339
- }
31340
- else if (ts__default["default"].isBindingElement(assignment.node)) {
31341
- interestingNodes.push(assignment.node.name);
31342
- }
31343
- else {
31344
- interestingNodes.push(assignment.node);
31345
- }
31346
- }
31347
- else if (flags & FlowFlags.Call) {
31348
- queue.add(flow.antecedent);
31349
- // Arguments can be narrowed using `FlowCall`s.
31350
- // See: node_modules/typescript/stable/src/compiler/checker.ts;l=28786-28810
31351
- interestingNodes.push(...flow.node.arguments);
31352
- }
31353
- else if (flags & FlowFlags.Condition) {
31354
- queue.add(flow.antecedent);
31355
- interestingNodes.push(flow.node);
31356
- }
31357
- else if (flags & FlowFlags.SwitchClause) {
31358
- queue.add(flow.antecedent);
31359
- // The switch expression can be narrowed, so it's an interesting node.
31360
- interestingNodes.push(flow.node.switchStatement.expression);
31361
- }
31362
- else if (flags & FlowFlags.Label) {
31363
- // simple label, a single ancestor.
31364
- if (flow.antecedent?.length === 1) {
31365
- queue.add(flow.antecedent[0]);
31366
- continue;
31367
- }
31368
- if (flags & FlowFlags.BranchLabel) {
31369
- // Normal branches. e.g. switch.
31370
- for (const f of flow.antecedent ?? []) {
31371
- queue.add(f);
31372
- }
31373
- }
31374
- else {
31375
- // Branch for loops.
31376
- // The first antecedent always points to the flow node before the loop
31377
- // was entered. All other narrowing expressions, if present, are direct
31378
- // antecedents of the starting flow node, so we only need to look at the first.
31379
- // See: node_modules/typescript/stable/src/compiler/checker.ts;l=28108-28109
31380
- queue.add(flow.antecedent[0]);
31381
- }
31382
- }
31383
- else if (flags & FlowFlags.ArrayMutation) {
31384
- queue.add(flow.antecedent);
31385
- // Array mutations are never interesting for inputs, as we cannot migrate
31386
- // assignments to inputs.
31387
- }
31388
- else if (flags & FlowFlags.ReduceLabel) {
31389
- // reduce label is a try/catch re-routing.
31390
- // visit all possible branches.
31391
- // TODO: explore this more.
31392
- // See: node_modules/typescript/stable/src/compiler/binder.ts;l=1636-1649.
31393
- queue.add(flow.antecedent);
31394
- for (const f of flow.node.antecedents) {
31395
- queue.add(f);
31396
- }
31397
- }
31398
- else if (flags & FlowFlags.Start) {
31399
- // Note: TS itself only ever continues with parent control flows, if the pre-determined `flowContainer`
31400
- // of the referenced is different. E.g. narrowing might decide to choose a higher flow container if we
31401
- // reference a constant. In which case, TS allows escaping the flow container for narrowing. See:
31402
- // http://google3/third_party/javascript/node_modules/typescript/stable/src/compiler/checker.ts;l=29399-29414;rcl=623599846.
31403
- // and TypeScript's `narrowedConstInMethod` baseline test.
31404
- // --> We don't need this as an input cannot be a constant!
31405
- return interestingNodes;
31406
- }
31407
- else {
31408
- break;
31409
- }
31410
- }
31411
- return null;
31412
- }
31413
- /** Gets the flow node for the given node. */
31414
- function getFlowNode(node) {
31415
- return node.flowNode ?? null;
31416
- }
31417
-
31418
- /**
31419
- * Analyzes the control flow of a list of references and returns
31420
- * information about which nodes can be shared via a temporary variable
31421
- * to enable narrowing.
31422
- *
31423
- * E.g. consider the following snippet:
31424
- *
31425
- * ```
31426
- * someMethod() {
31427
- * if (this.bla) {
31428
- * this.bla.charAt(0);
31429
- * }
31430
- * }
31431
- * ```
31432
- *
31433
- * The analysis would inform the caller that `this.bla.charAt` can
31434
- * be shared with the `this.bla` of the `if` condition.
31435
- *
31436
- * This is useful for the signal migration as it allows us to efficiently,
31437
- * and minimally transform references into shared variables where needed.
31438
- * Needed because signals are not narrowable by default, as they are functions.
31439
- */
31440
- function analyzeControlFlow(entries, checker) {
31441
- const result = [];
31442
- const referenceToMetadata = new Map();
31443
- // Prepare easy lookups for reference nodes to flow info.
31444
- for (const [idx, entry] of entries.entries()) {
31445
- referenceToMetadata.set(entry, {
31446
- flowContainer: getControlFlowContainer(entry),
31447
- resultIndex: idx,
31448
- });
31449
- }
31450
- for (const entry of entries) {
31451
- const { flowContainer, resultIndex } = referenceToMetadata.get(entry);
31452
- const flowPathInterestingNodes = traverseFlowForInterestingNodes(getFlowNode(entry));
31453
- assert__default["default"](flowContainer !== null && flowPathInterestingNodes !== null, 'Expected a flow container to exist.');
31454
- const narrowPartners = getAllMatchingReferencesInFlowPath(flowPathInterestingNodes, entry, referenceToMetadata, flowContainer, checker);
31455
- result.push({
31456
- id: resultIndex,
31457
- originalNode: entry,
31458
- flowContainer,
31459
- recommendedNode: 'preserve',
31460
- });
31461
- if (narrowPartners.length !== 0) {
31462
- connectSharedReferences(result, narrowPartners, resultIndex);
31463
- }
31464
- }
31465
- return result;
31466
- }
31467
- /**
31468
- * Iterates through all partner flow nodes and connects them so that
31469
- * the first node will act as the share partner, while all subsequent
31470
- * nodes will point to the share node.
31471
- */
31472
- function connectSharedReferences(result, flowPartners, refId) {
31473
- const refFlowContainer = result[refId].flowContainer;
31474
- // Inside the list of flow partners (i.e. references to the same target),
31475
- // find the node that is the first one in the flow container (via its start pos).
31476
- let earliestPartner = null;
31477
- let earliestPartnerId = null;
31478
- for (const partnerId of flowPartners) {
31479
- if (earliestPartner === null ||
31480
- result[partnerId].originalNode.getStart() < earliestPartner.getStart()) {
31481
- earliestPartner = result[partnerId].originalNode;
31482
- earliestPartnerId = partnerId;
31483
- }
31484
- }
31485
- assert__default["default"](earliestPartner !== null, 'Expected an earliest partner to be found.');
31486
- assert__default["default"](earliestPartnerId !== null, 'Expected an earliest partner to be found.');
31487
- // Then, incorporate all similar references (or flow nodes) in between
31488
- // the reference and the earliest partner. References in between can also
31489
- // use the shared flow node and not preserve their original reference— as
31490
- // this would be rather unreadable and inefficient.
31491
- let highestBlock = null;
31492
- for (let i = earliestPartnerId; i <= refId; i++) {
31493
- // Different flow container captured sequentially in result. Ignore.
31494
- if (result[i].flowContainer !== refFlowContainer) {
31495
- continue;
31496
- }
31497
- // Iterate up the block, find the highest block within the flow container.
31498
- let block = result[i].originalNode.parent;
31499
- while (!isBlockLikeAncestor(block)) {
31500
- block = block.parent;
31501
- }
31502
- if (highestBlock === null || block.getStart() < highestBlock.getStart()) {
31503
- highestBlock = block;
31504
- }
31505
- if (i !== earliestPartnerId) {
31506
- result[i].recommendedNode = earliestPartnerId;
31507
- }
31508
- }
31509
- assert__default["default"](highestBlock, 'Expected a block anchor to be found');
31510
- result[earliestPartnerId].recommendedNode = highestBlock;
31511
- }
31512
- function isBlockLikeAncestor(node) {
31513
- // Note: Arrow functions may not have a block, but instead use an expression
31514
- // directly. This still signifies a "block" as we can convert the concise body
31515
- // to a block.
31516
- return ts__default["default"].isSourceFile(node) || ts__default["default"].isBlock(node) || ts__default["default"].isArrowFunction(node);
31517
- }
31518
- /**
31519
- * Looks through the flow path and interesting nodes to determine which
31520
- * of the potential "interesting" nodes point to the same reference.
31521
- *
31522
- * These nodes are then considered "partners" and will be returned via
31523
- * their IDs (or practically their result indices).
31524
- */
31525
- function getAllMatchingReferencesInFlowPath(flowPathInterestingNodes, reference, referenceToMetadata, restrainingFlowContainer, checker) {
31526
- const partners = [];
31527
- for (const flowNode of flowPathInterestingNodes) {
31528
- // quick naive perf-optimized check to see if the flow node has a potential
31529
- // similar reference.
31530
- if (!flowNode.getText().includes(reference.getText())) {
31531
- continue;
31532
- }
31533
- const similarRefNodeId = findSimilarReferenceNode(flowNode, reference, referenceToMetadata, restrainingFlowContainer, checker);
31534
- if (similarRefNodeId !== null) {
31535
- partners.push(similarRefNodeId);
31536
- }
31537
- }
31538
- return partners;
31539
- }
31540
- /**
31541
- * Checks if the given node contains an identifier that
31542
- * matches the given reference. If so, returns its flow ID.
31543
- */
31544
- function findSimilarReferenceNode(start, reference, referenceToMetadata, restrainingFlowContainer, checker) {
31545
- return (ts__default["default"].forEachChild(start, function visitChild(node) {
31546
- // do not descend into control flow boundaries.
31547
- // only references sharing the same container are relevant.
31548
- // This is a performance optimization.
31549
- if (isControlFlowBoundary(node)) {
31550
- return;
31551
- }
31552
- // If this is not a potential matching identifier, check its children.
31553
- if (!ts__default["default"].isIdentifier(node) ||
31554
- referenceToMetadata.get(node)?.flowContainer !== restrainingFlowContainer) {
31555
- return ts__default["default"].forEachChild(node, visitChild);
31556
- }
31557
- // If this refers to a different instantiation of the input reference,
31558
- // continue looking.
31559
- if (!isLexicalSameReference(checker, node, reference)) {
31560
- return;
31561
- }
31562
- return { idx: referenceToMetadata.get(node).resultIndex };
31563
- })?.idx ?? null);
31564
- }
31565
- /**
31566
- * Checks whether a given identifier is lexically equivalent.
31567
- * e.g. checks that they have similar property receiver accesses.
31568
- */
31569
- function isLexicalSameReference(checker, sharePartner, reference) {
31570
- const aParent = unwrapParent(reference.parent);
31571
- // If the reference is not part a property access, return true. The references
31572
- // are guaranteed symbol matches.
31573
- if (!ts__default["default"].isPropertyAccessExpression(aParent) && !ts__default["default"].isElementAccessExpression(aParent)) {
31574
- return sharePartner.text === reference.text;
31575
- }
31576
- // If reference parent is part of a property expression, but the share
31577
- // partner not, then this cannot be shared.
31578
- const bParent = unwrapParent(sharePartner.parent);
31579
- if (aParent.kind !== bParent.kind) {
31580
- return false;
31581
- }
31582
- const aParentExprSymbol = checker.getSymbolAtLocation(aParent.expression);
31583
- const bParentExprSymbol = checker.getSymbolAtLocation(bParent.expression);
31584
- return aParentExprSymbol === bParentExprSymbol;
31585
- }
31586
-
31587
- function migrateStandardTsReference(tsReferencesWithNarrowing, checker, info, replacements) {
31588
- const nameGenerator = new UniqueNamesGenerator(['Value', 'Val', 'Input']);
31589
- // TODO: Consider checking/properly handling optional chaining and narrowing.
31590
- for (const reference of tsReferencesWithNarrowing.values()) {
31591
- const controlFlowResult = analyzeControlFlow(reference.accesses, checker);
31592
- const idToSharedField = new Map();
31593
- for (const { id, originalNode, recommendedNode } of controlFlowResult) {
31594
- const sf = originalNode.getSourceFile();
31595
- // Original node is preserved. No narrowing, and hence not shared.
31596
- // Unwrap the signal directly.
31597
- if (recommendedNode === 'preserve') {
31598
- // Append `()` to unwrap the signal.
31599
- replacements.push(new Replacement(projectFile(sf, info), new TextUpdate({
31600
- position: originalNode.getEnd(),
31601
- end: originalNode.getEnd(),
31602
- toInsert: '()',
31603
- })));
31604
- continue;
31605
- }
31606
- // This reference is shared with a previous reference. Replace the access
31607
- // with the temporary variable.
31608
- if (typeof recommendedNode === 'number') {
31609
- const replaceNode = traverseAccess(originalNode);
31610
- replacements.push(new Replacement(projectFile(sf, info), new TextUpdate({
31611
- position: replaceNode.getStart(),
31612
- end: replaceNode.getEnd(),
31613
- // Extract the shared field name.
31614
- toInsert: idToSharedField.get(recommendedNode),
31615
- })));
31616
- continue;
31617
- }
31618
- // Otherwise, we are creating a "shared reference" at the given node and
31619
- // block.
31620
- // Iterate up the original node, until we hit the "recommended block" level.
31621
- // We then use the previous child as anchor for inserting. This allows us
31622
- // to insert right before the first reference in the container, at the proper
31623
- // block level— instead of always inserting at the beginning of the container.
31624
- let parent = originalNode.parent;
31625
- let referenceNodeInBlock = originalNode;
31626
- while (parent !== recommendedNode) {
31627
- referenceNodeInBlock = parent;
31628
- parent = parent.parent;
31629
- }
31630
- const replaceNode = traverseAccess(originalNode);
31631
- const fieldName = nameGenerator.generate(originalNode.text, referenceNodeInBlock);
31632
- const filePath = projectFile(sf, info);
31633
- const temporaryVariableStr = `const ${fieldName} = ${replaceNode.getText()}();`;
31634
- idToSharedField.set(id, fieldName);
31635
- // If the common ancestor block of all shared references is an arrow function
31636
- // without a block, convert the arrow function to a block and insert the temporary
31637
- // variable at the beginning.
31638
- if (ts__default["default"].isArrowFunction(parent) && !ts__default["default"].isBlock(parent.body)) {
31639
- replacements.push(...createNewBlockToInsertVariable(parent, filePath, temporaryVariableStr));
31640
- }
31641
- else {
31642
- const leadingSpace = ts__default["default"].getLineAndCharacterOfPosition(sf, referenceNodeInBlock.getStart());
31643
- replacements.push(new Replacement(filePath, new TextUpdate({
31644
- position: referenceNodeInBlock.getStart(),
31645
- end: referenceNodeInBlock.getStart(),
31646
- toInsert: `${temporaryVariableStr}\n${' '.repeat(leadingSpace.character)}`,
31647
- })));
31648
- }
31649
- replacements.push(new Replacement(projectFile(sf, info), new TextUpdate({
31650
- position: replaceNode.getStart(),
31651
- end: replaceNode.getEnd(),
31652
- toInsert: fieldName,
31653
- })));
31654
- }
31655
- }
31656
- }
31657
-
31658
- /**
31659
- * Migrates TypeScript input references to be signal compatible.
31660
- *
31661
- * The phase takes care of control flow analysis and generates temporary variables
31662
- * where needed to ensure narrowing continues to work. E.g.
31663
- *
31664
- * ```
31665
- * someMethod() {
31666
- * if (this.input) {
31667
- * this.input.charAt(0);
31668
- * }
31669
- * }
31670
- * ```
31671
- *
31672
- * will be transformed into:
31673
- *
31674
- * ```
31675
- * someMethod() {
31676
- * const input_1 = this.input();
31677
- * if (input_1) {
31678
- * input_1.charAt(0);
31679
- * }
31680
- * }
31681
- * ```
31682
- */
31683
- function migrateTypeScriptReferences(host, references, checker, info) {
31684
- const tsReferencesWithNarrowing = new Map();
31685
- const tsReferencesInBindingElements = new Set();
31686
- const seenIdentifiers = new WeakSet();
31687
- for (const reference of references) {
31688
- // This pass only deals with TS references.
31689
- if (!isTsReference(reference)) {
31690
- continue;
31691
- }
31692
- // Skip references to incompatible inputs.
31693
- if (!host.shouldMigrateReferencesToField(reference.target)) {
31694
- continue;
31695
- }
31696
- // Never attempt to migrate write references.
31697
- // Those usually invalidate the target input most of the time, but in
31698
- // best-effort mode they are not.
31699
- if (reference.from.isWrite) {
31700
- continue;
31701
- }
31702
- // Skip duplicate references. E.g. in batching.
31703
- if (seenIdentifiers.has(reference.from.node)) {
31704
- continue;
31705
- }
31706
- seenIdentifiers.add(reference.from.node);
31707
- const targetKey = reference.target.key;
31708
- if (reference.from.isPartOfElementBinding) {
31709
- tsReferencesInBindingElements.add(reference.from.node);
31710
- }
31711
- else {
31712
- if (!tsReferencesWithNarrowing.has(targetKey)) {
31713
- tsReferencesWithNarrowing.set(targetKey, { accesses: [] });
31714
- }
31715
- tsReferencesWithNarrowing.get(targetKey).accesses.push(reference.from.node);
31716
- }
31717
- }
31718
- migrateBindingElementInputReference(tsReferencesInBindingElements, info, host.replacements, host.printer);
31719
- migrateStandardTsReference(tsReferencesWithNarrowing, checker, info, host.replacements);
31720
- }
31721
-
31722
- /**
31723
- * Migrates TypeScript "ts.Type" references. E.g.
31724
-
31725
- * - `Partial<MyComp>` will be converted to `UnwrapSignalInputs<Partial<MyComp>>`.
31726
- in Catalyst test files.
31727
- */
31728
- function migrateTypeScriptTypeReferences(host, references, importManager, info) {
31729
- const seenTypeNodes = new WeakSet();
31730
- for (const reference of references) {
31731
- // This pass only deals with TS input class type references.
31732
- if (!isTsClassTypeReference(reference)) {
31733
- continue;
31734
- }
31735
- // Skip references to classes that are not fully migrated.
31736
- if (!host.shouldMigrateReferencesToClass(reference.target)) {
31737
- continue;
31738
- }
31739
- // Skip duplicate references. E.g. in batching.
31740
- if (seenTypeNodes.has(reference.from.node)) {
31741
- continue;
31742
- }
31743
- seenTypeNodes.add(reference.from.node);
31744
- if (reference.isPartialReference && reference.isPartOfCatalystFile) {
31745
- assert__default["default"](reference.from.node.typeArguments, 'Expected type arguments for partial reference.');
31746
- assert__default["default"](reference.from.node.typeArguments.length === 1, 'Expected an argument for reference.');
31747
- const firstArg = reference.from.node.typeArguments[0];
31748
- const sf = firstArg.getSourceFile();
31749
- // Naive detection of the import. Sufficient for this test file migration.
31750
- const catalystImport = sf.text.includes('google3/javascript/angular2/testing/catalyst/fake_async')
31751
- ? 'google3/javascript/angular2/testing/catalyst/fake_async'
31752
- : 'google3/javascript/angular2/testing/catalyst/async';
31753
- const unwrapImportExpr = importManager.addImport({
31754
- exportModuleSpecifier: catalystImport,
31755
- exportSymbolName: 'UnwrapSignalInputs',
31756
- requestedFile: sf,
31757
- });
31758
- host.replacements.push(new Replacement(projectFile(sf, info), new TextUpdate({
31759
- position: firstArg.getStart(),
31760
- end: firstArg.getStart(),
31761
- toInsert: `${host.printer.printNode(ts__default["default"].EmitHint.Unspecified, unwrapImportExpr, sf)}<`,
31762
- })));
31763
- host.replacements.push(new Replacement(projectFile(sf, info), new TextUpdate({ position: firstArg.getEnd(), end: firstArg.getEnd(), toInsert: '>' })));
31764
- }
31765
- }
31766
- }
31767
-
31768
- /**
31769
- * Angular compiler file system implementation that leverages an
31770
- * CLI schematic virtual file tree.
31771
- */
31772
- class DevkitMigrationFilesystem {
31773
- constructor(tree) {
31774
- this.tree = tree;
31775
- }
31776
- extname(path) {
31777
- return core.extname(path);
31778
- }
31779
- isRoot(path) {
31780
- return path === core.normalize('/');
31781
- }
31782
- isRooted(path) {
31783
- return this.normalize(path).startsWith('/');
31784
- }
31785
- dirname(file) {
31786
- return this.normalize(core.dirname(file));
31787
- }
31788
- join(basePath, ...paths) {
31789
- return this.normalize(core.join(basePath, ...paths));
31790
- }
31791
- relative(from, to) {
31792
- return this.normalize(core.relative(from, to));
31793
- }
31794
- basename(filePath, extension) {
31795
- return posixPath__namespace.basename(filePath, extension);
31796
- }
31797
- normalize(path) {
31798
- return core.normalize(path);
31799
- }
31800
- resolve(...paths) {
31801
- const normalizedPaths = paths.map((p) => core.normalize(p));
31802
- // In dev-kit, the NodeJS working directory should never be
31803
- // considered, so `/` is the last resort over `cwd`.
31804
- return this.normalize(posixPath__namespace.resolve(core.normalize('/'), ...normalizedPaths));
31805
- }
31806
- pwd() {
31807
- return '/';
31808
- }
31809
- isCaseSensitive() {
31810
- return true;
31811
- }
31812
- exists(path) {
31813
- return statPath(this.tree, path) !== null;
31814
- }
31815
- readFile(path) {
31816
- return this.tree.readText(path);
31817
- }
31818
- readFileBuffer(path) {
31819
- const buffer = this.tree.read(path);
31820
- if (buffer === null) {
31821
- throw new Error(`File does not exist: ${path}`);
31822
- }
31823
- return buffer;
31824
- }
31825
- readdir(path) {
31826
- const dir = this.tree.getDir(path);
31827
- return [
31828
- ...dir.subdirs,
31829
- ...dir.subfiles,
31830
- ];
31831
- }
31832
- lstat(path) {
31833
- const stat = statPath(this.tree, path);
31834
- if (stat === null) {
31835
- throw new Error(`File does not exist for "lstat": ${path}`);
31836
- }
31837
- return stat;
31838
- }
31839
- stat(path) {
31840
- const stat = statPath(this.tree, path);
31841
- if (stat === null) {
31842
- throw new Error(`File does not exist for "stat": ${path}`);
31843
- }
31844
- return stat;
31845
- }
31846
- realpath(filePath) {
31847
- return filePath;
31848
- }
31849
- getDefaultLibLocation() {
31850
- return 'node_modules/typescript/lib';
31851
- }
31852
- ensureDir(path) {
31853
- // Migrations should compute replacements and not write directly.
31854
- throw new Error('DevkitFilesystem#ensureDir is not supported.');
31855
- }
31856
- writeFile(path, data) {
31857
- // Migrations should compute replacements and not write directly.
31858
- throw new Error('DevkitFilesystem#writeFile is not supported.');
31859
- }
31860
- removeFile(path) {
31861
- // Migrations should compute replacements and not write directly.
31862
- throw new Error('DevkitFilesystem#removeFile is not supported.');
31863
- }
31864
- copyFile(from, to) {
31865
- // Migrations should compute replacements and not write directly.
31866
- throw new Error('DevkitFilesystem#copyFile is not supported.');
31867
- }
31868
- moveFile(from, to) {
31869
- // Migrations should compute replacements and not write directly.
31870
- throw new Error('DevkitFilesystem#moveFile is not supported.');
31871
- }
31872
- removeDeep(path) {
31873
- // Migrations should compute replacements and not write directly.
31874
- throw new Error('DevkitFilesystem#removeDeep is not supported.');
31875
- }
31876
- chdir(_path) {
31877
- throw new Error('FileSystem#chdir is not supported.');
31878
- }
31879
- symlink() {
31880
- throw new Error('FileSystem#symlink is not supported.');
31881
- }
31882
- }
31883
- /** Stats the given path in the virtual tree. */
31884
- function statPath(tree, path) {
31885
- let fileInfo = null;
31886
- let dirInfo = null;
31887
- try {
31888
- fileInfo = tree.get(path);
31889
- }
31890
- catch (e) {
31891
- if (e.constructor.name === 'PathIsDirectoryException') {
31892
- dirInfo = tree.getDir(path);
31893
- }
31894
- else {
31895
- throw e;
31896
- }
31897
- }
31898
- if (fileInfo !== null || dirInfo !== null) {
31899
- return {
31900
- isDirectory: () => dirInfo !== null,
31901
- isFile: () => fileInfo !== null,
31902
- isSymbolicLink: () => false,
31903
- };
31904
- }
31905
- return null;
31906
- }
31907
-
31908
- /**
31909
- * Groups the given replacements per project relative
31910
- * file path.
31911
- *
31912
- * This allows for simple execution of the replacements
31913
- * against a given file. E.g. via {@link applyTextUpdates}.
31914
- */
31915
- function groupReplacementsByFile(replacements) {
31916
- const result = new Map();
31917
- for (const { projectFile, update } of replacements) {
31918
- if (!result.has(projectFile.rootRelativePath)) {
31919
- result.set(projectFile.rootRelativePath, []);
31920
- }
31921
- result.get(projectFile.rootRelativePath).push(update);
31922
- }
31923
- return result;
31924
- }
31925
-
31926
30534
  exports.DevkitMigrationFilesystem = DevkitMigrationFilesystem;
31927
- exports.GroupedTsAstVisitor = GroupedTsAstVisitor;
31928
- exports.InheritanceGraph = InheritanceGraph;
31929
30535
  exports.Replacement = Replacement;
31930
30536
  exports.TextUpdate = TextUpdate;
31931
30537
  exports.TsurgeComplexMigration = TsurgeComplexMigration;
30538
+ exports.TsurgeFunnelMigration = TsurgeFunnelMigration;
31932
30539
  exports.applyImportManagerChanges = applyImportManagerChanges;
31933
- exports.checkIncompatiblePatterns = checkIncompatiblePatterns;
31934
- exports.checkInheritanceOfKnownFields = checkInheritanceOfKnownFields;
31935
30540
  exports.confirmAsSerializable = confirmAsSerializable;
31936
30541
  exports.createFindAllSourceFileReferencesVisitor = createFindAllSourceFileReferencesVisitor;
31937
30542
  exports.createNgtscProgram = createNgtscProgram;
31938
- exports.cutStringToLineLimit = cutStringToLineLimit;
31939
- exports.getMessageForClassIncompatibility = getMessageForClassIncompatibility;
31940
- exports.getMessageForFieldIncompatibility = getMessageForFieldIncompatibility;
30543
+ exports.getBindingElementDeclaration = getBindingElementDeclaration;
30544
+ exports.getMemberName = getMemberName;
31941
30545
  exports.groupReplacementsByFile = groupReplacementsByFile;
31942
- exports.insertPrecedingLine = insertPrecedingLine;
31943
- exports.insertTodoForIncompatibility = insertTodoForIncompatibility;
31944
- exports.isFieldIncompatibility = isFieldIncompatibility;
31945
30546
  exports.isHostBindingReference = isHostBindingReference;
31946
30547
  exports.isInputContainerNode = isInputContainerNode;
31947
30548
  exports.isTemplateReference = isTemplateReference;
30549
+ exports.isTsClassTypeReference = isTsClassTypeReference;
31948
30550
  exports.isTsReference = isTsReference;
31949
- exports.migrateTypeScriptReferences = migrateTypeScriptReferences;
31950
- exports.migrateTypeScriptTypeReferences = migrateTypeScriptTypeReferences;
31951
- exports.nonIgnorableFieldIncompatibilities = nonIgnorableFieldIncompatibilities;
31952
- exports.pickFieldIncompatibility = pickFieldIncompatibility;
31953
30551
  exports.projectFile = projectFile;
31954
- exports.removeFromUnionIfPossible = removeFromUnionIfPossible;
31955
30552
  exports.traverseAccess = traverseAccess;
30553
+ exports.unwrapParent = unwrapParent;