diff --git a/packages/vite/LICENSE.md b/packages/vite/LICENSE.md index 293bbb5d1be0cc..221dc1af9b1491 100644 --- a/packages/vite/LICENSE.md +++ b/packages/vite/LICENSE.md @@ -1846,60 +1846,6 @@ Repository: https://github.com/micromatch/to-regex-range --------------------------------------- -## tsconfck -License: MIT -By: dominikg -Repository: https://github.com/dominikg/tsconfck - -> MIT License -> -> Copyright (c) 2021-present dominikg and [contributors](https://github.com/dominikg/tsconfck/graphs/contributors) -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in all -> copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -> SOFTWARE. -> -> -- Licenses for 3rd-party code included in tsconfck -- -> -> # strip-bom and strip-json-comments -> MIT License -> -> Copyright (c) Sindre Sorhus (https://sindresorhus.com) -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in all -> copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -> SOFTWARE. - ---------------------------------------- - ## unpipe License: MIT By: Douglas Christopher Wilson diff --git a/packages/vite/package.json b/packages/vite/package.json index c48899e5fb29cc..a75deb5ca03e8c 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -137,7 +137,6 @@ "sirv": "^3.0.2", "strip-literal": "^3.1.0", "terser": "^5.46.0", - "tsconfck": "^3.1.6", "ufo": "^1.6.3", "ws": "^8.19.0" }, diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index 1a38799e752735..b35e209fa6ad91 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -16,7 +16,7 @@ export { minifySync, type MinifyOptions, type MinifyResult, -} from 'rolldown/experimental' +} from 'rolldown/utils' /** @deprecated - use `parse` instead */ export const parseAst: typeof _parseAst = _parseAst diff --git a/packages/vite/src/node/plugins/esbuild.ts b/packages/vite/src/node/plugins/esbuild.ts index 19ba2467f869ef..3d72a4cd685b15 100644 --- a/packages/vite/src/node/plugins/esbuild.ts +++ b/packages/vite/src/node/plugins/esbuild.ts @@ -2,8 +2,8 @@ import path from 'node:path' import colors from 'picocolors' import type { RawSourceMap } from '@jridgewell/remapping' import type { InternalModuleFormat, SourceMap } from 'rolldown' -import type { TSConfckParseResult } from 'tsconfck' -import { TSConfckCache, TSConfckParseError, parse } from 'tsconfck' +import { resolveTsconfig } from 'rolldown/experimental' +import { TsconfigCache } from 'rolldown/utils' import type { EsbuildLoader, EsbuildMessage, @@ -145,13 +145,19 @@ export async function transformWithEsbuild( ] const compilerOptionsForFile: TSCompilerOptions = {} if (loader === 'ts' || loader === 'tsx') { - try { - const { tsconfig: loadedTsconfig, tsconfigFile } = - await loadTsconfigJsonForFile(filename, config) + const result = resolveTsconfig( + filename, + getTSConfigResolutionCache(config), + ) + if (result) { + const { tsconfig: loadedTsconfig, tsconfigFilePaths } = result // tsconfig could be out of root, make sure it is watched on dev - if (watcher && tsconfigFile && config) { - ensureWatchedFile(watcher, tsconfigFile, config.root) + if (watcher && config) { + for (const tsconfigFile of tsconfigFilePaths) { + ensureWatchedFile(watcher, tsconfigFile, config.root) + } } + const loadedCompilerOptions = loadedTsconfig.compilerOptions ?? {} for (const field of meaningfulFields) { @@ -160,14 +166,6 @@ export async function transformWithEsbuild( compilerOptionsForFile[field] = loadedCompilerOptions[field] } } - } catch (e) { - if (e instanceof TSConfckParseError) { - // tsconfig could be out of root, make sure it is watched on dev - if (watcher && e.tsconfigFile && config) { - ensureWatchedFile(watcher, e.tsconfigFile, config.root) - } - } - throw e } } @@ -522,35 +520,21 @@ function prettifyMessage(m: EsbuildMessage, code: string): string { return res + `\n` } -let globalTSConfckCache: TSConfckCache | undefined -const tsconfckCacheMap = new WeakMap< - ResolvedConfig, - TSConfckCache ->() +let globalTSConfigResolutionCache: TsconfigCache | undefined +const tsconfigResolutionCacheMap = new WeakMap() -function getTSConfckCache(config?: ResolvedConfig) { +function getTSConfigResolutionCache(config?: ResolvedConfig) { if (!config) { - return (globalTSConfckCache ??= new TSConfckCache()) + return (globalTSConfigResolutionCache ??= new TsconfigCache()) } - let cache = tsconfckCacheMap.get(config) + let cache = tsconfigResolutionCacheMap.get(config) if (!cache) { - cache = new TSConfckCache() - tsconfckCacheMap.set(config, cache) + cache = new TsconfigCache() + tsconfigResolutionCacheMap.set(config, cache) } return cache } -export async function loadTsconfigJsonForFile( - filename: string, - config?: ResolvedConfig, -): Promise<{ tsconfigFile: string; tsconfig: TSConfigJSON }> { - const { tsconfig, tsconfigFile } = await parse(filename, { - cache: getTSConfckCache(config), - ignoreNodeModules: true, - }) - return { tsconfigFile, tsconfig } -} - export async function reloadOnTsconfigChange( server: ViteDevServer, changedFile: string, @@ -558,11 +542,8 @@ export async function reloadOnTsconfigChange( // any tsconfig.json that's added in the workspace could be closer to a code file than a previously cached one // any json file in the tsconfig cache could have been used to compile ts if (changedFile.endsWith('.json')) { - const cache = getTSConfckCache(server.config) - if ( - changedFile.endsWith('/tsconfig.json') || - cache.hasParseResult(changedFile) - ) { + const cache = getTSConfigResolutionCache(server.config) + if (changedFile.endsWith('/tsconfig.json') || cache.size() > 0) { server.config.logger.info( `changed tsconfig file detected: ${changedFile} - Clearing cache and forcing full-reload to ensure TypeScript is compiled with updated config values.`, { clear: server.config.clearScreen, timestamp: true }, @@ -575,7 +556,7 @@ export async function reloadOnTsconfigChange( environment.moduleGraph.invalidateAll() } - // reset tsconfck cache so that recompile works with up2date configs + // reset the cache so that recompile works with up2date configs cache.clear() // reload environments diff --git a/packages/vite/src/node/plugins/oxc.ts b/packages/vite/src/node/plugins/oxc.ts index fd77b98d174c12..01fd0e1b3abfb0 100644 --- a/packages/vite/src/node/plugins/oxc.ts +++ b/packages/vite/src/node/plugins/oxc.ts @@ -5,18 +5,11 @@ import type { } from 'rolldown/utils' import { transformSync } from 'rolldown/utils' import { viteTransformPlugin as nativeTransformPlugin } from 'rolldown/experimental' -import type { RawSourceMap } from '@jridgewell/remapping' import type { RolldownError, RolldownLog, SourceMap } from 'rolldown' -import { TSConfckParseError } from 'tsconfck' import colors from 'picocolors' import { prefixRegex } from 'rolldown/filter' import type { FSWatcher } from '#dep-types/chokidar' -import { - combineSourcemaps, - createFilter, - ensureWatchedFile, - normalizePath, -} from '../utils' +import { createFilter, ensureWatchedFile, normalizePath } from '../utils' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' import { cleanUrl } from '../../shared/utils' @@ -24,8 +17,7 @@ import { type Environment, perEnvironmentPlugin } from '..' import type { ViteDevServer } from '../server' import { JS_TYPES_RE, VITE_PACKAGE_DIR } from '../constants' import type { Logger } from '../logger' -import type { ESBuildOptions, TSCompilerOptions } from './esbuild' -import { loadTsconfigJsonForFile } from './esbuild' +import type { ESBuildOptions } from './esbuild' // IIFE content looks like `var MyLib = (function() {` or `this.nested.myLib = (function() {`. export const IIFE_BEGIN_RE: RegExp = @@ -69,146 +61,6 @@ export function getRollupJsxPresets( preset satisfies never } -function setOxcTransformOptionsFromTsconfigOptions( - oxcOptions: Omit & { - jsx?: - | OxcTransformOptions['jsx'] - | 'react' - | 'react-jsx' - | 'preserve-react' - | false - }, - tsCompilerOptions: Readonly | undefined = {}, - warnings: string[], -): void { - // when both the normal options and tsconfig is set, - // we want to prioritize the normal options - if (oxcOptions.jsx === 'preserve-react') { - oxcOptions.jsx = 'preserve' - } - if ( - tsCompilerOptions.jsx === 'preserve' && - (oxcOptions.jsx === undefined || - (typeof oxcOptions.jsx === 'object' && - oxcOptions.jsx.runtime === undefined)) - ) { - oxcOptions.jsx = 'preserve' - } - if (oxcOptions.jsx !== 'preserve' && oxcOptions.jsx !== false) { - const jsxOptions: OxcJsxOptions = - typeof oxcOptions.jsx === 'string' - ? getRollupJsxPresets(oxcOptions.jsx) - : { ...oxcOptions.jsx } - const typescriptOptions = { ...oxcOptions.typescript } - - if (tsCompilerOptions.jsxFactory) { - jsxOptions.pragma ??= tsCompilerOptions.jsxFactory - typescriptOptions.jsxPragma = jsxOptions.pragma - } - if (tsCompilerOptions.jsxFragmentFactory) { - jsxOptions.pragmaFrag ??= tsCompilerOptions.jsxFragmentFactory - typescriptOptions.jsxPragmaFrag = jsxOptions.pragmaFrag - } - if (tsCompilerOptions.jsxImportSource) { - jsxOptions.importSource ??= tsCompilerOptions.jsxImportSource - } - - if (!jsxOptions.runtime) { - switch (tsCompilerOptions.jsx) { - case 'react': - jsxOptions.runtime = 'classic' - // this option should not be set when using classic runtime - jsxOptions.importSource = undefined - break - case 'react-jsxdev': - jsxOptions.development = true - // eslint-disable-next-line no-fallthrough - case 'react-jsx': - jsxOptions.runtime = 'automatic' - // these options should not be set when using automatic runtime - jsxOptions.pragma = undefined - typescriptOptions.jsxPragma = undefined - jsxOptions.pragmaFrag = undefined - typescriptOptions.jsxPragmaFrag = undefined - break - default: - break - } - } - - oxcOptions.jsx = jsxOptions - oxcOptions.typescript = typescriptOptions - } - - if (oxcOptions.decorator?.legacy === undefined) { - const experimentalDecorators = tsCompilerOptions.experimentalDecorators - if (experimentalDecorators !== undefined) { - oxcOptions.decorator ??= {} - oxcOptions.decorator.legacy = experimentalDecorators - } - const emitDecoratorMetadata = tsCompilerOptions.emitDecoratorMetadata - if (emitDecoratorMetadata !== undefined) { - oxcOptions.decorator ??= {} - oxcOptions.decorator.emitDecoratorMetadata = emitDecoratorMetadata - } - } - - /** - * | preserveValueImports | importsNotUsedAsValues | verbatimModuleSyntax | onlyRemoveTypeImports | - * | -------------------- | ---------------------- | -------------------- |---------------------- | - * | false | remove | false | false | - * | false | preserve, error | - | - | - * | true | remove | - | - | - * | true | preserve, error | true | true | - */ - if (oxcOptions.typescript?.onlyRemoveTypeImports === undefined) { - if (tsCompilerOptions.verbatimModuleSyntax !== undefined) { - oxcOptions.typescript ??= {} - oxcOptions.typescript.onlyRemoveTypeImports = - tsCompilerOptions.verbatimModuleSyntax - } else if ( - tsCompilerOptions.preserveValueImports !== undefined || - tsCompilerOptions.importsNotUsedAsValues !== undefined - ) { - const preserveValueImports = - tsCompilerOptions.preserveValueImports ?? false - const importsNotUsedAsValues = - tsCompilerOptions.importsNotUsedAsValues ?? 'remove' - if ( - preserveValueImports === false && - importsNotUsedAsValues === 'remove' - ) { - oxcOptions.typescript ??= {} - oxcOptions.typescript.onlyRemoveTypeImports = true - } else if ( - preserveValueImports === true && - (importsNotUsedAsValues === 'preserve' || - importsNotUsedAsValues === 'error') - ) { - oxcOptions.typescript ??= {} - oxcOptions.typescript.onlyRemoveTypeImports = false - } else { - warnings.push( - `preserveValueImports=${preserveValueImports} + importsNotUsedAsValues=${importsNotUsedAsValues} is not supported by oxc.` + - 'Please migrate to the new verbatimModuleSyntax option.', - ) - oxcOptions.typescript ??= {} - oxcOptions.typescript.onlyRemoveTypeImports = false - } - } - } - - const resolvedTsconfigTarget = resolveTsconfigTarget(tsCompilerOptions.target) - const useDefineForClassFields = - tsCompilerOptions.useDefineForClassFields ?? - (resolvedTsconfigTarget === 'next' || resolvedTsconfigTarget >= 2022) - oxcOptions.assumptions ??= {} - oxcOptions.assumptions.setPublicClassFields = !useDefineForClassFields - oxcOptions.typescript ??= {} - oxcOptions.typescript.removeClassFieldsWithoutInitializer = - !useDefineForClassFields -} - // Copy from rolldown's packages/rolldown/src/utils/errors.ts function joinNewLine(s1: string, s2: string): string { // ensure single new line in between @@ -267,9 +119,7 @@ export async function transformWithOxc( config?: ResolvedConfig, watcher?: FSWatcher, ): Promise> { - const warnings: string[] = [] let lang = options?.lang - if (!lang) { // if the id ends with a valid ext, use it (e.g. vue blocks) // otherwise, cleanup the query before checking the ext @@ -289,36 +139,22 @@ export async function transformWithOxc( const resolvedOptions = { sourcemap: true, ...options, + inputMap: inMap as SourceMap | undefined, lang, - tsconfig: false, } - if (lang === 'ts' || lang === 'tsx') { - try { - const { tsconfig: loadedTsconfig, tsconfigFile } = - await loadTsconfigJsonForFile(filename, config) - // tsconfig could be out of root, make sure it is watched on dev - if (watcher && tsconfigFile && config) { - ensureWatchedFile(watcher, tsconfigFile, config.root) - } - setOxcTransformOptionsFromTsconfigOptions( - resolvedOptions, - loadedTsconfig.compilerOptions, - warnings, - ) - } catch (e) { - if (e instanceof TSConfckParseError) { - // tsconfig could be out of root, make sure it is watched on dev - if (watcher && e.tsconfigFile && config) { - ensureWatchedFile(watcher, e.tsconfigFile, config.root) - } - } - throw e + const result = transformSync(filename, code, resolvedOptions) + if ( + watcher && + config && + result.tsconfigFilePaths && + result.tsconfigFilePaths.length > 0 + ) { + for (const tsconfigFile of result.tsconfigFilePaths) { + ensureWatchedFile(watcher, normalizePath(tsconfigFile), config.root) } } - const result = transformSync(filename, code, resolvedOptions) - if (result.errors.length > 0) { // Copy from rolldown's packages/rolldown/src/utils/errors.ts let summary = `Transform failed with ${result.errors.length} error${result.errors.length < 2 ? '' : 's'}:\n` @@ -348,32 +184,7 @@ export async function transformWithOxc( }) throw wrapper } - - let map: SourceMap - if (inMap && result.map) { - const nextMap = result.map - nextMap.sourcesContent = [] - map = combineSourcemaps(filename, [ - nextMap as RawSourceMap, - inMap as RawSourceMap, - ]) as SourceMap - } else { - map = result.map as SourceMap - } - return { - ...result, - map, - } -} - -function resolveTsconfigTarget(target: string | undefined): number | 'next' { - if (!target) return 5 - - const targetLowered = target.toLowerCase() - if (!targetLowered.startsWith('es')) return 5 - - if (targetLowered === 'esnext') return 'next' - return parseInt(targetLowered.slice(2)) + return result } const warnedMessages = new Set() diff --git a/playground/tsconfig-json-load-error/__tests__/tsconfig-json-load-error.spec.ts b/playground/tsconfig-json-load-error/__tests__/tsconfig-json-load-error.spec.ts index 64b5b96d17d6d2..9adf918f1e6c3f 100644 --- a/playground/tsconfig-json-load-error/__tests__/tsconfig-json-load-error.spec.ts +++ b/playground/tsconfig-json-load-error/__tests__/tsconfig-json-load-error.spec.ts @@ -3,7 +3,7 @@ import { clearServeError, serveError } from './serve' import { browserLogs, editFile, isBuild, isServe, page, readFile } from '~utils' const unexpectedTokenSyntaxErrorRE = - /(\[vite:esbuild\] )*parsing .* failed: SyntaxError: Unexpected token.*\}.*|Build failed/ + /(\[TSCONFIG_ERROR\] )*Failed to load tsconfig .*: JSON parse error|JSONError/ describe.runIf(isBuild)('build', () => { test('should throw an error on build', () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ed269dd879f41..3713c139704314 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -415,9 +415,6 @@ importers: terser: specifier: ^5.46.0 version: 5.46.0 - tsconfck: - specifier: ^3.1.6 - version: 3.1.6(typescript@5.9.3) ufo: specifier: ^1.6.3 version: 1.6.3 @@ -7382,16 +7379,6 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - tsconfck@3.1.6: - resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} - engines: {node: ^18 || >=20} - hasBin: true - peerDependencies: - typescript: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true - tsdown@0.20.3: resolution: {integrity: sha512-qWOUXSbe4jN8JZEgrkc/uhJpC8VN2QpNu3eZkBWwNuTEjc/Ik1kcc54ycfcQ5QPRHeu9OQXaLfCI3o7pEJgB2w==} engines: {node: '>=20.19.0'} @@ -13627,10 +13614,6 @@ snapshots: ts-interface-checker@0.1.13: {} - tsconfck@3.1.6(typescript@5.9.3): - optionalDependencies: - typescript: 5.9.3 - tsdown@0.20.3(publint@0.3.17)(typescript@5.9.3)(vue-tsc@3.2.4(typescript@5.9.3)): dependencies: ansis: 4.2.0