Skip to content

Commit

Permalink
feat(js): generate package.json with overrides and resolutions (#27601)
Browse files Browse the repository at this point in the history
This PR ensures that `overrides` and `resolutions` are in the generated
package.json file as well. If they are missing, then using
`--frozen-lockfile` will fail due to mismatched overrides in the
lockfile.

Also adds a `skipOverrides` flag to the affected executors and plugins
-- same as `skipPackageManger` that was added previously.

Affected executors/plugins:
- `@nx/vite:build`
- `@nx/webpack:webpack`
- `@nx/remix:build`
- `@nx/next:build`
- `NxAppWebpackPlugin`


<!-- 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
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

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

Fixes #26884
  • Loading branch information
jaysoo authored Aug 22, 2024
1 parent 35899e3 commit 042049c
Show file tree
Hide file tree
Showing 17 changed files with 305 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/generated/packages/next/executors/build.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@
"default": false,
"x-priority": "internal"
},
"skipOverrides": {
"type": "boolean",
"description": "Do not add a `overrides` and `resolutions` entries to the generated package.json file. Only works in conjunction with `generatePackageJson` option."
},
"skipPackageManager": {
"type": "boolean",
"description": "Do not add a `packageManager` entry to the generated package.json file."
Expand Down
4 changes: 4 additions & 0 deletions docs/generated/packages/remix/executors/build.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
"description": "Generate a lockfile (e.g. package-lock.json) that matches the workspace lockfile to ensure package versions match.",
"default": false
},
"skipOverrides": {
"type": "boolean",
"description": "Do not add a `overrides` and `resolutions` entries to the generated package.json file. Only works in conjunction with `generatePackageJson` option."
},
"skipPackageManager": {
"type": "boolean",
"description": "Do not add a `packageManager` entry to the generated package.json file. Only works in conjunction with `generatePackageJson` option."
Expand Down
4 changes: 4 additions & 0 deletions docs/generated/packages/vite/executors/build.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
"description": "Include devDependencies in the generated package.json.",
"type": "boolean"
},
"skipOverrides": {
"type": "boolean",
"description": "Do not add a `overrides` and `resolutions` entries to the generated package.json file. Only works in conjunction with `generatePackageJson` option."
},
"skipPackageManager": {
"type": "boolean",
"description": "Do not add a `packageManager` entry to the generated package.json file. Only works in conjunction with `generatePackageJson` option."
Expand Down
4 changes: 4 additions & 0 deletions docs/generated/packages/webpack/executors/webpack.json
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@
"type": "boolean",
"description": "Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated."
},
"skipOverrides": {
"type": "boolean",
"description": "Do not add a `overrides` and `resolutions` entries to the generated package.json file. Only works in conjunction with `generatePackageJson` option."
},
"skipPackageManager": {
"type": "boolean",
"description": "Do not add a `packageManager` entry to the generated package.json file. Only works in conjunction with `generatePackageJson` option."
Expand Down
12 changes: 12 additions & 0 deletions docs/shared/packages/webpack/webpack-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,18 @@ Type: `string[]`

External scripts that will be included before the main application entry.

##### skipOverrides

Type: `boolean`

Do not add a `overrides` and `resolutions` entries to the generated package.json file. Only works in conjunction with `generatePackageJson` option.

##### skipPackageManager

Type: `boolean`

Do not add a `packageManager` entry to the generated package.json file. Only works in conjunction with `generatePackageJson` option.

##### skipTypeChecking

Type: `boolean`
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/executors/build/build.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export default async function buildExecutor(
target: context.targetName,
root: context.root,
isProduction: !options.includeDevDependenciesInPackageJson, // By default we remove devDependencies since this is a production build.
skipOverrides: options.skipOverrides,
skipPackageManager: options.skipPackageManager,
}
);
Expand Down
4 changes: 4 additions & 0 deletions packages/next/src/executors/build/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
"default": false,
"x-priority": "internal"
},
"skipOverrides": {
"type": "boolean",
"description": "Do not add a `overrides` and `resolutions` entries to the generated package.json file. Only works in conjunction with `generatePackageJson` option."
},
"skipPackageManager": {
"type": "boolean",
"description": "Do not add a `packageManager` entry to the generated package.json file."
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface NextBuildBuilderOptions {
nextConfig?: string;
outputPath: string;
profile?: boolean;
skipOverrides?: boolean;
skipPackageManager?: boolean;
watch?: boolean;
}
Expand Down
221 changes: 221 additions & 0 deletions packages/nx/src/plugins/js/package-json/create-package-json.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,227 @@ describe('createPackageJson', () => {
/Package Manager Mismatch/
);
});

