|
1 | | -const { cpus } = require("node:os"); |
2 | | - |
3 | | -const { stringify } = require("flatted"); |
4 | | -const { Worker: JestWorker } = require("jest-worker"); |
5 | | - |
6 | 1 | const { getESLintOptions } = require("./options"); |
7 | | -const { jsonStringifyReplacerSortKeys } = require("./utils"); |
8 | | -const { lintFiles, setup } = require("./worker"); |
9 | | - |
10 | | -/** @type {{ [key: string]: Linter }} */ |
11 | | -const cache = {}; |
12 | 2 |
|
13 | 3 | /** @typedef {import("eslint").ESLint} ESLint */ |
14 | 4 | /** @typedef {import("eslint").ESLint.LintResult} LintResult */ |
15 | 5 | /** @typedef {import("./options").Options} Options */ |
16 | | -/** @typedef {() => Promise<void>} AsyncTask */ |
17 | 6 | /** @typedef {(files: string | string[]) => Promise<LintResult[]>} LintTask */ |
18 | | -/** @typedef {{ threads: number, eslint: ESLint, lintFiles: LintTask, cleanup: AsyncTask }} Linter */ |
19 | | -/** @typedef {JestWorker & { lintFiles: LintTask }} Worker */ |
| 7 | +/** @typedef {{ eslint: ESLint, lintFiles: LintTask }} Linter */ |
| 8 | +/** @typedef {import("eslint").ESLint.Options} ESLintOptions */ |
| 9 | +/** @typedef {{ new (arg0: ESLintOptions): ESLint, outputFixes: (arg0: LintResult[]) => Promise<void> }} ESLintClass */ |
20 | 10 |
|
21 | 11 | /** |
22 | 12 | * @param {Options} options options |
23 | 13 | * @returns {Promise<Linter>} linter |
24 | 14 | */ |
25 | | -async function loadESLint(options) { |
26 | | - const { eslintPath } = options; |
27 | | - const eslint = await setup({ |
28 | | - eslintPath, |
29 | | - configType: options.configType, |
30 | | - eslintOptions: getESLintOptions(options), |
| 15 | +async function getESLint(options) { |
| 16 | + const eslintOptions = getESLintOptions(options); |
| 17 | + const fix = Boolean(eslintOptions && eslintOptions.fix); |
| 18 | + |
| 19 | + const eslintModule = require(options.eslintPath || "eslint"); |
| 20 | + |
| 21 | + /** @type {ESLintClass} */ |
| 22 | + const ESLint = await eslintModule.loadESLint({ |
| 23 | + useFlatConfig: options.configType === "flat", |
31 | 24 | }); |
32 | 25 |
|
| 26 | + /** @type {ESLint} */ |
| 27 | + const eslint = new ESLint(eslintOptions); |
| 28 | + |
| 29 | + /** |
| 30 | + * @param {string | string[]} files files |
| 31 | + * @returns {Promise<LintResult[]>} lint results |
| 32 | + */ |
| 33 | + async function lintFiles(files) { |
| 34 | + /** @type {LintResult[]} */ |
| 35 | + const result = await eslint.lintFiles(files); |
| 36 | + // if enabled, use eslint autofixing where possible |
| 37 | + if (fix) { |
| 38 | + await ESLint.outputFixes(result); |
| 39 | + } |
| 40 | + return result; |
| 41 | + } |
| 42 | + |
33 | 43 | return { |
34 | | - threads: 1, |
35 | 44 | lintFiles, |
36 | 45 | eslint, |
37 | | - // no-op for non-threaded |
38 | | - cleanup: async () => {}, |
39 | | - }; |
40 | | -} |
41 | | - |
42 | | -/** |
43 | | - * @param {string | undefined} key a cache key |
44 | | - * @param {Options} options options |
45 | | - * @returns {string} a stringified cache key |
46 | | - */ |
47 | | -function getCacheKey(key, options) { |
48 | | - return stringify({ key, options }, jsonStringifyReplacerSortKeys); |
49 | | -} |
50 | | - |
51 | | -/** |
52 | | - * @param {string | undefined} key a cache key |
53 | | - * @param {number} poolSize number of workers |
54 | | - * @param {Options} options options |
55 | | - * @returns {Promise<Linter>} linter |
56 | | - */ |
57 | | -async function loadESLintThreaded(key, poolSize, options) { |
58 | | - const cacheKey = getCacheKey(key, options); |
59 | | - const { eslintPath = "eslint" } = options; |
60 | | - const source = require.resolve("./worker"); |
61 | | - const workerOptions = { |
62 | | - enableWorkerThreads: true, |
63 | | - numWorkers: poolSize, |
64 | | - setupArgs: [ |
65 | | - { |
66 | | - eslintPath, |
67 | | - configType: options.configType, |
68 | | - eslintOptions: getESLintOptions(options), |
69 | | - }, |
70 | | - ], |
71 | | - }; |
72 | | - |
73 | | - const local = await loadESLint(options); |
74 | | - |
75 | | - let worker = |
76 | | - /** @type {Worker | null} */ |
77 | | - (new JestWorker(source, workerOptions)); |
78 | | - |
79 | | - /** @type {Linter} */ |
80 | | - const context = { |
81 | | - ...local, |
82 | | - threads: poolSize, |
83 | | - lintFiles: async (files) => |
84 | | - (worker && (await worker.lintFiles(files))) || |
85 | | - /* istanbul ignore next */ [], |
86 | | - cleanup: async () => { |
87 | | - cache[cacheKey] = local; |
88 | | - context.lintFiles = (files) => local.lintFiles(files); |
89 | | - if (worker) { |
90 | | - worker.end(); |
91 | | - worker = null; |
92 | | - } |
93 | | - }, |
94 | 46 | }; |
95 | | - |
96 | | - return context; |
97 | | -} |
98 | | - |
99 | | -/** |
100 | | - * @param {string | undefined} key a cache key |
101 | | - * @param {Options} options options |
102 | | - * @returns {Promise<Linter>} linter |
103 | | - */ |
104 | | -async function getESLint(key, { threads, ...options }) { |
105 | | - const max = |
106 | | - typeof threads !== "number" |
107 | | - ? threads |
108 | | - ? cpus().length - 1 |
109 | | - : 1 |
110 | | - : /* istanbul ignore next */ |
111 | | - threads; |
112 | | - |
113 | | - const cacheKey = getCacheKey(key, { threads, ...options }); |
114 | | - if (!cache[cacheKey]) { |
115 | | - cache[cacheKey] = |
116 | | - max > 1 |
117 | | - ? await loadESLintThreaded(key, max, options) |
118 | | - : await loadESLint(options); |
119 | | - } |
120 | | - return cache[cacheKey]; |
121 | 47 | } |
122 | 48 |
|
123 | 49 | module.exports = { |
124 | 50 | getESLint, |
125 | | - loadESLint, |
126 | | - loadESLintThreaded, |
127 | 51 | }; |
0 commit comments