Skip to content

Commit 20ea8b3

Browse files
authored
[eslint-config] Update TypeScript/ESLint Utilities and Refactor Linting (#4883)
* Bump the version of eslint utils consumed by Rushstack * Update lint rules for back compat * Update eslint tests for compatibility * Bump cyclic versions and rush update * Lint fix * Refactor and cleanup of linters * Bump peer dependency of eslint config package to consume 8.57.0 or greater * Rush change * Fix install issues * Fix install * Fix readme * Drop Node 16 support * Add eslint 8.57.0 to legacy * Rush update * PR feedback * Update user-danade-BumpEslintUtils_2024-08-13-00-25.json * Empty change to trigger PR check analysis
1 parent 27837b6 commit 20ea8b3

File tree

58 files changed

+1890
-541
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1890
-541
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ jobs:
1111
strategy:
1212
matrix:
1313
include:
14-
- NodeVersion: 16.20.x
15-
NodeVersionDisplayName: 16
16-
OS: ubuntu-latest
1714
- NodeVersion: 18.18.x
1815
NodeVersionDisplayName: 18
1916
OS: ubuntu-latest

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ These GitHub repositories provide supplementary resources for Rush Stack:
153153
| [/build-tests/eslint-7-test](./build-tests/eslint-7-test/) | This project contains a build test to validate ESLint 7 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin) |
154154
| [/build-tests/eslint-8-test](./build-tests/eslint-8-test/) | This project contains a build test to validate ESLint 8 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin) |
155155
| [/build-tests/eslint-bulk-suppressions-test](./build-tests/eslint-bulk-suppressions-test/) | Sample code to test eslint bulk suppressions |
156+
| [/build-tests/eslint-bulk-suppressions-test-legacy](./build-tests/eslint-bulk-suppressions-test-legacy/) | Sample code to test eslint bulk suppressions for versions of eslint < 8.57.0 |
156157
| [/build-tests/hashed-folder-copy-plugin-webpack5-test](./build-tests/hashed-folder-copy-plugin-webpack5-test/) | Building this project exercises @rushstack/hashed-folder-copy-plugin with Webpack 5. NOTE - THIS TEST IS CURRENTLY EXPECTED TO BE BROKEN |
157158
| [/build-tests/heft-copy-files-test](./build-tests/heft-copy-files-test/) | Building this project tests copying files with Heft |
158159
| [/build-tests/heft-example-plugin-01](./build-tests/heft-example-plugin-01/) | This is an example heft plugin that exposes hooks for other plugins |

build-tests/eslint-7-11-test/.eslintrc.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ require('local-node-rig/profiles/default/includes/eslint/patch/custom-config-pac
88

99
module.exports = {
1010
extends: [
11-
'local-node-rig/profiles/default/includes/eslint/profile/node-trusted-tool',
12-
'local-node-rig/profiles/default/includes/eslint/mixins/friendly-locals'
11+
'@rushstack/eslint-config/profile/node-trusted-tool',
12+
'@rushstack/eslint-config/mixins/friendly-locals'
1313
],
1414
parserOptions: { tsconfigRootDir: __dirname },
1515

build-tests/eslint-7-11-test/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010
"_phase:build": "heft run --only build -- --clean"
1111
},
1212
"devDependencies": {
13+
"@rushstack/eslint-config": "3.7.0",
1314
"@rushstack/heft": "workspace:*",
14-
"local-node-rig": "workspace:*",
1515
"@types/node": "18.17.15",
1616
"@typescript-eslint/parser": "~6.19.0",
1717
"eslint": "7.11.0",
18+
"local-node-rig": "workspace:*",
1819
"typescript": "~5.4.2"
1920
}
2021
}

build-tests/eslint-7-7-test/.eslintrc.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ require('local-node-rig/profiles/default/includes/eslint/patch/custom-config-pac
88

99
module.exports = {
1010
extends: [
11-
'local-node-rig/profiles/default/includes/eslint/profile/node-trusted-tool',
12-
'local-node-rig/profiles/default/includes/eslint/mixins/friendly-locals'
11+
'@rushstack/eslint-config/profile/node-trusted-tool',
12+
'@rushstack/eslint-config/mixins/friendly-locals'
1313
],
1414
parserOptions: { tsconfigRootDir: __dirname },
1515

build-tests/eslint-7-7-test/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010
"_phase:build": "heft run --only build -- --clean"
1111
},
1212
"devDependencies": {
13+
"@rushstack/eslint-config": "3.7.0",
1314
"@rushstack/heft": "workspace:*",
14-
"local-node-rig": "workspace:*",
1515
"@types/node": "18.17.15",
1616
"@typescript-eslint/parser": "~6.19.0",
1717
"eslint": "7.7.0",
18+
"local-node-rig": "workspace:*",
1819
"typescript": "~5.4.2"
1920
}
2021
}

