From 74677f521f8b487ab27e9abeafa30af7082764e0 Mon Sep 17 00:00:00 2001 From: underfin Date: Sun, 29 Sep 2024 22:27:25 +0800 Subject: [PATCH] feat: using oxc transfomer --- packages/vite/src/node/config.ts | 2 + packages/vite/src/node/optimizer/index.ts | 9 +- packages/vite/src/node/plugins/index.ts | 4 +- packages/vite/src/node/plugins/oxc.ts | 119 ++++++++++++++++++++++ 4 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 packages/vite/src/node/plugins/oxc.ts diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index bb85d898fa4383..7c326c5e5c257f 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -89,6 +89,7 @@ import type { ResolvedSSROptions, SSROptions } from './ssr' import { resolveSSROptions } from './ssr' import { PartialEnvironment } from './baseEnvironment' import { createIdResolver } from './idResolver' +import type { OxcOptions } from './plugins/oxc' const debug = createDebugger('vite:config') const promisifiedRealpath = promisify(fs.realpath) @@ -571,6 +572,7 @@ export type ResolvedConfig = Readonly< plugins: readonly Plugin[] css: ResolvedCSSOptions esbuild: ESBuildOptions | false + oxc: OxcOptions | false server: ResolvedServerOptions dev: ResolvedDevEnvironmentOptions builder: ResolvedBuilderOptions diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index 99dad1a07e668a..cabc16ddab6e07 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -23,9 +23,10 @@ import { unique, } from '../utils' import { transformWithEsbuild } from '../plugins/esbuild' -import { ESBUILD_MODULES_TARGET, METADATA_FILENAME } from '../constants' +import { METADATA_FILENAME } from '../constants' import { isWindows } from '../../shared/utils' import type { Environment } from '../environment' +import { transformWithOxc } from '../plugins/oxc' import { ScanEnvironment, scanImports } from './scan' import { createOptimizeDepsIncludeResolver, expandGlobIds } from './resolve' import { @@ -752,12 +753,10 @@ async function prepareRolldownOptimizerRun( name: 'optimizer-transform', async transform(code, id) { if (/\.(?:m?[jt]s|[jt]sx)$/.test(id)) { - const result = await transformWithEsbuild(code, id, { + const result = await transformWithOxc(code, id, { sourcemap: true, - sourcefile: id, - loader: jsxLoader && /\.js$/.test(id) ? 'jsx' : undefined, + jsx: jsxLoader && /\.js$/.test(id) ? true : undefined, define, - target: ESBUILD_MODULES_TARGET, }) return { code: result.code, diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index 4bf0c449012860..2032b84b36e762 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -18,7 +18,6 @@ import { getFsUtils } from '../fsUtils' import { jsonPlugin } from './json' import { filteredResolvePlugin, resolvePlugin } from './resolve' import { optimizedDepsPlugin } from './optimizedDeps' -import { esbuildPlugin } from './esbuild' import { importAnalysisPlugin } from './importAnalysis' import { cssAnalysisPlugin, cssPlugin, cssPostPlugin } from './css' import { assetPlugin } from './asset' @@ -34,6 +33,7 @@ import { assetImportMetaUrlPlugin } from './assetImportMetaUrl' import { metadataPlugin } from './metadata' import { dynamicImportVarsPlugin } from './dynamicImportVars' import { importGlobPlugin } from './importMetaGlob' +import { oxcPlugin } from './oxc' export async function resolvePlugins( config: ResolvedConfig, @@ -114,7 +114,7 @@ export async function resolvePlugins( config.esbuild !== false ? enableNativePlugin ? nativeTransformPlugin() - : esbuildPlugin(config) + : oxcPlugin(config) : null, enableNativePlugin ? nativeJsonPlugin({ diff --git a/packages/vite/src/node/plugins/oxc.ts b/packages/vite/src/node/plugins/oxc.ts new file mode 100644 index 00000000000000..1b12d26e8daf78 --- /dev/null +++ b/packages/vite/src/node/plugins/oxc.ts @@ -0,0 +1,119 @@ +import path from 'node:path' +import { transform } from 'rolldown/experimental' +import type { RawSourceMap } from '@ampproject/remapping' +import type { SourceMap } from 'rolldown' +import { combineSourcemaps, createFilter } from '../utils' +import type { ResolvedConfig } from '../config' +import type { Plugin } from '../plugin' +import { cleanUrl } from '../../shared/utils' +import { loadTsconfigJsonForFile } from './esbuild' + +const jsxExtensionsRE = /\.(?:j|t)sx\b/ +const validExtensionRE = /\.\w+$/ + +// should import from rolldown +declare type TransformOptions = any +declare type TransformResult = any + +export interface OxcOptions extends TransformOptions { + include?: string | RegExp | string[] | RegExp[] + exclude?: string | RegExp | string[] | RegExp[] + jsxInject?: string +} + +export async function transformWithOxc( + code: string, + filename: string, + options?: TransformOptions, + inMap?: object, +): Promise { + const resolvedOptions = { + sourcemap: true, + ...options, + } + + const ext = path + .extname(validExtensionRE.test(filename) ? filename : cleanUrl(filename)) + .slice(1) + + if (ext === 'cts' || ext === 'mts' || ext === 'tsx' || ext === 'ts') { + const loadedTsconfig = await loadTsconfigJsonForFile(filename) + const loadedCompilerOptions = loadedTsconfig.compilerOptions ?? {} + // tsc compiler alwaysStrict/experimentalDecorators/importsNotUsedAsValues/preserveValueImports/target/useDefineForClassFields/verbatimModuleSyntax + + resolvedOptions.react = { + jsxFactory: loadedCompilerOptions.jsx, + pragma: loadedCompilerOptions.jsxFactory, + pragmaFrag: loadedCompilerOptions.jsxFragmentFactory, + importSource: loadedCompilerOptions.jsxImportSource, + development: loadedCompilerOptions.jsx === 'react-jsxdev', + runtime: loadedCompilerOptions.jsx === 'react-jsxdev', + } + + switch (loadedCompilerOptions.jsx) { + case 'react-jsxdev': + resolvedOptions.react.runtime = 'automatic' + resolvedOptions.react.development = true + break + + case 'react': + resolvedOptions.react.runtime = 'classic' + break + + case 'react-jsx': + resolvedOptions.react.runtime = 'automatic' + break + case 'preserve': + // not support + break + + default: + break + } + } + + const result = await transform(filename, code, resolvedOptions) + + if (result.errors.length > 0) { + throw new Error(result.errors[0]) + } + + let map: SourceMap + if (inMap && result.map) { + const nextMap = result.map + nextMap.sourcesContent = [] + map = combineSourcemaps(filename, [ + nextMap as RawSourceMap, + inMap as RawSourceMap, + ]) as SourceMap + } else { + map = result.map as SourceMap + } + return { + ...result, + map, + } +} + +export function oxcPlugin(config: ResolvedConfig): Plugin { + const options = config.oxc as OxcOptions + const { jsxInject, include, exclude, ...oxcTransformOptions } = options + + const filter = createFilter(include || /\.(m?ts|[jt]sx)$/, exclude || /\.js$/) + + return { + name: 'vite:oxc', + async transform(code, id) { + if (filter(id) || filter(cleanUrl(id))) { + const result = await transformWithOxc(code, id, oxcTransformOptions) + if (jsxInject && jsxExtensionsRE.test(id)) { + result.code = jsxInject + ';' + result.code + } + return { + code: result.code, + map: result.map, + } + } + }, + } +}