Skip to content

[heft-lint-plugin] Add support for ESLint 9 #5219

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

Draft
wants to merge 54 commits into
base: main
Choose a base branch
from

Conversation

D4N14L
Copy link
Member

@D4N14L D4N14L commented May 3, 2025

Summary

  • Adds support for ESLint 9 to the @rushstack/heft-lint-plugin.
  • Add new flat configs into @rushstack/eslint-config
  • Creates new eslint-9-test project to test the plugin against ESLint 9
  • [TODO] Updates projects that use the repo version of the ESLint plugin and configs to consume the new flat configs
  • [TODO] Adds ESLint 9 support to eslint-bulk-suppressions

NOTE: Once merged, a publish and bump of decoupled dependencies will be required to update the remaining projects. The new configs should be moved into the decoupled rig, which currently is the source for the configs for the local-node-rig configs.

How it was tested

This PR.

Impacted documentation

Any ESLint-related docs will need to be updated to indicate support.

//
// const { CONSTANT1, CONSTANT2 } = someNamespace.constants;
//
// Thus for now "property" is more like a variable than a class member.
Copy link
Contributor

Choose a reason for hiding this comment

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

Per ECMAScript, the above examples are, in fact, values of type "Property" so this comment is weird.


// Genuine properties
{
selectors: ['parameterProperty', 'accessor'],
Copy link
Contributor

Choose a reason for hiding this comment

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

If parameterProperty the deprecated constructor(public foo: string) nonsense or destructured parameters, e.g. function ({ foo }: { foo: string })?

import type * as TTypescript from 'typescript';
import type * as TEslint from 'eslint';
import path from 'node:path';
import { createHash, type Hash } from 'crypto';
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
import { createHash, type Hash } from 'crypto';
import { createHash, type Hash } from 'node:crypto';

import type * as TTypescript from 'typescript';
import type * as TEslint from 'eslint';
import path from 'node:path';
import { createHash, type Hash } from 'crypto';
import { performance } from 'perf_hooks';
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
import { performance } from 'perf_hooks';
import { performance } from 'node:perf_hooks';

let programs: TTypescript.Program[] | undefined;
if (this.languageOptions?.parserOptions?.programs) {
programs = this.languageOptions.parserOptions.programs;
delete this.languageOptions.parserOptions.programs;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we set this to undefined instead? The delete operator is a guaranteed deopt of property accesses from the object, and JSON serialization ignores properties with a value of undefined.

const eslintBaseConfiguration: any = await this._linter.calculateConfigForFile(
this._linterConfigFilePath
protected override async getCacheVersionAsync(): Promise<string> {
return `${this._eslintPackageVersion.version}_${process.version}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the version of Node.js really relevant to the eslint rule evaluation?

Comment on lines +250 to +252
// The eslint configuration object contains a toJSON() method that returns a serializable version of the
// configuration. However, we are manually injecting the TypeScript program into the parserOptions, which
// is not serializable. Patch the function to remove the program before returning the serializable version.
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if we instead inject it using Object.defineProperty and don't make it enumerable?

Comment on lines 285 to 286
this._fixesPossible =
this._fixesPossible ||
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
this._fixesPossible =
this._fixesPossible ||
this._fixesPossible ||=

// if (message.source) {
// physicalLocation.region ??= {};
// physicalLocation.region.snippet = {
// text: message.source
Copy link
Contributor

Choose a reason for hiding this comment

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

Is message.source no longer a field?

const { defineConfig } = require('eslint/config');
const friendlyLocalsMixin = require('local-eslint-config/flat/mixins/friendly-locals');

module.exports = defineConfig([...friendlyLocalsMixin]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a purpose to duplicating the array or could we just pass it directly?

Copy link
Member Author

Choose a reason for hiding this comment

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

Duplicating to allow for easy update in the future. Can be left as an array directly if we wanted to.

@iclanton iclanton moved this from Needs triage to In Progress in Bug Triage May 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

2 participants