Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@
"@middy/core": "7.8.0",
"@modelcontextprotocol/inspector": "2.4.0",
"@modelcontextprotocol/sdk": "1.30.0",
"@nx/devkit": "23.1.1",
"@nx/js": "23.1.1",
"@nx/playwright": "23.1.1",
"@nx/react": "23.1.1",
"@nx/vite": "23.1.1",
"@nx/vitest": "23.1.1",
"@nx/devkit": "23.1.2",
"@nx/js": "23.1.2",
"@nx/playwright": "23.1.2",
"@nx/react": "23.1.2",
"@nx/vite": "23.1.2",
"@nx/vitest": "23.1.2",
"@nxlv/python": "23.0.0",
"@phenomnomnominal/tsquery": "6.2.0",
"@quantco/pnpm-licenses": "2.4.2",
Expand Down Expand Up @@ -125,7 +125,7 @@
"mdast-util-mdx-jsx": "3.2.0",
"minimatch": "10.2.6",
"node-pty": "1.1.0",
"nx": "23.1.1",
"nx": "23.1.2",
"openapi-types": "12.1.3",
"pip-requirements-js": "1.0.3",
"playwright": "1.62.1",
Expand Down
9,517 changes: 2,647 additions & 6,870 deletions packages/nx-plugin/LICENSE-THIRD-PARTY

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions packages/nx-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"node": ">=20.19.0"
},
"peerDependencies": {
"nx": "23.1.1"
"nx": "23.1.2"
},
"dependencies": {
"@apidevtools/swagger-parser": "12.1.0",
Expand All @@ -46,10 +46,10 @@
"@getgrit/gritql": "0.0.3",
"@iarna/toml": "2.2.5",
"@modelcontextprotocol/sdk": "1.30.0",
"@nx/devkit": "23.1.1",
"@nx/js": "23.1.1",
"@nx/react": "23.1.1",
"@nx/vite": "23.1.1",
"@nx/devkit": "23.1.2",
"@nx/js": "23.1.2",
"@nx/react": "23.1.2",
"@nx/vite": "23.1.2",
"@nxlv/python": "23.0.0",
"@phenomnomnominal/tsquery": "6.2.0",
"@types/estree": "1.0.9",
Expand Down
33 changes: 33 additions & 0 deletions packages/nx-plugin/packageJsonUpdates.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,38 @@
"alwaysAddToPackageJson": false
}
}
},
"nx-23.1.2-nx-packages": {
"version": "latest",
"packages": {
"nx": {
"version": "23.1.2",
"alwaysAddToPackageJson": false
},
"@nx/devkit": {
"version": "23.1.2",
"alwaysAddToPackageJson": false
},
"@nx/js": {
"version": "23.1.2",
"alwaysAddToPackageJson": false
},
"@nx/react": {
"version": "23.1.2",
"alwaysAddToPackageJson": false
},
"@nx/vite": {
"version": "23.1.2",
"alwaysAddToPackageJson": false
},
"@nx/vitest": {
"version": "23.1.2",
"alwaysAddToPackageJson": false
},
"@nx/workspace": {
"version": "23.1.2",
"alwaysAddToPackageJson": false
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ exports[`infra generator > should add required dependencies to package.json > de

exports[`infra generator > should add required dependencies to package.json > dev-dependencies 1`] = `
{
"@nx/js": "23.1.1",
"@nx/vitest": "23.1.1",
"@nx/js": "23.1.2",
"@nx/vitest": "23.1.2",
"@swc/helpers": "~0.5.18",
"@types/node": "catalog:",
"@vitest/coverage-v8": "catalog:",
Expand All @@ -23,8 +23,8 @@ exports[`infra generator > should add required dependencies to package.json > pa
{
"dependencies": {},
"devDependencies": {
"@nx/js": "23.1.1",
"@nx/vitest": "23.1.1",
"@nx/js": "23.1.2",
"@nx/vitest": "23.1.2",
"@swc/helpers": "~0.5.18",
"@types/node": "catalog:",
"@vitest/coverage-v8": "catalog:",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"description": "Add an npm override pinning the express @nx/react resolves to the version generated websites use, so npm can satisfy its optional peer"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import * as devkit from '@nx/devkit';
import { readJson, type Tree, updateJson } from '@nx/devkit';
import { createTreeUsingTsSolutionSetup } from '../../../utils/test';
import { TS_VERSIONS } from '../../../utils/versions';
import migration from './migration';

const VENDED_EXPRESS = TS_VERSIONS.express;

describe('react-website-nx-react-express-override migration', () => {
let tree: Tree;

const usePackageManager = (pkgMgr: 'npm' | 'yarn' | 'pnpm' | 'bun') =>
vi.spyOn(devkit, 'detectPackageManager').mockReturnValue(pkgMgr);

// A workspace with a website carries `@nx/react` at the root.
const withWebsite = () =>
updateJson(tree, 'package.json', (json) => ({
...json,
devDependencies: { ...json.devDependencies, '@nx/react': '23.1.2' },
}));

beforeEach(() => {
tree = createTreeUsingTsSolutionSetup();
usePackageManager('npm');
});

it('should pin express under @nx/react for an npm workspace with a website', async () => {
withWebsite();

await migration(tree);

expect(readJson(tree, 'package.json').overrides).toEqual({
'@nx/react': { express: VENDED_EXPRESS },
});
});

it('should preserve other overrides and sibling keys', async () => {
withWebsite();
updateJson(tree, 'package.json', (json) => ({
...json,
overrides: {
zod: '4.3.0',
'@nx/react': { 'some-other-peer': '1.0.0' },
},
}));

await migration(tree);

expect(readJson(tree, 'package.json').overrides).toEqual({
zod: '4.3.0',
'@nx/react': {
'some-other-peer': '1.0.0',
express: VENDED_EXPRESS,
},
});
});

it.each(['pnpm', 'yarn', 'bun'] as const)(
'should not add an override on %s, which only warns',
async (pkgMgr) => {
usePackageManager(pkgMgr);
withWebsite();

await migration(tree);

expect(readJson(tree, 'package.json').overrides).toBeUndefined();
},
);

it('should not add an override to a workspace with no website', async () => {
await migration(tree);

expect(readJson(tree, 'package.json').overrides).toBeUndefined();
});

it('should keep a version the user pinned deliberately', async () => {
withWebsite();
updateJson(tree, 'package.json', (json) => ({
...json,
overrides: { '@nx/react': { express: '4.21.2' } },
}));

const result = await migration(tree);

expect(readJson(tree, 'package.json').overrides['@nx/react'].express).toBe(
'4.21.2',
);
expect(result.nextSteps).toHaveLength(0);
});

it('should skip and report an @nx/react override pinned as a string', async () => {
withWebsite();
updateJson(tree, 'package.json', (json) => ({
...json,
overrides: { '@nx/react': '23.1.1' },
}));

const result = await migration(tree);

expect(readJson(tree, 'package.json').overrides['@nx/react']).toBe(
'23.1.1',
);
expect(result.nextSteps).toHaveLength(1);
expect(result.nextSteps[0]).toContain(VENDED_EXPRESS);
});

it('should be idempotent', async () => {
withWebsite();

await migration(tree);
const afterFirst = tree.read('package.json', 'utf-8');
await migration(tree);

expect(tree.read('package.json', 'utf-8')).toBe(afterFirst);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import {
detectPackageManager,
type MigrationReturnObject,
readJson,
type Tree,
updateJson,
} from '@nx/devkit';
import { formatFilesInSubtree } from '../../../utils/format';
import { TS_VERSIONS } from '../../../utils/versions';

/**
* Pin the `express` `@nx/react` resolves to the version generated websites use.
*
* `@nx/react` declares an optional `express` peer for its module-federation
* dev-server, on the major behind the `express` vended here. npm is alone in
* failing the whole install on an optional peer it cannot satisfy:
*
* npm error ERESOLVE could not resolve
* npm error peerOptional express@"^4.21.2" from @nx/react
* npm error Conflicting peer dependency: express@5.2.1
*
* so an npm workspace with a website cannot install once its nx moves onto a
* version carrying that peer. pnpm, bun and yarn only warn, and get nothing.
*
* Scoped to `@nx/react` rather than pinning `express` workspace-wide: nothing
* here loads it, the peer only has to be satisfiable, and a bare override would
* reach every other consumer of express too.
*
* Guardrails:
* - npm only, and only for a workspace that has a website — the root `@nx/react`
* the generator adds. Anything else needs no override.
* - A user's own `overrides['@nx/react'].express` is left alone: theirs may pin a
* version deliberately.
* - Idempotent: writing the same pin twice is a no-op.
*/
export default async function migration(
tree: Tree,
): Promise<MigrationReturnObject> {
const nextSteps: string[] = [];

if (detectPackageManager(tree.root) !== 'npm') {
return { nextSteps };
}

const packageJson = readJson<{
devDependencies?: Record<string, string>;
dependencies?: Record<string, string>;
overrides?: Record<string, unknown>;
}>(tree, 'package.json');

const hasNxReact = Boolean(
packageJson.devDependencies?.['@nx/react'] ??
packageJson.dependencies?.['@nx/react'],
);
if (!hasNxReact) {
return { nextSteps }; // No website in this workspace.
}

const scoped = packageJson.overrides?.['@nx/react'] as
| Record<string, string>
| string
| undefined;

// The string form pins `@nx/react` itself, leaving nowhere to nest the peer.
if (typeof scoped === 'string') {
nextSteps.push(
`package.json: overrides['@nx/react'] pins a version as a string, so the express override could not be nested under it - left untouched. npm fails the install on @nx/react's optional express peer, so add { "@nx/react": { ".": "${scoped}", "express": "${TS_VERSIONS.express}" } } to keep it satisfiable.`,
);
return { nextSteps };
}

if (scoped?.express) {
return { nextSteps }; // Already pinned, by this migration or by the user.
}

updateJson(tree, 'package.json', (json) => ({
...json,
overrides: {
...json.overrides,
'@nx/react': { ...scoped, express: TS_VERSIONS.express },
},
}));

await formatFilesInSubtree(tree);

return { nextSteps };
}
Original file line number Diff line number Diff line change
Expand Up @@ -414,9 +414,9 @@ onlyBuiltDependencies:
- sharp
catalogMode: strict
catalog:
nx: 23.1.1
'@nx/js': 23.1.1
'@nx/workspace': 23.1.1
nx: 23.1.2
'@nx/js': 23.1.2
'@nx/workspace': 23.1.2
typescript: ~6.0.3
'@biomejs/biome': 2.5.10
'@aws/nx-plugin': ^0.0.0
Expand Down
Loading
Loading