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>
This commit is contained in:
parent
c38440e85f
commit
f6d2dccd62
@ -22,6 +22,7 @@ export type Options = [
|
||||
ignoredDependencies?: string[];
|
||||
ignoredFiles?: string[];
|
||||
includeTransitiveDependencies?: boolean;
|
||||
useLocalPathsForWorkspaceDependencies?: boolean;
|
||||
}
|
||||
];
|
||||
|
||||
@ -56,6 +57,7 @@ export default ESLintUtils.RuleCreator(
|
||||
checkObsoleteDependencies: { type: 'boolean' },
|
||||
checkVersionMismatches: { type: 'boolean' },
|
||||
includeTransitiveDependencies: { type: 'boolean' },
|
||||
useLocalPathsForWorkspaceDependencies: { type: 'boolean' },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
@ -76,6 +78,7 @@ export default ESLintUtils.RuleCreator(
|
||||
ignoredDependencies: [],
|
||||
ignoredFiles: [],
|
||||
includeTransitiveDependencies: false,
|
||||
useLocalPathsForWorkspaceDependencies: false,
|
||||
},
|
||||
],
|
||||
create(
|
||||
@ -89,6 +92,7 @@ export default ESLintUtils.RuleCreator(
|
||||
checkObsoleteDependencies,
|
||||
checkVersionMismatches,
|
||||
includeTransitiveDependencies,
|
||||
useLocalPathsForWorkspaceDependencies,
|
||||
},
|
||||
]
|
||||
) {
|
||||
@ -139,6 +143,7 @@ export default ESLintUtils.RuleCreator(
|
||||
{
|
||||
includeTransitiveDependencies,
|
||||
ignoredFiles,
|
||||
useLocalPathsForWorkspaceDependencies,
|
||||
}
|
||||
);
|
||||
const expectedDependencyNames = Object.keys(npmDependencies);
|
||||
@ -206,6 +211,8 @@ export default ESLintUtils.RuleCreator(
|
||||
return;
|
||||
}
|
||||
if (
|
||||
npmDependencies[packageName].startsWith('file:') ||
|
||||
packageRange.startsWith('file:') ||
|
||||
npmDependencies[packageName] === '*' ||
|
||||
packageRange === '*' ||
|
||||
satisfies(npmDependencies[packageName], packageRange, {
|
||||
|
||||
@ -389,12 +389,78 @@ describe('findNpmDependencies', () => {
|
||||
expect(
|
||||
findNpmDependencies('/root', lib1, projectGraph, projectFileMap, 'build')
|
||||
).toEqual({
|
||||
'@acme/lib3': '*',
|
||||
'@acme/lib3': '0.0.1',
|
||||
});
|
||||
expect(
|
||||
findNpmDependencies('/root', lib2, projectGraph, projectFileMap, 'build')
|
||||
).toEqual({
|
||||
'@acme/lib3': '*',
|
||||
'@acme/lib3': '0.0.1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should support local path for workspace dependencies', () => {
|
||||
vol.fromJSON(
|
||||
{
|
||||
'./libs/c/package.json': JSON.stringify({
|
||||
name: '@acme/c',
|
||||
version: '0.0.1',
|
||||
}),
|
||||
'./nx.json': JSON.stringify(nxJson),
|
||||
},
|
||||
'/root'
|
||||
);
|
||||
const a = {
|
||||
name: 'a',
|
||||
type: 'lib' as const,
|
||||
data: {
|
||||
root: 'libs/a',
|
||||
targets: { build: {} },
|
||||
},
|
||||
};
|
||||
const b = {
|
||||
name: 'b',
|
||||
type: 'lib' as const,
|
||||
data: {
|
||||
root: 'libs/b',
|
||||
targets: { build: {} },
|
||||
},
|
||||
};
|
||||
const c = {
|
||||
name: 'c',
|
||||
type: 'lib' as const,
|
||||
data: {
|
||||
root: 'libs/c',
|
||||
targets: { build: {} },
|
||||
},
|
||||
};
|
||||
const projectGraph = {
|
||||
nodes: {
|
||||
a: a,
|
||||
b: b,
|
||||
c: c,
|
||||
},
|
||||
externalNodes: {},
|
||||
dependencies: {},
|
||||
};
|
||||
const projectFileMap = {
|
||||
a: [{ file: 'libs/a/index.ts', hash: '123', deps: ['c'] }],
|
||||
b: [{ file: 'libs/a/index.ts', hash: '123', deps: ['c'] }],
|
||||
c: [],
|
||||
};
|
||||
|
||||
expect(
|
||||
findNpmDependencies('/root', a, projectGraph, projectFileMap, 'build', {
|
||||
useLocalPathsForWorkspaceDependencies: true,
|
||||
})
|
||||
).toEqual({
|
||||
'@acme/c': 'file:../c',
|
||||
});
|
||||
expect(
|
||||
findNpmDependencies('/root', b, projectGraph, projectFileMap, 'build', {
|
||||
useLocalPathsForWorkspaceDependencies: true,
|
||||
})
|
||||
).toEqual({
|
||||
'@acme/c': 'file:../c',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { join } from 'path';
|
||||
import { join, relative } from 'path';
|
||||
import { readNxJson } from 'nx/src/config/configuration';
|
||||
import {
|
||||
joinPathFragments,
|
||||
normalizePath,
|
||||
type ProjectFileMap,
|
||||
type ProjectGraph,
|
||||
type ProjectGraphProjectNode,
|
||||
type ProjectFileMap,
|
||||
readJsonFile,
|
||||
FileData,
|
||||
joinPathFragments,
|
||||
} from '@nx/devkit';
|
||||
import { fileExists } from 'nx/src/utils/fileutils';
|
||||
import { fileDataDepTarget } from 'nx/src/config/project-graph';
|
||||
@ -28,6 +28,7 @@ export function findNpmDependencies(
|
||||
options: {
|
||||
includeTransitiveDependencies?: boolean;
|
||||
ignoredFiles?: string[];
|
||||
useLocalPathsForWorkspaceDependencies?: boolean;
|
||||
} = {}
|
||||
): Record<string, string> {
|
||||
let seen: null | Set<string> = null;
|
||||
@ -51,6 +52,7 @@ export function findNpmDependencies(
|
||||
projectFileMap,
|
||||
buildTarget,
|
||||
options.ignoredFiles,
|
||||
options.useLocalPathsForWorkspaceDependencies,
|
||||
collectedDeps
|
||||
);
|
||||
|
||||
@ -86,6 +88,7 @@ function collectDependenciesFromFileMap(
|
||||
projectFileMap: ProjectFileMap,
|
||||
buildTarget: string,
|
||||
ignoredFiles: string[],
|
||||
useLocalPathsForWorkspaceDependencies: boolean,
|
||||
npmDeps: Record<string, string>
|
||||
): void {
|
||||
const rawFiles = projectFileMap[sourceProject.name];
|
||||
@ -140,12 +143,26 @@ function collectDependenciesFromFileMap(
|
||||
// Make sure package.json exists and has a valid name.
|
||||
packageJson?.name
|
||||
) {
|
||||
// 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.
|
||||
// ASSUMPTION: Most users will use '*' for workspace lib versions. Otherwise, they can manually update it.
|
||||
npmDeps[packageJson.name] = '*';
|
||||
let version: string;
|
||||
if (useLocalPathsForWorkspaceDependencies) {
|
||||
// Find the relative `file:...` path and use that as the version value.
|
||||
// This is useful for monorepos like Nx where the release will handle setting the correct version in dist.
|
||||
const depRoot = join(workspaceRoot, workspaceDep.data.root);
|
||||
const ownRoot = join(workspaceRoot, sourceProject.data.root);
|
||||
const relativePath = relative(ownRoot, depRoot);
|
||||
const filePath = normalizePath(relativePath); // normalize slashes for windows
|
||||
version = `file:${filePath}`;
|
||||
} else {
|
||||
// Otherwise, read the version from the dependencies `package.json` file.
|
||||
// This is useful for monorepos that commit release versions.
|
||||
// Users can also set version as "*" in source `package.json` files, which will be the value set here.
|
||||
// This is useful if they use custom scripts to update them in dist.
|
||||
version = packageJson.version ?? '*'; // fallback in case version is missing
|
||||
}
|
||||
npmDeps[packageJson.name] = version;
|
||||
seenWorkspaceDeps[workspaceDep.name] = {
|
||||
name: packageJson.name,
|
||||
version: '*',
|
||||
version,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -158,6 +175,7 @@ function readPackageJson(
|
||||
workspaceRoot: string
|
||||
): null | {
|
||||
name: string;
|
||||
version?: string;
|
||||
dependencies?: Record<string, string>;
|
||||
optionalDependencies?: Record<string, string>;
|
||||
peerDependencies?: Record<string, string>;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user