8000 feat(linter): add option for @nx/dependency-checks to update workspac… · nrwl/nx@f6d2dcc · GitHub
[go: up one dir, main page]

Skip to content

Commit f6d2dcc

Browse files
jaysoomeeroslav
andauthored
feat(linter): add option for @nx/dependency-checks to update workspace dependencies using local file paths (#20157)
Co-authored-by: Miroslav Jonaš <missing.manual@gmail.com>
1 parent c38440e commit f6d2dcc

File tree

3 files changed

+101
-10
lines changed

3 files changed

+101
-10
lines changed

packages/eslint-plugin/src/rules/dependency-checks.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export type Options = [
2222
ignoredDependencies?: string[];
2323
ignoredFiles?: string[];
2424
includeTransitiveDependencies?: boolean;
25+
useLocalPathsForWorkspaceDependencies?: boolean;
2526
}
2627
];
2728

@@ -56,6 +57,7 @@ export default ESLintUtils.RuleCreator(
5657
checkObsoleteDependencies: { type: 'boolean' },
5758
checkVersionMismatches: { type: 'boolean' },
5859
includeTransitiveDependencies: { type: 'boolean' },
60+
useLocalPathsForWorkspaceDependencies: { type: 'boolean' },
5961
},
6062
additionalProperties: false,
6163
},
@@ -76,6 +78,7 @@ export default ESLintUtils.RuleCreator(
7678
ignoredDependencies: [],
7779
ignoredFiles: [],
7880
includeTransitiveDependencies: false,
81+
useLocalPathsForWorkspaceDependencies: false,
7982
},
8083
],
8184
create(
@@ -89,6 +92,7 @@ export default ESLintUtils.RuleCreator(
8992
checkObsoleteDependencies,
9093
checkVersionMismatches,
9194
includeTransitiveDependencies,
95+
useLocalPathsForWorkspaceDependencies,
9296
},
9397
]
9498
) {
@@ -139,6 +143,7 @@ export default ESLintUtils.RuleCreator(
139143
{
140144
includeTransitiveDependencies,
141145
ignoredFiles,
146+
useLocalPathsForWorkspaceDependencies,
142147
}
143148
);
144149
const expectedDependencyNames = Object.keys(npmDependencies);
@@ -206,6 +211,8 @@ export default ESLintUtils.RuleCreator(
206211
return;
207212
}
208213
if (
214+
npmDependencies[packageName].startsWith('file:') ||
215+
packageRange.startsWith('file:') ||
209216
npmDependencies[packageName] === '*' ||
210217
packageRange === '*' ||
211218
satisfies(npmDependencies[packageName], packageRange, {

packages/js/src/utils/find-npm-dependencies.spec.ts

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,12 +389,78 @@ describe('findNpmDependencies', () => {
389389
expect(
390390
findNpmDependencies('/root', lib1, projectGraph, projectFileMap, 'build')
391391
).toEqual({
392-
'@acme/lib3': '*',
392+
'@acme/lib3': '0.0.1',
393393
});
394394
expect(
395395
findNpmDependencies('/root', lib2, projectGraph, projectFileMap, 'build')
396396
).toEqual({
397-
'@acme/lib3': '*',
397+
'@acme/lib3': '0.0.1',
398+
});
399+
});
400+
401+
it('should support local path for workspace dependencies', () => {
402+
vol.fromJSON(
403+
{
404+
'./libs/c/package.json': JSON.stringify({
405+
name: '@acme/c',
406+
version: '0.0.1',
407+
}),
408+
'./nx.json': JSON.stringify(nxJson),
409+
},
410+
'/root'
411+
);
412+
const a = {
413+
name: 'a',
414+
type: 'lib' as const,
415+
data: {
416+
root: 'libs/a',
417+
targets: { build: {} },
418+
},
419+
};
420+
const b = {
421+
name: 'b',
422+
type: 'lib' as const,
423+
data: {
424+
root: 'libs/b',
425+
targets: { build: {} },
426+
},
427+
};
428+
const c = {
429+
name: 'c',
430+
type: 'lib' as const,
431+
data: {
432+
root: 'libs/c',
433+
targets: { build: {} },
434+
},
435+
};
436+
const projectGraph = {
437+
nodes: {
438+
a: a,
439+
b: b,
440+
c: c,
441+
},
442+
externalNodes: {},
443+
dependencies: {},
444+
};
445+
const projectFileMap = {
446+
a: [{ file: 'libs/a/index.ts', hash: '123', deps: ['c'] }],
447+
b: [{ file: 'libs/a/index.ts', hash: '123', deps: ['c'] }],
448+
c: [],
449+
};
450+
451+
expect(
452+
findNpmDependencies('/root', a, projectGraph, projectFileMap, 'build', {
453+
useLocalPathsForWorkspaceDependencies: true,
454+
})
455+
).toEqual({
456+
'@acme/c': 'file:../c',
457+
});
458+
expect(
459+
findNpmDependencies('/root', b, projectGraph, projectFileMap, 'build', {
460+
useLocalPathsForWorkspaceDependencies: true,
461+
})
462+
).toEqual({
463+
'@acme/c': 'file:../c',
398464
});
399465
});
400466

packages/js/src/utils/find-npm-dependencies.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { join } from 'path';
1+
import { join, relative } from 'path';
22
import { readNxJson } from 'nx/src/config/configuration';
33
import {
4+
joinPathFragments,
5+
normalizePath,
6+
type ProjectFileMap,
47
type ProjectGraph,
58
type ProjectGraphProjectNode,
6-
type ProjectFileMap,
79
readJsonFile,
8-
FileData,
9-
joinPathFragments,
1010
} from '@nx/devkit';
1111
import { fileExists } from 'nx/src/utils/fileutils';
1212
import { fileDataDepTarget } from 'nx/src/config/project-graph';
@@ -28,6 +28,7 @@ export function findNpmDependencies(
2828
options: {
2929
includeTransitiveDependencies?: boolean;
3030
ignoredFiles?: string[];
31+
useLocalPathsForWorkspaceDependencies?: boolean;
3132
} = {}
3233
): Record<string, string> {
3334
let seen: null | Set<string> = null;
@@ -51,6 +52,7 @@ export function findNpmDependencies(
5152
projectFileMap,
5253
buildTarget,
5354
options.ignoredFiles,
55+
options.useLocalPathsForWorkspaceDependencies,
5456
collectedDeps
5557
);
5658

@@ -86,6 +88,7 @@ function collectDependenciesFromFileMap(
8688
projectFileMap: ProjectFileMap,
8789
buildTarget: string,
8890
ignoredFiles: string[],
91+
useLocalPathsForWorkspaceDependencies: boolean,
8992
npmDeps: Record<string, string>
9093
): void {
9194
const rawFiles = projectFileMap[sourceProject.name];
@@ -140,12 +143,26 @@ function collectDependenciesFromFileMap(
140143
// Make sure package.json exists and has a valid name.
141144
packageJson?.name
142145
) {
143-
// This is a workspace lib so we can't reliably read in a specific version since it depends on how the workspace is set up.
144-
// ASSUMPTION: Most users will use '*' for workspace lib versions. Otherwise, they can manually update it.
145-
npmDeps[packageJson.name] = '*';
146+
let version: string;
147+
if (useLocalPathsForWorkspaceDependencies) {
148+
// Find the relative `file:...` path and use that as the version value.
149+
// This is useful for monorepos like Nx where the release will handle setting the correct version in dist.
150+
const depRoot = join(workspaceRoot, workspaceDep.data.root);
151+
const ownRoot = join(workspaceRoot, sourceProject.data.root);
152+
const relativePath = relative(ownRoot, depRoot);
153+
const filePath = normalizePath(relativePath); // normalize slashes for windows
154+
version = `file:${filePath}`;
155+
} else {
156+
// Otherwise, read the version from the dependencies `package.json` file.
157+
// This is useful for monorepos that commit release versions.
158+
// Users can also set version as "*" in source `package.json` files, which will be the value set here.
159+
// This is useful if they use custom scripts to update them in dist.
160+
version = packageJson.version ?? '*'; // fallback in case version is missing
161+
}
162+
npmDeps[packageJson.name] = version;
146163
seenWorkspaceDeps[workspaceDep.name] = {
147164
name: packageJson.name,
148-
version: '*',
165+
version,
149166
};
150167
}
151168
}
@@ -158,6 +175,7 @@ function readPackageJson(
158175
workspaceRoot: string
159176
): null | {
160177
name: string;
178+
version?: string;
161179
dependencies?: Record<string, string>;
162180
optionalDependencies?: Record<string, string>;
163181
peerDependencies?: Record<string, string>;

0 commit comments

Comments
 (0)
0