Skip to content

Commit 73af4b7

Browse files
iclantonCopilot
andcommitted
Address PR review feedback
- heft-lint-plugin: resolve TypeScript program root file names against the project folder (not the process cwd), and derive the project-relative ESLint patterns by slicing the project-folder prefix instead of using path.relative. - @rushstack/eslint-config: drop the derived nonTypeAwareRules export/loop and keep only the explicit typeAwareRules group; simplify the without-type-information helper to an explicit disabled-rules list. - Defer the decoupled-local-node-rig / node-rig wiring to a follow-up PR (to be done after @rushstack/eslint-config is published and the dependency is bumped); fix @rushstack/playwright-browser-tunnel inline in its own eslint.config.js for now. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 2f28a5b commit 73af4b7

10 files changed

Lines changed: 88 additions & 123 deletions

File tree

apps/playwright-browser-tunnel/eslint.config.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33

44
const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool');
55
const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals');
6-
const {
7-
withoutTypeInformation
8-
} = require('local-node-rig/profiles/default/includes/eslint/flat/without-type-information');
96

107
module.exports = [
118
...nodeTrustedToolProfile,
@@ -18,7 +15,23 @@ module.exports = [
1815
}
1916
}
2017
},
21-
// The Playwright config and test files are not part of the project's TypeScript program (they are excluded
22-
// from tsconfig.json), so lint them with only the non-type-aware rules.
23-
...withoutTypeInformation({ files: ['playwright.config.ts', 'tests/**/*.ts'] })
18+
{
19+
// The Playwright config and test files are not part of the project's TypeScript program (they are excluded
20+
// from tsconfig.json), so disable type-aware parsing and the profile's type-aware rules and lint them with
21+
// only the non-type-aware rules.
22+
// TODO: Replace this with the `@rushstack/eslint-config` `without-type-information` helper once that package
23+
// is published and consumed by the node rigs.
24+
files: ['playwright.config.ts', 'tests/**/*.ts'],
25+
languageOptions: {
26+
parserOptions: {
27+
project: false,
28+
projectService: false
29+
}
30+
},
31+
rules: {
32+
'@typescript-eslint/naming-convention': 'off',
33+
'@typescript-eslint/no-floating-promises': 'off',
34+
'@typescript-eslint/no-for-in-array': 'off'
35+
}
36+
}
2437
];

common/changes/@rushstack/eslint-config/heft-lint-flat-config-files_2026-09-12-04-00-00.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"changes": [
33
{
44
"packageName": "@rushstack/eslint-config",
5-
"comment": "Group the type-aware rules separately (exposed as `typeAwareRules`/`nonTypeAwareRules`) and add a `flat/without-type-information` helper for linting files that are not part of the TypeScript program with only the non-type-aware rules.",
5+
"comment": "Group the profile's type-aware rules into an exported `typeAwareRules` set and add a `flat/without-type-information` helper for linting files that are not part of the TypeScript program with only the non-type-aware rules.",
66
"type": "minor"
77
}
88
]

eslint/eslint-config/flat/profile/_common.js

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -785,18 +785,4 @@ const commonConfig = [
785785
}
786786
];
787787

788-
// Derive the non-type-aware rule group from the authored TypeScript source-file config so that it stays in sync
789-
// with the rules above. Everything that is not part of typeAwareRules can be applied to files that lack type
790-
// information.
791-
const typeScriptSourceFileConfig = commonConfig.find(
792-
(configObject) =>
793-
Array.isArray(configObject.files) && configObject.files.includes('**/*.ts') && configObject.rules
794-
);
795-
const nonTypeAwareRules = {};
796-
for (const [ruleName, ruleValue] of Object.entries(typeScriptSourceFileConfig.rules)) {
797-
if (!(ruleName in typeAwareRules)) {
798-
nonTypeAwareRules[ruleName] = ruleValue;
799-
}
800-
}
801-
802-
module.exports = { commonNamingConventionSelectors, commonConfig, typeAwareRules, nonTypeAwareRules };
788+
module.exports = { commonNamingConventionSelectors, commonConfig, typeAwareRules };

eslint/eslint-config/flat/without-type-information.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
22
// See LICENSE in the project root for license information.
33

4-
const { typeAwareRules } = require('./profile/_common');
4+
// The profile's type-aware rules, turned off. Keep this in sync with the typeAwareRules group in
5+
// ./profile/_common.js.
6+
const disabledTypeAwareRules = {
7+
'@typescript-eslint/naming-convention': 'off',
8+
'@typescript-eslint/no-floating-promises': 'off',
9+
'@typescript-eslint/no-for-in-array': 'off'
10+
};
511

