diff --git a/docs/guide/features.md b/docs/guide/features.md index 6044aa99592765..6b195cd5ec41cb 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -57,6 +57,8 @@ Some configuration fields under `compilerOptions` in `tsconfig.json` require spe #### `isolatedModules` +- [TypeScript documentation](https://www.typescriptlang.org/tsconfig#isolatedModules) + Should be set to `true`. It is because `esbuild` only performs transpilation without type information, it doesn't support certain features like const enum and implicit type-only imports. @@ -67,8 +69,12 @@ However, some libraries (e.g. [`vue`](https://github.com/vuejs/core/issues/1228) #### `useDefineForClassFields` +- [TypeScript documentation](https://www.typescriptlang.org/tsconfig#useDefineForClassFields) + Starting from Vite 2.5.0, the default value will be `true` if the TypeScript target is `ESNext` or `ES2022` or newer. It is consistent with the [behavior of `tsc` 4.3.2 and later](https://github.com/microsoft/TypeScript/pull/42663). It is also the standard ECMAScript runtime behavior. +Other TypeScript targets will default to `false`. + But it may be counter-intuitive for those coming from other programming languages or older versions of TypeScript. You can read more about the transition in the [TypeScript 3.7 release notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#the-usedefineforclassfields-flag-and-the-declare-property-modifier). @@ -78,13 +84,32 @@ Most libraries expect `"useDefineForClassFields": true`, such as [MobX](https:// But a few libraries haven't transitioned to this new default yet, including [`lit-element`](https://github.com/lit/lit-element/issues/1030). Please explicitly set `useDefineForClassFields` to `false` in these cases. +#### `target` + +- [TypeScript documentation](https://www.typescriptlang.org/tsconfig#target) + +Vite does not transpile TypeScript with the configured `target` value by default, following the same behaviour as `esbuild`. + +The [`esbuild.target`](/config/shared-options.html#esbuild) option can be used instead, which defaults to `esnext` for minimal transpilation. In builds, the [`build.target`](/config/build-options.html#build-target) option takes higher priority and can also be set if needed. + +::: warning `useDefineForClassFields` +If `target` is not `ESNext` or `ES2022` or newer, or if there's no `tsconfig.json` file, `useDefineForClassFields` will default to `false` which can be problematic with the default `esbuild.target` value of `esnext`. It may transpile to [static initialization blocks](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Static_initialization_blocks#browser_compatibility) which may not be supported in your browser. + +As such, it is recommended to set `target` to `ESNext` or `ES2022` or newer, or set `useDefineForClassFields` to `true` explicitly when configuring `tsconfig.json`. +::: + #### Other Compiler Options Affecting the Build Result - [`extends`](https://www.typescriptlang.org/tsconfig#extends) - [`importsNotUsedAsValues`](https://www.typescriptlang.org/tsconfig#importsNotUsedAsValues) - [`preserveValueImports`](https://www.typescriptlang.org/tsconfig#preserveValueImports) +- [`verbatimModuleSyntax`](https://www.typescriptlang.org/tsconfig#verbatimModuleSyntax) +- [`jsx`](https://www.typescriptlang.org/tsconfig#jsx) - [`jsxFactory`](https://www.typescriptlang.org/tsconfig#jsxFactory) - [`jsxFragmentFactory`](https://www.typescriptlang.org/tsconfig#jsxFragmentFactory) +- [`jsxImportSource`](https://www.typescriptlang.org/tsconfig#jsxImportSource) +- [`experimentalDecorators`](https://www.typescriptlang.org/tsconfig#experimentalDecorators) +- [`alwaysStrict`](https://www.typescriptlang.org/tsconfig#alwaysStrict) If migrating your codebase to `"isolatedModules": true` is an insurmountable effort, you may be able to get around it with a third-party plugin such as [rollup-plugin-friendly-type-imports](https://www.npmjs.com/package/rollup-plugin-friendly-type-imports). However, this approach is not officially supported by Vite. diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 16f63bdceb2e57..b04e2ac72aaefc 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -136,6 +136,31 @@ CLI shortcuts, like `r` to restart the dev server, now require an additional `En This change prevents Vite from swallowing and controlling OS-specific shortcuts, allowing better compatibility when combining the Vite dev server with other processes, and avoids the [previous caveats](https://github.com/vitejs/vite/pull/14342). +### Update `experimentalDecorators` and `useDefineForClassFields` TypeScript behaviour + +Vite 5 uses esbuild 0.19 and removes the compatibility layer for esbuild 0.18, which changes how `experimentalDecorators` and `useDefineForClassFields` are handled. + +- **`experimentalDecorators` is not enabled by default** + + You need to set `compilerOptions.experimentalDecorators` to `true` in `tsconfig.json` to use decorators. + +- **`useDefineForClassFields` defaults depend on the TypeScript `target` value** + + If `target` is not `ESNext` or `ES2022` or newer, or if there's no `tsconfig.json` file, `useDefineForClassFields` will default to `false` which can be problematic with the default `esbuild.target` value of `esnext`. It may transpile to [static initialization blocks](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Static_initialization_blocks#browser_compatibility) which may not be supported in your browser. + + As such, it is recommended to set `target` to `ESNext` or `ES2022` or newer, or set `useDefineForClassFields` to `true` explicitly when configuring `tsconfig.json`. + +```jsonc +{ + "compilerOptions": { + // Set true if you use decorators + "experimentalDecorators": true, + // Set true if you see parsing errors in your browser + "useDefineForClassFields": true + } +} +``` + ### Remove `--https` flag and `https: true` `--https` flag sets `https: true`. This config was meant to be used together with the automatic https certification generation feature which [was dropped in Vite 3](https://v3.vitejs.dev/guide/migration.html#automatic-https-certificate-generation). This config no longer makes sense as it will make Vite start a HTTPS server without a certificate. diff --git a/packages/vite/src/node/__tests__/plugins/esbuild.spec.ts b/packages/vite/src/node/__tests__/plugins/esbuild.spec.ts index 0d6d81af898e5a..8992000da101a5 100644 --- a/packages/vite/src/node/__tests__/plugins/esbuild.spec.ts +++ b/packages/vite/src/node/__tests__/plugins/esbuild.spec.ts @@ -383,21 +383,6 @@ describe('transformWithEsbuild', () => { const actual = await transformClassCode('es2022', {}) expect(actual).toBe(defineForClassFieldsFalseTransformedCode) }) - - test('useDefineForClassFields: false and static property should not be transpile to static block', async () => { - const result = await transformWithEsbuild( - ` - class foo { - static bar = 'bar' - } - `, - 'bar.ts', - { - target: 'esnext', - }, - ) - expect(result?.code).not.toContain('static {') - }) }) }) diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index 986a748e055705..edc516000fa57c 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -27,7 +27,7 @@ import { import { transformWithEsbuild } from '../plugins/esbuild' import { ESBUILD_MODULES_TARGET } from '../constants' import { esbuildCjsExternalPlugin, esbuildDepPlugin } from './esbuildDepPlugin' -import { resolveTsconfigRaw, scanImports } from './scan' +import { scanImports } from './scan' import { createOptimizeDepsIncludeResolver, expandGlobIds } from './resolve' export { initDepsOptimizer, @@ -730,12 +730,8 @@ async function prepareEsbuildOptimizerRun( const optimizeDeps = getDepOptimizationConfig(config, ssr) - const { - plugins: pluginsFromConfig = [], - tsconfig, - tsconfigRaw, - ...esbuildOptions - } = optimizeDeps?.esbuildOptions ?? {} + const { plugins: pluginsFromConfig = [], ...esbuildOptions } = + optimizeDeps?.esbuildOptions ?? {} await Promise.all( Object.keys(depsInfo).map(async (id) => { @@ -826,8 +822,6 @@ async function prepareEsbuildOptimizerRun( metafile: true, plugins, charset: 'utf8', - tsconfig, - tsconfigRaw: resolveTsconfigRaw(tsconfig, tsconfigRaw), ...esbuildOptions, supported: { 'dynamic-import': true, diff --git a/packages/vite/src/node/optimizer/scan.ts b/packages/vite/src/node/optimizer/scan.ts index 9f4ceb28701e9f..c0d97f480ca9de 100644 --- a/packages/vite/src/node/optimizer/scan.ts +++ b/packages/vite/src/node/optimizer/scan.ts @@ -5,7 +5,6 @@ import { performance } from 'node:perf_hooks' import glob from 'fast-glob' import type { BuildContext, - BuildOptions, Loader, OnLoadArgs, OnLoadResult, @@ -214,12 +213,8 @@ async function prepareEsbuildScanner( const plugin = esbuildScanPlugin(config, container, deps, missing, entries) - const { - plugins = [], - tsconfig, - tsconfigRaw, - ...esbuildOptions - } = config.optimizeDeps?.esbuildOptions ?? {} + const { plugins = [], ...esbuildOptions } = + config.optimizeDeps?.esbuildOptions ?? {} return await esbuild.context({ absWorkingDir: process.cwd(), @@ -232,8 +227,6 @@ async function prepareEsbuildScanner( format: 'esm', logLevel: 'silent', plugins: [...plugins, plugin], - tsconfig, - tsconfigRaw: resolveTsconfigRaw(tsconfig, tsconfigRaw), ...esbuildOptions, }) } @@ -684,22 +677,3 @@ function isScannable(id: string, extensions: string[] | undefined): boolean { false ) } - -// esbuild v0.18 only transforms decorators when `experimentalDecorators` is set to `true`. -// To preserve compat with the esbuild breaking change, we set `experimentalDecorators` to -// `true` by default if it's unset. -// TODO: Remove this in Vite 5 and check https://github.com/vitejs/vite/pull/13805#issuecomment-1633612320 -export function resolveTsconfigRaw( - tsconfig: string | undefined, - tsconfigRaw: BuildOptions['tsconfigRaw'], -): BuildOptions['tsconfigRaw'] { - return tsconfig || typeof tsconfigRaw === 'string' - ? tsconfigRaw - : { - ...tsconfigRaw, - compilerOptions: { - experimentalDecorators: true, - ...tsconfigRaw?.compilerOptions, - }, - } -} diff --git a/packages/vite/src/node/plugins/esbuild.ts b/packages/vite/src/node/plugins/esbuild.ts index 3a0e1eb7616d1b..ba7f338c8eec59 100644 --- a/packages/vite/src/node/plugins/esbuild.ts +++ b/packages/vite/src/node/plugins/esbuild.ts @@ -93,7 +93,6 @@ export async function transformWithEsbuild( } let tsconfigRaw = options?.tsconfigRaw - const fallbackSupported: Record = {} // if options provide tsconfigRaw in string, it takes highest precedence if (typeof tsconfigRaw !== 'string') { @@ -140,23 +139,6 @@ export async function transformWithEsbuild( compilerOptions.useDefineForClassFields = false } - // esbuild v0.18 only transforms decorators when `experimentalDecorators` is set to `true`. - // To preserve compat with the esbuild breaking change, we set `experimentalDecorators` to - // `true` by default if it's unset. - // TODO: Remove this in Vite 5 - if (compilerOptions.experimentalDecorators === undefined) { - compilerOptions.experimentalDecorators = true - } - - // Compat with esbuild 0.17 where static properties are transpiled to - // static blocks when `useDefineForClassFields` is false. Its support - // is not great yet, so temporarily disable it for now. - // TODO: Remove this in Vite 5, don't pass hardcoded `esnext` target - // to `transformWithEsbuild` in the esbuild plugin. - if (compilerOptions.useDefineForClassFields !== true) { - fallbackSupported['class-static-blocks'] = false - } - // esbuild uses tsconfig fields when both the normal options and tsconfig was set // but we want to prioritize the normal options if (options) { @@ -179,10 +161,6 @@ export async function transformWithEsbuild( ...options, loader, tsconfigRaw, - supported: { - ...fallbackSupported, - ...options?.supported, - }, } // Some projects in the ecosystem are calling this function with an ESBuildOptions @@ -220,9 +198,13 @@ export async function transformWithEsbuild( if (e.errors) { e.frame = '' e.errors.forEach((m: Message) => { - if (m.text === 'Experimental decorators are not currently enabled') { + if ( + m.text === 'Experimental decorators are not currently enabled' || + m.text === + 'Parameter decorators only work when experimental decorators are enabled' + ) { m.text += - '. Vite 4.4+ now uses esbuild 0.18 and you need to enable them by adding "experimentalDecorators": true in your "tsconfig.json" file.' + '. Vite 5 now uses esbuild 0.18 and you need to enable them by adding "experimentalDecorators": true in your "tsconfig.json" file.' } e.frame += `\n` + prettifyMessage(m, code) }) diff --git a/playground/tsconfig-json/__tests__/tsconfig-json.spec.ts b/playground/tsconfig-json/__tests__/tsconfig-json.spec.ts index dd130a65291001..4ecdff687fbaa2 100644 --- a/playground/tsconfig-json/__tests__/tsconfig-json.spec.ts +++ b/playground/tsconfig-json/__tests__/tsconfig-json.spec.ts @@ -71,9 +71,8 @@ describe('transformWithEsbuild', () => { test('experimentalDecorators', async () => { const main = path.resolve(__dirname, '../src/decorator.ts') const mainContent = fs.readFileSync(main, 'utf-8') - // Should not error when transpiling decorators - // TODO: In Vite 5, this should require setting `tsconfigRaw.experimentalDecorators` - // or via the closest `tsconfig.json` + // Should not error when transpiling decorators as nearest tsconfig.json + // has "experimentalDecorators": true const result = await transformWithEsbuild(mainContent, main, { target: 'es2020', }) diff --git a/playground/tsconfig-json/tsconfig.json b/playground/tsconfig-json/tsconfig.json index f15aff13b3bc35..6445b1652ea0e1 100644 --- a/playground/tsconfig-json/tsconfig.json +++ b/playground/tsconfig-json/tsconfig.json @@ -14,7 +14,8 @@ "noImplicitReturns": true, "useDefineForClassFields": true, - "importsNotUsedAsValues": "preserve" + "importsNotUsedAsValues": "preserve", + "experimentalDecorators": true }, "include": ["./src"] }