feat(linter): Support --print-config feature of ESLint (#18260)
This commit is contained in:
parent
554bea7a4c
commit
30c3e9925a
@ -128,6 +128,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["off", "warn", "error"],
|
"enum": ["off", "warn", "error"],
|
||||||
"description": "The equivalent of the `--report-unused-disable-directives` flag on the ESLint CLI."
|
"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"],
|
"required": ["lintFilePatterns"],
|
||||||
|
|||||||
@ -246,6 +246,21 @@ describe('Linter', () => {
|
|||||||
'A project tagged with "validtag" can only depend on libs tagged with "validtag"'
|
'A project tagged with "validtag" can only depend on libs tagged with "validtag"'
|
||||||
);
|
);
|
||||||
}, 1000000);
|
}, 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', () => {
|
describe('workspace boundary rules', () => {
|
||||||
|
|||||||
@ -25,6 +25,9 @@ class MockESLint {
|
|||||||
loadFormatter = mockLoadFormatter;
|
loadFormatter = mockLoadFormatter;
|
||||||
isPathIgnored = mockIsPathIgnored;
|
isPathIgnored = mockIsPathIgnored;
|
||||||
lintFiles = mockLintFiles;
|
lintFiles = mockLintFiles;
|
||||||
|
calculateConfigForFile(file: string) {
|
||||||
|
return { file: file };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mockResolveAndInstantiateESLint = jest.fn().mockReturnValue(
|
const mockResolveAndInstantiateESLint = jest.fn().mockReturnValue(
|
||||||
@ -65,6 +68,7 @@ function createValidRunBuilderOptions(
|
|||||||
rulesdir: [],
|
rulesdir: [],
|
||||||
resolvePluginsRelativeTo: null,
|
resolvePluginsRelativeTo: null,
|
||||||
reportUnusedDisableDirectives: null,
|
reportUnusedDisableDirectives: null,
|
||||||
|
printConfig: null,
|
||||||
...additionalOptions,
|
...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();
|
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 });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -39,6 +39,8 @@ export default async function run(
|
|||||||
? join(options.cacheLocation, projectName)
|
? join(options.cacheLocation, projectName)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
const { printConfig, ...normalizedOptions } = options;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Until ESLint v9 is released and the new so called flat config is the default
|
* 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
|
* 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(
|
const { eslint, ESLint } = await resolveAndInstantiateESLint(
|
||||||
eslintConfigPath,
|
eslintConfigPath,
|
||||||
options,
|
normalizedOptions,
|
||||||
useFlatConfig
|
useFlatConfig
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -63,10 +65,25 @@ export default async function run(
|
|||||||
throw new Error('ESLint must be version 7.6 or higher.');
|
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[] = [];
|
let lintResults: ESLint.LintResult[] = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
lintResults = await eslint.lintFiles(options.lintFilePatterns);
|
lintResults = await eslint.lintFiles(normalizedOptions.lintFilePatterns);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (
|
if (
|
||||||
err.message.includes(
|
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) {
|
if (lintResults.length === 0) {
|
||||||
const ignoredPatterns = (
|
const ignoredPatterns = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
options.lintFilePatterns.map(async (pattern) =>
|
normalizedOptions.lintFilePatterns.map(async (pattern) =>
|
||||||
(await eslint.isPathIgnored(pattern)) ? pattern : null
|
(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);
|
await ESLint.outputFixes(lintResults);
|
||||||
|
|
||||||
// if quiet, only show errors
|
// if quiet, only show errors
|
||||||
if (options.quiet) {
|
if (normalizedOptions.quiet) {
|
||||||
console.debug('Quiet mode enabled - filtering out warnings\n');
|
console.debug('Quiet mode enabled - filtering out warnings\n');
|
||||||
lintResults = ESLint.getErrorResults(lintResults);
|
lintResults = ESLint.getErrorResults(lintResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatter = await eslint.loadFormatter(options.format);
|
const formatter = await eslint.loadFormatter(normalizedOptions.format);
|
||||||
|
|
||||||
let totalErrors = 0;
|
let totalErrors = 0;
|
||||||
let totalWarnings = 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);
|
const formattedResults = await formatter.format(lintResults);
|
||||||
|
|
||||||
if (options.outputFile) {
|
if (normalizedOptions.outputFile) {
|
||||||
const pathToOutputFile = join(context.root, options.outputFile);
|
const pathToOutputFile = join(context.root, normalizedOptions.outputFile);
|
||||||
mkdirSync(dirname(pathToOutputFile), { recursive: true });
|
mkdirSync(dirname(pathToOutputFile), { recursive: true });
|
||||||
writeFileSync(pathToOutputFile, formattedResults);
|
writeFileSync(pathToOutputFile, formattedResults);
|
||||||
} else {
|
} else {
|
||||||
@ -162,8 +179,9 @@ Please see https://nx.dev/guides/eslint for full guidance on how to resolve this
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
success:
|
success:
|
||||||
options.force ||
|
normalizedOptions.force ||
|
||||||
(totalErrors === 0 &&
|
(totalErrors === 0 &&
|
||||||
(options.maxWarnings === -1 || totalWarnings <= options.maxWarnings)),
|
(normalizedOptions.maxWarnings === -1 ||
|
||||||
|
totalWarnings <= normalizedOptions.maxWarnings)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export interface Schema extends JsonObject {
|
|||||||
rulesdir: string[];
|
rulesdir: string[];
|
||||||
resolvePluginsRelativeTo: string | null;
|
resolvePluginsRelativeTo: string | null;
|
||||||
reportUnusedDisableDirectives: Linter.RuleLevel | null;
|
reportUnusedDisableDirectives: Linter.RuleLevel | null;
|
||||||
|
printConfig?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Formatter =
|
type Formatter =
|
||||||
|
|||||||
@ -132,6 +132,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["off", "warn", "error"],
|
"enum": ["off", "warn", "error"],
|
||||||
"description": "The equivalent of the `--report-unused-disable-directives` flag on the ESLint CLI."
|
"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"],
|
"required": ["lintFilePatterns"],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user