fix(misc): fix misc issues with move generators (#17814)
This commit is contained in:
parent
c0508e38e3
commit
7055c724dc
4
packages/angular/src/generators/move/lib/index.ts
Normal file
4
packages/angular/src/generators/move/lib/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from './normalize-schema';
|
||||||
|
export * from './update-module-name';
|
||||||
|
export * from './update-ng-package';
|
||||||
|
export * from './update-secondary-entry-points';
|
||||||
15
packages/angular/src/generators/move/lib/normalize-schema.ts
Normal file
15
packages/angular/src/generators/move/lib/normalize-schema.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import type { Tree } from '@nx/devkit';
|
||||||
|
import { readProjectConfiguration } from '@nx/devkit';
|
||||||
|
import type { NormalizedSchema, Schema } from '../schema';
|
||||||
|
import { getNewProjectName } from '../../utils/get-new-project-name';
|
||||||
|
|
||||||
|
export function normalizeSchema(tree: Tree, schema: Schema): NormalizedSchema {
|
||||||
|
const newProjectName = getNewProjectName(schema.destination);
|
||||||
|
const { root } = readProjectConfiguration(tree, schema.projectName);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...schema,
|
||||||
|
newProjectName,
|
||||||
|
oldProjectRoot: root,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ import { Linter } from '@nx/linter';
|
|||||||
import { moveGenerator } from '@nx/workspace/generators';
|
import { moveGenerator } from '@nx/workspace/generators';
|
||||||
import { UnitTestRunner } from '../../../utils/test-runners';
|
import { UnitTestRunner } from '../../../utils/test-runners';
|
||||||
import { generateTestLibrary } from '../../utils/testing';
|
import { generateTestLibrary } from '../../utils/testing';
|
||||||
import { Schema } from '../schema';
|
import { NormalizedSchema } from '../schema';
|
||||||
import { updateModuleName } from './update-module-name';
|
import { updateModuleName } from './update-module-name';
|
||||||
|
|
||||||
describe('updateModuleName Rule', () => {
|
describe('updateModuleName Rule', () => {
|
||||||
@ -20,17 +20,19 @@ describe('updateModuleName Rule', () => {
|
|||||||
name: 'my-first',
|
name: 'my-first',
|
||||||
simpleName: true,
|
simpleName: true,
|
||||||
});
|
});
|
||||||
const schema: Schema = {
|
const schema: NormalizedSchema = {
|
||||||
projectName: 'my-first',
|
projectName: 'my-first',
|
||||||
destination: 'my/first',
|
destination: 'my/first',
|
||||||
updateImportPath: true,
|
updateImportPath: true,
|
||||||
|
newProjectName: 'my-first',
|
||||||
|
oldProjectRoot: 'libs/my-first',
|
||||||
};
|
};
|
||||||
await moveGenerator(tree, schema);
|
await moveGenerator(tree, schema);
|
||||||
|
|
||||||
updateModuleName(tree, { ...schema, destination: 'my/first' });
|
updateModuleName(tree, { ...schema, destination: 'my/first' });
|
||||||
|
|
||||||
expect(tree.exists(updatedModulePath)).toBe(true);
|
expect(tree.exists(updatedModulePath)).toBe(true);
|
||||||
const moduleFile = tree.read(updatedModulePath).toString('utf-8');
|
const moduleFile = tree.read(updatedModulePath, 'utf-8');
|
||||||
expect(moduleFile).toContain(`export class MyFirstModule {}`);
|
expect(moduleFile).toContain(`export class MyFirstModule {}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -42,10 +44,12 @@ describe('updateModuleName Rule', () => {
|
|||||||
const indexPath = '/libs/shared/my-first/src/index.ts';
|
const indexPath = '/libs/shared/my-first/src/index.ts';
|
||||||
const secondModulePath = '/libs/my-second/src/lib/my-second.module.ts';
|
const secondModulePath = '/libs/my-second/src/lib/my-second.module.ts';
|
||||||
|
|
||||||
const schema: Schema = {
|
const schema: NormalizedSchema = {
|
||||||
projectName: 'my-first',
|
projectName: 'my-first',
|
||||||
destination: 'shared/my-first',
|
destination: 'shared/my-first',
|
||||||
updateImportPath: true,
|
updateImportPath: true,
|
||||||
|
newProjectName: 'shared-my-first',
|
||||||
|
oldProjectRoot: 'libs/my-first',
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@ -111,10 +115,10 @@ describe('updateModuleName Rule', () => {
|
|||||||
expect(tree.exists(updatedModulePath)).toBe(true);
|
expect(tree.exists(updatedModulePath)).toBe(true);
|
||||||
expect(tree.exists(updatedModuleSpecPath)).toBe(true);
|
expect(tree.exists(updatedModuleSpecPath)).toBe(true);
|
||||||
|
|
||||||
const moduleFile = tree.read(updatedModulePath).toString('utf-8');
|
const moduleFile = tree.read(updatedModulePath, 'utf-8');
|
||||||
expect(moduleFile).toContain(`export class SharedMyFirstModule {}`);
|
expect(moduleFile).toContain(`export class SharedMyFirstModule {}`);
|
||||||
|
|
||||||
const moduleSpecFile = tree.read(updatedModuleSpecPath).toString('utf-8');
|
const moduleSpecFile = tree.read(updatedModuleSpecPath, 'utf-8');
|
||||||
expect(moduleSpecFile).toContain(
|
expect(moduleSpecFile).toContain(
|
||||||
`import { SharedMyFirstModule } from './shared-my-first.module';`
|
`import { SharedMyFirstModule } from './shared-my-first.module';`
|
||||||
);
|
);
|
||||||
@ -130,7 +134,7 @@ describe('updateModuleName Rule', () => {
|
|||||||
it('should update any references to the module', async () => {
|
it('should update any references to the module', async () => {
|
||||||
updateModuleName(tree, schema);
|
updateModuleName(tree, schema);
|
||||||
|
|
||||||
const importerFile = tree.read(secondModulePath).toString('utf-8');
|
const importerFile = tree.read(secondModulePath, 'utf-8');
|
||||||
expect(importerFile).toContain(
|
expect(importerFile).toContain(
|
||||||
`import { SharedMyFirstModule } from '@proj/shared/my-first';`
|
`import { SharedMyFirstModule } from '@proj/shared/my-first';`
|
||||||
);
|
);
|
||||||
@ -142,7 +146,7 @@ describe('updateModuleName Rule', () => {
|
|||||||
it('should update the index.ts file which exports the module', async () => {
|
it('should update the index.ts file which exports the module', async () => {
|
||||||
updateModuleName(tree, schema);
|
updateModuleName(tree, schema);
|
||||||
|
|
||||||
const indexFile = tree.read(indexPath).toString('utf-8');
|
const indexFile = tree.read(indexPath, 'utf-8');
|
||||||
expect(indexFile).toContain(
|
expect(indexFile).toContain(
|
||||||
`export * from './lib/shared-my-first.module';`
|
`export * from './lib/shared-my-first.module';`
|
||||||
);
|
);
|
||||||
@ -150,10 +154,12 @@ describe('updateModuleName Rule', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('rename', () => {
|
describe('rename', () => {
|
||||||
const schema: Schema = {
|
const schema: NormalizedSchema = {
|
||||||
projectName: 'my-source',
|
projectName: 'my-source',
|
||||||
destination: 'my-destination',
|
destination: 'my-destination',
|
||||||
updateImportPath: true,
|
updateImportPath: true,
|
||||||
|
newProjectName: 'my-destination',
|
||||||
|
oldProjectRoot: 'libs/my-source',
|
||||||
};
|
};
|
||||||
|
|
||||||
const modulePath = '/libs/my-destination/src/lib/my-destination.module.ts';
|
const modulePath = '/libs/my-destination/src/lib/my-destination.module.ts';
|
||||||
@ -233,10 +239,10 @@ describe('updateModuleName Rule', () => {
|
|||||||
expect(tree.exists(modulePath)).toBe(true);
|
expect(tree.exists(modulePath)).toBe(true);
|
||||||
expect(tree.exists(moduleSpecPath)).toBe(true);
|
expect(tree.exists(moduleSpecPath)).toBe(true);
|
||||||
|
|
||||||
const moduleFile = tree.read(modulePath).toString('utf-8');
|
const moduleFile = tree.read(modulePath, 'utf-8');
|
||||||
expect(moduleFile).toContain(`export class MyDestinationModule {}`);
|
expect(moduleFile).toContain(`export class MyDestinationModule {}`);
|
||||||
|
|
||||||
const moduleSpecFile = tree.read(moduleSpecPath).toString('utf-8');
|
const moduleSpecFile = tree.read(moduleSpecPath, 'utf-8');
|
||||||
expect(moduleSpecFile).toContain(
|
expect(moduleSpecFile).toContain(
|
||||||
`import { MyDestinationModule } from './my-destination.module';`
|
`import { MyDestinationModule } from './my-destination.module';`
|
||||||
);
|
);
|
||||||
@ -252,7 +258,7 @@ describe('updateModuleName Rule', () => {
|
|||||||
it('should update any references to the module', async () => {
|
it('should update any references to the module', async () => {
|
||||||
updateModuleName(tree, schema);
|
updateModuleName(tree, schema);
|
||||||
|
|
||||||
const importerFile = tree.read(importerPath).toString('utf-8');
|
const importerFile = tree.read(importerPath, 'utf-8');
|
||||||
expect(importerFile).toContain(
|
expect(importerFile).toContain(
|
||||||
`import { MyDestinationModule } from '@proj/my-destination';`
|
`import { MyDestinationModule } from '@proj/my-destination';`
|
||||||
);
|
);
|
||||||
@ -264,10 +270,32 @@ describe('updateModuleName Rule', () => {
|
|||||||
it('should update the index.ts file which exports the module', async () => {
|
it('should update the index.ts file which exports the module', async () => {
|
||||||
updateModuleName(tree, schema);
|
updateModuleName(tree, schema);
|
||||||
|
|
||||||
const indexFile = tree.read(indexPath).toString('utf-8');
|
const indexFile = tree.read(indexPath, 'utf-8');
|
||||||
expect(indexFile).toContain(
|
expect(indexFile).toContain(
|
||||||
`export * from './lib/my-destination.module';`
|
`export * from './lib/my-destination.module';`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not rename unrelated symbols with similar name in different projects', async () => {
|
||||||
|
// create different project whose main module name starts with the same
|
||||||
|
// name of the project we're moving
|
||||||
|
await generateTestLibrary(tree, {
|
||||||
|
name: 'my-source-demo',
|
||||||
|
buildable: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
publishable: false,
|
||||||
|
simpleName: true,
|
||||||
|
skipFormat: false,
|
||||||
|
unitTestRunner: UnitTestRunner.Jest,
|
||||||
|
});
|
||||||
|
|
||||||
|
updateModuleName(tree, schema);
|
||||||
|
|
||||||
|
const moduleFile = tree.read(
|
||||||
|
'/libs/my-source-demo/src/lib/my-source-demo.module.ts',
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(moduleFile).toContain(`export class MySourceDemoModule {}`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
getProjects,
|
getProjects,
|
||||||
|
joinPathFragments,
|
||||||
names,
|
names,
|
||||||
readProjectConfiguration,
|
readProjectConfiguration,
|
||||||
Tree,
|
Tree,
|
||||||
visitNotIgnoredFiles,
|
visitNotIgnoredFiles,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { getNewProjectName } from '../../utils/get-new-project-name';
|
import type { NormalizedSchema } from '../schema';
|
||||||
import { join } from 'path';
|
|
||||||
import { Schema } from '../schema';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the Angular module name (including the spec file and index.ts)
|
* Updates the Angular module name (including the spec file and index.ts)
|
||||||
@ -19,10 +18,8 @@ import { Schema } from '../schema';
|
|||||||
*/
|
*/
|
||||||
export function updateModuleName(
|
export function updateModuleName(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
{ projectName, destination }: Schema
|
{ projectName: oldProjectName, newProjectName }: NormalizedSchema
|
||||||
): void {
|
): void {
|
||||||
const newProjectName = getNewProjectName(destination);
|
|
||||||
|
|
||||||
const project = readProjectConfiguration(tree, newProjectName);
|
const project = readProjectConfiguration(tree, newProjectName);
|
||||||
|
|
||||||
if (project.projectType === 'application') {
|
if (project.projectType === 'application') {
|
||||||
@ -32,14 +29,14 @@ export function updateModuleName(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const moduleName = {
|
const moduleName = {
|
||||||
from: names(projectName).className,
|
from: `${names(oldProjectName).className}Module`,
|
||||||
to: names(newProjectName).className,
|
to: `${names(newProjectName).className}Module`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const findModuleName = new RegExp(`\\b${moduleName.from}`, 'g');
|
const findModuleName = new RegExp(`\\b${moduleName.from}`, 'g');
|
||||||
|
|
||||||
const moduleFile = {
|
const moduleFile = {
|
||||||
from: `${projectName}.module`,
|
from: `${oldProjectName}.module`,
|
||||||
to: `${newProjectName}.module`,
|
to: `${newProjectName}.module`,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -81,7 +78,7 @@ export function updateModuleName(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// update index file
|
// update index file
|
||||||
const indexFile = join(project.sourceRoot, 'index.ts');
|
const indexFile = joinPathFragments(project.sourceRoot, 'index.ts');
|
||||||
if (tree.exists(indexFile)) {
|
if (tree.exists(indexFile)) {
|
||||||
updateFileContent(tree, replacements, indexFile);
|
updateFileContent(tree, replacements, indexFile);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,13 +6,11 @@ import {
|
|||||||
updateJson,
|
updateJson,
|
||||||
workspaceRoot,
|
workspaceRoot,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { getNewProjectName } from '../../utils/get-new-project-name';
|
|
||||||
import { join, relative } from 'path';
|
import { join, relative } from 'path';
|
||||||
import { Schema } from '../schema';
|
import type { NormalizedSchema } from '../schema';
|
||||||
|
|
||||||
export function updateNgPackage(tree: Tree, schema: Schema): void {
|
export function updateNgPackage(tree: Tree, schema: NormalizedSchema): void {
|
||||||
const newProjectName = getNewProjectName(schema.destination);
|
const project = readProjectConfiguration(tree, schema.newProjectName);
|
||||||
const project = readProjectConfiguration(tree, newProjectName);
|
|
||||||
|
|
||||||
if (project.projectType === 'application') {
|
if (project.projectType === 'application') {
|
||||||
return;
|
return;
|
||||||
@ -29,13 +27,13 @@ export function updateNgPackage(tree: Tree, schema: Schema): void {
|
|||||||
const outputs = getOutputsForTargetAndConfiguration(
|
const outputs = getOutputsForTargetAndConfiguration(
|
||||||
{
|
{
|
||||||
target: {
|
target: {
|
||||||
project: newProjectName,
|
project: schema.newProjectName,
|
||||||
target: 'build',
|
target: 'build',
|
||||||
},
|
},
|
||||||
overrides: {},
|
overrides: {},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: newProjectName,
|
name: schema.newProjectName,
|
||||||
type: 'lib',
|
type: 'lib',
|
||||||
data: {
|
data: {
|
||||||
root: project.root,
|
root: project.root,
|
||||||
|
|||||||
@ -0,0 +1,71 @@
|
|||||||
|
import type { Tree } from '@nx/devkit';
|
||||||
|
import {
|
||||||
|
joinPathFragments,
|
||||||
|
normalizePath,
|
||||||
|
readProjectConfiguration,
|
||||||
|
visitNotIgnoredFiles,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { basename, dirname } from 'path';
|
||||||
|
import type { NormalizedSchema } from '../schema';
|
||||||
|
|
||||||
|
const libraryExecutors = [
|
||||||
|
'@angular-devkit/build-angular:ng-packagr',
|
||||||
|
'@nx/angular:ng-packagr-lite',
|
||||||
|
'@nx/angular:package',
|
||||||
|
// TODO(v17): remove when @nrwl/* scope is removed
|
||||||
|
'@nrwl/angular:ng-packagr-lite',
|
||||||
|
'@nrwl/angular:package',
|
||||||
|
];
|
||||||
|
|
||||||
|
export function updateSecondaryEntryPoints(
|
||||||
|
tree: Tree,
|
||||||
|
schema: NormalizedSchema
|
||||||
|
): void {
|
||||||
|
const project = readProjectConfiguration(tree, schema.newProjectName);
|
||||||
|
|
||||||
|
if (project.projectType !== 'library') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Object.values(project.targets ?? {}).some((target) =>
|
||||||
|
libraryExecutors.includes(target.executor)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitNotIgnoredFiles(tree, project.root, (filePath) => {
|
||||||
|
if (
|
||||||
|
basename(filePath) !== 'ng-package.json' ||
|
||||||
|
normalizePath(filePath) ===
|
||||||
|
joinPathFragments(project.root, 'ng-package.json')
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateReadme(
|
||||||
|
tree,
|
||||||
|
dirname(filePath),
|
||||||
|
schema.projectName,
|
||||||
|
schema.newProjectName
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateReadme(
|
||||||
|
tree: Tree,
|
||||||
|
dir: string,
|
||||||
|
oldProjectName: string,
|
||||||
|
newProjectName: string
|
||||||
|
) {
|
||||||
|
const readmePath = joinPathFragments(dir, 'README.md');
|
||||||
|
if (!tree.exists(readmePath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const findName = new RegExp(`${oldProjectName}`, 'g');
|
||||||
|
const oldContent = tree.read(readmePath, 'utf-8');
|
||||||
|
const newContent = oldContent.replace(findName, newProjectName);
|
||||||
|
tree.write(readmePath, newContent);
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ import { readJson, Tree } from '@nx/devkit';
|
|||||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
import { Linter } from '@nx/linter';
|
import { Linter } from '@nx/linter';
|
||||||
import { UnitTestRunner } from '../../utils/test-runners';
|
import { UnitTestRunner } from '../../utils/test-runners';
|
||||||
|
import librarySecondaryEntryPointGenerator from '../library-secondary-entry-point/library-secondary-entry-point';
|
||||||
import { generateTestLibrary } from '../utils/testing';
|
import { generateTestLibrary } from '../utils/testing';
|
||||||
import { angularMoveGenerator } from './move';
|
import { angularMoveGenerator } from './move';
|
||||||
|
|
||||||
@ -50,6 +51,28 @@ describe('@nx/angular:move', () => {
|
|||||||
expect(ngPackageJson.dest).toEqual('../../dist/libs/mynewlib2');
|
expect(ngPackageJson.dest).toEqual('../../dist/libs/mynewlib2');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should update secondary entry points readme file', async () => {
|
||||||
|
await generateTestLibrary(tree, { name: 'mylib2', buildable: true });
|
||||||
|
await librarySecondaryEntryPointGenerator(tree, {
|
||||||
|
library: 'mylib2',
|
||||||
|
name: 'testing',
|
||||||
|
});
|
||||||
|
|
||||||
|
await angularMoveGenerator(tree, {
|
||||||
|
projectName: 'mylib2',
|
||||||
|
destination: 'mynewlib2',
|
||||||
|
updateImportPath: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const readme = tree.read('libs/mynewlib2/testing/README.md', 'utf-8');
|
||||||
|
expect(readme).toMatchInlineSnapshot(`
|
||||||
|
"# @proj/mynewlib2/testing
|
||||||
|
|
||||||
|
Secondary entry point of \`@proj/mynewlib2\`. It can be used by importing from \`@proj/mynewlib2/testing\`.
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
it('should format files', async () => {
|
it('should format files', async () => {
|
||||||
jest.spyOn(devkit, 'formatFiles');
|
jest.spyOn(devkit, 'formatFiles');
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,12 @@
|
|||||||
import { formatFiles, Tree } from '@nx/devkit';
|
import { formatFiles, Tree } from '@nx/devkit';
|
||||||
import { moveGenerator } from '@nx/workspace/generators';
|
import { moveGenerator } from '@nx/workspace/generators';
|
||||||
import { updateModuleName } from './lib/update-module-name';
|
import {
|
||||||
import { updateNgPackage } from './lib/update-ng-package';
|
normalizeSchema,
|
||||||
import { Schema } from './schema';
|
updateModuleName,
|
||||||
|
updateNgPackage,
|
||||||
|
updateSecondaryEntryPoints,
|
||||||
|
} from './lib';
|
||||||
|
import type { Schema } from './schema';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves an Angular lib/app to another folder (and renames it in the process)
|
* Moves an Angular lib/app to another folder (and renames it in the process)
|
||||||
@ -15,11 +19,14 @@ export async function angularMoveGenerator(
|
|||||||
tree: Tree,
|
tree: Tree,
|
||||||
schema: Schema
|
schema: Schema
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await moveGenerator(tree, { ...schema, skipFormat: true });
|
const normalizedSchema = normalizeSchema(tree, schema);
|
||||||
updateModuleName(tree, schema);
|
|
||||||
updateNgPackage(tree, schema);
|
|
||||||
|
|
||||||
if (!schema.skipFormat) {
|
await moveGenerator(tree, { ...schema, skipFormat: true });
|
||||||
|
updateModuleName(tree, normalizedSchema);
|
||||||
|
updateNgPackage(tree, normalizedSchema);
|
||||||
|
updateSecondaryEntryPoints(tree, normalizedSchema);
|
||||||
|
|
||||||
|
if (!normalizedSchema.skipFormat) {
|
||||||
await formatFiles(tree);
|
await formatFiles(tree);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,3 +5,8 @@ export interface Schema {
|
|||||||
importPath?: string;
|
importPath?: string;
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface NormalizedSchema extends Schema {
|
||||||
|
oldProjectRoot: string;
|
||||||
|
newProjectName: string;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,8 +1,15 @@
|
|||||||
|
import { normalizePath } from '@nx/devkit';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces slashes with dashes
|
* Joins path segments replacing slashes with dashes
|
||||||
*
|
*
|
||||||
* @param path
|
* @param path
|
||||||
*/
|
*/
|
||||||
export function getNewProjectName(path: string): string {
|
export function getNewProjectName(path: string): string {
|
||||||
return path.replace(/\//g, '-');
|
// strip leading '/' or './' or '../' and trailing '/' and replaces '/' with '-'
|
||||||
|
return normalizePath(path)
|
||||||
|
.replace(/(^\.{0,2}\/|\.{1,2}\/|\/$)/g, '')
|
||||||
|
.split('/')
|
||||||
|
.filter((x) => !!x)
|
||||||
|
.join('-');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,6 +46,25 @@ describe('normalizeSchema', () => {
|
|||||||
expect(result).toEqual(expected);
|
expect(result).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should normalize destination and derive projectName correctly', () => {
|
||||||
|
const expected: NormalizedSchema = {
|
||||||
|
destination: 'my/library',
|
||||||
|
importPath: '@proj/my/library',
|
||||||
|
newProjectName: 'my-library',
|
||||||
|
projectName: 'my-library',
|
||||||
|
relativeToRootDestination: 'libs/my/library',
|
||||||
|
updateImportPath: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = normalizeSchema(
|
||||||
|
tree,
|
||||||
|
{ ...schema, destination: './my/library' },
|
||||||
|
projectConfiguration
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
it('should use provided import path', () => {
|
it('should use provided import path', () => {
|
||||||
const expected: NormalizedSchema = {
|
const expected: NormalizedSchema = {
|
||||||
destination: 'my/library',
|
destination: 'my/library',
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import { ProjectConfiguration, Tree } from '@nx/devkit';
|
import { ProjectConfiguration, Tree } from '@nx/devkit';
|
||||||
import type { NormalizedSchema, Schema } from '../schema';
|
import type { NormalizedSchema, Schema } from '../schema';
|
||||||
import { getDestination, getNewProjectName, normalizeSlashes } from './utils';
|
import {
|
||||||
|
getDestination,
|
||||||
|
getNewProjectName,
|
||||||
|
normalizePathSlashes,
|
||||||
|
} from './utils';
|
||||||
import { getImportPath } from '../../../utilities/get-import-path';
|
import { getImportPath } from '../../../utilities/get-import-path';
|
||||||
|
|
||||||
export function normalizeSchema(
|
export function normalizeSchema(
|
||||||
@ -8,9 +12,7 @@ export function normalizeSchema(
|
|||||||
schema: Schema,
|
schema: Schema,
|
||||||
projectConfiguration: ProjectConfiguration
|
projectConfiguration: ProjectConfiguration
|
||||||
): NormalizedSchema {
|
): NormalizedSchema {
|
||||||
const destination = schema.destination.startsWith('/')
|
const destination = normalizePathSlashes(schema.destination);
|
||||||
? normalizeSlashes(schema.destination.slice(1))
|
|
||||||
: schema.destination;
|
|
||||||
const newProjectName =
|
const newProjectName =
|
||||||
schema.newProjectName ?? getNewProjectName(destination);
|
schema.newProjectName ?? getNewProjectName(destination);
|
||||||
|
|
||||||
@ -18,7 +20,8 @@ export function normalizeSchema(
|
|||||||
...schema,
|
...schema,
|
||||||
destination,
|
destination,
|
||||||
importPath:
|
importPath:
|
||||||
schema.importPath ?? normalizeSlashes(getImportPath(tree, destination)),
|
schema.importPath ??
|
||||||
|
normalizePathSlashes(getImportPath(tree, destination)),
|
||||||
newProjectName,
|
newProjectName,
|
||||||
relativeToRootDestination: getDestination(
|
relativeToRootDestination: getDestination(
|
||||||
tree,
|
tree,
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
import { readJson, readProjectConfiguration, Tree } from '@nx/devkit';
|
import {
|
||||||
|
readJson,
|
||||||
|
readProjectConfiguration,
|
||||||
|
Tree,
|
||||||
|
updateJson,
|
||||||
|
} from '@nx/devkit';
|
||||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
import { Schema } from '../schema';
|
import { Schema } from '../schema';
|
||||||
import { updateImports } from './update-imports';
|
import { updateImports } from './update-imports';
|
||||||
@ -292,6 +297,37 @@ export MyExtendedClass extends MyClass {};`
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should update project ref in the root tsconfig.base.json for secondary entry points', async () => {
|
||||||
|
await libraryGenerator(tree, {
|
||||||
|
name: 'my-source',
|
||||||
|
});
|
||||||
|
updateJson(tree, '/tsconfig.base.json', (json) => {
|
||||||
|
json.compilerOptions.paths['@proj/my-source/testing'] = [
|
||||||
|
'libs/my-source/testing/src/index.ts',
|
||||||
|
];
|
||||||
|
json.compilerOptions.paths['@proj/different-alias'] = [
|
||||||
|
'libs/my-source/some-path/src/index.ts',
|
||||||
|
];
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
const projectConfig = readProjectConfiguration(tree, 'my-source');
|
||||||
|
|
||||||
|
updateImports(
|
||||||
|
tree,
|
||||||
|
normalizeSchema(tree, schema, projectConfig),
|
||||||
|
projectConfig
|
||||||
|
);
|
||||||
|
|
||||||
|
const tsConfig = readJson(tree, '/tsconfig.base.json');
|
||||||
|
expect(tsConfig.compilerOptions.paths).toEqual({
|
||||||
|
'@proj/my-destination': ['libs/my-destination/src/index.ts'],
|
||||||
|
'@proj/my-destination/testing': [
|
||||||
|
'libs/my-destination/testing/src/index.ts',
|
||||||
|
],
|
||||||
|
'@proj/different-alias': ['libs/my-destination/some-path/src/index.ts'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should update project ref of a project not under libs in the root tsconfig.base.json', async () => {
|
it('should update project ref of a project not under libs in the root tsconfig.base.json', async () => {
|
||||||
tree.delete('libs');
|
tree.delete('libs');
|
||||||
await libraryGenerator(tree, {
|
await libraryGenerator(tree, {
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import {
|
|||||||
findNodes,
|
findNodes,
|
||||||
} from '../../../utilities/ts-config';
|
} from '../../../utilities/ts-config';
|
||||||
import { NormalizedSchema } from '../schema';
|
import { NormalizedSchema } from '../schema';
|
||||||
import { normalizeSlashes } from './utils';
|
import { normalizePathSlashes } from './utils';
|
||||||
import { relative } from 'path';
|
import { relative } from 'path';
|
||||||
import { ensureTypescript } from '../../../utilities/typescript';
|
import { ensureTypescript } from '../../../utilities/typescript';
|
||||||
import { getImportPath } from '../../../utilities/get-import-path';
|
import { getImportPath } from '../../../utilities/get-import-path';
|
||||||
@ -39,37 +39,63 @@ export function updateImports(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { npmScope, libsDir } = getWorkspaceLayout(tree);
|
const { libsDir } = getWorkspaceLayout(tree);
|
||||||
const projects = getProjects(tree);
|
const projects = getProjects(tree);
|
||||||
|
|
||||||
// use the source root to find the from location
|
// use the source root to find the from location
|
||||||
// this attempts to account for libs that have been created with --importPath
|
// this attempts to account for libs that have been created with --importPath
|
||||||
const tsConfigPath = getRootTsConfigPathInTree(tree);
|
const tsConfigPath = getRootTsConfigPathInTree(tree);
|
||||||
let tsConfig: any;
|
let tsConfig: any;
|
||||||
let fromPath: string;
|
let mainEntryPointImportPath: string;
|
||||||
|
let secondaryEntryPointImportPaths: string[];
|
||||||
if (tree.exists(tsConfigPath)) {
|
if (tree.exists(tsConfigPath)) {
|
||||||
tsConfig = readJson(tree, tsConfigPath);
|
tsConfig = readJson(tree, tsConfigPath);
|
||||||
|
const sourceRoot =
|
||||||
|
project.sourceRoot ?? joinPathFragments(project.root, 'src');
|
||||||
|
|
||||||
fromPath = Object.keys(tsConfig.compilerOptions.paths).find((path) =>
|
mainEntryPointImportPath = Object.keys(
|
||||||
|
tsConfig.compilerOptions?.paths ?? {}
|
||||||
|
).find((path) =>
|
||||||
tsConfig.compilerOptions.paths[path].some((x) =>
|
tsConfig.compilerOptions.paths[path].some((x) =>
|
||||||
x.startsWith(project.sourceRoot)
|
x.startsWith(ensureTrailingSlash(sourceRoot))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
secondaryEntryPointImportPaths = Object.keys(
|
||||||
|
tsConfig.compilerOptions?.paths ?? {}
|
||||||
|
).filter((path) =>
|
||||||
|
tsConfig.compilerOptions.paths[path].some(
|
||||||
|
(x) =>
|
||||||
|
x.startsWith(ensureTrailingSlash(project.root)) &&
|
||||||
|
!x.startsWith(ensureTrailingSlash(sourceRoot))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const projectRef = {
|
mainEntryPointImportPath ??= normalizePathSlashes(
|
||||||
from:
|
|
||||||
fromPath ||
|
|
||||||
normalizeSlashes(
|
|
||||||
getImportPath(
|
getImportPath(
|
||||||
tree,
|
tree,
|
||||||
project.root.slice(libsDir.length).replace(/^\/|\\/, '')
|
project.root.slice(libsDir.length).replace(/^\/|\\/, '')
|
||||||
)
|
)
|
||||||
),
|
);
|
||||||
to: schema.importPath,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (schema.updateImportPath) {
|
const projectRefs = [
|
||||||
|
{
|
||||||
|
from: mainEntryPointImportPath,
|
||||||
|
to: schema.importPath,
|
||||||
|
},
|
||||||
|
...secondaryEntryPointImportPaths.map((p) => ({
|
||||||
|
from: p,
|
||||||
|
// if the import path doesn't start with the main entry point import path,
|
||||||
|
// it's a custom import path we don't know how to update the name, we keep
|
||||||
|
// it as-is, but we'll update the path it points to
|
||||||
|
to: p.startsWith(mainEntryPointImportPath)
|
||||||
|
? p.replace(mainEntryPointImportPath, schema.importPath)
|
||||||
|
: null,
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const projectRef of projectRefs) {
|
||||||
|
if (schema.updateImportPath && projectRef.to) {
|
||||||
const replaceProjectRef = new RegExp(projectRef.from, 'g');
|
const replaceProjectRef = new RegExp(projectRef.from, 'g');
|
||||||
|
|
||||||
for (const [name, definition] of Array.from(projects.entries())) {
|
for (const [name, definition] of Array.from(projects.entries())) {
|
||||||
@ -108,7 +134,7 @@ export function updateImports(
|
|||||||
joinPathFragments(projectRoot.to, relative(projectRoot.from, x))
|
joinPathFragments(projectRoot.to, relative(projectRoot.from, x))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (schema.updateImportPath) {
|
if (schema.updateImportPath && projectRef.to) {
|
||||||
tsConfig.compilerOptions.paths[projectRef.to] = updatedPath;
|
tsConfig.compilerOptions.paths[projectRef.to] = updatedPath;
|
||||||
if (projectRef.from !== projectRef.to) {
|
if (projectRef.from !== projectRef.to) {
|
||||||
delete tsConfig.compilerOptions.paths[projectRef.from];
|
delete tsConfig.compilerOptions.paths[projectRef.from];
|
||||||
@ -116,11 +142,16 @@ export function updateImports(
|
|||||||
} else {
|
} else {
|
||||||
tsConfig.compilerOptions.paths[projectRef.from] = updatedPath;
|
tsConfig.compilerOptions.paths[projectRef.from] = updatedPath;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
writeJson(tree, tsConfigPath, tsConfig);
|
writeJson(tree, tsConfigPath, tsConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ensureTrailingSlash(path: string): string {
|
||||||
|
return path.endsWith('/') ? path : `${path}/`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes imports in a file from one import to another
|
* Changes imports in a file from one import to another
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
getWorkspaceLayout,
|
getWorkspaceLayout,
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
|
normalizePath,
|
||||||
ProjectConfiguration,
|
ProjectConfiguration,
|
||||||
Tree,
|
Tree,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
|
|
||||||
import { Schema } from '../schema';
|
import { Schema } from '../schema';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,12 +35,17 @@ export function getDestination(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces slashes with dashes
|
* Joins path segments replacing slashes with dashes
|
||||||
*
|
*
|
||||||
* @param path
|
* @param path
|
||||||
*/
|
*/
|
||||||
export function getNewProjectName(path: string): string {
|
export function getNewProjectName(path: string): string {
|
||||||
return path.replace(/\//g, '-');
|
// strip leading '/' or './' or '../' and trailing '/' and replaces '/' with '-'
|
||||||
|
return normalizePath(path)
|
||||||
|
.replace(/(^\.{0,2}\/|\.{1,2}\/|\/$)/g, '')
|
||||||
|
.split('/')
|
||||||
|
.filter((x) => !!x)
|
||||||
|
.join('-');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,9 +53,13 @@ export function getNewProjectName(path: string): string {
|
|||||||
*
|
*
|
||||||
* @param input
|
* @param input
|
||||||
*/
|
*/
|
||||||
export function normalizeSlashes(input: string): string {
|
export function normalizePathSlashes(input: string): string {
|
||||||
return input
|
return (
|
||||||
|
normalizePath(input)
|
||||||
|
// strip leading ./ or /
|
||||||
|
.replace(/^\.?\//, '')
|
||||||
.split('/')
|
.split('/')
|
||||||
.filter((x) => !!x)
|
.filter((x) => !!x)
|
||||||
.join('/');
|
.join('/')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user