Skip to content

Commit 5d56e21

Browse files
authored
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
1 parent 471f82c commit 5d56e21

File tree

5 files changed

+167
-3
lines changed

5 files changed

+167
-3
lines changed

packages/nx/migrations.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@
8383
"version": "19.2.2-beta.0",
8484
"description": "Updates the nx wrapper.",
8585
"implementation": "./src/migrations/update-17-3-0/update-nxw"
86+
},
87+
"19-2-4-set-project-name": {
88+
"version": "19.2.4-beta.0",
89+
"description": "Set project name in nx.json explicitly",
90+
"implementation": "./src/migrations/update-19-2-4/set-project-name",
91+
"x-repair-skip": true
8692
}
8793
}
8894
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { createTreeWithEmptyWorkspace } from '../../generators/testing-utils/create-tree-with-empty-workspace';
2+
import { readJson } from '../../generators/utils/json';
3+
import migrate from './set-project-name';
4+
5+
describe('set project name', () => {
6+
it('should not update packageJson projects', async () => {
7+
const tree = createTreeWithEmptyWorkspace();
8+
tree.write(
9+
'libs/proj/package.json',
10+
JSON.stringify({
11+
name: '@scoped/package',
12+
})
13+
);
14+
await migrate(tree);
15+
expect(tree.exists('libs/proj/project.json')).toBe(false);
16+
});
17+
18+
it('should not update projectJson if name specified', async () => {
19+
const tree = createTreeWithEmptyWorkspace();
20+
tree.write(
21+
'libs/proj/project.json',
22+
JSON.stringify({
23+
name: 'foo',
24+
})
25+
);
26+
await migrate(tree);
27+
const project = readJson(tree, 'libs/proj/project.json');
28+
expect(project.name).toBe('foo');
29+
});
30+
31+
it('should not update projectJson if name is not specified but no sibling package json', async () => {
32+
const tree = createTreeWithEmptyWorkspace();
33+
tree.write('libs/proj/project.json', JSON.stringify({}));
34+
await migrate(tree);
35+
const project = readJson(tree, 'libs/proj/project.json');
36+
expect(project.name).not.toBeDefined();
37+
});
38+
39+
it('should not update projectJson if name is identical to package name', async () => {
40+
const tree = createTreeWithEmptyWorkspace();
41+
tree.write(
42+
'libs/proj/package.json',
43+
JSON.stringify({
44+
name: 'proj',
45+
})
46+
);
47+
tree.write('libs/proj/project.json', JSON.stringify({}));
48+
await migrate(tree);
49+
const project = readJson(tree, 'libs/proj/project.json');
50+
expect(project.name).not.toBeDefined();
51+
});
52+
53+
it('should not update projectJson if name is identical to name in nx field', async () => {
54+
const tree = createTreeWithEmptyWorkspace();
55+
tree.write(
56+
'libs/proj/package.json',
57+
JSON.stringify({
58+
name: '@scoped/proj',
59+
nx: {
60+
name: 'proj',
61+
},
62+
})
63+
);
64+
tree.write('libs/proj/project.json', JSON.stringify({}));
65+
await migrate(tree);
66+
const project = readJson(tree, 'libs/proj/project.json');
67+
expect(project.name).not.toBeDefined();
68+
});
69+
70+
it('should update projectJson if name is not specified and package name is different', async () => {
71+
const tree = createTreeWithEmptyWorkspace();
72+
tree.write(
73+
'libs/proj/package.json',
74+
JSON.stringify({
75+
name: '@scoped/proj',
76+
})
77+
);
78+
tree.write('libs/proj/project.json', JSON.stringify({}));
79+
await migrate(tree);
80+
const project = readJson(tree, 'libs/proj/project.json');
81+
expect(project.name).toBe('proj');
82+
});
83+
84+
it('should update projectJson if name is not specified and name in nx field is different', async () => {
85+
const tree = createTreeWithEmptyWorkspace();
86+
tree.write(
87+
'libs/foo/package.json',
88+
JSON.stringify({
89+
name: '@scoped/proj',
90+
nx: {
91+
name: 'proj',
92+
},
93+
})
94+
);
95+
tree.write('libs/foo/project.json', JSON.stringify({}));
96+
await migrate(tree);
97+
const project = readJson(tree, 'libs/foo/project.json');
98+
expect(project.name).toBe('foo');
99+
});
100+
});
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { toProjectName } from '../../config/to-project-name';
2+
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
3+
import { Tree } from '../../generators/tree';
4+
import { formatChangedFilesWithPrettierIfAvailable } from '../../generators/internal-utils/format-changed-files-with-prettier-if-available';
5+
import { readJson, writeJson } from '../../generators/utils/json';
6+
import { getProjects } from '../../generators/utils/project-configuration';
7+
import type { PackageJson } from '../../utils/package-json';
8+
9+
export default async function setProjectName(tree: Tree) {
10+
// We are explicitly looking for project.json files here, so getProjects is fine.
11+
const projects = getProjects(tree);
12+
13+
for (const { root } of projects.values()) {
14+
const projectJsonPath = `${root}/project.json`;
15+
const packageJsonPath = `${root}/package.json`;
16+
17+
// If either of these files doesn't exist, theres no behavioral difference
18+
if (!tree.exists(projectJsonPath) || !tree.exists(packageJsonPath)) {
19+
continue;
20+
}
21+
22+
const projectJson: ProjectConfiguration = readJson(tree, projectJsonPath);
23+
24+
// In Nx 19.1+, the way the project name is inferred is different.
25+
// For existing projects, if the name is not set, we can inline it
26+
// based on the existing logic. This makes sure folks aren't caught
27+
// off guard by the new behavior.
28+
if (!projectJson.name) {
29+
const siblingPackageJson = readJson<PackageJson>(tree, packageJsonPath);
30+
31+
const newName = siblingPackageJson.nx?.name ?? siblingPackageJson.name;
32+
33+
const oldName = toProjectName(projectJsonPath);
34+
35+
if (newName && oldName !== newName) {
36+
projectJson.name = oldName;
37+
writeJson(tree, projectJsonPath, projectJson);
38+
}
39+
}
40+
}
41+
42+
await formatChangedFilesWithPrettierIfAvailable(tree);
43+
}

