Skip to content

Commit 868f40b

Browse files
ndcunninghamColy010
authored andcommitted
chore(testing): simplify jest resolver for workspace package imports
1 parent 73a3e29 commit 868f40b

2 files changed

Lines changed: 111 additions & 188 deletions

File tree

jest.preset.js

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -14,62 +14,4 @@ module.exports = {
1414
maxWorkers: 1,
1515
testEnvironment: 'node',
1616
setupFiles: ['../../scripts/unit-test-setup.js'],
17-
moduleNameMapper: {
18-
// TypeScript source mappings needed for Jest to resolve imports correctly
19-
// Until ts-jest supports ts references, see: https://github.com/kulshekhar/ts-jest/pull/4689 & https://github.com/kulshekhar/ts-jest/issues/1648
20-
'^@nx/devkit$': '<rootDir>/../devkit/index.ts',
21-
'^@nx/devkit/testing$': '<rootDir>/../devkit/testing.ts',
22-
'^@nx/devkit/internal-testing-utils$':
23-
'<rootDir>/../devkit/internal-testing-utils.ts',
24-
'^@nx/devkit/src/(.*)$': '<rootDir>/../devkit/src/$1',
25-
'^nx/src/devkit-exports$': '<rootDir>/../nx/src/devkit-exports.ts',
26-
'^nx/src/devkit-internals$': '<rootDir>/../nx/src/devkit-internals.ts',
27-
'^nx/src/devkit-testing-exports$':
28-
'<rootDir>/../nx/src/devkit-testing-exports.ts',
29-
'^nx/src/internal-testing-utils/(.*)$':
30-
'<rootDir>/../nx/src/internal-testing-utils/$1',
31-
'^nx/src/generators/(.*)$': '<rootDir>/../nx/src/generators/$1',
32-
'^nx/src/plugins/(.*)$': '<rootDir>/../nx/src/plugins/$1',
33-
'^nx/src/utils/(.*)$': '<rootDir>/../nx/src/utils/$1',
34-
'^nx/src/config/(.*)$': '<rootDir>/../nx/src/config/$1',
35-
'^nx/src/command-line/(.*)$': '<rootDir>/../nx/src/command-line/$1',
36-
'^nx/src/project-graph/(.*)$': '<rootDir>/../nx/src/project-graph/$1',
37-
'^nx/src/daemon/(.*)$': '<rootDir>/../nx/src/daemon/$1',
38-
'^nx/src/utils/(.*)$': '<rootDir>/../nx/src/utils/$1',
39-
'^nx/src/hasher/(.*)$': '<rootDir>/../nx/src/hasher/$1',
40-
'^nx/src/tasks-runner/(.*)$': '<rootDir>/../nx/src/tasks-runner/$1',
41-
'^nx/src/adapter/(.*)$': '<rootDir>/../nx/src/adapter/$1',
42-
'^nx/package.json$': '<rootDir>/../nx/package.json',
43-
'^@nx/workspace$': '<rootDir>/../workspace/index.ts',
44-
'^@nx/workspace/src/(.*)$': '<rootDir>/../workspace/src/$1',
45-
// TS Solution: Map workspace packages to their TypeScript source
46-
'^@nx/rollup$': '<rootDir>/../rollup/index.ts',
47-
'^@nx/eslint$': '<rootDir>/../eslint/index.ts',
48-
'^@nx/vite$': '<rootDir>/../vite/index.ts',
49-
'^@nx/vite/src/(.*)$': '<rootDir>/../vite/src/$1',
50-
'^@nx/jest$': '<rootDir>/../jest/index.ts',
51-
'^@nx/js$': '<rootDir>/../js/src/index.ts',
52-
'^@nx/js/src/(.*)$': '<rootDir>/../js/src/$1',
53-
'^@nx/eslint/src/(.*)$': '<rootDir>/../eslint/src/$1',
54-
'^@nx/esbuild$': '<rootDir>/../esbuild/index.ts',
55-
'^@nx/react$': '<rootDir>/../react/index.ts',
56-
'^@nx/playwright/plugin$': '<rootDir>/../playwright/plugin.ts',
57-
'^@nx/rollup$': '<rootDir>/../rollup/index.ts',
58-
'^@nx/rspack$': '<rootDir>/../rspack/src/index.ts',
59-
'^@nx/plugin$': '<rootDir>/../plugin/index.ts',
60-
'^@nx/plugin/src/(.*)$': '<rootDir>/../plugin/src/$1',
61-
'^@nx/angular$': '<rootDir>/../angular/index.ts',
62-
'^@nx/angular/src/(.*)$': '<rootDir>/../angular/src/$1',
63-
'^@nx/react/src/(.*)$': '<rootDir>/../react/src/$1',
64-
'^@nx/next$': '<rootDir>/../next/index.ts',
65-
'^@nx/next/src/(.*)$': '<rootDir>/../next/src/$1',
66-
'^@nx/nuxt$': '<rootDir>/../nuxt/index.ts',
67-
'^@nx/nuxt/src/(.*)$': '<rootDir>/../nuxt/src/$1',
68-
'^@nx/node$': '<rootDir>/../node/index.ts',
69-
'^@nx/node/src/(.*)$': '<rootDir>/../node/src/$1',
70-
'^@nx/web/src/(.*)$': '<rootDir>/../web/src/$1',
71-
'^@nx/webpack/src/(.*)$': '<rootDir>/../webpack/src/$1',
72-
'^@nx/cypress/src/(.*)$': '<rootDir>/../cypress/src/$1',
73-
'^@nx/jest/src/(.*)$': '<rootDir>/../jest/src/$1',
74-
},
7517
};

