Files
nrwl--nx/packages/plugin/src/generators/migration/migration.ts
T
2026-07-13 12:38:36 +08:00

211 lines
5.5 KiB
TypeScript

import { determineArtifactNameAndDirectoryOptions } from '@nx/devkit/internal';
import {
formatFiles,
generateFiles,
joinPathFragments,
offsetFromRoot,
readJson,
readProjectConfiguration,
updateJson,
updateProjectConfiguration,
writeJson,
type Tree,
} from '@nx/devkit';
import { isUsingTsSolutionSetup } from '@nx/js/internal';
import { dirname, join } from 'node:path';
import { PackageJson, readNxMigrateConfig } from 'nx/src/utils/package-json';
import { getArtifactMetadataDirectory } from '../../utils/paths';
import { nxVersion } from '../../utils/versions';
import { addMigrationJsonChecks } from '../lint-checks/generator';
import type { Schema } from './schema';
interface NormalizedSchema extends Schema {
directory: string;
fileName: string;
projectRoot: string;
projectSourceRoot: string;
project: string;
isTsSolutionSetup: boolean;
}
async function normalizeOptions(
tree: Tree,
options: Schema
): Promise<NormalizedSchema> {
const {
artifactName: name,
directory,
fileName,
project,
} = await determineArtifactNameAndDirectoryOptions(tree, {
path: options.path,
name: options.name,
allowedFileExtensions: ['ts'],
fileExtension: 'ts',
});
const { root: projectRoot, sourceRoot: projectSourceRoot } =
readProjectConfiguration(tree, project);
const description: string =
options.description ?? `Migration for v${options.packageVersion}`;
const normalized: NormalizedSchema = {
...options,
directory,
fileName,
project,
name,
description,
projectRoot,
projectSourceRoot: projectSourceRoot ?? join(projectRoot, 'src'),
isTsSolutionSetup: isUsingTsSolutionSetup(tree),
};
return normalized;
}
function addFiles(host: Tree, options: NormalizedSchema) {
generateFiles(host, join(__dirname, 'files/migration'), options.directory, {
...options,
tmpl: '',
});
}
function updateMigrationsJson(host: Tree, options: NormalizedSchema) {
const configuredMigrationPath = readNxMigrateConfig(
readJson<PackageJson>(
host,
joinPathFragments(options.projectRoot, 'package.json')
)
).migrations;
const migrationsPath = joinPathFragments(
options.projectRoot,
configuredMigrationPath ?? 'migrations.json'
);
const migrations = host.exists(migrationsPath)
? readJson(host, migrationsPath)
: {
$schema: `${offsetFromRoot(
dirname(migrationsPath)
)}node_modules/nx/schemas/migrations-schema.json`,
};
const generators = migrations.generators ?? {};
const dir = getArtifactMetadataDirectory(
host,
options.project,
options.directory,
options.isTsSolutionSetup
);
generators[options.name] = {
version: options.packageVersion,
description: options.description,
implementation: `${dir}/${options.fileName}`,
};
migrations.generators = generators;
if (options.packageJsonUpdates) {
const packageJsonUpdatesObj = migrations.packageJsonUpdates ?? {};
if (!packageJsonUpdatesObj[options.packageVersion]) {
packageJsonUpdatesObj[options.packageVersion] = {
version: options.packageVersion,
packages: {},
};
}
migrations.packageJsonUpdates = packageJsonUpdatesObj;
}
writeJson(host, migrationsPath, migrations);
}
function updatePackageJson(host: Tree, options: NormalizedSchema) {
updateJson<PackageJson>(
host,
join(options.projectRoot, 'package.json'),
(json) => {
const addFile = (file: string) => {
if (options.isTsSolutionSetup) {
const filesSet = new Set(json.files ?? ['dist', '!**/*.tsbuildinfo']);
filesSet.add(file.replace(/^\.\//, ''));
json.files = [...filesSet];
}
};
const migrationKey = json['ng-update'] ? 'ng-update' : 'nx-migrations';
if (typeof json[migrationKey] === 'string') {
addFile(json[migrationKey]);
return json;
} else if (!json[migrationKey]) {
json[migrationKey] = {
migrations: './migrations.json',
};
} else if (json[migrationKey].migrations) {
json[migrationKey].migrations = './migrations.json';
}
addFile(json[migrationKey].migrations);
// add dependencies
json.dependencies = {
'@nx/devkit': nxVersion,
...json.dependencies,
};
return json;
}
);
}
function updateProjectConfig(host: Tree, options: NormalizedSchema) {
if (options.isTsSolutionSetup) {
return;
}
const project = readProjectConfiguration(host, options.project);
const assets = project.targets.build?.options?.assets;
if (
assets &&
(assets as any[]).filter((a) => a.glob === 'migrations.json').length === 0
) {
project.targets.build.options.assets = [
...assets,
{
input: `./${options.projectRoot}`,
glob: 'migrations.json',
output: '.',
},
];
updateProjectConfiguration(host, options.project, project);
}
}
export async function migrationGenerator(host: Tree, schema: Schema) {
const options = await normalizeOptions(host, schema);
addFiles(host, options);
updatePackageJson(host, options);
updateMigrationsJson(host, options);
updateProjectConfig(host, options);
if (!host.exists('migrations.json')) {
const packageJsonPath = joinPathFragments(
options.projectRoot,
'package.json'
);
addMigrationJsonChecks(
host,
{ projectName: options.project },
readJson<PackageJson>(host, packageJsonPath)
);
}
if (!options.skipFormat) {
await formatFiles(host);
}
}
export default migrationGenerator;