explicit-standalone-flag.cjs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. 'use strict';
  2. /**
  3. * @license Angular v19.2.13
  4. * (c) 2010-2025 Google LLC. https://angular.io/
  5. * License: MIT
  6. */
  7. 'use strict';
  8. var schematics = require('@angular-devkit/schematics');
  9. var p = require('path');
  10. var project_tsconfig_paths = require('./project_tsconfig_paths-CDVxT6Ov.cjs');
  11. var compiler_host = require('./compiler_host-B1Gyeytz.cjs');
  12. var ts = require('typescript');
  13. var imports = require('./imports-CIX-JgAN.cjs');
  14. require('@angular-devkit/core');
  15. require('./checker-5pyJrZ9G.cjs');
  16. require('os');
  17. require('fs');
  18. require('module');
  19. require('url');
  20. const CORE = '@angular/core';
  21. const DIRECTIVE = 'Directive';
  22. const COMPONENT = 'Component';
  23. const PIPE = 'Pipe';
  24. function migrateFile(sourceFile, rewriteFn) {
  25. const changeTracker = new compiler_host.ChangeTracker(ts.createPrinter());
  26. // Check if there are any imports of the `AfterRenderPhase` enum.
  27. const coreImports = imports.getNamedImports(sourceFile, CORE);
  28. if (!coreImports) {
  29. return;
  30. }
  31. const directive = imports.getImportSpecifier(sourceFile, CORE, DIRECTIVE);
  32. const component = imports.getImportSpecifier(sourceFile, CORE, COMPONENT);
  33. const pipe = imports.getImportSpecifier(sourceFile, CORE, PIPE);
  34. if (!directive && !component && !pipe) {
  35. return;
  36. }
  37. ts.forEachChild(sourceFile, function visit(node) {
  38. ts.forEachChild(node, visit);
  39. // First we need to check for class declarations
  40. // Decorators will come after
  41. if (!ts.isClassDeclaration(node)) {
  42. return;
  43. }
  44. ts.getDecorators(node)?.forEach((decorator) => {
  45. if (!ts.isDecorator(decorator)) {
  46. return;
  47. }
  48. const callExpression = decorator.expression;
  49. if (!ts.isCallExpression(callExpression)) {
  50. return;
  51. }
  52. const decoratorIdentifier = callExpression.expression;
  53. if (!ts.isIdentifier(decoratorIdentifier)) {
  54. return;
  55. }
  56. // Checking the identifier of the decorator by comparing to the import specifier
  57. switch (decoratorIdentifier.text) {
  58. case directive?.name.text:
  59. case component?.name.text:
  60. case pipe?.name.text:
  61. break;
  62. default:
  63. // It's not a decorator to migrate
  64. return;
  65. }
  66. const [decoratorArgument] = callExpression.arguments;
  67. if (!decoratorArgument || !ts.isObjectLiteralExpression(decoratorArgument)) {
  68. return;
  69. }
  70. const properties = decoratorArgument.properties;
  71. const standaloneProp = getStandaloneProperty(properties);
  72. const hasImports = decoratorHasImports(decoratorArgument);
  73. // We'll use the presence of imports to keep the migration idempotent
  74. // We need to take care of 3 cases
  75. // - standalone: true => remove the property if we have imports
  76. // - standalone: false => nothing
  77. // - No standalone property => add a standalone: false property if there are no imports
  78. let newProperties;
  79. if (!standaloneProp) {
  80. if (!hasImports) {
  81. const standaloneFalseProperty = ts.factory.createPropertyAssignment('standalone', ts.factory.createFalse());
  82. newProperties = [...properties, standaloneFalseProperty];
  83. }
  84. }
  85. else if (standaloneProp.value === ts.SyntaxKind.TrueKeyword && hasImports) {
  86. // To keep the migration idempotent, we'll only remove the standalone prop when there are imports
  87. newProperties = properties.filter((p) => p !== standaloneProp.property);
  88. }
  89. if (newProperties) {
  90. // At this point we know that we need to add standalone: false or
  91. // remove an existing standalone: true property.
  92. const newPropsArr = ts.factory.createNodeArray(newProperties);
  93. const newFirstArg = ts.factory.createObjectLiteralExpression(newPropsArr, true);
  94. changeTracker.replaceNode(decoratorArgument, newFirstArg);
  95. }
  96. });
  97. });
  98. // Write the changes.
  99. for (const changesInFile of changeTracker.recordChanges().values()) {
  100. for (const change of changesInFile) {
  101. rewriteFn(change.start, change.removeLength ?? 0, change.text);
  102. }
  103. }
  104. }
  105. function getStandaloneProperty(properties) {
  106. for (const prop of properties) {
  107. if (ts.isShorthandPropertyAssignment(prop) && prop.name.text) {
  108. return { property: prop, value: prop.objectAssignmentInitializer };
  109. }
  110. if (isStandaloneProperty(prop)) {
  111. if (prop.initializer.kind === ts.SyntaxKind.TrueKeyword ||
  112. prop.initializer.kind === ts.SyntaxKind.FalseKeyword) {
  113. return { property: prop, value: prop.initializer.kind };
  114. }
  115. else {
  116. return { property: prop, value: prop.initializer };
  117. }
  118. }
  119. }
  120. return undefined;
  121. }
  122. function isStandaloneProperty(prop) {
  123. return (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'standalone');
  124. }
  125. function decoratorHasImports(decoratorArgument) {
  126. for (const prop of decoratorArgument.properties) {
  127. if (ts.isPropertyAssignment(prop) &&
  128. ts.isIdentifier(prop.name) &&
  129. prop.name.text === 'imports') {
  130. if (prop.initializer.kind === ts.SyntaxKind.ArrayLiteralExpression ||
  131. prop.initializer.kind === ts.SyntaxKind.Identifier) {
  132. return true;
  133. }
  134. }
  135. }
  136. return false;
  137. }
  138. function migrate() {
  139. return async (tree) => {
  140. const { buildPaths, testPaths } = await project_tsconfig_paths.getProjectTsConfigPaths(tree);
  141. const basePath = process.cwd();
  142. const allPaths = [...buildPaths, ...testPaths];
  143. if (!allPaths.length) {
  144. throw new schematics.SchematicsException('Could not find any tsconfig file. Cannot run the explicit-standalone-flag migration.');
  145. }
  146. for (const tsconfigPath of allPaths) {
  147. runMigration(tree, tsconfigPath, basePath);
  148. }
  149. };
  150. }
  151. function runMigration(tree, tsconfigPath, basePath) {
  152. const program = compiler_host.createMigrationProgram(tree, tsconfigPath, basePath);
  153. const sourceFiles = program
  154. .getSourceFiles()
  155. .filter((sourceFile) => compiler_host.canMigrateFile(basePath, sourceFile, program));
  156. for (const sourceFile of sourceFiles) {
  157. let update = null;
  158. const rewriter = (startPos, width, text) => {
  159. if (update === null) {
  160. // Lazily initialize update, because most files will not require migration.
  161. update = tree.beginUpdate(p.relative(basePath, sourceFile.fileName));
  162. }
  163. update.remove(startPos, width);
  164. if (text !== null) {
  165. update.insertLeft(startPos, text);
  166. }
  167. };
  168. migrateFile(sourceFile, rewriter);
  169. if (update !== null) {
  170. tree.commitUpdate(update);
  171. }
  172. }
  173. }
  174. exports.migrate = migrate;