From a1628dbcf86b53e983c543161dfc7fdfd8f0623c Mon Sep 17 00:00:00 2001 From: Kai Haase Date: Mon, 13 Jul 2026 20:13:13 +0200 Subject: [PATCH] chore: make `tsc --noEmit` clean again -- and fix the one real bug it was hiding `tsc --noEmit -p tsconfig.json` reported 8 errors on develop. Nothing in the check chain runs it (`nest build` uses tsconfig.build.json, which already excludes the two problem files), so the errors only surfaced as red squiggles in the editor -- and were ignored long enough that a genuine bug hid among them. The real bug: vite.config.ts sets `tsCompiler: 'esbuild'`, but vite-plugin-node@8 narrowed SupportedTSCompiler to 'vite' | 'swc'. The config would not work if anyone ran it. Switched to 'swc', which the project already uses elsewhere (`nest start -b swc`). The rest were configuration mismatches, each fixed at the root rather than silenced: - scripts/generate-framework-api.ts shimmed __dirname from `import.meta.url`, which cannot typecheck under `module: commonjs`. The package IS CommonJS (no "type": "module"), so tsx runs the script as CJS and __dirname is native -- the shim was never needed. Removed. (A separate ESM tsconfig for scripts/ is not an option: scripts/init-server.ts imports src/, which would drag the whole `import x = require(...)` tree into ESM mode.) - vite.config.ts and the migrate template are excluded from the root tsconfig, mirroring tsconfig.build.json. vite@8 is ESM-only and cannot be resolved under `moduleResolution: node`, which `module: commonjs` forces; the template deliberately imports '@lenne.tech/nest-server' because it is copied into consumer projects. - performance-caches.spec.ts marked a method `override` against a base class whose compile-time type is `any` (it comes from a dynamic import), which TS rejects outright (TS4113). - unified-field-whitelist.spec.ts redeclared `email` without an initializer (TS2612). Added `= undefined`, matching every Input class in the codebase. Verification: `tsc --noEmit` now reports 0 errors; the generator still runs; full check green. --- scripts/generate-framework-api.ts | 6 +++--- tests/unit/performance-caches.spec.ts | 4 +++- tests/unit/unified-field-whitelist.spec.ts | 6 +++--- tsconfig.json | 12 ++++++++++-- vite.config.ts | 2 +- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/scripts/generate-framework-api.ts b/scripts/generate-framework-api.ts index 7e94b112..74781685 100644 --- a/scripts/generate-framework-api.ts +++ b/scripts/generate-framework-api.ts @@ -8,10 +8,10 @@ import { Project, type InterfaceDeclaration } from 'ts-morph'; import { readFileSync, writeFileSync } from 'node:fs'; -import { resolve, dirname } from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { resolve } from 'node:path'; -const __dirname = dirname(fileURLToPath(import.meta.url)); +// The package is CommonJS (no "type": "module"), so tsx runs this as CJS and __dirname is native. +// An import.meta.url shim would not typecheck against the root tsconfig (module: commonjs). const ROOT = resolve(__dirname, '..'); const OUTPUT = resolve(ROOT, 'FRAMEWORK-API.md'); diff --git a/tests/unit/performance-caches.spec.ts b/tests/unit/performance-caches.spec.ts index e8f2d2e1..164b0d43 100644 --- a/tests/unit/performance-caches.spec.ts +++ b/tests/unit/performance-caches.spec.ts @@ -728,8 +728,10 @@ describe('Native access security gates', () => { }); function createTestService(mockModel: any) { + // CrudService is resolved at runtime via a dynamic import, so its compile-time type is `any`. + // TypeScript rejects `override` against an `any` base (TS4113); the method still overrides at runtime. class TestService extends CrudService { - override get() { return null; } + get() { return null; } callCollection(reason: string) { return this.getNativeCollection(reason); } callConnection(reason: string) { return this.getNativeConnection(reason); } } diff --git a/tests/unit/unified-field-whitelist.spec.ts b/tests/unit/unified-field-whitelist.spec.ts index 57637eb7..835df444 100644 --- a/tests/unit/unified-field-whitelist.spec.ts +++ b/tests/unit/unified-field-whitelist.spec.ts @@ -35,7 +35,7 @@ class ChildInput extends BaseInput { class ChildWithExclude extends BaseInput { @UnifiedField({ exclude: true }) - override email?: string; + override email?: string = undefined; @UnifiedField({ description: 'Phone', isOptional: true }) phone?: string; @@ -43,12 +43,12 @@ class ChildWithExclude extends BaseInput { class GrandchildReEnable extends ChildWithExclude { @UnifiedField({ exclude: false, description: 'Email re-enabled', isOptional: true }) - override email?: string; + override email?: string = undefined; } class ChildImplicitOverrideAttempt extends ChildWithExclude { @UnifiedField({ description: 'Email implicit', isOptional: true }) - override email?: string; + override email?: string = undefined; } class NoUnifiedFieldClass { diff --git a/tsconfig.json b/tsconfig.json index b55ac510..e1c85d08 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,15 @@ "incremental": true, "noImplicitOverride": true, "skipLibCheck": true, - "types": ["vitest/globals", "node"] + "types": [ + "vitest/globals", + "node" + ] }, - "exclude": ["node_modules", "dist"] + "exclude": [ + "node_modules", + "dist", + "vite.config.ts", + "src/core/modules/migrate/templates/migration-project.template.ts" + ] } diff --git a/vite.config.ts b/vite.config.ts index f6c52838..b88dfa4d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -18,7 +18,7 @@ export default defineConfig({ adapter: 'nest', appPath: './src/main.ts', exportName: 'viteNodeApp', - tsCompiler: 'esbuild', + tsCompiler: 'swc', }), ], server: {