Skip to content

Commit

Permalink
fix: derive useDefineForClassFields value from `tsconfig.compilerOp…
Browse files Browse the repository at this point in the history
…tions.target` (fixes #10296) (#11301)
  • Loading branch information
sapphi-red committed Dec 11, 2022
1 parent 9c2b1c0 commit 42976d8
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 2 deletions.
2 changes: 1 addition & 1 deletion docs/guide/features.md
Expand Up @@ -57,7 +57,7 @@ However, some libraries (e.g. [`vue`](https://github.com/vuejs/core/issues/1228)

#### `useDefineForClassFields`

Starting from Vite 2.5.0, the default value will be `true` if the TypeScript target is `ESNext`. 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.
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.

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 Down
76 changes: 75 additions & 1 deletion packages/vite/src/node/__tests__/plugins/esbuild.spec.ts
@@ -1,7 +1,6 @@
import { describe, expect, test } from 'vitest'
import type { ResolvedConfig, UserConfig } from '../../config'
import {
ESBuildTransformResult,
resolveEsbuildTranspileOptions,
transformWithEsbuild,
} from '../../plugins/esbuild'
Expand Down Expand Up @@ -305,6 +304,81 @@ describe('transformWithEsbuild', () => {
`/* @__PURE__ */ ${jsxFactory}(${jsxFragment}, null)`,
)
})

describe('useDefineForClassFields', async () => {
const transformClassCode = async (
target: string,
tsconfigCompilerOptions: {
target?: string
useDefineForClassFields?: boolean
},
) => {
const result = await transformWithEsbuild(
`
class foo {
bar = 'bar'
}
`,
'bar.ts',
{
target,
tsconfigRaw: { compilerOptions: tsconfigCompilerOptions },
},
)
return result?.code
}

const [
defineForClassFieldsTrueTransformedCode,
defineForClassFieldsTrueLowerTransformedCode,
defineForClassFieldsFalseTransformedCode,
] = await Promise.all([
transformClassCode('esnext', {
useDefineForClassFields: true,
}),
transformClassCode('es2021', {
useDefineForClassFields: true,
}),
transformClassCode('esnext', {
useDefineForClassFields: false,
}),
])

test('target: esnext and tsconfig.target: esnext => true', async () => {
const actual = await transformClassCode('esnext', {
target: 'esnext',
})
expect(actual).toBe(defineForClassFieldsTrueTransformedCode)
})

test('target: es2021 and tsconfig.target: esnext => true', async () => {
const actual = await transformClassCode('es2021', {
target: 'esnext',
})
expect(actual).toBe(defineForClassFieldsTrueLowerTransformedCode)
})

test('target: es2021 and tsconfig.target: es2021 => false', async () => {
const actual = await transformClassCode('es2021', {
target: 'es2021',
})
expect(actual).toBe(defineForClassFieldsFalseTransformedCode)
})

test('target: esnext and tsconfig.target: es2021 => false', async () => {
const actual = await transformClassCode('esnext', {
target: 'es2021',
})
expect(actual).toBe(defineForClassFieldsFalseTransformedCode)
})

test('target: es2022 and tsconfig.target: es2022 => true', async () => {
const actual = await transformClassCode('es2022', {
target: 'es2022',
})
expect(actual).toBe(defineForClassFieldsTrueTransformedCode)
})
})
})

/**
Expand Down
17 changes: 17 additions & 0 deletions packages/vite/src/node/plugins/esbuild.ts
Expand Up @@ -125,6 +125,23 @@ export async function transformWithEsbuild(
...tsconfigRaw?.compilerOptions,
},
}

const { compilerOptions } = tsconfigRaw
if (compilerOptions) {
// esbuild derives `useDefineForClassFields` from `target` instead of `tsconfig.compilerOptions.target`
// https://github.com/evanw/esbuild/issues/2584
// but we want `useDefineForClassFields` to be derived from `tsconfig.compilerOptions.target`
if (compilerOptions.useDefineForClassFields === undefined) {
const lowercaseTarget = compilerOptions.target?.toLowerCase() ?? 'es3'
if (lowercaseTarget.startsWith('es')) {
const esVersion = lowercaseTarget.slice(2)
compilerOptions.useDefineForClassFields =
esVersion === 'next' || +esVersion >= 2022
} else {
compilerOptions.useDefineForClassFields = false
}
}
}
}

const resolvedOptions = {
Expand Down

0 comments on commit 42976d8

Please sign in to comment.