Emotion upgrade for React and Next plugins (#4088)
* chore(react): update emotion to new versions and new package names on react plugin * chore(nextjs): update emotion to latest version and the new package names * feat(misc): have react and next plugin migrations update emotion imports * add renamePackageImports and renameNpmPackage rules to workspace utils * update migrations to update emotion imports to the new name and version
This commit is contained in:
parent
81ba64ad1f
commit
889b648886
@ -14,6 +14,16 @@
|
||||
"version": "9.3.1-beta.1",
|
||||
"description": "Rename @nrwl/next:dev-server to @nrwl/next:server",
|
||||
"factory": "./src/migrations/update-9-3-1/update-9-3-1"
|
||||
},
|
||||
"rename-emotion-packages-11.0.0": {
|
||||
"version": "11.0.0-beta.0",
|
||||
"description": "Rename emotion packages to match new 11.0.0 package names",
|
||||
"factory": "./src/migrations/update-11-0-0/rename-emotion-packages-11-0-0"
|
||||
},
|
||||
"update-11.0.0": {
|
||||
"version": "11.0.0-beta.0",
|
||||
"description": "Update libraries",
|
||||
"factory": "./src/migrations/update-11-0-0/update-11-0-0"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
@ -47,6 +57,23 @@
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"11.0.0": {
|
||||
"version": "11.0.0-beta.0",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "10.0.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@emotion/server": {
|
||||
"version": "11.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"version": "11.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
|
||||
import { readJsonInTree } from '@nrwl/workspace';
|
||||
import * as path from 'path';
|
||||
import { createEmptyWorkspace, runSchematic } from '@nrwl/workspace/testing';
|
||||
import { emotionServerVersion } from '../../utils/versions';
|
||||
|
||||
describe('Rename Emotion Packages 11.0.0', () => {
|
||||
let tree: Tree;
|
||||
let schematicRunner: SchematicTestRunner;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = Tree.empty();
|
||||
tree = createEmptyWorkspace(tree);
|
||||
schematicRunner = new SchematicTestRunner(
|
||||
'@nrwl/next',
|
||||
path.join(__dirname, '../../../migrations.json')
|
||||
);
|
||||
tree.overwrite(
|
||||
'package.json',
|
||||
JSON.stringify({
|
||||
devDependencies: {
|
||||
'emotion-server': '10.0.27',
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it(`should update emotion, if used, to the new package names`, async () => {
|
||||
tree = await schematicRunner
|
||||
.runSchematicAsync('rename-emotion-packages-11.0.0', {}, tree)
|
||||
.toPromise();
|
||||
|
||||
const packageJson = readJsonInTree(tree, '/package.json');
|
||||
expect(packageJson).toMatchObject({
|
||||
devDependencies: {
|
||||
'@emotion/server': emotionServerVersion,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`should update emotion, if used, to the new package names where imported`, async () => {
|
||||
tree = await runSchematic('lib', { name: 'library-1' }, tree);
|
||||
|
||||
const moduleThatImports = 'libs/library-1/src/importer.ts';
|
||||
tree.create(
|
||||
moduleThatImports,
|
||||
`import serve from 'emotion-server';
|
||||
|
||||
export const doSomething = (...args) => serve(...args);
|
||||
`
|
||||
);
|
||||
|
||||
tree = await schematicRunner
|
||||
.runSchematicAsync('rename-emotion-packages-11.0.0', {}, tree)
|
||||
.toPromise();
|
||||
|
||||
expect(tree.read(moduleThatImports).toString()).toContain(
|
||||
`import serve from '@emotion/server'`
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,12 @@
|
||||
import { chain } from '@angular-devkit/schematics';
|
||||
import { formatFiles, renameNpmPackages } from '@nrwl/workspace';
|
||||
import { emotionServerVersion } from '../../utils/versions';
|
||||
|
||||
export default function update() {
|
||||
return chain([
|
||||
renameNpmPackages({
|
||||
'emotion-server': ['@emotion/server', emotionServerVersion],
|
||||
}),
|
||||
formatFiles(),
|
||||
]);
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
|
||||
import { readJsonInTree } from '@nrwl/workspace';
|
||||
import * as path from 'path';
|
||||
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
|
||||
|
||||
describe('Update 11.0.0', () => {
|
||||
let tree: Tree;
|
||||
let schematicRunner: SchematicTestRunner;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = Tree.empty();
|
||||
tree = createEmptyWorkspace(tree);
|
||||
schematicRunner = new SchematicTestRunner(
|
||||
'@nrwl/react',
|
||||
path.join(__dirname, '../../../migrations.json')
|
||||
);
|
||||
});
|
||||
|
||||
it(`should update libs`, async () => {
|
||||
tree.overwrite(
|
||||
'package.json',
|
||||
JSON.stringify({
|
||||
dependencies: {},
|
||||
devDependencies: {
|
||||
next: '9.5.2',
|
||||
'@emotion/server': '10.0.27',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
tree = await schematicRunner
|
||||
.runSchematicAsync('update-11.0.0', {}, tree)
|
||||
.toPromise();
|
||||
|
||||
const packageJson = readJsonInTree(tree, '/package.json');
|
||||
expect(packageJson).toMatchObject({
|
||||
dependencies: {},
|
||||
devDependencies: {
|
||||
next: '10.0.1',
|
||||
'@emotion/server': '11.0.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
13
packages/next/src/migrations/update-11-0-0/update-11-0-0.ts
Normal file
13
packages/next/src/migrations/update-11-0-0/update-11-0-0.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { chain, Rule } from '@angular-devkit/schematics';
|
||||
import { formatFiles, updatePackagesInPackageJson } from '@nrwl/workspace';
|
||||
import * as path from 'path';
|
||||
|
||||
export default function update(): Rule {
|
||||
return chain([
|
||||
updatePackagesInPackageJson(
|
||||
path.join(__dirname, '../../../', 'migrations.json'),
|
||||
'11.0.0'
|
||||
),
|
||||
formatFiles(),
|
||||
]);
|
||||
}
|
||||
@ -20,7 +20,7 @@ export const NEXT_SPECIFIC_STYLE_DEPENDENCIES = {
|
||||
'@emotion/styled': {
|
||||
dependencies: {
|
||||
...CSS_IN_JS_DEPENDENCIES['@emotion/styled'].dependencies,
|
||||
'emotion-server': emotionServerVersion,
|
||||
'@emotion/server': emotionServerVersion,
|
||||
},
|
||||
devDependencies: CSS_IN_JS_DEPENDENCIES['@emotion/styled'].devDependencies,
|
||||
},
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
export const nxVersion = '*';
|
||||
|
||||
export const nextVersion = '10.0.0';
|
||||
export const nextVersion = '10.0.1';
|
||||
export const zeitNextCss = '1.0.1';
|
||||
export const zeitNextSass = '1.0.1';
|
||||
export const nodeSass = '4.14.1';
|
||||
export const zeitNextLess = '1.0.1';
|
||||
export const zeitNextStylus = '1.0.1';
|
||||
export const emotionServerVersion = '10.0.27';
|
||||
export const emotionServerVersion = '11.0.0';
|
||||
export const babelPluginStyledComponentsVersion = '1.10.7';
|
||||
|
||||
@ -59,6 +59,16 @@
|
||||
"version": "10.4.0-beta.1",
|
||||
"description": "Update libraries",
|
||||
"factory": "./src/migrations/update-10-4-0/update-10-4-0"
|
||||
},
|
||||
"rename-emotion-packages-11.0.0": {
|
||||
"version": "11.0.0-beta.0",
|
||||
"description": "Rename emotion packages to match new 11.0.0 package names",
|
||||
"factory": "./src/migrations/update-11-0-0/rename-emotion-packages-11-0-0"
|
||||
},
|
||||
"update-11.0.0": {
|
||||
"version": "11.0.0-beta.0",
|
||||
"description": "Update libraries",
|
||||
"factory": "./src/migrations/update-11-0-0/update-11-0-0"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
@ -380,6 +390,27 @@
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"11.0.0": {
|
||||
"version": "11.0.0-beta.0",
|
||||
"packages": {
|
||||
"@emotion/react": {
|
||||
"version": "11.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"version": "11.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@emotion/babel-preset-css-prop": {
|
||||
"version": "11.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@emotion/server": {
|
||||
"version": "11.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ function getRollupOptions(options: rollup.RollupOptions) {
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
'styled-components': 'styled',
|
||||
'@emotion/core': 'emotionCore',
|
||||
'@emotion/react': 'emotionReact',
|
||||
'@emotion/styled': 'emotionStyled',
|
||||
};
|
||||
if (Array.isArray(options.output)) {
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
|
||||
import { readJsonInTree } from '@nrwl/workspace';
|
||||
import * as path from 'path';
|
||||
import { createEmptyWorkspace, runSchematic } from '@nrwl/workspace/testing';
|
||||
import { emotionReactVersion } from '../../utils/versions';
|
||||
|
||||
describe('Rename Emotion Packages 11.0.0', () => {
|
||||
let tree: Tree;
|
||||
let schematicRunner: SchematicTestRunner;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = Tree.empty();
|
||||
tree = createEmptyWorkspace(tree);
|
||||
schematicRunner = new SchematicTestRunner(
|
||||
'@nrwl/react',
|
||||
path.join(__dirname, '../../../migrations.json')
|
||||
);
|
||||
tree.overwrite(
|
||||
'package.json',
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
'@emotion/core': '10.1.1',
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it(`should update emotion, if used, to the new package names`, async () => {
|
||||
tree = await schematicRunner
|
||||
.runSchematicAsync('rename-emotion-packages-11.0.0', {}, tree)
|
||||
.toPromise();
|
||||
|
||||
const packageJson = readJsonInTree(tree, '/package.json');
|
||||
expect(packageJson).toMatchObject({
|
||||
dependencies: {
|
||||
'@emotion/react': emotionReactVersion,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`should update emotion, if used, to the new package names where imported`, async () => {
|
||||
tree = await runSchematic('lib', { name: 'library-1' }, tree);
|
||||
|
||||
const moduleThatImports = 'libs/library-1/src/importer.ts';
|
||||
tree.create(
|
||||
moduleThatImports,
|
||||
`import emotion from '@emotion/core';
|
||||
|
||||
export const doSomething = (...args) => something(...args);
|
||||
`
|
||||
);
|
||||
|
||||
tree = await schematicRunner
|
||||
.runSchematicAsync('rename-emotion-packages-11.0.0', {}, tree)
|
||||
.toPromise();
|
||||
|
||||
expect(tree.read(moduleThatImports).toString()).toContain(
|
||||
`import emotion from '@emotion/react'`
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,12 @@
|
||||
import { chain } from '@angular-devkit/schematics';
|
||||
import { formatFiles, renameNpmPackages } from '@nrwl/workspace';
|
||||
import { emotionReactVersion } from '../../utils/versions';
|
||||
|
||||
export default function update() {
|
||||
return chain([
|
||||
renameNpmPackages({
|
||||
'@emotion/core': ['@emotion/react', emotionReactVersion],
|
||||
}),
|
||||
formatFiles(),
|
||||
]);
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
|
||||
import { readJsonInTree } from '@nrwl/workspace';
|
||||
import * as path from 'path';
|
||||
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
|
||||
|
||||
describe('Update 11.0.0', () => {
|
||||
let tree: Tree;
|
||||
let schematicRunner: SchematicTestRunner;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = Tree.empty();
|
||||
tree = createEmptyWorkspace(tree);
|
||||
schematicRunner = new SchematicTestRunner(
|
||||
'@nrwl/react',
|
||||
path.join(__dirname, '../../../migrations.json')
|
||||
);
|
||||
});
|
||||
|
||||
it(`should update libs`, async () => {
|
||||
tree.overwrite(
|
||||
'package.json',
|
||||
JSON.stringify({
|
||||
dependencies: {},
|
||||
devDependencies: {
|
||||
'@emotion/styled': '10.0.27',
|
||||
'@emotion/babel-preset-css-prop': '10.0.27',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
tree = await schematicRunner
|
||||
.runSchematicAsync('update-11.0.0', {}, tree)
|
||||
.toPromise();
|
||||
|
||||
const packageJson = readJsonInTree(tree, '/package.json');
|
||||
expect(packageJson).toMatchObject({
|
||||
dependencies: {},
|
||||
devDependencies: {
|
||||
'@emotion/styled': '11.0.0',
|
||||
'@emotion/babel-preset-css-prop': '11.0.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
13
packages/react/src/migrations/update-11-0-0/update-11-0-0.ts
Normal file
13
packages/react/src/migrations/update-11-0-0/update-11-0-0.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { chain, Rule } from '@angular-devkit/schematics';
|
||||
import { formatFiles, updatePackagesInPackageJson } from '@nrwl/workspace';
|
||||
import * as path from 'path';
|
||||
|
||||
export default function update(): Rule {
|
||||
return chain([
|
||||
updatePackagesInPackageJson(
|
||||
path.join(__dirname, '../../../', 'migrations.json'),
|
||||
'11.0.0'
|
||||
),
|
||||
formatFiles(),
|
||||
]);
|
||||
}
|
||||
@ -531,7 +531,7 @@ describe('app', () => {
|
||||
);
|
||||
|
||||
const packageJSON = readJsonInTree(tree, 'package.json');
|
||||
expect(packageJSON.dependencies['@emotion/core']).toBeDefined();
|
||||
expect(packageJSON.dependencies['@emotion/react']).toBeDefined();
|
||||
expect(packageJSON.dependencies['@emotion/styled']).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@ -175,7 +175,7 @@ describe('component', () => {
|
||||
|
||||
const packageJSON = readJsonInTree(tree, 'package.json');
|
||||
expect(packageJSON.dependencies['@emotion/styled']).toBeDefined();
|
||||
expect(packageJSON.dependencies['@emotion/core']).toBeDefined();
|
||||
expect(packageJSON.dependencies['@emotion/react']).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -455,7 +455,7 @@ describe('lib', () => {
|
||||
|
||||
expect(workspaceJson.projects['my-lib'].architect.build).toMatchObject({
|
||||
options: {
|
||||
external: ['react', 'react-dom', '@emotion/styled', '@emotion/core'],
|
||||
external: ['react', 'react-dom', '@emotion/styled', '@emotion/react'],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@ -22,7 +22,7 @@ export function updateBabelOptions(options: any): void {
|
||||
const packageJson = readJsonFile(join(appRootPath, 'package.json'));
|
||||
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
||||
const hasStyledComponents = !!deps['styled-components'];
|
||||
const hasEmotion = !!deps['@emotion/core'];
|
||||
const hasEmotion = !!deps['@emotion/react'];
|
||||
if (hasStyledComponents && !hasEmotion) {
|
||||
options.plugins.splice(0, 0, [
|
||||
require.resolve('babel-plugin-styled-components'),
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {
|
||||
babelPluginStyledComponentsVersion,
|
||||
emotionBabelPresetCssPropVersion,
|
||||
emotionCoreVersion,
|
||||
emotionReactVersion,
|
||||
emotionStyledVersion,
|
||||
reactIsVersion,
|
||||
styledComponentsVersion,
|
||||
@ -29,7 +29,7 @@ export const CSS_IN_JS_DEPENDENCIES: {
|
||||
'@emotion/styled': {
|
||||
dependencies: {
|
||||
'@emotion/styled': emotionStyledVersion,
|
||||
'@emotion/core': emotionCoreVersion,
|
||||
'@emotion/react': emotionReactVersion,
|
||||
},
|
||||
devDependencies: {
|
||||
'@emotion/babel-preset-css-prop': emotionBabelPresetCssPropVersion,
|
||||
|
||||
@ -11,9 +11,9 @@ export const typesStyledComponentsVersion = '5.1.4';
|
||||
export const reactIsVersion = '17.0.1';
|
||||
export const typesReactIsVersion = '16.7.1';
|
||||
|
||||
export const emotionStyledVersion = '10.0.27';
|
||||
export const emotionCoreVersion = '10.0.28';
|
||||
export const emotionBabelPresetCssPropVersion = '10.0.27';
|
||||
export const emotionStyledVersion = '11.0.0';
|
||||
export const emotionReactVersion = '11.0.0';
|
||||
export const emotionBabelPresetCssPropVersion = '11.0.0';
|
||||
|
||||
export const styledJsxVersion = '3.3.1';
|
||||
export const typesStyledJsxVersion = '2.2.8';
|
||||
|
||||
@ -74,6 +74,8 @@ export * from './src/utils/rules/ng-add';
|
||||
export { updateKarmaConf } from './src/utils/rules/update-karma-conf';
|
||||
export { visitNotIgnoredFiles } from './src/utils/rules/visit-not-ignored-files';
|
||||
export { setDefaultCollection } from './src/utils/rules/workspace';
|
||||
export { renamePackageImports } from './src/utils/rules/rename-package-imports';
|
||||
export { renameNpmPackages } from './src/utils/rules/rename-npm-packages';
|
||||
import * as strings from './src/utils/strings';
|
||||
export { checkAndCleanWithSemver } from './src/utils/version-utils';
|
||||
export { updatePackagesInPackageJson } from './src/utils/update-packages-in-package-json';
|
||||
|
||||
245
packages/workspace/src/utils/rules/rename-npm-packages.spec.ts
Normal file
245
packages/workspace/src/utils/rules/rename-npm-packages.spec.ts
Normal file
@ -0,0 +1,245 @@
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
import { UnitTestTree } from '@angular-devkit/schematics/testing';
|
||||
import { readJsonInTree } from '../ast-utils';
|
||||
import { callRule, runSchematic, createEmptyWorkspace } from '../../../testing';
|
||||
import { renameNpmPackages, PackageRenameMapping } from './rename-npm-packages';
|
||||
|
||||
describe('renameNpmPackages Rule', () => {
|
||||
let tree: UnitTestTree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = new UnitTestTree(Tree.empty());
|
||||
tree = createEmptyWorkspace(tree) as UnitTestTree;
|
||||
});
|
||||
|
||||
it('should rename an npm package in both package.json and any file that imports it', async () => {
|
||||
tree.overwrite(
|
||||
'package.json',
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
'package-to-rename': '1.2.3',
|
||||
},
|
||||
})
|
||||
);
|
||||
tree = await runSchematic('lib', { name: 'library-1' }, tree);
|
||||
|
||||
const moduleThatImports = 'libs/library-1/src/importer.ts';
|
||||
tree.create(
|
||||
moduleThatImports,
|
||||
`import { something } from 'package-to-rename';
|
||||
|
||||
export const doSomething = (...args) => something(...args);
|
||||
`
|
||||
);
|
||||
|
||||
tree = (await callRule(
|
||||
renameNpmPackages({ 'package-to-rename': '@package/renamed' }),
|
||||
tree
|
||||
)) as UnitTestTree;
|
||||
|
||||
const packageJson = readJsonInTree(tree, '/package.json');
|
||||
expect(packageJson).toMatchObject({
|
||||
dependencies: {
|
||||
'@package/renamed': '1.2.3',
|
||||
},
|
||||
});
|
||||
|
||||
expect(tree.read(moduleThatImports).toString()).toContain(
|
||||
`import { something } from '@package/renamed'`
|
||||
);
|
||||
});
|
||||
|
||||
it('should accept a new version that will also be updated in the package.json when renamed', async () => {
|
||||
tree.overwrite(
|
||||
'package.json',
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
'package-to-rename': '1.2.3',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
tree = (await callRule(
|
||||
renameNpmPackages({ 'package-to-rename': ['@package/renamed', '9.9.9'] }),
|
||||
tree
|
||||
)) as UnitTestTree;
|
||||
|
||||
const packageJson = readJsonInTree(tree, '/package.json');
|
||||
expect(packageJson).toMatchObject({
|
||||
dependencies: {
|
||||
'@package/renamed': '9.9.9',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should rename multiple npm packages if more are passed in the PackageRenameMapping', async () => {
|
||||
tree.overwrite(
|
||||
'package.json',
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
'package-to-rename': '1.2.3',
|
||||
},
|
||||
devDependencies: {
|
||||
'@old/packageName': '0.0.1',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
tree = await runSchematic('lib', { name: 'library-1' }, tree);
|
||||
tree = await runSchematic('lib', { name: 'library-2' }, tree);
|
||||
tree = await runSchematic(
|
||||
'preset',
|
||||
{ name: 'app-one', preset: 'angular' },
|
||||
tree
|
||||
);
|
||||
|
||||
const lib1ImportFile = 'libs/library-1/src/importer.ts';
|
||||
tree.create(
|
||||
lib1ImportFile,
|
||||
`import { something } from 'package-to-rename';
|
||||
import { anotherThing } from '@old/packageName';
|
||||
|
||||
export const doSomething = (...args) => something(...args);
|
||||
export const doSomethingElse = (...args) => anotherThing(...args);
|
||||
`
|
||||
);
|
||||
const lib2ImportFile = 'libs/library-2/src/importer.ts';
|
||||
tree.create(
|
||||
lib2ImportFile,
|
||||
`import { something } from 'package-to-rename';
|
||||
|
||||
export const doSomething = (...args) => something(...args);
|
||||
`
|
||||
);
|
||||
const lib2ImportFile2 = 'libs/library-2/src/lib/second-importer.ts';
|
||||
tree.create(
|
||||
lib2ImportFile2,
|
||||
`import { something } from '@old/packageName';
|
||||
|
||||
export const doSomething = (...args) => something(...args);
|
||||
`
|
||||
);
|
||||
|
||||
const appImportFile = 'apps/app-one/src/importer.ts';
|
||||
tree.create(
|
||||
appImportFile,
|
||||
`import { something } from 'package-to-rename';
|
||||
|
||||
export const doSomething = (...args) => something(...args);
|
||||
`
|
||||
);
|
||||
|
||||
tree = (await callRule(
|
||||
renameNpmPackages({
|
||||
'package-to-rename': '@package/renamed',
|
||||
'@old/packageName': 'new-improved-pacakge',
|
||||
}),
|
||||
tree
|
||||
)) as UnitTestTree;
|
||||
|
||||
const packageJson = readJsonInTree(tree, '/package.json');
|
||||
expect(packageJson).toMatchObject({
|
||||
dependencies: {
|
||||
'@package/renamed': '1.2.3',
|
||||
},
|
||||
devDependencies: {
|
||||
'new-improved-pacakge': '0.0.1',
|
||||
},
|
||||
});
|
||||
|
||||
// Lib1 (one file with multiple import name changes)
|
||||
expect(tree.read(lib1ImportFile).toString()).toContain(
|
||||
`import { anotherThing } from 'new-improved-pacakge'`
|
||||
);
|
||||
expect(tree.read(lib1ImportFile).toString()).toContain(
|
||||
`import { something } from '@package/renamed'`
|
||||
);
|
||||
|
||||
// Lib2 (one lib with multiple files with import changes)
|
||||
expect(tree.read(lib2ImportFile).toString()).toContain(
|
||||
`import { something } from '@package/renamed'`
|
||||
);
|
||||
expect(tree.read(lib2ImportFile2).toString()).toContain(
|
||||
`import { something } from 'new-improved-pacakge'`
|
||||
);
|
||||
|
||||
// App (make sure it's changed in apps too)
|
||||
expect(tree.read(appImportFile).toString()).toContain(
|
||||
`import { something } from '@package/renamed'`
|
||||
);
|
||||
});
|
||||
|
||||
it('should only update libs / apps that import the npm package as a dep', async () => {
|
||||
tree.overwrite(
|
||||
'package.json',
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
'package-to-rename': '1.2.3',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
tree = await runSchematic('lib', { name: 'library-1' }, tree);
|
||||
tree = await runSchematic('lib', { name: 'library-2' }, tree);
|
||||
|
||||
const lib1ImportFile = 'libs/library-1/src/importer.ts';
|
||||
tree.create(
|
||||
lib1ImportFile,
|
||||
`import { something } from 'package-to-rename';
|
||||
|
||||
export const doSomething = (...args) => something(...args);
|
||||
`
|
||||
);
|
||||
const lib2ImportFile = 'libs/library-2/src/non-importer.ts';
|
||||
tree.create(
|
||||
lib2ImportFile,
|
||||
`// just a comment about import { something } from 'package-to-rename'`
|
||||
);
|
||||
|
||||
tree = (await callRule(
|
||||
renameNpmPackages({
|
||||
'package-to-rename': '@package/renamed',
|
||||
}),
|
||||
tree
|
||||
)) as UnitTestTree;
|
||||
|
||||
expect(tree.read(lib1ImportFile).toString()).toContain(
|
||||
`import { something } from '@package/renamed'`
|
||||
);
|
||||
|
||||
expect(tree.read(lib2ImportFile).toString()).toContain(
|
||||
`// just a comment about import { something } from 'package-to-rename'`
|
||||
);
|
||||
});
|
||||
|
||||
it('should do nothing if the packages are not found in the package.json', async () => {
|
||||
tree.overwrite(
|
||||
'package.json',
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
'not-me': '1.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
'nor-me': '0.0.2',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
tree = (await callRule(
|
||||
renameNpmPackages({
|
||||
'package-to-rename': '@package/renamed',
|
||||
}),
|
||||
tree
|
||||
)) as UnitTestTree;
|
||||
|
||||
const packageJson = readJsonInTree(tree, '/package.json');
|
||||
expect(packageJson).toMatchObject({
|
||||
dependencies: {
|
||||
'not-me': '1.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
'nor-me': '0.0.2',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
127
packages/workspace/src/utils/rules/rename-npm-packages.ts
Normal file
127
packages/workspace/src/utils/rules/rename-npm-packages.ts
Normal file
@ -0,0 +1,127 @@
|
||||
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(),
|
||||
]);
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,156 @@
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
import { UnitTestTree } from '@angular-devkit/schematics/testing';
|
||||
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
|
||||
import { callRule, runSchematic } from '../testing';
|
||||
import { renamePackageImports } from './rename-package-imports';
|
||||
|
||||
describe('renamePackageImports Rule', () => {
|
||||
let tree: UnitTestTree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = new UnitTestTree(Tree.empty());
|
||||
tree = createEmptyWorkspace(tree) as UnitTestTree;
|
||||
tree.overwrite(
|
||||
'package.json',
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
'package-to-rename': '1.2.3',
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should rename package imports', async () => {
|
||||
tree = await runSchematic('lib', { name: 'library-1' }, tree);
|
||||
|
||||
const moduleThatImports = 'libs/library-1/src/importer.ts';
|
||||
tree.create(
|
||||
moduleThatImports,
|
||||
`import { something } from 'package-to-rename';
|
||||
|
||||
export const doSomething = (...args) => something(...args);
|
||||
`
|
||||
);
|
||||
|
||||
tree = (await callRule(
|
||||
renamePackageImports({ 'package-to-rename': '@package/renamed' }),
|
||||
tree
|
||||
)) as UnitTestTree;
|
||||
|
||||
expect(tree.read(moduleThatImports).toString()).toContain(
|
||||
`import { something } from '@package/renamed'`
|
||||
);
|
||||
});
|
||||
|
||||
it('should be able to rename multiple package imports to the new packageName', async () => {
|
||||
tree = await runSchematic('lib', { name: 'library-1' }, tree);
|
||||
tree = await runSchematic('lib', { name: 'library-2' }, tree);
|
||||
tree = await runSchematic('lib', { name: 'dont-include-me' }, tree);
|
||||
tree = await runSchematic(
|
||||
'preset',
|
||||
{ name: 'app-one', preset: 'angular' },
|
||||
tree
|
||||
);
|
||||
|
||||
const lib1ImportFile = 'libs/library-1/src/importer.ts';
|
||||
tree.create(
|
||||
lib1ImportFile,
|
||||
`import { something } from 'package-to-rename';
|
||||
import { anotherThing } from '@old/packageName';
|
||||
|
||||
export const doSomething = (...args) => something(...args);
|
||||
export const doSomethingElse = (...args) => anotherThing(...args);
|
||||
`
|
||||
);
|
||||
const lib2ImportFile = 'libs/library-2/src/importer.ts';
|
||||
tree.create(
|
||||
lib2ImportFile,
|
||||
`import { something } from 'package-to-rename';
|
||||
|
||||
export const doSomething = (...args) => something(...args);
|
||||
`
|
||||
);
|
||||
const lib2ImportFile2 = 'libs/library-2/src/lib/second-importer.ts';
|
||||
tree.create(
|
||||
lib2ImportFile2,
|
||||
`import { something } from '@old/packageName';
|
||||
|
||||
export const doSomething = (...args) => something(...args);
|
||||
`
|
||||
);
|
||||
|
||||
const appImportFile = 'apps/app-one/src/importer.ts';
|
||||
tree.create(
|
||||
appImportFile,
|
||||
`import { something } from 'package-to-rename';
|
||||
|
||||
export const doSomething = (...args) => something(...args);
|
||||
`
|
||||
);
|
||||
|
||||
tree = (await callRule(
|
||||
renamePackageImports({
|
||||
'package-to-rename': '@package/renamed',
|
||||
'@old/packageName': 'new-improved-pacakge',
|
||||
}),
|
||||
tree
|
||||
)) as UnitTestTree;
|
||||
|
||||
// Lib1 (one file with multiple import name changes)
|
||||
expect(tree.read(lib1ImportFile).toString()).toContain(
|
||||
`import { anotherThing } from 'new-improved-pacakge'`
|
||||
);
|
||||
expect(tree.read(lib1ImportFile).toString()).toContain(
|
||||
`import { something } from '@package/renamed'`
|
||||
);
|
||||
|
||||
// Lib2 (one lib with multiple files with import changes)
|
||||
expect(tree.read(lib2ImportFile).toString()).toContain(
|
||||
`import { something } from '@package/renamed'`
|
||||
);
|
||||
expect(tree.read(lib2ImportFile2).toString()).toContain(
|
||||
`import { something } from 'new-improved-pacakge'`
|
||||
);
|
||||
|
||||
// App (make sure it's changed in apps too)
|
||||
expect(tree.read(appImportFile).toString()).toContain(
|
||||
`import { something } from '@package/renamed'`
|
||||
);
|
||||
});
|
||||
|
||||
it('should NOT modify anything BUT the module import', async () => {
|
||||
tree = await runSchematic('lib', { name: 'library-1' }, tree);
|
||||
|
||||
const moduleThatImports = 'libs/library-1/src/importer.ts';
|
||||
tree.create(
|
||||
moduleThatImports,
|
||||
`// a comment about package-to-rename
|
||||
import { something } from 'package-to-rename';
|
||||
|
||||
// a comment about package-to-rename
|
||||
export const objectThingy = {
|
||||
'package-to-rename': something
|
||||
};
|
||||
`
|
||||
);
|
||||
|
||||
tree = (await callRule(
|
||||
renamePackageImports({ 'package-to-rename': '@package/renamed' }),
|
||||
tree
|
||||
)) as UnitTestTree;
|
||||
|
||||
const fileContents = tree.read(moduleThatImports).toString();
|
||||
|
||||
expect(fileContents).toContain(
|
||||
`import { something } from '@package/renamed'`
|
||||
);
|
||||
// Leave comment alone
|
||||
expect(tree.read(moduleThatImports).toString()).toContain(
|
||||
`// a comment about package-to-rename`
|
||||
);
|
||||
// Leave object key alone
|
||||
expect(tree.read(moduleThatImports).toString()).toContain(
|
||||
`'package-to-rename': something`
|
||||
);
|
||||
});
|
||||
});
|
||||
113
packages/workspace/src/utils/rules/rename-package-imports.ts
Normal file
113
packages/workspace/src/utils/rules/rename-package-imports.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import * as ts from 'typescript';
|
||||
import { SchematicContext, Tree } from '@angular-devkit/schematics';
|
||||
import { getWorkspace } from '@nrwl/workspace';
|
||||
import {
|
||||
getFullProjectGraphFromHost,
|
||||
findNodes,
|
||||
insert,
|
||||
ReplaceChange,
|
||||
} from '@nrwl/workspace/src/utils/ast-utils';
|
||||
|
||||
export interface PackageNameMapping {
|
||||
[packageName: string]: string;
|
||||
}
|
||||
|
||||
const getProjectNamesWithDepsToRename = (
|
||||
packageNameMapping: PackageNameMapping,
|
||||
tree: Tree
|
||||
) => {
|
||||
const packagesToRename = Object.entries(packageNameMapping);
|
||||
const projectGraph = getFullProjectGraphFromHost(tree);
|
||||
|
||||
return Object.entries(projectGraph.dependencies)
|
||||
.filter(([, deps]) =>
|
||||
deps.some(
|
||||
(dep) =>
|
||||
dep.type === 'static' &&
|
||||
packagesToRename.some(
|
||||
([packageName]) => packageName === dep.target.replace('npm:', '')
|
||||
)
|
||||
)
|
||||
)
|
||||
.map(([projectName]) => projectName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates all the imports found in the workspace
|
||||
*
|
||||
* @param packageNameMapping The packageNameMapping provided to the schematic
|
||||
*/
|
||||
export function renamePackageImports(packageNameMapping: PackageNameMapping) {
|
||||
return async (tree: Tree, _context: SchematicContext): Promise<void> => {
|
||||
const workspace = await getWorkspace(tree);
|
||||
|
||||
const projectNamesThatImportAPackageToRename = getProjectNamesWithDepsToRename(
|
||||
packageNameMapping,
|
||||
tree
|
||||
);
|
||||
|
||||
const projectsThatImportPackage = [...workspace.projects].filter(([name]) =>
|
||||
projectNamesThatImportAPackageToRename.includes(name)
|
||||
);
|
||||
|
||||
projectsThatImportPackage
|
||||
.map(([, definition]) => tree.getDir(definition.root))
|
||||
.forEach((projectDir) => {
|
||||
projectDir.visit((file) => {
|
||||
// only look at .(j|t)s(x) files
|
||||
if (!/(j|t)sx?$/.test(file)) {
|
||||
return;
|
||||
}
|
||||
// if it doesn't contain at least 1 reference to the packages to be renamed bail out
|
||||
const contents = tree.read(file).toString('utf-8');
|
||||
if (
|
||||
!Object.keys(packageNameMapping).some((packageName) =>
|
||||
contents.includes(packageName)
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const astSource = ts.createSourceFile(
|
||||
file,
|
||||
contents,
|
||||
ts.ScriptTarget.Latest,
|
||||
true
|
||||
);
|
||||
const changes = Object.entries(packageNameMapping)
|
||||
.map(([packageName, newPackageName]) => {
|
||||
const nodes = findNodes(
|
||||
astSource,
|
||||
ts.SyntaxKind.ImportDeclaration
|
||||
) as ts.ImportDeclaration[];
|
||||
|
||||
return nodes
|
||||
.filter((node) => {
|
||||
return (
|
||||
// remove quotes from module name
|
||||
node.moduleSpecifier.getText().slice(1).slice(0, -1) ===
|
||||
packageName
|
||||
);
|
||||
})
|
||||
.map(
|
||||
(node) =>
|
||||
new ReplaceChange(
|
||||
file,
|
||||
node.moduleSpecifier.getStart(),
|
||||
node.moduleSpecifier.getText(),
|
||||
`'${newPackageName}'`
|
||||
)
|
||||
);
|
||||
})
|
||||
// .flatMap()/.flat() is not available? So, here's a flat poly
|
||||
.reduce((acc, val) => acc.concat(val), []);
|
||||
|
||||
// if the reference to packageName was in fact an import statement
|
||||
if (changes.length > 0) {
|
||||
// update the file in the tree
|
||||
insert(tree, file, changes);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
@ -3,4 +3,4 @@ export {
|
||||
getFileContent,
|
||||
MockBuilderContext,
|
||||
} from './src/utils/testing-utils';
|
||||
export { callRule } from './src/utils/testing';
|
||||
export { callRule, runSchematic } from './src/utils/testing';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user