feat(devkit): allow updating package json based projects (#27138)

<!-- 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` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
Calling `updateProjectConfiguration` on a package json based project
fails

## Expected Behavior
Calling `updateProjectConfiguration` on a package json based project
works

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #26530
This commit is contained in:
Craigory Coppola 2024-07-26 15:07:57 -04:00 committed by GitHub
parent 9a556f45b8
commit 3dbbfd73ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 131 additions and 28 deletions

View File

@ -277,5 +277,57 @@ describe('project configuration', () => {
root: 'proj',
});
});
it('should handle reading + writing project configuration', () => {
writeJson(tree, 'proj/package.json', {
name: 'proj',
nx: {},
});
const proj = readProjectConfiguration(tree, 'proj');
expect(proj).toEqual({
name: 'proj',
root: 'proj',
});
updateProjectConfiguration(tree, 'proj', {
name: 'proj',
root: 'proj',
sourceRoot: 'proj/src',
targets: {
build: {
command: 'echo "building"',
},
},
});
const updatedProj = readProjectConfiguration(tree, 'proj');
expect(updatedProj).toEqual({
name: 'proj',
root: 'proj',
sourceRoot: 'proj/src',
targets: {
build: {
command: 'echo "building"',
},
},
});
expect(tree.read('proj/package.json', 'utf-8')).toMatchInlineSnapshot(`
"{
"name": "proj",
"nx": {
"sourceRoot": "proj/src",
"targets": {
"build": {
"command": "echo \\"building\\""
}
}
}
}
"
`);
expect(tree.exists('proj/project.json')).toBeFalsy();
});
});
});

View File

@ -1,10 +1,7 @@
import { minimatch } from 'minimatch';
import { basename, join, relative } from 'path';
import { basename, dirname, join, relative } from 'path';
import {
buildProjectConfigurationFromPackageJson,
getGlobPatternsFromPackageManagerWorkspaces,
} from '../../plugins/package-json';
import { getGlobPatternsFromPackageManagerWorkspaces } from '../../plugins/package-json';
import { buildProjectFromProjectJson } from '../../plugins/project-json/build-nodes/project-json';
import { renamePropertyWithStableKeys } from '../../adapter/angular-json';
import {
@ -23,6 +20,7 @@ import { readJson, writeJson } from './json';
import { readNxJson } from './nx-json';
import type { Tree } from '../tree';
import { toProjectName } from '../../config/to-project-name';
export { readNxJson, updateNxJson } from './nx-json';
@ -82,17 +80,73 @@ export function updateProjectConfiguration(
projectName: string,
projectConfiguration: ProjectConfiguration
): void {
if (
tree.exists(joinPathFragments(projectConfiguration.root, 'project.json'))
) {
updateProjectConfigurationInProjectJson(
tree,
projectName,
projectConfiguration
);
} else if (
tree.exists(joinPathFragments(projectConfiguration.root, 'package.json'))
) {
updateProjectConfigurationInPackageJson(
tree,
projectName,
projectConfiguration
);
} else {
throw new Error(
`Cannot update Project ${projectName} at ${projectConfiguration.root}. It either doesn't exist yet, or may not use project.json for configuration. Use \`addProjectConfiguration()\` instead if you want to create a new project.`
);
}
}
function updateProjectConfigurationInPackageJson(
tree: Tree,
projectName: string,
projectConfiguration: ProjectConfiguration
) {
const packageJsonFile = joinPathFragments(
projectConfiguration.root,
'package.json'
);
const packageJson = readJson<PackageJson>(tree, packageJsonFile);
if (packageJson.name === projectConfiguration.name ?? projectName) {
delete projectConfiguration.name;
}
if (
projectConfiguration.targets &&
!Object.keys(projectConfiguration.targets).length
) {
delete projectConfiguration.targets;
}
packageJson.nx = {
...packageJson.nx,
...projectConfiguration,
root: undefined,
};
writeJson(tree, packageJsonFile, packageJson);
}
function updateProjectConfigurationInProjectJson(
tree: Tree,
projectName: string,
projectConfiguration: ProjectConfiguration
) {
const projectConfigFile = joinPathFragments(
projectConfiguration.root,
'project.json'
);
if (!tree.exists(projectConfigFile)) {
throw new Error(
`Cannot update Project ${projectName} at ${projectConfiguration.root}. It either doesn't exist yet, or may not use project.json for configuration. Use \`addProjectConfiguration()\` instead if you want to create a new project.`
);
}
handleEmptyTargets(projectName, projectConfiguration);
writeJson(tree, projectConfigFile, {
name: projectConfiguration.name ?? projectName,
$schema: getRelativeProjectJsonSchemaPath(tree, projectConfiguration),
@ -215,21 +269,22 @@ function readAndCombineAllProjectConfigurations(tree: Tree): {
);
} else if (basename(projectFile) === 'package.json') {
const packageJson = readJson<PackageJson>(tree, projectFile);
const config = buildProjectConfigurationFromPackageJson(
packageJson,
tree.root,
projectFile,
readNxJson(tree)
);
// We don't want to have all of the extra inferred stuff in here, as
// when generators update the project they shouldn't inline that stuff.
// so rather than using `buildProjectFromPackageJson` and stripping it out
// we are going to build the config manually.
const config = {
root: dirname(projectFile),
name: packageJson.name ?? toProjectName(projectFile),
...packageJson.nx,
};
if (!rootMap[config.root]) {
mergeProjectConfigurationIntoRootMap(
rootMap,
// Inferred targets, tags, etc don't show up when running generators
// This is to help avoid running into issues when trying to update the workspace
{
name: config.name,
root: config.root,
},
config,
undefined,
undefined,
true

View File

@ -1,7 +1,7 @@
import { existsSync } from 'fs';
import { dirname, join } from 'path';
import {
InputDefinition,
ProjectConfiguration,
ProjectMetadata,
TargetConfiguration,
} from '../config/workspace-json-project-json';
@ -13,12 +13,8 @@ import {
getPackageManagerCommand,
} from './package-manager';
export interface NxProjectPackageJsonConfiguration {
name?: string;
implicitDependencies?: string[];
tags?: string[];
namedInputs?: { [inputName: string]: (string | InputDefinition)[] };
targets?: Record<string, TargetConfiguration>;
export interface NxProjectPackageJsonConfiguration
extends Partial<ProjectConfiguration> {
includedScripts?: string[];
}