Skip to content

Commit d0835ed

Browse files
feat: Eslint v9 (#195)
* feat!: update dependency to eslint v9 @W-17445058 (#176) * feat!: update dependency to eslint v9 @W-17445058 BREAKING CHANGE: dropping support for ESLint v7 and v8. The only supported option is ESLint v9 * fix: lock file update * fix: update ci config * feat: migrate rule to ESLint v9 * fix: address PR feedback * chore: fixing lock file to have consistent registry (#178) * 2.0.0-beta.1 * chore: fix lock file to use consistent registry * 2.0.0-beta.2 (#179) * fix: add meta tags for plugin exports (#183) * feat!: v3.0.0-beta.1 (#184) * 2.0.0 * 3.0.0-beta.1 * fix: update globals dependency (#190) * 3.0.0-beta.2 (#191) * chore: update yarn.lock * chore: fix tests --------- Co-authored-by: Ravi Jayaramappa <ravi.jayaramappa@salesforce.com>
1 parent f0e96f1 commit d0835ed

22 files changed

+830
-849
lines changed

Diff for: .circleci/config.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
version: 2.1
22

3-
supported-eslint-versions: &supported-eslint-versions ["local", "7"]
3+
supported-eslint-versions: &supported-eslint-versions ["local"]
44

55
deploy_filters: &deploy_filters
66
filters:

Diff for: .eslintrc

-15
This file was deleted.

Diff for: README.md

+60-39
Original file line numberDiff line numberDiff line change
@@ -10,53 +10,74 @@ $ npm install eslint @babel/core @babel/eslint-parser @lwc/eslint-plugin-lwc --s
1010

1111
## Usage
1212

13-
Add `@lwc/eslint-plugin-lwc` to the `plugins` section of your configuration. Then configure the desired rules in the `rules` sections. Some of the syntax used in Lightning Web Components is not yet stage 4 (eg. class fields or decorators), and the out-of-the-box parser from ESLint doesn't support this syntax yet. In order to parse the LWC files properly, set the `parser` field to [`@babel/eslint-parser`](https://github.com/babel/babel/tree/main/eslint/babel-eslint-parser).
14-
15-
Example of `.eslintrc`:
16-
17-
```json
18-
{
19-
"parser": "@babel/eslint-parser",
20-
"parserOptions": {
21-
"requireConfigFile": false,
22-
"babelOptions": {
23-
"parserOpts": {
24-
"plugins": ["classProperties", ["decorators", { "decoratorsBeforeExport": false }]]
25-
}
26-
}
13+
_Starting with v3.0.0, @lwc/eslint-plugin-lwc only supports eslint@v9. Use @lwc/eslint-plugin-lwc@v1.x for older versions of eslint._
14+
15+
Import `@lwc/eslint-plugin-lwc` and use it in the `plugins` section of your configuration as shown below. Then configure the desired rules in the `rules` sections. Some of the syntax used in Lightning Web Components is not yet stage 4 (eg. class fields or decorators), and the out-of-the-box parser from ESLint doesn't support this syntax yet. In order to parse the LWC files properly, set the `parser` field to [`@babel/eslint-parser`](https://github.com/babel/babel/tree/main/eslint/babel-eslint-parser) in the `languageOptions` section of the eslint config.
16+
17+
Example of `eslint.config.js`:
18+
19+
```js
20+
const eslintPluginLwc = require('@lwc/eslint-plugin-lwc');
21+
const babelParser = require('@babel/eslint-parser');
22+
23+
module.exports = [
24+
{
25+
languageOptions: {
26+
parser: babelParser,
27+
parserOptions: {
28+
requireConfigFile: false,
29+
babelOptions: {
30+
parserOpts: {
31+
plugins: [
32+
'classProperties',
33+
['decorators', { decoratorsBeforeExport: false }],
34+
],
35+
},
36+
},
37+
},
38+
},
39+
plugins: {
40+
'@lwc/lwc': eslintPluginLwc, // https://github.com/salesforce/eslint-plugin-lwc
41+
},
42+
rules: {
43+
'@lwc/lwc/no-deprecated': 'error',
44+
'@lwc/lwc/valid-api': 'error',
45+
'@lwc/lwc/no-document-query': 'error',
46+
'@lwc/lwc/ssr-no-unsupported-properties': 'error',
47+
},
2748
},
28-
29-
"plugins": ["@lwc/eslint-plugin-lwc"],
30-
31-
"rules": {
32-
"@lwc/lwc/no-deprecated": "error",
33-
"@lwc/lwc/valid-api": "error",
34-
"@lwc/lwc/no-document-query": "error",
35-
"@lwc/lwc/ssr-no-unsupported-properties": "error"
36-
}
37-
}
49+
];
3850
```
3951

4052
### Usage with TypeScript
4153

42-
To enable working with TypeScript projects, install `@babel/preset-typescript` as a dependency add `"typescript"` to `parserOptions.babelOptions.parserOpts.plugins` in your `.eslintrc`.
54+
To enable working with TypeScript projects, install `@babel/preset-typescript` as a dependency add `"typescript"` to `languageOptions.parserOptions.babelOptions.parserOpts.plugins` in your `eslint.config.js`.
4355

4456
Example:
4557

46-
```json
47-
{
48-
"parserOptions": {
49-
"babelOptions": {
50-
"parserOpts": {
51-
"plugins": [
52-
"classProperties",
53-
["decorators", { "decoratorsBeforeExport": false }],
54-
"typescript"
55-
]
56-
}
57-
}
58-
}
59-
}
58+
```js
59+
const eslintPluginLwc = require('@lwc/eslint-plugin-lwc');
60+
const babelParser = require('@babel/eslint-parser');
61+
62+
module.exports = [
63+
{
64+
languageOptions: {
65+
parser: babelParser,
66+
parserOptions: {
67+
requireConfigFile: false,
68+
babelOptions: {
69+
parserOpts: {
70+
plugins: [
71+
'classProperties',
72+
['decorators', { decoratorsBeforeExport: false }],
73+
'typescript',
74+
],
75+
},
76+
},
77+
},
78+
},
79+
},
80+
];
6081
```
6182

6283
For more details about configuration please refer to the dedicated section in the ESLint documentation: https://eslint.org/docs/user-guide/configuring

Diff for: eslint.config.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
const globals = require('globals');
4+
const js = require('@eslint/js');
5+
6+
module.exports = [
7+
js.configs.recommended,
8+
{
9+
languageOptions: {
10+
globals: {
11+
...globals.mocha,
12+
...globals.node,
13+
},
14+
15+
sourceType: 'commonjs',
16+
},
17+
18+
rules: {
19+
strict: ['error', 'global'],
20+
},
21+
},
22+
];

Diff for: lib/index.js

+7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77
'use strict';
88

9+
const { version } = require('../package.json');
10+
911
const rules = {
1012
'consistent-component-name': require('./rules/consistent-component-name'),
1113
'no-api-reassignments': require('./rules/no-api-reassignments'),
@@ -44,6 +46,11 @@ const processors = {
4446
};
4547

4648
module.exports = {
49+
// https://eslint.org/docs/latest/extend/plugins#meta-data-in-plugins
50+
meta: {
51+
name: '@lwc/eslint-plugin-lwc',
52+
version,
53+
},
4754
processors,
4855
rules,
4956
};

Diff for: lib/rule-helpers.js

+8-7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
const { analyze } = require('./analyze-component');
99
const { isSSREscape } = require('./util/ssr');
1010
const { isGlobalIdentifier } = require('./util/scope');
11+
const { getAncestors, getScope } = require('./util/context');
1112

1213
/**
1314
* Visitors for detecting methods/functions that are reachable during SSR
@@ -133,7 +134,7 @@ const noReferenceParentQualifiers = new Set([
133134
const globalAccessQualifiers = new Set(['CallExpression', 'MemberExpression']);
134135

135136
function inModuleScope(node, context) {
136-
for (const ancestor of context.getAncestors()) {
137+
for (const ancestor of getAncestors(context, node)) {
137138
if (moduleScopeDisqualifiers.has(ancestor.type)) {
138139
return false;
139140
}
@@ -168,7 +169,7 @@ function noReferenceDuringSSR(forbiddenGlobalNames, messageIds, context) {
168169
node.object.name === 'window') &&
169170
node.property.type === 'Identifier' &&
170171
forbiddenGlobalNames.has(node.property.name) &&
171-
isGlobalIdentifier(node.object, context.getScope())
172+
isGlobalIdentifier(node.object, getScope(context, node))
172173
) {
173174
// Prevents expressions like:
174175
// globalThis.document.addEventListener('click', () => { ... });
@@ -193,7 +194,7 @@ function noReferenceDuringSSR(forbiddenGlobalNames, messageIds, context) {
193194
node.object.name === 'globalThis' &&
194195
node.property.type === 'Identifier' &&
195196
forbiddenGlobalNames.has(node.property.name) &&
196-
isGlobalIdentifier(node.object, context.getScope())
197+
isGlobalIdentifier(node.object, getScope(context, node))
197198
) {
198199
// Prevents expressions like:
199200
// globalThis.addEventListener('click', () => { ... });
@@ -214,7 +215,7 @@ function noReferenceDuringSSR(forbiddenGlobalNames, messageIds, context) {
214215
node.parent.type !== 'MemberExpression' &&
215216
node.object.type === 'Identifier' &&
216217
forbiddenGlobalNames.has(node.object.name) &&
217-
isGlobalIdentifier(node.object, context.getScope())
218+
isGlobalIdentifier(node.object, getScope(context, node))
218219
) {
219220
// Prevents expressions like:
220221
// window.addEventListener('click', () => { ... });
@@ -244,7 +245,7 @@ function noReferenceDuringSSR(forbiddenGlobalNames, messageIds, context) {
244245
if (
245246
noReferenceParentQualifiers.has(node.parent.type) &&
246247
forbiddenGlobalNames.has(node.name) &&
247-
isGlobalIdentifier(node, context.getScope())
248+
isGlobalIdentifier(node, getScope(context, node))
248249
) {
249250
// Prevents expressions like:
250251
// doSomethingWith(window);
@@ -264,7 +265,7 @@ function noReferenceDuringSSR(forbiddenGlobalNames, messageIds, context) {
264265
node.parent.operator === 'instanceof' &&
265266
node.parent.right === node &&
266267
forbiddenGlobalNames.has(node.name) &&
267-
isGlobalIdentifier(node, context.getScope())
268+
isGlobalIdentifier(node, getScope(context, node))
268269
) {
269270
// Prevents expressions like:
270271
// if (value instanceof Element) { ... }
@@ -284,7 +285,7 @@ function noReferenceDuringSSR(forbiddenGlobalNames, messageIds, context) {
284285
node.expression.type === 'CallExpression' &&
285286
node.expression.callee.type === 'Identifier' &&
286287
forbiddenGlobalNames.has(node.expression.callee.name) &&
287-
isGlobalIdentifier(node, context.getScope())
288+
isGlobalIdentifier(node, getScope(context, node))
288289
) {
289290
// Prevents global expressions like:
290291
// addEventListener('resize', () => {...});

Diff for: lib/rules/consistent-component-name.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const path = require('path');
1010

1111
const { docUrl } = require('../util/doc-url');
1212
const { isComponent } = require('../util/component');
13+
const { getSourceCode } = require('../util/context');
1314

1415
module.exports = {
1516
meta: {
@@ -25,8 +26,8 @@ module.exports = {
2526
schema: [],
2627
},
2728
create(context) {
28-
const fileName = context.getFilename();
29-
const sourceCode = context.getSourceCode();
29+
const sourceCode = getSourceCode(context);
30+
const fileName = context.filename ?? context.getFilename();
3031

3132
const fileBasename = path.basename(fileName, path.extname(fileName));
3233
const expectComponentName = fileBasename.charAt(0).toUpperCase() + fileBasename.slice(1);

Diff for: lib/rules/no-async-operation.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
'use strict';
88

9+
const { getScope } = require('../util/context');
910
const { docUrl } = require('../util/doc-url');
1011
const { isGlobalIdentifier } = require('../util/scope');
1112

@@ -29,7 +30,7 @@ module.exports = {
2930
return {
3031
CallExpression(node) {
3132
const { callee } = node;
32-
const scope = context.getScope();
33+
const scope = getScope(context, node);
3334

3435
// Check for direct invocation of global restricted APIs.
3536
// eg. setTimeout() or requestAnimationFrame();

Diff for: lib/rules/no-leaky-event-listeners.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
'use strict';
88

9+
const { getScope } = require('../util/context');
910
const { docUrl } = require('../util/doc-url');
1011
const { isGlobalIdentifier } = require('../util/scope');
1112

@@ -83,7 +84,7 @@ module.exports = {
8384
return {
8485
CallExpression(node) {
8586
const { callee } = node;
86-
const scope = context.getScope();
87+
const scope = getScope(context, node);
8788

8889
// Handle cases where the method is attached on the global object:
8990
// - addEventListener('click', () => {});

Diff for: lib/rules/no-unexpected-wire-adapter-usages.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
const { Minimatch } = require('minimatch');
1010
const { docUrl } = require('../util/doc-url');
1111
const { isWireDecorator } = require('../util/decorator');
12+
const { getScope } = require('../util/context');
1213

1314
function getImportedIdentifier(specifierNode) {
1415
// Namespace imports are not analyzed because it is impossible to track accurately the usage of
@@ -66,7 +67,7 @@ module.exports = {
6667

6768
return {
6869
ImportDeclaration(node) {
69-
const scope = context.getScope();
70+
const scope = getScope(context, node);
7071
const moduleIdentifier = node.source.value;
7172

7273
for (const specifier of node.specifiers) {

Diff for: lib/rules/no-unknown-wire-adapters.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
const { Minimatch } = require('minimatch');
1010

1111
const { docUrl } = require('../util/doc-url');
12+
const { getScope } = require('../util/context');
1213

1314
module.exports = {
1415
meta: {
@@ -76,7 +77,7 @@ module.exports = {
7677
const adapterName = adapterNode.name;
7778

7879
// Let's resolve the reference to the wire adapter identifier in the current scope.
79-
const scope = context.getScope();
80+
const scope = getScope(context, node);
8081
const adapterVariable = scope.references.find(
8182
(r) => r.identifier === adapterNode,
8283
).resolved;

Diff for: lib/rules/prefer-custom-event.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
'use strict';
88

9+
const { getScope } = require('../util/context');
910
const { docUrl } = require('../util/doc-url');
1011
const { isGlobalIdentifier } = require('../util/scope');
1112

@@ -25,7 +26,7 @@ module.exports = {
2526
return {
2627
NewExpression(node) {
2728
const { callee } = node;
28-
const scope = context.getScope();
29+
const scope = getScope(context, node);
2930

3031
if (
3132
callee.type === 'Identifier' &&

Diff for: lib/util/component.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77
'use strict';
88

9+
const { getAncestors } = require('./context');
10+
911
/**
1012
* Returns true if the node is a LWC component.
1113
*
@@ -19,7 +21,7 @@ function isComponent(node, context) {
1921
return false;
2022
}
2123

22-
const program = context.getAncestors(node).find(({ type }) => type === 'Program');
24+
const program = getAncestors(context, node).find(({ type }) => type === 'Program');
2325

2426
const importDeclaration = program.body
2527
.filter(({ type }) => type === 'ImportDeclaration')

0 commit comments

Comments
 (0)