Skip to content

Commit

Permalink
refactor(esbuild)!: remove esbuild 0.17 -> 0.18 compat (#14804)
Browse files Browse the repository at this point in the history
  • Loading branch information
bluwy authored Oct 30, 2023
1 parent 4f71ae8 commit 7234021
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 80 deletions.
25 changes: 25 additions & 0 deletions docs/guide/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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).

Expand All @@ -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.

Expand Down
25 changes: 25 additions & 0 deletions docs/guide/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
15 changes: 0 additions & 15 deletions packages/vite/src/node/__tests__/plugins/esbuild.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {')
})
})
})

Expand Down
12 changes: 3 additions & 9 deletions packages/vite/src/node/optimizer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -826,8 +822,6 @@ async function prepareEsbuildOptimizerRun(
metafile: true,
plugins,
charset: 'utf8',
tsconfig,
tsconfigRaw: resolveTsconfigRaw(tsconfig, tsconfigRaw),
...esbuildOptions,
supported: {
'dynamic-import': true,
Expand Down
30 changes: 2 additions & 28 deletions packages/vite/src/node/optimizer/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { performance } from 'node:perf_hooks'
import glob from 'fast-glob'
import type {
BuildContext,
BuildOptions,
Loader,
OnLoadArgs,
OnLoadResult,
Expand Down Expand Up @@ -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(),
Expand All @@ -232,8 +227,6 @@ async function prepareEsbuildScanner(
format: 'esm',
logLevel: 'silent',
plugins: [...plugins, plugin],
tsconfig,
tsconfigRaw: resolveTsconfigRaw(tsconfig, tsconfigRaw),
...esbuildOptions,
})
}
Expand Down Expand Up @@ -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,
},
}
}
30 changes: 6 additions & 24 deletions packages/vite/src/node/plugins/esbuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ export async function transformWithEsbuild(
}

let tsconfigRaw = options?.tsconfigRaw
const fallbackSupported: Record<string, boolean> = {}

// if options provide tsconfigRaw in string, it takes highest precedence
if (typeof tsconfigRaw !== 'string') {
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand Down Expand Up @@ -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)
})
Expand Down
5 changes: 2 additions & 3 deletions playground/tsconfig-json/__tests__/tsconfig-json.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
})
Expand Down
3 changes: 2 additions & 1 deletion playground/tsconfig-json/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"noImplicitReturns": true,

"useDefineForClassFields": true,
"importsNotUsedAsValues": "preserve"
"importsNotUsedAsValues": "preserve",
"experimentalDecorators": true
},
"include": ["./src"]
}

0 comments on commit 7234021

Please sign in to comment.