index.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  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.io/license
  8. */
  9. Object.defineProperty(exports, "__esModule", { value: true });
  10. const core_1 = require("@angular-devkit/core");
  11. const glob_1 = require("glob");
  12. const path_1 = require("path");
  13. const ts = require("typescript");
  14. const component_resource_collector_1 = require("./component-resource-collector");
  15. const parse_tsconfig_1 = require("./utils/parse-tsconfig");
  16. function runMigrationRules(tree, logger, tsconfigPath, targetVersion, ruleTypes, upgradeData, analyzedFiles) {
  17. // The CLI uses the working directory as the base directory for the
  18. // virtual file system tree.
  19. const basePath = process.cwd();
  20. const parsed = parse_tsconfig_1.parseTsconfigFile(tsconfigPath, path_1.dirname(tsconfigPath));
  21. const host = ts.createCompilerHost(parsed.options, true);
  22. // We need to overwrite the host "readFile" method, as we want the TypeScript
  23. // program to be based on the file contents in the virtual file tree.
  24. host.readFile = fileName => {
  25. const buffer = tree.read(getProjectRelativePath(fileName));
  26. // Strip BOM as otherwise TSC methods (e.g. "getWidth") will return an offset which
  27. // which breaks the CLI UpdateRecorder. https://github.com/angular/angular/pull/30719
  28. return buffer ? buffer.toString().replace(/^\uFEFF/, '') : undefined;
  29. };
  30. const program = ts.createProgram(parsed.fileNames, parsed.options, host);
  31. const typeChecker = program.getTypeChecker();
  32. const rules = [];
  33. // Create instances of all specified migration rules.
  34. for (const ruleCtor of ruleTypes) {
  35. const rule = new ruleCtor(program, typeChecker, targetVersion, upgradeData);
  36. rule.getUpdateRecorder = getUpdateRecorder;
  37. rule.init();
  38. if (rule.ruleEnabled) {
  39. rules.push(rule);
  40. }
  41. }
  42. const sourceFiles = program.getSourceFiles().filter(f => !f.isDeclarationFile && !program.isSourceFileFromExternalLibrary(f));
  43. const resourceCollector = new component_resource_collector_1.ComponentResourceCollector(typeChecker);
  44. const updateRecorderCache = new Map();
  45. sourceFiles.forEach(sourceFile => {
  46. const relativePath = getProjectRelativePath(sourceFile.fileName);
  47. // Do not visit source files which have been checked as part of a
  48. // previously migrated TypeScript project.
  49. if (!analyzedFiles.has(relativePath)) {
  50. _visitTypeScriptNode(sourceFile);
  51. analyzedFiles.add(relativePath);
  52. }
  53. });
  54. resourceCollector.resolvedTemplates.forEach(template => {
  55. const relativePath = getProjectRelativePath(template.filePath);
  56. // Do not visit the template if it has been checked before. Inline
  57. // templates cannot be referenced multiple times.
  58. if (template.inline || !analyzedFiles.has(relativePath)) {
  59. rules.forEach(r => r.visitTemplate(template));
  60. analyzedFiles.add(relativePath);
  61. }
  62. });
  63. resourceCollector.resolvedStylesheets.forEach(stylesheet => {
  64. const relativePath = getProjectRelativePath(stylesheet.filePath);
  65. // Do not visit the stylesheet if it has been checked before. Inline
  66. // stylesheets cannot be referenced multiple times.
  67. if (stylesheet.inline || !analyzedFiles.has(relativePath)) {
  68. rules.forEach(r => r.visitStylesheet(stylesheet));
  69. analyzedFiles.add(relativePath);
  70. }
  71. });
  72. // In some applications, developers will have global stylesheets which are not specified in any
  73. // Angular component. Therefore we glob up all CSS and SCSS files outside of node_modules and
  74. // dist. The files will be read by the individual stylesheet rules and checked.
  75. // TODO(devversion): double-check if we can solve this in a more elegant way.
  76. glob_1.sync('!(node_modules|dist)/**/*.+(css|scss)', { absolute: true, cwd: basePath })
  77. .filter(filePath => !resourceCollector.resolvedStylesheets.some(s => s.filePath === filePath))
  78. .forEach(filePath => {
  79. const stylesheet = resourceCollector.resolveExternalStylesheet(filePath, null);
  80. const relativePath = getProjectRelativePath(filePath);
  81. // do not visit stylesheets which have been referenced from a component.
  82. if (!analyzedFiles.has(relativePath)) {
  83. rules.forEach(r => r.visitStylesheet(stylesheet));
  84. }
  85. });
  86. // Commit all recorded updates in the update recorder. We need to perform the
  87. // replacements per source file in order to ensure that offsets in the TypeScript
  88. // program are not incorrectly shifted.
  89. updateRecorderCache.forEach(recorder => tree.commitUpdate(recorder));
  90. // Collect all failures reported by individual migration rules.
  91. const ruleFailures = rules.reduce((res, rule) => res.concat(rule.failures), []);
  92. // In case there are rule failures, print these to the CLI logger as warnings.
  93. if (ruleFailures.length) {
  94. ruleFailures.forEach(({ filePath, message, position }) => {
  95. const normalizedFilePath = core_1.normalize(getProjectRelativePath(filePath));
  96. const lineAndCharacter = `${position.line + 1}:${position.character + 1}`;
  97. logger.warn(`${normalizedFilePath}@${lineAndCharacter} - ${message}`);
  98. });
  99. }
  100. return !!ruleFailures.length;
  101. function getUpdateRecorder(filePath) {
  102. const treeFilePath = getProjectRelativePath(filePath);
  103. if (updateRecorderCache.has(treeFilePath)) {
  104. return updateRecorderCache.get(treeFilePath);
  105. }
  106. const treeRecorder = tree.beginUpdate(treeFilePath);
  107. updateRecorderCache.set(treeFilePath, treeRecorder);
  108. return treeRecorder;
  109. }
  110. function _visitTypeScriptNode(node) {
  111. rules.forEach(r => r.visitNode(node));
  112. ts.forEachChild(node, _visitTypeScriptNode);
  113. resourceCollector.visitNode(node);
  114. }
  115. /** Gets the specified path relative to the project root in POSIX format. */
  116. function getProjectRelativePath(filePath) {
  117. return path_1.relative(basePath, filePath).replace(/\\/g, '/');
  118. }
  119. }
  120. exports.runMigrationRules = runMigrationRules;
  121. //# sourceMappingURL=index.js.map