Skip to content

Commit 1794fb1

Browse files
feat: use typescript-eslint's EXPERIMENTAL_useProjectService (#1466)
## PR Checklist - [x] Addresses an existing open issue: fixes #1211 - [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/create-typescript-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22) - [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/create-typescript-app/blob/main/.github/CONTRIBUTING.md) were taken ## Overview Unifies `*.js` and `*.ts` linting with the experimental project service option. Hooray! In doing so, I found it easier to lint the `script/*.js` files by switching them to `.ts`. This also removes the nice `--no-dts` optimization for `pnpm build`. Linting files like `bin/index.js` now has types, and `lib/*` needs those `.d.ts `generated.
1 parent 1ebd471 commit 1794fb1

17 files changed

+98
-137
lines changed

.eslintrc.cjs

+25-34
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,20 @@ module.exports = {
3232
{
3333
extends: [
3434
"plugin:jsdoc/recommended-typescript-error",
35-
"plugin:@typescript-eslint/strict",
36-
"plugin:@typescript-eslint/stylistic",
35+
"plugin:@typescript-eslint/strict-type-checked",
36+
"plugin:@typescript-eslint/stylistic-type-checked",
3737
],
38-
files: ["**/*.ts"],
38+
files: ["**/*.js", "**/*.ts"],
3939
parser: "@typescript-eslint/parser",
40+
parserOptions: {
41+
EXPERIMENTAL_useProjectService: {
42+
allowDefaultProjectForFiles: ["./*.*s"],
43+
defaultProject: "./tsconfig.json",
44+
},
45+
},
4046
rules: {
4147
// These off-by-default rules work well for this repo and we like them on.
48+
"deprecation/deprecation": "error",
4249
"jsdoc/informative-docs": "error",
4350
"logical-assignment-operators": [
4451
"error",
@@ -47,37 +54,6 @@ module.exports = {
4754
],
4855
"operator-assignment": "error",
4956

50-
// These on-by-default rules don't work well for this repo and we like them off.
51-
"jsdoc/require-jsdoc": "off",
52-
"jsdoc/require-param": "off",
53-
"jsdoc/require-property": "off",
54-
"jsdoc/require-returns": "off",
55-
},
56-
},
57-
{
58-
files: "**/*.md/*.ts",
59-
rules: {
60-
"n/no-missing-import": [
61-
"error",
62-
{ allowModules: ["create-typescript-app"] },
63-
],
64-
},
65-
},
66-
{
67-
excludedFiles: ["**/*.md/*.ts"],
68-
extends: [
69-
"plugin:@typescript-eslint/strict-type-checked",
70-
"plugin:@typescript-eslint/stylistic-type-checked",
71-
],
72-
files: ["**/*.ts"],
73-
parser: "@typescript-eslint/parser",
74-
parserOptions: {
75-
project: "./tsconfig.eslint.json",
76-
},
77-
rules: {
78-
// These off-by-default rules work well for this repo and we like them on.
79-
"deprecation/deprecation": "error",
80-
8157
// These more-strict-by-default rules don't work well for this repo and we like them less strict.
8258
"@typescript-eslint/no-unnecessary-condition": [
8359
"error",
@@ -93,6 +69,21 @@ module.exports = {
9369
"error",
9470
{ allowBoolean: true, allowNullish: true, allowNumber: true },
9571
],
72+
73+
// These on-by-default rules don't work well for this repo and we like them off.
74+
"jsdoc/require-jsdoc": "off",
75+
"jsdoc/require-param": "off",
76+
"jsdoc/require-property": "off",
77+
"jsdoc/require-returns": "off",
78+
},
79+
},
80+
{
81+
files: "**/*.md/*.ts",
82+
rules: {
83+
"n/no-missing-import": [
84+
"error",
85+
{ allowModules: ["create-typescript-app"] },
86+
],
9687
},
9788
},
9889
{

.github/DEVELOPMENT.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ Note that the files need to be built with `pnpm run build` beforehand.
146146
pnpm run test:create
147147
```
148148

149-
That end-to-end test executes `script/create-test-e2e.js`, which:
149+
That end-to-end test executes `script/create-test-e2e.ts`, which:
150150

151151
1. Runs the creation script to create a new `test-repository` child directory and repository, capturing code coverage
152152
2. Asserts that commands such as `build` and `lint` each pass
@@ -175,7 +175,7 @@ Note that files need to be built with `pnpm run build` beforehand.
175175
pnpm run test:initialize
176176
```
177177

178-
That end-to-end test executes `script/initialize-test-e2e.js`, which:
178+
That end-to-end test executes `script/initialize-test-e2e.ts`, which:
179179

180180
1. Runs the initialization script using `--skip-github-api` and other skip flags
181181
2. Checks that the local repository's files were changed correctly (e.g. removed initialization-only files)
@@ -221,7 +221,7 @@ You can run the end-to-end test for migrating locally on the command-line:
221221
pnpm run test:migrate
222222
```
223223

224-
That end-to-end test executes `script/migrate-test-e2e.js`, which:
224+
That end-to-end test executes `script/migrate-test-e2e.ts`, which:
225225

226226
1. Runs the migration script using `--skip-github-api` and other skip flags, capturing code coverage
227227
2. Checks that only a small list of allowed files were changed
@@ -234,7 +234,7 @@ See `.github/workflows/test-migrate.yml`.
234234
235235
##### Migration Snapshot Failures
236236

237-
The migration test uses the [Vitest file snapshot](https://vitest.dev/guide/snapshot#file-snapshots) in `script/__snapshots__/migrate-test-e2e.js.snap` to store expected differences to this repository after running the migration script.
237+
The migration test uses the [Vitest file snapshot](https://vitest.dev/guide/snapshot#file-snapshots) in `script/__snapshots__/migrate-test-e2e.ts.snap` to store expected differences to this repository after running the migration script.
238238
The end-to-end migration test will fail any changes that don't keep the same differences in that snapshot.
239239

240240
You can update the snapshot file by:
@@ -245,7 +245,7 @@ You can update the snapshot file by:
245245

246246
At this point there will be some files changed:
247247

248-
- `script/__snapshots__/migrate-test-e2e.js.snap` will have updates if any files mismatched templates
248+
- `script/__snapshots__/migrate-test-e2e.ts.snap` will have updates if any files mismatched templates
249249
- The actual updated files on disk will be there too
250250

251251
If the snapshot file changes are what you expected, then you can commit them.

.github/workflows/lint.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ jobs:
44
steps:
55
- uses: actions/checkout@v4
66
- uses: ./.github/actions/prepare
7-
- run: pnpm build --no-dts
7+
- run: pnpm build
88
- run: pnpm lint
99

1010
name: Lint

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
"lint:spelling": "cspell \"**\" \".github/**/*\"",
3030
"prepare": "husky",
3131
"test": "vitest",
32-
"test:create": "node script/create-test-e2e.js",
33-
"test:initialize": "node script/initialize-test-e2e.js",
32+
"test:create": "npx tsx script/create-test-e2e.ts",
33+
"test:initialize": "npx tsx script/initialize-test-e2e.ts",
3434
"test:migrate": "vitest run -r script/",
3535
"tsc": "tsc"
3636
},

script/__snapshots__/migrate-test-e2e.js.snap script/__snapshots__/migrate-test-e2e.ts.snap

+8-7
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,9 @@ exports[`expected file changes > .eslintrc.cjs 1`] = `
2727
{
2828
extends: ["plugin:markdown/recommended-legacy"],
2929
@@ ... @@ module.exports = {
30-
rules: {
31-
// These off-by-default rules work well for this repo and we like them on.
32-
"deprecation/deprecation": "error",
33-
-
30+
],
31+
"operator-assignment": "error",
32+
3433
- // These more-strict-by-default rules don't work well for this repo and we like them less strict.
3534
- "@typescript-eslint/no-unnecessary-condition": [
3635
- "error",
@@ -46,9 +45,10 @@ exports[`expected file changes > .eslintrc.cjs 1`] = `
4645
- "error",
4746
- { allowBoolean: true, allowNullish: true, allowNumber: true },
4847
- ],
49-
},
50-
},
51-
{"
48+
-
49+
// These on-by-default rules don't work well for this repo and we like them off.
50+
"jsdoc/require-jsdoc": "off",
51+
"jsdoc/require-param": "off","
5252
`;
5353
5454
exports[`expected file changes > .github/workflows/test.yml 1`] = `
@@ -129,6 +129,7 @@ exports[`expected file changes > cspell.json 1`] = `
129129
"execa",
130130
"infile",
131131
+ "joshuakgoldberg",
132+
+ "jsdoc",
132133
"knip",
133134
+ "markdownlint",
134135
"markdownlintignore",
File renamed without changes.

script/initialize-test-e2e.js script/initialize-test-e2e.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ await $({
1919

2020
const newPackageJson = JSON.parse(
2121
(await fs.readFile("./package.json")).toString(),
22-
);
22+
) as Record<string, string>;
2323
console.log("New package JSON:", newPackageJson);
2424

2525
assert.equal(newPackageJson.description, description);
@@ -45,7 +45,9 @@ for (const search of [`/JoshuaKGoldberg/`, "create-typescript-app"]) {
4545
try {
4646
await $`pnpm run lint:knip`;
4747
} catch (error) {
48-
throw new Error("Error running lint:knip:", { cause: error });
48+
throw new Error(
49+
`Error running lint:knip: ${(error as Error).stack ?? (error as string)}`,
50+
);
4951
}
5052

5153
// Now that initialize has passed normal steps, we reset everything,

script/migrate-test-e2e.js script/migrate-test-e2e.ts

+19-15
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const filesExpectedToBeChanged = [
1818

1919
const filesThatMightBeChanged = new Set([
2020
...filesExpectedToBeChanged,
21-
"script/__snapshots__/migrate-test-e2e.js.snap",
21+
"script/__snapshots__/migrate-test-e2e.ts.snap",
2222
]);
2323

2424
const {
@@ -41,7 +41,7 @@ await rimraf("coverage*");
4141
const originalReadme = (await fs.readFile("README.md")).toString();
4242

4343
const originalSnapshots = (
44-
await fs.readFile("script/__snapshots__/migrate-test-e2e.js.snap")
44+
await fs.readFile("script/__snapshots__/migrate-test-e2e.ts.snap")
4545
).toString();
4646

4747
await $({
@@ -71,27 +71,31 @@ await fs.writeFile(
7171
updatedReadme.slice(updatedReadme.indexOf("<!-- markdownlint-restore -->")),
7272
]
7373
.join("")
74-
.replaceAll(
74+
.replace(
7575
/All Contributors: \d+/g,
76-
originalReadme.match(/All Contributors: \d+/)[0],
76+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
77+
originalReadme.match(/All Contributors: \d+/)![0],
7778
)
78-
.replaceAll(
79+
.replace(
7980
/all_contributors-\d+/g,
80-
originalReadme.match(/all_contributors-\d+/)[0],
81+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
82+
originalReadme.match(/all_contributors-\d+/)![0],
8183
),
8284
);
8385

8486
// ...and even to the snapshot file, so diffs don't mind it.
8587
await fs.writeFile(
86-
"script/__snapshots__/migrate-test-e2e.js.snap",
88+
"script/__snapshots__/migrate-test-e2e.ts.snap",
8789
originalSnapshots
88-
.replaceAll(
90+
.replace(
8991
/All Contributors: \d+/g,
90-
originalReadme.match(/All Contributors: \d+/)[0],
92+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
93+
originalReadme.match(/All Contributors: \d+/)![0],
9194
)
92-
.replaceAll(
95+
.replace(
9396
/all_contributors-\d+/g,
94-
originalReadme.match(/all_contributors-\d+/)[0],
97+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
98+
originalReadme.match(/all_contributors-\d+/)![0],
9599
),
96100
);
97101

@@ -102,7 +106,7 @@ describe("expected file changes", () => {
102106
.split("\n")
103107
.slice(2)
104108
.join("\n")
105-
.replaceAll(/@@ -\d+,\d+ \+\d+,\d+ @@/g, "@@ ... @@");
109+
.replace(/@@ -\d+,\d+ \+\d+,\d+ @@/g, "@@ ... @@");
106110

107111
assert(
108112
stdout,
@@ -130,13 +134,13 @@ test("unexpected file changes", async () => {
130134

131135
const unstagedModifiedFiles = gitStatus
132136
.slice(indexOfUnstagedFilesMessage)
133-
.match(/modified: {3}(\S+)\n/g)
134-
.map((match) => match.split(/\s+/g)[1])
137+
.match(/modified: {3}\S+\n/g)
138+
?.map((match) => match.split(/\s+/)[1])
135139
.filter((filePath) => !filesThatMightBeChanged.has(filePath));
136140

137141
console.log("Unexpected modified files are:", unstagedModifiedFiles);
138142

139-
if (unstagedModifiedFiles.length) {
143+
if (unstagedModifiedFiles?.length) {
140144
const gitDiffCommand = `git diff HEAD -- ${unstagedModifiedFiles.join(
141145
" ",
142146
)}`;

script/vitest.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import { defineConfig } from "vitest/config";
22

3-
export default defineConfig({ test: { include: ["./migrate-test-e2e.js"] } });
3+
export default defineConfig({ test: { include: ["./migrate-test-e2e.ts"] } });

src/shared/createCleanupCommands.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe("createCleanupCommands", () => {
2020

2121
expect(actual).toEqual([
2222
"pnpm dedupe",
23-
"pnpm build --no-dts",
23+
"pnpm build",
2424
"pnpm lint --fix",
2525
"pnpm format --write",
2626
]);

src/shared/createCleanupCommands.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export function createCleanupCommands({
88
// There's no need to dedupe when initializing from the fixed template
99
...(mode === "initialize" ? [] : ["pnpm dedupe"]),
1010
// n/no-missing-import rightfully reports on a missing the bin .js file
11-
...(bin ? ["pnpm build --no-dts"] : []),
11+
...(bin ? ["pnpm build"] : []),
1212
"pnpm lint --fix",
1313
"pnpm format --write",
1414
];

src/steps/writing/creation/createESLintRC.test.ts

+19-29
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,18 @@ describe("createESLintRC", () => {
6161
ignorePatterns: ["!.*", "lib", "node_modules", "pnpm-lock.yaml"],
6262
overrides: [
6363
{
64-
extends: ["plugin:@typescript-eslint/recommended"],
65-
files: ["**/*.ts"],
64+
extends: ["plugin:@typescript-eslint/recommended-type-checked"],
65+
files: ["**/*.js", "**/*.ts"],
6666
parser: "@typescript-eslint/parser",
67+
parserOptions: {
68+
EXPERIMENTAL_useProjectService: {
69+
allowDefaultProjectForFiles: ["./*.*s"],
70+
defaultProject: "./tsconfig.json",
71+
},
72+
},
6773
rules: {
6874
// These off-by-default rules work well for this repo and we like them on.
75+
"deprecation/deprecation": "error",
6976
"logical-assignment-operators": [
7077
"error",
7178
"always",
@@ -80,14 +87,6 @@ describe("createESLintRC", () => {
8087
"n/no-missing-import": ["error", { allowModules: ["test-repository"] }],
8188
},
8289
},
83-
{
84-
extends: ["plugin:@typescript-eslint/recommended-type-checked"],
85-
files: ["**/*.ts"],
86-
parser: "@typescript-eslint/parser",
87-
parserOptions: {
88-
project: "./tsconfig.eslint.json",
89-
},
90-
},
9190
],
9291
parser: "@typescript-eslint/parser",
9392
plugins: ["@typescript-eslint"],
@@ -135,13 +134,20 @@ describe("createESLintRC", () => {
135134
{
136135
extends: [
137136
"plugin:jsdoc/recommended-typescript-error",
138-
"plugin:@typescript-eslint/strict",
139-
"plugin:@typescript-eslint/stylistic",
137+
"plugin:@typescript-eslint/strict-type-checked",
138+
"plugin:@typescript-eslint/stylistic-type-checked",
140139
],
141-
files: ["**/*.ts"],
140+
files: ["**/*.js", "**/*.ts"],
142141
parser: "@typescript-eslint/parser",
142+
parserOptions: {
143+
EXPERIMENTAL_useProjectService: {
144+
allowDefaultProjectForFiles: ["./*.*s"],
145+
defaultProject: "./tsconfig.json",
146+
},
147+
},
143148
rules: {
144149
// These off-by-default rules work well for this repo and we like them on.
150+
"deprecation/deprecation": "error",
145151
"jsdoc/informative-docs": "error",
146152
"logical-assignment-operators": [
147153
"error",
@@ -163,22 +169,6 @@ describe("createESLintRC", () => {
163169
"n/no-missing-import": ["error", { allowModules: ["test-repository"] }],
164170
},
165171
},
166-
{
167-
excludedFiles: ["**/*.md/*.ts"],
168-
extends: [
169-
"plugin:@typescript-eslint/strict-type-checked",
170-
"plugin:@typescript-eslint/stylistic-type-checked",
171-
],
172-
files: ["**/*.ts"],
173-
parser: "@typescript-eslint/parser",
174-
parserOptions: {
175-
project: "./tsconfig.eslint.json",
176-
},
177-
rules: {
178-
// These off-by-default rules work well for this repo and we like them on.
179-
"deprecation/deprecation": "error",
180-
},
181-
},
182172
{
183173
excludedFiles: ["package.json"],
184174
extends: ["plugin:jsonc/recommended-with-json"],

0 commit comments

Comments
 (0)