scripts/patched-jest-resolver.js

Lines changed: 111 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ module.exports = function (modulePath, options) {
5050
'@nx/eslint',
5151
'@nx/vite',
5252
'@nx/jest',
53+
'@nx/docker',
5354
'@nx/js',
5455
'@nx/next',
5556
'@nx/storybook',
@@ -61,7 +62,6 @@ module.exports = function (modulePath, options) {
6162
'@nx/workspace',
6263
'@nx/module-federation',
6364
'@nx/rspack',
64-
'@nx/docker',
6565
'@nx/eslint-plugin',
6666
'@nx/angular',
6767
'@nx/create-nx-plugin',
@@ -109,155 +109,136 @@ module.exports = function (modulePath, options) {
109109
return options.defaultResolver(modulePath, options);
110110
}
111111

112-
const packagesPath = '../';
112+
// Find workspace root - avoid filesystem lookups inside node_modules
113+
// For PNPM workspaces, we know the structure: workspace/packages/packageName
114+
let workspaceRoot = options.rootDir;
115+
116+
// If we're in a packages subdirectory, go up two levels to workspace root
117+
if (workspaceRoot.includes('/packages/')) {
118+
const packagesIndex = workspaceRoot.lastIndexOf('/packages/');
119+
workspaceRoot = workspaceRoot.substring(0, packagesIndex);
120+
} else {
121+
// Fallback: go up directories until we find packages/ (but check pnpm-lock.yaml for validation)
122+
while (workspaceRoot && workspaceRoot !== path.dirname(workspaceRoot)) {
123+
const pnpmLock = path.join(workspaceRoot, 'pnpm-lock.yaml');
124+
const packagesDir = path.join(workspaceRoot, 'packages');
125+
if (fs.existsSync(pnpmLock) && fs.existsSync(packagesDir)) {
126+
break;
127+
}
128+
workspaceRoot = path.dirname(workspaceRoot);
129+
}
130+
}
131+
132+
const packagesPath = path.join(workspaceRoot, 'packages');
133+
134+
// Handle main @nx/* package imports (e.g., '@nx/devkit', '@nx/js')
135+
const nxPackageMatch = modulePath.match(/^@nx\/([^/]+)$/);
136+
if (nxPackageMatch) {
137+
const packageName = nxPackageMatch[1];
113138

114-
// TS Solution: Allow specific workspace packages to be resolved to TypeScript source
115-
const tsWorkspacePackages = {
116-
'@nx/rollup': path.resolve(
117-
options.rootDir,
118-
`${packagesPath}rollup/index.ts`
119-
),
120-
'@nx/eslint': path.resolve(
121-
options.rootDir,
122-
`${packagesPath}eslint/index.ts`
123-
),
124-
'@nx/vite': path.resolve(options.rootDir, `${packagesPath}vite/index.ts`),
125-
'@nx/jest': path.resolve(options.rootDir, `${packagesPath}jest/index.ts`),
126-
'@nx/js': path.resolve(options.rootDir, `${packagesPath}js/src/index.ts`),
127-
// Additional packages where tests are working
128-
'@nx/next': path.resolve(options.rootDir, `${packagesPath}next/index.ts`),
129-
'@nx/storybook': path.resolve(
130-
options.rootDir,
131-
`${packagesPath}storybook/index.ts`
132-
),
133-
'@nx/rsbuild': path.resolve(
134-
options.rootDir,
135-
`${packagesPath}rsbuild/index.ts`
136-
),
137-
'@nx/react-native': path.resolve(
138-
options.rootDir,
139-
`${packagesPath}react-native/index.ts`
140-
),
141-
'@nx/express': path.resolve(
142-
options.rootDir,
143-
`${packagesPath}express/index.ts`
144-
),
145-
'@nx/web': path.resolve(options.rootDir, `${packagesPath}web/index.ts`),
146-
'@nx/vue': path.resolve(options.rootDir, `${packagesPath}vue/index.ts`),
147-
'@nx/workspace': path.resolve(
148-
options.rootDir,
149-
`${packagesPath}workspace/index.ts`
150-
),
151-
'@nx/module-federation': path.resolve(
152-
options.rootDir,
153-
`${packagesPath}module-federation/index.ts`
154-
),
155-
'@nx/react': path.resolve(
156-
options.rootDir,
157-
`${packagesPath}react/index.ts`
158-
),
159-
'@nx/remix': path.resolve(
160-
options.rootDir,
161-
`${packagesPath}remix/index.ts`
162-
),
163-
'@nx/webpack': path.resolve(
164-
options.rootDir,
165-
`${packagesPath}webpack/index.ts`
166-
),
167-
'@nx/playwright': path.resolve(
168-
options.rootDir,
169-
`${packagesPath}playwright/index.ts`
170-
),
171-
'@nx/rspack': path.resolve(
172-
options.rootDir,
173-
`${packagesPath}rspack/src/index.ts`
174-
),
175-
};
139+
// Check if this package exists in workspace
140+
const packageDir = path.join(packagesPath, packageName);
176141

177-
if (tsWorkspacePackages[modulePath]) {
178-
return tsWorkspacePackages[modulePath];
142+
// Try different entry points based on package structure
143+
const possibleEntries = [
144+
path.join(packageDir, 'index.ts'),
145+
path.join(packageDir, 'src', 'index.ts'),
146+
];
147+
148+
for (const entry of possibleEntries) {
149+
if (fs.existsSync(entry) && fs.lstatSync(entry).isFile()) {
150+
return entry;
151+
}
152+
}
179153
}
180154

181-
// Handle @nx/js/src/* paths
182-
if (modulePath.startsWith('@nx/js/src/')) {
183-
const relativePath = modulePath.replace('@nx/js/src/', '');
184-
return path.resolve(
185-
options.rootDir,
186-
`${packagesPath}js/src/`,
187-
relativePath + '.ts'
188-
);
155+
// Handle @nx/*/src/* subpath imports (e.g., '@nx/devkit/src/utils/something')
156+
const nxSubpathMatch = modulePath.match(/^@nx\/([^/]+)\/src\/(.+)$/);
157+
if (nxSubpathMatch) {
158+
const packageName = nxSubpathMatch[1];
159+
const subpath = nxSubpathMatch[2];
160+
161+
// Try different patterns for subpath resolution
162+
const possiblePaths = [
163+
path.join(packagesPath, packageName, 'src', subpath + '.ts'), // Direct file
164+
path.join(packagesPath, packageName, 'src', subpath, 'index.ts'), // Directory with index.ts
165+
];
166+
167+
for (const possiblePath of possiblePaths) {
168+
if (
169+
fs.existsSync(possiblePath) &&
170+
fs.lstatSync(possiblePath).isFile()
171+
) {
172+
return possiblePath;
173+
}
174+
}
189175
}
190176

191-
// Handle @nx/eslint/src/* paths
192-
if (modulePath.startsWith('@nx/eslint/src/')) {
193-
const relativePath = modulePath.replace('@nx/eslint/src/', '');
194-
return path.resolve(
195-
options.rootDir,
196-
`${packagesPath}eslint/src/`,
197-
relativePath + '.ts'
198-
);
177+
// Handle @nx/* other subpaths (e.g., '@nx/devkit/testing', '@nx/devkit/package.json')
178+
const nxOtherMatch = modulePath.match(/^@nx\/([^/]+)\/(.+)$/);
179+
if (nxOtherMatch) {
180+
const packageName = nxOtherMatch[1];
181+
const subpath = nxOtherMatch[2];
182+
183+
const packageDir = path.join(packagesPath, packageName);
184+
185+
// Try different patterns for subpath resolution
186+
const possiblePaths = [
187+
path.join(packageDir, subpath), // For files like package.json
188+
path.join(packageDir, subpath + '.ts'),
189+
path.join(packageDir, 'src', subpath + '.ts'),
190+
];
191+
192+
for (const possiblePath of possiblePaths) {
193+
if (
194+
fs.existsSync(possiblePath) &&
195+
fs.lstatSync(possiblePath).isFile()
196+
) {
197+
return possiblePath;
198+
}
199+
}
199200
}
200201

201-
if (modulePath.startsWith('@nx/docker/generators')) {
202-
return path.resolve(
203-
options.rootDir,
204-
`${packagesPath}docker/generators.ts`
202+
// Handle nx/src/* imports (direct nx package imports)
203+
const nxSrcMatch = modulePath.match(/^nx\/src\/(.+)$/);
204+
if (nxSrcMatch) {
205+
const subpath = nxSrcMatch[1];
206+
const resolvedPath = path.join(
207+
packagesPath,
208+
'nx',
209+
'src',
210+
subpath + '.ts'
205211
);
212+
if (fs.existsSync(resolvedPath) && fs.lstatSync(resolvedPath).isFile()) {
213+
return resolvedPath;
214+
}
206215
}
207216

208-
// Handle other packages with src/* structure where tests are working
209-
const srcPackages = [
210-
'rspack',
211-
'nx',
212-
'eslint-plugin',
213-
'react',
214-
'vite',
215-
'rollup',
216-
'workspace',
217-
'angular',
218-
'next',
219-
'node',
220-
'web',
221-
'webpack',
222-
'cypress',
223-
'jest',
224-
];
225-
for (const pkg of srcPackages) {
226-
if (modulePath.startsWith(`@nx/${pkg}/src/`)) {
227-
const relativePath = modulePath.replace(`@nx/${pkg}/src/`, '');
228-
return path.resolve(
229-
options.rootDir,
230-
`${packagesPath}${pkg}/src/`,
231-
relativePath + '.ts'
232-
);
233-
}
217+
// Handle nx/package.json specifically
218+
if (modulePath === 'nx/package.json') {
219+
return path.join(packagesPath, 'nx', 'package.json');
234220
}
235221

236-
// Handle nx/src/* paths (for direct nx package imports)
237-
if (modulePath.startsWith('nx/src/')) {
238-
const relativePath = modulePath.replace('nx/src/', '');
239-
return path.resolve(
240-
options.rootDir,
241-
`${packagesPath}nx/src/`,
242-
relativePath + '.ts'
243-
);
222+
// Handle other nx/* patterns
223+
const nxOtherPatternMatch = modulePath.match(/^nx\/(.+)$/);
224+
if (nxOtherPatternMatch) {
225+
const subpath = nxOtherPatternMatch[1];
226+
const resolvedPath = path.join(packagesPath, 'nx', subpath + '.ts');
227+
if (fs.existsSync(resolvedPath)) {
228+
return resolvedPath;
229+
}
244230
}
245231

246-
// Block other Nx packages from auto-resolution
232+
// Block excluded Nx packages from auto-resolution
247233
if (
248234
modulePath.startsWith('@nx/') &&
249235
!modulePath.startsWith('@nx/powerpack-') &&
250236
!excludedPackages.some((pkg) => modulePath.startsWith(pkg))
251237
) {
252-
throw new Error('custom resolution blocked');
253-
}
254-
255-
if (modulePath.startsWith('nx/') && !modulePath.startsWith('nx/src/'))
256-
throw new Error('custom resolution blocked');
257-
258-
if (modulePath.includes('@nx/workspace')) {
259-
throw new Error(
260-
'Reference to local Nx package found. Use local version instead.'
238+
// If we get here, it means the workspace package couldn't be resolved above
239+
// This might indicate a missing file or incorrect import
240+
console.warn(
241+
`[resolver] Could not resolve workspace package: ${modulePath}`
261242
);
262243
}
263244

0 commit comments

Comments
 (0)