build-tests/eslint-7-test/.eslintrc.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ require('local-node-rig/profiles/default/includes/eslint/patch/custom-config-pac
88

99
module.exports = {
1010
extends: [
11-
'local-node-rig/profiles/default/includes/eslint/profile/node-trusted-tool',
12-
'local-node-rig/profiles/default/includes/eslint/mixins/friendly-locals'
11+
'@rushstack/eslint-config/profile/node-trusted-tool',
12+
'@rushstack/eslint-config/mixins/friendly-locals'
1313
],
1414
parserOptions: { tsconfigRootDir: __dirname },
1515

build-tests/eslint-7-test/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010
"_phase:build": "heft run --only build -- --clean"
1111
},
1212
"devDependencies": {
13+
"@rushstack/eslint-config": "3.7.0",
1314
"@rushstack/heft": "workspace:*",
14-
"local-node-rig": "workspace:*",
1515
"@types/node": "18.17.15",
1616
"@typescript-eslint/parser": "~6.19.0",
1717
"eslint": "~7.30.0",
18+
"local-node-rig": "workspace:*",
1819
"typescript": "~5.4.2"
1920
}
2021
}

build-tests/eslint-8-test/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@rushstack/heft": "workspace:*",
1414
"local-node-rig": "workspace:*",
1515
"@types/node": "18.17.15",
16-
"@typescript-eslint/parser": "~6.19.0",
16+
"@typescript-eslint/parser": "~8.1.0",
1717
"eslint": "~8.57.0",
1818
"typescript": "~5.4.2"
1919
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// This project is a duplicate of "eslint-bulk-suppressions-test" intended to test eslint
2+
// against the older version of the TypeScript parser. Any modifications made to this project
3+
// should be reflected in "eslint-bulk-suppressions-test" as well.
4+
5+
const { FileSystem, Executable, Text, Import } = require('@rushstack/node-core-library');
6+
const path = require('path');
7+
const {
8+
ESLINT_PACKAGE_NAME_ENV_VAR_NAME
9+
} = require('@rushstack/eslint-patch/lib/eslint-bulk-suppressions/constants');
10+
11+
const eslintBulkStartPath = Import.resolveModule({
12+
modulePath: '@rushstack/eslint-bulk/lib/start',
13+
baseFolderPath: __dirname
14+
});
15+
16+
function tryLoadSuppressions(suppressionsJsonPath) {
17+
try {
18+
return Text.convertToLf(FileSystem.readFile(suppressionsJsonPath)).trim();
19+
} catch (e) {
20+
if (FileSystem.isNotExistError(e)) {
21+
return '';
22+
} else {
23+
throw e;
24+
}
25+
}
26+
}
27+
28+
const RUN_FOLDER_PATHS = ['client', 'server'];
29+
const ESLINT_PACKAGE_NAMES = ['eslint', 'eslint-8.23', 'eslint-oldest'];
30+
31+
const updateFilePaths = new Set();
32+
33+
for (const runFolderPath of RUN_FOLDER_PATHS) {
34+
const folderPath = `${__dirname}/${runFolderPath}`;
35+
const suppressionsJsonPath = `${folderPath}/.eslint-bulk-suppressions.json`;
36+
37+
const folderItems = FileSystem.readFolderItems(folderPath);
38+
for (const folderItem of folderItems) {
39+
if (folderItem.isFile() && folderItem.name.match(/^\.eslint\-bulk\-suppressions\-[\d.]+\.json$/)) {
40+
const fullPath = `${folderPath}/${folderItem.name}`;
41+
updateFilePaths.add(fullPath);
42+
}
43+
}
44+
45+
for (const eslintPackageName of ESLINT_PACKAGE_NAMES) {
46+
const { version: eslintVersion } = require(`${eslintPackageName}/package.json`);
47+
48+
const startLoggingMessage = `-- Running eslint-bulk-suppressions for eslint@${eslintVersion} in ${runFolderPath} --`;
49+
console.log(startLoggingMessage);
50+
const referenceSuppressionsJsonPath = `${folderPath}/.eslint-bulk-suppressions-${eslintVersion}.json`;
51+
const existingSuppressions = tryLoadSuppressions(referenceSuppressionsJsonPath);
52+
53+
// The eslint-bulk-suppressions patch expects to find "eslint" in the shell PATH. To ensure deterministic
54+
// test behavior, we need to designate an explicit "node_modules/.bin" folder.
55+
//
56+
// Use the ".bin" folder from @rushstack/eslint-patch as a workaround for this PNPM bug:
57+
// https://github.com/pnpm/pnpm/issues/7833
58+
const dependencyBinFolder = path.join(
59+
__dirname,
60+
'node_modules',
61+
'@rushstack',
62+
'eslint-patch',
63+
'node_modules',
64+
'.bin'
65+
);
66+
const shellPathWithEslint = `${dependencyBinFolder}${path.delimiter}${process.env['PATH']}`;
67+
68+
const executableResult = Executable.spawnSync(
69+
process.argv0,
70+
[eslintBulkStartPath, 'suppress', '--all', 'src'],
71+
{
72+
currentWorkingDirectory: folderPath,
73+
environment: {
74+
...process.env,
75+
PATH: shellPathWithEslint,
76+
[ESLINT_PACKAGE_NAME_ENV_VAR_NAME]: eslintPackageName
77+
}
78+
}
79+
);
80+
81+
if (executableResult.status !== 0) {
82+
console.error('The eslint-bulk-suppressions command failed.');
83+
console.error('STDOUT:');
84+
console.error(executableResult.stdout.toString());
85+
console.error('STDERR:');
86+
console.error(executableResult.stderr.toString());
87+
process.exit(1);
88+
}
89+
90+
const newSuppressions = tryLoadSuppressions(suppressionsJsonPath);
91+
if (newSuppressions === existingSuppressions) {
92+
updateFilePaths.delete(referenceSuppressionsJsonPath);
93+
} else {
94+
updateFilePaths.add(referenceSuppressionsJsonPath);
95+
FileSystem.writeFile(referenceSuppressionsJsonPath, newSuppressions);
96+
}
97+
98+
FileSystem.deleteFile(suppressionsJsonPath);
99+
}
100+
}
101+
102+
if (updateFilePaths.size > 0) {
103+
for (const updateFilePath of updateFilePaths) {
104+
console.log(`The suppressions file "${updateFilePath}" was updated and must be committed to git.`);
105+
}
106+
107+
process.exit(1);
108+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
{
2+
"suppressions": [
3+
{
4+
"file": "src/index.ts",
5+
"scopeId": ".",
6+
"rule": "prefer-const"
7+
},
8+
{
9+
"file": "src/index.ts",
10+
"scopeId": ".ExampleClass",
11+
"rule": "@typescript-eslint/ban-types"
12+
},
13+
{
14+
"file": "src/index.ts",
15+
"scopeId": ".ExampleClass",
16+
"rule": "@typescript-eslint/explicit-member-accessibility"
17+
},
18+
{
19+
"file": "src/index.ts",
20+
"scopeId": ".ExampleClass.exampleMethod",
21+
"rule": "@typescript-eslint/explicit-function-return-type"
22+
},
23+
{
24+
"file": "src/index.ts",
25+
"scopeId": ".ExampleClass.exampleMethod",
26+
"rule": "@typescript-eslint/explicit-member-accessibility"
27+
},
28+
{
29+
"file": "src/index.ts",
30+
"scopeId": ".ExampleClass.exampleMethod",
31+
"rule": "@typescript-eslint/typedef"
32+
},
33+
{
34+
"file": "src/index.ts",
35+
"scopeId": ".ExampleClass.exampleMethod",
36+
"rule": "no-var"
37+
},
38+
{
39+
"file": "src/index.ts",
40+
"scopeId": ".exampleAnonymousClass",
41+
"rule": "@typescript-eslint/explicit-member-accessibility"
42+
},
43+
{
44+
"file": "src/index.ts",
45+
"scopeId": ".exampleAnonymousClass",
46+
"rule": "@typescript-eslint/typedef"
47+
},
48+
{
49+
"file": "src/index.ts",
50+
"scopeId": ".exampleAnonymousClass",
51+
"rule": "no-useless-concat"
52+
},
53+
{
54+
"file": "src/index.ts",
55+
"scopeId": ".exampleAnonymousClass.constructor",
56+
"rule": "@typescript-eslint/explicit-member-accessibility"
57+
},
58+
{
59+
"file": "src/index.ts",
60+
"scopeId": ".exampleAnonymousClass.exampleSetGet",
61+
"rule": "@typescript-eslint/ban-types"
62+
},
63+
{
64+
"file": "src/index.ts",
65+
"scopeId": ".exampleAnonymousClass.exampleSetGet",
66+
"rule": "@typescript-eslint/explicit-function-return-type"
67+
},
68+
{
69+
"file": "src/index.ts",
70+
"scopeId": ".exampleAnonymousClass.exampleSetGet",
71+
"rule": "@typescript-eslint/explicit-member-accessibility"
72+
},
73+
{
74+
"file": "src/index.ts",
75+
"scopeId": ".exampleArrowFunction",
76+
"rule": "@typescript-eslint/explicit-function-return-type"
77+
},
78+
{
79+
"file": "src/index.ts",
80+
"scopeId": ".exampleArrowFunction",
81+
"rule": "@typescript-eslint/typedef"
82+
},
83+
{
84+
"file": "src/index.ts",
85+
"scopeId": ".exampleArrowFunction",
86+
"rule": "dot-notation"
87+
},
88+
{
89+
"file": "src/index.ts",
90+
"scopeId": ".exampleArrowFunction",
91+
"rule": "no-empty"
92+
},
93+
{
94+
"file": "src/index.ts",
95+
"scopeId": ".exampleArrowFunction",
96+
"rule": "no-unused-expressions"
97+
},
98+
{
99+
"file": "src/index.ts",
100+
"scopeId": ".exampleFunction",
101+
"rule": "@typescript-eslint/ban-types"
102+
},
103+
{
104+
"file": "src/index.ts",
105+
"scopeId": ".exampleFunction",
106+
"rule": "@typescript-eslint/explicit-function-return-type"
107+
},
108+
{
109+
"file": "src/index.ts",
110+
"scopeId": ".exampleFunction",
111+
"rule": "no-empty-pattern"
112+
},
113+
{
114+
"file": "src/index.ts",
115+
"scopeId": ".exampleFunction",
116+
"rule": "no-extra-boolean-cast"
117+
},
118+
{
119+
"file": "src/index.ts",
120+
"scopeId": ".exampleObject",
121+
"rule": "@typescript-eslint/typedef"
122+
},
123+
{
124+
"file": "src/index.ts",
125+
"scopeId": ".x",
126+
"rule": "@typescript-eslint/explicit-function-return-type"
127+
},
128+
{
129+
"file": "src/index.ts",
130+
"scopeId": ".y",
131+
"rule": "@typescript-eslint/explicit-function-return-type"
132+
},
133+
{
134+
"file": "src/index.ts",
135+
"scopeId": ".z",
136+
"rule": "@typescript-eslint/explicit-function-return-type"
137+
}
138+
]
139+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2+
// See LICENSE in the project root for license information.
3+
4+
// This is a workaround for https://github.com/eslint/eslint/issues/3458
5+
require('local-node-rig/profiles/default/includes/eslint/patch/modern-module-resolution');
6+
// This is a workaround for https://github.com/microsoft/rushstack/issues/3021
7+
require('local-node-rig/profiles/default/includes/eslint/patch/custom-config-package-names');
8+
require('local-node-rig/profiles/default/includes/eslint/patch/eslint-bulk-suppressions');
9+
10+
module.exports = {
11+
extends: [
12+
'@rushstack/eslint-config/profile/node-trusted-tool',
13+
'@rushstack/eslint-config/mixins/friendly-locals'
14+
],
15+
ignorePatterns: ['.eslintrc.js'],
16+
17+
overrides: [
18+
/**
19+
* Override the parser from @rushstack/eslint-config. Since the config is coming
20+
* from the workspace instead of the external NPM package, the versions of ESLint
21+
* and TypeScript that the config consumes will be resolved from the devDependencies
22+
* of the config instead of from the eslint-8-test package. Overriding the parser
23+
* ensures that the these dependencies come from the eslint-8-test package. See:
24+
* https://github.com/microsoft/rushstack/issues/3021
25+
*/
26+
{
27+
files: ['**/*.ts', '**/*.tsx'],
28+
parser: '@typescript-eslint/parser',
29+
parserOptions: { project: '../tsconfig.json', tsconfigRootDir: __dirname }
30+
}
31+
]
32+
};

0 commit comments

Comments
 (0)