Skip to content

Commit 3dfcbfc

Browse files
perf: add experimental.enableNativePlugin
Co-authored-by: IWANABETHATGUY <[email protected]>
1 parent 6eefaf4 commit 3dfcbfc

File tree

11 files changed

+562
-91
lines changed

11 files changed

+562
-91
lines changed

packages/vite/src/node/build.ts

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import type {
2121
WarningHandlerWithDefault,
2222
// WatcherOptions,
2323
} from 'rolldown'
24+
import {
25+
loadFallbackPlugin as nativeLoadFallbackPlugin,
26+
manifestPlugin as nativeManifestPlugin,
27+
} from 'rolldown/experimental'
2428
import type { RollupCommonJSOptions } from 'dep-types/commonjs'
2529
import type { RollupDynamicImportVarsOptions } from 'dep-types/dynamicImportVars'
2630
import type { EsbuildTarget } from 'types/internal/esbuildOptions'
@@ -474,10 +478,12 @@ export async function resolveBuildPlugins(config: ResolvedConfig): Promise<{
474478
pre: Plugin[]
475479
post: Plugin[]
476480
}> {
481+
const enableNativePlugin = config.experimental.enableNativePlugin
477482
return {
478483
pre: [
479484
completeSystemWrapPlugin(),
480-
dataURIPlugin(),
485+
// rolldown has builtin support datauri, use a switch to control it for convenience
486+
...(enableNativePlugin === true ? [] : [dataURIPlugin()]),
481487
perEnvironmentPlugin(
482488
'vite:rollup-options-plugins',
483489
async (environment) =>
@@ -491,13 +497,39 @@ export async function resolveBuildPlugins(config: ResolvedConfig): Promise<{
491497
],
492498
post: [
493499
buildImportAnalysisPlugin(config),
500+
...(enableNativePlugin !== true
501+
? [
502+
buildOxcPlugin(),
503+
...(config.build.minify === 'esbuild'
504+
? [buildEsbuildPlugin()]
505+
: []),
506+
]
507+
: []),
494508
buildOxcPlugin(),
495509
...(config.build.minify === 'esbuild' ? [buildEsbuildPlugin()] : []),
496510
terserPlugin(config),
497511
...(!config.isWorker
498-
? [manifestPlugin(), ssrManifestPlugin(), buildReporterPlugin(config)]
512+
? [
513+
config.build.manifest && enableNativePlugin === true
514+
? perEnvironmentPlugin('native:manifest', (environment) => {
515+
if (!environment.config.build.manifest) return false
516+
517+
return nativeManifestPlugin({
518+
root: environment.config.root,
519+
outPath:
520+
environment.config.build.manifest === true
521+
? '.vite/manifest.json'
522+
: environment.config.build.manifest,
523+
})
524+
})
525+
: manifestPlugin(),
526+
ssrManifestPlugin(),
527+
buildReporterPlugin(config),
528+
]
499529
: []),
500-
buildLoadFallbackPlugin(),
530+
enableNativePlugin === true
531+
? nativeLoadFallbackPlugin()
532+
: buildLoadFallbackPlugin(),
501533
],
502534
}
503535
}
@@ -1160,7 +1192,11 @@ export function injectEnvironmentToHooks(
11601192
): Plugin {
11611193
const { resolveId, load, transform } = plugin
11621194

1163-
const clone = { ...plugin }
1195+
// the plugin can be a class instance (e.g. native plugins)
1196+
const clone: Plugin = Object.assign(
1197+
Object.create(Object.getPrototypeOf(plugin)),
1198+
plugin,
1199+
)
11641200

11651201
for (const hook of Object.keys(clone) as RollupPluginHooks[]) {
11661202
switch (hook) {

packages/vite/src/node/config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,13 @@ export interface ExperimentalOptions {
515515
* @default false
516516
*/
517517
skipSsrTransform?: boolean
518+
/**
519+
* Enable builtin plugin that written by rust, which is faster than js plugin.
520+
*
521+
* @experimental
522+
* @default false
523+
*/
524+
enableNativePlugin?: boolean | 'resolver'
518525
}
519526

520527
export interface LegacyOptions {
@@ -702,6 +709,7 @@ export const configDefaults = Object.freeze({
702709
renderBuiltUrl: undefined,
703710
hmrPartialAccept: false,
704711
skipSsrTransform: false,
712+
enableNativePlugin: false,
705713
},
706714
future: {
707715
removePluginHookHandleHotUpdate: undefined,
@@ -1634,6 +1642,7 @@ export async function resolveConfig(
16341642
experimental: {
16351643
importGlobRestoreExtension: false,
16361644
hmrPartialAccept: false,
1645+
enableNativePlugin: false,
16371646
...config.experimental,
16381647
},
16391648
future: config.future,

packages/vite/src/node/idResolver.ts

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import aliasPlugin from '@rollup/plugin-alias'
33
import type { ResolvedConfig } from './config'
44
import type { EnvironmentPluginContainer } from './server/pluginContainer'
55
import { createEnvironmentPluginContainer } from './server/pluginContainer'
6-
import { resolvePlugin } from './plugins/resolve'
6+
import { oxcResolvePlugin, resolvePlugin } from './plugins/resolve'
77
import type { InternalResolveOptions } from './plugins/resolve'
88
import type { Environment } from './environment'
99
import type { PartialEnvironment } from './baseEnvironment'
@@ -61,17 +61,34 @@ export function createIdResolver(
6161
[
6262
// @ts-expect-error the aliasPlugin uses rollup types
6363
aliasPlugin({ entries: environment.config.resolve.alias }),
64-
resolvePlugin({
65-
root: config.root,
66-
isProduction: config.isProduction,
67-
isBuild: config.command === 'build',
68-
asSrc: true,
69-
preferRelative: false,
70-
tryIndex: true,
71-
...options,
72-
// Ignore sideEffects and other computations as we only need the id
73-
idOnly: true,
74-
}),
64+
...(config.experimental.enableNativePlugin
65+
? oxcResolvePlugin(
66+
{
67+
root: config.root,
68+
isProduction: config.isProduction,
69+
isBuild: config.command === 'build',
70+
asSrc: true,
71+
preferRelative: false,
72+
tryIndex: true,
73+
...options,
74+
// Ignore sideEffects and other computations as we only need the id
75+
idOnly: true,
76+
},
77+
environment.config,
78+
)
79+
: [
80+
resolvePlugin({
81+
root: config.root,
82+
isProduction: config.isProduction,
83+
isBuild: config.command === 'build',
84+
asSrc: true,
85+
preferRelative: false,
86+
tryIndex: true,
87+
...options,
88+
// Ignore sideEffects and other computations as we only need the id
89+
idOnly: true,
90+
}),
91+
]),
7592
],
7693
undefined,
7794
false,

packages/vite/src/node/plugins/css.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,7 @@ function createCSSResolvers(config: ResolvedConfig): CSSAtImportResolvers {
12841284
tryIndex: true,
12851285
tryPrefix: '_',
12861286
preferRelative: true,
1287+
skipMainField: true,
12871288
})
12881289
sassResolve = async (...args) => {
12891290
// the modern API calls `canonicalize` with resolved file URLs

packages/vite/src/node/plugins/define.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,19 @@ export function definePlugin(config: ResolvedConfig): Plugin {
110110
return pattern
111111
}
112112

113+
if (config.experimental.enableNativePlugin === true) {
114+
return {
115+
name: 'vite:define',
116+
options(option) {
117+
const [define, _pattern, importMetaEnvVal] = getPattern(
118+
this.environment,
119+
)
120+
define['import.meta.env'] = importMetaEnvVal
121+
option.define = define
122+
},
123+
}
124+
}
125+
113126
return {
114127
name: 'vite:define',
115128

packages/vite/src/node/plugins/importAnalysisBuild.ts

Lines changed: 67 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
} from 'es-module-lexer'
77
import { init, parse as parseImports } from 'es-module-lexer'
88
import type { SourceMap } from 'rolldown'
9+
import { buildImportAnalysisPlugin as nativeBuildImportAnalysisPlugin } from 'rolldown/experimental'
910
import type { RawSourceMap } from '@ampproject/remapping'
1011
import convertSourceMap from 'convert-source-map'
1112
import {
@@ -14,11 +15,11 @@ import {
1415
isInNodeModules,
1516
numberToPos,
1617
} from '../utils'
17-
import type { Plugin } from '../plugin'
18+
import { type Plugin, perEnvironmentPlugin } from '../plugin'
1819
import type { ResolvedConfig } from '../config'
1920
import { toOutputFilePathInJS } from '../build'
2021
import { genSourceMapUrl } from '../server/sourcemap'
21-
import type { Environment } from '../environment'
22+
import type { PartialEnvironment } from '../baseEnvironment'
2223
import { removedPureCssFilesCache } from './css'
2324
import { createParseErrorInfo } from './importAnalysis'
2425

@@ -166,19 +167,51 @@ function preload(
166167
})
167168
}
168169

170+
function getPreloadCode(
171+
environment: PartialEnvironment,
172+
renderBuiltUrlBoolean: boolean,
173+
isRelativeBase: boolean,
174+
) {
175+
const { modulePreload } = environment.config.build
176+
177+
const scriptRel =
178+
modulePreload && modulePreload.polyfill
179+
? `'modulepreload'`
180+
: `/* @__PURE__ */ (${detectScriptRel.toString()})()`
181+
182+
// There are two different cases for the preload list format in __vitePreload
183+
//
184+
// __vitePreload(() => import(asyncChunk), [ ...deps... ])
185+
//
186+
// This is maintained to keep backwards compatibility as some users developed plugins
187+
// using regex over this list to workaround the fact that module preload wasn't
188+
// configurable.
189+
const assetsURL =
190+
renderBuiltUrlBoolean || isRelativeBase
191+
? // If `experimental.renderBuiltUrl` is used, the dependencies might be relative to the current chunk.
192+
// If relative base is used, the dependencies are relative to the current chunk.
193+
// The importerUrl is passed as third parameter to __vitePreload in this case
194+
`function(dep, importerUrl) { return new URL(dep, importerUrl).href }`
195+
: // If the base isn't relative, then the deps are relative to the projects `outDir` and the base
196+
// is appended inside __vitePreload too.
197+
`function(dep) { return ${JSON.stringify(environment.config.base)}+dep }`
198+
const preloadCode = `const scriptRel = ${scriptRel};const assetsURL = ${assetsURL};const seen = {};export const ${preloadMethod} = ${preload.toString()}`
199+
return preloadCode
200+
}
201+
169202
/**
170203
* Build only. During serve this is performed as part of ./importAnalysis.
171204
*/
172205
export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
173-
const getInsertPreload = (environment: Environment) =>
206+
const getInsertPreload = (environment: PartialEnvironment) =>
174207
environment.config.consumer === 'client' &&
175208
!config.isWorker &&
176209
!config.build.lib
177210

178211
const renderBuiltUrl = config.experimental.renderBuiltUrl
179212
const isRelativeBase = config.base === './' || config.base === ''
180213

181-
return {
214+
const plugin: Plugin = {
182215
name: 'vite:build-import-analysis',
183216
resolveId: {
184217
filter: {
@@ -194,30 +227,11 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
194227
id: preloadHelperId,
195228
},
196229
handler(_id) {
197-
const { modulePreload } = this.environment.config.build
198-
199-
const scriptRel =
200-
modulePreload && modulePreload.polyfill
201-
? `'modulepreload'`
202-
: `/* @__PURE__ */ (${detectScriptRel.toString()})()`
203-
204-
// There are two different cases for the preload list format in __vitePreload
205-
//
206-
// __vitePreload(() => import(asyncChunk), [ ...deps... ])
207-
//
208-
// This is maintained to keep backwards compatibility as some users developed plugins
209-
// using regex over this list to workaround the fact that module preload wasn't
210-
// configurable.
211-
const assetsURL =
212-
renderBuiltUrl || isRelativeBase
213-
? // If `experimental.renderBuiltUrl` is used, the dependencies might be relative to the current chunk.
214-
// If relative base is used, the dependencies are relative to the current chunk.
215-
// The importerUrl is passed as third parameter to __vitePreload in this case
216-
`function(dep, importerUrl) { return new URL(dep, importerUrl).href }`
217-
: // If the base isn't relative, then the deps are relative to the projects `outDir` and the base
218-
// is appended inside __vitePreload too.
219-
`function(dep) { return ${JSON.stringify(config.base)}+dep }`
220-
const preloadCode = `const scriptRel = ${scriptRel};const assetsURL = ${assetsURL};const seen = {};export const ${preloadMethod} = ${preload.toString()}`
230+
const preloadCode = getPreloadCode(
231+
this.environment,
232+
!!renderBuiltUrl,
233+
isRelativeBase,
234+
)
221235
return { code: preloadCode, moduleSideEffects: false }
222236
},
223237
},
@@ -738,4 +752,29 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
738752
}
739753
},
740754
}
755+
756+
if (config.experimental.enableNativePlugin === true) {
757+
delete plugin.transform
758+
delete plugin.resolveId
759+
delete plugin.load
760+
return perEnvironmentPlugin(
761+
'native:import-analysis-build',
762+
(environment) => {
763+
const preloadCode = getPreloadCode(
764+
environment,
765+
!!renderBuiltUrl,
766+
isRelativeBase,
767+
)
768+
return nativeBuildImportAnalysisPlugin({
769+
preloadCode,
770+
insertPreload: getInsertPreload(environment),
771+
// this field looks redundant, put a dummy value for now
772+
optimizeModulePreloadRelativePaths: false,
773+
renderBuiltUrl: !!renderBuiltUrl,
774+
isRelativeBase,
775+
})
776+
},
777+
)
778+
}
779+
return plugin
741780
}

0 commit comments

Comments
 (0)