Skip to content

[heft-lint-plugin] Lint files selected by ESLint flat config - #6006

Draft
Ian Clanton-Thuon (iclanton) wants to merge 3 commits into
microsoft:mainfrom
iclanton:fix/heft-lint-flat-config-files
Draft

[heft-lint-plugin] Lint files selected by ESLint flat config#6006
Ian Clanton-Thuon (iclanton) wants to merge 3 commits into
microsoft:mainfrom
iclanton:fix/heft-lint-flat-config-files

Conversation

@iclanton

@iclanton Ian Clanton-Thuon (iclanton) commented Sep 2, 2026

Copy link
Copy Markdown
Member

Summary

The Heft lint plugin currently sends ESLint only files from the TypeScript program. As a result, files selected exclusively by ESLint flat config, such as Markdown or JSON, are never linted.

This change uses ESLint's native flat-config enumeration to find configured extensions outside the TypeScript program and runs them through the plugin's existing cache, fix, diagnostic, and SARIF pipeline.

Details

For ESLint 9, the plugin creates a discovery-only ESLint instance and calls lintFiles() with rules disabled. TypeScript program roots and primary TypeScript emit directories are excluded from discovery. ESLint's built-in JavaScript extensions (.js, .mjs, and .cjs) are excluded from the additional-file pass because ESLint enumerates them even when flat config did not introduce them; this also prevents build outputs produced by other Heft tasks from becoming new lint inputs. Extensions introduced by flat config, such as Markdown, JSON, JSX, or custom language extensions, remain discoverable.

Discovered files are linted with a separate ESLint instance so the TypeScript Program parser override is not applied to non-program files. They are then included in the existing per-file content/config cache and result reporting. SARIF metadata is collected from the ESLint instance that created each result. In builds with multiple TypeScript programs, additional files are included only once.

ESLint 8 and TSLint behavior is unchanged. The extra rule-free enumeration has a performance cost, but delegates flat-config matching and ignore semantics to ESLint instead of reimplementing them.

How it was tested

All tests were run against commit 0cb1dd40b0.

  • heft test --clean in heft-plugins/heft-lint-plugin: 8 passed
  • heft test --clean in build-tests/eslint-9-test: 1 passed, including a non-TypeScript custom-extension SARIF result
  • heft build --clean in libraries/rush-lib: passed; generated dist/**/*.js files were not linted
  • heft build --clean in build-tests/localization-plugin-test-02: passed; generated dist-dev/**/*.js files were not linted
  • heft test --clean --test-name-pattern "returns all files are ignored" in apps/heft: passed after the unrelated full-load timeout
  • rush change --verify
  • git diff --check

Use ESLint's native flat-config enumeration to find files outside the TypeScript program, then lint them through the existing cache and reporting pipeline. Exclude TypeScript emit folders and cover the behavior with the ESLint 9 SARIF fixture.
The lint plugin imports the TypeScript configuration loader at runtime. Publish that dependency and update both affected subspace lockfiles so isolated build-test installations can load the plugin.
Comment thread heft-plugins/heft-lint-plugin/package.json Outdated
Keep the TypeScript plugin as an accessor-only development dependency and exclude ESLint's built-in JavaScript extensions from the additional-file pass. Partition SARIF metadata by ESLint instance and classify the new lint behavior as a minor release.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Needs triage

Development

Successfully merging this pull request may close these issues.

1 participant