fix(core): read project name from package json if not set in project json (#26386)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> ## Current Behavior If `project.json` exists without a name, we infer one based on the root path. This can be confusing when there exists a `package.json` alongside it that contains a name which doesn't match our inferred name. ## Expected Behavior If `project.json` and `package.json` both exist, the name from `package.json` will be used if `project.json` contains no name. ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #26347
This commit is contained in:
parent
471f82c991
commit
5d56e21163
@ -83,6 +83,12 @@
|
|||||||
"version": "19.2.2-beta.0",
|
"version": "19.2.2-beta.0",
|
||||||
"description": "Updates the nx wrapper.",
|
"description": "Updates the nx wrapper.",
|
||||||
"implementation": "./src/migrations/update-17-3-0/update-nxw"
|
"implementation": "./src/migrations/update-17-3-0/update-nxw"
|
||||||
|
},
|
||||||
|
"19-2-4-set-project-name": {
|
||||||
|
"version": "19.2.4-beta.0",
|
||||||
|
"description": "Set project name in nx.json explicitly",
|
||||||
|
"implementation": "./src/migrations/update-19-2-4/set-project-name",
|
||||||
|
"x-repair-skip": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,100 @@
|
|||||||
|
import { createTreeWithEmptyWorkspace } from '../../generators/testing-utils/create-tree-with-empty-workspace';
|
||||||
|
import { readJson } from '../../generators/utils/json';
|
||||||
|
import migrate from './set-project-name';
|
||||||
|
|
||||||
|
describe('set project name', () => {
|
||||||
|
it('should not update packageJson projects', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
tree.write(
|
||||||
|
'libs/proj/package.json',
|
||||||
|
JSON.stringify({
|
||||||
|
name: '@scoped/package',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
await migrate(tree);
|
||||||
|
expect(tree.exists('libs/proj/project.json')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not update projectJson if name specified', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
tree.write(
|
||||||
|
'libs/proj/project.json',
|
||||||
|
JSON.stringify({
|
||||||
|
name: 'foo',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
await migrate(tree);
|
||||||
|
const project = readJson(tree, 'libs/proj/project.json');
|
||||||
|
expect(project.name).toBe('foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not update projectJson if name is not specified but no sibling package json', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
tree.write('libs/proj/project.json', JSON.stringify({}));
|
||||||
|
await migrate(tree);
|
||||||
|
const project = readJson(tree, 'libs/proj/project.json');
|
||||||
|
expect(project.name).not.toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not update projectJson if name is identical to package name', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
tree.write(
|
||||||
|
'libs/proj/package.json',
|
||||||
|
JSON.stringify({
|
||||||
|
name: 'proj',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
tree.write('libs/proj/project.json', JSON.stringify({}));
|
||||||
|
await migrate(tree);
|
||||||
|
const project = readJson(tree, 'libs/proj/project.json');
|
||||||
|
expect(project.name).not.toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not update projectJson if name is identical to name in nx field', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
tree.write(
|
||||||
|
'libs/proj/package.json',
|
||||||
|
JSON.stringify({
|
||||||
|
name: '@scoped/proj',
|
||||||
|
nx: {
|
||||||
|
name: 'proj',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
tree.write('libs/proj/project.json', JSON.stringify({}));
|
||||||
|
await migrate(tree);
|
||||||
|
const project = readJson(tree, 'libs/proj/project.json');
|
||||||
|
expect(project.name).not.toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update projectJson if name is not specified and package name is different', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
tree.write(
|
||||||
|
'libs/proj/package.json',
|
||||||
|
JSON.stringify({
|
||||||
|
name: '@scoped/proj',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
tree.write('libs/proj/project.json', JSON.stringify({}));
|
||||||
|
await migrate(tree);
|
||||||
|
const project = readJson(tree, 'libs/proj/project.json');
|
||||||
|
expect(project.name).toBe('proj');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update projectJson if name is not specified and name in nx field is different', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
tree.write(
|
||||||
|
'libs/foo/package.json',
|
||||||
|
JSON.stringify({
|
||||||
|
name: '@scoped/proj',
|
||||||
|
nx: {
|
||||||
|
name: 'proj',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
tree.write('libs/foo/project.json', JSON.stringify({}));
|
||||||
|
await migrate(tree);
|
||||||
|
const project = readJson(tree, 'libs/foo/project.json');
|
||||||
|
expect(project.name).toBe('foo');
|
||||||
|
});
|
||||||
|
});
|
||||||
43
packages/nx/src/migrations/update-19-2-4/set-project-name.ts
Normal file
43
packages/nx/src/migrations/update-19-2-4/set-project-name.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { toProjectName } from '../../config/to-project-name';
|
||||||
|
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||||
|
import { Tree } from '../../generators/tree';
|
||||||
|
import { formatChangedFilesWithPrettierIfAvailable } from '../../generators/internal-utils/format-changed-files-with-prettier-if-available';
|
||||||
|
import { readJson, writeJson } from '../../generators/utils/json';
|
||||||
|
import { getProjects } from '../../generators/utils/project-configuration';
|
||||||
|
import type { PackageJson } from '../../utils/package-json';
|
||||||
|
|
||||||
|
export default async function setProjectName(tree: Tree) {
|
||||||
|
// We are explicitly looking for project.json files here, so getProjects is fine.
|
||||||
|
const projects = getProjects(tree);
|
||||||
|
|
||||||
|
for (const { root } of projects.values()) {
|
||||||
|
const projectJsonPath = `${root}/project.json`;
|
||||||
|
const packageJsonPath = `${root}/package.json`;
|
||||||
|
|
||||||
|
// If either of these files doesn't exist, theres no behavioral difference
|
||||||
|
if (!tree.exists(projectJsonPath) || !tree.exists(packageJsonPath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectJson: ProjectConfiguration = readJson(tree, projectJsonPath);
|
||||||
|
|
||||||
|
// In Nx 19.1+, the way the project name is inferred is different.
|
||||||
|
// For existing projects, if the name is not set, we can inline it
|
||||||
|
// based on the existing logic. This makes sure folks aren't caught
|
||||||
|
// off guard by the new behavior.
|
||||||
|
if (!projectJson.name) {
|
||||||
|
const siblingPackageJson = readJson<PackageJson>(tree, packageJsonPath);
|
||||||
|
|
||||||
|
const newName = siblingPackageJson.nx?.name ?? siblingPackageJson.name;
|
||||||
|
|
||||||
|
const oldName = toProjectName(projectJsonPath);
|
||||||
|
|
||||||
|
if (newName && oldName !== newName) {
|
||||||
|
projectJson.name = oldName;
|
||||||
|
writeJson(tree, projectJsonPath, projectJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await formatChangedFilesWithPrettierIfAvailable(tree);
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ import { ProjectConfiguration } from '../../../config/workspace-json-project-jso
|
|||||||
import { toProjectName } from '../../../config/to-project-name';
|
import { toProjectName } from '../../../config/to-project-name';
|
||||||
import { readJsonFile } from '../../../utils/fileutils';
|
import { readJsonFile } from '../../../utils/fileutils';
|
||||||
import { NxPluginV2 } from '../../../project-graph/plugins';
|
import { NxPluginV2 } from '../../../project-graph/plugins';
|
||||||
|
import { PackageJson } from '../../../utils/package-json';
|
||||||
|
|
||||||
export const ProjectJsonProjectsPlugin: NxPluginV2 = {
|
export const ProjectJsonProjectsPlugin: NxPluginV2 = {
|
||||||
name: 'nx/core/project-json',
|
name: 'nx/core/project-json',
|
||||||
@ -15,6 +16,7 @@ export const ProjectJsonProjectsPlugin: NxPluginV2 = {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const project = buildProjectFromProjectJson(json, file);
|
const project = buildProjectFromProjectJson(json, file);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
projects: {
|
projects: {
|
||||||
[project.root]: project,
|
[project.root]: project,
|
||||||
@ -30,9 +32,21 @@ export function buildProjectFromProjectJson(
|
|||||||
json: Partial<ProjectConfiguration>,
|
json: Partial<ProjectConfiguration>,
|
||||||
path: string
|
path: string
|
||||||
): ProjectConfiguration {
|
): ProjectConfiguration {
|
||||||
|
const packageJsonPath = join(dirname(path), 'package.json');
|
||||||
|
const { name, root, ...rest } = json;
|
||||||
return {
|
return {
|
||||||
name: toProjectName(path),
|
name:
|
||||||
root: dirname(path),
|
name ?? readNameFromPackageJson(packageJsonPath) ?? toProjectName(path),
|
||||||
...json,
|
root: root ?? dirname(path),
|
||||||
|
...rest,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function readNameFromPackageJson(path: string): string {
|
||||||
|
try {
|
||||||
|
const json = readJsonFile<PackageJson>(path);
|
||||||
|
return json.nx?.name ?? json.name;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import {
|
|||||||
} from './package-manager';
|
} from './package-manager';
|
||||||
|
|
||||||
export interface NxProjectPackageJsonConfiguration {
|
export interface NxProjectPackageJsonConfiguration {
|
||||||
|
name?: string;
|
||||||
implicitDependencies?: string[];
|
implicitDependencies?: string[];
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
namedInputs?: { [inputName: string]: (string | InputDefinition)[] };
|
namedInputs?: { [inputName: string]: (string | InputDefinition)[] };
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user