UNPKG

@nx/angular

Version:

The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: - Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, Playwright and Cypre

218 lines (217 loc) • 10.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProjectMigrator = void 0; const devkit_1 = require("@nx/devkit"); const node_path_1 = require("node:path"); const utilities_1 = require("../../utilities"); const migrator_1 = require("../migrator"); class ProjectMigrator extends migrator_1.Migrator { get projectName() { return this.project.name; } constructor(tree, options, targets, project, rootDir, logger, // TODO(leo): this will replace `targets` and become required once the full // refactor is done. supportedBuilderMigrators) { super(tree, project.config, logger ?? new utilities_1.Logger(project.name)); this.options = options; this.targets = targets; // TODO(leo): need to handle targets until all are converted to builder migrators, // at that point, project migrators only care about skipping the whole project migration // and each builder migrator will handle whether it should be skipped or not this.skipMigration = false; this.targetNames = {}; this.project = { name: project.name, oldRoot: this.projectConfig.root ?? '', oldSourceRoot: this.projectConfig.sourceRoot || (0, devkit_1.joinPathFragments)(this.projectConfig.root ?? '', 'src'), newRoot: `${rootDir}/${project.name}`, newSourceRoot: `${rootDir}/${project.name}/src`, }; this.collectTargetNames(); this.createBuilderMigrators(supportedBuilderMigrators); } getWorkspaceRootFileTypesInfo() { const workspaceRootFileTypesInfo = { eslint: Boolean(this.projectConfig.targets?.lint) || this.tree.exists(`${this.projectConfig.root}/.eslintrc.json`), karma: this.builderMigrators.some((migrator) => migrator.rootFileType === 'karma' && migrator.isBuilderUsed()), }; return workspaceRootFileTypesInfo; } migrate() { const validationResult = this.validate(); if (!validationResult) { this.updateGitAndPrettierIgnore(); return; } this.logger.warn((0, utilities_1.getProjectValidationResultMessage)(validationResult)); } validate() { const errors = []; // check project root if (this.projectConfig.root === undefined || this.projectConfig.root === null) { errors.push({ message: 'The project root is not defined in the project configuration. The project will be skipped.', hint: 'Make sure to manually migrate its configuration and files or remove the project if it is not valid. ' + `Alternatively, you could revert the migration, ensure the value for "projects.${this.project.name}.root" ` + 'is set and run the migration again.', }); this.skipProjectMigration(); } else if (this.projectConfig.root !== '' && !this.tree.exists(this.projectConfig.root)) { errors.push({ message: `The project root "${this.project.oldRoot}" could not be found. The project will be skipped.`, hint: 'Make sure to manually migrate its configuration and files or remove the project if it is not valid. ' + `Alternatively, you could revert the migration, ensure the value for "projects.${this.project.name}.root" ` + 'is correct and run the migration again.', }); this.skipProjectMigration(); } // check project source root if (this.projectConfig.sourceRoot && !this.tree.exists(this.projectConfig.sourceRoot)) { errors.push({ message: `The project source root "${this.project.oldSourceRoot}" could not be found. The project will be skipped.`, hint: 'Make sure to manually migrate its configuration and files or remove the project if it is not valid. ' + `Alternatively, you could revert the migration, ensure the value for "projects.${this.project.name}.sourceRoot" ` + 'is correct and run the migration again.', }); this.skipProjectMigration(); } // check for usage of unsupported builders const allSupportedBuilders = [ ...Object.values(this.targets) .map((x) => x.builders) .flat(), ]; allSupportedBuilders.push(...this.builderMigrators.flatMap((migrator) => migrator.possibleBuilderNames)); const unsupportedBuilders = []; Object.entries(this.projectConfig.targets ?? {}).forEach(([targetName, target]) => { if (!allSupportedBuilders.includes(target.executor)) { unsupportedBuilders.push([targetName, target.executor]); } }); if (unsupportedBuilders.length) { errors.push({ messageGroup: { title: 'Unsupported builders', messages: unsupportedBuilders.map(([target, builder]) => `The "${target}" target is using a builder "${builder}" that's not currently supported by the automated migration. ` + 'The target will be skipped.'), }, hint: 'Make sure to manually migrate the target configuration and any possible associated files. Alternatively, you could ' + `revert the migration, change the builder to one of the builders supported by the automated migration (${(0, utilities_1.arrayToString)(allSupportedBuilders)}), and run the migration again.`, }); } // check for multiple targets for the same type of target const targetTypes = Object.keys(this.targets); const targetsByType = Object.entries(this.projectConfig.targets ?? {}).reduce((acc, [target, { executor }]) => { targetTypes.forEach((targetType) => { if (this.targets[targetType].builders.includes(executor)) { acc[targetType].push(target); return acc; } }); return acc; }, targetTypes.reduce((acc, targetType) => ({ ...acc, [targetType]: [] }), {})); targetTypes.forEach((targetType) => { if (this.targets[targetType].acceptMultipleDefinitions || targetsByType[targetType].length <= 1) { return; } errors.push({ message: `There is more than one target using a builder that is used to ${targetType} the project (${(0, utilities_1.arrayToString)(targetsByType[targetType])}). This is not currently supported by the automated migration. These targets will be skipped.`, hint: 'Make sure to manually migrate their configuration and any possible associated files.', }); this.skipTargetTypeMigration(targetType); }); return errors.length ? errors : null; } convertPath(originalPath) { if (originalPath?.startsWith(this.project.oldSourceRoot)) { return (0, devkit_1.joinPathFragments)(this.project.newSourceRoot, originalPath.replace(this.project.oldSourceRoot, '')); } if (originalPath?.startsWith(this.project.oldRoot)) { return (0, devkit_1.joinPathFragments)(this.project.newRoot, originalPath.replace(this.project.oldRoot, '')); } return originalPath; } moveDir(from, to) { this.visitFiles(from, (file) => { this.moveFile(file, (0, devkit_1.normalizePath)(file).replace(from, to), true); }); } shouldSkipTargetTypeMigration(targetType) { return (Array.isArray(this.skipMigration) && this.skipMigration.includes(targetType)); } visitFiles(dirPath, visitor) { dirPath = (0, devkit_1.normalizePath)((0, node_path_1.relative)(this.tree.root, (0, devkit_1.joinPathFragments)(this.tree.root, dirPath))); for (const child of this.tree.children(dirPath)) { const fullPath = (0, devkit_1.joinPathFragments)(dirPath, child); if (this.tree.isFile(fullPath)) { visitor(fullPath); } else { this.visitFiles(fullPath, visitor); } } } updateGitAndPrettierIgnore() { if (!this.tree.exists('.prettierignore') && !this.tree.exists('.gitignore')) { return; } let from = this.project.oldRoot; let to = this.project.newRoot; if (this.project.oldRoot === '') { from = this.project.oldSourceRoot; to = this.project.newSourceRoot; } const regex = new RegExp(`(^!?\\/?)(${from})(\\/|$)`, 'gm'); if (this.tree.exists('.gitignore')) { const gitignore = this.tree.read('.gitignore', 'utf-8'); const content = gitignore.replace(regex, `$1${to}$3`); this.tree.write('.gitignore', content); } if (this.tree.exists('.prettierignore')) { const prettierIgnore = this.tree.read('.prettierignore', 'utf-8'); const content = prettierIgnore.replace(regex, `$1${to}$3`); this.tree.write('.prettierignore', content); } } collectTargetNames() { const targetTypes = Object.keys(this.targets); Object.entries(this.projectConfig.targets ?? {}).forEach(([targetName, target]) => { targetTypes.forEach((targetType) => { if (!this.targetNames[targetType] && this.targets[targetType].builders.includes(target.executor)) { this.targetNames[targetType] = targetName; } }); }); } createBuilderMigrators(supportedBuilderMigrators) { if (!supportedBuilderMigrators) { this.builderMigrators = []; return; } this.builderMigrators = supportedBuilderMigrators.map((migratorClass) => new migratorClass(this.tree, this.project, this.projectConfig, this.logger)); } skipProjectMigration() { this.skipMigration = true; } skipTargetTypeMigration(targetType) { if (this.skipMigration === true) { return; } if (!Array.isArray(this.skipMigration)) { this.skipMigration = []; } this.skipMigration.push(targetType); } } exports.ProjectMigrator = ProjectMigrator;