612
// Returns ESLint flat-config objects that lint the specified files WITHOUT type information: type-aware parsing
713
// is disabled and the profile's type-aware rules are turned off, leaving only the non-type-aware rules in effect.
@@ -11,9 +17,6 @@ const { typeAwareRules } = require('./profile/_common');
1117
// this, typescript-eslint reports a fatal parsing error because it cannot associate those files with the project,
1218
// and any type-aware rule would be unable to run.
1319
//
14-
// If your ESLint configuration layers additional type-aware rules on top of this profile, pass their rule names
15-
// via "additionalTypeAwareRuleNames" so that they are disabled as well.
16-
//
1720
// IMPORTANT: These config objects must be included in your ESLint configuration AFTER the profile, so that they
1821
// override the profile's type-aware parser options and rules for the specified files.
1922
//
@@ -25,12 +28,7 @@ const { typeAwareRules } = require('./profile/_common');
2528
// ...nodeTrustedToolProfile,
2629
// ...withoutTypeInformation({ files: ['tests/**/*.ts', 'playwright.config.ts'] })
2730
// ];
28-
function withoutTypeInformation({ files, additionalTypeAwareRuleNames = [] }) {
29-
const disabledTypeAwareRules = {};
30-
for (const ruleName of [...Object.keys(typeAwareRules), ...additionalTypeAwareRuleNames]) {
31-
disabledTypeAwareRules[ruleName] = 'off';
32-
}
33-
31+
function withoutTypeInformation({ files }) {
3432
return [
3533
{
3634
files,
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
/flat/mixins
22
/flat/patch
3-
/flat/profile
4-
/flat/without-type-information.js
3+
/flat/profile

heft-plugins/heft-lint-plugin/src/Eslint.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ export class Eslint extends LinterBase<
140140
this.#sarifLogPath = sarifLogPath;
141141

142142
this.#typeScriptFilenames = new Set(
143-
tsProgram.getRootFileNames().map((filePath: string) => path.resolve(filePath))
143+
tsProgram.getRootFileNames().map((filePath: string) => path.resolve(buildFolderPath, filePath))
144144
);
145145
// ESLint configuration paths are relative to the project folder. Compute the project-relative paths of the
146146
// files in the TypeScript program so that the injected program can be scoped to just those files, and so
@@ -149,7 +149,9 @@ export class Eslint extends LinterBase<
149149
const typeScriptFilePatterns: string[] = [];
150150
for (const filePath of this.#typeScriptFilenames) {
151151
if (Path.isUnder(filePath, buildFolderPath)) {
152-
typeScriptFilePatterns.push(Path.convertToSlashes(path.relative(buildFolderPath, filePath)));
152+
// filePath is already an absolute path under buildFolderPath, so strip the prefix (plus the separator)
153+
// instead of recomputing the relative path.
154+
typeScriptFilePatterns.push(Path.convertToSlashes(filePath.slice(buildFolderPath.length + 1)));
153155
}
154156
}
155157

heft-plugins/heft-lint-plugin/src/LintPlugin.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -307,25 +307,29 @@ export default class LintPlugin implements IHeftTaskPlugin<ILintPluginOptions> {
307307
heftConfiguration: HeftConfiguration,
308308
tsPrograms: IExtendedProgram[]
309309
): string[] {
310+
const { buildFolderPath } = heftConfiguration;
310311
const outputFolderPaths: Set<string> = new Set();
311312
for (const tsProgram of tsPrograms) {
312313
const { outDir, declarationDir } = tsProgram.getCompilerOptions();
313314
if (outDir) {
314-
outputFolderPaths.add(outDir);
315+
outputFolderPaths.add(path.resolve(buildFolderPath, outDir));
315316
}
316317

317318
if (declarationDir) {
318-
outputFolderPaths.add(declarationDir);
319+
outputFolderPaths.add(path.resolve(buildFolderPath, declarationDir));
319320
}
320321
}
321322

322-
const { buildFolderPath } = heftConfiguration;
323-
return Array.from(outputFolderPaths, (outputFolderPath: string) => {
324-
const relativePath: string = Path.convertToSlashes(path.relative(buildFolderPath, outputFolderPath));
325-
return `${relativePath}/**`;
326-
}).filter(
327-
(relativePath: string) =>
328-
relativePath !== '/**' && relativePath !== '../**' && !relativePath.startsWith('../')
329-
);
323+
const ignorePatterns: string[] = [];
324+
for (const outputFolderPath of outputFolderPaths) {
325+
// Only output folders under the project folder can be expressed as ESLint ignore patterns.
326+
if (Path.isUnder(outputFolderPath, buildFolderPath)) {
327+
ignorePatterns.push(
328+
`${Path.convertToSlashes(outputFolderPath.slice(buildFolderPath.length + 1))}/**`
329+
);
330+
}
331+
}
332+
333+
return ignorePatterns;
330334
}
331335
}

rigs/decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/_common.js

Lines changed: 40 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -11,51 +11,6 @@ const headersEslintPlugin = require('eslint-plugin-headers');
1111

1212
const nodeImportResolverPath = require.resolve('eslint-import-resolver-node');
1313

14-
// These localCommonConfig rules require type information (i.e. the TypeScript program). They are grouped
15-
// separately so that TypeScript files which are NOT part of the project's TypeScript program can be linted with
16-
// only the non-type-aware rules. See the "without-type-information" helper.
17-
const localTypeAwareRules = {
18-
// Rationale: Use of `void` to explicitly indicate that a floating promise is expected
19-
// and allowed.
20-
'@typescript-eslint/no-floating-promises': [
21-
'error',
22-
{
23-
ignoreVoid: true,
24-
checkThenables: true
25-
}
26-
],
27-
28-
// Docs: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/naming-convention.md
29-
'@typescript-eslint/naming-convention': [
30-
'warn',
31-
...expandNamingConventionSelectors([
32-
...commonNamingConventionSelectors,
33-
{
34-
selectors: ['method'],
35-
modifiers: ['async'],
36-
enforceLeadingUnderscoreWhenPrivate: true,
37-
38-
format: null,
39-
custom: {
40-
regex: '^_?[a-zA-Z]\\w*Async$',
41-
match: true
42-
},
43-
leadingUnderscore: 'allow',
44-
45-
filter: {
46-
regex: [
47-
// Specifically allow ts-command-line's "onExecute" function.
48-
'^onExecute$'
49-
]
50-
.map((x) => `(${x})`)
51-
.join('|'),
52-
match: false
53-
}
54-
}
55-
])
56-
]
57-
};
58-
5914
module.exports = {
6015
localCommonConfig: [
6116
{
@@ -87,9 +42,15 @@ module.exports = {
8742
// understand where the dependency is coming from.
8843
'@rushstack/normalized-imports': 'warn',
8944

90-
// Type-aware rules (require the TypeScript program) are grouped in localTypeAwareRules so that files
91-
// outside the TypeScript program can be linted with only the non-type-aware rules.
92-
...localTypeAwareRules,
45+
// Rationale: Use of `void` to explicitly indicate that a floating promise is expected
46+
// and allowed.
47+
'@typescript-eslint/no-floating-promises': [
48+
'error',
49+
{
50+
ignoreVoid: true,
51+
checkThenables: true
52+
}
53+
],
9354

9455
// Rationale: Redeclaring a variable likely indicates a mistake in the code.
9556
'no-redeclare': 'off',
@@ -149,6 +110,36 @@ module.exports = {
149110
}
150111
],
151112

113+
// Docs: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/naming-convention.md
114+
'@typescript-eslint/naming-convention': [
115+
'warn',
116+
...expandNamingConventionSelectors([
117+
...commonNamingConventionSelectors,
118+
{
119+
selectors: ['method'],
120+
modifiers: ['async'],
121+
enforceLeadingUnderscoreWhenPrivate: true,
122+
123+
format: null,
124+
custom: {
125+
regex: '^_?[a-zA-Z]\\w*Async$',
126+
match: true
127+
},
128+
leadingUnderscore: 'allow',
129+
130+
filter: {
131+
regex: [
132+
// Specifically allow ts-command-line's "onExecute" function.
133+
'^onExecute$'
134+
]
135+
.map((x) => `(${x})`)
136+
.join('|'),
137+
match: false
138+
}
139+
}
140+
])
141+
],
142+
152143
// Require `node:` protocol for imports of Node.js built-in modules
153144
'import/enforce-node-protocol-usage': ['warn', 'always'],
154145

@@ -224,6 +215,5 @@ module.exports = {
224215
'import/no-duplicates': 'off'
225216
}
226217
}
227-
],
228-
localTypeAwareRules
218+
]
229219
};

rigs/decoupled-local-node-rig/profiles/default/includes/eslint/flat/without-type-information.js

Lines changed: 0 additions & 23 deletions
This file was deleted.

rigs/local-node-rig/profiles/default/includes/eslint/flat/without-type-information.js

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)