Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(scanner): respect experimentalDecorators: true #15206

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/vite/src/node/optimizer/scan.ts
Expand Up @@ -37,6 +37,7 @@ import {
import type { PluginContainer } from '../server/pluginContainer'
import { createPluginContainer } from '../server/pluginContainer'
import { transformGlobImport } from '../plugins/importMetaGlob'
import { loadTsconfigJsonForFile } from '../plugins/esbuild'

type ResolveIdOptions = Parameters<PluginContainer['resolveId']>[2]

Expand Down Expand Up @@ -216,6 +217,21 @@ async function prepareEsbuildScanner(
const { plugins = [], ...esbuildOptions } =
config.optimizeDeps?.esbuildOptions ?? {}

// The plugin pipeline automatically loads the closest tsconfig.json.
// But esbuild doesn't support reading tsconfig.json if the plugin has resolved the path (https://github.com/evanw/esbuild/issues/2265).
// Due to syntax incompatibilities between the experimental decorators in TypeScript and TC39 decorators,
// we cannot simply set `"experimentalDecorators": true` or `false`. (https://github.com/vitejs/vite/pull/15206#discussion_r1417414715)
// Therefore, we use the closest tsconfig.json from the root to make it work in most cases.
let tsconfigRaw = esbuildOptions.tsconfigRaw
if (!tsconfigRaw && !esbuildOptions.tsconfig) {
const tsconfigResult = await loadTsconfigJsonForFile(
path.join(config.root, '_dummy.js'),
)
if (tsconfigResult.compilerOptions?.experimentalDecorators) {
tsconfigRaw = { compilerOptions: { experimentalDecorators: true } }
}
}

return await esbuild.context({
absWorkingDir: process.cwd(),
write: false,
Expand All @@ -228,6 +244,7 @@ async function prepareEsbuildScanner(
logLevel: 'silent',
plugins: [...plugins, plugin],
...esbuildOptions,
tsconfigRaw,
})
}

Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/node/plugins/esbuild.ts
Expand Up @@ -440,7 +440,7 @@ function prettifyMessage(m: Message, code: string): string {

let tsconfckCache: TSConfckCache<TSConfckParseResult> | undefined

async function loadTsconfigJsonForFile(
export async function loadTsconfigJsonForFile(
filename: string,
): Promise<TSConfigJSON> {
try {
Expand Down
12 changes: 11 additions & 1 deletion playground/tsconfig-json/__tests__/tsconfig-json.spec.ts
Expand Up @@ -2,7 +2,7 @@ import path from 'node:path'
import fs from 'node:fs'
import { transformWithEsbuild } from 'vite'
import { describe, expect, test } from 'vitest'
import { browserLogs } from '~utils'
import { browserLogs, isServe, serverLogs } from '~utils'

test('should respected each `tsconfig.json`s compilerOptions', () => {
// main side effect should be called (because of `"importsNotUsedAsValues": "preserve"`)
Expand All @@ -21,6 +21,16 @@ test('should respected each `tsconfig.json`s compilerOptions', () => {
expect(browserLogs).toContain('data setter in NestedWithExtendsBase')
})

test.runIf(isServe)('scanner should not error with decorators', () => {
expect(serverLogs).not.toStrictEqual(
expect.arrayContaining([
expect.stringContaining(
'Parameter decorators only work when experimental decorators are enabled',
),
]),
)
})

describe('transformWithEsbuild', () => {
test('merge tsconfigRaw object', async () => {
const main = path.resolve(__dirname, '../src/main.ts')
Expand Down
4 changes: 2 additions & 2 deletions playground/tsconfig-json/src/decorator.ts
@@ -1,11 +1,11 @@
// @ts-nocheck playground/tsconfig.json does not have decorators enabled
function first() {
return function (...args: any[]) {}
}

export class Foo {
@first()
// @ts-expect-error we intentionally not enable `experimentalDecorators` to test esbuild compat
bluwy marked this conversation as resolved.
Show resolved Hide resolved
method(@first test: string) {
method(@first() test: string) {
return test
}
}