-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Refactor ESLint config #13482
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor ESLint config #13482
Conversation
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
81c1e52 to
f410eda
Compare
56c33ea to
ba551c5
Compare
| rules: { | ||
| // This rule does not work with CommonJS modules. We will just have to | ||
| // trust that all of the files specified above are indeed modules. | ||
| 'import/unambiguous': 'off', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In refactoring this config, I discovered that ESLint was confused over whether this file was a script or a module. It seemed that this was not possible because I thought that we'd configured eslint-plugin-import to work with CommonJS. That is true, but that setting only applies to the no-unresolved rule. The no-ambiguous rule is separate and only seems to work on ES module files . As far as I can tell, this is a long-standing bug and not a feature. In the meantime, we just have to turn this rule off for CommonJS files.
Builds ready [ba551c5]Page Load Metrics (1066 ± 19 ms)
|
ba551c5 to
8a2321b
Compare
Builds ready [8a2321b]Page Load Metrics (1159 ± 67 ms)
|
brad-decker
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
| }, | ||
| "@eslint/eslintrc": { | ||
| "packages": { | ||
| "<root>": true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Q] What does this mean? Why was added and is it a good thing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When running yarn lavamoat:auto after these changes, I saw this message show up:
[beta] scripts:core:prod:standardEntryPoints: (node:91080) UnhandledPromiseRejectionWarning: Error: Cannot read config file: /Users/elliot/code/metamask/metamask-extension/.eslintrc.base.js
[beta] Error: LavaMoat - required package not in whitelist: package "@eslint/eslintrc" requested "<root>" as "/Users/elliot/code/metamask/metamask-extension/.eslintrc.base.js"
[beta] Referenced from: BaseConfig#overrides[0] while parsing file: /Users/elliot/code/metamask/metamask-extension/ui/helpers/constants/routes.js
[beta] at requireRelative (LavaMoat/core/kernel:519:15)
[beta] at requireRelativeWithContext (LavaMoat/core/kernel:471:16)
[beta] at loadJSConfigFile (/Users/elliot/code/metamask/metamask-extension/node_modules/@eslint/eslintrc/lib/config-array-factory.js:225:16)
[beta] at loadConfigFile (/Users/elliot/code/metamask/metamask-extension/node_modules/@eslint/eslintrc/lib/config-array-factory.js:309:20)
[beta] at ConfigArrayFactory._loadConfigData (/Users/elliot/code/metamask/metamask-extension/node_modules/@eslint/eslintrc/lib/config-array-factory.js:609:42)
[beta] at ConfigArrayFactory._loadExtendedShareableConfig (/Users/elliot/code/metamask/metamask-extension/node_modules/@eslint/eslintrc/lib/config-array-factory.js:889:21)
[beta] at ConfigArrayFactory._loadExtends (/Users/elliot/code/metamask/metamask-extension/node_modules/@eslint/eslintrc/lib/config-array-factory.js:781:25)
[beta] at ConfigArrayFactory._normalizeObjectConfigDataBody (/Users/elliot/code/metamask/metamask-extension/node_modules/@eslint/eslintrc/lib/config-array-factory.js:720:25)
[beta] at _normalizeObjectConfigDataBody.next (<anonymous>)
[beta] at ConfigArrayFactory._normalizeObjectConfigData (/Users/elliot/code/metamask/metamask-extension/node_modules/@eslint/eslintrc/lib/config-array-factory.js:665:20)
I am not sure what LavaMoat needs access to in @eslint/eslintrc exactly. But after reviewing the documentation for the policy file, I think that <root> is a quirk of the format of the policy file and is a self-reference to @eslint/eslintrc within the config for @eslint/eslintrc. In other words I believe it instructs LavaMoat to process @eslint/eslintrc, as if you'd said this:
{
"packagesToProcess": {
"@eslint/eslintrc": {
"packagesToProcess": {
"<this package>": true,
"@babel/eslint-parser": true,
}
}
}
}One would think that if you add a package to the list of resources that LavaMoat would process it, but it looks like that's not true and you have to be explicit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That doesn't sound right. LavaMoat isn't asking for access to anything, it has access already. All packages have access to everything by default, that's what LavaMoat is preventing. LavaMoat is gating access, it's controlling which packages and APIs each package can access.
Here is the relevant portion of the quoted error message:
package "@eslint/eslintrc" requested "<root>" as "/Users/elliot/code/metamask/metamask-extension/.eslintrc.base.js"
Which means pretty much what it sounds like - the @eslint/eslintrc package is requesting the <root> package, but it wasn't explicitly granted access to that package so it was denied. The <root> package here is not @eslint/eslintrc, it's the root project, the MetaMask extension itself.
I'm guessing this is the case because now the .eslintrc.js file is extending another module in this repository. So when @eslint/eslintrc tries parsing that config file, it will have to import the base config as well, thus requiring access to the MetaMask extension itself. It didn't need that before, because we weren't extending from another config in this repo before. We were just extending from @metamask/eslint-config (which was already added to the policy override a few lines down from here, for this same reason).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, okay. That makes more sense.
|
There were some good suggestions provided in the walkthrough call today on how we should add TS to ESLint in a way that doesn't decrease performance. I'm going to move this back into draft status as this will prompt some changes. |
8a2321b to
4b74949
Compare
4b74949 to
c983c6c
Compare
We would like to insert TypeScript into the ESLint configuration, and because of the way that the current config is organized, that is not easy to do. Most files are assumed to be files that are suited for running in a browser context. This isn't correct, as we should expect most files to work in a Node context instead. This is because all browser-based files will be run through a transpiler that is able to make use of Node-specific variables anyway. There are a couple of important ways we can categories files which our ESLint config should be capable of handling well: * Is the file a script or a module? In other words, does the file run procedurally or is the file intended to be brought into an existing file? * If the file is a module, does it use the CommonJS syntax (`require()`) or does it use the ES syntax (`import`/`export`)? When we introduce TypeScript, this set of questions will become: * Is the file a script or a module? * If the file is a module, is it a JavaScript module or a TypeScript module? * If the file is a JavaScript module, does it use the CommonJS syntax (`require()`) or does it use the ES syntax (`import`/`export`)? To represent these divisions, this commit removes global rules — so now all of the rules are kept in `overrides` for explicitness — and sets up rules for CommonJS- and ES-module-compatible files that intentionally do not overlap with each other. This way TypeScript (which has its own set of rules independent from JavaScript and therefore shouldn't overlap with the other rules either) can be easily added later. Finally, this commit splits up the ESLint config into separate files and adds documentation to each section. This way sets of rules which are connected to a particular plugin (`jsdoc`, `@babel`, etc.) can be easily understood instead of being obscured.
c983c6c to
fd16fdf
Compare
Builds ready [fd16fdf]Page Load Metrics (1105 ± 40 ms)
|
| @@ -0,0 +1,9 @@ | |||
| module.exports = { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Babel-specific configuration was pulled out into its own file from .eslintrc, but there should be no changes with what was there before.
| @@ -0,0 +1,67 @@ | |||
| const path = require('path'); | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Base configuration was pulled out into its own file from .eslintrc, but there should be no changes with what was there before.
| @@ -0,0 +1,23 @@ | |||
| module.exports = { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JSDoc-specific configuration was pulled out into its own file from .eslintrc, but there should be no changes with what was there before.
| // Sometimes we use `let` instead of `const` to assign variables depending on | ||
| // the build type. | ||
| eslintrc.rules['prefer-const'] = 'off'; | ||
| eslintrc.overrides.forEach((override) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes are necessary to this file (which is part of the build system) because we run ESLint during the build system. To do this we pull in the ESLint configuration object from .eslintrc.js and we explicitly modify that object to disable some rules as this would cause failures which we are not interested in. Because ALL ESLint rules are now in overrides, I have to change how these modifications work.
| }); | ||
| }); | ||
|
|
||
| /* eslint-disable-next-line mocha/max-top-level-suites */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the updates to the ESLint config, these test files are now failing the lint step. I suspect this is the case because not all of the Mocha rules were getting applied to the e2e tests previously and now they are.
Builds ready [62d95fe]Page Load Metrics (1235 ± 66 ms)
|
brad-decker
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
We would like to insert TypeScript into the ESLint configuration, and
because of the way that the current config is organized, that is not
easy to do.
Most files are assumed to be files that are suited for running in a
browser context. This isn't correct, as we should expect most files to
work in a Node context instead. This is because all browser-based files
will be run through a transpiler that is able to make use of
Node-specific variables anyway.
There are a couple of important ways we can categories files which our
ESLint config should be capable of handling well:
procedurally or is the file intended to be brought into an existing
file?
require())or does it use the ES syntax (
import/export)?When we introduce TypeScript, this set of questions will become:
module?
(
require()) or does it use the ES syntax (import/export)?To represent these divisions, this commit removes global rules — so now
all of the rules are kept in
overridesfor explicitness — and sets uprules for CommonJS- and ES-module-compatible files that intentionally do
not overlap with each other. This way TypeScript (which has its own set
of rules independent from JavaScript and therefore shouldn't overlap
with the other rules either) can be easily added later.
Finally, this commit splits up the ESLint config into separate files and
adds documentation to each section. This way sets of rules which are
connected to a particular plugin (
jsdoc,@babel, etc.) can be easilyunderstood instead of being obscured.
Testing steps:
yarn lint. This command should not fail. (This is probably checked on CI anyway.)yarn start. This command should not fail with a linting error.yarn dist. This command should not fail with a linting error.