@angular/core 19.0.0 → 19.0.2

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 (32) hide show
  1. package/fesm2022/core.mjs +7117 -7025
  2. package/fesm2022/core.mjs.map +1 -1
  3. package/fesm2022/primitives/event-dispatch.mjs +1 -1
  4. package/fesm2022/primitives/signals.mjs +1 -1
  5. package/fesm2022/rxjs-interop.mjs +1 -1
  6. package/fesm2022/testing.mjs +4 -4
  7. package/index.d.ts +107 -38
  8. package/package.json +1 -1
  9. package/primitives/event-dispatch/index.d.ts +1 -1
  10. package/primitives/signals/index.d.ts +1 -1
  11. package/rxjs-interop/index.d.ts +1 -1
  12. package/schematics/bundles/{checker-e3da3b0a.js → checker-a00b735e.js} +43 -27
  13. package/schematics/bundles/{combine_units-2adebceb.js → combine_units-60865867.js} +5 -3
  14. package/schematics/bundles/{compiler_host-d642e87e.js → compiler_host-3e96c3f7.js} +2 -2
  15. package/schematics/bundles/control-flow-migration.js +3 -3
  16. package/schematics/bundles/explicit-standalone-flag.js +5 -5
  17. package/schematics/bundles/{imports-4ac08251.js → imports-44987700.js} +1 -1
  18. package/schematics/bundles/inject-migration.js +137 -51
  19. package/schematics/bundles/{leading_space-d190b83b.js → leading_space-6e7a8ec6.js} +1 -1
  20. package/schematics/bundles/{migrate_ts_type_references-ed2c0669.js → migrate_ts_type_references-676612f5.js} +5 -5
  21. package/schematics/bundles/{nodes-0e7d45ca.js → nodes-b12e919a.js} +2 -2
  22. package/schematics/bundles/output-migration.js +5 -5
  23. package/schematics/bundles/pending-tasks.js +5 -5
  24. package/schematics/bundles/{program-f984ab63.js → program-a6be5d4a.js} +75 -33
  25. package/schematics/bundles/{project_tsconfig_paths-e9ccccbf.js → project_tsconfig_paths-6c9cde78.js} +1 -1
  26. package/schematics/bundles/provide-initializer.js +38 -31
  27. package/schematics/bundles/route-lazy-loading.js +28 -24
  28. package/schematics/bundles/signal-input-migration.js +7 -7
  29. package/schematics/bundles/signal-queries-migration.js +7 -7
  30. package/schematics/bundles/signals.js +7 -7
  31. package/schematics/bundles/standalone-migration.js +8 -8
  32. package/testing/index.d.ts +1 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v19.0.0
