[heft-lint-plugin] Lint files selected by ESLint flat config - #6006
Draft
Ian Clanton-Thuon (iclanton) wants to merge 7 commits into
Draft
Ian Clanton-Thuon (iclanton) wants to merge 7 commits into
Ian Clanton-Thuon (iclanton) wants to merge 7 commits into
Conversation
Ian Clanton-Thuon (iclanton)
force-pushed
the
fix/heft-lint-flat-config-files
branch
2 times, most recently
from
September 5, 2026 00:05
bca4d92 to
54eb97a
Compare
Ian Clanton-Thuon (iclanton)
force-pushed
the
fix/heft-lint-flat-config-files
branch
from
September 13, 2026 04:48
0cb1dd4 to
2f28a5b
Compare
Use ESLint's native flat-config enumeration to find files outside the TypeScript program and lint them through the existing cache and reporting pipeline with a single ESLint instance. When a type-aware rule is applied to a file that is not part of the TypeScript program, emit actionable guidance to either exclude the file or lint it with a configuration that does not enable type-aware rules. Also fix the lint issues this surfaces in @rushstack/playwright-browser-tunnel's Playwright config and test fixture files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Now that @rushstack/eslint-config is published with the `flat/without-type-information` helper, group the rig's own type-aware rules (localCommonConfig) into a `localTypeAwareRules` set and expose a `without-type-information` helper from `decoupled-local-node-rig` and `local-node-rig` that disables type-aware parsing plus both the base profile's and the rig's type-aware rules. Use it in @rushstack/playwright-browser-tunnel to lint the Playwright config and test files, which are excluded from the TypeScript program. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ian Clanton-Thuon (iclanton)
force-pushed
the
fix/heft-lint-flat-config-files
branch
from
September 16, 2026 04:40
73af4b7 to
843e29b
Compare
Simplify the additional-file sort to a default lexicographic sort, note that the enumerator lints relative to `buildFolderPath` (its `cwd`), convert the type-info error helper to a loose function that takes the TypeScript file set and build folder as parameters, and empty the playwright-browser-tunnel change comment. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address review feedback: - Remove the `additionalFiles` option and the extra-file generic from LinterBase; the base now exposes a neutral `getExtraSourceFilesToLintAsync` hook (default empty) that Eslint overrides to enumerate the files selected by the ESLint configuration. The base only deals with a generic `ISourceFileToLint` shape. - Thread the (project-folder-resolved) TypeScript file set into the enumeration as a parameter instead of reading it from a field, resolving the paths at the LintPlugin call site. - Make `ISourceFileToLint.version` optional (omit it for enumerated files) so the base computes the version from file contents, instead of passing an empty string. - Drop the redundant TypeScript-output-folder ignore patterns; emitted JavaScript is already excluded by the default-JavaScript-extension filter (and emit folders such as `lib-esm` cannot be derived from the compiler options anyway). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of blanket-ignoring ESLint's default JavaScript extensions (which also prevented authored `.js` files from being linted), discover the TypeScript output folders and ignore only those when enumerating additional files. - heft-typescript-plugin: expose `emitFolderPaths` on `IChangedFilesHookOptions` (the `outDir`/`declarationDir` plus any `additionalModuleKindsToEmit` folders, such as `lib-esm`, which are not part of the compiler options). - heft-lint-plugin: aggregate those folders from the accessor and ignore them in the additional-file enumerator, and remove the default-JavaScript-extension filter so that authored `.js` files selected by the ESLint configuration are linted. Update the eslint-9-test snapshot, which now lints `eslint.config.js` while still excluding the emitted `lib-commonjs`/`lib-esm` output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… by extension
Now that the additional-file enumeration lints authored JavaScript (not just
TypeScript sources), ESLint's `lintFiles('.')` would walk into generated output,
which ESLint's flat config does not exclude (it does not respect `.gitignore`).
Rather than blanket-ignoring `.js`/`.mjs`/`.cjs`, ignore build output explicitly:
- @rushstack/eslint-config: globally ignore `lib`, `lib-*`, `dist`, `temp`, and
`coverage` (anchored to the project root).
- decoupled-local-node-rig: repeat the same global ignores so projects consuming
the currently-published @rushstack/eslint-config via this rig also get them
(to be removed once that dependency is bumped).
- Add project-level ignores for non-standard generated/fixture folders:
package-extractor `test-output`, rush-redis-cobuild sandbox `sandbox`, and the
rush vscode extension `webview` bundle output.
- Revert the `emitFolderPaths` TypeScript-plugin accessor addition (and its
consumption), since the explicit config ignores cover the build output.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The additional-file feature resolved the TypeScript program file names with `path.resolve(buildFolderPath, ...)`, which produces backslash paths on Windows. LinterBase then compared those against `SourceFile.fileName` (which TypeScript always reports with forward slashes), so on Windows every program file missed the lookup and was skipped -- producing empty lint results for the program files (observed as an eslint-9-test SARIF snapshot mismatch on Windows). Normalize all file paths to forward slashes before comparing them: the resolved TypeScript program file names, the paths ESLint reports for enumerated additional files, and the project-folder prefix used to compute the ignore patterns. This is a no-op on POSIX and corrects the comparison on Windows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The Heft lint plugin currently sends ESLint only the files from the TypeScript program. As a result, files selected exclusively by the ESLint flat config — for example configuration files, tests excluded from
tsconfig.json, or custom extensions such as Markdown or JSON — are never linted.This change uses ESLint's native flat-config enumeration to discover those files and lints them through the plugin's existing cache, fix, diagnostic, and SARIF pipeline with a single ESLint instance.
Details
For ESLint 9, the plugin creates a discovery-only ESLint instance and calls
lintFiles()with rules disabled to enumerate the files the flat config selects. TypeScript program roots and primary TypeScript emit directories are excluded from discovery. Extensions introduced by the flat config (Markdown, JSON, JSX, custom languages,.tsfiles outside the program, etc.) remain discoverable.A single ESLint instance lints both the program files and the discovered additional files. The injected TypeScript
Programis scoped (via afiles-limited override) to the program's own files, so additional files fall through to the flat config's own parser instead of failing to resolve against the program. Additional files are run through the existing per-file content/config cache and result reporting, and SARIF metadata is collected from that instance.When a type-aware rule would apply to a file that is not part of the TypeScript program (typescript-eslint cannot produce type information for it), the plugin reports an actionable error telling the user to either exclude the file from ESLint or lint it with a configuration that does not enable type-aware rules.
ESLint 8 and TSLint behavior is unchanged.
Adopting the pattern in this repo
@rushstack/eslint-config(published via #6026) exposes aflat/without-type-informationhelper that disables type-aware parsing and rules for a given set of files. This PR wires that helper through the repo's node rigs (decoupled-local-node-rigandlocal-node-rig, including the rig's ownlocalTypeAwareRulesgroup), and uses it in@rushstack/playwright-browser-tunnelto lint itsplaywright.config.tsandtests/**files — which are excluded from the TypeScript program — with only the non-type-aware rules. This also surfaces and fixes several previously-unlinted issues in those files (including a broken import in the test fixture).How it was tested
heft buildinheft-plugins/heft-lint-plugin(TypeScript + self-lint + API Extractor): passedheft test --cleaninbuild-tests/eslint-9-test: 1 passed, including a non-TypeScript custom-extension SARIF resultheft build --cleaninapps/playwright-browser-tunnel: passes, linting the Playwright config and test files with only the non-type-aware rules via the rig helperrush rebuildfor a decoupled-rig consumer (@rushstack/eslint-plugin): no lint regressions from the rig's type-aware rule splitrush change --verify