perf(typecheck): check shared tests only in the Node project (#29381)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-15 20:52:31 -07:00
committed by GitHub
co-authored by OrcaWin
parent 79d131d521
commit a3dda8af42
4 changed files with 74 additions and 0 deletions
+6
View File
@@ -60,6 +60,12 @@ pnpm test
pnpm build
```
For repeated local typechecks, use `pnpm tc` (or `pnpm tc:node`, `pnpm tc:web`,
and `pnpm tc:cli`). These keep TypeScript's incremental cache. CI uses
`pnpm typecheck:ci` with Bun for full checks. Shared tests are checked by the
Node project; shared production code is checked in all three environments.
Run `pnpm tc:node` after editing shared tests, or `pnpm tc` to check everything.
Add high-quality tests for behavior changes and bug fixes. Prefer tests that would actually catch a regression, not shallow coverage that only exercises the happy path.
If your change affects UI or interaction behavior, verify it on the platforms it could impact.
@@ -0,0 +1,54 @@
import { globSync } from 'node:fs'
import { resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import ts from 'typescript-api'
import { describe, expect, it } from 'vitest'
import { TYPECHECK_PROJECTS } from './run-typecheck-projects-in-parallel.mjs'
const root = fileURLToPath(new URL('../..', import.meta.url))
const isTest = (file) => /\.(?:test|spec)\.(?:[cm]?ts|tsx)$/.test(file)
const projects = TYPECHECK_PROJECTS.filter((project) => project.config.startsWith('config/')).map(
({ config }) => {
const parsed = ts.getParsedCommandLineOfConfigFile(resolve(root, config), undefined, {
...ts.sys,
onUnRecoverableConfigFileDiagnostic(diagnostic) {
throw new Error(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'))
}
})
if (!parsed || parsed.errors.length) {
throw new Error(`Cannot parse ${config}`)
}
return { config, files: new Set(parsed.fileNames.map((file) => resolve(file))) }
}
)
function typedFiles(directory) {
return globSync(`${directory}/**/*`, { cwd: root }).filter((file) =>
/\.(?:[cm]?ts|tsx)$/.test(file)
)
}
describe('typecheck project coverage', () => {
it('checks every shared test exactly once, in the scheduled Node project', () => {
const tests = typedFiles('src/shared').filter(isTest)
expect(tests.length).toBeGreaterThan(0)
const violations = tests.flatMap((file) => {
const owners = projects.filter((project) => project.files.has(resolve(root, file)))
return owners.length === 1 && owners[0].config === 'config/tsconfig.node.json'
? []
: [{ file, owners: owners.map((owner) => owner.config) }]
})
expect(violations).toEqual([])
})
it('keeps all shared production files in every root typecheck project', () => {
const files = typedFiles('src/shared').filter((file) => !isTest(file))
expect(files.length).toBeGreaterThan(0)
const missing = projects.flatMap((project) =>
files
.filter((file) => !project.files.has(resolve(root, file)))
.map((file) => ({ config: project.config, file }))
)
expect(missing).toEqual([])
})
})
+7
View File
@@ -1,5 +1,12 @@
{
"extends": "./tsconfig.cli.json",
// Shared tests are checked by tsconfig.node.json; shared production code stays in every project.
"exclude": [
"../src/shared/**/*.test.ts",
"../src/shared/**/*.test.tsx",
"../src/shared/**/*.spec.ts",
"../src/shared/**/*.spec.tsx"
],
"compilerOptions": {
"module": "Node20",
"moduleResolution": "node16"
+7
View File
@@ -1,5 +1,12 @@
{
"extends": "./tsconfig.web.json",
// Shared tests are checked by tsconfig.node.json; shared production code stays in every project.
"exclude": [
"../src/shared/**/*.test.ts",
"../src/shared/**/*.test.tsx",
"../src/shared/**/*.spec.ts",
"../src/shared/**/*.spec.tsx"
],
"include": [
"../src/renderer/src/env.d.ts",
"../src/renderer/src/**/*",