@nx/angular
Version:
218 lines (217 loc) • 10.6 kB
JavaScript
;
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;