Skip to content

Commit dbe64a5

Browse files
committed
fix: don't error on circular eslint plugins
Replace the getESLint.js cache object with a Map, using object keys instead of JSON strings.
1 parent e23dd77 commit dbe64a5

File tree

3 files changed

+56
-40
lines changed

3 files changed

+56
-40
lines changed

src/getESLint.js

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,20 @@ const { Worker: JestWorker } = require('jest-worker');
55
// @ts-ignore
66
const { setup, lintFiles } = require('./worker');
77
const { getESLintOptions } = require('./options');
8-
const { jsonStringifyReplacerSortKeys } = require('./utils');
98

10-
/** @type {{[key: string]: any}} */
11-
const cache = {};
9+
/** @type {Map<CacheKey, any>} */
10+
const cache = new Map();
11+
12+
class CacheKey {
13+
/**
14+
* @param {string|undefined} key
15+
* @param {Options} options
16+
*/
17+
constructor(key, options) {
18+
this.key = key;
19+
this.options = options;
20+
}
21+
}
1222

1323
/** @typedef {import('eslint').ESLint} ESLint */
1424
/** @typedef {import('eslint').ESLint.LintResult} LintResult */
@@ -46,7 +56,7 @@ async function loadESLint(options) {
4656
* @returns {Promise<Linter>}
4757
*/
4858
async function loadESLintThreaded(key, poolSize, options) {
49-
const cacheKey = getCacheKey(key, options);
59+
const cacheKey = new CacheKey(key, options);
5060
const { eslintPath = 'eslint' } = options;
5161
const source = require.resolve('./worker');
5262
const workerOptions = {
@@ -73,7 +83,7 @@ async function loadESLintThreaded(key, poolSize, options) {
7383
(worker && (await worker.lintFiles(files))) ||
7484
/* istanbul ignore next */ [],
7585
cleanup: async () => {
76-
cache[cacheKey] = local;
86+
cache.set(cacheKey, local);
7787
context.lintFiles = (files) => local.lintFiles(files);
7888
if (worker) {
7989
worker.end();
@@ -99,23 +109,16 @@ async function getESLint(key, { threads, ...options }) {
99109
: /* istanbul ignore next */
100110
threads;
101111

102-
const cacheKey = getCacheKey(key, { threads, ...options });
103-
if (!cache[cacheKey]) {
104-
cache[cacheKey] =
112+
const cacheKey = new CacheKey(key, { threads, ...options });
113+
if (!cache.has(cacheKey)) {
114+
cache.set(
115+
cacheKey,
105116
max > 1
106117
? await loadESLintThreaded(key, max, options)
107-
: await loadESLint(options);
118+
: await loadESLint(options),
119+
);
108120
}
109-
return cache[cacheKey];
110-
}
111-
112-
/**
113-
* @param {string|undefined} key
114-
* @param {Options} options
115-
* @returns {string}
116-
*/
117-
function getCacheKey(key, options) {
118-
return JSON.stringify({ key, options }, jsonStringifyReplacerSortKeys);
121+
return cache.get(cacheKey);
119122
}
120123

121124
module.exports = {

src/utils.js

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -89,29 +89,8 @@ function parseFoldersToGlobs(patterns, extensions = []) {
8989
});
9090
}
9191

92-
/**
93-
* @param {string} _ key, but unused
94-
* @param {any} value
95-
*/
96-
const jsonStringifyReplacerSortKeys = (_, value) => {
97-
/**
98-
* @param {{ [x: string]: any; }} sorted
99-
* @param {string | number} key
100-
*/
101-
const insert = (sorted, key) => {
102-
// eslint-disable-next-line no-param-reassign
103-
sorted[key] = value[key];
104-
return sorted;
105-
};
106-
107-
return value instanceof Object && !(value instanceof Array)
108-
? Object.keys(value).sort().reduce(insert, {})
109-
: value;
110-
};
111-
11292
module.exports = {
11393
arrify,
11494
parseFiles,
11595
parseFoldersToGlobs,
116-
jsonStringifyReplacerSortKeys,
11796
};

test/circular-plugin.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import pack from './utils/pack';
2+
3+
describe('circular plugin', () => {
4+
it('should support plugins with circular configs', async () => {
5+
const plugin = {
6+
configs: {},
7+
rules: {},
8+
processors: {},
9+
};
10+
11+
Object.assign(plugin.configs, {
12+
recommended: {
13+
plugins: {
14+
self: plugin,
15+
},
16+
rules: {},
17+
},
18+
});
19+
20+
const loaderOptions = {
21+
configType: 'flat',
22+
overrideConfig: {
23+
plugins: { plugin: plugin },
24+
},
25+
overrideConfigFile: true,
26+
};
27+
28+
const compiler = pack('good', loaderOptions);
29+
30+
const stats = await compiler.runAsync();
31+
expect(stats.hasWarnings()).toBe(false);
32+
expect(stats.hasErrors()).toBe(false);
33+
});
34+
});

0 commit comments

Comments
 (0)