Skip to content

Updates to support ESLint 9.x #61

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

khawkins
Copy link
Contributor

What does this PR do?

Updates our ESLint support to 9.x. I'll add comments inline.

@khawkins khawkins requested review from dbreese, sfdctaka, maliroteh-sf and a team as code owners June 10, 2025 20:46
const js = require('@eslint/js');
const nodePlugin = require('eslint-plugin-n');
const eslintPlugin = require('eslint-plugin-eslint-plugin');
const tseslint = require('typescript-eslint');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added TypeScript linting for the index.d.ts type declarations.

*/

const js = require('@eslint/js');
const nodePlugin = require('eslint-plugin-n');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eslint-plugin-node is a project that has apparently fallen by the wayside. So the ESLint community forked it and went with eslint-plugin-n. This project, by contrast, gets a goodly amount of attention.

@@ -5,8 +5,6 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/

'use strict';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are going to be a lot of these to ignore, unfortunately. Apparently this isn't required for modules, and throws linting exceptions if you keep them.

@@ -0,0 +1,21 @@
/*
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

base.js was migrated into base-legacy.js. This is the configuration that ESLint 8.x consumers will need to use.

@@ -5,18 +5,21 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/

'use strict';
const bundleAnalyzer = require('../processor');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

base.js is now the ESLint flat config, supported by ESLint 9. ESLint 9 support is now the default.

* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/

module.exports = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

recommended-legacy.js is the same as base-legacy.js. ESLint 8.x consumers will need to use these configs.

@@ -5,80 +5,63 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/

'use strict';
const baseConfig = require('./base');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...and recommended.js becomes the flat config for ESLint 9.x support.

const LwcBundle = require('./lwc-bundle');

const noGetterContainsMoreThanReturnStatement = require('./rules/no-getter-contains-more-than-return-statement');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These have all been moved into an aggregate export in rules/index.js. Makes the plugin spec easier to read.

recommended
base: require('./configs/base'),
recommended: require('./configs/recommended'),
'base-legacy': require('./configs/base-legacy'),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both "current" and "legacy" configs are now available. Any ESLint engine >=7 will work with the plugin, as long as you use the right config.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to update the readme.md to let the user now how to use legacy config in ESLint 8. For example, for ESLint 8, use

{
    "extends": ["eslint:recommended", "plugin:@salesforce/lwc-graph-analyzer/recommended-legacy"]
}

@@ -151,7 +149,6 @@ class BundleAnalyzer {
* by the ESLint CLI, or a dummy value (__placeholder__.js) if called programatically.
* @returns A one-dimensional flattened array of messages.
*/
// eslint-disable-next-line no-unused-vars
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason no-unused-vars no longer flags here, and honestly I can't figure out why. ¯\(ツ)

@@ -0,0 +1,40 @@
module.exports = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the rule definitions moved in here. Keep all the clutter in one place.

*/
function createLinter(rulename) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the options specified in here were legacy 8.x options, I just got rid of the method and added const linter = new Linter(); where it was necessary.

linter.defineParser('@babel/eslint-parser', babelParser);
linter.defineRule(rulename, lwcGraphAnalyzer.rules[rulename]);
return linter;
function filterCodeBlock(blockFilename) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out this (and its use below) is important for calling Linter.verify() programmatically. Without it, ESLint will just silently drop any files coming out of preprocessing, that don't have a .js extension. So .html files were not working.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the linter.verify(srcCode, config, { filename: testPath, filterCodeBlock }), the config already has the html file configured. If that is not enough, does that mean lgpt-core lint has the same issue?

const testPath = join(dirname(rulePath), target);
const srcCode = readFileSync(testPath).toString();

return linter.verify(srcCode, config, {
filename: testPath,
preprocess: lwcGraphAnalyzer.processors.bundleAnalyzer.preprocess,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has all just been normalized to standard ESLint config, so the config returned by createLinterConfig() is sufficient to capture the proper options now.

const { RuleTester } = require('eslint');
const { RULE_TESTER_CONFIG } = require('./shared');
const lwcGraphAnalyzer = require('../../../lib/index');
const bundleAnalyzer = lwcGraphAnalyzer.processors.bundleAnalyzer;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting from here, you'll see this across the rest of the test/lib/rules tests. The updates are all the same: remove the weird configuration override from 8.x where there was no other way to specify a processor, in favor of the ESLint config in shared.js, which defines it.

babelOptions: {
parserOpts: {
plugins: [['decorators', { decoratorsBeforeExport: false }]]
languageOptions: {
Copy link
Contributor Author

@khawkins khawkins Jun 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the shared ESLint config update that replaces all of those test configs above.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to define the 'files' sections?

const { RuleTester } = require('eslint');
const { RULE_TESTER_CONFIG } = require('./shared');
const lwcGraphAnalyzer = require('../../../lib/index');
const bundleAnalyzer = lwcGraphAnalyzer.processors.bundleAnalyzer;
const ruleTester = new RuleTester(RULE_TESTER_CONFIG);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the ESLint 9 flat config?

Copy link

@haifeng-li-at-salesforce haifeng-li-at-salesforce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look great! The only remaining update is to the Readme.md to reflect support for both ESLint v8 and v9.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants