util.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. "use strict";
  2. /**
  3. * @license
  4. * Copyright Google LLC All Rights Reserved.
  5. *
  6. * Use of this source code is governed by an MIT-style license that can be
  7. * found in the LICENSE file at https://angular.dev/license
  8. */
  9. var __importDefault = (this && this.__importDefault) || function (mod) {
  10. return (mod && mod.__esModule) ? mod : { "default": mod };
  11. };
  12. Object.defineProperty(exports, "__esModule", { value: true });
  13. exports.getMainFilePath = getMainFilePath;
  14. exports.getSourceFile = getSourceFile;
  15. exports.findBootstrapApplicationCall = findBootstrapApplicationCall;
  16. exports.applyChangesToFile = applyChangesToFile;
  17. exports.isMergeAppConfigCall = isMergeAppConfigCall;
  18. exports.findProvidersLiteral = findProvidersLiteral;
  19. const schematics_1 = require("@angular-devkit/schematics");
  20. const posix_1 = require("node:path/posix");
  21. const typescript_1 = __importDefault(require("../../third_party/github.com/Microsoft/TypeScript/lib/typescript"));
  22. const change_1 = require("../change");
  23. const project_targets_1 = require("../project-targets");
  24. const workspace_1 = require("../workspace");
  25. const workspace_models_1 = require("../workspace-models");
  26. /**
  27. * Finds the main file of a project.
  28. * @param tree File tree for the project.
  29. * @param projectName Name of the project in which to search.
  30. */
  31. async function getMainFilePath(tree, projectName) {
  32. const workspace = await (0, workspace_1.getWorkspace)(tree);
  33. const project = workspace.projects.get(projectName);
  34. const buildTarget = project?.targets.get('build');
  35. if (!project || !buildTarget) {
  36. throw (0, project_targets_1.targetBuildNotFoundError)();
  37. }
  38. const options = buildTarget.options;
  39. if (buildTarget.builder === workspace_models_1.Builders.Application ||
  40. buildTarget.builder === workspace_models_1.Builders.BuildApplication) {
  41. // These builders support a default of `<project_source_root>/main.ts`
  42. const projectSourceRoot = project.sourceRoot ?? (0, posix_1.join)(project.root, 'src');
  43. return options.browser ?? (0, posix_1.join)(projectSourceRoot, 'main.ts');
  44. }
  45. return options.main;
  46. }
  47. /**
  48. * Gets a TypeScript source file at a specific path.
  49. * @param tree File tree of a project.
  50. * @param path Path to the file.
  51. */
  52. function getSourceFile(tree, path) {
  53. const content = tree.readText(path);
  54. const source = typescript_1.default.createSourceFile(path, content, typescript_1.default.ScriptTarget.Latest, true);
  55. return source;
  56. }
  57. /** Finds the call to `bootstrapApplication` within a file. */
  58. function findBootstrapApplicationCall(tree, mainFilePath) {
  59. const sourceFile = getSourceFile(tree, mainFilePath);
  60. const localName = findImportLocalName(sourceFile, 'bootstrapApplication', '@angular/platform-browser');
  61. if (localName) {
  62. let result = null;
  63. sourceFile.forEachChild(function walk(node) {
  64. if (typescript_1.default.isCallExpression(node) &&
  65. typescript_1.default.isIdentifier(node.expression) &&
  66. node.expression.text === localName) {
  67. result = node;
  68. }
  69. if (!result) {
  70. node.forEachChild(walk);
  71. }
  72. });
  73. if (result) {
  74. return result;
  75. }
  76. }
  77. throw new schematics_1.SchematicsException(`Could not find bootstrapApplication call in ${mainFilePath}`);
  78. }
  79. /**
  80. * Finds the local name of an imported symbol. Could be the symbol name itself or its alias.
  81. * @param sourceFile File within which to search for the import.
  82. * @param name Actual name of the import, not its local alias.
  83. * @param moduleName Name of the module from which the symbol is imported.
  84. */
  85. function findImportLocalName(sourceFile, name, moduleName) {
  86. for (const node of sourceFile.statements) {
  87. // Only look for top-level imports.
  88. if (!typescript_1.default.isImportDeclaration(node) ||
  89. !typescript_1.default.isStringLiteral(node.moduleSpecifier) ||
  90. node.moduleSpecifier.text !== moduleName) {
  91. continue;
  92. }
  93. // Filter out imports that don't have the right shape.
  94. if (!node.importClause ||
  95. !node.importClause.namedBindings ||
  96. !typescript_1.default.isNamedImports(node.importClause.namedBindings)) {
  97. continue;
  98. }
  99. // Look through the elements of the declaration for the specific import.
  100. for (const element of node.importClause.namedBindings.elements) {
  101. if ((element.propertyName || element.name).text === name) {
  102. // The local name is always in `name`.
  103. return element.name.text;
  104. }
  105. }
  106. }
  107. return null;
  108. }
  109. /**
  110. * Applies a set of changes to a file.
  111. * @param tree File tree of the project.
  112. * @param path Path to the file that is being changed.
  113. * @param changes Changes that should be applied to the file.
  114. */
  115. function applyChangesToFile(tree, path, changes) {
  116. if (changes.length > 0) {
  117. const recorder = tree.beginUpdate(path);
  118. (0, change_1.applyToUpdateRecorder)(recorder, changes);
  119. tree.commitUpdate(recorder);
  120. }
  121. }
  122. /** Checks whether a node is a call to `mergeApplicationConfig`. */
  123. function isMergeAppConfigCall(node) {
  124. if (!typescript_1.default.isCallExpression(node)) {
  125. return false;
  126. }
  127. const localName = findImportLocalName(node.getSourceFile(), 'mergeApplicationConfig', '@angular/core');
  128. return !!localName && typescript_1.default.isIdentifier(node.expression) && node.expression.text === localName;
  129. }
  130. /** Finds the `providers` array literal within an application config. */
  131. function findProvidersLiteral(config) {
  132. for (const prop of config.properties) {
  133. if (typescript_1.default.isPropertyAssignment(prop) &&
  134. typescript_1.default.isIdentifier(prop.name) &&
  135. prop.name.text === 'providers' &&
  136. typescript_1.default.isArrayLiteralExpression(prop.initializer)) {
  137. return prop.initializer;
  138. }
  139. }
  140. return null;
  141. }