@nestjs/schematics
Version:
Nest - modern, fast, powerful node.js web framework (@schematics)
135 lines (134 loc) • 5.5 kB
JavaScript
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks/index.js';
import { addPackageJsonDependency, getPackageJsonDependency, NodeDependencyType, } from '../../../utils/dependencies.utils.js';
import { JSONFile } from '../../../utils/json-file.util.js';
import { parseMajor, parseMajorMinor, readPackageJson, } from '../upgrade.utils.js';
const NEST_V12 = '^12.0.0';
const GRAPHQL_V14 = '^14.0.0';
export const NEST_V12_PACKAGES = {
'@nestjs/common': NEST_V12,
'@nestjs/core': NEST_V12,
'@nestjs/testing': NEST_V12,
'@nestjs/platform-express': NEST_V12,
'@nestjs/platform-fastify': NEST_V12,
'@nestjs/platform-ws': NEST_V12,
'@nestjs/platform-socket.io': NEST_V12,
'@nestjs/websockets': NEST_V12,
'@nestjs/microservices': NEST_V12,
'@nestjs/cli': NEST_V12,
'@nestjs/schematics': NEST_V12,
'@nestjs/swagger': NEST_V12,
'@nestjs/config': NEST_V12,
'@nestjs/graphql': GRAPHQL_V14,
'@nestjs/apollo': GRAPHQL_V14,
'@nestjs/mercurius': GRAPHQL_V14,
};
const VERSION_AGNOSTIC_PACKAGES = ['@nestjs/mau', '@nestjs/observe'];
const TYPESCRIPT_V6 = '^6.0.0';
const MIN_NODE_ENGINE = '>=20.19.0';
const ALL_DEPENDENCY_TYPES = [
NodeDependencyType.Default,
NodeDependencyType.Dev,
NodeDependencyType.Peer,
NodeDependencyType.Optional,
];
export function updateNestDependencies(options, report) {
return (tree) => {
const updated = [];
for (const [name, version] of Object.entries(NEST_V12_PACKAGES)) {
const dependency = getPackageJsonDependency(tree, name);
if (!dependency) {
continue;
}
const target = options.tag ? options.tag : version;
if (dependency.version !== target) {
addPackageJsonDependency(tree, {
...dependency,
version: target,
overwrite: true,
});
}
updated.push(`${name}@${target}`);
}
if (updated.length > 0) {
report.change(`Updated NestJS packages: ${updated.join(', ')}`);
}
const unknown = findUnknownNestPackages(tree);
if (unknown.length > 0) {
report.warn(`The following @nestjs packages were left untouched because their v12-compatible release is not known to this schematic: ${unknown.join(', ')}. ` +
`Review them with "npx npm-check-updates '/^@nestjs\\//i' -u" and update them to releases that support NestJS 12.`);
}
const typescript = getPackageJsonDependency(tree, 'typescript');
const typescriptMajor = parseMajor(typescript?.version);
if (typescript && typescriptMajor !== null && typescriptMajor < 6) {
addPackageJsonDependency(tree, {
...typescript,
version: TYPESCRIPT_V6,
overwrite: true,
});
report.change(`Updated "typescript" from "${typescript.version}" to "${TYPESCRIPT_V6}" (required by @nestjs/cli and @nestjs/schematics v12)`);
report.warn('TypeScript 6 deprecates several legacy "tsconfig.json" options (e.g. "baseUrl", "esModuleInterop" defaults changed). Run "npx tsc --noEmit" after installing and adjust your configuration if needed.');
}
updateNodeEngine(tree, report);
return tree;
};
}
function updateNodeEngine(tree, report) {
const engines = readPackageJson(tree)?.engines;
const current = engines?.node;
if (typeof current !== 'string') {
return;
}
const lowest = current
.split('||')
.map((range) => parseMajorMinor(range) ?? [parseMajor(range) ?? NaN, 0])
.filter(([major]) => !Number.isNaN(major))
.sort((a, b) => a[0] - b[0] || a[1] - b[1])[0];
if (!lowest) {
return;
}
const [major, minor] = lowest;
const belowMinimum = major < 20 || (major === 20 && minor < 19);
if (!belowMinimum) {
return;
}
new JSONFile(tree, '/package.json').modify(['engines', 'node'], MIN_NODE_ENGINE);
report.change(`Updated "engines.node" from "${current}" to "${MIN_NODE_ENGINE}" (NestJS 12 minimum)`);
}
function findUnknownNestPackages(tree) {
const packageJson = readPackageJson(tree) ?? {};
const unknown = new Set();
for (const type of ALL_DEPENDENCY_TYPES) {
for (const name of Object.keys(packageJson[type] ?? {})) {
if (name.startsWith('@nestjs/') &&
!(name in NEST_V12_PACKAGES) &&
!VERSION_AGNOSTIC_PACKAGES.includes(name)) {
unknown.add(name);
}
}
}
return [...unknown].sort();
}
export function detectPackageManager(tree) {
if (tree.exists('bun.lock') || tree.exists('bun.lockb')) {
return 'bun';
}
if (tree.exists('pnpm-lock.yaml')) {
return 'pnpm';
}
if (tree.exists('yarn.lock')) {
return 'yarn';
}
return 'npm';
}
export function installDependencies(options, report) {
return (tree, context) => {
if (options.skipInstall) {
report.action("Dependencies were not installed (--skip-install). Run your package manager's install command to finish the upgrade.");
return tree;
}
const packageManager = detectPackageManager(tree);
context.addTask(new NodePackageInstallTask({ packageManager }));
report.change(`Scheduled "${packageManager} install"`);
return tree;
};
}