From d304770a75c71adcec796ae93f93c8ea3a1bff73 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Tue, 31 Mar 2026 03:34:30 +0000 Subject: [PATCH] chore: enforce .js file extensions in imports via nodenext module resolution (#1232) Switch tsconfig from moduleResolution "bundler" to "nodenext" to enforce explicit .js extensions on all relative imports. This provides IDE-level enforcement (ts(2835) errors) and tsc --noEmit validation. - Change module/moduleResolution to "nodenext" - Remove allowImportingTsExtensions (no longer needed) - Fix all relative imports to use .js extensions - Exclude vite submodule symlinks from type-checking (packages/core/) ![image.png](https://app.graphite.com/user-attachments/assets/196a7707-f88e-40d5-9f33-0d853dc51508.png) --- packages/cli/build.ts | 4 ++-- packages/cli/src/__tests__/index.spec.ts | 2 +- packages/cli/src/__tests__/pack.spec.ts | 2 +- .../cli/src/__tests__/resolve-vite-config.spec.ts | 2 +- packages/cli/src/define-config.ts | 2 +- packages/cli/src/index.ts | 6 +++--- packages/cli/templates/generator/bin/index.ts | 2 +- packages/core/build-support/find-create-require.ts | 2 +- packages/core/build.ts | 12 ++++++------ packages/test/build.ts | 2 +- packages/tools/src/__tests__/utils.spec.ts | 2 +- packages/tools/src/index.ts | 14 +++++++------- packages/tools/src/snap-test.ts | 2 +- packages/tools/src/sync-remote-deps.ts | 2 +- tsconfig.json | 10 +++++++--- 15 files changed, 35 insertions(+), 31 deletions(-) diff --git a/packages/cli/build.ts b/packages/cli/build.ts index 133862b8c1..9e705d9ceb 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -38,7 +38,7 @@ import { ModuleKind, } from 'typescript'; -import { generateLicenseFile } from '../../scripts/generate-license.ts'; +import { generateLicenseFile } from '../../scripts/generate-license.js'; import corePkg from '../core/package.json' with { type: 'json' }; import testPkg from '../test/package.json' with { type: 'json' }; @@ -115,7 +115,7 @@ async function buildNapiBinding() { }); const outputs = await task; - const viteConfig = await import('../../vite.config'); + const viteConfig = await import('../../vite.config.js'); for (const output of outputs) { if (output.kind !== 'node') { const { code, errors } = await format(output.path, await readFile(output.path, 'utf8'), { diff --git a/packages/cli/src/__tests__/index.spec.ts b/packages/cli/src/__tests__/index.spec.ts index 072f85683b..ca5757de8b 100644 --- a/packages/cli/src/__tests__/index.spec.ts +++ b/packages/cli/src/__tests__/index.spec.ts @@ -8,7 +8,7 @@ import { defaultBrowserPort, defineConfig, defineProject, -} from '../index'; +} from '../index.js'; test('should keep vitest exports stable', () => { expect(defineConfig).toBeTypeOf('function'); diff --git a/packages/cli/src/__tests__/pack.spec.ts b/packages/cli/src/__tests__/pack.spec.ts index 4bfb07db86..4ab798865c 100644 --- a/packages/cli/src/__tests__/pack.spec.ts +++ b/packages/cli/src/__tests__/pack.spec.ts @@ -8,7 +8,7 @@ import { resolveUserConfig, buildWithConfigs, enableDebug, -} from '../pack'; +} from '../pack.js'; test('should export all pack APIs from @voidzero-dev/vite-plus-core/pack', () => { expect(defineConfig).toBeTypeOf('function'); diff --git a/packages/cli/src/__tests__/resolve-vite-config.spec.ts b/packages/cli/src/__tests__/resolve-vite-config.spec.ts index a1205f1d05..e73a5187ce 100644 --- a/packages/cli/src/__tests__/resolve-vite-config.spec.ts +++ b/packages/cli/src/__tests__/resolve-vite-config.spec.ts @@ -5,7 +5,7 @@ import path from 'node:path'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; -import { findViteConfigUp } from '../resolve-vite-config'; +import { findViteConfigUp } from '../resolve-vite-config.js'; describe('findViteConfigUp', () => { let tempDir: string; diff --git a/packages/cli/src/define-config.ts b/packages/cli/src/define-config.ts index 73f99888c3..8e1d4d192a 100644 --- a/packages/cli/src/define-config.ts +++ b/packages/cli/src/define-config.ts @@ -3,7 +3,7 @@ import { type ConfigEnv, } from '@voidzero-dev/vite-plus-test/config'; -import type { UserConfig } from './index'; +import type { UserConfig } from './index.js'; type ViteUserConfigFnObject = (env: ConfigEnv) => UserConfig; type ViteUserConfigFnPromise = (env: ConfigEnv) => Promise; diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 290d2a7f7a..d069f57ed4 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -3,9 +3,9 @@ import type { OxfmtConfig } from 'oxfmt'; import type { OxlintConfig } from 'oxlint'; import { defineConfig } from './define-config.js'; -import type { PackUserConfig } from './pack'; -import type { RunConfig } from './run-config'; -import type { StagedConfig } from './staged-config'; +import type { PackUserConfig } from './pack.js'; +import type { RunConfig } from './run-config.js'; +import type { StagedConfig } from './staged-config.js'; declare module '@voidzero-dev/vite-plus-core' { interface UserConfig { diff --git a/packages/cli/templates/generator/bin/index.ts b/packages/cli/templates/generator/bin/index.ts index 0e838241b9..2d02121f59 100755 --- a/packages/cli/templates/generator/bin/index.ts +++ b/packages/cli/templates/generator/bin/index.ts @@ -2,6 +2,6 @@ import { runTemplateCLI } from 'bingo'; -import template from '../src/template.ts'; +import template from '../src/template.js'; process.exitCode = await runTemplateCLI(template); diff --git a/packages/core/build-support/find-create-require.ts b/packages/core/build-support/find-create-require.ts index a21e099417..f5c2c01fac 100644 --- a/packages/core/build-support/find-create-require.ts +++ b/packages/core/build-support/find-create-require.ts @@ -10,7 +10,7 @@ import { type VariableDeclarator, } from 'oxc-parser'; -import { createModuleEntryFileName } from './build-cjs-deps'; +import { createModuleEntryFileName } from './build-cjs-deps.js'; // Node.js built-in modules (without node: prefix) const nodeBuiltins = new Set(builtinModules); diff --git a/packages/core/build.ts b/packages/core/build.ts index 3c32cc9641..96285ef403 100644 --- a/packages/core/build.ts +++ b/packages/core/build.ts @@ -15,18 +15,18 @@ import { build, type BuildOptions } from 'rolldown'; import { dts } from 'rolldown-plugin-dts'; import { glob } from 'tinyglobby'; -import { generateLicenseFile } from '../../scripts/generate-license.ts'; -import { buildCjsDeps } from './build-support/build-cjs-deps'; -import { replaceThirdPartyCjsRequires } from './build-support/find-create-require'; -import { RewriteImportsPlugin } from './build-support/rewrite-imports'; +import { generateLicenseFile } from '../../scripts/generate-license.js'; +import { buildCjsDeps } from './build-support/build-cjs-deps.js'; +import { replaceThirdPartyCjsRequires } from './build-support/find-create-require.js'; +import { RewriteImportsPlugin } from './build-support/rewrite-imports.js'; import { createRolldownRewriteRules, createViteRewriteRules, rewriteModuleSpecifiers, type ReplacementRule, -} from './build-support/rewrite-module-specifiers'; +} from './build-support/rewrite-module-specifiers.js'; import pkgJson from './package.json' with { type: 'json' }; -import viteRolldownConfig from './vite-rolldown.config'; +import viteRolldownConfig from './vite-rolldown.config.js'; const projectDir = join(fileURLToPath(import.meta.url), '..'); diff --git a/packages/test/build.ts b/packages/test/build.ts index a0cbceace2..3defd727f0 100644 --- a/packages/test/build.ts +++ b/packages/test/build.ts @@ -56,7 +56,7 @@ import { format } from 'oxfmt'; import { build } from 'rolldown'; import { dts } from 'rolldown-plugin-dts'; -import { generateLicenseFile } from '../../scripts/generate-license.ts'; +import { generateLicenseFile } from '../../scripts/generate-license.js'; import pkg from './package.json' with { type: 'json' }; const projectDir = dirname(fileURLToPath(import.meta.url)); diff --git a/packages/tools/src/__tests__/utils.spec.ts b/packages/tools/src/__tests__/utils.spec.ts index b9b448ef31..b318ddc8fc 100644 --- a/packages/tools/src/__tests__/utils.spec.ts +++ b/packages/tools/src/__tests__/utils.spec.ts @@ -5,7 +5,7 @@ import path from 'node:path'; import { describe, expect, test } from '@voidzero-dev/vite-plus-test'; -import { isPassThroughEnv, replaceUnstableOutput } from '../utils'; +import { isPassThroughEnv, replaceUnstableOutput } from '../utils.js'; describe('replaceUnstableOutput()', () => { test('strip ANSI escape sequences', () => { diff --git a/packages/tools/src/index.ts b/packages/tools/src/index.ts index 5a6fa9b58e..564ac5ee9f 100644 --- a/packages/tools/src/index.ts +++ b/packages/tools/src/index.ts @@ -2,31 +2,31 @@ const subcommand = process.argv[2]; switch (subcommand) { case 'snap-test': - const { snapTest } = await import('./snap-test.ts'); + const { snapTest } = await import('./snap-test.js'); await snapTest(); break; case 'replace-file-content': - const { replaceFileContent } = await import('./replace-file-content.ts'); + const { replaceFileContent } = await import('./replace-file-content.js'); replaceFileContent(); break; case 'sync-remote': - const { syncRemote } = await import('./sync-remote-deps.ts'); + const { syncRemote } = await import('./sync-remote-deps.js'); await syncRemote(); break; case 'json-sort': - const { jsonSort } = await import('./json-sort.ts'); + const { jsonSort } = await import('./json-sort.js'); jsonSort(); break; case 'merge-peer-deps': - const { mergePeerDeps } = await import('./merge-peer-deps.ts'); + const { mergePeerDeps } = await import('./merge-peer-deps.js'); mergePeerDeps(); break; case 'install-global-cli': - const { installGlobalCli } = await import('./install-global-cli.ts'); + const { installGlobalCli } = await import('./install-global-cli.js'); installGlobalCli(); break; case 'brand-vite': - const { brandVite } = await import('./brand-vite.ts'); + const { brandVite } = await import('./brand-vite.js'); brandVite(); break; default: diff --git a/packages/tools/src/snap-test.ts b/packages/tools/src/snap-test.ts index e72cf3276f..e86b1d6f37 100755 --- a/packages/tools/src/snap-test.ts +++ b/packages/tools/src/snap-test.ts @@ -10,7 +10,7 @@ import { debuglog, parseArgs } from 'node:util'; import { npath } from '@yarnpkg/fslib'; import { execute } from '@yarnpkg/shell'; -import { isPassThroughEnv, replaceUnstableOutput } from './utils'; +import { isPassThroughEnv, replaceUnstableOutput } from './utils.js'; const debug = debuglog('vite-plus/snap-test'); diff --git a/packages/tools/src/sync-remote-deps.ts b/packages/tools/src/sync-remote-deps.ts index edf8e6bb29..1c1f3cb022 100755 --- a/packages/tools/src/sync-remote-deps.ts +++ b/packages/tools/src/sync-remote-deps.ts @@ -773,7 +773,7 @@ export async function syncRemote() { log('✓ package.json exports updated successfully!'); // Apply Vite+ branding patches to vite source - const { brandVite } = await import('./brand-vite.ts'); + const { brandVite } = await import('./brand-vite.js'); brandVite(rootDir); log('✓ Done!'); diff --git a/tsconfig.json b/tsconfig.json index 61c9698a71..ec021ce1e0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,10 @@ { "compilerOptions": { - "allowImportingTsExtensions": true, "declaration": true, "esModuleInterop": true, - "module": "preserve", + "module": "nodenext", "moduleDetection": "force", - "moduleResolution": "bundler", + "moduleResolution": "nodenext", "noEmit": true, "noUnusedLocals": true, "resolveJsonModule": true, @@ -19,6 +18,11 @@ "ecosystem-ci", "packages/cli/snap-tests", "packages/cli/snap-tests-global", + // Symlinks to vite submodule files that don't use .js extensions; + // excluding build.ts too because it imports vite-rolldown.config.ts + "packages/core/build.ts", + "packages/core/rollupLicensePlugin.ts", + "packages/core/vite-rolldown.config.ts", "vite", "rolldown", "target"