From 0eddb11bc5d60c67ddb10ba336534b138ca8f1a3 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 11 Sep 2024 19:56:17 +0900 Subject: [PATCH 01/46] feat: use rolldown in the dep optimizer Co-authored-by: underfin --- packages/vite/package.json | 1 + packages/vite/rollup.config.ts | 1 + packages/vite/rollup.dts.config.ts | 1 + packages/vite/src/node/config.ts | 1 + .../src/node/optimizer/esbuildDepPlugin.ts | 347 ---------- packages/vite/src/node/optimizer/index.ts | 278 ++++---- .../src/node/optimizer/rolldownDepPlugin.ts | 336 +++++++++ packages/vite/src/node/optimizer/scan.ts | 642 ++++++++---------- packages/vite/src/node/plugin.ts | 11 +- .../vite/src/node/plugins/importAnalysis.ts | 12 +- playground/optimize-deps/vite.config.js | 26 +- pnpm-lock.yaml | 166 +++++ 12 files changed, 953 insertions(+), 869 deletions(-) delete mode 100644 packages/vite/src/node/optimizer/esbuildDepPlugin.ts create mode 100644 packages/vite/src/node/optimizer/rolldownDepPlugin.ts diff --git a/packages/vite/package.json b/packages/vite/package.json index 9d7e3abf004ade..f413a228ab4fad 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -88,6 +88,7 @@ "dependencies": { "esbuild": "^0.24.0", "postcss": "^8.4.48", + "rolldown": "https://pkg.pr.new/rolldown@dab103f", "rollup": "^4.23.0" }, "optionalDependencies": { diff --git a/packages/vite/rollup.config.ts b/packages/vite/rollup.config.ts index 1cf3015f15d52f..d4f1423a6cef97 100644 --- a/packages/vite/rollup.config.ts +++ b/packages/vite/rollup.config.ts @@ -112,6 +112,7 @@ const nodeConfig = defineConfig({ 'fsevents', 'rollup/parseAst', /^tsx\//, + 'rolldown/experimental', ...Object.keys(pkg.dependencies), ...Object.keys(pkg.peerDependencies), ], diff --git a/packages/vite/rollup.dts.config.ts b/packages/vite/rollup.dts.config.ts index fd09273f780e8f..ff8a800f50b142 100644 --- a/packages/vite/rollup.dts.config.ts +++ b/packages/vite/rollup.dts.config.ts @@ -51,6 +51,7 @@ const identifierReplacements: Record> = { PluginContext$1: 'rollup.PluginContext', TransformPluginContext$1: 'rollup.TransformPluginContext', TransformResult$2: 'rollup.TransformResult', + RollupOptions$1: 'rollup.RollupOptions', }, esbuild: { TransformResult$1: 'esbuild_TransformResult', diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 946e013bab82aa..67f812628769e5 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -690,6 +690,7 @@ export const configDefaults = Object.freeze({ exclude: [], needsInterop: [], // esbuildOptions + rollupOptions: {}, /** @experimental */ extensions: [], /** @deprecated @experimental */ diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts deleted file mode 100644 index 7afbb7fbdea82f..00000000000000 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ /dev/null @@ -1,347 +0,0 @@ -import path from 'node:path' -import type { ImportKind, Plugin } from 'esbuild' -import { JS_TYPES_RE, KNOWN_ASSET_TYPES } from '../constants' -import type { PackageCache } from '../packages' -import { - escapeRegex, - flattenId, - isBuiltin, - isExternalUrl, - moduleListContains, - normalizePath, -} from '../utils' -import { browserExternalId, optionalPeerDepId } from '../plugins/resolve' -import { isCSSRequest, isModuleCSSRequest } from '../plugins/css' -import type { Environment } from '../environment' -import { createBackCompatIdResolver } from '../idResolver' - -const externalWithConversionNamespace = - 'vite:dep-pre-bundle:external-conversion' -const convertedExternalPrefix = 'vite-dep-pre-bundle-external:' - -const cjsExternalFacadeNamespace = 'vite:cjs-external-facade' -const nonFacadePrefix = 'vite-cjs-external-facade:' - -const externalTypes = [ - 'css', - // supported pre-processor types - 'less', - 'sass', - 'scss', - 'styl', - 'stylus', - 'pcss', - 'postcss', - // wasm - 'wasm', - // known SFC types - 'vue', - 'svelte', - 'marko', - 'astro', - 'imba', - // JSX/TSX may be configured to be compiled differently from how esbuild - // handles it by default, so exclude them as well - 'jsx', - 'tsx', - ...KNOWN_ASSET_TYPES, -] - -export function esbuildDepPlugin( - environment: Environment, - qualified: Record, - external: string[], -): Plugin { - const { isProduction } = environment.config - const { extensions } = environment.config.optimizeDeps - - // remove optimizable extensions from `externalTypes` list - const allExternalTypes = extensions - ? externalTypes.filter((type) => !extensions?.includes('.' + type)) - : externalTypes - - // use separate package cache for optimizer as it caches paths around node_modules - // and it's unlikely for the core Vite process to traverse into node_modules again - const esmPackageCache: PackageCache = new Map() - const cjsPackageCache: PackageCache = new Map() - - // default resolver which prefers ESM - const _resolve = createBackCompatIdResolver(environment.getTopLevelConfig(), { - asSrc: false, - scan: true, - packageCache: esmPackageCache, - }) - - // cjs resolver that prefers Node - const _resolveRequire = createBackCompatIdResolver( - environment.getTopLevelConfig(), - { - asSrc: false, - isRequire: true, - scan: true, - packageCache: cjsPackageCache, - }, - ) - - const resolve = ( - id: string, - importer: string, - kind: ImportKind, - resolveDir?: string, - ): Promise => { - let _importer: string - // explicit resolveDir - this is passed only during yarn pnp resolve for - // entries - if (resolveDir) { - _importer = normalizePath(path.join(resolveDir, '*')) - } else { - // map importer ids to file paths for correct resolution - _importer = importer in qualified ? qualified[importer] : importer - } - const resolver = kind.startsWith('require') ? _resolveRequire : _resolve - return resolver(environment, id, _importer) - } - - const resolveResult = (id: string, resolved: string) => { - if (resolved.startsWith(browserExternalId)) { - return { - path: id, - namespace: 'browser-external', - } - } - if (resolved.startsWith(optionalPeerDepId)) { - return { - path: resolved, - namespace: 'optional-peer-dep', - } - } - if (environment.config.consumer === 'server' && isBuiltin(resolved)) { - return - } - if (isExternalUrl(resolved)) { - return { - path: resolved, - external: true, - } - } - return { - path: path.resolve(resolved), - } - } - - return { - name: 'vite:dep-pre-bundle', - setup(build) { - // clear package cache when esbuild is finished - build.onEnd(() => { - esmPackageCache.clear() - cjsPackageCache.clear() - }) - - // externalize assets and commonly known non-js file types - // See #8459 for more details about this require-import conversion - build.onResolve( - { - filter: new RegExp( - `\\.(` + allExternalTypes.join('|') + `)(\\?.*)?$`, - ), - }, - async ({ path: id, importer, kind }) => { - // if the prefix exist, it is already converted to `import`, so set `external: true` - if (id.startsWith(convertedExternalPrefix)) { - return { - path: id.slice(convertedExternalPrefix.length), - external: true, - } - } - - const resolved = await resolve(id, importer, kind) - if (resolved) { - // `resolved` can be javascript even when `id` matches `allExternalTypes` - // due to cjs resolution (e.g. require("./test.pdf") for "./test.pdf.js") - // or package name (e.g. import "some-package.pdf") - if (JS_TYPES_RE.test(resolved)) { - return { - path: resolved, - external: false, - } - } - - if (kind === 'require-call') { - // here it is not set to `external: true` to convert `require` to `import` - return { - path: resolved, - namespace: externalWithConversionNamespace, - } - } - return { - path: resolved, - external: true, - } - } - }, - ) - build.onLoad( - { filter: /./, namespace: externalWithConversionNamespace }, - (args) => { - // import itself with prefix (this is the actual part of require-import conversion) - const modulePath = `"${convertedExternalPrefix}${args.path}"` - return { - contents: - isCSSRequest(args.path) && !isModuleCSSRequest(args.path) - ? `import ${modulePath};` - : `export { default } from ${modulePath};` + - `export * from ${modulePath};`, - loader: 'js', - } - }, - ) - - function resolveEntry(id: string) { - const flatId = flattenId(id) - if (flatId in qualified) { - return { - path: qualified[flatId], - } - } - } - - build.onResolve( - { filter: /^[\w@][^:]/ }, - async ({ path: id, importer, kind }) => { - if (moduleListContains(external, id)) { - return { - path: id, - external: true, - } - } - - // ensure esbuild uses our resolved entries - let entry: { path: string } | undefined - // if this is an entry, return entry namespace resolve result - if (!importer) { - if ((entry = resolveEntry(id))) return entry - // check if this is aliased to an entry - also return entry namespace - const aliased = await _resolve(environment, id, undefined, true) - if (aliased && (entry = resolveEntry(aliased))) { - return entry - } - } - - // use vite's own resolver - const resolved = await resolve(id, importer, kind) - if (resolved) { - return resolveResult(id, resolved) - } - }, - ) - - build.onLoad( - { filter: /.*/, namespace: 'browser-external' }, - ({ path }) => { - if (isProduction) { - return { - contents: 'module.exports = {}', - } - } else { - return { - // Return in CJS to intercept named imports. Use `Object.create` to - // create the Proxy in the prototype to workaround esbuild issue. Why? - // - // In short, esbuild cjs->esm flow: - // 1. Create empty object using `Object.create(Object.getPrototypeOf(module.exports))`. - // 2. Assign props of `module.exports` to the object. - // 3. Return object for ESM use. - // - // If we do `module.exports = new Proxy({}, {})`, step 1 returns empty object, - // step 2 does nothing as there's no props for `module.exports`. The final object - // is just an empty object. - // - // Creating the Proxy in the prototype satisfies step 1 immediately, which means - // the returned object is a Proxy that we can intercept. - // - // Note: Skip keys that are accessed by esbuild and browser devtools. - contents: `\ -module.exports = Object.create(new Proxy({}, { - get(_, key) { - if ( - key !== '__esModule' && - key !== '__proto__' && - key !== 'constructor' && - key !== 'splice' - ) { - console.warn(\`Module "${path}" has been externalized for browser compatibility. Cannot access "${path}.\${key}" in client code. See https://vite.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibility for more details.\`) - } - } -}))`, - } - } - }, - ) - - build.onLoad( - { filter: /.*/, namespace: 'optional-peer-dep' }, - ({ path }) => { - if (isProduction) { - return { - contents: 'module.exports = {}', - } - } else { - const [, peerDep, parentDep] = path.split(':') - return { - contents: `throw new Error(\`Could not resolve "${peerDep}" imported by "${parentDep}". Is it installed?\`)`, - } - } - }, - ) - }, - } -} - -const matchesEntireLine = (text: string) => `^${escapeRegex(text)}$` - -// esbuild doesn't transpile `require('foo')` into `import` statements if 'foo' is externalized -// https://github.com/evanw/esbuild/issues/566#issuecomment-735551834 -export function esbuildCjsExternalPlugin( - externals: string[], - platform: 'node' | 'browser' | 'neutral', -): Plugin { - return { - name: 'cjs-external', - setup(build) { - const filter = new RegExp(externals.map(matchesEntireLine).join('|')) - - build.onResolve({ filter: new RegExp(`^${nonFacadePrefix}`) }, (args) => { - return { - path: args.path.slice(nonFacadePrefix.length), - external: true, - } - }) - - build.onResolve({ filter }, (args) => { - // preserve `require` for node because it's more accurate than converting it to import - if (args.kind === 'require-call' && platform !== 'node') { - return { - path: args.path, - namespace: cjsExternalFacadeNamespace, - } - } - - return { - path: args.path, - external: true, - } - }) - - build.onLoad( - { filter: /.*/, namespace: cjsExternalFacadeNamespace }, - (args) => ({ - contents: - `import * as m from ${JSON.stringify( - nonFacadePrefix + args.path, - )};` + `module.exports = m;`, - }), - ) - }, - } -} diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index b859319c0a81e3..028f156dd3717a 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -4,12 +4,13 @@ import path from 'node:path' import { promisify } from 'node:util' import { performance } from 'node:perf_hooks' import colors from 'picocolors' -import type { BuildContext, BuildOptions as EsbuildBuildOptions } from 'esbuild' -import esbuild, { build } from 'esbuild' +import type { BuildOptions as EsbuildBuildOptions } from 'esbuild' import { init, parse } from 'es-module-lexer' import { isDynamicPattern } from 'tinyglobby' +import { type RollupOptions, type RollupOutput, rolldown } from 'rolldown' import type { ResolvedConfig } from '../config' import { + asyncFlatten, createDebugger, flattenId, getHash, @@ -21,21 +22,20 @@ import { tryStatSync, unique, } from '../utils' -import { - defaultEsbuildSupported, - transformWithEsbuild, -} from '../plugins/esbuild' +import { transformWithEsbuild } from '../plugins/esbuild' import { ESBUILD_MODULES_TARGET, METADATA_FILENAME } from '../constants' import { isWindows } from '../../shared/utils' import type { Environment } from '../environment' -import { esbuildCjsExternalPlugin, esbuildDepPlugin } from './esbuildDepPlugin' import { ScanEnvironment, scanImports } from './scan' import { createOptimizeDepsIncludeResolver, expandGlobIds } from './resolve' +import { + rolldownCjsExternalPlugin, + rolldownDepPlugin, +} from './rolldownDepPlugin' const debug = createDebugger('vite:deps') const jsExtensionRE = /\.js$/i -const jsMapExtensionRE = /\.js\.map$/i export type ExportsData = { hasModuleSyntax: boolean @@ -103,6 +103,7 @@ export interface DepOptimizationConfig { | 'outExtension' | 'metafile' > + rollupOptions?: RollupOptions /** * List of file extensions that can be optimized. A corresponding esbuild * plugin must exist to handle the specific extension. @@ -201,6 +202,7 @@ export interface OptimizedDepInfo { * data used both to define if interop is needed and when pre-bundling */ exportsData?: Promise + isDynamicEntry?: boolean } export interface DepOptimizationMetadata { @@ -594,7 +596,7 @@ export function runOptimizeDeps( const start = performance.now() - const preparedRun = prepareEsbuildOptimizerRun( + const preparedRun = prepareRolldownOptimizerRun( environment, depsInfo, processingCacheDir, @@ -602,63 +604,44 @@ export function runOptimizeDeps( ) const runResult = preparedRun.then(({ context, idToExports }) => { - function disposeContext() { - return context?.dispose().catch((e) => { - environment.logger.error('Failed to dispose esbuild context', { - error: e, - }) - }) - } if (!context || optimizerContext.cancelled) { - disposeContext() return cancelledResult } return context - .rebuild() + .build() .then((result) => { - const meta = result.metafile! - - // the paths in `meta.outputs` are relative to `process.cwd()` - const processingCacheDirOutputPath = path.relative( - process.cwd(), - processingCacheDir, - ) - - for (const id in depsInfo) { - const output = esbuildOutputFromId( - meta.outputs, - id, - processingCacheDir, - ) - - const { exportsData, ...info } = depsInfo[id] - addOptimizedDepInfo(metadata, 'optimized', { - ...info, - // We only need to hash the output.imports in to check for stability, but adding the hash - // and file path gives us a unique hash that may be useful for other things in the future - fileHash: getHash( - metadata.hash + - depsInfo[id].file + - JSON.stringify(output.imports), - ), - browserHash: metadata.browserHash, - // After bundling we have more information and can warn the user about legacy packages - // that require manual configuration - needsInterop: needsInterop( - environment, - id, - idToExports[id], - output, - ), - }) - } + for (const chunk of result.output) { + if (chunk.type !== 'chunk') continue - for (const o of Object.keys(meta.outputs)) { - if (!jsMapExtensionRE.test(o)) { - const id = path - .relative(processingCacheDirOutputPath, o) - .replace(jsExtensionRE, '') + if (chunk.isEntry) { + // One chunk maybe corresponding multiply entry + const deps = Object.values(depsInfo).filter( + (d) => d.src === normalizePath(chunk.facadeModuleId!), + ) + for (const { exportsData, file, id, ...info } of deps) { + addOptimizedDepInfo(metadata, 'optimized', { + id, + file, + ...info, + // We only need to hash the output.imports in to check for stability, but adding the hash + // and file path gives us a unique hash that may be useful for other things in the future + fileHash: getHash( + metadata.hash + file + JSON.stringify(chunk.modules), + ), + browserHash: metadata.browserHash, + // After bundling we have more information and can warn the user about legacy packages + // that require manual configuration + needsInterop: needsInterop( + environment, + id, + idToExports[id], + chunk, + ), + }) + } + } else { + const id = chunk.fileName.replace(jsExtensionRE, '') const file = getOptimizedDepPath(environment, id) if ( !findOptimizedDepInfoInRecord( @@ -671,6 +654,7 @@ export function runOptimizeDeps( file, needsInterop: false, browserHash: metadata.browserHash, + isDynamicEntry: chunk.isDynamicEntry, }) } } @@ -682,18 +666,14 @@ export function runOptimizeDeps( return successfulResult }) - .catch((e) => { if (e.errors && e.message.includes('The build was canceled')) { - // esbuild logs an error when cancelling, but this is expected so + // an error happens when cancelling, but this is expected so // return an empty result instead return cancelledResult } throw e }) - .finally(() => { - return disposeContext() - }) }) runResult.catch(() => { @@ -704,20 +684,20 @@ export function runOptimizeDeps( async cancel() { optimizerContext.cancelled = true const { context } = await preparedRun - await context?.cancel() + context?.cancel() cleanUp() }, result: runResult, } } -async function prepareEsbuildOptimizerRun( +async function prepareRolldownOptimizerRun( environment: Environment, depsInfo: Record, processingCacheDir: string, optimizerContext: { cancelled: boolean }, ): Promise<{ - context?: BuildContext + context?: { build: () => Promise; cancel: () => void } idToExports: Record }> { // esbuild generates nested directory output with lowest common ancestor base @@ -731,21 +711,19 @@ async function prepareEsbuildOptimizerRun( const { optimizeDeps } = environment.config - const { plugins: pluginsFromConfig = [], ...esbuildOptions } = - optimizeDeps?.esbuildOptions ?? {} + const { plugins: pluginsFromConfig = [], ...rollupOptions } = + optimizeDeps?.rollupOptions ?? {} + let jsxLoader = false await Promise.all( Object.keys(depsInfo).map(async (id) => { const src = depsInfo[id].src! const exportsData = await (depsInfo[id].exportsData ?? extractExportsData(environment, src)) - if (exportsData.jsxLoader && !esbuildOptions.loader?.['.js']) { + if (exportsData.jsxLoader) { // Ensure that optimization won't fail by defaulting '.js' to the JSX parser. // This is useful for packages such as Gatsby. - esbuildOptions.loader = { - '.js': 'jsx', - ...esbuildOptions.loader, - } + jsxLoader = true } const flatId = flattenId(id) flatIdDeps[flatId] = src @@ -762,7 +740,7 @@ async function prepareEsbuildOptimizerRun( } const platform = - optimizeDeps.esbuildOptions?.platform ?? + optimizeDeps.rollupOptions?.platform ?? // We generally don't want to use platform 'neutral', as esbuild has custom handling // when the platform is 'node' or 'browser' that can't be emulated by using mainFields // and conditions @@ -773,43 +751,68 @@ async function prepareEsbuildOptimizerRun( const external = [...(optimizeDeps?.exclude ?? [])] - const plugins = [...pluginsFromConfig] + const plugins = await asyncFlatten(pluginsFromConfig) if (external.length) { - plugins.push(esbuildCjsExternalPlugin(external, platform)) + plugins.push(rolldownCjsExternalPlugin(external, platform)) } - plugins.push(esbuildDepPlugin(environment, flatIdDeps, external)) - - const context = await esbuild.context({ - absWorkingDir: process.cwd(), - entryPoints: Object.keys(flatIdDeps), - bundle: true, - platform, - define, - format: 'esm', - // See https://github.com/evanw/esbuild/issues/1921#issuecomment-1152991694 - banner: - platform === 'node' - ? { - js: `import { createRequire } from 'module';const require = createRequire(import.meta.url);`, - } - : undefined, - target: ESBUILD_MODULES_TARGET, - external, - logLevel: 'error', - splitting: true, - sourcemap: true, - outdir: processingCacheDir, - ignoreAnnotations: true, - metafile: true, - plugins, - charset: 'utf8', - ...esbuildOptions, - supported: { - ...defaultEsbuildSupported, - ...esbuildOptions.supported, + plugins.push(rolldownDepPlugin(environment, flatIdDeps, external)) + plugins.push({ + name: 'optimizer-transform', + async transform(code, id) { + if (/\.(?:m?[jt]s|[jt]sx)$/.test(id)) { + const result = await transformWithEsbuild(code, id, { + sourcemap: true, + sourcefile: id, + loader: jsxLoader && /\.js$/.test(id) ? 'jsx' : undefined, + define, + target: ESBUILD_MODULES_TARGET, + }) + return { + code: result.code, + map: result.map, + } + } }, }) - return { context, idToExports } + + let canceled = false + async function build() { + const bundle = await rolldown({ + input: flatIdDeps, + logLevel: 'warn', + plugins, + resolve: { + // TODO: set aliasFields, conditionNames depending on `platform` + mainFields: ['module', 'main'], + aliasFields: [['browser']], + extensions: ['.js', '.css'], + conditionNames: ['browser'], + }, + ...rollupOptions, + }) + if (canceled) { + await bundle.close() + throw new Error('The build was canceled') + } + const result = await bundle.write({ + format: 'esm', + sourcemap: true, + dir: processingCacheDir, + banner: + platform === 'node' + ? `import { createRequire } from 'module';const require = createRequire(import.meta.url);` + : undefined, + ...rollupOptions.output, + }) + await bundle.close() + return result + } + + function cancel() { + canceled = true + } + + return { context: { build, cancel }, idToExports } } export async function addManuallyIncludedOptimizeDeps( @@ -1006,19 +1009,23 @@ function stringifyDepsOptimizerMetadata( browserHash, optimized: Object.fromEntries( Object.values(optimized).map( - ({ id, src, file, fileHash, needsInterop }) => [ + ({ id, src, file, fileHash, needsInterop, isDynamicEntry }) => [ id, { src, file, fileHash, needsInterop, + isDynamicEntry, }, ], ), ), chunks: Object.fromEntries( - Object.values(chunks).map(({ id, file }) => [id, { file }]), + Object.values(chunks).map(({ id, file, isDynamicEntry }) => [ + id, + { file, isDynamicEntry }, + ]), ), }, (key: string, value: string) => { @@ -1033,29 +1040,6 @@ function stringifyDepsOptimizerMetadata( ) } -function esbuildOutputFromId( - outputs: Record, - id: string, - cacheDirOutputPath: string, -): any { - const cwd = process.cwd() - const flatId = flattenId(id) + '.js' - const normalizedOutputPath = normalizePath( - path.relative(cwd, path.join(cacheDirOutputPath, flatId)), - ) - const output = outputs[normalizedOutputPath] - if (output) { - return output - } - // If the root dir was symlinked, esbuild could return output keys as `../cwd/` - // Normalize keys to support this case too - for (const [key, value] of Object.entries(outputs)) { - if (normalizePath(path.relative(cwd, key)) === normalizedOutputPath) { - return value - } - } -} - export async function extractExportsData( environment: Environment, filePath: string, @@ -1064,18 +1048,32 @@ export async function extractExportsData( const { optimizeDeps } = environment.config - const esbuildOptions = optimizeDeps?.esbuildOptions ?? {} + const rollupOptions = optimizeDeps?.rollupOptions ?? {} if (optimizeDeps.extensions?.some((ext) => filePath.endsWith(ext))) { // For custom supported extensions, build the entry file to transform it into JS, // and then parse with es-module-lexer. Note that the `bundle` option is not `true`, // so only the entry file is being transformed. - const result = await build({ - ...esbuildOptions, - entryPoints: [filePath], - write: false, + const { plugins: pluginsFromConfig = [], ...remainingRollupOptions } = + rollupOptions + const plugins = await asyncFlatten(pluginsFromConfig) + plugins.unshift({ + name: 'externalize', + resolveId(id, importer) { + if (importer !== undefined) { + return { id, external: true } + } + }, + }) + const build = await rolldown({ + ...remainingRollupOptions, + plugins, + input: [filePath], + }) + const result = await build.generate({ + ...rollupOptions.output, format: 'esm', }) - const [, exports, , hasModuleSyntax] = parse(result.outputFiles[0].text) + const [, exports, , hasModuleSyntax] = parse(result.output[0].code) return { hasModuleSyntax, exports: exports.map((e) => e.n), @@ -1089,7 +1087,7 @@ export async function extractExportsData( try { parseResult = parse(entryContent) } catch { - const loader = esbuildOptions.loader?.[path.extname(filePath)] || 'jsx' + const loader = rollupOptions.moduleTypes?.[path.extname(filePath)] || 'jsx' debug?.( `Unable to parse: ${filePath}.\n Trying again with a ${loader} transform.`, ) diff --git a/packages/vite/src/node/optimizer/rolldownDepPlugin.ts b/packages/vite/src/node/optimizer/rolldownDepPlugin.ts new file mode 100644 index 00000000000000..072506e71df905 --- /dev/null +++ b/packages/vite/src/node/optimizer/rolldownDepPlugin.ts @@ -0,0 +1,336 @@ +import path from 'node:path' +import type { ImportKind, Plugin } from 'rolldown' +import { JS_TYPES_RE, KNOWN_ASSET_TYPES } from '../constants' +import type { PackageCache } from '../packages' +import { + escapeRegex, + flattenId, + isBuiltin, + isExternalUrl, + moduleListContains, + normalizePath, +} from '../utils' +import { browserExternalId, optionalPeerDepId } from '../plugins/resolve' +import { isCSSRequest, isModuleCSSRequest } from '../plugins/css' +import type { Environment } from '../environment' +import { createBackCompatIdResolver } from '../idResolver' + +const externalWithConversionNamespace = + 'vite:dep-pre-bundle:external-conversion' +const convertedExternalPrefix = 'vite-dep-pre-bundle-external:' + +const cjsExternalFacadeNamespace = 'vite:cjs-external-facade' +const nonFacadePrefix = 'vite-cjs-external-facade:' + +const externalTypes = [ + 'css', + // supported pre-processor types + 'less', + 'sass', + 'scss', + 'styl', + 'stylus', + 'pcss', + 'postcss', + // wasm + 'wasm', + // known SFC types + 'vue', + 'svelte', + 'marko', + 'astro', + 'imba', + // JSX/TSX may be configured to be compiled differently from how esbuild + // handles it by default, so exclude them as well + 'jsx', + 'tsx', + ...KNOWN_ASSET_TYPES, +] + +const optionalPeerDepNamespace = 'optional-peer-dep:' +const browserExternalNamespace = 'browser-external:' + +export function rolldownDepPlugin( + environment: Environment, + qualified: Record, + external: string[], +): Plugin { + const { isProduction } = environment.config + const { extensions } = environment.config.optimizeDeps + + // remove optimizable extensions from `externalTypes` list + const allExternalTypes = extensions + ? externalTypes.filter((type) => !extensions?.includes('.' + type)) + : externalTypes + + // use separate package cache for optimizer as it caches paths around node_modules + // and it's unlikely for the core Vite process to traverse into node_modules again + const esmPackageCache: PackageCache = new Map() + const cjsPackageCache: PackageCache = new Map() + + // default resolver which prefers ESM + const _resolve = createBackCompatIdResolver(environment.getTopLevelConfig(), { + asSrc: false, + scan: true, + packageCache: esmPackageCache, + }) + + // cjs resolver that prefers Node + const _resolveRequire = createBackCompatIdResolver( + environment.getTopLevelConfig(), + { + asSrc: false, + isRequire: true, + scan: true, + packageCache: cjsPackageCache, + }, + ) + + const resolve = ( + id: string, + importer: string | undefined, + kind: ImportKind, + resolveDir?: string, + ): Promise => { + let _importer: string | undefined + // explicit resolveDir - this is passed only during yarn pnp resolve for + // entries + if (resolveDir) { + _importer = normalizePath(path.join(resolveDir, '*')) + } else if (importer) { + // map importer ids to file paths for correct resolution + _importer = importer in qualified ? qualified[importer] : importer + } + const resolver = kind.startsWith('require') ? _resolveRequire : _resolve + return resolver(environment, id, _importer) + } + + const resolveResult = (id: string, resolved: string) => { + if (resolved.startsWith(browserExternalId)) { + return { + id: browserExternalNamespace + id, + } + } + if (resolved.startsWith(optionalPeerDepId)) { + return { + id: optionalPeerDepNamespace + resolved, + } + } + if (environment.config.consumer === 'server' && isBuiltin(resolved)) { + return + } + if (isExternalUrl(resolved)) { + return { + id: resolved, + external: true, + } + } + return { + id: path.resolve(resolved), + } + } + + const allExternalTypesReg = new RegExp( + `\\.(` + allExternalTypes.join('|') + `)(\\?.*)?$`, + ) + + function resolveEntry(id: string) { + const flatId = flattenId(id) + if (flatId in qualified) { + return { + id: qualified[flatId], + } + } + } + + return { + name: 'vite:dep-pre-bundle', + // clear package cache when build is finished + buildEnd() { + esmPackageCache.clear() + cjsPackageCache.clear() + }, + resolveId: async function (id, importer, options) { + const kind = options.kind + // externalize assets and commonly known non-js file types + // See #8459 for more details about this require-import conversion + if (allExternalTypesReg.test(id)) { + // if the prefix exist, it is already converted to `import`, so set `external: true` + if (id.startsWith(convertedExternalPrefix)) { + return { + id: id.slice(convertedExternalPrefix.length), + external: true, + } + } + + const resolved = await resolve(id, importer, kind) + if (resolved) { + // `resolved` can be javascript even when `id` matches `allExternalTypes` + // due to cjs resolution (e.g. require("./test.pdf") for "./test.pdf.js") + // or package name (e.g. import "some-package.pdf") + if (JS_TYPES_RE.test(resolved)) { + return { + id: resolved, + external: false, + } + } + + if (kind === 'require-call') { + // here it is not set to `external: true` to convert `require` to `import` + return { + id: externalWithConversionNamespace + resolved, + } + } + return { + id: resolved, + external: true, + } + } + } + + if (/^[\w@][^:]/.test(id)) { + if (moduleListContains(external, id)) { + return { + id: id, + external: true, + } + } + + // ensure esbuild uses our resolved entries + let entry: { id: string } | undefined + // if this is an entry, return entry namespace resolve result + if (!importer) { + if ((entry = resolveEntry(id))) return entry + // check if this is aliased to an entry - also return entry namespace + const aliased = await _resolve(environment, id, undefined, true) + if (aliased && (entry = resolveEntry(aliased))) { + return entry + } + } + + // use vite's own resolver + const resolved = await resolve(id, importer, kind) + if (resolved) { + return resolveResult(id, resolved) + } + } + }, + load(id) { + if (id.startsWith(externalWithConversionNamespace)) { + const path = id.slice(externalWithConversionNamespace.length) + // import itself with prefix (this is the actual part of require-import conversion) + const modulePath = `"${convertedExternalPrefix}${path}"` + return { + code: + isCSSRequest(path) && !isModuleCSSRequest(path) + ? `import ${modulePath};` + : `export { default } from ${modulePath};` + + `export * from ${modulePath};`, + } + } + + if (id.startsWith(browserExternalNamespace)) { + const path = id.slice(browserExternalNamespace.length) + if (isProduction) { + return { + code: 'module.exports = {}', + } + } else { + return { + // Return in CJS to intercept named imports. Use `Object.create` to + // create the Proxy in the prototype to workaround esbuild issue. Why? + // + // In short, esbuild cjs->esm flow: + // 1. Create empty object using `Object.create(Object.getPrototypeOf(module.exports))`. + // 2. Assign props of `module.exports` to the object. + // 3. Return object for ESM use. + // + // If we do `module.exports = new Proxy({}, {})`, step 1 returns empty object, + // step 2 does nothing as there's no props for `module.exports`. The final object + // is just an empty object. + // + // Creating the Proxy in the prototype satisfies step 1 immediately, which means + // the returned object is a Proxy that we can intercept. + // + // Note: Skip keys that are accessed by esbuild and browser devtools. + code: `\ +module.exports = Object.create(new Proxy({}, { + get(_, key) { + if ( + key !== '__esModule' && + key !== '__proto__' && + key !== 'constructor' && + key !== 'splice' + ) { + console.warn(\`Module "${path}" has been externalized for browser compatibility. Cannot access "${path}.\${key}" in client code. See http://vite.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibility for more details.\`) + } + } +}))`, + } + } + } + + if (id.startsWith(optionalPeerDepNamespace)) { + if (isProduction) { + return { + code: 'module.exports = {}', + } + } else { + const path = id.slice(externalWithConversionNamespace.length) + const [, peerDep, parentDep] = path.split(':') + return { + code: `throw new Error(\`Could not resolve "${peerDep}" imported by "${parentDep}". Is it installed?\`)`, + } + } + } + }, + } +} + +const matchesEntireLine = (text: string) => `^${escapeRegex(text)}$` + +// esbuild doesn't transpile `require('foo')` into `import` statements if 'foo' is externalized +// https://github.com/evanw/esbuild/issues/566#issuecomment-735551834 +export function rolldownCjsExternalPlugin( + externals: string[], + platform: 'node' | 'browser' | 'neutral', +): Plugin { + const filter = new RegExp(externals.map(matchesEntireLine).join('|')) + + return { + name: 'cjs-external', + resolveId(id, _importer, options) { + if (id.startsWith(nonFacadePrefix)) { + return { + id: id.slice(nonFacadePrefix.length), + external: true, + } + } + + if (filter.test(id)) { + const kind = options.kind + // preserve `require` for node because it's more accurate than converting it to import + if (kind === 'require-call' && platform !== 'node') { + return { + id: cjsExternalFacadeNamespace + id, + } + } + + return { + id, + external: true, + } + } + }, + load(id) { + if (id.startsWith(cjsExternalFacadeNamespace)) { + return { + code: + `import * as m from ${JSON.stringify( + nonFacadePrefix + id.slice(cjsExternalFacadeNamespace.length), + )};` + `module.exports = m;`, + } + } + }, + } +} diff --git a/packages/vite/src/node/optimizer/scan.ts b/packages/vite/src/node/optimizer/scan.ts index 10bb9960cb759e..5c9df43d7e530e 100644 --- a/packages/vite/src/node/optimizer/scan.ts +++ b/packages/vite/src/node/optimizer/scan.ts @@ -2,17 +2,13 @@ import fs from 'node:fs' import fsp from 'node:fs/promises' import path from 'node:path' import { performance } from 'node:perf_hooks' -import type { - BuildContext, - Loader, - OnLoadArgs, - OnLoadResult, - Plugin, -} from 'esbuild' -import esbuild, { formatMessages, transform } from 'esbuild' +import type { Loader } from 'esbuild' +import { transform } from 'esbuild' import type { PartialResolvedId } from 'rollup' import colors from 'picocolors' import { glob, isDynamicPattern } from 'tinyglobby' +import type { Plugin } from 'rolldown' +import { scan } from 'rolldown/experimental' import { CSS_LANGS_RE, JS_TYPES_RE, @@ -21,6 +17,7 @@ import { } from '../constants' import { arraify, + asyncFlatten, createDebugger, dataUrlRE, externalRE, @@ -112,7 +109,7 @@ type ResolveIdOptions = Omit< const debug = createDebugger('vite:deps') -const htmlTypesRE = /\.(html|vue|svelte|astro|imba)$/ +const htmlTypesRE = /\.(?:html|vue|svelte|astro|imba)$/ // A simple regex to detect import sources. This is only used on // + const filePath = id.replace(normalizePath(config.root), '') + addToHTMLProxyCache(config, filePath, inlineModuleIndex, { + code: contents, + }) + js += `\nimport "${id}?html-proxy&index=${inlineModuleIndex}.js"` + shouldRemove = true + } + everyScriptIsAsync &&= isAsync + someScriptsAreAsync ||= isAsync + someScriptsAreDefer ||= !isAsync + } else if (url && !isPublicFile) { + if (!isExcludedUrl(url)) { + config.logger.warn( + ` - const filePath = id.replace(normalizePath(config.root), '') - addToHTMLProxyCache(config, filePath, inlineModuleIndex, { - code: contents, - }) - js += `\nimport "${id}?html-proxy&index=${inlineModuleIndex}.js"` - shouldRemove = true - } - - everyScriptIsAsync &&= isAsync - someScriptsAreAsync ||= isAsync - someScriptsAreDefer ||= !isAsync - } else if (url && !isPublicFile) { - if (!isExcludedUrl(url)) { - config.logger.warn( - ` asset - for (const { start, end, url } of scriptUrls) { - if (checkPublicFile(url, config)) { - s.update( - start, - end, - partialEncodeURIPath(toOutputPublicFilePath(url)), - ) - } else if (!isExcludedUrl(url)) { - s.update( - start, - end, - partialEncodeURIPath(await urlToBuiltUrl(this, url, id)), - ) + // emit asset + for (const { start, end, url } of scriptUrls) { + if (checkPublicFile(url, config)) { + s.update( + start, + end, + partialEncodeURIPath(toOutputPublicFilePath(url)), + ) + } else if (!isExcludedUrl(url)) { + s.update( + start, + end, + partialEncodeURIPath(await urlToBuiltUrl(this, url, id)), + ) + } } - } - // ignore if its url can't be resolved - const resolvedStyleUrls = await Promise.all( - styleUrls.map(async (styleUrl) => ({ - ...styleUrl, - resolved: await this.resolve(styleUrl.url, id), - })), - ) - for (const { start, end, url, resolved } of resolvedStyleUrls) { - if (resolved == null) { - config.logger.warnOnce( - `\n${url} doesn't exist at build time, it will remain unchanged to be resolved at runtime`, - ) - const importExpression = `\nimport ${JSON.stringify(url)}` - js = js.replace(importExpression, '') - } else { - s.remove(start, end) + // ignore if its url can't be resolved + const resolvedStyleUrls = await Promise.all( + styleUrls.map(async (styleUrl) => ({ + ...styleUrl, + resolved: await this.resolve(styleUrl.url, id), + })), + ) + for (const { start, end, url, resolved } of resolvedStyleUrls) { + if (resolved == null) { + config.logger.warnOnce( + `\n${url} doesn't exist at build time, it will remain unchanged to be resolved at runtime`, + ) + const importExpression = `\nimport ${JSON.stringify(url)}` + js = js.replace(importExpression, '') + } else { + s.remove(start, end) + } } - } - processedHtml(this).set(id, s.toString()) + processedHtml(this).set(id, s.toString()) - // inject module preload polyfill only when configured and needed - const { modulePreload } = this.environment.config.build - if ( - modulePreload !== false && - modulePreload.polyfill && - (someScriptsAreAsync || someScriptsAreDefer) - ) { - js = `import "${modulePreloadPolyfillId}";\n${js}` - } + // inject module preload polyfill only when configured and needed + const { modulePreload } = this.environment.config.build + if ( + modulePreload !== false && + modulePreload.polyfill && + (someScriptsAreAsync || someScriptsAreDefer) + ) { + js = `import "${modulePreloadPolyfillId}";\n${js}` + } - await Promise.all(setModuleSideEffectPromises) + await Promise.all(setModuleSideEffectPromises) - // Force rollup to keep this module from being shared between other entry points. - // If the resulting chunk is empty, it will be removed in generateBundle. - return { code: js, moduleSideEffects: 'no-treeshake' } - } + // Force rollup to keep this module from being shared between other entry points. + // If the resulting chunk is empty, it will be removed in generateBundle. + return { code: js, moduleSideEffects: 'no-treeshake' } + } + }, }, async generateBundle(options, bundle) { diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 0ef75e28ccd2c0..7d57ed7a48f7b4 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -1,6 +1,6 @@ import path from 'node:path' import MagicString from 'magic-string' -import type { OutputChunk, RollupError } from 'rolldown' +import type { OutputChunk, RolldownPlugin, RollupError } from 'rolldown' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' import { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants' @@ -237,7 +237,7 @@ export function webWorkerPostPlugin(): Plugin { } } -export function webWorkerPlugin(config: ResolvedConfig): Plugin { +export function webWorkerPlugin(config: ResolvedConfig): RolldownPlugin { const isBuild = config.command === 'build' const isWorker = config.isWorker @@ -255,10 +255,17 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { }) }, - load(id) { - if (isBuild && workerOrSharedWorkerRE.test(id)) { - return '' - } + load: { + filter: { + id: { + include: [workerOrSharedWorkerRE], + }, + }, + handler(id) { + if (isBuild && workerOrSharedWorkerRE.test(id)) { + return '' + } + }, }, // shouldTransformCachedModule({ id }) { @@ -267,147 +274,154 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { // } // }, - async transform(raw, id) { - const workerFileMatch = workerFileRE.exec(id) - if (workerFileMatch) { - // if import worker by worker constructor will have query.type - // other type will be import worker by esm - const workerType = workerFileMatch[1] as WorkerType - let injectEnv = '' - - const scriptPath = JSON.stringify( - path.posix.join(config.base, ENV_PUBLIC_PATH), - ) + transform: { + filter: { + id: { + include: [workerOrSharedWorkerRE, workerFileRE], + }, + }, + async handler(raw, id) { + const workerFileMatch = workerFileRE.exec(id) + if (workerFileMatch) { + // if import worker by worker constructor will have query.type + // other type will be import worker by esm + const workerType = workerFileMatch[1] as WorkerType + let injectEnv = '' + + const scriptPath = JSON.stringify( + path.posix.join(config.base, ENV_PUBLIC_PATH), + ) - if (workerType === 'classic') { - injectEnv = `importScripts(${scriptPath})\n` - } else if (workerType === 'module') { - injectEnv = `import ${scriptPath}\n` - } else if (workerType === 'ignore') { - if (isBuild) { - injectEnv = '' - } else { - // dynamic worker type we can't know how import the env - // so we copy /@vite/env code of server transform result into file header - const environment = this.environment - const moduleGraph = - environment.mode === 'dev' ? environment.moduleGraph : undefined - const module = moduleGraph?.getModuleById(ENV_ENTRY) - injectEnv = module?.transformResult?.code || '' + if (workerType === 'classic') { + injectEnv = `importScripts(${scriptPath})\n` + } else if (workerType === 'module') { + injectEnv = `import ${scriptPath}\n` + } else if (workerType === 'ignore') { + if (isBuild) { + injectEnv = '' + } else { + // dynamic worker type we can't know how import the env + // so we copy /@vite/env code of server transform result into file header + const environment = this.environment + const moduleGraph = + environment.mode === 'dev' ? environment.moduleGraph : undefined + const module = moduleGraph?.getModuleById(ENV_ENTRY) + injectEnv = module?.transformResult?.code || '' + } } - } - if (injectEnv) { - const s = new MagicString(raw) - s.prepend(injectEnv + ';\n') - return { - code: s.toString(), - map: s.generateMap({ hires: 'boundary' }), + if (injectEnv) { + const s = new MagicString(raw) + s.prepend(injectEnv + ';\n') + return { + code: s.toString(), + map: s.generateMap({ hires: 'boundary' }), + } } + return } - return - } - const workerMatch = workerOrSharedWorkerRE.exec(id) - if (!workerMatch) return - - const { format } = config.worker - const workerConstructor = - workerMatch[1] === 'sharedworker' ? 'SharedWorker' : 'Worker' - const workerType = isBuild - ? format === 'es' - ? 'module' - : 'classic' - : 'module' - const workerTypeOption = `{ - ${workerType === 'module' ? `type: "module",` : ''} - name: options?.name - }` - - let urlCode: string - if (isBuild) { - if (isWorker && config.bundleChain.at(-1) === cleanUrl(id)) { - urlCode = 'self.location.href' - } else if (inlineRE.test(id)) { - const chunk = await bundleWorkerEntry(config, id) - const encodedJs = `const encodedJs = "${Buffer.from( - chunk.code, - ).toString('base64')}";` - - const code = - // Using blob URL for SharedWorker results in multiple instances of a same worker - workerConstructor === 'Worker' - ? `${encodedJs} - const decodeBase64 = (base64) => Uint8Array.from(atob(base64), c => c.charCodeAt(0)); - const blob = typeof self !== "undefined" && self.Blob && new Blob([${ - workerType === 'classic' - ? '' - : // `URL` is always available, in `Worker[type="module"]` - `'URL.revokeObjectURL(import.meta.url);',` - }decodeBase64(encodedJs)], { type: "text/javascript;charset=utf-8" }); - export default function WorkerWrapper(options) { - let objURL; - try { - objURL = blob && (self.URL || self.webkitURL).createObjectURL(blob); - if (!objURL) throw '' - const worker = new ${workerConstructor}(objURL, ${workerTypeOption}); - worker.addEventListener("error", () => { - (self.URL || self.webkitURL).revokeObjectURL(objURL); - }); - return worker; - } catch(e) { + const workerMatch = workerOrSharedWorkerRE.exec(id) + if (!workerMatch) return + + const { format } = config.worker + const workerConstructor = + workerMatch[1] === 'sharedworker' ? 'SharedWorker' : 'Worker' + const workerType = isBuild + ? format === 'es' + ? 'module' + : 'classic' + : 'module' + const workerTypeOption = `{ + ${workerType === 'module' ? `type: "module",` : ''} + name: options?.name + }` + + let urlCode: string + if (isBuild) { + if (isWorker && config.bundleChain.at(-1) === cleanUrl(id)) { + urlCode = 'self.location.href' + } else if (inlineRE.test(id)) { + const chunk = await bundleWorkerEntry(config, id) + const encodedJs = `const encodedJs = "${Buffer.from( + chunk.code, + ).toString('base64')}";` + + const code = + // Using blob URL for SharedWorker results in multiple instances of a same worker + workerConstructor === 'Worker' + ? `${encodedJs} + const decodeBase64 = (base64) => Uint8Array.from(atob(base64), c => c.charCodeAt(0)); + const blob = typeof self !== "undefined" && self.Blob && new Blob([${ + workerType === 'classic' + ? '' + : // `URL` is always available, in `Worker[type="module"]` + `'URL.revokeObjectURL(import.meta.url);',` + }decodeBase64(encodedJs)], { type: "text/javascript;charset=utf-8" }); + export default function WorkerWrapper(options) { + let objURL; + try { + objURL = blob && (self.URL || self.webkitURL).createObjectURL(blob); + if (!objURL) throw '' + const worker = new ${workerConstructor}(objURL, ${workerTypeOption}); + worker.addEventListener("error", () => { + (self.URL || self.webkitURL).revokeObjectURL(objURL); + }); + return worker; + } catch(e) { + return new ${workerConstructor}( + "data:text/javascript;base64," + encodedJs, + ${workerTypeOption} + ); + }${ + // For module workers, we should not revoke the URL until the worker runs, + // otherwise the worker fails to run + workerType === 'classic' + ? ` finally { + objURL && (self.URL || self.webkitURL).revokeObjectURL(objURL); + }` + : '' + } + }` + : `${encodedJs} + export default function WorkerWrapper(options) { return new ${workerConstructor}( "data:text/javascript;base64," + encodedJs, ${workerTypeOption} ); - }${ - // For module workers, we should not revoke the URL until the worker runs, - // otherwise the worker fails to run - workerType === 'classic' - ? ` finally { - objURL && (self.URL || self.webkitURL).revokeObjectURL(objURL); - }` - : '' } - }` - : `${encodedJs} - export default function WorkerWrapper(options) { - return new ${workerConstructor}( - "data:text/javascript;base64," + encodedJs, - ${workerTypeOption} - ); + ` + + return { + code, + // Empty sourcemap to suppress Rollup warning + map: { mappings: '' }, + } + } else { + urlCode = JSON.stringify(await workerFileToUrl(config, id)) } - ` + } else { + let url = await fileToUrl(this, cleanUrl(id)) + url = injectQuery(url, `${WORKER_FILE_ID}&type=${workerType}`) + urlCode = JSON.stringify(url) + } + if (urlRE.test(id)) { return { - code, - // Empty sourcemap to suppress Rollup warning - map: { mappings: '' }, + code: `export default ${urlCode}`, + map: { mappings: '' }, // Empty sourcemap to suppress Rollup warning } - } else { - urlCode = JSON.stringify(await workerFileToUrl(config, id)) } - } else { - let url = await fileToUrl(this, cleanUrl(id)) - url = injectQuery(url, `${WORKER_FILE_ID}&type=${workerType}`) - urlCode = JSON.stringify(url) - } - if (urlRE.test(id)) { return { - code: `export default ${urlCode}`, + code: `export default function WorkerWrapper(options) { + return new ${workerConstructor}( + ${urlCode}, + ${workerTypeOption} + ); + }`, map: { mappings: '' }, // Empty sourcemap to suppress Rollup warning } - } - - return { - code: `export default function WorkerWrapper(options) { - return new ${workerConstructor}( - ${urlCode}, - ${workerTypeOption} - ); - }`, - map: { mappings: '' }, // Empty sourcemap to suppress Rollup warning - } + }, }, renderChunk(code, chunk, outputOptions) { diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index 54f83324303399..d1e484acd54891 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -1,9 +1,8 @@ import path from 'node:path' import MagicString from 'magic-string' -import type { RollupError } from 'rolldown' +import type { RolldownPlugin, RollupError } from 'rolldown' import { stripLiteral } from 'strip-literal' import type { ResolvedConfig } from '../config' -import type { Plugin } from '../plugin' import { evalValue, injectQuery, transformStableResult } from '../utils' import { createBackCompatIdResolver } from '../idResolver' import type { ResolveIdFn } from '../idResolver' @@ -104,7 +103,9 @@ function isIncludeWorkerImportMetaUrl(code: string): boolean { return false } -export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { +export function workerImportMetaUrlPlugin( + config: ResolvedConfig, +): RolldownPlugin { const isBuild = config.command === 'build' let workerResolver: ResolveIdFn @@ -126,82 +127,89 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { // } // }, - async transform(code, id) { - if ( - this.environment.config.consumer === 'client' && - isIncludeWorkerImportMetaUrl(code) - ) { - let s: MagicString | undefined - const cleanString = stripLiteral(code) - const workerImportMetaUrlRE = - /\bnew\s+(?:Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/dg - - let match: RegExpExecArray | null - while ((match = workerImportMetaUrlRE.exec(cleanString))) { - const [[, endIndex], [expStart, expEnd], [urlStart, urlEnd]] = - match.indices! - - const rawUrl = code.slice(urlStart, urlEnd) - - // potential dynamic template string - if (rawUrl[0] === '`' && rawUrl.includes('${')) { - this.error( - `\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, - expStart, - ) - } + transform: { + filter: { + code: { + include: [/(?:new Worker|new SharedWorker)/], + }, + }, + async handler(code, id) { + if ( + this.environment.config.consumer === 'client' && + isIncludeWorkerImportMetaUrl(code) + ) { + let s: MagicString | undefined + const cleanString = stripLiteral(code) + const workerImportMetaUrlRE = + /\bnew\s+(?:Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/dg + + let match: RegExpExecArray | null + while ((match = workerImportMetaUrlRE.exec(cleanString))) { + const [[, endIndex], [expStart, expEnd], [urlStart, urlEnd]] = + match.indices! + + const rawUrl = code.slice(urlStart, urlEnd) + + // potential dynamic template string + if (rawUrl[0] === '`' && rawUrl.includes('${')) { + this.error( + `\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, + expStart, + ) + } - s ||= new MagicString(code) - const workerType = getWorkerType(code, cleanString, endIndex) - const url = rawUrl.slice(1, -1) - let file: string | undefined - if (url[0] === '.') { - file = path.resolve(path.dirname(id), url) - file = tryFsResolve(file, fsResolveOptions) ?? file - } else { - workerResolver ??= createBackCompatIdResolver(config, { - extensions: [], - tryIndex: false, - preferRelative: true, - }) - file = await workerResolver(this.environment, url, id) - file ??= - url[0] === '/' - ? slash(path.join(config.publicDir, url)) - : slash(path.resolve(path.dirname(id), url)) - } + s ||= new MagicString(code) + const workerType = getWorkerType(code, cleanString, endIndex) + const url = rawUrl.slice(1, -1) + let file: string | undefined + if (url[0] === '.') { + file = path.resolve(path.dirname(id), url) + file = tryFsResolve(file, fsResolveOptions) ?? file + } else { + workerResolver ??= createBackCompatIdResolver(config, { + extensions: [], + tryIndex: false, + preferRelative: true, + }) + file = await workerResolver(this.environment, url, id) + file ??= + url[0] === '/' + ? slash(path.join(config.publicDir, url)) + : slash(path.resolve(path.dirname(id), url)) + } - if ( - isBuild && - config.isWorker && - config.bundleChain.at(-1) === cleanUrl(file) - ) { - s.update(expStart, expEnd, 'self.location.href') - } else { - let builtUrl: string - if (isBuild) { - builtUrl = await workerFileToUrl(config, file) + if ( + isBuild && + config.isWorker && + config.bundleChain.at(-1) === cleanUrl(file) + ) { + s.update(expStart, expEnd, 'self.location.href') } else { - builtUrl = await fileToUrl(this, cleanUrl(file)) - builtUrl = injectQuery( - builtUrl, - `${WORKER_FILE_ID}&type=${workerType}`, + let builtUrl: string + if (isBuild) { + builtUrl = await workerFileToUrl(config, file) + } else { + builtUrl = await fileToUrl(this, cleanUrl(file)) + builtUrl = injectQuery( + builtUrl, + `${WORKER_FILE_ID}&type=${workerType}`, + ) + } + s.update( + expStart, + expEnd, + `new URL(/* @vite-ignore */ ${JSON.stringify(builtUrl)}, import.meta.url)`, ) } - s.update( - expStart, - expEnd, - `new URL(/* @vite-ignore */ ${JSON.stringify(builtUrl)}, import.meta.url)`, - ) } - } - if (s) { - return transformStableResult(s, id, config) - } + if (s) { + return transformStableResult(s, id, config) + } - return null - } + return null + } + }, }, } } From dbf006fb6a0edd759fa6beaae2842a74eb90dac7 Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY Date: Sat, 14 Sep 2024 18:59:59 +0800 Subject: [PATCH 13/46] =?UTF-8?q?perf:=20=E2=9A=A1=EF=B8=8F=20use=20defaul?= =?UTF-8?q?t=20resolver=20(#56)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/vite/src/node/plugins/index.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index db3cb6655a81af..3a2c4856be33c8 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -81,15 +81,17 @@ export async function resolvePlugins( }) : modulePreloadPolyfillPlugin(config) : null, - resolvePlugin({ - root: config.root, - isProduction: config.isProduction, - isBuild, - packageCache: config.packageCache, - asSrc: true, - optimizeDeps: true, - externalize: true, - }), + enableNativePlugin + ? null + : resolvePlugin({ + root: config.root, + isProduction: config.isProduction, + isBuild, + packageCache: config.packageCache, + asSrc: true, + optimizeDeps: true, + externalize: true, + }), htmlInlineProxyPlugin(config), cssPlugin(config), config.esbuild !== false From 1b9602cc9eac8b66d90880e0527f7549f9873efb Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY Date: Fri, 20 Sep 2024 21:00:05 +0800 Subject: [PATCH 14/46] =?UTF-8?q?fix:=20=F0=9F=90=9B=20lint=20(#58)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/vite/src/node/build.ts | 66 ++++++++++++++++----------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 006a70ce446a08..97b9f0179f244d 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -1018,10 +1018,10 @@ export function resolveBuildOutputs( } const warningIgnoreList = [`CIRCULAR_DEPENDENCY`, `THIS_IS_UNDEFINED`] -const dynamicImportWarningIgnoreList = [ - `Unsupported expression`, - `statically analyzed`, -] +// const dynamicImportWarningIgnoreList = [ +// `Unsupported expression`, +// `statically analyzed`, +// ] function clearLine() { const tty = process.stdout.isTTY && !process.env.CI @@ -1046,41 +1046,41 @@ export function onRollupWarning( } if (typeof warning === 'object') { - if (warning.code === 'UNRESOLVED_IMPORT') { - const id = warning.id - const exporter = warning.exporter - // throw unless it's commonjs external... - if (!id || !id.endsWith('?commonjs-external')) { - throw new Error( - `[vite]: Rollup failed to resolve import "${exporter}" from "${id}".\n` + - `This is most likely unintended because it can break your application at runtime.\n` + - `If you do want to externalize this module explicitly add it to\n` + - `\`build.rollupOptions.external\``, - ) - } - } + // if (warning.code === 'UNRESOLVED_IMPORT') { + // const id = warning.id + // const exporter = warning.exporter + // // throw unless it's commonjs external... + // if (!id || !id.endsWith('?commonjs-external')) { + // throw new Error( + // `[vite]: Rollup failed to resolve import "${exporter}" from "${id}".\n` + + // `This is most likely unintended because it can break your application at runtime.\n` + + // `If you do want to externalize this module explicitly add it to\n` + + // `\`build.rollupOptions.external\``, + // ) + // } + // } - if ( - warning.plugin === 'rollup-plugin-dynamic-import-variables' && - dynamicImportWarningIgnoreList.some((msg) => - warning.message.includes(msg), - ) - ) { - return - } + // if ( + // warning.plugin === 'rollup-plugin-dynamic-import-variables' && + // dynamicImportWarningIgnoreList.some((msg) => + // warning.message.includes(msg), + // ) + // ) { + // return + // } if (warningIgnoreList.includes(warning.code!)) { return } - if (warning.code === 'PLUGIN_WARNING') { - environment.logger.warn( - `${colors.bold( - colors.yellow(`[plugin:${warning.plugin}]`), - )} ${colors.yellow(warning.message)}`, - ) - return - } + // if (warning.code === 'PLUGIN_WARNING') { + // environment.logger.warn( + // `${colors.bold( + // colors.yellow(`[plugin:${warning.plugin}]`), + // )} ${colors.yellow(warning.message)}`, + // ) + // return + // } } warn(warnLog) From 049058dac186dd38db1b45fb4825e02a445375e7 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Tue, 24 Sep 2024 13:47:21 +0900 Subject: [PATCH 15/46] chore: bump rolldown --- packages/vite/package.json | 2 +- packages/vite/src/node/build.ts | 5 + packages/vite/src/node/optimizer/index.ts | 10 ++ packages/vite/src/node/plugins/worker.ts | 5 + .../vite/src/node/server/pluginContainer.ts | 6 +- packages/vite/src/node/utils.ts | 7 +- pnpm-lock.yaml | 106 +++++++++--------- 7 files changed, 83 insertions(+), 58 deletions(-) diff --git a/packages/vite/package.json b/packages/vite/package.json index f413a228ab4fad..a483d3a7d4f4eb 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -88,7 +88,7 @@ "dependencies": { "esbuild": "^0.24.0", "postcss": "^8.4.48", - "rolldown": "https://pkg.pr.new/rolldown@dab103f", + "rolldown": "https://pkg.pr.new/rolldown@e65437c", "rollup": "^4.23.0" }, "optionalDependencies": { diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 97b9f0179f244d..70cc65954828bc 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -635,6 +635,11 @@ async function buildEnvironment( onwarn(warning, warn) { onRollupWarning(warning, warn, environment) }, + // TODO: remove this and enable rolldown's CSS support later + moduleTypes: { + ...options.rollupOptions.moduleTypes, + '.css': 'js', + }, } /** diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index 028f156dd3717a..91be5926bb14a4 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -789,6 +789,11 @@ async function prepareRolldownOptimizerRun( conditionNames: ['browser'], }, ...rollupOptions, + // TODO: remove this and enable rolldown's CSS support later + moduleTypes: { + '.css': 'js', + ...rollupOptions.moduleTypes, + }, }) if (canceled) { await bundle.close() @@ -1068,6 +1073,11 @@ export async function extractExportsData( ...remainingRollupOptions, plugins, input: [filePath], + // TODO: remove this and enable rolldown's CSS support later + moduleTypes: { + '.css': 'js', + ...remainingRollupOptions.moduleTypes, + }, }) const result = await build.generate({ ...rollupOptions.output, diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 7d57ed7a48f7b4..dc44624f05d694 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -87,6 +87,11 @@ async function bundleWorkerEntry( onwarn(warning, warn) { onRollupWarning(warning, warn, workerEnvironment) }, + // TODO: remove this and enable rolldown's CSS support later + moduleTypes: { + '.css': 'js', + ...rollupOptions.moduleTypes, + }, // preserveEntrySignatures: false, }) let chunk: OutputChunk diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index e58c897a932745..e09b995ae5dcc6 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -73,6 +73,7 @@ import { normalizePath, numberToPos, prettifyUrl, + rolldownVersion, rollupVersion, timeFrom, } from '../utils' @@ -178,6 +179,7 @@ class EnvironmentPluginContainer { this.minimalContext = { meta: { rollupVersion, + rolldownVersion, watchMode: true, }, debug: noop, @@ -543,7 +545,7 @@ class PluginContext implements Omit { _activeId: string | null = null _activeCode: string | null = null _resolveSkips?: Set - // meta: RollupPluginContext['meta'] + meta: RollupPluginContext['meta'] environment: Environment constructor( @@ -551,7 +553,7 @@ class PluginContext implements Omit { public _container: EnvironmentPluginContainer, ) { this.environment = this._container.environment - // this.meta = this._container.minimalContext.meta + this.meta = this._container.minimalContext.meta } parse(code: string, opts: any) { diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index f2aa131b9b4924..0a424ab8a2177a 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -146,8 +146,11 @@ const _require = createRequire(import.meta.url) const _dirname = path.dirname(fileURLToPath(import.meta.url)) -// NOTE: we don't use VERSION variable exported from rollup to avoid importing rollup in dev -export const rollupVersion = +// https://github.com/rolldown/rolldown/blob/7bc51f099a916dbe31bc0582995c58cf0d0f8924/packages/rolldown/src/log/logger.ts#L67 +export const rollupVersion = '4.23.0' + +// NOTE: we don't use VERSION variable exported from rolldown to avoid importing rolldown in dev +export const rolldownVersion = resolvePackageData('rolldown', _dirname, true)?.data.version ?? '' // set in bin/vite.js diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3cc62d58b32b99..b88cf59f165a4f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -230,8 +230,8 @@ importers: specifier: ^8.4.48 version: 8.4.48 rolldown: - specifier: https://pkg.pr.new/rolldown@dab103f - version: https://pkg.pr.new/rolldown@dab103f + specifier: https://pkg.pr.new/rolldown@e65437c + version: https://pkg.pr.new/rolldown@e65437c rollup: specifier: ^4.23.0 version: 4.24.4 @@ -2812,63 +2812,63 @@ packages: '@polka/url@1.0.0-next.28': resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} - '@rolldown/binding-darwin-arm64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@dab103f': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@dab103f} + '@rolldown/binding-darwin-arm64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@e65437c': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@e65437c} version: 0.13.2 os: [darwin] - '@rolldown/binding-darwin-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@dab103f': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@dab103f} + '@rolldown/binding-darwin-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@e65437c': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@e65437c} version: 0.13.2 os: [darwin] - '@rolldown/binding-freebsd-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@dab103f': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@dab103f} + '@rolldown/binding-freebsd-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@e65437c': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@e65437c} version: 0.13.2 os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@dab103f': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@dab103f} + '@rolldown/binding-linux-arm-gnueabihf@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@e65437c': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@e65437c} version: 0.13.2 os: [linux] - '@rolldown/binding-linux-arm64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@dab103f': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@dab103f} + '@rolldown/binding-linux-arm64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@e65437c': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@e65437c} version: 0.13.2 os: [linux] - '@rolldown/binding-linux-arm64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@dab103f': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@dab103f} + '@rolldown/binding-linux-arm64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@e65437c': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@e65437c} version: 0.13.2 os: [linux] - '@rolldown/binding-linux-x64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@dab103f': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@dab103f} + '@rolldown/binding-linux-x64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@e65437c': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@e65437c} version: 0.13.2 os: [linux] - '@rolldown/binding-linux-x64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@dab103f': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@dab103f} + '@rolldown/binding-linux-x64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@e65437c': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@e65437c} version: 0.13.2 os: [linux] - '@rolldown/binding-wasm32-wasi@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@dab103f': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@dab103f} + '@rolldown/binding-wasm32-wasi@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@e65437c': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@e65437c} version: 0.13.2 engines: {node: '>=14.21.3'} - '@rolldown/binding-win32-arm64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@dab103f': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@dab103f} + '@rolldown/binding-win32-arm64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@e65437c': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@e65437c} version: 0.13.2 os: [win32] - '@rolldown/binding-win32-ia32-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@dab103f': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@dab103f} + '@rolldown/binding-win32-ia32-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@e65437c': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@e65437c} version: 0.13.2 os: [win32] - '@rolldown/binding-win32-x64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@dab103f': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@dab103f} + '@rolldown/binding-win32-x64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@e65437c': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@e65437c} version: 0.13.2 os: [win32] @@ -6310,8 +6310,8 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rolldown@https://pkg.pr.new/rolldown@dab103f: - resolution: {tarball: https://pkg.pr.new/rolldown@dab103f} + rolldown@https://pkg.pr.new/rolldown@e65437c: + resolution: {tarball: https://pkg.pr.new/rolldown@e65437c} version: 0.13.2 hasBin: true @@ -8459,42 +8459,42 @@ snapshots: '@polka/url@1.0.0-next.28': {} - '@rolldown/binding-darwin-arm64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@dab103f': + '@rolldown/binding-darwin-arm64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@e65437c': optional: true - '@rolldown/binding-darwin-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@dab103f': + '@rolldown/binding-darwin-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@e65437c': optional: true - '@rolldown/binding-freebsd-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@dab103f': + '@rolldown/binding-freebsd-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@e65437c': optional: true - '@rolldown/binding-linux-arm-gnueabihf@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@dab103f': + '@rolldown/binding-linux-arm-gnueabihf@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@e65437c': optional: true - '@rolldown/binding-linux-arm64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@dab103f': + '@rolldown/binding-linux-arm64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@e65437c': optional: true - '@rolldown/binding-linux-arm64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@dab103f': + '@rolldown/binding-linux-arm64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@e65437c': optional: true - '@rolldown/binding-linux-x64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@dab103f': + '@rolldown/binding-linux-x64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@e65437c': optional: true - '@rolldown/binding-linux-x64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@dab103f': + '@rolldown/binding-linux-x64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@e65437c': optional: true - '@rolldown/binding-wasm32-wasi@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@dab103f': + '@rolldown/binding-wasm32-wasi@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@e65437c': dependencies: '@napi-rs/wasm-runtime': 0.2.5 optional: true - '@rolldown/binding-win32-arm64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@dab103f': + '@rolldown/binding-win32-arm64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@e65437c': optional: true - '@rolldown/binding-win32-ia32-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@dab103f': + '@rolldown/binding-win32-ia32-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@e65437c': optional: true - '@rolldown/binding-win32-x64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@dab103f': + '@rolldown/binding-win32-x64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@e65437c': optional: true '@rollup/plugin-alias@5.1.1(rollup@4.24.4)': @@ -12166,22 +12166,22 @@ snapshots: dependencies: glob: 7.2.3 - rolldown@https://pkg.pr.new/rolldown@dab103f: + rolldown@https://pkg.pr.new/rolldown@e65437c: dependencies: zod: 3.23.8 optionalDependencies: - '@rolldown/binding-darwin-arm64': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@dab103f - '@rolldown/binding-darwin-x64': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@dab103f - '@rolldown/binding-freebsd-x64': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@dab103f - '@rolldown/binding-linux-arm-gnueabihf': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@dab103f - '@rolldown/binding-linux-arm64-gnu': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@dab103f - '@rolldown/binding-linux-arm64-musl': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@dab103f - '@rolldown/binding-linux-x64-gnu': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@dab103f - '@rolldown/binding-linux-x64-musl': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@dab103f - '@rolldown/binding-wasm32-wasi': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@dab103f - '@rolldown/binding-win32-arm64-msvc': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@dab103f - '@rolldown/binding-win32-ia32-msvc': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@dab103f - '@rolldown/binding-win32-x64-msvc': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@dab103f + '@rolldown/binding-darwin-arm64': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@e65437c + '@rolldown/binding-darwin-x64': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@e65437c + '@rolldown/binding-freebsd-x64': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@e65437c + '@rolldown/binding-linux-arm-gnueabihf': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@e65437c + '@rolldown/binding-linux-arm64-gnu': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@e65437c + '@rolldown/binding-linux-arm64-musl': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@e65437c + '@rolldown/binding-linux-x64-gnu': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@e65437c + '@rolldown/binding-linux-x64-musl': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@e65437c + '@rolldown/binding-wasm32-wasi': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@e65437c + '@rolldown/binding-win32-arm64-msvc': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@e65437c + '@rolldown/binding-win32-ia32-msvc': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@e65437c + '@rolldown/binding-win32-x64-msvc': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@e65437c rollup-plugin-dts@6.1.1(rollup@4.24.4)(typescript@5.6.3): dependencies: From b68f46f165261caca43af98f3a66c8a0e9da5ff8 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:16:46 +0900 Subject: [PATCH 16/46] chore: skip data uri by load fallback plugin for native data uri handling --- .../vite/src/node/plugins/loadFallback.ts | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/packages/vite/src/node/plugins/loadFallback.ts b/packages/vite/src/node/plugins/loadFallback.ts index f221ce56bdd2fb..6771d303d71ef1 100644 --- a/packages/vite/src/node/plugins/loadFallback.ts +++ b/packages/vite/src/node/plugins/loadFallback.ts @@ -1,24 +1,31 @@ import fsp from 'node:fs/promises' +import type { RolldownPlugin } from 'rolldown' import { cleanUrl } from '../../shared/utils' -import type { Plugin } from '../plugin' /** * A plugin to provide build load fallback for arbitrary request with queries. */ -export function buildLoadFallbackPlugin(): Plugin { +export function buildLoadFallbackPlugin(): RolldownPlugin { return { name: 'vite:load-fallback', - async load(id) { - try { - const cleanedId = cleanUrl(id) - const content = await fsp.readFile(cleanedId, 'utf-8') - this.addWatchFile(cleanedId) - return content - } catch { - const content = await fsp.readFile(id, 'utf-8') - this.addWatchFile(id) - return content - } + load: { + filter: { + id: { + exclude: [/^data:/], + }, + }, + async handler(id) { + try { + const cleanedId = cleanUrl(id) + const content = await fsp.readFile(cleanedId, 'utf-8') + this.addWatchFile(cleanedId) + return content + } catch { + const content = await fsp.readFile(id, 'utf-8') + this.addWatchFile(id) + return content + } + }, }, } } From 477701bd13e122245fe49d4d58723d840255f469 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:14:00 +0900 Subject: [PATCH 17/46] feat: handle non-relative paths by Vite's resolver for enableNativePlugin --- packages/vite/src/node/plugins/index.ts | 12 +++++-- packages/vite/src/node/plugins/resolve.ts | 36 +++++++++++++++++++- playground/resolve/browser-field/relative.js | 3 +- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index 3a2c4856be33c8..e393a292d559d3 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -15,7 +15,7 @@ import { isDepOptimizationDisabled } from '../optimizer' import type { HookHandler, Plugin, PluginWithRequiredHook } from '../plugin' import { watchPackageDataPlugin } from '../packages' import { jsonPlugin } from './json' -import { resolvePlugin } from './resolve' +import { filteredResolvePlugin, resolvePlugin } from './resolve' import { optimizedDepsPlugin } from './optimizedDeps' import { esbuildPlugin } from './esbuild' import { importAnalysisPlugin } from './importAnalysis' @@ -82,7 +82,15 @@ export async function resolvePlugins( : modulePreloadPolyfillPlugin(config) : null, enableNativePlugin - ? null + ? filteredResolvePlugin({ + root: config.root, + isProduction: config.isProduction, + isBuild, + packageCache: config.packageCache, + asSrc: true, + optimizeDeps: true, + externalize: true, + }) : resolvePlugin({ root: config.root, isProduction: config.isProduction, diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 916d6e1b0b294e..cbeed8e2f3c99d 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -2,7 +2,7 @@ import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' import colors from 'picocolors' -import type { PartialResolvedId } from 'rolldown' +import type { PartialResolvedId, RolldownPlugin } from 'rolldown' import { exports, imports } from 'resolve.exports' import { hasESMSyntax } from 'mlly' import type { Plugin } from '../plugin' @@ -178,6 +178,40 @@ export interface ResolvePluginOptionsWithOverrides extends ResolveOptions, ResolvePluginOptions {} +export function filteredResolvePlugin( + resolveOptions: ResolvePluginOptionsWithOverrides, +): RolldownPlugin { + const originalPlugin = resolvePlugin(resolveOptions) + + return { + name: 'vite:resolve', + options(option) { + option.resolve ??= {} + option.resolve.extensions = this.environment.config.resolve.extensions + option.resolve.extensionAlias = { + '.js': ['.ts', '.tsx', '.js'], + '.jsx': ['.ts', '.tsx', '.jsx'], + '.mjs': ['.mts', '.mjs'], + '.cjs': ['.cts', '.cjs'], + } + }, + resolveId: { + filter: { + id: { + exclude: [ + // relative paths without query + /^\.\.?[/\\][^?]+$/, + /^(?:\0|\/?virtual:)/, + ], + }, + }, + // @ts-expect-error the options is incompatible + handler: originalPlugin.resolveId!, + }, + load: originalPlugin.load, + } +} + export function resolvePlugin( resolveOptions: ResolvePluginOptionsWithOverrides, ): Plugin { diff --git a/playground/resolve/browser-field/relative.js b/playground/resolve/browser-field/relative.js index 660d6be578a728..3be4d7452be75d 100644 --- a/playground/resolve/browser-field/relative.js +++ b/playground/resolve/browser-field/relative.js @@ -4,7 +4,8 @@ import rb from './no-ext.js' // no substitution import rc from './ext' import rd from './ext.js' import re from './ext-index/index.js' -import rf from './ext-index' +// import rf from './ext-index' +const rf = 'FIXME' import rg from './no-ext-index/index.js' // no substitution export { ra, rb, rc, rd, re, rf, rg } From 68a94bc3a11eb1ea07e0e6db25cdaf858769bed9 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 25 Sep 2024 15:57:33 +0900 Subject: [PATCH 18/46] chore: remove browser field edge case test --- playground/resolve/browser-field/relative.js | 6 ++---- playground/resolve/index.html | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/playground/resolve/browser-field/relative.js b/playground/resolve/browser-field/relative.js index 3be4d7452be75d..6b45c5758d37dd 100644 --- a/playground/resolve/browser-field/relative.js +++ b/playground/resolve/browser-field/relative.js @@ -4,8 +4,6 @@ import rb from './no-ext.js' // no substitution import rc from './ext' import rd from './ext.js' import re from './ext-index/index.js' -// import rf from './ext-index' -const rf = 'FIXME' -import rg from './no-ext-index/index.js' // no substitution +import rf from './no-ext-index/index.js' // no substitution -export { ra, rb, rc, rd, re, rf, rg } +export { ra, rb, rc, rd, re, rf } diff --git a/playground/resolve/index.html b/playground/resolve/index.html index 8c315f74dcd2c1..d42d1a39904fe1 100644 --- a/playground/resolve/index.html +++ b/playground/resolve/index.html @@ -325,11 +325,10 @@

resolve non normalized absolute path

rd, re, rf, - rg, } from '@vitejs/test-resolve-browser-field/relative' - const success = [main, a, c, d, e, f, h, i, ra, rc, rd, re, rf] - const noSuccess = [b, g, rb, rg] + const success = [main, a, c, d, e, f, h, i, ra, rc, rd, re] + const noSuccess = [b, g, rb, rf] if ( [...success, ...noSuccess].filter((text) => text.includes('[success]')) From 93fd15f81c10dab3b652a08ffa0c51d6f2ea12df Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Tue, 1 Oct 2024 16:06:31 +0900 Subject: [PATCH 19/46] chore: use advancedChunks instead of manualChunks --- .../css-codesplit-consistent.spec.ts | 4 +- playground/css-codesplit/vite.config.js | 19 ++++++--- .../__tests__/js-sourcemap.spec.ts | 6 +-- playground/js-sourcemap/vite.config.js | 40 +++++++++++++------ vitest.config.e2e.ts | 2 - 5 files changed, 46 insertions(+), 25 deletions(-) diff --git a/playground/css-codesplit/__tests__/css-codesplit-consistent.spec.ts b/playground/css-codesplit/__tests__/css-codesplit-consistent.spec.ts index 4da121a652d0db..d06d87e9af8223 100644 --- a/playground/css-codesplit/__tests__/css-codesplit-consistent.spec.ts +++ b/playground/css-codesplit/__tests__/css-codesplit-consistent.spec.ts @@ -8,8 +8,8 @@ beforeEach(async () => { for (let i = 0; i < 5; i++) { describe.runIf(isBuild)('css-codesplit build', () => { test('should be consistent with same content', () => { - expect(findAssetFile(/style-.+\.css/)).toMatch('h2{color:#00f}') - expect(findAssetFile(/style2-.+\.css/)).toBe('') + expect(findAssetFile(/style2-.+\.css/)).toMatch('h2{color:#00f}') + expect(findAssetFile(/style-.+\.css/)).toBe('') }) }) } diff --git a/playground/css-codesplit/vite.config.js b/playground/css-codesplit/vite.config.js index 5042b6d9b9cab7..0d3d006e4dda7f 100644 --- a/playground/css-codesplit/vite.config.js +++ b/playground/css-codesplit/vite.config.js @@ -13,11 +13,20 @@ export default defineConfig({ 'shared-css-no-js': resolve(__dirname, 'shared-css-no-js.html'), }, output: { - manualChunks(id) { - // make `chunk.css` it's own chunk for easier testing of pure css chunks - if (id.includes('chunk.css')) { - return 'chunk' - } + // manualChunks(id) { + // // make `chunk.css` it's own chunk for easier testing of pure css chunks + // if (id.includes('chunk.css')) { + // return 'chunk' + // } + // }, + advancedChunks: { + groups: [ + // make `chunk.css` it's own chunk for easier testing of pure css chunks + { + name: 'chunk', + test: 'chunk.css', + }, + ], }, }, }, diff --git a/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts b/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts index 6b8d02a407fe63..a991f8d378d609 100644 --- a/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts +++ b/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts @@ -140,7 +140,7 @@ describe.runIf(isBuild)('build tests', () => { expect(formatSourcemapForSnapshot(JSON.parse(map))).toMatchInlineSnapshot(` { "ignoreList": [], - "mappings": ";+8BAAA,OAAO,2BAAuB,0BAE9B,QAAQ,IAAI,uBAAuB", + "mappings": ";4jCAAA,OAAO,6BAAuB,wBAE9B,QAAQ,IAAI", "sources": [ "../../after-preload-dynamic.js", ], @@ -156,7 +156,7 @@ describe.runIf(isBuild)('build tests', () => { // verify sourcemap comment is preserved at the last line const js = findAssetFile(/after-preload-dynamic-[-\w]{8}\.js$/) expect(js).toMatch( - /\n\/\/# sourceMappingURL=after-preload-dynamic-[-\w]{8}\.js\.map\n$/, + /\n\/\/# sourceMappingURL=after-preload-dynamic-[-\w]{8}\.js\.map\n?$/, ) }) @@ -177,7 +177,7 @@ describe.runIf(isBuild)('build tests', () => { const map = findAssetFile(/with-define-object.*\.js\.map/) expect(formatSourcemapForSnapshot(JSON.parse(map))).toMatchInlineSnapshot(` { - "mappings": "qBAEA,SAASA,GAAO,CACJC,EAAA,CACZ,CAEA,SAASA,GAAY,CAEX,QAAA,MAAM,qBAAsBC,CAAkB,CACxD,CAEAF,EAAK", + "mappings": "qBAEA,SAAS,GAAO,CACd,EAAA,CACF,CAEA,SAAS,GAAY,CAEnB,QAAQ,MAAM,qBAAsB,CAAA,CACtC,CAEA,EAAA", "sources": [ "../../with-define-object.ts", ], diff --git a/playground/js-sourcemap/vite.config.js b/playground/js-sourcemap/vite.config.js index f47c89eff07ebf..959c94adf060a4 100644 --- a/playground/js-sourcemap/vite.config.js +++ b/playground/js-sourcemap/vite.config.js @@ -11,19 +11,33 @@ export default defineConfig({ sourcemap: true, rollupOptions: { output: { - manualChunks(name) { - if (name.endsWith('after-preload-dynamic.js')) { - return 'after-preload-dynamic' - } - if (name.endsWith('after-preload-dynamic-hashbang.js')) { - return 'after-preload-dynamic-hashbang' - } - if (name.endsWith('after-preload-dynamic-no-dep.js')) { - return 'after-preload-dynamic-no-dep' - } - if (name.includes('with-define-object')) { - return 'with-define-object' - } + // manualChunks(name) { + // if (name.endsWith('after-preload-dynamic.js')) { + // return 'after-preload-dynamic' + // } + // if (name.endsWith('after-preload-dynamic-hashbang.js')) { + // return 'after-preload-dynamic-hashbang' + // } + // if (name.endsWith('after-preload-dynamic-no-dep.js')) { + // return 'after-preload-dynamic-no-dep' + // } + // if (name.includes('with-define-object')) { + // return 'with-define-object' + // } + // }, + advancedChunks: { + groups: [ + { name: 'after-preload-dynamic', test: 'after-preload-dynamic.js' }, + { + name: 'after-preload-dynamic-hashbang', + test: 'after-preload-dynamic-hashbang.js', + }, + { + name: 'after-preload-dynamic-no-dep', + test: 'after-preload-dynamic-no-dep.js', + }, + { name: 'with-define-object', test: 'with-define-object' }, + ], }, banner(chunk) { if (chunk.name.endsWith('after-preload-dynamic-hashbang')) { diff --git a/vitest.config.e2e.ts b/vitest.config.e2e.ts index 157565f6847514..20603ce18f5901 100644 --- a/vitest.config.e2e.ts +++ b/vitest.config.e2e.ts @@ -18,12 +18,10 @@ export default defineConfig({ ...(isBuild ? [ './playground/backend-integration/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2207 - './playground/css-codesplit/**/*.spec.[tj]s', // manualChunks './playground/css-dynamic-import/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/1842 './playground/dynamic-import/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/1842, https://github.com/rolldown/rolldown/issues/1843 './playground/external/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2041 './playground/glob-import/**/*.spec.[tj]s', // remove empty CSS generate chunk https://github.com/rolldown/rolldown/issues/1842 - './playground/js-sourcemap/**/*.spec.[tj]s', // manualChunks './playground/lib/**/*.spec.[tj]s', // umd format './playground/object-hooks/**/*.spec.[tj]s', // object hook sequential './playground/optimize-deps/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2031 From 404fccc63cb2d2230c1191763206f29dc1af5c02 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Sat, 5 Oct 2024 02:29:23 +0900 Subject: [PATCH 20/46] feat: convert `optimizeDeps.esbuildOptions` to `optimizeDeps.rollupOptions` --- packages/vite/src/node/config.ts | 112 ++++++++++++++++++++++ packages/vite/src/node/optimizer/index.ts | 20 +++- packages/vite/src/node/optimizer/scan.ts | 2 +- 3 files changed, 129 insertions(+), 5 deletions(-) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 368b33ef3bd04b..0d941d8a8ca81e 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -782,6 +782,7 @@ function resolveEnvironmentOptions( options.optimizeDeps, resolve.preserveSymlinks, consumer, + logger, ), dev: resolveDevEnvironmentOptions( options.dev, @@ -953,7 +954,118 @@ function resolveDepOptimizationOptions( optimizeDeps: DepOptimizationOptions | undefined, preserveSymlinks: boolean, consumer: 'client' | 'server' | undefined, + logger: Logger, ): DepOptimizationOptions { + if (optimizeDeps?.esbuildOptions) { + logger.warn( + colors.yellow( + `You have set \`optimizeDeps.esbuildOptions\` but this options is now deprecated. ` + + `Vite now uses Rolldown to optimize the dependencies. ` + + `Please use \`optimizeDeps.rollupOptions\` instead.`, + ), + ) + + optimizeDeps.rollupOptions ??= {} + optimizeDeps.rollupOptions.resolve ??= {} + optimizeDeps.rollupOptions.output ??= {} + + const setResolveOptions = < + T extends keyof Exclude, + >( + key: T, + value: Exclude[T], + ) => { + if ( + value !== undefined && + optimizeDeps.rollupOptions!.resolve![key] === undefined + ) { + optimizeDeps.rollupOptions!.resolve![key] = value + } + } + + if ( + optimizeDeps.esbuildOptions.minify !== undefined && + optimizeDeps.rollupOptions.output.minify === undefined + ) { + optimizeDeps.rollupOptions.output.minify = + optimizeDeps.esbuildOptions.minify + } + if ( + optimizeDeps.esbuildOptions.treeShaking !== undefined && + optimizeDeps.rollupOptions.treeshake === undefined + ) { + optimizeDeps.rollupOptions.treeshake = + optimizeDeps.esbuildOptions.treeShaking + } + if ( + optimizeDeps.esbuildOptions.define !== undefined && + optimizeDeps.rollupOptions.define === undefined + ) { + optimizeDeps.rollupOptions.define = optimizeDeps.esbuildOptions.define + } + if (optimizeDeps.esbuildOptions.loader !== undefined) { + const loader = optimizeDeps.esbuildOptions.loader + optimizeDeps.rollupOptions.moduleTypes ??= {} + for (const [key, value] of Object.entries(loader)) { + if ( + optimizeDeps.rollupOptions.moduleTypes[key] === undefined && + value !== 'copy' && + value !== 'css' && + value !== 'default' && + value !== 'file' && + value !== 'local-css' + ) { + optimizeDeps.rollupOptions.moduleTypes[key] = value + } + } + } + setResolveOptions('symlinks', optimizeDeps.esbuildOptions.preserveSymlinks) + setResolveOptions( + 'extensions', + optimizeDeps.esbuildOptions.resolveExtensions, + ) + setResolveOptions('mainFields', optimizeDeps.esbuildOptions.mainFields) + setResolveOptions('conditionNames', optimizeDeps.esbuildOptions.conditions) + + // NOTE: the following options cannot be converted + // - legalComments + // - target, supported (Vite used to transpile down to `ESBUILD_MODULES_TARGET`) + // - ignoreAnnotations + // - jsx, jsxFactory, jsxFragment, jsxImportSource, jsxDev, jsxSideEffects + // - tsconfigRaw, tsconfig + + // NOTE: the following options can be converted but probably not worth it + // - sourceRoot + // - sourcesContent (`output.sourcemapExcludeSources` is not supported by rolldown) + // - drop + // - dropLabels + // - mangleProps, reserveProps, mangleQuoted, mangleCache + // - minifyWhitespace, minifyIdentifiers, minifySyntax + // - lineLimit + // - charset + // - pure (`treeshake.manualPureFunctions` is not supported by rolldown) + // - alias (it probably does not work the same with `resolve.alias`) + // - inject + // - banner, footer + // - plugins (not sure if it's possible and need to check if it's worth it before) + // - nodePaths + + // NOTE: the following options does not make sense to set / convert it + // - globalName (we only use ESM format) + // - keepNames (probably rolldown does not need it? not sure) + // - color + // - logLimit + // - logOverride + // - splitting + // - outbase + // - packages (this should not be set) + // - allowOverwrite + // - publicPath (`file` loader is not supported by rolldown) + // - entryNames, chunkNames, assetNames (Vite does not support changing these options) + // - stdin + // - absWorkingDir + } + return mergeWithDefaults( { ...configDefaults.optimizeDeps, diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index 91be5926bb14a4..885ede3c2b7441 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -7,7 +7,12 @@ import colors from 'picocolors' import type { BuildOptions as EsbuildBuildOptions } from 'esbuild' import { init, parse } from 'es-module-lexer' import { isDynamicPattern } from 'tinyglobby' -import { type RollupOptions, type RollupOutput, rolldown } from 'rolldown' +import { + type RollupOptions, + type RollupOutput, + type OutputOptions as RollupOutputOptions, + rolldown, +} from 'rolldown' import type { ResolvedConfig } from '../config' import { asyncFlatten, @@ -103,7 +108,12 @@ export interface DepOptimizationConfig { | 'outExtension' | 'metafile' > - rollupOptions?: RollupOptions + rollupOptions?: Omit & { + output?: Omit< + RollupOutputOptions, + 'format' | 'sourcemap' | 'dir' | 'banner' + > + } /** * List of file extensions that can be optimized. A corresponding esbuild * plugin must exist to handle the specific extension. @@ -778,9 +788,11 @@ async function prepareRolldownOptimizerRun( let canceled = false async function build() { const bundle = await rolldown({ + ...rollupOptions, input: flatIdDeps, logLevel: 'warn', plugins, + platform, resolve: { // TODO: set aliasFields, conditionNames depending on `platform` mainFields: ['module', 'main'], @@ -788,7 +800,6 @@ async function prepareRolldownOptimizerRun( extensions: ['.js', '.css'], conditionNames: ['browser'], }, - ...rollupOptions, // TODO: remove this and enable rolldown's CSS support later moduleTypes: { '.css': 'js', @@ -800,6 +811,7 @@ async function prepareRolldownOptimizerRun( throw new Error('The build was canceled') } const result = await bundle.write({ + ...rollupOptions.output, format: 'esm', sourcemap: true, dir: processingCacheDir, @@ -807,7 +819,6 @@ async function prepareRolldownOptimizerRun( platform === 'node' ? `import { createRequire } from 'module';const require = createRequire(import.meta.url);` : undefined, - ...rollupOptions.output, }) await bundle.close() return result @@ -1082,6 +1093,7 @@ export async function extractExportsData( const result = await build.generate({ ...rollupOptions.output, format: 'esm', + sourcemap: false, }) const [, exports, , hasModuleSyntax] = parse(result.output[0].code) return { diff --git a/packages/vite/src/node/optimizer/scan.ts b/packages/vite/src/node/optimizer/scan.ts index 579ab8bff09eae..995037b12e1fdb 100644 --- a/packages/vite/src/node/optimizer/scan.ts +++ b/packages/vite/src/node/optimizer/scan.ts @@ -282,10 +282,10 @@ async function prepareRolldownScanner( async function build() { await scan({ + ...rollupOptions, input: entries, logLevel: 'silent', plugins, - ...rollupOptions, }) } From 72fb85964ed9942c1bb1a9fedec84f1f2bf33402 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:26:45 +0900 Subject: [PATCH 21/46] fix: optimizeDeps test pass on windows --- packages/vite/src/node/optimizer/rolldownDepPlugin.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/node/optimizer/rolldownDepPlugin.ts b/packages/vite/src/node/optimizer/rolldownDepPlugin.ts index 072506e71df905..29a35d05e5e1d1 100644 --- a/packages/vite/src/node/optimizer/rolldownDepPlugin.ts +++ b/packages/vite/src/node/optimizer/rolldownDepPlugin.ts @@ -14,6 +14,7 @@ import { browserExternalId, optionalPeerDepId } from '../plugins/resolve' import { isCSSRequest, isModuleCSSRequest } from '../plugins/css' import type { Environment } from '../environment' import { createBackCompatIdResolver } from '../idResolver' +import { isWindows } from '../../shared/utils' const externalWithConversionNamespace = 'vite:dep-pre-bundle:external-conversion' @@ -170,7 +171,8 @@ export function rolldownDepPlugin( // or package name (e.g. import "some-package.pdf") if (JS_TYPES_RE.test(resolved)) { return { - id: resolved, + // normalize to \\ on windows for esbuild/rolldown behavior difference: https://github.com/sapphi-red-repros/rolldown-esbuild-path-normalization + id: isWindows ? resolved.replaceAll('/', '\\') : resolved, external: false, } } From b5034b246f0312b7f65968a3893f830e3f2bd4e7 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:55:26 +0900 Subject: [PATCH 22/46] chore: make some tests passing --- .../__tests__/backend-integration.spec.ts | 9 ++++++--- .../dynamic-import/__tests__/dynamic-import.spec.ts | 2 ++ vitest.config.e2e.ts | 5 ++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/playground/backend-integration/__tests__/backend-integration.spec.ts b/playground/backend-integration/__tests__/backend-integration.spec.ts index c6ac1aaf59cb91..fe90be8c532cbc 100644 --- a/playground/backend-integration/__tests__/backend-integration.spec.ts +++ b/playground/backend-integration/__tests__/backend-integration.spec.ts @@ -56,10 +56,13 @@ describe.runIf(isBuild)('build', () => { const imgAssetEntry = manifest['../images/logo.png'] const dirFooAssetEntry = manifest['../../dir/foo.css'] const iconEntrypointEntry = manifest['icon.png'] - expect(htmlEntry.css.length).toEqual(1) + expect(htmlEntry.css.length).toEqual(2) expect(htmlEntry.assets.length).toEqual(1) - expect(mainTsEntry.assets?.length ?? 0).toBeGreaterThanOrEqual(1) - expect(mainTsEntry.assets).toContainEqual( + expect(mainTsEntry.imports.length).toBeGreaterThanOrEqual(1) + const mainTsEntryImported = manifest[mainTsEntry.imports[0]] + expect(mainTsEntryImported).toBeDefined() + expect(mainTsEntryImported.assets?.length ?? 0).toBeGreaterThanOrEqual(1) + expect(mainTsEntryImported.assets).toContainEqual( expect.stringMatching(/assets\/url-[-\w]{8}\.css/), ) expect(cssAssetEntry?.file).not.toBeUndefined() diff --git a/playground/dynamic-import/__tests__/dynamic-import.spec.ts b/playground/dynamic-import/__tests__/dynamic-import.spec.ts index a1a843a0b04f29..d1d5d6716391c6 100644 --- a/playground/dynamic-import/__tests__/dynamic-import.spec.ts +++ b/playground/dynamic-import/__tests__/dynamic-import.spec.ts @@ -170,6 +170,8 @@ test('should work a load path that contains parentheses.', async () => { test.runIf(isBuild)( 'should rollup warn when static and dynamic import a module in same chunk', + // NOTE: this is a warning related to rollup's chunking behavior + { skip: true }, async () => { const log = serverLogs.join('\n') expect(log).toContain( diff --git a/vitest.config.e2e.ts b/vitest.config.e2e.ts index 20603ce18f5901..005035225fc6f7 100644 --- a/vitest.config.e2e.ts +++ b/vitest.config.e2e.ts @@ -17,9 +17,8 @@ export default defineConfig({ './playground/legacy/**/*.spec.[tj]s', // system format ...(isBuild ? [ - './playground/backend-integration/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2207 - './playground/css-dynamic-import/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/1842 - './playground/dynamic-import/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/1842, https://github.com/rolldown/rolldown/issues/1843 + './playground/backend-integration/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2392 + './playground/dynamic-import/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/1843 './playground/external/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2041 './playground/glob-import/**/*.spec.[tj]s', // remove empty CSS generate chunk https://github.com/rolldown/rolldown/issues/1842 './playground/lib/**/*.spec.[tj]s', // umd format From 0b9ce31916c740fe2463d177432b565a5058c082 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 9 Oct 2024 18:48:36 +0900 Subject: [PATCH 23/46] chore: make glob-import test pass with workarounds --- packages/vite/src/node/plugins/css.ts | 9 +++++++-- vitest.config.e2e.ts | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 6a9f5affaae077..3e2b4a3f839b7e 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -624,7 +624,8 @@ export function cssPostPlugin(config: ResolvedConfig): RolldownPlugin { code = `export default ${JSON.stringify(content)}` } else { // empty module when it's not a CSS module nor `?inline` - code = '' + // NOTE: add `export {}` otherwise rolldown treats the module as CJS (https://github.com/rolldown/rolldown/issues/2394) + code = 'export {}' } return { @@ -641,7 +642,11 @@ export function cssPostPlugin(config: ResolvedConfig): RolldownPlugin { let chunkCSS = '' // the chunk is empty if it's a dynamic entry chunk that only contains a CSS import const isJsChunkEmpty = code === '' && !chunk.isEntry - let isPureCssChunk = chunk.exports.length === 0 + // NOTE: use this instead of "chunk.exports.length === 0" because of https://github.com/rolldown/rolldown/issues/2395 + let isPureCssChunk = + opts.format === 'es' + ? !/export\s+/.test(code) + : !/exports(?:\.\w|\[)/.test(code) const ids = Object.keys(chunk.modules) for (const id of ids) { if (styles.has(id)) { diff --git a/vitest.config.e2e.ts b/vitest.config.e2e.ts index 005035225fc6f7..363c2be35ad3b0 100644 --- a/vitest.config.e2e.ts +++ b/vitest.config.e2e.ts @@ -20,7 +20,6 @@ export default defineConfig({ './playground/backend-integration/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2392 './playground/dynamic-import/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/1843 './playground/external/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2041 - './playground/glob-import/**/*.spec.[tj]s', // remove empty CSS generate chunk https://github.com/rolldown/rolldown/issues/1842 './playground/lib/**/*.spec.[tj]s', // umd format './playground/object-hooks/**/*.spec.[tj]s', // object hook sequential './playground/optimize-deps/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2031 From b91ddef676384c2d0bc5522ed452f10006bb1ab6 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 9 Oct 2024 19:14:17 +0900 Subject: [PATCH 24/46] chore: run worker-es tests --- playground/worker/__tests__/es/worker-es.spec.ts | 2 +- vitest.config.e2e.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/playground/worker/__tests__/es/worker-es.spec.ts b/playground/worker/__tests__/es/worker-es.spec.ts index 7933b518a4235a..6d32dd6b516fce 100644 --- a/playground/worker/__tests__/es/worker-es.spec.ts +++ b/playground/worker/__tests__/es/worker-es.spec.ts @@ -111,7 +111,7 @@ describe.runIf(isBuild)('build', () => { test('inlined code generation', async () => { const assetsDir = path.resolve(testDir, 'dist/es/assets') const files = fs.readdirSync(assetsDir) - expect(files.length).toBe(36) + expect(files.length).toBe(41) const index = files.find((f) => f.includes('main-module')) const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') const worker = files.find((f) => f.includes('my-worker')) diff --git a/vitest.config.e2e.ts b/vitest.config.e2e.ts index 363c2be35ad3b0..f101748e3cc3b6 100644 --- a/vitest.config.e2e.ts +++ b/vitest.config.e2e.ts @@ -23,7 +23,6 @@ export default defineConfig({ './playground/lib/**/*.spec.[tj]s', // umd format './playground/object-hooks/**/*.spec.[tj]s', // object hook sequential './playground/optimize-deps/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2031 - './playground/worker/__tests__/es/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2208 ] : []), ...defaultExclude, From 732d0ebbe444ce5bbf7c8bac1fad55b860c2c49b Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:03:47 +0900 Subject: [PATCH 25/46] fix: use preliminaryFileName for chunkMetadata key --- packages/vite/src/node/plugins/metadata.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/vite/src/node/plugins/metadata.ts b/packages/vite/src/node/plugins/metadata.ts index 3b9469dbd27ab6..12147461158bf4 100644 --- a/packages/vite/src/node/plugins/metadata.ts +++ b/packages/vite/src/node/plugins/metadata.ts @@ -15,7 +15,7 @@ export function metadataPlugin(): Plugin { async renderChunk(_code, chunk) { // Since the chunk come from rust side, mutate it directly will not sync back to rust side. // The next usage will lost the metadata - chunkMetadataMap.set(chunk.name, { + chunkMetadataMap.set(chunk.fileName, { importedAssets: new Set(), importedCss: new Set(), }) @@ -28,6 +28,7 @@ export function metadataPlugin(): Plugin { export function getChunkMetadata( chunk: RenderedChunk | OutputChunk, ): ChunkMetadata | undefined { - // TODO: chunk.name is not unique, use something unique like chunk.preliminaryFileName / chunk.fileName - return chunkMetadataMap.get(chunk.name) + const preliminaryFileName = + 'preliminaryFileName' in chunk ? chunk.preliminaryFileName : chunk.fileName + return chunkMetadataMap.get(preliminaryFileName) } From 5f46885081b5b556f6786f3cfe3d1fdc2606c307 Mon Sep 17 00:00:00 2001 From: underfin <2218301630@qq.com> Date: Fri, 11 Oct 2024 19:34:42 +0800 Subject: [PATCH 26/46] feat: oxc tranformer (#60) Co-authored-by: IWANABETHATGUY --- packages/vite/rollup.dts.config.ts | 4 + .../src/node/__tests__/plugins/import.spec.ts | 10 +- packages/vite/src/node/config.ts | 34 ++- packages/vite/src/node/optimizer/index.ts | 11 +- packages/vite/src/node/plugins/index.ts | 6 +- packages/vite/src/node/plugins/oxc.ts | 248 ++++++++++++++++++ .../__tests__/js-sourcemap.spec.ts | 6 +- .../__tests__/tsconfig-json.spec.ts | 2 +- vitest.config.e2e.ts | 1 + 9 files changed, 301 insertions(+), 21 deletions(-) create mode 100644 packages/vite/src/node/plugins/oxc.ts diff --git a/packages/vite/rollup.dts.config.ts b/packages/vite/rollup.dts.config.ts index b47e1d70a4dcb5..3a3876f46bbc70 100644 --- a/packages/vite/rollup.dts.config.ts +++ b/packages/vite/rollup.dts.config.ts @@ -17,6 +17,7 @@ const external = [ /^node:*/, /^vite\//, 'rollup/parseAst', + 'rolldown/experimental', ...Object.keys(pkg.dependencies), ...Object.keys(pkg.peerDependencies), ...Object.keys(pkg.devDependencies), @@ -52,6 +53,9 @@ const identifierReplacements: Record> = { TransformPluginContext$1: 'rolldown.TransformPluginContext', TransformResult$2: 'rolldown.TransformResult', }, + 'rolldown/experimental': { + TransformOptions$2: 'rolldown_experimental_TransformOptions', + }, esbuild: { TransformResult$1: 'esbuild_TransformResult', TransformOptions$1: 'esbuild_TransformOptions', diff --git a/packages/vite/src/node/__tests__/plugins/import.spec.ts b/packages/vite/src/node/__tests__/plugins/import.spec.ts index 89fbd80d8ecdc1..d5841a6327e690 100644 --- a/packages/vite/src/node/__tests__/plugins/import.spec.ts +++ b/packages/vite/src/node/__tests__/plugins/import.spec.ts @@ -73,9 +73,13 @@ describe('transformCjsImport', () => { '', config, ), - ).toBe( - 'import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js"; ' + - `const react = ((m) => m?.__esModule ? m : { ...typeof m === "object" && !Array.isArray(m) || typeof m === "function" ? m : {}, default: m })(__vite__cjsImport0_react)`, + ).toMatchInlineSnapshot( + ` + "import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js"; const react = ((m) => m?.__esModule ? m : { + ...typeof m === "object" && !Array.isArray(m) || typeof m === "function" ? m : {}, + default: m + })(__vite__cjsImport0_react)" + `, ) }) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 0d941d8a8ca81e..b794da21d89291 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -98,6 +98,7 @@ import type { ResolvedSSROptions, SSROptions } from './ssr' import { resolveSSROptions, ssrConfigDefaults } from './ssr' import { PartialEnvironment } from './baseEnvironment' import { createIdResolver } from './idResolver' +import { type OxcOptions, convertEsbuildConfigToOxcConfig } from './plugins/oxc' const debug = createDebugger('vite:config', { depth: 10 }) const promisifiedRealpath = promisify(fs.realpath) @@ -349,6 +350,11 @@ export interface UserConfig extends DefaultEnvironmentOptions { * Or set to `false` to disable esbuild. */ esbuild?: ESBuildOptions | false + /** + * Transform options to pass to esbuild. + * Or set to `false` to disable esbuild. + */ + oxc?: OxcOptions | false /** * Specify additional picomatch patterns to be treated as static assets. */ @@ -584,7 +590,8 @@ export type ResolvedConfig = Readonly< plugins: readonly Plugin[] css: ResolvedCSSOptions json: Required - esbuild: ESBuildOptions | false + // esbuild: ESBuildOptions | false + oxc: OxcOptions | false server: ResolvedServerOptions dev: ResolvedDevEnvironmentOptions /** @experimental */ @@ -1472,6 +1479,18 @@ export async function resolveConfig( const base = withTrailingSlash(resolvedBase) + let oxc: OxcOptions | false | undefined = config.oxc + + if (config.esbuild) { + if (config.oxc) { + logger.warn( + `Found esbuild and oxc options, will use oxc and ignore esbuild at transformer.`, + ) + } else { + oxc = convertEsbuildConfigToOxcConfig(config.esbuild, logger) + } + } + resolved = { configFile: configFile ? normalizePath(configFile) : undefined, configFileDependencies: configFileDependencies.map((name) => @@ -1493,12 +1512,17 @@ export async function resolveConfig( plugins: userPlugins, // placeholder to be replaced css: resolveCSSOptions(config.css), json: mergeWithDefaults(configDefaults.json, config.json ?? {}), - esbuild: - config.esbuild === false + // preserve esbuild for buildEsbuildPlugin + esbuild: config.esbuild ?? {}, + oxc: + oxc === false ? false : { - jsxDev: !isProduction, - ...config.esbuild, + ...oxc, + jsx: { + development: !isProduction, + ...oxc?.jsx, + }, }, server, builder, diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index 885ede3c2b7441..5bc40f2d705498 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -28,9 +28,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 { @@ -770,12 +771,9 @@ 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(this, code, id, { sourcemap: true, - sourcefile: id, - loader: jsxLoader && /\.js$/.test(id) ? 'jsx' : undefined, - define, - target: ESBUILD_MODULES_TARGET, + lang: jsxLoader && /\.js$/.test(id) ? 'jsx' : undefined, }) return { code: result.code, @@ -792,6 +790,7 @@ async function prepareRolldownOptimizerRun( input: flatIdDeps, logLevel: 'warn', plugins, + define, platform, resolve: { // TODO: set aliasFields, conditionNames depending on `platform` diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index e393a292d559d3..5fd032ce6fdccf 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -17,7 +17,6 @@ import { watchPackageDataPlugin } from '../packages' 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' @@ -33,6 +32,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, @@ -102,10 +102,10 @@ export async function resolvePlugins( }), htmlInlineProxyPlugin(config), cssPlugin(config), - config.esbuild !== false + config.oxc !== 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..c27fcdf2dadec3 --- /dev/null +++ b/packages/vite/src/node/plugins/oxc.ts @@ -0,0 +1,248 @@ +import path from 'node:path' +import type { + TransformOptions as OxcTransformOptions, + TransformResult as OxcTransformResult, +} from 'rolldown/experimental' +import { transform } from 'rolldown/experimental' +import type { RawSourceMap } from '@ampproject/remapping' +import type { SourceMap } from 'rolldown' +import type { FSWatcher } from 'dep-types/chokidar' +import { TSConfckParseError } from 'tsconfck' +import { combineSourcemaps, createFilter, ensureWatchedFile } from '../utils' +import type { ResolvedConfig } from '../config' +import type { Plugin, PluginContext } from '../plugin' +import { cleanUrl } from '../../shared/utils' +import type { Logger } from '..' +import type { ViteDevServer } from '../server' +import type { ESBuildOptions } from './esbuild' +import { loadTsconfigJsonForFile } from './esbuild' + +const jsxExtensionsRE = /\.(?:j|t)sx\b/ +const validExtensionRE = /\.\w+$/ + +export interface OxcOptions extends OxcTransformOptions { + include?: string | RegExp | string[] | RegExp[] + exclude?: string | RegExp | string[] | RegExp[] + jsxInject?: string +} + +export async function transformWithOxc( + ctx: PluginContext, + code: string, + filename: string, + options?: OxcTransformOptions, + inMap?: object, + config?: ResolvedConfig, + watcher?: FSWatcher, +): Promise { + let lang = options?.lang + + if (!lang) { + // if the id ends with a valid ext, use it (e.g. vue blocks) + // otherwise, cleanup the query before checking the ext + const ext = path + .extname(validExtensionRE.test(filename) ? filename : cleanUrl(filename)) + .slice(1) + + if (ext === 'cjs' || ext === 'mjs') { + lang = 'js' + } else if (ext === 'cts' || ext === 'mts') { + lang = 'ts' + } else { + lang = ext as 'js' | 'jsx' | 'ts' | 'tsx' + } + } + + const resolvedOptions = { + sourcemap: true, + ...options, + lang, + } + + if (lang === 'ts' || lang === 'tsx') { + try { + const { tsconfig: loadedTsconfig, tsconfigFile } = + await loadTsconfigJsonForFile(filename, config) + // tsconfig could be out of root, make sure it is watched on dev + if (watcher && tsconfigFile && config) { + ensureWatchedFile(watcher, tsconfigFile, config.root) + } + const loadedCompilerOptions = loadedTsconfig.compilerOptions ?? {} + // tsc compiler alwaysStrict/experimentalDecorators/importsNotUsedAsValues/preserveValueImports/target/useDefineForClassFields/verbatimModuleSyntax + + resolvedOptions.jsx ??= {} + if (loadedCompilerOptions.jsxFactory) { + resolvedOptions.jsx.pragma = loadedCompilerOptions.jsxFactory + } + if (loadedCompilerOptions.jsxFragmentFactory) { + resolvedOptions.jsx.pragmaFrag = + loadedCompilerOptions.jsxFragmentFactory + } + if (loadedCompilerOptions.jsxImportSource) { + resolvedOptions.jsx.importSource = loadedCompilerOptions.jsxImportSource + } + + switch (loadedCompilerOptions.jsx) { + case 'react-jsxdev': + resolvedOptions.jsx.runtime = 'automatic' + resolvedOptions.jsx.development = true + break + case 'react': + resolvedOptions.jsx.runtime = 'classic' + break + case 'react-jsx': + resolvedOptions.jsx.runtime = 'automatic' + break + case 'preserve': + ctx.warn('The tsconfig jsx preserve is not supported by oxc') + break + default: + break + } + } catch (e) { + if (e instanceof TSConfckParseError) { + // tsconfig could be out of root, make sure it is watched on dev + if (watcher && e.tsconfigFile && config) { + ensureWatchedFile(watcher, e.tsconfigFile, config.root) + } + } + throw e + } + } + + const result = 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$/) + + let server: ViteDevServer + + return { + name: 'vite:oxc', + configureServer(_server) { + server = _server + }, + async transform(code, id) { + if (filter(id) || filter(cleanUrl(id))) { + const result = await transformWithOxc( + this, + code, + id, + oxcTransformOptions, + undefined, + config, + server?.watcher, + ) + if (jsxInject && jsxExtensionsRE.test(id)) { + result.code = jsxInject + ';' + result.code + } + return { + code: result.code, + map: result.map, + } + } + }, + } +} + +export function convertEsbuildConfigToOxcConfig( + esbuildConfig: ESBuildOptions, + logger: Logger, +): OxcOptions { + const { jsxInject, include, exclude, ...esbuildTransformOptions } = + esbuildConfig + + const oxcOptions: OxcOptions = { + jsxInject, + include, + exclude, + jsx: {}, + } + + switch (esbuildTransformOptions.jsx) { + case 'automatic': + oxcOptions.jsx!.runtime = 'automatic' + break + + case 'transform': + oxcOptions.jsx!.runtime = 'classic' + break + + case 'preserve': + logger.warn('The esbuild jsx preserve is not supported by oxc') + break + + default: + break + } + + if (esbuildTransformOptions.jsxDev) { + oxcOptions.jsx!.development = true + } + if (esbuildTransformOptions.jsxFactory) { + oxcOptions.jsx!.pragma = esbuildTransformOptions.jsxFactory + } + if (esbuildTransformOptions.jsxFragment) { + oxcOptions.jsx!.pragmaFrag = esbuildTransformOptions.jsxFragment + } + if (esbuildTransformOptions.jsxImportSource) { + oxcOptions.jsx!.importSource = esbuildTransformOptions.jsxImportSource + } + if (esbuildTransformOptions.loader) { + if ( + ['.js', '.jsx', '.ts', 'tsx'].includes(esbuildTransformOptions.loader) + ) { + oxcOptions.lang = esbuildTransformOptions.loader as + | 'js' + | 'jsx' + | 'ts' + | 'tsx' + } else { + logger.warn( + `The esbuild loader ${esbuildTransformOptions.loader} is not supported by oxc`, + ) + } + } + if (esbuildTransformOptions.define) { + oxcOptions.define = esbuildTransformOptions.define + } + + switch (esbuildTransformOptions.sourcemap) { + case true: + case false: + oxcOptions.sourcemap = esbuildTransformOptions.sourcemap + break + + default: + logger.warn( + `The esbuild sourcemap ${esbuildTransformOptions.sourcemap} is not supported by oxc`, + ) + break + } + + return oxcOptions +} diff --git a/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts b/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts index a991f8d378d609..48c03ae77be5a9 100644 --- a/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts +++ b/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts @@ -82,7 +82,7 @@ if (!isBuild) { const map = extractSourcemap(js) expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` { - "mappings": "AAAO,aAAM,MAAM;", + "mappings": "AAAA,OAAO,MAAM,MAAM", "sources": [ "bar.ts", ], @@ -103,7 +103,7 @@ if (!isBuild) { const map = extractSourcemap(multi) expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` { - "mappings": "AACA;AAAA,EACE;AAAA,OACK;AAEP,QAAQ,IAAI,yBAAyB,GAAG;", + "mappings": "AACA,SACE,WACK,2BAA2B;AAElC,QAAQ,IAAI,yBAAyB,IAAI", "sources": [ "with-multiline-import.ts", ], @@ -177,7 +177,7 @@ describe.runIf(isBuild)('build tests', () => { const map = findAssetFile(/with-define-object.*\.js\.map/) expect(formatSourcemapForSnapshot(JSON.parse(map))).toMatchInlineSnapshot(` { - "mappings": "qBAEA,SAAS,GAAO,CACd,EAAA,CACF,CAEA,SAAS,GAAY,CAEnB,QAAQ,MAAM,qBAAsB,CAAA,CACtC,CAEA,EAAA", + "mappings": "qBAEA,SAAS,GAAO,CACd,EAAA,CACD,CAED,SAAS,GAAY,CAEnB,QAAQ,MAAM,qBAAsB,CAAA,CACrC,CAED,EAAA", "sources": [ "../../with-define-object.ts", ], diff --git a/playground/tsconfig-json/__tests__/tsconfig-json.spec.ts b/playground/tsconfig-json/__tests__/tsconfig-json.spec.ts index 1a4bcbff29cb8a..34704dc9dcad1c 100644 --- a/playground/tsconfig-json/__tests__/tsconfig-json.spec.ts +++ b/playground/tsconfig-json/__tests__/tsconfig-json.spec.ts @@ -4,7 +4,7 @@ import { transformWithEsbuild } from 'vite' import { describe, expect, test } from 'vitest' import { browserLogs, isServe, serverLogs } from '~utils' -test('should respected each `tsconfig.json`s compilerOptions', () => { +test.skip('should respected each `tsconfig.json`s compilerOptions', () => { // main side effect should be called (because of `"verbatimModuleSyntax": true`) expect(browserLogs).toContain('main side effect') // main base setter should not be called (because of `"useDefineForClassFields": true"`) diff --git a/vitest.config.e2e.ts b/vitest.config.e2e.ts index f101748e3cc3b6..370c48aef9a503 100644 --- a/vitest.config.e2e.ts +++ b/vitest.config.e2e.ts @@ -23,6 +23,7 @@ export default defineConfig({ './playground/lib/**/*.spec.[tj]s', // umd format './playground/object-hooks/**/*.spec.[tj]s', // object hook sequential './playground/optimize-deps/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2031 + './playground/tsconfig-json/__tests__/**/*.spec.[tj]s', // decorators is not supported by oxc ] : []), ...defaultExclude, From d9e05eddc799a73a29833465d69cd305b6837077 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Fri, 11 Oct 2024 21:34:06 +0900 Subject: [PATCH 27/46] feat: use `onlyRemoveTypeImports` --- packages/vite/src/node/plugins/oxc.ts | 43 ++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/node/plugins/oxc.ts b/packages/vite/src/node/plugins/oxc.ts index c27fcdf2dadec3..e0578bb314d391 100644 --- a/packages/vite/src/node/plugins/oxc.ts +++ b/packages/vite/src/node/plugins/oxc.ts @@ -68,7 +68,7 @@ export async function transformWithOxc( ensureWatchedFile(watcher, tsconfigFile, config.root) } const loadedCompilerOptions = loadedTsconfig.compilerOptions ?? {} - // tsc compiler alwaysStrict/experimentalDecorators/importsNotUsedAsValues/preserveValueImports/target/useDefineForClassFields/verbatimModuleSyntax + // tsc compiler experimentalDecorators/target/useDefineForClassFields resolvedOptions.jsx ??= {} if (loadedCompilerOptions.jsxFactory) { @@ -99,6 +99,47 @@ export async function transformWithOxc( default: break } + + /** + * | preserveValueImports | importsNotUsedAsValues | verbatimModuleSyntax | onlyRemoveTypeImports | + * | -------------------- | ---------------------- | -------------------- |---------------------- | + * | false | remove | false | false | + * | false | preserve, error | - | - | + * | true | remove | - | - | + * | true | preserve, error | true | true | + */ + if (loadedCompilerOptions.verbatimModuleSyntax !== undefined) { + resolvedOptions.typescript ??= {} + resolvedOptions.typescript.onlyRemoveTypeImports = + loadedCompilerOptions.verbatimModuleSyntax + } else if ( + loadedCompilerOptions.preserveValueImports !== undefined || + loadedCompilerOptions.importsNotUsedAsValues !== undefined + ) { + const preserveValueImports = + loadedCompilerOptions.preserveValueImports ?? false + const importsNotUsedAsValues = + loadedCompilerOptions.importsNotUsedAsValues ?? 'remove' + if ( + preserveValueImports === false && + importsNotUsedAsValues === 'remove' + ) { + resolvedOptions.typescript ??= {} + resolvedOptions.typescript.onlyRemoveTypeImports = true + } else if ( + preserveValueImports === true && + (importsNotUsedAsValues === 'preserve' || + importsNotUsedAsValues === 'error') + ) { + resolvedOptions.typescript ??= {} + resolvedOptions.typescript.onlyRemoveTypeImports = false + } else { + ctx.warn( + `preserveValueImports=${preserveValueImports} + importsNotUsedAsValues=${importsNotUsedAsValues} is not supported by oxc.` + + 'Please migrate to the new verbatimModuleSyntax option.', + ) + } + } } catch (e) { if (e instanceof TSConfckParseError) { // tsconfig could be out of root, make sure it is watched on dev From 4595c00bec932e2e68d7aacb5401b83f0aac8576 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sat, 12 Oct 2024 17:47:53 +0800 Subject: [PATCH 28/46] chore: enable continuous release under rolldown-vite (#61) chore: revert package name change + use non-compact continuous release URLs chore: update lockfile --- .github/workflows/preview-release.yml | 6 +++--- packages/vite/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/preview-release.yml b/.github/workflows/preview-release.yml index 988f430572751d..06f7d4ce81ed3d 100644 --- a/.github/workflows/preview-release.yml +++ b/.github/workflows/preview-release.yml @@ -10,14 +10,14 @@ permissions: on: push: branches: - - main + - rolldown-v6 pull_request: types: [opened, synchronize, labeled] jobs: preview: if: > - github.repository == 'vitejs/vite' && + github.repository == 'rolldown/vite' && (github.event_name == 'push' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'trigger: preview'))) runs-on: ubuntu-latest @@ -35,4 +35,4 @@ jobs: working-directory: ./packages/vite run: pnpm build - - run: pnpm dlx pkg-pr-new@0.0 publish --compact --pnpm ./packages/vite + - run: pnpm dlx pkg-pr-new@0.0 publish --pnpm ./packages/vite diff --git a/packages/vite/package.json b/packages/vite/package.json index a483d3a7d4f4eb..febfe724dc561f 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -4,7 +4,7 @@ "type": "module", "license": "MIT", "author": "Evan You", - "description": "Native-ESM powered web dev build tool", + "description": "Vite on Rolldown preview", "bin": { "vite": "bin/vite.js" }, From 708eef3b322904a02a995ae5b96b85c423d4c451 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:08:21 +0900 Subject: [PATCH 29/46] feat: environment aware native plugins --- packages/vite/src/node/build.ts | 17 +++-- .../src/node/plugins/importAnalysisBuild.ts | 71 ++++++++++++------- packages/vite/src/node/plugins/index.ts | 23 ++++-- 3 files changed, 73 insertions(+), 38 deletions(-) diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 70cc65954828bc..17c953dd709706 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -507,13 +507,16 @@ export async function resolveBuildPlugins(config: ResolvedConfig): Promise<{ ...(!config.isWorker ? [ config.build.manifest && enableNativePlugin - ? // TODO: make this environment-specific - nativeManifestPlugin({ - root: config.root, - outPath: - config.build.manifest === true - ? '.vite/manifest.json' - : config.build.manifest, + ? perEnvironmentPlugin('native:manifest', (environment) => { + if (!environment.config.build.manifest) return false + + return nativeManifestPlugin({ + root: environment.config.root, + outPath: + environment.config.build.manifest === true + ? '.vite/manifest.json' + : environment.config.build.manifest, + }) as unknown as Plugin }) : manifestPlugin(), ssrManifestPlugin(), diff --git a/packages/vite/src/node/plugins/importAnalysisBuild.ts b/packages/vite/src/node/plugins/importAnalysisBuild.ts index bb24f9610194dd..7a475e5f7af59d 100644 --- a/packages/vite/src/node/plugins/importAnalysisBuild.ts +++ b/packages/vite/src/node/plugins/importAnalysisBuild.ts @@ -15,11 +15,11 @@ import { isInNodeModules, numberToPos, } from '../utils' -import type { Plugin } from '../plugin' +import { type Plugin, perEnvironmentPlugin } from '../plugin' import type { ResolvedConfig } from '../config' import { toOutputFilePathInJS } from '../build' import { genSourceMapUrl } from '../server/sourcemap' -import type { Environment } from '../environment' +import type { PartialEnvironment } from '../baseEnvironment' import { removedPureCssFilesCache } from './css' import { createParseErrorInfo } from './importAnalysis' import { getChunkMetadata } from './metadata' @@ -168,21 +168,12 @@ function preload( }) } -/** - * Build only. During serve this is performed as part of ./importAnalysis. - */ -export function buildImportAnalysisPlugin(config: ResolvedConfig): [Plugin] { - const getInsertPreload = (environment: Environment) => - environment.config.consumer === 'client' && - !config.isWorker && - !config.build.lib - - const enableNativePlugin = config.experimental.enableNativePlugin - const renderBuiltUrl = config.experimental.renderBuiltUrl - const isRelativeBase = config.base === './' || config.base === '' - - // TODO: make this environment-specific - const { modulePreload } = config.build // this.environment.config.build +function getPreloadCode( + environment: PartialEnvironment, + renderBuiltUrlBoolean: boolean, + isRelativeBase: boolean, +) { + const { modulePreload } = environment.config.build const scriptRel = modulePreload && modulePreload.polyfill @@ -197,15 +188,30 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): [Plugin] { // using regex over this list to workaround the fact that module preload wasn't // configurable. const assetsURL = - renderBuiltUrl || isRelativeBase + renderBuiltUrlBoolean || isRelativeBase ? // If `experimental.renderBuiltUrl` is used, the dependencies might be relative to the current chunk. // If relative base is used, the dependencies are relative to the current chunk. // The importerUrl is passed as third parameter to __vitePreload in this case `function(dep, importerUrl) { return new URL(dep, importerUrl).href }` : // If the base isn't relative, then the deps are relative to the projects `outDir` and the base // is appended inside __vitePreload too. - `function(dep) { return ${JSON.stringify(config.base)}+dep }` + `function(dep) { return ${JSON.stringify(environment.config.base)}+dep }` const preloadCode = `const scriptRel = ${scriptRel};const assetsURL = ${assetsURL};const seen = {};export const ${preloadMethod} = ${preload.toString()}` + return preloadCode +} + +/** + * Build only. During serve this is performed as part of ./importAnalysis. + */ +export function buildImportAnalysisPlugin(config: ResolvedConfig): [Plugin] { + const getInsertPreload = (environment: PartialEnvironment) => + environment.config.consumer === 'client' && + !config.isWorker && + !config.build.lib + + const enableNativePlugin = config.experimental.enableNativePlugin + const renderBuiltUrl = config.experimental.renderBuiltUrl + const isRelativeBase = config.base === './' || config.base === '' const jsPlugin = { name: 'vite:build-import-analysis', @@ -217,6 +223,11 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): [Plugin] { load(id) { if (id === preloadHelperId) { + const preloadCode = getPreloadCode( + this.environment, + !!renderBuiltUrl, + isRelativeBase, + ) return { code: preloadCode, moduleSideEffects: false } } }, @@ -731,14 +742,20 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): [Plugin] { return [ jsPlugin, enableNativePlugin - ? nativeBuildImportAnalysisPlugin({ - preloadCode: preloadCode, - // @ts-expect-error make this environment-specific - insertPreload: getInsertPreload({ config: { consumer: 'client' } }), - /// this field looks redundant, put a dummy value for now - optimizeModulePreloadRelativePaths: false, - renderBuiltUrl: Boolean(renderBuiltUrl), - isRelativeBase: isRelativeBase, + ? perEnvironmentPlugin('native:import-analysis-build', (environment) => { + const preloadCode = getPreloadCode( + environment, + !!renderBuiltUrl, + isRelativeBase, + ) + return nativeBuildImportAnalysisPlugin({ + preloadCode, + insertPreload: getInsertPreload(environment), + // this field looks redundant, put a dummy value for now + optimizeModulePreloadRelativePaths: false, + renderBuiltUrl: !!renderBuiltUrl, + isRelativeBase, + }) as unknown as Plugin }) : null, ].filter(Boolean) as [Plugin] diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index 5fd032ce6fdccf..968dd94b586eab 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -12,7 +12,12 @@ import { } from 'rolldown/experimental' import type { PluginHookUtils, ResolvedConfig } from '../config' import { isDepOptimizationDisabled } from '../optimizer' -import type { HookHandler, Plugin, PluginWithRequiredHook } from '../plugin' +import { + type HookHandler, + type Plugin, + type PluginWithRequiredHook, + perEnvironmentPlugin, +} from '../plugin' import { watchPackageDataPlugin } from '../packages' import { jsonPlugin } from './json' import { filteredResolvePlugin, resolvePlugin } from './resolve' @@ -76,9 +81,19 @@ export async function resolvePlugins( modulePreload !== false && modulePreload.polyfill ? enableNativePlugin - ? nativeModulePreloadPolyfillPlugin({ - skip: Boolean(config.command !== 'build' || config.build.ssr), - }) + ? perEnvironmentPlugin( + 'native:modulepreload-polyfill', + (environment) => { + if ( + config.command !== 'build' || + environment.config.consumer !== 'client' + ) + return false + return nativeModulePreloadPolyfillPlugin({ + skip: false, + }) as unknown as Plugin + }, + ) : modulePreloadPolyfillPlugin(config) : null, enableNativePlugin From bf628df5d107a8620c49abbe62f1424a697894ca Mon Sep 17 00:00:00 2001 From: underfin Date: Thu, 17 Oct 2024 17:48:37 +0800 Subject: [PATCH 30/46] feat: export transformWithOxc (#62) --- packages/vite/index.cjs | 1 + packages/vite/rollup.dts.config.ts | 3 ++- packages/vite/src/node/index.ts | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/vite/index.cjs b/packages/vite/index.cjs index 70515aa90c7a8d..520c58c7cccc38 100644 --- a/packages/vite/index.cjs +++ b/packages/vite/index.cjs @@ -15,6 +15,7 @@ const asyncFunctions = [ 'createServer', 'preview', 'transformWithEsbuild', + 'transformWithOxc', 'resolveConfig', 'optimizeDeps', 'formatPostcssSourceMap', diff --git a/packages/vite/rollup.dts.config.ts b/packages/vite/rollup.dts.config.ts index 3a3876f46bbc70..0b3423f4b0ab6b 100644 --- a/packages/vite/rollup.dts.config.ts +++ b/packages/vite/rollup.dts.config.ts @@ -51,10 +51,11 @@ const identifierReplacements: Record> = { Plugin$1: 'rolldown.Plugin', PluginContext$1: 'rolldown.PluginContext', TransformPluginContext$1: 'rolldown.TransformPluginContext', - TransformResult$2: 'rolldown.TransformResult', + TransformResult$3: 'rolldown.TransformResult', }, 'rolldown/experimental': { TransformOptions$2: 'rolldown_experimental_TransformOptions', + TransformResult$2: 'rolldown_experimental_TransformResult', }, esbuild: { TransformResult$1: 'esbuild_TransformResult', diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index 312dbb2defbf6e..04b5c7d8d6e621 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -19,6 +19,7 @@ export { createIdResolver } from './idResolver' export { formatPostcssSourceMap, preprocessCSS } from './plugins/css' export { transformWithEsbuild } from './plugins/esbuild' +export { transformWithOxc } from './plugins/oxc' export { buildErrorMessage } from './server/middlewares/error' export { From 19acd227e5c59e6f8102483a0c74cdf7cba7146c Mon Sep 17 00:00:00 2001 From: underfin Date: Fri, 18 Oct 2024 17:56:28 +0800 Subject: [PATCH 31/46] feat: add oxc jsxInclude and jsxExclude (#63) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 翠 / green --- packages/vite/src/node/plugins/oxc.ts | 37 +++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/plugins/oxc.ts b/packages/vite/src/node/plugins/oxc.ts index e0578bb314d391..d7d5b03ec2f80e 100644 --- a/packages/vite/src/node/plugins/oxc.ts +++ b/packages/vite/src/node/plugins/oxc.ts @@ -24,6 +24,8 @@ export interface OxcOptions extends OxcTransformOptions { include?: string | RegExp | string[] | RegExp[] exclude?: string | RegExp | string[] | RegExp[] jsxInject?: string + jsxInclude?: string | RegExp | string[] | RegExp[] + jsxExclude?: string | RegExp | string[] | RegExp[] } export async function transformWithOxc( @@ -176,9 +178,26 @@ export async function transformWithOxc( export function oxcPlugin(config: ResolvedConfig): Plugin { const options = config.oxc as OxcOptions - const { jsxInject, include, exclude, ...oxcTransformOptions } = options + const { + jsxInject, + include, + exclude, + jsxInclude, + jsxExclude, + ...oxcTransformOptions + } = options - const filter = createFilter(include || /\.(m?ts|[jt]sx)$/, exclude || /\.js$/) + const defaultInclude = Array.isArray(include) + ? include + : [include || /\.(m?ts|[jt]sx)$/] + const filter = createFilter( + defaultInclude.concat(jsxInclude || []), + exclude || /\.js$/, + ) + const jsxFilter = createFilter( + jsxInclude || /\.jsx$/, + jsxExclude || /\.(m?[jt]s|tsx)$/, + ) let server: ViteDevServer @@ -189,6 +208,20 @@ export function oxcPlugin(config: ResolvedConfig): Plugin { }, async transform(code, id) { if (filter(id) || filter(cleanUrl(id))) { + // disable refresh at ssr + if ( + this.environment.config.consumer === 'server' && + oxcTransformOptions.jsx?.refresh + ) { + oxcTransformOptions.jsx.refresh = false + } + if ( + (jsxFilter(id) || jsxFilter(cleanUrl(id))) && + !oxcTransformOptions.lang + ) { + oxcTransformOptions.lang = 'jsx' + } + const result = await transformWithOxc( this, code, From 5705e759395d093460283641a9efeed4b6253627 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:30:02 +0900 Subject: [PATCH 32/46] chore: update some tests that relies on rollup's behavior --- packages/vite/src/node/__tests__/build.spec.ts | 2 +- playground/worker/__tests__/iife/worker-iife.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/__tests__/build.spec.ts b/packages/vite/src/node/__tests__/build.spec.ts index 12f83edf840367..a61db117e81112 100644 --- a/packages/vite/src/node/__tests__/build.spec.ts +++ b/packages/vite/src/node/__tests__/build.spec.ts @@ -707,7 +707,7 @@ test('default sharedConfigBuild true on build api', async () => { expect(counter).toBe(1) }) -test('adjust worker build error for worker.format', async () => { +test.skip('adjust worker build error for worker.format', async () => { try { await build({ root: resolve(__dirname, 'fixtures/worker-dynamic'), diff --git a/playground/worker/__tests__/iife/worker-iife.spec.ts b/playground/worker/__tests__/iife/worker-iife.spec.ts index a91ead2aee2df4..3a16a56fd03054 100644 --- a/playground/worker/__tests__/iife/worker-iife.spec.ts +++ b/playground/worker/__tests__/iife/worker-iife.spec.ts @@ -189,7 +189,7 @@ test.runIf(isServe)('sourcemap is correct after env is injected', async () => { const content = await (await response).text() const { mappings } = decodeSourceMapUrl(content) expect(mappings).toMatchInlineSnapshot( - `";;AAAA,SAAS,OAAO,kBAAkB;AAClC,SAAS,MAAM,WAAW;AAC1B,SAAS,wBAAwB;AACjC,OAAO,aAAa;AACpB,MAAM,UAAU,YAAY;AAE5B,KAAK,YAAY,CAAC,MAAM;AACtB,MAAI,EAAE,SAAS,QAAQ;AACrB,SAAK,YAAY,EAAE,KAAK,MAAM,kBAAkB,SAAS,SAAS,KAAK,CAAC;AAAA,EAC1E;AACA,MAAI,EAAE,SAAS,gBAAgB;AAC7B,SAAK,YAAY;AAAA,MACf,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AACF;AACA,KAAK,YAAY;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,QAAQ,IAAI,cAAc"`, + `";;AAAA,SAAS,OAAO,kBAAkB,8BAA8B;AAChE,SAAS,MAAM,WAAW,2BAA2B;AACrD,SAAS,wBAAwB,uBAAuB;AACxD,OAAO,aAAa,YAAY;AAChC,MAAM,UAAU,OAAO,KAAK;AAE5B,KAAK,YAAY,CAAC,MAAM;AACtB,KAAI,EAAE,SAAS,QAAQ;AACrB,OAAK,YAAY;GAAE;GAAK;GAAM;GAAkB;GAAS;GAAS;EAAM,EAAC;CAC1E;AACD,KAAI,EAAE,SAAS,gBAAgB;AAC7B,OAAK,YAAY;GACf,KAAK;GACL;GACA;GACA;GACA;GACA;EACD,EAAC;CACH;AACF;AACD,KAAK,YAAY;CACf;CACA;CACA;CACA;CACA;CACA;CACA;AACD,EAAC;AAGF,QAAQ,IAAI,eAAe"`, ) }) From 30b40e4750601926b933895c7a07f9cf8639175a Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:35:07 +0900 Subject: [PATCH 33/46] chore: skip environment-react-ssr for now --- vitest.config.e2e.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/vitest.config.e2e.ts b/vitest.config.e2e.ts index 370c48aef9a503..d93b87cb0d70a0 100644 --- a/vitest.config.e2e.ts +++ b/vitest.config.e2e.ts @@ -18,6 +18,7 @@ export default defineConfig({ ...(isBuild ? [ './playground/backend-integration/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2392 + './playground/environment-react-ssr/**/*.spec.[tj]s', // needs investigation './playground/dynamic-import/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/1843 './playground/external/**/*.spec.[tj]s', // https://github.com/rolldown/rolldown/issues/2041 './playground/lib/**/*.spec.[tj]s', // umd format From 8356aaace931aad6f34b19620d792458ea8c28e5 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:11:42 +0900 Subject: [PATCH 34/46] chore: bump rolldown --- packages/vite/package.json | 2 +- pnpm-lock.yaml | 106 ++++++++++++++++++------------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/packages/vite/package.json b/packages/vite/package.json index febfe724dc561f..85035f68801d37 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -88,7 +88,7 @@ "dependencies": { "esbuild": "^0.24.0", "postcss": "^8.4.48", - "rolldown": "https://pkg.pr.new/rolldown@e65437c", + "rolldown": "https://pkg.pr.new/rolldown@1fa5d4b", "rollup": "^4.23.0" }, "optionalDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b88cf59f165a4f..a921bc8370946f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -230,8 +230,8 @@ importers: specifier: ^8.4.48 version: 8.4.48 rolldown: - specifier: https://pkg.pr.new/rolldown@e65437c - version: https://pkg.pr.new/rolldown@e65437c + specifier: https://pkg.pr.new/rolldown@1fa5d4b + version: https://pkg.pr.new/rolldown@1fa5d4b rollup: specifier: ^4.23.0 version: 4.24.4 @@ -2812,63 +2812,63 @@ packages: '@polka/url@1.0.0-next.28': resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} - '@rolldown/binding-darwin-arm64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@e65437c': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@e65437c} + '@rolldown/binding-darwin-arm64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@1fa5d4b': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@1fa5d4b} version: 0.13.2 os: [darwin] - '@rolldown/binding-darwin-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@e65437c': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@e65437c} + '@rolldown/binding-darwin-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@1fa5d4b': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@1fa5d4b} version: 0.13.2 os: [darwin] - '@rolldown/binding-freebsd-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@e65437c': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@e65437c} + '@rolldown/binding-freebsd-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@1fa5d4b': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@1fa5d4b} version: 0.13.2 os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@e65437c': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@e65437c} + '@rolldown/binding-linux-arm-gnueabihf@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@1fa5d4b': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@1fa5d4b} version: 0.13.2 os: [linux] - '@rolldown/binding-linux-arm64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@e65437c': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@e65437c} + '@rolldown/binding-linux-arm64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@1fa5d4b': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@1fa5d4b} version: 0.13.2 os: [linux] - '@rolldown/binding-linux-arm64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@e65437c': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@e65437c} + '@rolldown/binding-linux-arm64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@1fa5d4b': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@1fa5d4b} version: 0.13.2 os: [linux] - '@rolldown/binding-linux-x64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@e65437c': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@e65437c} + '@rolldown/binding-linux-x64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@1fa5d4b': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@1fa5d4b} version: 0.13.2 os: [linux] - '@rolldown/binding-linux-x64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@e65437c': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@e65437c} + '@rolldown/binding-linux-x64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@1fa5d4b': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@1fa5d4b} version: 0.13.2 os: [linux] - '@rolldown/binding-wasm32-wasi@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@e65437c': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@e65437c} + '@rolldown/binding-wasm32-wasi@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@1fa5d4b': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@1fa5d4b} version: 0.13.2 engines: {node: '>=14.21.3'} - '@rolldown/binding-win32-arm64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@e65437c': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@e65437c} + '@rolldown/binding-win32-arm64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@1fa5d4b': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@1fa5d4b} version: 0.13.2 os: [win32] - '@rolldown/binding-win32-ia32-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@e65437c': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@e65437c} + '@rolldown/binding-win32-ia32-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@1fa5d4b': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@1fa5d4b} version: 0.13.2 os: [win32] - '@rolldown/binding-win32-x64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@e65437c': - resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@e65437c} + '@rolldown/binding-win32-x64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@1fa5d4b': + resolution: {tarball: https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@1fa5d4b} version: 0.13.2 os: [win32] @@ -6310,8 +6310,8 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rolldown@https://pkg.pr.new/rolldown@e65437c: - resolution: {tarball: https://pkg.pr.new/rolldown@e65437c} + rolldown@https://pkg.pr.new/rolldown@1fa5d4b: + resolution: {tarball: https://pkg.pr.new/rolldown@1fa5d4b} version: 0.13.2 hasBin: true @@ -8459,42 +8459,42 @@ snapshots: '@polka/url@1.0.0-next.28': {} - '@rolldown/binding-darwin-arm64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@e65437c': + '@rolldown/binding-darwin-arm64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@1fa5d4b': optional: true - '@rolldown/binding-darwin-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@e65437c': + '@rolldown/binding-darwin-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@1fa5d4b': optional: true - '@rolldown/binding-freebsd-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@e65437c': + '@rolldown/binding-freebsd-x64@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@1fa5d4b': optional: true - '@rolldown/binding-linux-arm-gnueabihf@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@e65437c': + '@rolldown/binding-linux-arm-gnueabihf@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@1fa5d4b': optional: true - '@rolldown/binding-linux-arm64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@e65437c': + '@rolldown/binding-linux-arm64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@1fa5d4b': optional: true - '@rolldown/binding-linux-arm64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@e65437c': + '@rolldown/binding-linux-arm64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@1fa5d4b': optional: true - '@rolldown/binding-linux-x64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@e65437c': + '@rolldown/binding-linux-x64-gnu@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@1fa5d4b': optional: true - '@rolldown/binding-linux-x64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@e65437c': + '@rolldown/binding-linux-x64-musl@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@1fa5d4b': optional: true - '@rolldown/binding-wasm32-wasi@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@e65437c': + '@rolldown/binding-wasm32-wasi@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@1fa5d4b': dependencies: '@napi-rs/wasm-runtime': 0.2.5 optional: true - '@rolldown/binding-win32-arm64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@e65437c': + '@rolldown/binding-win32-arm64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@1fa5d4b': optional: true - '@rolldown/binding-win32-ia32-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@e65437c': + '@rolldown/binding-win32-ia32-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@1fa5d4b': optional: true - '@rolldown/binding-win32-x64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@e65437c': + '@rolldown/binding-win32-x64-msvc@https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@1fa5d4b': optional: true '@rollup/plugin-alias@5.1.1(rollup@4.24.4)': @@ -12166,22 +12166,22 @@ snapshots: dependencies: glob: 7.2.3 - rolldown@https://pkg.pr.new/rolldown@e65437c: + rolldown@https://pkg.pr.new/rolldown@1fa5d4b: dependencies: zod: 3.23.8 optionalDependencies: - '@rolldown/binding-darwin-arm64': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@e65437c - '@rolldown/binding-darwin-x64': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@e65437c - '@rolldown/binding-freebsd-x64': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@e65437c - '@rolldown/binding-linux-arm-gnueabihf': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@e65437c - '@rolldown/binding-linux-arm64-gnu': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@e65437c - '@rolldown/binding-linux-arm64-musl': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@e65437c - '@rolldown/binding-linux-x64-gnu': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@e65437c - '@rolldown/binding-linux-x64-musl': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@e65437c - '@rolldown/binding-wasm32-wasi': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@e65437c - '@rolldown/binding-win32-arm64-msvc': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@e65437c - '@rolldown/binding-win32-ia32-msvc': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@e65437c - '@rolldown/binding-win32-x64-msvc': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@e65437c + '@rolldown/binding-darwin-arm64': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@1fa5d4b + '@rolldown/binding-darwin-x64': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@1fa5d4b + '@rolldown/binding-freebsd-x64': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@1fa5d4b + '@rolldown/binding-linux-arm-gnueabihf': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@1fa5d4b + '@rolldown/binding-linux-arm64-gnu': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@1fa5d4b + '@rolldown/binding-linux-arm64-musl': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@1fa5d4b + '@rolldown/binding-linux-x64-gnu': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@1fa5d4b + '@rolldown/binding-linux-x64-musl': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@1fa5d4b + '@rolldown/binding-wasm32-wasi': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@1fa5d4b + '@rolldown/binding-win32-arm64-msvc': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@1fa5d4b + '@rolldown/binding-win32-ia32-msvc': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-ia32-msvc@1fa5d4b + '@rolldown/binding-win32-x64-msvc': https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@1fa5d4b rollup-plugin-dts@6.1.1(rollup@4.24.4)(typescript@5.6.3): dependencies: From ae2080329d16ed1f8153151b4b741f097751c7e9 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Thu, 24 Oct 2024 18:01:59 +0900 Subject: [PATCH 35/46] chore: skip minifySyntax test as it relies on specific esbuild option --- playground/minify/__tests__/minify.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playground/minify/__tests__/minify.spec.ts b/playground/minify/__tests__/minify.spec.ts index 7b672d21134257..5f90e695e6c459 100644 --- a/playground/minify/__tests__/minify.spec.ts +++ b/playground/minify/__tests__/minify.spec.ts @@ -13,7 +13,7 @@ test.runIf(isBuild)('no minifySyntax', () => { const cssFile = files.find((f) => f.endsWith('.css')) const cssContent = readFile(path.resolve(assetsDir, cssFile)) - expect(jsContent).toContain('{console.log("hello world")}') + // expect(jsContent).toContain('{console.log("hello world")}') expect(jsContent).not.toContain('/*! explicit comment */') expect(cssContent).toContain('color:#ff0000') From 74b287ebfd6bc3468f6a4fe5a4493205ed9c7628 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Thu, 24 Oct 2024 18:04:41 +0900 Subject: [PATCH 36/46] chore: fix type errors --- packages/vite/src/node/build.ts | 8 ++++---- packages/vite/src/node/constants.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 17c953dd709706..d6665884233256 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -1340,10 +1340,10 @@ const relativeUrlMechanisms: Record< // getResolveUrl( // `'${escapeId(partialEncodeURIPath(relativePath))}', module.meta.url`, // ), - // umd: (relativePath) => - // `(typeof document === 'undefined' && typeof location === 'undefined' ? ${getFileUrlFromRelativePath( - // relativePath, - // )} : ${getRelativeUrlFromDocument(relativePath, true)})`, + umd: (relativePath) => + `(typeof document === 'undefined' && typeof location === 'undefined' ? ${getFileUrlFromRelativePath( + relativePath, + )} : ${getRelativeUrlFromDocument(relativePath, true)})`, } /* end of copy */ diff --git a/packages/vite/src/node/constants.ts b/packages/vite/src/node/constants.ts index 37065de5fc2363..35b23efef139b4 100644 --- a/packages/vite/src/node/constants.ts +++ b/packages/vite/src/node/constants.ts @@ -26,10 +26,10 @@ export const ROLLUP_HOOKS = [ 'intro', 'outro', 'closeBundle', - // 'closeWatcher', + 'closeWatcher', 'load', 'moduleParsed', - // 'watchChange', + 'watchChange', 'resolveDynamicImport', 'resolveId', // 'shouldTransformCachedModule', From cd614c5ec613c36f32ed3409cf330f2441d28aa3 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 6 Nov 2024 16:16:50 +0900 Subject: [PATCH 37/46] chore: skip sideeffects set for HTML scripts for now --- packages/vite/src/node/plugins/html.ts | 37 +++++++++++++------------- playground/html/__tests__/html.spec.ts | 2 +- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index e4ede5aa48a2a5..35093e91ac912b 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -488,24 +488,25 @@ export function buildHtmlPlugin(config: ResolvedConfig): RolldownPlugin { if (isModule) { inlineModuleIndex++ if (url && !isExcludedUrl(url) && !isPublicFile) { - setModuleSideEffectPromises.push( - this.resolve(url, id).then((resolved) => { - if (!resolved) { - return Promise.reject( - new Error(`Failed to resolve ${url} from ${id}`), - ) - } - // set moduleSideEffects to keep the module even if `treeshake.moduleSideEffects=false` is set - const moduleInfo = this.getModuleInfo(resolved.id) - if (moduleInfo) { - moduleInfo.moduleSideEffects = true - } else if (!resolved.external) { - return this.load(resolved).then((mod) => { - mod.moduleSideEffects = true - }) - } - }), - ) + // FIXME: rolldown does not support `this.load`: https://github.com/rolldown/rolldown/issues/2355 + // setModuleSideEffectPromises.push( + // this.resolve(url, id).then((resolved) => { + // if (!resolved) { + // return Promise.reject( + // new Error(`Failed to resolve ${url} from ${id}`), + // ) + // } + // // set moduleSideEffects to keep the module even if `treeshake.moduleSideEffects=false` is set + // const moduleInfo = this.getModuleInfo(resolved.id) + // if (moduleInfo) { + // moduleInfo.moduleSideEffects = true + // } else if (!resolved.external) { + // return this.load(resolved).then((mod) => { + // mod.moduleSideEffects = true + // }) + // } + // }), + // ) //