Skip to content

Commit 39a2fb3

Browse files
authored
perf: fix lazy loading of core rules (#15606)
* perf: fix lazy loading of core rules * add test
1 parent 3fc9196 commit 39a2fb3

File tree

6 files changed

+123
-2
lines changed

6 files changed

+123
-2
lines changed

lib/config/flat-config-array.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ const { flatConfigSchema } = require("./flat-config-schema");
1414
const { RuleValidator } = require("./rule-validator");
1515
const { defaultConfig } = require("./default-config");
1616
const recommendedConfig = require("../../conf/eslint-recommended");
17-
const allConfig = require("../../conf/eslint-all");
1817

1918
//-----------------------------------------------------------------------------
2019
// Helpers
@@ -79,7 +78,13 @@ class FlatConfigArray extends ConfigArray {
7978
}
8079

8180
if (config === "eslint:all") {
82-
return allConfig;
81+
82+
/*
83+
* Load `eslint-all.js` here instead of at the top level to avoid loading all rule modules
84+
* when it isn't necessary. `eslint-all.js` reads `meta` of rule objects to filter out deprecated ones,
85+
* so requiring `eslint-all.js` module loads all rule modules as a consequence.
86+
*/
87+
return require("../../conf/eslint-all");
8388
}
8489

8590
return config;

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
"node-polyfill-webpack-plugin": "^1.0.3",
123123
"npm-license": "^0.3.3",
124124
"nyc": "^15.0.1",
125+
"pirates": "^4.0.5",
125126
"progress": "^2.0.3",
126127
"proxyquire": "^2.0.1",
127128
"puppeteer": "^9.1.1",
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* @fileoverview Tests lazy-loading of core rules
3+
* @author Milos Djermanovic
4+
*/
5+
6+
/*
7+
* This module should be run as a child process, with `fork()`,
8+
* because it is important to run this test with a separate, clean Node process
9+
* in order to add hooks before any of the ESLint modules is loaded.
10+
*/
11+
12+
"use strict";
13+
14+
const path = require("path");
15+
const assert = require("assert");
16+
const { addHook } = require("pirates");
17+
18+
const {
19+
dir: rulesDirectoryPath,
20+
name: rulesDirectoryIndexFilename
21+
} = path.parse(require.resolve("../../lib/rules"));
22+
23+
// Show full stack trace. The default 10 is usually not enough to find the root cause of this problem.
24+
Error.stackTraceLimit = Infinity;
25+
26+
const [cwd, pattern, usedRulesCommaSeparated] = process.argv.slice(2);
27+
28+
assert.ok(cwd, "cwd argument isn't provided");
29+
assert.ok(pattern, "pattern argument isn't provided");
30+
assert.ok(usedRulesCommaSeparated, "used rules argument isn't provided");
31+
32+
const usedRules = usedRulesCommaSeparated.split(",");
33+
34+
// `require()` hook
35+
addHook(
36+
(_code, filename) => {
37+
throw new Error(`Unexpected attempt to load unused rule ${filename}`);
38+
},
39+
{
40+
41+
// returns `true` if the hook (the function passed in as the first argument) should be called for this filename
42+
matcher(filename) {
43+
const { dir, name } = path.parse(filename);
44+
45+
if (dir === rulesDirectoryPath && ![rulesDirectoryIndexFilename, ...usedRules].includes(name)) {
46+
return true;
47+
}
48+
49+
return false;
50+
}
51+
52+
}
53+
);
54+
55+
/*
56+
* Everything related to loading any ESLint modules should be in this IIFE
57+
*/
58+
(async () => {
59+
const { ESLint } = require("../..");
60+
const eslint = new ESLint({ cwd });
61+
62+
await eslint.lintFiles([pattern]);
63+
})().catch(({ message, stack }) => {
64+
process.send({ message, stack });
65+
process.exit(1);
66+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
root: true,
3+
rules: {
4+
semi: 2
5+
}
6+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* content is not necessary */

tests/lib/eslint/eslint.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const {
2727
const hash = require("../../../lib/cli-engine/hash");
2828
const { unIndent, createCustomTeardown } = require("../../_utils");
2929
const coreRules = require("../../../lib/rules");
30+
const childProcess = require("child_process");
3031

3132
//------------------------------------------------------------------------------
3233
// Tests
@@ -6694,4 +6695,45 @@ describe("ESLint", () => {
66946695
});
66956696
});
66966697
});
6698+
6699+
describe("loading rules", () => {
6700+
it("should not load unused core rules", done => {
6701+
let calledDone = false;
6702+
6703+
const cwd = getFixturePath("lazy-loading-rules");
6704+
const pattern = "foo.js";
6705+
const usedRules = ["semi"];
6706+
6707+
const forkedProcess = childProcess.fork(
6708+
path.join(__dirname, "../../_utils/test-lazy-loading-rules.js"),
6709+
[cwd, pattern, String(usedRules)]
6710+
);
6711+
6712+
// this is an error message
6713+
forkedProcess.on("message", ({ message, stack }) => {
6714+
if (calledDone) {
6715+
return;
6716+
}
6717+
calledDone = true;
6718+
6719+
const error = new Error(message);
6720+
6721+
error.stack = stack;
6722+
done(error);
6723+
});
6724+
6725+
forkedProcess.on("exit", exitCode => {
6726+
if (calledDone) {
6727+
return;
6728+
}
6729+
calledDone = true;
6730+
6731+
if (exitCode === 0) {
6732+
done();
6733+
} else {
6734+
done(new Error("Forked process exited with a non-zero exit code"));
6735+
}
6736+
});
6737+
});
6738+
});
66976739
});

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy