feat(linter): Support --print-config feature of ESLint (#18260)

This commit is contained in:
Tony Ganchev 2023-07-31 17:11:21 +03:00 committed by GitHub
parent 554bea7a4c
commit 30c3e9925a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 9 deletions

View File

@ -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"],

View File

@ -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', () => {

View File

@ -25,6 +25,9 @@ class MockESLint {
loadFormatter = mockLoadFormatter;
isPathIgnored = mockIsPathIgnored;
lintFiles = mockLintFiles;
calculateConfigForFile(file: string) {
return { file: file };
}
}
const mockResolveAndInstantiateESLint = jest.fn().mockReturnValue(
@ -65,6 +68,7 @@ function createValidRunBuilderOptions(
rulesdir: [],
resolvePluginsRelativeTo: null,
reportUnusedDisableDirectives: null,
printConfig: null,
...additionalOptions,
};
}
@ -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 });
});
});

View File

@ -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
@ -49,7 +51,7 @@ export default async function run(
);
const { eslint, ESLint } = await resolveAndInstantiateESLint(
eslintConfigPath,
options,
normalizedOptions,
useFlatConfig
);
@ -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(
@ -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
)
)
@ -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;
@ -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 {
@ -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)),
};
}

View File

@ -20,6 +20,7 @@ export interface Schema extends JsonObject {
rulesdir: string[];
resolvePluginsRelativeTo: string | null;
reportUnusedDisableDirectives: Linter.RuleLevel | null;
printConfig?: string | null;
}
type Formatter =

View File

@ -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"],