3
+ * @license Angular v19.0.2
4
4
  * (c) 2010-2024 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -6466,10 +6466,23 @@ class ParseError {
6466
6466
  span;
6467
6467
  msg;
6468
6468
  level;
6469
- constructor(span, msg, level = ParseErrorLevel.ERROR) {
6469
+ relatedError;
6470
+ constructor(
6471
+ /** Location of the error. */
6472
+ span,
6473
+ /** Error message. */
6474
+ msg,
6475
+ /** Severity level of the error. */
6476
+ level = ParseErrorLevel.ERROR,
6477
+ /**
6478
+ * Error that caused the error to be surfaced. For example, an error in a sub-expression that
6479
+ * couldn't be parsed. Not guaranteed to be defined, but can be used to provide more context.
6480
+ */
6481
+ relatedError) {
6470
6482
  this.span = span;
6471
6483
  this.msg = msg;
6472
6484
  this.level = level;
6485
+ this.relatedError = relatedError;
6473
6486
  }
6474
6487
  contextualMessage() {
6475
6488
  const ctx = this.span.start.getContext(100, 3);
@@ -10680,6 +10693,7 @@ function createDeferOp(xref, main, mainSlot, ownResolverFn, resolverFn, sourceSp
10680
10693
  errorSlot: null,
10681
10694
  ownResolverFn,
10682
10695
  resolverFn,
10696
+ flags: null,
10683
10697
  sourceSpan,
10684
10698
  ...NEW_OP,
10685
10699
  ...TRAIT_CONSUMES_SLOT,
@@ -22097,7 +22111,7 @@ function text(slot, initialValue, sourceSpan) {
22097
22111
  }
22098
22112
  return call(Identifiers.text, args, sourceSpan);
22099
22113
  }
22100
- function defer(selfSlot, primarySlot, dependencyResolverFn, loadingSlot, placeholderSlot, errorSlot, loadingConfig, placeholderConfig, enableTimerScheduling, sourceSpan) {
22114
+ function defer(selfSlot, primarySlot, dependencyResolverFn, loadingSlot, placeholderSlot, errorSlot, loadingConfig, placeholderConfig, enableTimerScheduling, sourceSpan, flags) {
22101
22115
  const args = [
22102
22116
  literal$1(selfSlot),
22103
22117
  literal$1(primarySlot),
@@ -22108,6 +22122,7 @@ function defer(selfSlot, primarySlot, dependencyResolverFn, loadingSlot, placeho
22108
22122
  loadingConfig ?? literal$1(null),
22109
22123
  placeholderConfig ?? literal$1(null),
22110
22124
  enableTimerScheduling ? importExpr(Identifiers.deferEnableTimerScheduling) : literal$1(null),
22125
+ literal$1(flags),
22111
22126
  ];
22112
22127
  let expr;
22113
22128
  while ((expr = args[args.length - 1]) !== null &&
@@ -22701,7 +22716,7 @@ function reifyCreateOperations(unit, ops) {
22701
22716
  break;
22702
22717
  case OpKind.Defer:
22703
22718
  const timerScheduling = !!op.loadingMinimumTime || !!op.loadingAfterTime || !!op.placeholderMinimumTime;
22704
- OpList.replace(op, defer(op.handle.slot, op.mainSlot.slot, op.resolverFn, op.loadingSlot?.slot ?? null, op.placeholderSlot?.slot ?? null, op.errorSlot?.slot ?? null, op.loadingConfig, op.placeholderConfig, timerScheduling, op.sourceSpan));
22719
+ OpList.replace(op, defer(op.handle.slot, op.mainSlot.slot, op.resolverFn, op.loadingSlot?.slot ?? null, op.placeholderSlot?.slot ?? null, op.errorSlot?.slot ?? null, op.loadingConfig, op.placeholderConfig, timerScheduling, op.sourceSpan, op.flags));
22705
22720
  break;
22706
22721
  case OpKind.DeferOn:
22707
22722
  let args = [];
@@ -25280,6 +25295,7 @@ function ingestDeferBlock(unit, deferBlock) {
25280
25295
  deferOp.placeholderMinimumTime = deferBlock.placeholder?.minimumTime ?? null;
25281
25296
  deferOp.loadingMinimumTime = deferBlock.loading?.minimumTime ?? null;
25282
25297
  deferOp.loadingAfterTime = deferBlock.loading?.afterTime ?? null;
25298
+ deferOp.flags = calcDeferBlockFlags(deferBlock);
25283
25299
  unit.create.push(deferOp);
25284
25300
  // Configure all defer `on` conditions.
25285
25301
  // TODO: refactor prefetch triggers to use a separate op type, with a shared superclass. This will
@@ -25299,6 +25315,12 @@ function ingestDeferBlock(unit, deferBlock) {
25299
25315
  unit.create.push(deferOnOps);
25300
25316
  unit.update.push(deferWhenOps);
25301
25317
  }
25318
+ function calcDeferBlockFlags(deferBlockDetails) {
25319
+ if (Object.keys(deferBlockDetails.hydrateTriggers).length > 0) {
25320
+ return 1 /* ir.TDeferDetailsFlags.HasHydrateTriggers */;
25321
+ }
25322
+ return null;
25323
+ }
25302
25324
  function ingestDeferTriggers(modifier, triggers, onOps, whenOps, unit, deferXref) {
25303
25325
  if (triggers.idle !== undefined) {
25304
25326
  const deferOnOp = createDeferOnOp(deferXref, { kind: DeferTriggerKind.Idle }, modifier, triggers.idle.sourceSpan);
@@ -26148,13 +26170,11 @@ class BindingParser {
26148
26170
  _interpolationConfig;
26149
26171
  _schemaRegistry;
26150
26172
  errors;
26151
- _allowInvalidAssignmentEvents;
26152
- constructor(_exprParser, _interpolationConfig, _schemaRegistry, errors, _allowInvalidAssignmentEvents = false) {
26173
+ constructor(_exprParser, _interpolationConfig, _schemaRegistry, errors) {
26153
26174
  this._exprParser = _exprParser;
26154
26175
  this._interpolationConfig = _interpolationConfig;
26155
26176
  this._schemaRegistry = _schemaRegistry;
26156
26177
  this.errors = errors;
26157
- this._allowInvalidAssignmentEvents = _allowInvalidAssignmentEvents;
26158
26178
  }
26159
26179
  get interpolationConfig() {
26160
26180
  return this._interpolationConfig;
@@ -26503,12 +26523,12 @@ class BindingParser {
26503
26523
  return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
26504
26524
  }
26505
26525
  }
26506
- _reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
26507
- this.errors.push(new ParseError(sourceSpan, message, level));
26526
+ _reportError(message, sourceSpan, level = ParseErrorLevel.ERROR, relatedError) {
26527
+ this.errors.push(new ParseError(sourceSpan, message, level, relatedError));
26508
26528
  }
26509
26529
  _reportExpressionParserErrors(errors, sourceSpan) {
26510
26530
  for (const error of errors) {
26511
- this._reportError(error.message, sourceSpan);
26531
+ this._reportError(error.message, sourceSpan, undefined, error);
26512
26532
  }
26513
26533
  }
26514
26534
  /**
@@ -26538,16 +26558,7 @@ class BindingParser {
26538
26558
  if (ast instanceof PropertyRead || ast instanceof KeyedRead) {
26539
26559
  return true;
26540
26560
  }
26541
- // TODO(crisbeto): this logic is only here to support the automated migration away
26542
- // from invalid bindings. It should be removed once the migration is deleted.
26543
- if (!this._allowInvalidAssignmentEvents) {
26544
- return false;
26545
- }
26546
- if (ast instanceof Binary) {
26547
- return ((ast.operation === '&&' || ast.operation === '||' || ast.operation === '??') &&
26548
- (ast.right instanceof PropertyRead || ast.right instanceof KeyedRead));
26549
- }
26550
- return ast instanceof Conditional || ast instanceof PrefixNot;
26561
+ return false;
26551
26562
  }
26552
26563
  }
26553
26564
  function isAnimationLabel(name) {
@@ -28171,8 +28182,8 @@ const LEADING_TRIVIA_CHARS = [' ', '\n', '\r', '\t'];
28171
28182
  * @param options options to modify how the template is parsed
28172
28183
  */
28173
28184
  function parseTemplate(template, templateUrl, options = {}) {
28174
- const { interpolationConfig, preserveWhitespaces, enableI18nLegacyMessageIdFormat, allowInvalidAssignmentEvents, } = options;
28175
- const bindingParser = makeBindingParser(interpolationConfig, allowInvalidAssignmentEvents);
28185
+ const { interpolationConfig, preserveWhitespaces, enableI18nLegacyMessageIdFormat } = options;
28186
+ const bindingParser = makeBindingParser(interpolationConfig);
28176
28187
  const htmlParser = new HtmlParser();
28177
28188
  const parseResult = htmlParser.parse(template, templateUrl, {
28178
28189
  leadingTriviaChars: LEADING_TRIVIA_CHARS,
@@ -28278,8 +28289,8 @@ const elementRegistry = new DomElementSchemaRegistry();
28278
28289
  /**
28279
28290
  * Construct a `BindingParser` with a default configuration.
28280
28291
  */
28281
- function makeBindingParser(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG, allowInvalidAssignmentEvents = false) {
28282
- return new BindingParser(new Parser(new Lexer()), interpolationConfig, elementRegistry, [], allowInvalidAssignmentEvents);
28292
+ function makeBindingParser(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
28293
+ return new BindingParser(new Parser(new Lexer()), interpolationConfig, elementRegistry, []);
28283
28294
  }
28284
28295
 
28285
28296
  const COMPONENT_VARIABLE = '%COMP%';
@@ -30310,7 +30321,7 @@ function publishFacade(global) {
30310
30321
  * @description
30311
30322
  * Entry point for all public APIs of the compiler package.
30312
30323
  */
30313
- new Version('19.0.0');
30324
+ new Version('19.0.2');
30314
30325
 
30315
30326
  const _I18N_ATTR = 'i18n';
30316
30327
  const _I18N_ATTR_PREFIX = 'i18n-';
@@ -31718,7 +31729,7 @@ class NodeJSPathManipulation {
31718
31729
  // G3-ESM-MARKER: G3 uses CommonJS, but externally everything in ESM.
31719
31730
  // CommonJS/ESM interop for determining the current file name and containing dir.
31720
31731
  const isCommonJS = typeof __filename !== 'undefined';
31721
- const currentFileUrl = isCommonJS ? null : (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || new URL('checker-e3da3b0a.js', document.baseURI).href));
31732
+ const currentFileUrl = isCommonJS ? null : (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || new URL('checker-a00b735e.js', document.baseURI).href));
31722
31733
  const currentFileName = isCommonJS ? __filename : url.fileURLToPath(currentFileUrl);
31723
31734
  /**
31724
31735
  * A wrapper around the Node.js file-system that supports readonly operations and path manipulation.
@@ -32327,7 +32338,7 @@ const patchedReferencedAliasesSymbol = Symbol('patchedReferencedAliases');
32327
32338
  * results in a slow-down due to the type checker being involved multiple times. The CLI worked
32328
32339
  * around this import preserving issue by having another complex post-process step that detects and
32329
32340
  * elides unused imports. Note that these unused imports could cause unused chunks being generated
32330
- * by Webpack if the application or library is not marked as side-effect free.
32341
+ * by webpack if the application or library is not marked as side-effect free.
32331
32342
  *
32332
32343
  * This is not ideal though, as we basically re-implement the complex import usage resolution
32333
32344
  * from TypeScript. We can do better by letting TypeScript do the import eliding, but providing
@@ -38505,6 +38516,9 @@ class SourceMap {
38505
38516
  if (typeof properties.x_google_ignoreList !== 'undefined') {
38506
38517
  this.x_google_ignoreList = properties.x_google_ignoreList;
38507
38518
  }
38519
+ if (typeof properties.debugId !== 'undefined') {
38520
+ this.debugId = properties.debugId;
38521
+ }
38508
38522
  }
38509
38523
 
38510
38524
  toString() {
@@ -45799,6 +45813,7 @@ exports.DEFAULT_INTERPOLATION_CONFIG = DEFAULT_INTERPOLATION_CONFIG;
45799
45813
  exports.DYNAMIC_TYPE = DYNAMIC_TYPE;
45800
45814
  exports.Declaration = Declaration;
45801
45815
  exports.DeclareFunctionStmt = DeclareFunctionStmt;
45816
+ exports.DeclareVarStmt = DeclareVarStmt;
45802
45817
  exports.DefaultImportTracker = DefaultImportTracker;
45803
45818
  exports.DefinitionMap = DefinitionMap;
45804
45819
  exports.DomElementSchemaRegistry = DomElementSchemaRegistry;
@@ -45840,6 +45855,7 @@ exports.ParseLocation = ParseLocation;
45840
45855
  exports.ParseSourceFile = ParseSourceFile;
45841
45856
  exports.ParseSourceSpan = ParseSourceSpan;
45842
45857
  exports.Parser = Parser$1;
45858
+ exports.ParserError = ParserError;
45843
45859
  exports.Placeholder = Placeholder;
45844
45860
  exports.PropertyRead = PropertyRead;
45845
45861
  exports.PropertyWrite = PropertyWrite;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v19.0.0
3
+ * @license Angular v19.0.2
4
4
  * (c) 2010-2024 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -10,8 +10,8 @@ var core = require('@angular-devkit/core');
10
10
  var posixPath = require('node:path/posix');
11
11
  var os = require('os');
12
12
  var ts = require('typescript');
13
- var checker = require('./checker-e3da3b0a.js');
14
- var program = require('./program-f984ab63.js');
13
+ var checker = require('./checker-a00b735e.js');
14
+ var program = require('./program-a6be5d4a.js');
15
15
  require('path');
16
16
 
17
17
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -435,6 +435,8 @@ const defaultMigrationTsOptions = {
435
435
  skipLibCheck: true,
436
436
  skipDefaultLibCheck: true,
437
437
  noEmit: true,
438
+ // Does not apply to g3 and externally is enforced when the app is built by the compiler.
439
+ disableTypeScriptVersionCheck: true,
438
440
  };
439
441
  /**
440
442
  * Creates an instance of a TypeScript program for the given project.
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v19.0.0
3
+ * @license Angular v19.0.2
4
4
  * (c) 2010-2024 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
7
7
  'use strict';
8
8
 
9
9
  var ts = require('typescript');
10
- var checker = require('./checker-e3da3b0a.js');
10
+ var checker = require('./checker-a00b735e.js');
11
11
  require('os');
12
12
  var p = require('path');
13
13
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v19.0.0
3
+ * @license Angular v19.0.2
4
4
  * (c) 2010-2024 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -10,8 +10,8 @@ Object.defineProperty(exports, '__esModule', { value: true });
10
10
 
11
11
  var schematics = require('@angular-devkit/schematics');
12
12
  var p = require('path');
13
- var compiler_host = require('./compiler_host-d642e87e.js');
14
- var checker = require('./checker-e3da3b0a.js');
13
+ var compiler_host = require('./compiler_host-3e96c3f7.js');
14
+ var checker = require('./checker-a00b735e.js');
15
15
  var ts = require('typescript');
16
16
  require('os');
17
17
  require('fs');
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v19.0.0
3
+ * @license Angular v19.0.2
4
4
  * (c) 2010-2024 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -10,12 +10,12 @@ Object.defineProperty(exports, '__esModule', { value: true });
10
10
 
11
11
  var schematics = require('@angular-devkit/schematics');
12
12
  var p = require('path');
13
- var project_tsconfig_paths = require('./project_tsconfig_paths-e9ccccbf.js');
14
- var compiler_host = require('./compiler_host-d642e87e.js');
13
+ var project_tsconfig_paths = require('./project_tsconfig_paths-6c9cde78.js');
14
+ var compiler_host = require('./compiler_host-3e96c3f7.js');
15
15
  var ts = require('typescript');
16
- var imports = require('./imports-4ac08251.js');
16
+ var imports = require('./imports-44987700.js');
17
17
  require('@angular-devkit/core');
18
- require('./checker-e3da3b0a.js');
18
+ require('./checker-a00b735e.js');
19
19
  require('os');
20
20
  require('fs');
21
21
  require('module');
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v19.0.0
3
+ * @license Angular v19.0.2
4
4
  * (c) 2010-2024 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v19.0.0
3
+ * @license Angular v19.0.2
4
4
  * (c) 2010-2024 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -10,12 +10,12 @@ Object.defineProperty(exports, '__esModule', { value: true });
10
10
 
11
11
  var schematics = require('@angular-devkit/schematics');
12
12
  var p = require('path');
13
- var compiler_host = require('./compiler_host-d642e87e.js');
13
+ var compiler_host = require('./compiler_host-3e96c3f7.js');
14
14
  var ts = require('typescript');
15
- var nodes = require('./nodes-0e7d45ca.js');
16
- var imports = require('./imports-4ac08251.js');
17
- var leading_space = require('./leading_space-d190b83b.js');
18
- require('./checker-e3da3b0a.js');
15
+ var nodes = require('./nodes-b12e919a.js');
16
+ var imports = require('./imports-44987700.js');
17
+ var leading_space = require('./leading_space-6e7a8ec6.js');
18
+ require('./checker-a00b735e.js');
19
19
  require('os');
20
20
  require('fs');
21
21
  require('module');
@@ -50,6 +50,15 @@ const DI_PARAM_SYMBOLS = new Set([
50
50
  'Host',
51
51
  'forwardRef',
52
52
  ]);
53
+ /** Kinds of nodes which aren't injectable when set as a type of a parameter. */
54
+ const UNINJECTABLE_TYPE_KINDS = new Set([
55
+ ts__default["default"].SyntaxKind.TrueKeyword,
56
+ ts__default["default"].SyntaxKind.FalseKeyword,
57
+ ts__default["default"].SyntaxKind.NumberKeyword,
58
+ ts__default["default"].SyntaxKind.StringKeyword,
59
+ ts__default["default"].SyntaxKind.NullKeyword,
60
+ ts__default["default"].SyntaxKind.VoidKeyword,
61
+ ]);
53
62
  /**
54
63
  * Finds the necessary information for the `inject` migration in a file.
55
64
  * @param sourceFile File which to analyze.
@@ -76,12 +85,23 @@ function analyzeFile(sourceFile, localTypeChecker, options) {
76
85
  if (ts__default["default"].isImportDeclaration(node)) {
77
86
  return;
78
87
  }
79
- // Only visit the initializer of parameters, because we won't exclude
80
- // their decorators from the identifier counting result below.
81
88
  if (ts__default["default"].isParameter(node)) {
89
+ const closestConstructor = nodes.closestNode(node, ts__default["default"].isConstructorDeclaration);
90
+ // Visiting the same parameters that we're about to remove can throw off the reference
91
+ // counting logic below. If we run into an initializer, we always visit its initializer
92
+ // and optionally visit the modifiers/decorators if it's not due to be deleted. Note that
93
+ // here we technically aren't dealing with the the full list of classes, but the parent class
94
+ // will have been visited by the time we reach the parameters.
82
95
  if (node.initializer) {
83
96
  walk(node.initializer);
84
97
  }
98
+ if (closestConstructor === null ||
99
+ // This is meant to avoid the case where this is a
100
+ // parameter inside a function placed in a constructor.
101
+ !closestConstructor.parameters.includes(node) ||
102
+ !classes.some((c) => c.constructor === closestConstructor)) {
103
+ node.modifiers?.forEach(walk);
104
+ }
85
105
  return;
86
106
  }
87
107
  if (ts__default["default"].isIdentifier(node) && importsToSpecifiers.size > 0) {
@@ -111,9 +131,21 @@ function analyzeFile(sourceFile, localTypeChecker, options) {
111
131
  const constructorNode = node.members.find((member) => ts__default["default"].isConstructorDeclaration(member) &&
112
132
  member.body != null &&
113
133
  member.parameters.length > 0);
134
+ // Basic check to determine if all parameters are injectable. This isn't exhaustive, but it
135
+ // should catch the majority of cases. An exhaustive check would require a full type checker
136
+ // which we don't have in this migration.
137
+ const allParamsInjectable = !!constructorNode?.parameters.every((param) => {
138
+ if (!param.type || !UNINJECTABLE_TYPE_KINDS.has(param.type.kind)) {
139
+ return true;
140
+ }
141
+ return nodes.getAngularDecorators(localTypeChecker, ts__default["default"].getDecorators(param) || []).some((dec) => dec.name === 'Inject' || dec.name === 'Attribute');
142
+ });
114
143
  // Don't migrate abstract classes by default, because
115
144
  // their parameters aren't guaranteed to be injectable.
116
- if (supportsDI && constructorNode && (!isAbstract || options.migrateAbstractClasses)) {
145
+ if (supportsDI &&
146
+ constructorNode &&
147
+ allParamsInjectable &&
148
+ (!isAbstract || options.migrateAbstractClasses)) {
117
149
  classes.push({
118
150
  node,
119
151
  constructor: constructorNode,
@@ -146,13 +178,13 @@ function getConstructorUnusedParameters(declaration, localTypeChecker, removedSt
146
178
  if (!declaration.body) {
147
179
  return topLevelParameters;
148
180
  }
149
- declaration.body.forEachChild(function walk(node) {
181
+ const analyze = (node) => {
150
182
  // Don't descend into statements that were removed already.
151
183
  if (ts__default["default"].isStatement(node) && removedStatements.has(node)) {
152
184
  return;
153
185
  }
154
186
  if (!ts__default["default"].isIdentifier(node) || !topLevelParameterNames.has(node.text)) {
155
- node.forEachChild(walk);
187
+ node.forEachChild(analyze);
156
188
  return;
157
189
  }
158
190
  // Don't consider `this.<name>` accesses as being references to
@@ -171,7 +203,13 @@ function getConstructorUnusedParameters(declaration, localTypeChecker, removedSt
171
203
  }
172
204
  }
173
205
  });
206
+ };
207
+ declaration.parameters.forEach((param) => {
208
+ if (param.initializer) {
209
+ analyze(param.initializer);
210
+ }
174
211
  });
212
+ declaration.body.forEachChild(analyze);
175
213
  for (const param of topLevelParameters) {
176
214
  if (!accessedTopLevelParameters.has(param)) {
177
215
  unusedParams.add(param);
@@ -210,6 +248,41 @@ function getSuperParameters(declaration, superCall, localTypeChecker) {
210
248
  });
211
249
  return usedParams;
212
250
  }
251
+ /**
252
+ * Determines if a specific parameter has references to other parameters.
253
+ * @param param Parameter to check.
254
+ * @param allParameters All parameters of the containing function.
255
+ * @param localTypeChecker Type checker scoped to the current file.
256
+ */
257
+ function parameterReferencesOtherParameters(param, allParameters, localTypeChecker) {
258
+ // A parameter can only reference other parameters through its initializer.
259
+ if (!param.initializer || allParameters.length < 2) {
260
+ return false;
261
+ }
262
+ const paramNames = new Set();
263
+ for (const current of allParameters) {
264
+ if (current !== param && ts__default["default"].isIdentifier(current.name)) {
265
+ paramNames.add(current.name.text);
266
+ }
267
+ }
268
+ let result = false;
269
+ const analyze = (node) => {
270
+ if (ts__default["default"].isIdentifier(node) && paramNames.has(node.text) && !isAccessedViaThis(node)) {
271
+ const symbol = localTypeChecker.getSymbolAtLocation(node);
272
+ const referencesOtherParam = symbol?.declarations?.some((decl) => {
273
+ return allParameters.includes(decl);
274
+ });
275
+ if (referencesOtherParam) {
276
+ result = true;
277
+ }
278
+ }
279
+ if (!result) {
280
+ node.forEachChild(analyze);
281
+ }
282
+ };
283
+ analyze(param.initializer);
284
+ return result;
285
+ }
213
286
  /** Checks whether a parameter node declares a property on its class. */
214
287
  function parameterDeclaresProperty(node) {
215
288
  return !!node.modifiers?.some(({ kind }) => kind === ts__default["default"].SyntaxKind.PublicKeyword ||
@@ -587,7 +660,8 @@ function migrateClass(node, constructor, superCall, options, memberIndentation,
587
660
  for (const param of constructor.parameters) {
588
661
  const usedInSuper = superParameters !== null && superParameters.has(param);
589
662
  const usedInConstructor = !unusedParameters.has(param);
590
- migrateParameter(param, options, localTypeChecker, printer, tracker, superCall, usedInSuper, usedInConstructor, memberIndentation, innerIndentation, prependToConstructor, prependToClass, afterSuper);
663
+ const usesOtherParams = parameterReferencesOtherParameters(param, constructor.parameters, localTypeChecker);
664
+ migrateParameter(param, options, localTypeChecker, printer, tracker, superCall, usedInSuper, usedInConstructor, usesOtherParams, memberIndentation, innerIndentation, prependToConstructor, prependToClass, afterSuper);
591
665
  }
592
666
  // Delete all of the constructor overloads since below we're either going to
593
667
  // remove the implementation, or we're going to delete all of the parameters.
@@ -597,7 +671,7 @@ function migrateClass(node, constructor, superCall, options, memberIndentation,
597
671
  tracker.removeNode(member, true);
598
672
  }
599
673
  }
600
- if (canRemoveConstructor(options, constructor, removedStatementCount, superCall)) {
674
+ if (canRemoveConstructor(options, constructor, removedStatementCount, prependToConstructor, superCall)) {
601
675
  // Drop the constructor if it was empty.
602
676
  removedMembers.add(constructor);
603
677
  tracker.removeNode(constructor, true);
@@ -607,7 +681,15 @@ function migrateClass(node, constructor, superCall, options, memberIndentation,
607
681
  // We always do this no matter what is passed into `backwardsCompatibleConstructors`.
608
682
  stripConstructorParameters(constructor, tracker);
609
683
  if (prependToConstructor.length > 0) {
610
- tracker.insertText(sourceFile, (firstConstructorStatement || innerReference).getFullStart(), `\n${prependToConstructor.join('\n')}\n`);
684
+ if (firstConstructorStatement ||
685
+ (innerReference !== constructor &&
686
+ innerReference.getStart() >= constructor.getStart() &&
687
+ innerReference.getEnd() <= constructor.getEnd())) {
688
+ tracker.insertText(sourceFile, (firstConstructorStatement || innerReference).getFullStart(), `\n${prependToConstructor.join('\n')}\n`);
689
+ }
690
+ else {
691
+ tracker.insertText(sourceFile, constructor.body.getStart() + 1, `\n${prependToConstructor.map((p) => innerIndentation + p).join('\n')}\n${innerIndentation}`);
692
+ }
611
693
  }
612
694
  }
613
695
  if (afterSuper.length > 0 && superCall !== null) {
@@ -616,7 +698,7 @@ function migrateClass(node, constructor, superCall, options, memberIndentation,
616
698
  // the text if the statement after the `super` call is being deleted. This appears to be because
617
699
  // the full start of the next statement appears to always be the end of the `super` call plus 1.
618
700
  const nextStatement = getNextPreservedStatement(superCall, removedStatements);
619
- tracker.insertText(sourceFile, nextStatement ? nextStatement.getFullStart() : superCall.getEnd() + 1, `\n${afterSuper.join('\n')}\n`);
701
+ tracker.insertText(sourceFile, nextStatement ? nextStatement.getFullStart() : constructor.getEnd() - 1, `\n${afterSuper.join('\n')}\n` + (nextStatement ? '' : memberIndentation));
620
702
  }
621
703
  // Need to resolve this once all constructor signatures have been removed.
622
704
  const memberReference = node.members.find((m) => !removedMembers.has(m)) || node.members[0];
@@ -638,7 +720,12 @@ function migrateClass(node, constructor, superCall, options, memberIndentation,
638
720
  prependToClass.push(...afterInjectCalls);
639
721
  if (prependToClass.length > 0) {
640
722
  if (removedMembers.size === node.members.length) {
641
- tracker.insertText(sourceFile, constructor.getEnd() + 1, `${prependToClass.join('\n')}\n`);
723
+ tracker.insertText(sourceFile,
724
+ // If all members were deleted, insert after the last one.
725
+ // This allows us to preserve the indentation.
726
+ node.members.length > 0
727
+ ? node.members[node.members.length - 1].getEnd() + 1
728
+ : node.getEnd() - 1, `${prependToClass.join('\n')}\n`);
642
729
  }
643
730
  else {
644
731
  // Insert the new properties after the first member that hasn't been deleted.
@@ -662,7 +749,7 @@ function migrateClass(node, constructor, superCall, options, memberIndentation,
662
749
  * @param propsToAdd Properties to be added to the class.
663
750
  * @param afterSuper Statements to be added after the `super` call.
664
751
  */
665
- function migrateParameter(node, options, localTypeChecker, printer, tracker, superCall, usedInSuper, usedInConstructor, memberIndentation, innerIndentation, prependToConstructor, propsToAdd, afterSuper) {
752
+ function migrateParameter(node, options, localTypeChecker, printer, tracker, superCall, usedInSuper, usedInConstructor, usesOtherParams, memberIndentation, innerIndentation, prependToConstructor, propsToAdd, afterSuper) {
666
753
  if (!ts__default["default"].isIdentifier(node.name)) {
667
754
  return;
668
755
  }
@@ -671,6 +758,9 @@ function migrateParameter(node, options, localTypeChecker, printer, tracker, sup
671
758
  const declaresProp = parameterDeclaresProperty(node);
672
759
  // If the parameter declares a property, we need to declare it (e.g. `private foo: Foo`).
673
760
  if (declaresProp) {
761
+ // We can't initialize the property if it's referenced within a `super` call or it references
762
+ // other parameters. See the logic further below for the initialization.
763
+ const canInitialize = !usedInSuper && !usesOtherParams;
674
764
  const prop = ts__default["default"].factory.createPropertyDeclaration(cloneModifiers(node.modifiers?.filter((modifier) => {
675
765
  // Strip out the DI decorators, as well as `public` which is redundant.
676
766
  return !ts__default["default"].isDecorator(modifier) && modifier.kind !== ts__default["default"].SyntaxKind.PublicKeyword;
@@ -678,10 +768,7 @@ function migrateParameter(node, options, localTypeChecker, printer, tracker, sup
678
768
  // Don't add the question token to private properties since it won't affect interface implementation.
679
769
  node.modifiers?.some((modifier) => modifier.kind === ts__default["default"].SyntaxKind.PrivateKeyword)
680
770
  ? undefined
681
- : node.questionToken,
682
- // We can't initialize the property if it's referenced within a `super` call.
683
- // See the logic further below for the initialization.
684
- usedInSuper ? node.type : undefined, usedInSuper ? undefined : ts__default["default"].factory.createIdentifier(PLACEHOLDER));
771
+ : node.questionToken, canInitialize ? undefined : node.type, canInitialize ? ts__default["default"].factory.createIdentifier(PLACEHOLDER) : undefined);
685
772
  propsToAdd.push(memberIndentation +
686
773
  replaceNodePlaceholder(node.getSourceFile(), prop, replacementCall, printer));
687
774
  }
@@ -714,6 +801,15 @@ function migrateParameter(node, options, localTypeChecker, printer, tracker, sup
714
801
  prependToConstructor.push(`${innerIndentation}const ${name} = ${replacementCall};`);
715
802
  }
716
803
  }
804
+ else if (usesOtherParams && declaresProp) {
805
+ const toAdd = `${innerIndentation}this.${name} = ${replacementCall};`;
806
+ if (superCall === null) {
807
+ prependToConstructor.push(toAdd);
808
+ }
809
+ else {
810
+ afterSuper.push(toAdd);
811
+ }
812
+ }
717
813
  }
718
814
  /**
719
815
  * Creates a replacement `inject` call from a function parameter.
@@ -800,6 +896,10 @@ function createInjectReplacementCall(param, options, localTypeChecker, printer,
800
896
  expression = ts__default["default"].factory.createNonNullExpression(expression);
801
897
  }
802
898
  }
899
+ // If the parameter is initialized, add the initializer as a fallback.
900
+ if (param.initializer) {
901
+ expression = ts__default["default"].factory.createBinaryExpression(expression, ts__default["default"].SyntaxKind.QuestionQuestionToken, param.initializer);
902
+ }
803
903
  return replaceNodePlaceholder(param.getSourceFile(), expression, injectedType, printer);
804
904
  }
805
905
  /**
@@ -839,15 +939,10 @@ function migrateInjectDecorator(firstArg, type, localTypeChecker) {
839
939
  }
840
940
  }
841
941
  }
842
- else if (
843
- // Pass the type for cases like `@Inject(FOO_TOKEN) foo: Foo`, because:
844
- // 1. It guarantees that the type stays the same as before.
845
- // 2. Avoids leaving unused imports behind.
846
- // We only do this for type references since the `@Inject` pattern above is fairly common and
847
- // apps don't necessarily type their injection tokens correctly, whereas doing it for literal
848
- // types will add a lot of noise to the generated code.
849
- type &&
942
+ else if (type &&
850
943
  (ts__default["default"].isTypeReferenceNode(type) ||
944
+ ts__default["default"].isTypeLiteralNode(type) ||
945
+ ts__default["default"].isTupleTypeNode(type) ||
851
946
  (ts__default["default"].isUnionTypeNode(type) && type.types.some(ts__default["default"].isTypeReferenceNode)))) {
852
947
  typeArguments = [type];
853
948
  }
@@ -869,28 +964,17 @@ function stripConstructorParameters(node, tracker) {
869
964
  const constructorText = node.getText();
870
965
  const lastParamText = node.parameters[node.parameters.length - 1].getText();
871
966
  const lastParamStart = constructorText.indexOf(lastParamText);
872
- const whitespacePattern = /\s/;
873
- let trailingCharacters = 0;
874
- if (lastParamStart > -1) {
875
- let lastParamEnd = lastParamStart + lastParamText.length;
876
- let closeParenIndex = -1;
877
- for (let i = lastParamEnd; i < constructorText.length; i++) {
878
- const char = constructorText[i];
879
- if (char === ')') {
880
- closeParenIndex = i;
881
- break;
882
- }
883
- else if (!whitespacePattern.test(char)) {
884
- // The end of the last parameter won't include
885
- // any trailing commas which we need to account for.
886
- lastParamEnd = i + 1;
887
- }
888
- }
889
- if (closeParenIndex > -1) {
890
- trailingCharacters = closeParenIndex - lastParamEnd;
967
+ // This shouldn't happen, but bail out just in case so we don't mangle the code.
968
+ if (lastParamStart === -1) {
969
+ return;
970
+ }
971
+ for (let i = lastParamStart + lastParamText.length; i < constructorText.length; i++) {
972
+ const char = constructorText[i];
973
+ if (char === ')') {
974
+ tracker.replaceText(node.getSourceFile(), node.parameters.pos, node.getStart() + i - node.parameters.pos, '');
975
+ break;
891
976
  }
892
977
  }
893
- tracker.replaceText(node.getSourceFile(), node.parameters.pos, node.parameters.end - node.parameters.pos + trailingCharacters, '');
894
978
  }
895
979
  /**
896
980
  * Creates a type checker scoped to a specific file.
@@ -956,10 +1040,11 @@ function cloneName(node) {
956
1040
  * @param options Options used to configure the migration.
957
1041
  * @param constructor Node representing the constructor.
958
1042
  * @param removedStatementCount Number of statements that were removed by the migration.
1043
+ * @param prependToConstructor Statements that should be prepended to the constructor.
959
1044
  * @param superCall Node representing the `super()` call within the constructor.
960
1045
  */
961
- function canRemoveConstructor(options, constructor, removedStatementCount, superCall) {
962
- if (options.backwardsCompatibleConstructors) {
1046
+ function canRemoveConstructor(options, constructor, removedStatementCount, prependToConstructor, superCall) {
1047
+ if (options.backwardsCompatibleConstructors || prependToConstructor.length > 0) {
963
1048
  return false;
964
1049
  }
965
1050
  const statementCount = constructor.body
@@ -1039,6 +1124,7 @@ function applyInternalOnlyChanges(node, constructor, localTypeChecker, tracker,
1039
1124
  result.toHoist.forEach((decl) => {
1040
1125
  prependToClass.push(memberIndentation + printer.printNode(ts__default["default"].EmitHint.Unspecified, decl, decl.getSourceFile()));
1041
1126
  tracker.removeNode(decl, true);
1127
+ removedMembers.add(decl);
1042
1128
  });
1043
1129
  // If we added any hoisted properties, separate them visually with a new line.
1044
1130
  if (prependToClass.length > 0) {