Skip to content

Commit

Permalink
feat(linter): add option to ignore files based on pattern (#18863)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaysoo authored Aug 28, 2023
1 parent 29850b0 commit 90ca436
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 15 deletions.
69 changes: 69 additions & 0 deletions packages/eslint-plugin/src/rules/dependency-checks.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,75 @@ describe('Dependency checks (eslint)', () => {
expect(failures.length).toEqual(0);
});

it('should exclude files that are ignored', () => {
const packageJson = {
name: '@mycompany/liba',
dependencies: {},
};

const fileSys = {
'./libs/liba/package.json': JSON.stringify(packageJson, null, 2),
'./libs/liba/vite.config.ts': '',
'./libs/liba/project.json': JSON.stringify(
{
name: 'liba',
targets: {
build: {
command: 'tsc -p tsconfig.lib.json',
},
},
},
null,
2
),
'./nx.json': JSON.stringify({
targetDefaults: {
build: {
inputs: [
'{projectRoot}/**/*',
'!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)',
],
},
},
}),
'./package.json': JSON.stringify(rootPackageJson, null, 2),
};
vol.fromJSON(fileSys, '/root');

const failures = runRule(
{
ignoredFiles: ['{projectRoot}/vite.config.ts'],
},
`/root/libs/liba/package.json`,
JSON.stringify(packageJson, null, 2),
{
nodes: {
liba: {
name: 'liba',
type: 'lib',
data: {
root: 'libs/liba',
targets: {
build: {},
},
},
},
},
externalNodes,
dependencies: {
liba: [{ source: 'liba', target: 'npm:external1', type: 'static' }],
},
},
{
liba: [
createFile(`libs/liba/vite.config.ts`, ['npm:external1']),
createFile(`libs/liba/package.json`, []),
],
}
);
expect(failures.length).toEqual(0);
});

it('should report missing dependencies section and fix it', () => {
const packageJson = {
name: '@mycompany/liba',
Expand Down
5 changes: 5 additions & 0 deletions packages/eslint-plugin/src/rules/dependency-checks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export type Options = [
checkVersionMismatches?: boolean;
checkMissingPackageJson?: boolean;
ignoredDependencies?: string[];
ignoredFiles?: string[];
includeTransitiveDependencies?: boolean;
}
];
Expand Down Expand Up @@ -49,6 +50,7 @@ export default createESLintRule<Options, MessageIds>({
properties: {
buildTargets: [{ type: 'string' }],
ignoredDependencies: [{ type: 'string' }],
ignoredFiles: [{ type: 'string' }],
checkMissingDependencies: { type: 'boolean' },
checkObsoleteDependencies: { type: 'boolean' },
checkVersionMismatches: { type: 'boolean' },
Expand All @@ -71,6 +73,7 @@ export default createESLintRule<Options, MessageIds>({
checkObsoleteDependencies: true,
checkVersionMismatches: true,
ignoredDependencies: [],
ignoredFiles: [],
includeTransitiveDependencies: false,
},
],
Expand All @@ -80,6 +83,7 @@ export default createESLintRule<Options, MessageIds>({
{
buildTargets,
ignoredDependencies,
ignoredFiles,
checkMissingDependencies,
checkObsoleteDependencies,
checkVersionMismatches,
Expand Down Expand Up @@ -133,6 +137,7 @@ export default createESLintRule<Options, MessageIds>({
buildTarget, // TODO: What if child library has a build target different from the parent?
{
includeTransitiveDependencies,
ignoredFiles,
}
);
const expectedDependencyNames = Object.keys(npmDependencies);
Expand Down
74 changes: 74 additions & 0 deletions packages/js/src/generators/library/library.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,20 @@ describe('lib', () => {
executor: '@nx/vite:test',
});
expect(tree.exists('libs/my-lib/vite.config.ts')).toBeTruthy();
expect(
readJson(tree, 'libs/my-lib/.eslintrc.json').overrides
).toContainEqual({
files: ['*.json'],
parser: 'jsonc-eslint-parser',
rules: {
'@nx/dependency-checks': [
'error',
{
ignoredFiles: ['{projectRoot}/vite.config.{js,ts,mjs,mts}'],
},
],
},
});
});

it.each`
Expand All @@ -1159,6 +1173,66 @@ describe('lib', () => {
);
});

describe('--bundler=esbuild', () => {
it('should add build with esbuild', async () => {
await libraryGenerator(tree, {
...defaultOptions,
name: 'myLib',
bundler: 'esbuild',
unitTestRunner: 'none',
});

const project = readProjectConfiguration(tree, 'my-lib');
expect(project.targets.build).toMatchObject({
executor: '@nx/esbuild:esbuild',
});
expect(
readJson(tree, 'libs/my-lib/.eslintrc.json').overrides
).toContainEqual({
files: ['*.json'],
parser: 'jsonc-eslint-parser',
rules: {
'@nx/dependency-checks': [
'error',
{
ignoredFiles: ['{projectRoot}/esbuild.config.{js,ts,mjs,mts}'],
},
],
},
});
});
});

describe('--bundler=rollup', () => {
it('should add build with rollup', async () => {
await libraryGenerator(tree, {
...defaultOptions,
name: 'myLib',
bundler: 'rollup',
unitTestRunner: 'none',
});

const project = readProjectConfiguration(tree, 'my-lib');
expect(project.targets.build).toMatchObject({
executor: '@nx/rollup:rollup',
});
expect(
readJson(tree, 'libs/my-lib/.eslintrc.json').overrides
).toContainEqual({
files: ['*.json'],
parser: 'jsonc-eslint-parser',
rules: {
'@nx/dependency-checks': [
'error',
{
ignoredFiles: ['{projectRoot}/rollup.config.{js,ts,mjs,mts}'],
},
],
},
});
});
});

describe('--minimal', () => {
it('should generate a README.md when minimal is set to false', async () => {
await libraryGenerator(tree, {
Expand Down
67 changes: 61 additions & 6 deletions packages/js/src/generators/library/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
names,
offsetFromRoot,
ProjectConfiguration,
readProjectConfiguration,
runTasksInSerial,
toJS,
Tree,
Expand Down Expand Up @@ -236,12 +237,14 @@ export type AddLintOptions = Pick<
| 'js'
| 'setParserOptionsProject'
| 'rootProject'
| 'bundler'
>;
export async function addLint(
tree: Tree,
options: AddLintOptions
): Promise<GeneratorCallback> {
const { lintProjectGenerator } = ensurePackage('@nx/linter', nxVersion);
const projectConfiguration = readProjectConfiguration(tree, options.name);
const task = lintProjectGenerator(tree, {
project: options.name,
linter: options.linter,
Expand All @@ -256,15 +259,17 @@ export async function addLint(
setParserOptionsProject: options.setParserOptionsProject,
rootProject: options.rootProject,
});
const {
addOverrideToLintConfig,
lintConfigHasOverride,
isEslintConfigSupported,
updateOverrideInLintConfig,
// nx-ignore-next-line
} = require('@nx/linter/src/generators/utils/eslint-file');

// Also update the root ESLint config. The lintProjectGenerator will not generate it for root projects.
// But we need to set the package.json checks.
if (options.rootProject) {
const {
addOverrideToLintConfig,
isEslintConfigSupported,
// nx-ignore-next-line
} = require('@nx/linter/src/generators/utils/eslint-file');

if (isEslintConfigSupported(tree)) {
addOverrideToLintConfig(tree, '', {
files: ['*.json'],
Expand All @@ -275,6 +280,56 @@ export async function addLint(
});
}
}

// If project lints package.json with @nx/dependency-checks, then add ignore files for
// build configuration files such as vite.config.ts. These config files need to be
// ignored, otherwise we will errors on missing dependencies that are for dev only.
if (
lintConfigHasOverride(
tree,
projectConfiguration.root,
(o) =>
Array.isArray(o.files)
? o.files.some((f) => f.match(/\.json$/))
: !!o.files?.match(/\.json$/),
true
)
) {
updateOverrideInLintConfig(
tree,
projectConfiguration.root,
(o) => o.rules?.['@nx/dependency-checks'],
(o) => {
const value = o.rules['@nx/dependency-checks'];
let ruleSeverity: string;
let ruleOptions: any;
if (Array.isArray(value)) {
ruleSeverity = value[0];
ruleOptions = value[1];
} else {
ruleSeverity = value;
ruleOptions = {};
}
if (options.bundler === 'vite' || options.unitTestRunner === 'vitest') {
ruleOptions.ignoredFiles = [
'{projectRoot}/vite.config.{js,ts,mjs,mts}',
];
o.rules['@nx/dependency-checks'] = [ruleSeverity, ruleOptions];
} else if (options.bundler === 'rollup') {
ruleOptions.ignoredFiles = [
'{projectRoot}/rollup.config.{js,ts,mjs,mts}',
];
o.rules['@nx/dependency-checks'] = [ruleSeverity, ruleOptions];
} else if (options.bundler === 'esbuild') {
ruleOptions.ignoredFiles = [
'{projectRoot}/esbuild.config.{js,ts,mjs,mts}',
];
o.rules['@nx/dependency-checks'] = [ruleSeverity, ruleOptions];
}
return o;
}
);
}
return task;
}

Expand Down
53 changes: 53 additions & 0 deletions packages/js/src/utils/find-npm-dependencies.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,4 +397,57 @@ describe('findNpmDependencies', () => {
'@acme/lib3': '*',
});
});

it('should support ignoring extra file patterns in addition to task input', () => {
vol.fromJSON(
{
'./nx.json': JSON.stringify(nxJson),
},
'/root'
);
const lib = {
name: 'my-lib',
type: 'lib' as const,
data: {
root: 'libs/my-lib',
targets: { build: {} },
},
};
const projectGraph = {
nodes: {
'my-lib': lib,
},
externalNodes: {
'npm:foo': {
name: 'npm:foo' as const,
type: 'npm' as const,
data: {
packageName: 'foo',
version: '1.0.0',
},
},
},
dependencies: {},
};
const projectFileMap = {
'my-lib': [
{
file: 'libs/my-lib/vite.config.ts',
hash: '123',
deps: ['npm:foo'],
},
],
};

const results = findNpmDependencies(
'/root',
lib,
projectGraph,
projectFileMap,
'build',
{ ignoredFiles: ['{projectRoot}/vite.config.ts'] }
);

expect(results).toEqual({});
});
});
Loading

0 comments on commit 90ca436

Please sign in to comment.