Repository: vitejs/vite
File: packages/vite/src/node/optimizer/scan.ts (TypeScript)

Code before:
  return entries
}

async function prepareEsbuildScanner(
  environment: ScanEnvironment,
  entries: string[],
  deps: Record<string, string>,
  missing: Record<string, string>,
): Promise<BuildContext> {
  const plugin = esbuildScanPlugin(environment, deps, missing, entries)

  const { plugins = [], ...esbuildOptions } =
    environment.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 { tsconfig } = await loadTsconfigJsonForFile(
      path.join(environment.config.root, '_dummy.js'),
    )
    if (
      tsconfig.compilerOptions?.experimentalDecorators ||
      tsconfig.compilerOptions?.jsx ||
      tsconfig.compilerOptions?.jsxFactory ||
      tsconfig.compilerOptions?.jsxFragmentFactory ||
      tsconfig.compilerOptions?.jsxImportSource
    ) {
      tsconfigRaw = {
        compilerOptions: {
          experimentalDecorators:
            tsconfig.compilerOptions?.experimentalDecorators,
          jsx: tsconfig.compilerOptions?.jsx,
          jsxFactory: tsconfig.compilerOptions?.jsxFactory,
          jsxFragmentFactory: tsconfig.compilerOptions?.jsxFragmentFactory,
          jsxImportSource: tsconfig.compilerOptions?.jsxImportSource,
        },
      }
    }
  }

  return await esbuild.context({
    absWorkingDir: process.cwd(),
    write: false,
    stdin: {
      contents: entries.map((e) => `import ${JSON.stringify(e)}`).join('\n'),
      loader: 'js',
    },

Review comment (bug):
I'd imagine there was a reason to only pick up `experimentalDecorators` instead of entire `tsconfig`, but I didn't understand the comment above and didn't have tests failing with expanding jsx here too.

Code after:
  return entries
}

async function prepareEsbuildScanner(
  environment: ScanEnvironment,
  entries: string[],
  deps: Record<string, string>,
  missing: Record<string, string>,
): Promise<BuildContext> {
  const plugin = esbuildScanPlugin(environment, deps, missing, entries)

  const { plugins = [], ...esbuildOptions } =
    environment.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 { tsconfig } = await loadTsconfigJsonForFile(
      path.join(environment.config.root, '_dummy.js'),
    )
    if (
      tsconfig.compilerOptions?.experimentalDecorators ||
      tsconfig.compilerOptions?.jsx ||
      tsconfig.compilerOptions?.jsxFactory ||
      tsconfig.compilerOptions?.jsxFragmentFactory ||
      tsconfig.compilerOptions?.jsxImportSource
    ) {
      tsconfigRaw = {
        compilerOptions: {
          experimentalDecorators:
            tsconfig.compilerOptions?.experimentalDecorators,
          // esbuild uses tsconfig fields when both the normal options and tsconfig was set
          // but we want to prioritize the normal options
          jsx: esbuildOptions.jsx ? undefined : tsconfig.compilerOptions?.jsx,
          jsxFactory: esbuildOptions.jsxFactory
            ? undefined
            : tsconfig.compilerOptions?.jsxFactory,
          jsxFragmentFactory: esbuildOptions.jsxFragment
            ? undefined
            : tsconfig.compilerOptions?.jsxFragmentFactory,
          jsxImportSource: esbuildOptions.jsxImportSource
            ? undefined
            : tsconfig.compilerOptions?.jsxImportSource,
        },
      }
    }
  }