packages/nx/src/plugins/project-json/build-nodes/project-json.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ProjectConfiguration } from '../../../config/workspace-json-project-jso
44
import { toProjectName } from '../../../config/to-project-name';
55
import { readJsonFile } from '../../../utils/fileutils';
66
import { NxPluginV2 } from '../../../project-graph/plugins';
7+
import { PackageJson } from '../../../utils/package-json';
78

89
export const ProjectJsonProjectsPlugin: NxPluginV2 = {
910
name: 'nx/core/project-json',
@@ -15,6 +16,7 @@ export const ProjectJsonProjectsPlugin: NxPluginV2 = {
1516
);
1617

1718
const project = buildProjectFromProjectJson(json, file);
19+
1820
return {
1921
projects: {
2022
[project.root]: project,
@@ -30,9 +32,21 @@ export function buildProjectFromProjectJson(
3032
json: Partial<ProjectConfiguration>,
3133
path: string
3234
): ProjectConfiguration {
35+
const packageJsonPath = join(dirname(path), 'package.json');
36+
const { name, root, ...rest } = json;
3337
return {
34-
name: toProjectName(path),
35-
root: dirname(path),
36-
...json,
38+
name:
39+
name ?? readNameFromPackageJson(packageJsonPath) ?? toProjectName(path),
40+
root: root ?? dirname(path),
41+
...rest,
3742
};
3843
}
44+
45+
export function readNameFromPackageJson(path: string): string {
46+
try {
47+
const json = readJsonFile<PackageJson>(path);
48+
return json.nx?.name ?? json.name;
49+
} catch {
50+
return undefined;
51+
}
52+
}

packages/nx/src/utils/package-json.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
} from './package-manager';
1515

1616
export interface NxProjectPackageJsonConfiguration {
17+
name?: string;
1718
implicitDependencies?: string[];
1819
tags?: string[];
1920
namedInputs?: { [inputName: string]: (string | InputDefinition)[] };

0 commit comments

Comments
 (0)