import { chain, Rule, noop, Tree, SchematicContext, } from '@angular-devkit/schematics'; import { addDepsToPackageJson, updateJsonInTree, readJsonInTree, } from '../ast-utils'; import { renamePackageImports, PackageNameMapping, } from './rename-package-imports'; import { formatFiles } from './format-files'; export interface PackageRenameMapping { [packageName: string]: string | [newPackageName: string, version: string]; } interface NormalizedRenameDescriptors { packageName: string; newPackageName: string; version: string; isDevDep: boolean; inPackageJson: boolean; } const normalizeToDescriptors = (packageJson: any) => ([ packageName, newPackageNameConfig, ]): NormalizedRenameDescriptors => { const isDevDep = !!packageJson.devDependencies && packageName in packageJson.devDependencies; const inPackageJson = (packageJson.dependencies && packageName in packageJson.dependencies) || isDevDep; const newPackageName = Array.isArray(newPackageNameConfig) ? newPackageNameConfig[0] : newPackageNameConfig; const version = Array.isArray(newPackageNameConfig) && newPackageNameConfig[1] ? newPackageNameConfig[1] : isDevDep ? packageJson.devDependencies[packageName] : packageJson.dependencies[packageName]; return { packageName, newPackageName, version, isDevDep, inPackageJson, }; }; /** * Updates all the imports in the workspace, and adjust the package.json appropriately. * * @param packageNameMapping The packageNameMapping provided to the schematic */ export function renameNpmPackages(packageRenameMapping: PackageRenameMapping) { return (tree: Tree, context: SchematicContext): Rule => { const pkg = readJsonInTree(tree, 'package.json'); const renameDescriptors = Object.entries(packageRenameMapping).map( normalizeToDescriptors(pkg) ); // if you don't find the packageName in package.json abort if ( renameDescriptors.filter(({ inPackageJson }) => inPackageJson).length === 0 ) { return noop(); } const packageNameMapping: PackageNameMapping = renameDescriptors.reduce( (mapping, { packageName, newPackageName }) => { mapping[packageName] = newPackageName; return mapping; }, {} ); const depAdditions = renameDescriptors.reduce( (mapping, { newPackageName, version, isDevDep }) => { if (!isDevDep) { mapping[newPackageName] = version; } return mapping; }, {} ); const devDepAdditions = renameDescriptors.reduce( (mapping, { newPackageName, version, isDevDep }) => { if (isDevDep) { mapping[newPackageName] = version; } return mapping; }, {} ); return chain([ // rename all the imports before the package.json changes and we can't find the imports renamePackageImports(packageNameMapping), // add the new name at either the old version or a new version addDepsToPackageJson(depAdditions, devDepAdditions), // delete the old entry from the package.json updateJsonInTree('package.json', (json) => { renameDescriptors.forEach(({ packageName, isDevDep }) => { if (isDevDep) { delete json.devDependencies[packageName]; } else { delete json.dependencies[packageName]; } }); return json; }), formatFiles(), ]); }; }