it('should add overrides (pnpm)', () => {
spies.push(
jest
.spyOn(fs, 'existsSync')
.mockImplementation(
(path) =>
path === 'libs/lib1/package.json' ||
path === 'apps/app1/package.json' ||
path === 'package.json'
)
);
spies.push(
jest
.spyOn(fileutilsModule, 'readJsonFile')
.mockImplementation((path) => {
if (path === 'package.json') {
return {
...rootPackageJson(),
pnpm: {
overrides: {
foo: '1.0.0',
},
},
};
}
if (path === 'libs/lib1/package.json') {
return projectPackageJson();
}
if (path === 'apps/app1/package.json') {
return {
...projectPackageJson(),
pnpm: {
overrides: {
foo: '2.0.0',
bar: '1.0.0',
},
},
};
}
})
);

expect(
createPackageJson('lib1', graph, {
root: '',
})
).toEqual({
dependencies: {
random: '1.0.0',
typescript: '^4.8.4',
},
name: 'other-name',
version: '1.2.3',
pnpm: {
overrides: {
foo: '1.0.0',
},
},
});
expect(
createPackageJson('app1', graph, {
root: '',
})
).toEqual({
dependencies: {
random: '1.0.0',
typescript: '^4.8.4',
},
name: 'other-name',
version: '1.2.3',
pnpm: {
overrides: {
foo: '2.0.0',
bar: '1.0.0',
},
},
});
});

it('should add overrides (npm)', () => {
spies.push(
jest
.spyOn(fs, 'existsSync')
.mockImplementation(
(path) =>
path === 'libs/lib1/package.json' ||
path === 'apps/app1/package.json' ||
path === 'package.json'
)
);
spies.push(
jest
.spyOn(fileutilsModule, 'readJsonFile')
.mockImplementation((path) => {
if (path === 'package.json') {
return {
...rootPackageJson(),
overrides: {
foo: '1.0.0',
},
};
}
if (path === 'libs/lib1/package.json') {
return projectPackageJson();
}
if (path === 'apps/app1/package.json') {
return {
...projectPackageJson(),
overrides: {
foo: '2.0.0',
bar: '1.0.0',
},
};
}
})
);

expect(
createPackageJson('lib1', graph, {
root: '',
})
).toEqual({
dependencies: {
random: '1.0.0',
typescript: '^4.8.4',
},
name: 'other-name',
version: '1.2.3',
overrides: {
foo: '1.0.0',
},
});
expect(
createPackageJson('app1', graph, {
root: '',
})
).toEqual({
dependencies: {
random: '1.0.0',
typescript: '^4.8.4',
},
name: 'other-name',
version: '1.2.3',
overrides: {
foo: '2.0.0',
bar: '1.0.0',
},
});
});

