feat(linter): add runtimeHelpers option to @nx/dependency-checks rule (#29954)
This commit is contained in:
parent
9672949f82
commit
713b9fbaaf
@ -79,13 +79,14 @@ Sometimes we intentionally want to add or remove a dependency from our `package.
|
||||
|
||||
## Options
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
| ------------------------------------- | --------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| buildTargets | _Array<string>_ | _["build"]_ | List of build target names |
|
||||
| checkMissingDependencies | _boolean_ | _true_ | Disable to skip checking for missing dependencies |
|
||||
| checkObsoleteDependencies | _boolean_ | _true_ | Disable to skip checking for unused dependencies |
|
||||
| checkVersionMismatches | _boolean_ | _true_ | Disable to skip checking if version specifier matches installed version |
|
||||
| ignoredDependencies | _Array<string>_ | _[]_ | List of dependencies to ignore for checks |
|
||||
| ignoredFiles | _Array<string>_ | N/A | List of files to ignore when collecting dependencies. The default value will be set based on the selected executor during the generation. |
|
||||
| includeTransitiveDependencies | _boolean_ | _false_ | Enable to collect dependencies of children projects |
|
||||
| useLocalPathsForWorkspaceDependencies | _boolean_ | _false_ | Set workspace dependencies as relative file:// paths. Useful for monorepos that link via file:// in package.json files. |
|
||||
| Property | Type | Default | Description |
|
||||
| ------------------------------------- | --------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| buildTargets | _Array<string>_ | _["build"]_ | List of build target names |
|
||||
| checkMissingDependencies | _boolean_ | _true_ | Disable to skip checking for missing dependencies |
|
||||
| checkObsoleteDependencies | _boolean_ | _true_ | Disable to skip checking for unused dependencies |
|
||||
| checkVersionMismatches | _boolean_ | _true_ | Disable to skip checking if version specifier matches installed version |
|
||||
| ignoredDependencies | _Array<string>_ | _[]_ | List of dependencies to ignore for checks |
|
||||
| ignoredFiles | _Array<string>_ | N/A | List of files to ignore when collecting dependencies. The default value will be set based on the selected executor during the generation. |
|
||||
| includeTransitiveDependencies | _boolean_ | _false_ | Enable to collect dependencies of children projects |
|
||||
| useLocalPathsForWorkspaceDependencies | _boolean_ | _false_ | Set workspace dependencies as relative file:// paths. Useful for monorepos that link via file:// in package.json files. |
|
||||
| runtimeHelpers | _Array<string>_ | _[]_ | List of helper packages used by the built output (e.g. `tslib` when using `tsc` and `importHelpers` is set to `true`). The rule already detects some of them in some scenarios, but this option can be used to detect them when it doesn't happen automatically. |
|
||||
|
||||
@ -79,13 +79,14 @@ Sometimes we intentionally want to add or remove a dependency from our `package.
|
||||
|
||||
## Options
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
| ------------------------------------- | --------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| buildTargets | _Array<string>_ | _["build"]_ | List of build target names |
|
||||
| checkMissingDependencies | _boolean_ | _true_ | Disable to skip checking for missing dependencies |
|
||||
| checkObsoleteDependencies | _boolean_ | _true_ | Disable to skip checking for unused dependencies |
|
||||
| checkVersionMismatches | _boolean_ | _true_ | Disable to skip checking if version specifier matches installed version |
|
||||
| ignoredDependencies | _Array<string>_ | _[]_ | List of dependencies to ignore for checks |
|
||||
| ignoredFiles | _Array<string>_ | N/A | List of files to ignore when collecting dependencies. The default value will be set based on the selected executor during the generation. |
|
||||
| includeTransitiveDependencies | _boolean_ | _false_ | Enable to collect dependencies of children projects |
|
||||
| useLocalPathsForWorkspaceDependencies | _boolean_ | _false_ | Set workspace dependencies as relative file:// paths. Useful for monorepos that link via file:// in package.json files. |
|
||||
| Property | Type | Default | Description |
|
||||
| ------------------------------------- | --------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| buildTargets | _Array<string>_ | _["build"]_ | List of build target names |
|
||||
| checkMissingDependencies | _boolean_ | _true_ | Disable to skip checking for missing dependencies |
|
||||
| checkObsoleteDependencies | _boolean_ | _true_ | Disable to skip checking for unused dependencies |
|
||||
| checkVersionMismatches | _boolean_ | _true_ | Disable to skip checking if version specifier matches installed version |
|
||||
| ignoredDependencies | _Array<string>_ | _[]_ | List of dependencies to ignore for checks |
|
||||
| ignoredFiles | _Array<string>_ | N/A | List of files to ignore when collecting dependencies. The default value will be set based on the selected executor during the generation. |
|
||||
| includeTransitiveDependencies | _boolean_ | _false_ | Enable to collect dependencies of children projects |
|
||||
| useLocalPathsForWorkspaceDependencies | _boolean_ | _false_ | Set workspace dependencies as relative file:// paths. Useful for monorepos that link via file:// in package.json files. |
|
||||
| runtimeHelpers | _Array<string>_ | _[]_ | List of helper packages used by the built output (e.g. `tslib` when using `tsc` and `importHelpers` is set to `true`). The rule already detects some of them in some scenarios, but this option can be used to detect them when it doesn't happen automatically. |
|
||||
|
||||
@ -1699,6 +1699,113 @@ describe('Dependency checks (eslint)', () => {
|
||||
`);
|
||||
expect(failures[0].line).toEqual(3);
|
||||
});
|
||||
|
||||
it('should require packages in runtimeHelpers', () => {
|
||||
const packageJson = {
|
||||
name: '@mycompany/liba',
|
||||
dependencies: { external1: '^16.0.0' },
|
||||
};
|
||||
|
||||
const swcrc = { jsc: { externalHelpers: true } };
|
||||
|
||||
const fileSys = {
|
||||
'./libs/liba/package.json': JSON.stringify(packageJson, null, 2),
|
||||
'./libs/liba/src/index.ts': '',
|
||||
'./libs/liba/.swcrc': JSON.stringify(swcrc, null, 2),
|
||||
'./package.json': JSON.stringify(rootPackageJson, null, 2),
|
||||
};
|
||||
vol.fromJSON(fileSys, '/root');
|
||||
|
||||
const failures = runRule(
|
||||
{ runtimeHelpers: ['@swc/helpers'] },
|
||||
`/root/libs/liba/package.json`,
|
||||
JSON.stringify(packageJson, null, 2),
|
||||
{
|
||||
nodes: {
|
||||
liba: {
|
||||
name: 'liba',
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/liba',
|
||||
targets: {
|
||||
build: {
|
||||
// custom executor that the rule wouldn't know about
|
||||
executor: '@my-org/some-package:build',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
externalNodes,
|
||||
dependencies: {
|
||||
liba: [{ source: 'liba', target: 'npm:external1', type: 'static' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
liba: [
|
||||
createFile(`libs/liba/src/main.ts`, ['npm:external1']),
|
||||
createFile(`libs/liba/package.json`, ['npm:external1']),
|
||||
],
|
||||
}
|
||||
);
|
||||
expect(failures.length).toEqual(1);
|
||||
expect(failures[0].message).toMatchInlineSnapshot(`
|
||||
"The "liba" project uses the following packages, but they are missing from "dependencies":
|
||||
- @swc/helpers"
|
||||
`);
|
||||
expect(failures[0].line).toEqual(3);
|
||||
});
|
||||
|
||||
it('should not report unused packages when specified in runtimeHelpers', () => {
|
||||
const packageJson = {
|
||||
name: '@mycompany/liba',
|
||||
dependencies: { '@swc/helpers': '1.2.3', external1: '^16.0.0' },
|
||||
};
|
||||
|
||||
const swcrc = { jsc: { externalHelpers: true } };
|
||||
|
||||
const fileSys = {
|
||||
'./libs/liba/package.json': JSON.stringify(packageJson, null, 2),
|
||||
'./libs/liba/src/index.ts': '',
|
||||
'./libs/liba/.swcrc': JSON.stringify(swcrc, null, 2),
|
||||
'./package.json': JSON.stringify(rootPackageJson, null, 2),
|
||||
};
|
||||
vol.fromJSON(fileSys, '/root');
|
||||
|
||||
const failures = runRule(
|
||||
{ runtimeHelpers: ['@swc/helpers'] },
|
||||
`/root/libs/liba/package.json`,
|
||||
JSON.stringify(packageJson, null, 2),
|
||||
{
|
||||
nodes: {
|
||||
liba: {
|
||||
name: 'liba',
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/liba',
|
||||
targets: {
|
||||
build: {
|
||||
// custom executor that the rule wouldn't know about
|
||||
executor: '@my-org/some-package:build',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
externalNodes,
|
||||
dependencies: {
|
||||
liba: [{ source: 'liba', target: 'npm:external1', type: 'static' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
liba: [
|
||||
createFile(`libs/liba/src/main.ts`, ['npm:external1']),
|
||||
createFile(`libs/liba/package.json`, ['npm:external1']),
|
||||
],
|
||||
}
|
||||
);
|
||||
expect(failures.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
function createFile(f: string, deps?: FileDataDependency[]): FileData {
|
||||
|
||||
@ -27,6 +27,7 @@ export type Options = [
|
||||
ignoredFiles?: string[];
|
||||
includeTransitiveDependencies?: boolean;
|
||||
useLocalPathsForWorkspaceDependencies?: boolean;
|
||||
runtimeHelpers?: string[];
|
||||
}
|
||||
];
|
||||
|
||||
@ -61,6 +62,7 @@ export default ESLintUtils.RuleCreator(
|
||||
checkVersionMismatches: { type: 'boolean' },
|
||||
includeTransitiveDependencies: { type: 'boolean' },
|
||||
useLocalPathsForWorkspaceDependencies: { type: 'boolean' },
|
||||
runtimeHelpers: { type: 'array', items: { type: 'string' } },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
@ -82,6 +84,7 @@ export default ESLintUtils.RuleCreator(
|
||||
ignoredFiles: [],
|
||||
includeTransitiveDependencies: false,
|
||||
useLocalPathsForWorkspaceDependencies: false,
|
||||
runtimeHelpers: [],
|
||||
},
|
||||
],
|
||||
create(
|
||||
@ -96,6 +99,7 @@ export default ESLintUtils.RuleCreator(
|
||||
checkVersionMismatches,
|
||||
includeTransitiveDependencies,
|
||||
useLocalPathsForWorkspaceDependencies,
|
||||
runtimeHelpers,
|
||||
},
|
||||
]
|
||||
) {
|
||||
@ -147,6 +151,7 @@ export default ESLintUtils.RuleCreator(
|
||||
includeTransitiveDependencies,
|
||||
ignoredFiles,
|
||||
useLocalPathsForWorkspaceDependencies,
|
||||
runtimeHelpers,
|
||||
}
|
||||
);
|
||||
const expectedDependencyNames = Object.keys(npmDependencies);
|
||||
|
||||
@ -374,6 +374,104 @@ describe('findNpmDependencies', () => {
|
||||
expect(results).toEqual({});
|
||||
});
|
||||
|
||||
it('should pick up helper npm dependencies from runtimeHelpers', () => {
|
||||
const libWithUnknownExecutor = {
|
||||
name: 'my-lib',
|
||||
type: 'lib' as const,
|
||||
data: {
|
||||
root: 'libs/my-lib',
|
||||
targets: {
|
||||
build: { executor: '@my-org/some-package:build' },
|
||||
},
|
||||
},
|
||||
};
|
||||
const projectGraph = {
|
||||
nodes: {
|
||||
'my-lib': libWithUnknownExecutor,
|
||||
},
|
||||
externalNodes: {
|
||||
'npm:tslib': {
|
||||
name: 'npm:tslib' as const,
|
||||
type: 'npm' as const,
|
||||
data: { packageName: 'tslib', version: '2.6.0' },
|
||||
},
|
||||
'npm:@swc/helpers': {
|
||||
name: 'npm:@swc/helpers' as const,
|
||||
type: 'npm' as const,
|
||||
data: { packageName: '@swc/helpers', version: '0.5.0' },
|
||||
},
|
||||
},
|
||||
dependencies: {},
|
||||
};
|
||||
const projectFileMap = { 'my-lib': [] };
|
||||
|
||||
const results = findNpmDependencies(
|
||||
'/root',
|
||||
libWithUnknownExecutor,
|
||||
projectGraph,
|
||||
projectFileMap,
|
||||
'build',
|
||||
{ runtimeHelpers: ['tslib'] }
|
||||
);
|
||||
|
||||
expect(results).toStrictEqual({ tslib: '2.6.0' });
|
||||
});
|
||||
|
||||
it('should not duplicate helper npm dependencies when additionally set in runtimeHelpers', () => {
|
||||
vol.fromJSON(
|
||||
{
|
||||
'./nx.json': JSON.stringify(nxJson),
|
||||
'./libs/my-lib/tsconfig.json': JSON.stringify({
|
||||
compilerOptions: { importHelpers: true },
|
||||
}),
|
||||
},
|
||||
'/root'
|
||||
);
|
||||
const libWithUnknownExecutor = {
|
||||
name: 'my-lib',
|
||||
type: 'lib' as const,
|
||||
data: {
|
||||
root: 'libs/my-lib',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nx/js:tsc',
|
||||
options: { tsConfig: 'libs/my-lib/tsconfig.json' },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const projectGraph = {
|
||||
nodes: {
|
||||
'my-lib': libWithUnknownExecutor,
|
||||
},
|
||||
externalNodes: {
|
||||
'npm:tslib': {
|
||||
name: 'npm:tslib' as const,
|
||||
type: 'npm' as const,
|
||||
data: { packageName: 'tslib', version: '2.6.0' },
|
||||
},
|
||||
'npm:@swc/helpers': {
|
||||
name: 'npm:@swc/helpers' as const,
|
||||
type: 'npm' as const,
|
||||
data: { packageName: '@swc/helpers', version: '0.5.0' },
|
||||
},
|
||||
},
|
||||
dependencies: {},
|
||||
};
|
||||
const projectFileMap = { 'my-lib': [] };
|
||||
|
||||
const results = findNpmDependencies(
|
||||
'/root',
|
||||
libWithUnknownExecutor,
|
||||
projectGraph,
|
||||
projectFileMap,
|
||||
'build',
|
||||
{ runtimeHelpers: ['tslib'] }
|
||||
);
|
||||
|
||||
expect(results).toStrictEqual({ tslib: '2.6.0' });
|
||||
});
|
||||
|
||||
it('should support recursive collection of dependencies', () => {
|
||||
vol.fromJSON(
|
||||
{
|
||||
|
||||
@ -29,6 +29,7 @@ export function findNpmDependencies(
|
||||
includeTransitiveDependencies?: boolean;
|
||||
ignoredFiles?: string[];
|
||||
useLocalPathsForWorkspaceDependencies?: boolean;
|
||||
runtimeHelpers?: string[];
|
||||
} = {}
|
||||
): Record<string, string> {
|
||||
let seen: null | Set<string> = null;
|
||||
@ -61,6 +62,7 @@ export function findNpmDependencies(
|
||||
currentProject,
|
||||
projectGraph,
|
||||
buildTarget,
|
||||
options.runtimeHelpers,
|
||||
collectedDeps
|
||||
);
|
||||
|
||||
@ -194,8 +196,23 @@ function collectHelperDependencies(
|
||||
sourceProject: ProjectGraphProjectNode,
|
||||
projectGraph: ProjectGraph,
|
||||
buildTarget: string,
|
||||
runtimeHelpers: string[] | undefined,
|
||||
npmDeps: Record<string, string>
|
||||
): void {
|
||||
if (runtimeHelpers?.length > 0) {
|
||||
for (const helper of runtimeHelpers) {
|
||||
if (
|
||||
!npmDeps[helper] &&
|
||||
projectGraph.externalNodes[`npm:${helper}`]?.type === 'npm'
|
||||
) {
|
||||
npmDeps[helper] =
|
||||
projectGraph.externalNodes[`npm:${helper}`].data.version;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const target = sourceProject.data.targets[buildTarget];
|
||||
if (!target) return;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user