Skip to content

Commit

Permalink
feat(linter): Support --print-config feature of ESLint (#18260)
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyganchev committed Jul 31, 2023
1 parent 554bea7 commit 30c3e99
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 9 deletions.
5 changes: 5 additions & 0 deletions docs/generated/packages/linter/executors/eslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@
"type": "string",
"enum": ["off", "warn", "error"],
"description": "The equivalent of the `--report-unused-disable-directives` flag on the ESLint CLI."
},
"printConfig": {
"type": "string",
"description": "The equivalent of the `--print-config` flag on the ESLint CLI.",
"x-completion-type": "file"
}
},
"required": ["lintFilePatterns"],
Expand Down
15 changes: 15 additions & 0 deletions e2e/linter/src/linter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,21 @@ describe('Linter', () => {
'A project tagged with "validtag" can only depend on libs tagged with "validtag"'
);
}, 1000000);

it('should print the effective configuration for a file specified using --printConfig', () => {
const eslint = readJson('.eslintrc.json');
eslint.overrides.push({
files: ['src/index.ts'],
rules: {
'specific-rule': 'off',
},
});
updateFile('.eslintrc.json', JSON.stringify(eslint, null, 2));
const out = runCLI(`lint ${myapp} --printConfig src/index.ts`, {
silenceError: true,
});
expect(out).toContain('"specific-rule": [');
}, 1000000);
});

describe('workspace boundary rules', () => {
Expand Down
30 changes: 30 additions & 0 deletions packages/linter/src/executors/eslint/lint.impl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ class MockESLint {
loadFormatter = mockLoadFormatter;
isPathIgnored = mockIsPathIgnored;
lintFiles = mockLintFiles;
calculateConfigForFile(file: string) {
return { file: file };
}
}

const mockResolveAndInstantiateESLint = jest.fn().mockReturnValue(
Expand Down Expand Up @@ -65,6 +68,7 @@ function createValidRunBuilderOptions(
rulesdir: [],
resolvePluginsRelativeTo: null,
reportUnusedDisableDirectives: null,
printConfig: null,
...additionalOptions,
};
}
Expand Down Expand Up @@ -619,4 +623,30 @@ Please see https://nx.dev/guides/eslint for full guidance on how to resolve this
);
expect(fs.writeFileSync).not.toHaveBeenCalled();
});

it('should print the ESLint configuration for a specified file if printConfiguration is specified', async () => {
setupMocks();
jest.spyOn(console, 'log');
const result = await lintExecutor(
createValidRunBuilderOptions({
lintFilePatterns: [],
eslintConfig: './.eslintrc.json',
fix: true,
cache: true,
cacheLocation: 'cacheLocation1',
format: 'stylish',
force: false,
silent: false,
ignorePath: null,
maxWarnings: null,
outputFile: null,
quiet: false,
reportUnusedDisableDirectives: null,
printConfig: 'test-source.ts',
}),
mockContext
);
expect(console.log).toHaveBeenCalledWith('{\n "file": "test-source.ts"\n}');
expect(result).toEqual({ success: true });
});
});
36 changes: 27 additions & 9 deletions packages/linter/src/executors/eslint/lint.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export default async function run(
? join(options.cacheLocation, projectName)
: undefined;

const { printConfig, ...normalizedOptions } = options;

/**
* Until ESLint v9 is released and the new so called flat config is the default
* we only want to support it if the user has explicitly opted into it by converting
Expand All @@ -49,7 +51,7 @@ export default async function run(
);
const { eslint, ESLint } = await resolveAndInstantiateESLint(
eslintConfigPath,
options,
normalizedOptions,
useFlatConfig
);

Expand All @@ -63,10 +65,25 @@ export default async function run(
throw new Error('ESLint must be version 7.6 or higher.');
}

if (printConfig) {
try {
const fileConfig = await eslint.calculateConfigForFile(printConfig);
console.log(JSON.stringify(fileConfig, null, ' '));
return {
success: true,
};
} catch (err) {
console.error(err);
return {
success: false,
};
}
}

let lintResults: ESLint.LintResult[] = [];

try {
lintResults = await eslint.lintFiles(options.lintFilePatterns);
lintResults = await eslint.lintFiles(normalizedOptions.lintFilePatterns);
} catch (err) {
if (
err.message.includes(
Expand Down Expand Up @@ -98,7 +115,7 @@ Please see https://nx.dev/guides/eslint for full guidance on how to resolve this
if (lintResults.length === 0) {
const ignoredPatterns = (
await Promise.all(
options.lintFilePatterns.map(async (pattern) =>
normalizedOptions.lintFilePatterns.map(async (pattern) =>
(await eslint.isPathIgnored(pattern)) ? pattern : null
)
)
Expand All @@ -121,12 +138,12 @@ Please see https://nx.dev/guides/eslint for full guidance on how to resolve this
await ESLint.outputFixes(lintResults);

// if quiet, only show errors
if (options.quiet) {
if (normalizedOptions.quiet) {
console.debug('Quiet mode enabled - filtering out warnings\n');
lintResults = ESLint.getErrorResults(lintResults);
}

const formatter = await eslint.loadFormatter(options.format);
const formatter = await eslint.loadFormatter(normalizedOptions.format);

let totalErrors = 0;
let totalWarnings = 0;
Expand All @@ -140,8 +157,8 @@ Please see https://nx.dev/guides/eslint for full guidance on how to resolve this

const formattedResults = await formatter.format(lintResults);

if (options.outputFile) {
const pathToOutputFile = join(context.root, options.outputFile);
if (normalizedOptions.outputFile) {
const pathToOutputFile = join(context.root, normalizedOptions.outputFile);
mkdirSync(dirname(pathToOutputFile), { recursive: true });
writeFileSync(pathToOutputFile, formattedResults);
} else {
Expand All @@ -162,8 +179,9 @@ Please see https://nx.dev/guides/eslint for full guidance on how to resolve this

return {
success:
options.force ||
normalizedOptions.force ||
(totalErrors === 0 &&
(options.maxWarnings === -1 || totalWarnings <= options.maxWarnings)),
(normalizedOptions.maxWarnings === -1 ||
totalWarnings <= normalizedOptions.maxWarnings)),
};
}
1 change: 1 addition & 0 deletions packages/linter/src/executors/eslint/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface Schema extends JsonObject {
rulesdir: string[];
resolvePluginsRelativeTo: string | null;
reportUnusedDisableDirectives: Linter.RuleLevel | null;
printConfig?: string | null;
}

type Formatter =
Expand Down
5 changes: 5 additions & 0 deletions packages/linter/src/executors/eslint/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@
"type": "string",
"enum": ["off", "warn", "error"],
"description": "The equivalent of the `--report-unused-disable-directives` flag on the ESLint CLI."
},
"printConfig": {
"type": "string",
"description": "The equivalent of the `--print-config` flag on the ESLint CLI.",
"x-completion-type": "file"
}
},
"required": ["lintFilePatterns"],
Expand Down

1 comment on commit 30c3e99

@vercel
Copy link

@vercel vercel bot commented on 30c3e99 Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-five.vercel.app
nx-dev-git-master-nrwl.vercel.app
nx.dev
nx-dev-nrwl.vercel.app

Please sign in to comment.