it('should add resolutions (yarn)', () => {
spies.push(
jest
.spyOn(fs, 'existsSync')
.mockImplementation(
(path) =>
path === 'libs/lib1/package.json' ||
path === 'apps/app1/package.json' ||
path === 'package.json'
)
);
spies.push(
jest
.spyOn(fileutilsModule, 'readJsonFile')
.mockImplementation((path) => {
if (path === 'package.json') {
return {
...rootPackageJson(),
resolutions: {
foo: '1.0.0',
},
};
}
if (path === 'libs/lib1/package.json') {
return projectPackageJson();
}
if (path === 'apps/app1/package.json') {
return {
...projectPackageJson(),
resolutions: {
foo: '2.0.0',
bar: '1.0.0',
},
};
}
})
);

expect(
createPackageJson('lib1', graph, {
root: '',
})
).toEqual({
dependencies: {
random: '1.0.0',
typescript: '^4.8.4',
},
name: 'other-name',
version: '1.2.3',
resolutions: {
foo: '1.0.0',
},
});
expect(
createPackageJson('app1', graph, {
root: '',
})
).toEqual({
dependencies: {
random: '1.0.0',
typescript: '^4.8.4',
},
name: 'other-name',
version: '1.2.3',
resolutions: {
foo: '2.0.0',
bar: '1.0.0',
},
});
});
});
});

Expand Down
29 changes: 29 additions & 0 deletions packages/nx/src/plugins/js/package-json/create-package-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export function createPackageJson(
isProduction?: boolean;
helperDependencies?: string[];
skipPackageManager?: boolean;
skipOverrides?: boolean;
} = {},
fileMap: ProjectFileMap = null
): PackageJson {
Expand Down Expand Up @@ -198,6 +199,34 @@ export function createPackageJson(
packageJson.packageManager = rootPackageJson.packageManager;
}

// region Overrides/Resolutions

// npm
if (rootPackageJson.overrides && !options.skipOverrides) {
packageJson.overrides = {
...rootPackageJson.overrides,
...packageJson.overrides,
};
}

// pnpm
if (rootPackageJson.pnpm?.overrides && !options.skipOverrides) {
packageJson.pnpm ??= {};
packageJson.pnpm.overrides = {
...rootPackageJson.pnpm.overrides,
...packageJson.pnpm.overrides,
};
}

// yarn
if (rootPackageJson.resolutions && !options.skipOverrides) {
packageJson.resolutions = {
...rootPackageJson.resolutions,
...packageJson.resolutions,
};
}
// endregion Overrides/Resolutions

return packageJson;
}

Expand Down
3 changes: 3 additions & 0 deletions packages/nx/src/utils/package-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ export interface PackageJson {
peerDependencies?: Record<string, string>;
peerDependenciesMeta?: Record<string, { optional: boolean }>;
resolutions?: Record<string, string>;
pnpm?: {
overrides?: PackageOverride;
};
overrides?: PackageOverride;
bin?: Record<string, string> | string;
workspaces?:
Expand Down
4 changes: 4 additions & 0 deletions packages/remix/src/executors/build/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
"description": "Generate a lockfile (e.g. package-lock.json) that matches the workspace lockfile to ensure package versions match.",
"default": false
},
"skipOverrides": {
"type": "boolean",
"description": "Do not add a `overrides` and `resolutions` entries to the generated package.json file. Only works in conjunction with `generatePackageJson` option."
},
"skipPackageManager": {
"type": "boolean",
"description": "Do not add a `packageManager` entry to the generated package.json file. Only works in conjunction with `generatePackageJson` option."
Expand Down
1 change: 1 addition & 0 deletions packages/vite/src/executors/build/build.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export async function* viteBuildExecutor(
target: context.targetName,
root: context.root,
isProduction: !options.includeDevDependenciesInPackageJson, // By default we remove devDependencies since this is a production build.
skipOverrides: options.skipOverrides,
skipPackageManager: options.skipPackageManager,
}
);
Expand Down
1 change: 1 addition & 0 deletions packages/vite/src/executors/build/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface ViteBuildExecutorOptions {
generatePackageJson?: boolean;
includeDevDependenciesInPackageJson?: boolean;
outputPath?: string;
skipOverrides?: boolean;
skipPackageManager?: boolean;
skipTypeCheck?: boolean;
tsConfig?: string;
Expand Down
Loading

0 comments on commit 042049c

Please sign in to comment.