From 0cc743bc1818f0de83d55581e91d03c15611c0e6 Mon Sep 17 00:00:00 2001 From: patak-dev Date: Tue, 22 Mar 2022 14:16:10 +0100 Subject: [PATCH 01/92] release: plugin-vue@2.3.0-beta.0 --- packages/plugin-vue/CHANGELOG.md | 9 +++++++++ packages/plugin-vue/package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/plugin-vue/CHANGELOG.md b/packages/plugin-vue/CHANGELOG.md index 6fb47f160b1689..c4b3f8e1a1ddb0 100644 --- a/packages/plugin-vue/CHANGELOG.md +++ b/packages/plugin-vue/CHANGELOG.md @@ -1,3 +1,12 @@ +## 2.3.0-beta.0 (2022-03-22) + +* fix(deps): update all non-major dependencies (#7392) ([b63fc3b](https://github.com/vitejs/vite/commit/b63fc3b)), closes [#7392](https://github.com/vitejs/vite/issues/7392) +* feat: css sourcemap support during dev (#7173) ([38a655f](https://github.com/vitejs/vite/commit/38a655f)), closes [#7173](https://github.com/vitejs/vite/issues/7173) +* chore(deps): update all non-major dependencies (#6905) ([839665c](https://github.com/vitejs/vite/commit/839665c)), closes [#6905](https://github.com/vitejs/vite/issues/6905) +* docs(vue): add transformAssetUrls example (#7232) ([08e928c](https://github.com/vitejs/vite/commit/08e928c)), closes [#7232](https://github.com/vitejs/vite/issues/7232) + + + ## [2.2.4](https://github.com/vitejs/vite/compare/plugin-vue@2.2.3...plugin-vue@2.2.4) (2022-02-28) diff --git a/packages/plugin-vue/package.json b/packages/plugin-vue/package.json index 6eccae93df840d..bf4749ac8ee428 100644 --- a/packages/plugin-vue/package.json +++ b/packages/plugin-vue/package.json @@ -1,6 +1,6 @@ { "name": "@vitejs/plugin-vue", - "version": "2.2.4", + "version": "2.3.0-beta.0", "license": "MIT", "author": "Evan You", "files": [ From 676f54584c981c18413194bb539165292a66bf7c Mon Sep 17 00:00:00 2001 From: patak Date: Wed, 23 Mar 2022 09:13:06 +0100 Subject: [PATCH 02/92] feat: non-blocking scanning of dependencies (#7379) --- .../playground/cli-module/__tests__/serve.js | 2 +- packages/vite/src/node/index.ts | 3 +- .../src/node/optimizer/esbuildDepPlugin.ts | 10 +- packages/vite/src/node/optimizer/index.ts | 858 +++++++++--------- .../src/node/optimizer/registerMissing.ts | 283 ++++-- packages/vite/src/node/optimizer/scan.ts | 15 +- packages/vite/src/node/plugin.ts | 9 +- .../vite/src/node/plugins/importAnalysis.ts | 30 +- .../vite/src/node/plugins/optimizedDeps.ts | 10 +- packages/vite/src/node/plugins/preAlias.ts | 6 +- packages/vite/src/node/plugins/resolve.ts | 60 +- packages/vite/src/node/server/index.ts | 70 +- .../vite/src/node/server/pluginContainer.ts | 15 +- 13 files changed, 736 insertions(+), 635 deletions(-) diff --git a/packages/playground/cli-module/__tests__/serve.js b/packages/playground/cli-module/__tests__/serve.js index 1cda05f0adc21a..2b354f566524bf 100644 --- a/packages/playground/cli-module/__tests__/serve.js +++ b/packages/playground/cli-module/__tests__/serve.js @@ -78,7 +78,7 @@ exports.serve = async function serve(root, isProd) { const timeoutError = `server process still alive after 3s` try { killProcess(serverProcess) - await resolvedOrTimeout(serverProcess, 3000, timeoutError) + await resolvedOrTimeout(serverProcess, 10000, timeoutError) } catch (e) { if (e === timeoutError || (!serverProcess.killed && !isWindows)) { collectErrorStreams('server', e) diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index f0f217c9be03d3..027a715c454a74 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -40,7 +40,8 @@ export type { DepOptimizationOptions, DepOptimizationResult, DepOptimizationProcessing, - OptimizedDepInfo + OptimizedDepInfo, + OptimizedDeps } from './optimizer' export type { Plugin } from './plugin' export type { PackageCache, PackageData } from './packages' diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index 3ff86c213a54a2..4303be0ec876e7 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -37,8 +37,7 @@ const externalTypes = [ export function esbuildDepPlugin( qualified: Record, exportsData: Record, - config: ResolvedConfig, - ssr?: boolean + config: ResolvedConfig ): Plugin { // remove optimizable extensions from `externalTypes` list const allExternalTypes = config.optimizeDeps.extensions @@ -48,12 +47,13 @@ export function esbuildDepPlugin( : externalTypes // default resolver which prefers ESM - const _resolve = config.createResolver({ asSrc: false }) + const _resolve = config.createResolver({ asSrc: false, scan: true }) // cjs resolver that prefers Node const _resolveRequire = config.createResolver({ asSrc: false, - isRequire: true + isRequire: true, + scan: true }) const resolve = ( @@ -72,7 +72,7 @@ export function esbuildDepPlugin( _importer = importer in qualified ? qualified[importer] : importer } const resolver = kind.startsWith('require') ? _resolveRequire : _resolve - return resolver(id, _importer, undefined, ssr) + return resolver(id, _importer, undefined) } return { diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index 2bdd54dc3a899a..cefa24133db529 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -21,7 +21,8 @@ import { scanImports } from './scan' import { transformWithEsbuild } from '../plugins/esbuild' import { performance } from 'perf_hooks' -const debug = createDebugger('vite:deps') +export const debuggerViteDeps = createDebugger('vite:deps') +const debug = debuggerViteDeps const isDebugEnabled = _debug('vite:deps').enabled const jsExtensionRE = /\.js$/i @@ -33,6 +34,12 @@ export type ExportsData = ReturnType & { hasReExports?: true } +export interface OptimizedDeps { + metadata: DepOptimizationMetadata + scanProcessing?: Promise + registerMissingImport: (id: string, resolved: string) => OptimizedDepInfo +} + export interface DepOptimizationOptions { /** * By default, Vite will crawl your `index.html` to detect dependencies that @@ -97,17 +104,11 @@ export interface DepOptimizationOptions { } export interface DepOptimizationResult { - /** - * After a re-optimization, the internal bundled chunks may change - * and a full page reload is required if that is the case - * If the files are stable, we can avoid the reload that is expensive - * for large applications - */ - alteredFiles: boolean + metadata: DepOptimizationMetadata /** * When doing a re-run, if there are newly discovered dependendencies - * the page reload will be delayed until the next rerun so the - * result will be discarded + * the page reload will be delayed until the next rerun so we need + * to be able to discard the result */ commit: () => void cancel: () => void @@ -119,8 +120,9 @@ export interface DepOptimizationProcessing { } export interface OptimizedDepInfo { + id: string file: string - src: string + src?: string needsInterop?: boolean browserHash?: string fileHash?: string @@ -155,6 +157,10 @@ export interface DepOptimizationMetadata { * Metadata for each newly discovered dependency after processing */ discovered: Record + /** + * OptimizedDepInfo list + */ + depInfoList: OptimizedDepInfo[] } /** @@ -163,45 +169,65 @@ export interface DepOptimizationMetadata { export async function optimizeDeps( config: ResolvedConfig, force = config.server.force, - asCommand = false, - newDeps?: Record, // missing imports encountered after server has started - ssr?: boolean + asCommand = false ): Promise { - const { metadata, run } = await createOptimizeDepsRun( + const log = asCommand ? config.logger.info : debug + + const cachedMetadata = loadCachedDepOptimizationMetadata( config, force, - asCommand, - null, - newDeps, - ssr + asCommand ) - const result = await run() + if (cachedMetadata) { + return cachedMetadata + } + const depsInfo = await discoverProjectDependencies(config) + + const depsString = depsLogString(Object.keys(depsInfo)) + log(colors.green(`Optimizing dependencies:\n ${depsString}`)) + + const result = await runOptimizeDeps(config, depsInfo) + result.commit() - return metadata + + return result.metadata +} + +export function createOptimizedDepsMetadata( + config: ResolvedConfig, + timestamp?: string +): DepOptimizationMetadata { + const hash = getDepHash(config) + return { + hash, + browserHash: getOptimizedBrowserHash(hash, {}, timestamp), + optimized: {}, + chunks: {}, + discovered: {}, + depInfoList: [] + } +} + +export function addOptimizedDepInfo( + metadata: DepOptimizationMetadata, + type: 'optimized' | 'discovered' | 'chunks', + depInfo: OptimizedDepInfo +): OptimizedDepInfo { + metadata[type][depInfo.id] = depInfo + metadata.depInfoList.push(depInfo) + return depInfo } /** - * Internally, Vite uses this function to prepare a optimizeDeps run. When Vite starts, we can get - * the metadata and start the server without waiting for the optimizeDeps processing to be completed + * Creates the initial dep optimization metadata, loading it from the deps cache + * if it exists and pre-bundling isn't forced */ -export async function createOptimizeDepsRun( +export function loadCachedDepOptimizationMetadata( config: ResolvedConfig, force = config.server.force, - asCommand = false, - currentData: DepOptimizationMetadata | null = null, - newDeps?: Record, // missing imports encountered after server has started - ssr?: boolean -): Promise<{ - metadata: DepOptimizationMetadata - run: () => Promise -}> { - config = { - ...config, - command: 'build' - } - - const { root, logger } = config - const log = asCommand ? logger.info : debug + asCommand = false +): DepOptimizationMetadata | undefined { + const log = asCommand ? config.logger.info : debug // Before Vite 2.9, dependencies were cached in the root of the cacheDir // For compat, we remove the cache if we find the old structure @@ -210,49 +236,103 @@ export async function createOptimizeDepsRun( } const depsCacheDir = getDepsCacheDir(config) - const processingCacheDir = getProcessingDepsCacheDir(config) - - const mainHash = getDepHash(root, config) - - const processing = newDepOptimizationProcessing() - - const metadata: DepOptimizationMetadata = { - hash: mainHash, - browserHash: mainHash, - optimized: {}, - chunks: {}, - discovered: {} - } if (!force) { - let prevData: DepOptimizationMetadata | undefined + let cachedMetadata: DepOptimizationMetadata | undefined try { - const prevDataPath = path.join(depsCacheDir, '_metadata.json') - prevData = parseOptimizedDepsMetadata( - fs.readFileSync(prevDataPath, 'utf-8'), + const cachedMetadataPath = path.join(depsCacheDir, '_metadata.json') + cachedMetadata = parseOptimizedDepsMetadata( + fs.readFileSync(cachedMetadataPath, 'utf-8'), depsCacheDir ) } catch (e) {} // hash is consistent, no need to re-bundle - if (prevData && prevData.hash === metadata.hash) { + if (cachedMetadata && cachedMetadata.hash === getDepHash(config)) { log('Hash is consistent. Skipping. Use --force to override.') // Nothing to commit or cancel as we are using the cache, we only // need to resolve the processing promise so requests can move on - const resolve = () => { - processing.resolve() - } - return { - metadata: prevData, - run: async () => { - return { - alteredFiles: false, - commit: resolve, - cancel: resolve - } - } - } + return cachedMetadata + } + } + + // Start with a fresh cache + removeDirSync(depsCacheDir) +} + +/** + * Initial optimizeDeps at server start. Perform a fast scan using esbuild to + * find deps to pre-bundle and include user hard-coded dependencies + */ +export async function discoverProjectDependencies( + config: ResolvedConfig, + timestamp?: string +): Promise> { + const { deps, missing } = await scanImports(config) + + const missingIds = Object.keys(missing) + if (missingIds.length) { + throw new Error( + `The following dependencies are imported but could not be resolved:\n\n ${missingIds + .map( + (id) => + `${colors.cyan(id)} ${colors.white( + colors.dim(`(imported by ${missing[id]})`) + )}` + ) + .join(`\n `)}\n\nAre they installed?` + ) + } + + await addManuallyIncludedOptimizeDeps(deps, config) + + const browserHash = getOptimizedBrowserHash( + getDepHash(config), + deps, + timestamp + ) + const discovered: Record = {} + for (const id in deps) { + const entry = deps[id] + discovered[id] = { + id, + file: getOptimizedDepPath(id, config), + src: entry, + browserHash: browserHash } } + return discovered +} + +export function depsLogString(qualifiedIds: string[]): string { + if (isDebugEnabled) { + return colors.yellow(qualifiedIds.join(`\n `)) + } else { + const total = qualifiedIds.length + const maxListed = 5 + const listed = Math.min(total, maxListed) + const extra = Math.max(0, total - maxListed) + return colors.yellow( + qualifiedIds.slice(0, listed).join(`, `) + + (extra > 0 ? `, ...and ${extra} more` : ``) + ) + } +} + +/** + * Internally, Vite uses this function to prepare a optimizeDeps run. When Vite starts, we can get + * the metadata and start the server without waiting for the optimizeDeps processing to be completed + */ +export async function runOptimizeDeps( + config: ResolvedConfig, + depsInfo: Record +): Promise { + config = { + ...config, + command: 'build' + } + + const depsCacheDir = getDepsCacheDir(config) + const processingCacheDir = getProcessingDepsCacheDir(config) // Create a temporal directory so we don't need to delete optimized deps // until they have been processed. This also avoids leaving the deps cache @@ -270,319 +350,191 @@ export async function createOptimizeDepsRun( JSON.stringify({ type: 'module' }) ) - let newBrowserHash: string - - let deps: Record - if (!newDeps) { - // Initial optimizeDeps at server start. Perform a fast scan using esbuild to - // find deps to pre-bundle and include user hard-coded dependencies - - let missing: Record - ;({ deps, missing } = await scanImports(config)) - - const missingIds = Object.keys(missing) - if (missingIds.length) { - processing.resolve() - throw new Error( - `The following dependencies are imported but could not be resolved:\n\n ${missingIds - .map( - (id) => - `${colors.cyan(id)} ${colors.white( - colors.dim(`(imported by ${missing[id]})`) - )}` - ) - .join(`\n `)}\n\nAre they installed?` - ) - } - - try { - await addManuallyIncludedOptimizeDeps(deps, config) - } catch (e) { - processing.resolve() - throw e - } - - // update browser hash - newBrowserHash = metadata.browserHash = getOptimizedBrowserHash( - metadata.hash, - deps - ) + const metadata = createOptimizedDepsMetadata(config) - // We generate the mapping of dependency ids to their cache file location - // before processing the dependencies with esbuild. This allow us to continue - // processing files in the importAnalysis and resolve plugins - for (const id in deps) { - const entry = deps[id] - metadata.optimized[id] = { - file: getOptimizedDepPath(id, config), - src: entry, - browserHash: newBrowserHash, - processing: processing.promise - } - } - } else { - // Missing dependencies were found at run-time, optimizeDeps called while the - // server is running - deps = depsFromOptimizedDepInfo(newDeps) + metadata.browserHash = getOptimizedBrowserHash( + metadata.hash, + depsFromOptimizedDepInfo(depsInfo) + ) - metadata.optimized = newDeps + // We prebundle dependencies with esbuild and cache them, but there is no need + // to wait here. Code that needs to access the cached deps needs to await + // the optimizedDepInfo.processing promise for each dep - // For reruns keep current global browser hash and newDeps individual hashes until we know - // if files are stable so we can avoid a full page reload - metadata.browserHash = currentData!.browserHash - newBrowserHash = getOptimizedBrowserHash(metadata.hash, deps) - } + const qualifiedIds = Object.keys(depsInfo) - return { metadata, run: prebundleDeps } - - async function prebundleDeps(): Promise { - // We prebundle dependencies with esbuild and cache them, but there is no need - // to wait here. Code that needs to access the cached deps needs to await - // the optimizeDepInfo.processing promise for each dep - - const qualifiedIds = Object.keys(deps) - - if (!qualifiedIds.length) { - return { - alteredFiles: false, - commit() { - // Write metadata file, delete `deps` folder and rename the `processing` folder to `deps` - commitProcessingDepsCacheSync() - log(`No dependencies to bundle. Skipping.\n\n\n`) - processing.resolve() - }, - cancel - } + if (!qualifiedIds.length) { + return { + metadata, + commit() { + // Write metadata file, delete `deps` folder and rename the `processing` folder to `deps` + commitProcessingDepsCacheSync() + config.logger.info(`No dependencies to bundle. Skipping.\n\n\n`) + }, + cancel } + } - let depsString: string - if (isDebugEnabled) { - depsString = colors.yellow(qualifiedIds.join(`\n `)) + // esbuild generates nested directory output with lowest common ancestor base + // this is unpredictable and makes it difficult to analyze entry / output + // mapping. So what we do here is: + // 1. flatten all ids to eliminate slash + // 2. in the plugin, read the entry ourselves as virtual files to retain the + // path. + const flatIdDeps: Record = {} + const idToExports: Record = {} + const flatIdToExports: Record = {} + + const { plugins = [], ...esbuildOptions } = + config.optimizeDeps?.esbuildOptions ?? {} + + await init + for (const id in depsInfo) { + const flatId = flattenId(id) + const filePath = (flatIdDeps[flatId] = depsInfo[id].src!) + let exportsData: ExportsData + if (config.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, + plugins, + entryPoints: [filePath], + write: false, + format: 'esm' + }) + exportsData = parse(result.outputFiles[0].text) as ExportsData } else { - const total = qualifiedIds.length - const maxListed = 5 - const listed = Math.min(total, maxListed) - const extra = Math.max(0, total - maxListed) - depsString = colors.yellow( - qualifiedIds.slice(0, listed).join(`\n `) + - (extra > 0 ? `\n (...and ${extra} more)` : ``) - ) - } - - if (!asCommand) { - if (!newDeps) { - // This is auto run on server start - let the user know that we are - // pre-optimizing deps - logger.info(colors.green(`Pre-bundling dependencies:\n ${depsString}`)) - logger.info( - `(this will be run only when your dependencies or config have changed)` + const entryContent = fs.readFileSync(filePath, 'utf-8') + try { + exportsData = parse(entryContent) as ExportsData + } catch { + debug( + `Unable to parse dependency: ${id}. Trying again with a JSX transform.` ) - } - } else { - logger.info(colors.green(`Optimizing dependencies:\n ${depsString}`)) - } - - // esbuild generates nested directory output with lowest common ancestor base - // this is unpredictable and makes it difficult to analyze entry / output - // mapping. So what we do here is: - // 1. flatten all ids to eliminate slash - // 2. in the plugin, read the entry ourselves as virtual files to retain the - // path. - const flatIdDeps: Record = {} - const idToExports: Record = {} - const flatIdToExports: Record = {} - - const { plugins = [], ...esbuildOptions } = - config.optimizeDeps?.esbuildOptions ?? {} - - await init - for (const id in deps) { - const flatId = flattenId(id) - const filePath = (flatIdDeps[flatId] = deps[id]) - let exportsData: ExportsData - if ( - config.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, - plugins, - entryPoints: [filePath], - write: false, - format: 'esm' + const transformed = await transformWithEsbuild(entryContent, filePath, { + loader: 'jsx' }) - exportsData = parse(result.outputFiles[0].text) as ExportsData - } else { - const entryContent = fs.readFileSync(filePath, 'utf-8') - try { - exportsData = parse(entryContent) as ExportsData - } catch { - debug( - `Unable to parse dependency: ${id}. Trying again with a JSX transform.` - ) - const transformed = await transformWithEsbuild( - entryContent, - filePath, - { - loader: 'jsx' - } - ) - // 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 - } - exportsData = parse(transformed.code) as ExportsData + // 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 } - for (const { ss, se } of exportsData[0]) { - const exp = entryContent.slice(ss, se) - if (/export\s+\*\s+from/.test(exp)) { - exportsData.hasReExports = true - } + exportsData = parse(transformed.code) as ExportsData + } + for (const { ss, se } of exportsData[0]) { + const exp = entryContent.slice(ss, se) + if (/export\s+\*\s+from/.test(exp)) { + exportsData.hasReExports = true } } - - idToExports[id] = exportsData - flatIdToExports[flatId] = exportsData } - const define: Record = { - 'process.env.NODE_ENV': JSON.stringify(config.mode) - } - for (const key in config.define) { - const value = config.define[key] - define[key] = typeof value === 'string' ? value : JSON.stringify(value) - } + idToExports[id] = exportsData + flatIdToExports[flatId] = exportsData + } - const start = performance.now() - - const result = await build({ - absWorkingDir: process.cwd(), - entryPoints: Object.keys(flatIdDeps), - bundle: true, - format: 'esm', - target: config.build.target || undefined, - external: config.optimizeDeps?.exclude, - logLevel: 'error', - splitting: true, - sourcemap: true, - outdir: processingCacheDir, - ignoreAnnotations: true, - metafile: true, - define, - plugins: [ - ...plugins, - esbuildDepPlugin(flatIdDeps, flatIdToExports, config, ssr) - ], - ...esbuildOptions - }) + const define: Record = { + 'process.env.NODE_ENV': JSON.stringify(config.mode) + } + for (const key in config.define) { + const value = config.define[key] + define[key] = typeof value === 'string' ? value : JSON.stringify(value) + } - const meta = result.metafile! + const start = performance.now() + + const result = await build({ + absWorkingDir: process.cwd(), + entryPoints: Object.keys(flatIdDeps), + bundle: true, + format: 'esm', + target: config.build.target || undefined, + external: config.optimizeDeps?.exclude, + logLevel: 'error', + splitting: true, + sourcemap: true, + outdir: processingCacheDir, + ignoreAnnotations: true, + metafile: true, + define, + plugins: [ + ...plugins, + esbuildDepPlugin(flatIdDeps, flatIdToExports, config) + ], + ...esbuildOptions + }) - // the paths in `meta.outputs` are relative to `process.cwd()` - const processingCacheDirOutputPath = path.relative( - process.cwd(), - processingCacheDir - ) + const meta = result.metafile! - for (const id in deps) { - const optimizedInfo = metadata.optimized[id] - optimizedInfo.needsInterop = needsInterop( - id, - idToExports[id], - meta.outputs, - processingCacheDirOutputPath - ) - const output = - meta.outputs[ - path.relative(process.cwd(), getProcessingDepPath(id, config)) - ] - if (output) { - // 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 - optimizedInfo.fileHash = getHash( - metadata.hash + optimizedInfo.file + JSON.stringify(output.imports) - ) - } - } + // the paths in `meta.outputs` are relative to `process.cwd()` + const processingCacheDirOutputPath = path.relative( + process.cwd(), + processingCacheDir + ) - // This only runs when missing deps are processed. Previous optimized deps are stable if - // the newly discovered deps don't have common chunks with them. Comparing their fileHash we - // can find out if it is safe to keep the current browser state. If one of the file hashes - // changed, a full page reload is needed - let alteredFiles = false - if (currentData) { - alteredFiles = Object.keys(currentData.optimized).some((dep) => { - const currentInfo = currentData.optimized[dep] - const info = metadata.optimized[dep] - return ( - !info?.fileHash || - !currentInfo?.fileHash || - info?.fileHash !== currentInfo?.fileHash - ) - }) - debug(`optimized deps have altered files: ${alteredFiles}`) - } + for (const id in depsInfo) { + const output = esbuildOutputFromId(meta.outputs, id, processingCacheDir) + + addOptimizedDepInfo(metadata, 'optimized', { + ...depsInfo[id], + needsInterop: needsInterop(id, idToExports[id], output), + // 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 + }) + } - for (const o of Object.keys(meta.outputs)) { - if (!o.match(jsMapExtensionRE)) { - const id = path - .relative(processingCacheDirOutputPath, o) - .replace(jsExtensionRE, '') - const file = getOptimizedDepPath(id, config) - if (!findFileInfo(metadata.optimized, file)) { - metadata.chunks[id] = { - file, - src: '', - needsInterop: false, - browserHash: - (!alteredFiles && currentData?.chunks[id]?.browserHash) || - newBrowserHash - } - } + for (const o of Object.keys(meta.outputs)) { + if (!o.match(jsMapExtensionRE)) { + const id = path + .relative(processingCacheDirOutputPath, o) + .replace(jsExtensionRE, '') + const file = getOptimizedDepPath(id, config) + if ( + !findOptimizedDepInfoInRecord( + metadata.optimized, + (depInfo) => depInfo.file === file + ) + ) { + addOptimizedDepInfo(metadata, 'chunks', { + id, + file, + needsInterop: false, + browserHash: metadata.browserHash + }) } } + } - if (alteredFiles) { - metadata.browserHash = newBrowserHash - } + const dataPath = path.join(processingCacheDir, '_metadata.json') + writeFile(dataPath, stringifyOptimizedDepsMetadata(metadata, depsCacheDir)) - debug(`deps bundled in ${(performance.now() - start).toFixed(2)}ms`) + debug(`deps bundled in ${(performance.now() - start).toFixed(2)}ms`) - return { - alteredFiles, - commit() { - if (alteredFiles) { - // Overwrite individual hashes with the new global browserHash, a full page reload is required - // New deps that ended up with a different hash replaced while doing analysis import are going to - // return a not found so the browser doesn't cache them. And will properly get loaded after the reload - for (const id in deps) { - metadata.optimized[id].browserHash = newBrowserHash - } - } - // Write metadata file, delete `deps` folder and rename the new `processing` folder to `deps` in sync - commitProcessingDepsCacheSync() - processing.resolve() - }, - cancel - } + return { + metadata, + commit() { + // Write metadata file, delete `deps` folder and rename the new `processing` folder to `deps` in sync + commitProcessingDepsCacheSync() + }, + cancel } function commitProcessingDepsCacheSync() { - // Rewire the file paths from the temporal processing dir to the final deps cache dir - const dataPath = path.join(processingCacheDir, '_metadata.json') - writeFile(dataPath, stringifyOptimizedDepsMetadata(metadata, depsCacheDir)) // Processing is done, we can now replace the depsCacheDir with processingCacheDir + // Rewire the file paths from the temporal processing dir to the final deps cache dir removeDirSync(depsCacheDir) fs.renameSync(processingCacheDir, depsCacheDir) } function cancel() { removeDirSync(processingCacheDir) - processing.resolve() } } @@ -639,38 +591,20 @@ export function depsFromOptimizedDepInfo( depsInfo: Record ) { return Object.fromEntries( - Object.entries(depsInfo).map((d) => [d[0], d[1].src]) + Object.entries(depsInfo).map((d) => [d[0], d[1].src!]) ) } -export function getHash(text: string) { - return createHash('sha256').update(text).digest('hex').substring(0, 8) -} - -function getOptimizedBrowserHash(hash: string, deps: Record) { - return getHash(hash + JSON.stringify(deps)) -} - -function getCachedDepFilePath(id: string, depsCacheDir: string) { - return normalizePath(path.resolve(depsCacheDir, flattenId(id) + '.js')) -} - export function getOptimizedDepPath(id: string, config: ResolvedConfig) { - return getCachedDepFilePath(id, getDepsCacheDir(config)) + return normalizePath( + path.resolve(getDepsCacheDir(config), flattenId(id) + '.js') + ) } export function getDepsCacheDir(config: ResolvedConfig) { return normalizePath(path.resolve(config.cacheDir, 'deps')) } -function getProcessingDepFilePath(id: string, processingCacheDir: string) { - return normalizePath(path.resolve(processingCacheDir, flattenId(id) + '.js')) -} - -function getProcessingDepPath(id: string, config: ResolvedConfig) { - return getProcessingDepFilePath(id, getProcessingDepsCacheDir(config)) -} - function getProcessingDepsCacheDir(config: ResolvedConfig) { return normalizePath(path.resolve(config.cacheDir, 'processing')) } @@ -701,27 +635,48 @@ export function createIsOptimizedDepUrl(config: ResolvedConfig) { function parseOptimizedDepsMetadata( jsonMetadata: string, depsCacheDir: string -) { - const metadata = JSON.parse(jsonMetadata, (key: string, value: string) => { - // Paths can be absolute or relative to the deps cache dir where - // the _metadata.json is located - if (key === 'file' || key === 'src') { - return normalizePath(path.resolve(depsCacheDir, value)) +): DepOptimizationMetadata | undefined { + const { hash, browserHash, optimized, chunks } = JSON.parse( + jsonMetadata, + (key: string, value: string) => { + // Paths can be absolute or relative to the deps cache dir where + // the _metadata.json is located + if (key === 'file' || key === 'src') { + return normalizePath(path.resolve(depsCacheDir, value)) + } + return value } - return value - }) - const { browserHash } = metadata - for (const o of Object.keys(metadata.optimized)) { - const depInfo = metadata.optimized[o] - depInfo.browserHash = browserHash + ) + if ( + !chunks || + Object.values(optimized).some((depInfo: any) => !depInfo.fileHash) + ) { + // outdated _metadata.json version, ignore + return } - metadata.chunks ||= {} // Support missing chunks for back compat - for (const o of Object.keys(metadata.chunks)) { - const depInfo = metadata.chunks[o] - depInfo.src = '' - depInfo.browserHash = browserHash + const metadata = { + hash, + browserHash, + optimized: {}, + discovered: {}, + chunks: {}, + depInfoList: [] + } + for (const id of Object.keys(optimized)) { + addOptimizedDepInfo(metadata, 'optimized', { + ...optimized[id], + id, + browserHash + }) + } + for (const id of Object.keys(chunks)) { + addOptimizedDepInfo(metadata, 'chunks', { + ...chunks[id], + id, + browserHash, + needsInterop: false + }) } - metadata.discovered = {} return metadata } @@ -735,50 +690,53 @@ function stringifyOptimizedDepsMetadata( metadata: DepOptimizationMetadata, depsCacheDir: string ) { + const { hash, browserHash, optimized, chunks } = metadata return JSON.stringify( - metadata, - (key: string, value: any) => { - if (key === 'discovered' || key === 'processing') { - return - } + { + hash, + browserHash, + optimized: Object.fromEntries( + Object.values(optimized).map( + ({ id, src, file, fileHash, needsInterop }) => [ + id, + { + src, + file, + fileHash, + needsInterop + } + ] + ) + ), + chunks: Object.fromEntries( + Object.values(chunks).map(({ id, file }) => [id, { file }]) + ) + }, + (key: string, value: string) => { + // Paths can be absolute or relative to the deps cache dir where + // the _metadata.json is located if (key === 'file' || key === 'src') { return normalizePath(path.relative(depsCacheDir, value)) } - if (key === 'optimized') { - // Only remove browserHash for individual dep info - const cleaned: Record = {} - for (const dep of Object.keys(value)) { - const { browserHash, ...c } = value[dep] - cleaned[dep] = c - } - return cleaned - } - if (key === 'optimized') { - return Object.keys(value).reduce( - (cleaned: Record, dep: string) => { - const { browserHash, ...c } = value[dep] - cleaned[dep] = c - return cleaned - }, - {} - ) - } - if (key === 'chunks') { - return Object.keys(value).reduce( - (cleaned: Record, dep: string) => { - const { browserHash, needsInterop, src, ...c } = value[dep] - cleaned[dep] = c - return cleaned - }, - {} - ) - } return value }, 2 ) } +function esbuildOutputFromId( + outputs: Record, + id: string, + cacheDirOutputPath: string +): any { + const flatId = flattenId(id) + '.js' + return outputs[ + normalizePath( + path.relative(process.cwd(), path.join(cacheDirOutputPath, flatId)) + ) + ] +} + // https://github.com/vitejs/vite/issues/1724#issuecomment-767619642 // a list of modules that pretends to be ESM but still uses `require`. // this causes esbuild to wrap them as CJS even when its entry appears to be ESM. @@ -787,8 +745,7 @@ const KNOWN_INTEROP_IDS = new Set(['moment']) function needsInterop( id: string, exportsData: ExportsData, - outputs: Record, - cacheDirOutputPath: string + output: { exports: string[] } ): boolean { if (KNOWN_INTEROP_IDS.has(id)) { return true @@ -802,17 +759,7 @@ function needsInterop( // if a peer dependency used require() on a ESM dependency, esbuild turns the // ESM dependency's entry chunk into a single default export... detect // such cases by checking exports mismatch, and force interop. - const flatId = flattenId(id) + '.js' - let generatedExports: string[] | undefined - for (const output in outputs) { - if ( - normalizePath(output) === - normalizePath(path.join(cacheDirOutputPath, flatId)) - ) { - generatedExports = outputs[output].exports - break - } - } + const generatedExports: string[] = output.exports if ( !generatedExports || @@ -829,8 +776,8 @@ function isSingleDefaultExport(exports: readonly string[]) { const lockfileFormats = ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml'] -function getDepHash(root: string, config: ResolvedConfig): string { - let content = lookupFile(root, lockfileFormats) || '' +export function getDepHash(config: ResolvedConfig): string { + let content = lookupFile(config.root, lockfileFormats) || '' // also take config into account // only a subset of config options that can affect dep optimization content += JSON.stringify( @@ -860,27 +807,44 @@ function getDepHash(root: string, config: ResolvedConfig): string { return value } ) - return createHash('sha256').update(content).digest('hex').substring(0, 8) + return getHash(content) +} + +function getOptimizedBrowserHash( + hash: string, + deps: Record, + timestamp = '' +) { + return getHash(hash + JSON.stringify(deps) + timestamp) } -export function optimizeDepInfoFromFile( +export function getHash(text: string): string { + return createHash('sha256').update(text).digest('hex').substring(0, 8) +} + +export function optimizedDepInfoFromId( metadata: DepOptimizationMetadata, - file: string + id: string ): OptimizedDepInfo | undefined { return ( - findFileInfo(metadata.optimized, file) || - findFileInfo(metadata.discovered, file) || - findFileInfo(metadata.chunks, file) + metadata.optimized[id] || metadata.discovered[id] || metadata.chunks[id] ) } -function findFileInfo( - dependenciesInfo: Record, +export function optimizedDepInfoFromFile( + metadata: DepOptimizationMetadata, file: string +): OptimizedDepInfo | undefined { + return metadata.depInfoList.find((depInfo) => depInfo.file === file) +} + +function findOptimizedDepInfoInRecord( + dependenciesInfo: Record, + callbackFn: (depInfo: OptimizedDepInfo, id: string) => any ): OptimizedDepInfo | undefined { for (const o of Object.keys(dependenciesInfo)) { const info = dependenciesInfo[o] - if (info.file === file) { + if (callbackFn(info, o)) { return info } } @@ -890,7 +854,7 @@ export async function optimizedDepNeedsInterop( metadata: DepOptimizationMetadata, file: string ): Promise { - const depInfo = optimizeDepInfoFromFile(metadata, file) + const depInfo = optimizedDepInfoFromFile(metadata, file) if (!depInfo) return undefined diff --git a/packages/vite/src/node/optimizer/registerMissing.ts b/packages/vite/src/node/optimizer/registerMissing.ts index 256098f351e2e8..65508ac5ae5155 100644 --- a/packages/vite/src/node/optimizer/registerMissing.ts +++ b/packages/vite/src/node/optimizer/registerMissing.ts @@ -1,18 +1,26 @@ import colors from 'picocolors' +import _debug from 'debug' import { - createOptimizeDepsRun, + runOptimizeDeps, getOptimizedDepPath, getHash, depsFromOptimizedDepInfo, - newDepOptimizationProcessing + newDepOptimizationProcessing, + loadCachedDepOptimizationMetadata, + createOptimizedDepsMetadata, + addOptimizedDepInfo, + discoverProjectDependencies, + depsLogString, + debuggerViteDeps as debug } from '.' import type { - DepOptimizationMetadata, DepOptimizationProcessing, - OptimizedDepInfo + OptimizedDepInfo, + OptimizedDeps } from '.' import type { ViteDevServer } from '..' -import { resolveSSRExternal } from '../ssr/ssrExternal' + +const isDebugEnabled = _debug('vite:deps').enabled /** * The amount to wait for requests to register newly found dependencies before triggering @@ -20,16 +28,35 @@ import { resolveSSRExternal } from '../ssr/ssrExternal' */ const debounceMs = 100 -export function createMissingImporterRegisterFn( - server: ViteDevServer, - initialProcessingPromise: Promise -): (id: string, resolved: string, ssr?: boolean) => OptimizedDepInfo { - const { logger } = server.config - let metadata = server._optimizeDepsMetadata! +export function createOptimizedDeps(server: ViteDevServer): OptimizedDeps { + const { config } = server + const { logger } = config + + const sessionTimestamp = Date.now().toString() + + const cachedMetadata = loadCachedDepOptimizationMetadata(config) + + const optimizedDeps: OptimizedDeps = { + metadata: + cachedMetadata || createOptimizedDepsMetadata(config, sessionTimestamp), + registerMissingImport + } let handle: NodeJS.Timeout | undefined let newDepsDiscovered = false + let newDepsToLog: string[] = [] + let newDepsToLogHandle: NodeJS.Timeout | undefined + const logNewDeps = () => { + config.logger.info( + colors.green(`✨ dependencies optimized: ${depsLogString(newDepsToLog)}`), + { + timestamp: true + } + ) + newDepsToLog = [] + } + let depOptimizationProcessing = newDepOptimizationProcessing() let depOptimizationProcessingQueue: DepOptimizationProcessing[] = [] const resolveEnqueuedProcessingPromises = () => { @@ -41,40 +68,80 @@ export function createMissingImporterRegisterFn( } let enqueuedRerun: (() => void) | undefined - let currentlyProcessing = true - initialProcessingPromise.then(() => { - currentlyProcessing = false - enqueuedRerun?.() - }) + let currentlyProcessing = false - async function rerun(ssr: boolean | undefined) { - // debounce time to wait for new missing deps finished, issue a new - // optimization of deps (both old and newly found) once the previous - // optimizeDeps processing is finished + // If there wasn't a cache or it is outdated, perform a fast scan with esbuild + // to quickly find project dependencies and do a first optimize run + if (!cachedMetadata) { + currentlyProcessing = true + + const scanPhaseProcessing = newDepOptimizationProcessing() + optimizedDeps.scanProcessing = scanPhaseProcessing.promise + + const warmUp = async () => { + try { + debug(colors.green(`scanning for dependencies...`), { + timestamp: true + }) + + const { metadata } = optimizedDeps + + const discovered = await discoverProjectDependencies( + config, + sessionTimestamp + ) + + // Respect the scan phase discover order to improve reproducibility + for (const depInfo of Object.values(discovered)) { + addOptimizedDepInfo(metadata, 'discovered', { + ...depInfo, + processing: depOptimizationProcessing.promise + }) + } + + debug( + colors.green( + `dependencies found: ${depsLogString(Object.keys(discovered))}` + ), + { + timestamp: true + } + ) + + scanPhaseProcessing.resolve() + optimizedDeps.scanProcessing = undefined + + runOptimizer() + } catch (e) { + logger.error(e.message) + if (optimizedDeps.scanProcessing) { + scanPhaseProcessing.resolve() + optimizedDeps.scanProcessing = undefined + } + } + } + + setTimeout(warmUp, 0) + } + + async function runOptimizer() { + // Ensure that rerun is called sequentially + enqueuedRerun = undefined + currentlyProcessing = true + + // Ensure that a rerun will not be issued for current discovered deps + if (handle) clearTimeout(handle) // a succesful completion of the optimizeDeps rerun will end up // creating new bundled version of all current and discovered deps // in the cache dir and a new metadata info object assigned - // to server._optimizeDepsMetadata. A fullReload is only issued if + // to optimizeDeps.metadata. A fullReload is only issued if // the previous bundled dependencies have changed. - // if the rerun fails, server._optimizeDepsMetadata remains untouched, + // if the rerun fails, optimizeDeps.metadata remains untouched, // current discovered deps are cleaned, and a fullReload is issued - // Ensure that rerun is called sequentially - enqueuedRerun = undefined - currentlyProcessing = true - - logger.info( - colors.yellow( - `new dependencies found: ${Object.keys(metadata.discovered).join( - ', ' - )}, updating...` - ), - { - timestamp: true - } - ) + let { metadata } = optimizedDeps // All deps, previous known and newly discovered are rebundled, // respect insertion order to keep the metadata file stable @@ -85,9 +152,10 @@ export function createMissingImporterRegisterFn( for (const dep of Object.keys(metadata.optimized)) { newDeps[dep] = { ...metadata.optimized[dep] } } - // Don't clone discovered info objects, they are read after awaited for (const dep of Object.keys(metadata.discovered)) { - newDeps[dep] = metadata.discovered[dep] + // Clone the discovered info discarding its processing promise + const { processing, ...info } = metadata.discovered[dep] + newDeps[dep] = info } newDepsDiscovered = false @@ -100,51 +168,86 @@ export function createMissingImporterRegisterFn( // dependencies will be asigned this promise from this point depOptimizationProcessing = newDepOptimizationProcessing() - let newData: DepOptimizationMetadata | null = null - try { - const optimizeDeps = await createOptimizeDepsRun( - server.config, - true, - false, - metadata, - newDeps, - ssr - ) - - const processingResult = await optimizeDeps.run() + const processingResult = await runOptimizeDeps(config, newDeps) + + const newData = processingResult.metadata + + // After a re-optimization, if the internal bundled chunks change a full page reload + // is required. If the files are stable, we can avoid the reload that is expensive + // for large applications. Comparing their fileHash we can find out if it is safe to + // keep the current browser state. + const needsReload = + metadata.hash !== newData.hash || + Object.keys(metadata.optimized).some((dep) => { + return ( + metadata.optimized[dep].fileHash !== newData.optimized[dep].fileHash + ) + }) const commitProcessing = () => { processingResult.commit() - newData = optimizeDeps.metadata + // While optimizeDeps is running, new missing deps may be discovered, + // in which case they will keep being added to metadata.discovered + for (const id in metadata.discovered) { + if (!newData.optimized[id]) { + addOptimizedDepInfo(newData, 'discovered', metadata.discovered[id]) + } + } - // update ssr externals - if (ssr) { - server._ssrExternals = resolveSSRExternal( - server.config, - Object.keys(newData.optimized) - ) + // If we don't reload the page, we need to keep browserHash stable + if (!needsReload) { + newData.browserHash = metadata.browserHash + for (const dep in newData.chunks) { + newData.chunks[dep].browserHash = metadata.browserHash + } + for (const dep in newData.optimized) { + newData.optimized[dep].browserHash = ( + metadata.optimized[dep] || metadata.discovered[dep] + ).browserHash + } } - // While optimizeDeps is running, new missing deps may be discovered, - // in which case they will keep being added to metadata.discovered - for (const o of Object.keys(metadata.discovered)) { - if (!newData.optimized[o]) { - newData.discovered[o] = metadata.discovered[o] + // Commit hash and needsInterop changes to the discovered deps info + // object. Allow for code to await for the discovered processing promise + // and use the information in the same object + for (const o in newData.optimized) { + const discovered = metadata.discovered[o] + if (discovered) { + const optimized = newData.optimized[o] + discovered.browserHash = optimized.browserHash + discovered.fileHash = optimized.fileHash + discovered.needsInterop = optimized.needsInterop + discovered.processing = undefined } } - metadata = server._optimizeDepsMetadata = newData + newDepsToLog.push( + ...Object.keys(newData.optimized).filter( + (dep) => !metadata.optimized[dep] + ) + ) + + metadata = optimizedDeps.metadata = newData resolveEnqueuedProcessingPromises() } - if (!processingResult.alteredFiles) { + if (!needsReload) { commitProcessing() - logger.info(colors.green(`✨ new dependencies pre-bundled...`), { - timestamp: true - }) + if (isDebugEnabled) { + logNewDeps() + debug(colors.green(`✨ previous optimized dependencies unchanged`), { + timestamp: true + }) + } else { + if (newDepsToLogHandle) clearTimeout(newDepsToLogHandle) + newDepsToLogHandle = setTimeout(() => { + newDepsToLogHandle = undefined + logNewDeps() + }, 2 * debounceMs) + } } else { if (newDepsDiscovered) { // There are newly discovered deps, and another rerun is about to be @@ -153,7 +256,7 @@ export function createMissingImporterRegisterFn( // once a rerun is committed processingResult.cancel() - logger.info( + debug( colors.green( `✨ delaying reload as new dependencies have been found...` ), @@ -164,8 +267,14 @@ export function createMissingImporterRegisterFn( } else { commitProcessing() + if (newDepsToLogHandle) clearTimeout(newDepsToLogHandle) + newDepsToLogHandle = undefined + logNewDeps() + logger.info( - colors.green(`✨ dependencies updated, reloading page...`), + colors.green( + `✨ previous optimized dependencies have changed, reloading page` + ), { timestamp: true } @@ -202,7 +311,17 @@ export function createMissingImporterRegisterFn( }) } - const discoveredTimestamp = Date.now() + async function rerun() { + // debounce time to wait for new missing deps finished, issue a new + // optimization of deps (both old and newly found) once the previous + // optimizeDeps processing is finished + const deps = Object.keys(optimizedDeps.metadata.discovered) + const depsString = depsLogString(deps) + debug(colors.green(`new dependencies found: ${depsString}`), { + timestamp: true + }) + runOptimizer() + } function getDiscoveredBrowserHash( hash: string, @@ -210,18 +329,21 @@ export function createMissingImporterRegisterFn( missing: Record ) { return getHash( - hash + - JSON.stringify(deps) + - JSON.stringify(missing) + - discoveredTimestamp + hash + JSON.stringify(deps) + JSON.stringify(missing) + sessionTimestamp ) } - return function registerMissingImport( + function registerMissingImport( id: string, resolved: string, ssr?: boolean ): OptimizedDepInfo { + if (optimizedDeps.scanProcessing) { + config.logger.error( + 'Vite internal error: registering missing import before initial scanning is over' + ) + } + const { metadata } = optimizedDeps const optimized = metadata.optimized[id] if (optimized) { return optimized @@ -237,7 +359,8 @@ export function createMissingImporterRegisterFn( return missing } newDepsDiscovered = true - missing = metadata.discovered[id] = { + missing = addOptimizedDepInfo(metadata, 'discovered', { + id, file: getOptimizedDepPath(id, server.config), src: resolved, // Assing a browserHash to this missing dependency that is unique to @@ -252,15 +375,17 @@ export function createMissingImporterRegisterFn( // loading of this pre-bundled dep needs to await for its processing // promise to be resolved processing: depOptimizationProcessing.promise - } + }) // Debounced rerun, let other missing dependencies be discovered before // the running next optimizeDeps enqueuedRerun = undefined if (handle) clearTimeout(handle) + if (newDepsToLogHandle) clearTimeout(newDepsToLogHandle) + newDepsToLogHandle = undefined handle = setTimeout(() => { handle = undefined - enqueuedRerun = () => rerun(ssr) + enqueuedRerun = rerun if (!currentlyProcessing) { enqueuedRerun() } @@ -270,4 +395,6 @@ export function createMissingImporterRegisterFn( // esbuild is run to generate the pre-bundle return missing } + + return optimizedDeps } diff --git a/packages/vite/src/node/optimizer/scan.ts b/packages/vite/src/node/optimizer/scan.ts index d1ac4eb249a8f3..549f748d9a2b58 100644 --- a/packages/vite/src/node/optimizer/scan.ts +++ b/packages/vite/src/node/optimizer/scan.ts @@ -123,11 +123,19 @@ export async function scanImports(config: ResolvedConfig): Promise<{ debug(`Scan completed in ${(performance.now() - start).toFixed(2)}ms:`, deps) return { - deps, + // Ensure a fixed order so hashes are stable and improve logs + deps: orderedDependencies(deps), missing } } +function orderedDependencies(deps: Record) { + const depsList = Object.entries(deps) + // Ensure the same browserHash for the same set of dependencies + depsList.sort((a, b) => a[0].localeCompare(b[0])) + return Object.fromEntries(depsList) +} + function globEntries(pattern: string | string[], config: ResolvedConfig) { return glob(pattern, { cwd: config.root, @@ -165,7 +173,10 @@ function esbuildScanPlugin( } const resolved = await container.resolveId( id, - importer && normalizePath(importer) + importer && normalizePath(importer), + { + scan: true + } ) const res = resolved?.id seen.set(key, res) diff --git a/packages/vite/src/node/plugin.ts b/packages/vite/src/node/plugin.ts index 36674e242bd33e..354b246dd9f182 100644 --- a/packages/vite/src/node/plugin.ts +++ b/packages/vite/src/node/plugin.ts @@ -121,7 +121,14 @@ export interface Plugin extends RollupPlugin { this: PluginContext, source: string, importer: string | undefined, - options: { custom?: CustomPluginOptions; ssr?: boolean } + options: { + custom?: CustomPluginOptions + ssr?: boolean + /** + * @internal + */ + scan?: boolean + } ): Promise | ResolveIdResult load?( this: PluginContext, diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index a4d2acc44db953..e7336ae4947677 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -197,19 +197,20 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { } let importerFile = importer - if ( - moduleListContains(config.optimizeDeps?.exclude, url) && - server._optimizeDepsMetadata - ) { - // if the dependency encountered in the optimized file was excluded from the optimization - // the dependency needs to be resolved starting from the original source location of the optimized file - // because starting from node_modules/.vite will not find the dependency if it was not hoisted - // (that is, if it is under node_modules directory in the package source of the optimized file) - for (const optimizedModule of Object.values( - server._optimizeDepsMetadata.optimized - )) { - if (optimizedModule.file === importerModule.file) { - importerFile = optimizedModule.src + if (moduleListContains(config.optimizeDeps?.exclude, url)) { + const optimizedDeps = server._optimizedDeps + if (optimizedDeps) { + await optimizedDeps.scanProcessing + + // if the dependency encountered in the optimized file was excluded from the optimization + // the dependency needs to be resolved starting from the original source location of the optimized file + // because starting from node_modules/.vite will not find the dependency if it was not hoisted + // (that is, if it is under node_modules directory in the package source of the optimized file) + for (const optimizedModule of optimizedDeps.metadata.depInfoList) { + if (!optimizedModule.src) continue // Ignore chunks + if (optimizedModule.file === importerModule.file) { + importerFile = optimizedModule.src + } } } } @@ -439,6 +440,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { importRewrites.push(async () => { let rewriteDone = false if ( + server?._optimizedDeps && isOptimizedDepFile(resolvedId, config) && !resolvedId.match(optimizedDepChunkRE) ) { @@ -450,7 +452,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { const file = cleanUrl(resolvedId) // Remove ?v={hash} const needsInterop = await optimizedDepNeedsInterop( - server._optimizeDepsMetadata!, + server._optimizedDeps!.metadata, file ) diff --git a/packages/vite/src/node/plugins/optimizedDeps.ts b/packages/vite/src/node/plugins/optimizedDeps.ts index 8fbdca8d08905f..adab1bd9756251 100644 --- a/packages/vite/src/node/plugins/optimizedDeps.ts +++ b/packages/vite/src/node/plugins/optimizedDeps.ts @@ -3,7 +3,7 @@ import type { Plugin } from '../plugin' import colors from 'picocolors' import { DEP_VERSION_RE } from '../constants' import { cleanUrl, createDebugger } from '../utils' -import { isOptimizedDepFile, optimizeDepInfoFromFile } from '../optimizer' +import { isOptimizedDepFile, optimizedDepInfoFromFile } from '../optimizer' import type { ViteDevServer } from '..' export const ERR_OPTIMIZE_DEPS_PROCESSING_ERROR = @@ -25,7 +25,7 @@ export function optimizedDepsPlugin(): Plugin { async load(id) { if (server && isOptimizedDepFile(id, server.config)) { - const metadata = server?._optimizeDepsMetadata + const metadata = server?._optimizedDeps?.metadata if (metadata) { const file = cleanUrl(id) const versionMatch = id.match(DEP_VERSION_RE) @@ -34,7 +34,7 @@ export function optimizedDepsPlugin(): Plugin { : undefined // Search in both the currently optimized and newly discovered deps - const info = optimizeDepInfoFromFile(metadata, file) + const info = optimizedDepInfoFromFile(metadata, file) if (info) { if (browserHash && info.browserHash !== browserHash) { throwOutdatedRequest(id) @@ -49,9 +49,9 @@ export function optimizedDepsPlugin(): Plugin { throwProcessingError(id) return } - const newMetadata = server._optimizeDepsMetadata + const newMetadata = server._optimizedDeps?.metadata if (metadata !== newMetadata) { - const currentInfo = optimizeDepInfoFromFile(newMetadata!, file) + const currentInfo = optimizedDepInfoFromFile(newMetadata!, file) if (info.browserHash !== currentInfo?.browserHash) { throwOutdatedRequest(id) } diff --git a/packages/vite/src/node/plugins/preAlias.ts b/packages/vite/src/node/plugins/preAlias.ts index 75a0d8e5e6f9dc..dadb16aa4c28a9 100644 --- a/packages/vite/src/node/plugins/preAlias.ts +++ b/packages/vite/src/node/plugins/preAlias.ts @@ -13,9 +13,9 @@ export function preAliasPlugin(): Plugin { configureServer(_server) { server = _server }, - resolveId(id, importer, options) { - if (!options?.ssr && bareImportRE.test(id)) { - return tryOptimizedResolve(id, server, importer) + async resolveId(id, importer, options) { + if (!options?.ssr && bareImportRE.test(id) && !options?.scan) { + return await tryOptimizedResolve(id, server, importer) } } } diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 5dfaffafcbb7e4..8f6a48a28da463 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -33,7 +33,8 @@ import { import { createIsOptimizedDepUrl, isOptimizedDepFile, - optimizeDepInfoFromFile + optimizedDepInfoFromFile, + optimizedDepInfoFromId } from '../optimizer' import type { OptimizedDepInfo } from '../optimizer' import type { ViteDevServer, SSROptions } from '..' @@ -83,6 +84,8 @@ export interface InternalResolveOptions extends ResolveOptions { // should also try import from `.ts/tsx/mts/cts` source file as fallback. isFromTsImporter?: boolean tryEsmOnly?: boolean + // True when resolving during the scan phase to discover dependencies + scan?: boolean } export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { @@ -106,7 +109,7 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { isOptimizedDepUrl = createIsOptimizedDepUrl(server.config) }, - resolveId(id, importer, resolveOpts) { + async resolveId(id, importer, resolveOpts) { const ssr = resolveOpts?.ssr === true if (id.startsWith(browserExternalId)) { return id @@ -127,7 +130,8 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { isRequire, ...baseOptions, - isFromTsImporter: isTsRequest(importer ?? '') + isFromTsImporter: isTsRequest(importer ?? ''), + scan: resolveOpts?.scan ?? baseOptions.scan } let res: string | PartialResolvedId | undefined @@ -136,9 +140,10 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { // tryFileResolve or /fs/ resolution but these files may not yet // exists if we are in the middle of a deps re-processing if (asSrc && isOptimizedDepUrl?.(id)) { - return id.startsWith(FS_PREFIX) + const optimizedPath = id.startsWith(FS_PREFIX) ? fsPathFromId(id) : normalizePath(ensureVolumeInPath(path.resolve(root, id.slice(1)))) + return optimizedPath } // explicit fs paths that starts with /@fs/* @@ -169,12 +174,15 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { const normalizedFsPath = normalizePath(fsPath) - if (server && isOptimizedDepFile(normalizedFsPath, server!.config)) { + if ( + server?._optimizedDeps && + isOptimizedDepFile(normalizedFsPath, server!.config) + ) { // Optimized files could not yet exist in disk, resolve to the full path // Inject the current browserHash version if the path doesn't have one if (!normalizedFsPath.match(DEP_VERSION_RE)) { - const browserHash = optimizeDepInfoFromFile( - server._optimizeDepsMetadata!, + const browserHash = optimizedDepInfoFromFile( + server._optimizedDeps!.metadata!, normalizedFsPath )?.browserHash if (browserHash) { @@ -252,7 +260,8 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { asSrc && server && !ssr && - (res = tryOptimizedResolve(id, server, importer)) + !options.scan && + (res = await tryOptimizedResolve(id, server, importer)) ) { return res } @@ -606,7 +615,8 @@ export function tryNodeResolve( if ( !resolved.includes('node_modules') || // linked !server || // build - !server._registerMissingImport // initial esbuild scan phase + !server._optimizedDeps || // resolving before listening to the server + options.scan // initial esbuild scan phase ) { return { id: resolved } } @@ -627,14 +637,17 @@ export function tryNodeResolve( // otherwise we may introduce duplicated modules for externalized files // from pre-bundled deps. - const versionHash = server._optimizeDepsMetadata?.browserHash + const versionHash = server._optimizedDeps!.metadata.browserHash if (versionHash && isJsType) { resolved = injectQuery(resolved, `v=${versionHash}`) } } else { // this is a missing import, queue optimize-deps re-run and // get a resolved its optimized info - const optimizedInfo = server._registerMissingImport!(id, resolved, ssr) + const optimizedInfo = server._optimizedDeps!.registerMissingImport( + id, + resolved + ) resolved = getOptimizedUrl(optimizedInfo) } return { id: resolved! } @@ -644,24 +657,20 @@ export function tryNodeResolve( const getOptimizedUrl = (optimizedData: OptimizedDepInfo) => `${optimizedData.file}?v=${optimizedData.browserHash}` -export function tryOptimizedResolve( +export async function tryOptimizedResolve( id: string, server: ViteDevServer, importer?: string -): string | undefined { - const depData = server._optimizeDepsMetadata +): Promise { + const optimizedDeps = server._optimizedDeps - if (!depData) return + if (!optimizedDeps) return - // check if id has been optimized - const isOptimized = depData.optimized[id] - if (isOptimized) { - return getOptimizedUrl(isOptimized) - } + await optimizedDeps.scanProcessing - const isChunk = depData.chunks[id] - if (isChunk) { - return getOptimizedUrl(isChunk) + const depInfo = optimizedDepInfoFromId(optimizedDeps.metadata, id) + if (depInfo) { + return getOptimizedUrl(depInfo) } if (!importer) return @@ -669,7 +678,10 @@ export function tryOptimizedResolve( // further check if id is imported by nested dependency let resolvedSrc: string | undefined - for (const [pkgPath, optimizedData] of Object.entries(depData.optimized)) { + for (const optimizedData of optimizedDeps.metadata.depInfoList) { + if (!optimizedData.src) continue // Ignore chunks + + const pkgPath = optimizedData.id // check for scenarios, e.g. // pkgPath => "my-lib > foo" // id => "foo" diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index d56a6a705d5416..e8d4c3f1e5626d 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -44,8 +44,6 @@ import { transformRequest } from './transformRequest' import type { ESBuildTransformResult } from '../plugins/esbuild' import { transformWithEsbuild } from '../plugins/esbuild' import type { TransformOptions as EsbuildTransformOptions } from 'esbuild' -import type { DepOptimizationMetadata, OptimizedDepInfo } from '../optimizer' -import { createOptimizeDepsRun } from '../optimizer' import { ssrLoadModule } from '../ssr/ssrModuleLoader' import { resolveSSRExternal } from '../ssr/ssrExternal' import { @@ -53,7 +51,8 @@ import { ssrRewriteStacktrace } from '../ssr/ssrStacktrace' import { ssrTransform } from '../ssr/ssrTransform' -import { createMissingImporterRegisterFn } from '../optimizer/registerMissing' +import { createOptimizedDeps } from '../optimizer/registerMissing' +import type { OptimizedDeps } from '../optimizer' import { resolveHostname } from '../utils' import { searchForWorkspaceRoot } from './searchRoot' import { CLIENT_DIR } from '../constants' @@ -257,7 +256,7 @@ export interface ViteDevServer { /** * @internal */ - _optimizeDepsMetadata: DepOptimizationMetadata | null + _optimizedDeps: OptimizedDeps | null /** * Deps that are externalized * @internal @@ -284,16 +283,6 @@ export interface ViteDevServer { * @internal */ _forceOptimizeOnRestart: boolean - /** - * @internal - */ - _registerMissingImport: - | (( - id: string, - resolved: string, - ssr: boolean | undefined - ) => OptimizedDepInfo) - | null /** * @internal */ @@ -372,16 +361,18 @@ export async function createServer( }, transformIndexHtml: null!, // to be immediately set async ssrLoadModule(url, opts?: { fixStacktrace?: boolean }) { - let configFileDependencies: string[] = [] - const metadata = server._optimizeDepsMetadata - if (metadata) { - configFileDependencies = Object.keys(metadata.optimized) + if (!server._ssrExternals) { + let knownImports: string[] = [] + const optimizedDeps = server._optimizedDeps + if (optimizedDeps) { + await optimizedDeps.scanProcessing + knownImports = [ + ...Object.keys(optimizedDeps.metadata.optimized), + ...Object.keys(optimizedDeps.metadata.discovered) + ] + } + server._ssrExternals = resolveSSRExternal(config, knownImports) } - - server._ssrExternals ||= resolveSSRExternal( - config, - configFileDependencies - ) return ssrLoadModule( url, server, @@ -434,12 +425,11 @@ export async function createServer( return server._restartPromise }, - _optimizeDepsMetadata: null, + _optimizedDeps: null, _ssrExternals: null, _globImporters: Object.create(null), _restartPromise: null, _forceOptimizeOnRestart: false, - _registerMissingImport: null, _pendingRequests: new Map() } @@ -581,39 +571,15 @@ export async function createServer( // error handler middlewares.use(errorMiddleware(server, !!middlewareMode)) - const runOptimize = async () => { - const optimizeDeps = await createOptimizeDepsRun( - config, - config.server.force - ) - - // Don't await for the optimization to finish, we can start the - // server right away here - server._optimizeDepsMetadata = optimizeDeps.metadata - - // Run deps optimization in parallel - const initialProcessingPromise = optimizeDeps - .run() - .then((result) => result.commit()) - - // While running the first optimizeDeps, _registerMissingImport is null - // so the resolve plugin resolves straight to node_modules during the - // deps discovery scan phase - server._registerMissingImport = createMissingImporterRegisterFn( - server, - initialProcessingPromise - ) - } - if (!middlewareMode && httpServer) { let isOptimized = false - // overwrite listen to run optimizer before server start + // overwrite listen to init optimizer before server start const listen = httpServer.listen.bind(httpServer) httpServer.listen = (async (port: number, ...args: any[]) => { if (!isOptimized) { try { await container.buildStart({}) - await runOptimize() + server._optimizedDeps = createOptimizedDeps(server) isOptimized = true } catch (e) { httpServer.emit('error', e) @@ -624,7 +590,7 @@ export async function createServer( }) as any } else { await container.buildStart({}) - await runOptimize() + server._optimizedDeps = createOptimizedDeps(server) } return server diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index 2aab3e03097c99..590648ed58d164 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -90,6 +90,10 @@ export interface PluginContainer { options?: { skip?: Set ssr?: boolean + /** + * @internal + */ + scan?: boolean } ): Promise transform( @@ -212,6 +216,7 @@ export async function createPluginContainer( class Context implements PluginContext { meta = minimalContext.meta ssr = false + _scan = false _activePlugin: Plugin | null _activeId: string | null = null _activeCode: string | null = null @@ -241,7 +246,11 @@ export async function createPluginContainer( skip = new Set(this._resolveSkips) skip.add(this._activePlugin) } - let out = await container.resolveId(id, importer, { skip, ssr: this.ssr }) + let out = await container.resolveId(id, importer, { + skip, + ssr: this.ssr, + scan: this._scan + }) if (typeof out === 'string') out = { id: out } return out as ResolvedId | null } @@ -487,8 +496,10 @@ export async function createPluginContainer( async resolveId(rawId, importer = join(root, 'index.html'), options) { const skip = options?.skip const ssr = options?.ssr + const scan = !!options?.scan const ctx = new Context() ctx.ssr = !!ssr + ctx._scan = scan ctx._resolveSkips = skip const resolveStart = isDebug ? performance.now() : 0 @@ -505,7 +516,7 @@ export async function createPluginContainer( ctx as any, rawId, importer, - { ssr } + { ssr, scan } ) if (!result) continue From 4538e59c65f430268af6e1710abf84b8cfc88dd7 Mon Sep 17 00:00:00 2001 From: patak-dev Date: Wed, 23 Mar 2022 09:29:33 +0100 Subject: [PATCH 03/92] release: v2.9.0-beta.7 --- packages/vite/CHANGELOG.md | 6 ++++++ packages/vite/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/vite/CHANGELOG.md b/packages/vite/CHANGELOG.md index 1809b04a4e8c92..a90e6ef2a35369 100644 --- a/packages/vite/CHANGELOG.md +++ b/packages/vite/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.9.0-beta.7 (2022-03-23) + +* feat: non-blocking scanning of dependencies (#7379) ([676f545](https://github.com/vitejs/vite/commit/676f545)), closes [#7379](https://github.com/vitejs/vite/issues/7379) + + + ## 2.9.0-beta.6 (2022-03-22) diff --git a/packages/vite/package.json b/packages/vite/package.json index c1aa489f340fc0..d749419cc745a3 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -1,6 +1,6 @@ { "name": "vite", - "version": "2.9.0-beta.6", + "version": "2.9.0-beta.7", "license": "MIT", "author": "Evan You", "description": "Native-ESM powered web dev build tool", From 0535c7077b66e735217b790749a1a6fb4d58a44e Mon Sep 17 00:00:00 2001 From: Razio Date: Wed, 23 Mar 2022 16:55:10 +0800 Subject: [PATCH 04/92] fix(less): empty less file error (#7412) --- packages/vite/src/node/plugins/css.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index db8e0a2679f74f..f45a1ac01fb6f8 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -1316,8 +1316,10 @@ const less: StylePreprocessor = async (source, root, options, resolvers) => { return { code: '', errors: [normalizedError], deps: [] } } - const map: ExistingRawSourceMap = JSON.parse(result.map) - delete map.sourcesContent + const map: ExistingRawSourceMap = result.map && JSON.parse(result.map) + if (map) { + delete map.sourcesContent + } return { code: result.css.toString(), From 4e0b61593a0eba97a901f996619c5e1aae4a7b68 Mon Sep 17 00:00:00 2001 From: yoho <907415276@qq.com> Date: Wed, 23 Mar 2022 22:17:03 +0800 Subject: [PATCH 05/92] docs: classic worker (#7203) Co-authored-by: Bjorn Lu Co-authored-by: Jeff Yang <32727188+ydcjeff@users.noreply.github.com> --- docs/guide/features.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/guide/features.md b/docs/guide/features.md index 7bf870646da909..ebed85cd2d529d 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -352,6 +352,24 @@ In the production build, `.wasm` files smaller than `assetInlineLimit` will be i ## Web Workers +### Import with Constructors + +A web worker script can be imported using [`new Worker()`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker) and [`new SharedWorker()`](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker/SharedWorker). Compared to the worker suffixes, this syntax leans closer to the standards and is the **recommended** way to create workers. + +```ts +const worker = new Worker(new URL('./worker.js', import.meta.url)) +``` + +The worker constructor also accepts options, which can be used to create "module" workers: + +```ts +const worker = new Worker(new URL('./worker.js', import.meta.url), { + type: 'module' +}) +``` + +### Import with Query Suffixes + A web worker script can be directly imported by appending `?worker` or `?sharedworker` to the import request. The default export will be a custom worker constructor: ```js @@ -368,6 +386,8 @@ By default, the worker script will be emitted as a separate chunk in the product import MyWorker from './worker?worker&inline' ``` +See [Worker Options](/config/#worker-options) for details on configuring the bundling of all workers. + ## Build Optimizations > Features listed below are automatically applied as part of the build process and there is no need for explicit configuration unless you want to disable them. From 74277ecddc7de747fec7b218d73fda5063486870 Mon Sep 17 00:00:00 2001 From: patak Date: Wed, 23 Mar 2022 18:29:12 +0100 Subject: [PATCH 06/92] test: increment jest timeout (#7421) --- jest.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.ts b/jest.config.ts index 7d4831524d01c3..11663af4e08107 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -6,7 +6,7 @@ const config: Config.InitialOptions = { testMatch: process.env.VITE_TEST_BUILD ? ['**/playground/**/*.spec.[jt]s?(x)'] : ['**/*.spec.[jt]s?(x)'], - testTimeout: process.env.CI ? 30000 : 10000, + testTimeout: process.env.CI ? 50000 : 20000, globalSetup: './scripts/jestGlobalSetup.cjs', globalTeardown: './scripts/jestGlobalTeardown.cjs', testEnvironment: './scripts/jestEnv.cjs', From c7fc7c34029f109bb716c6a2888022b2f4c40e3d Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 23 Mar 2022 10:42:24 -0700 Subject: [PATCH 07/92] fix: consider undefined port when checking port (#7318) --- packages/vite/src/node/server/ws.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/vite/src/node/server/ws.ts b/packages/vite/src/node/server/ws.ts index 479fc63daef2b6..ffbfd7a56eca97 100644 --- a/packages/vite/src/node/server/ws.ts +++ b/packages/vite/src/node/server/ws.ts @@ -28,9 +28,11 @@ export function createWebSocketServer( let httpsServer: Server | undefined = undefined const hmr = isObject(config.server.hmr) && config.server.hmr - const wsServer = - (hmr && hmr.server) || - ((!(hmr && hmr.port) || hmr.port !== config.server.port) && server) + const hmrServer = hmr && hmr.server + const hmrPort = hmr && hmr.port + // TODO: the main server port may not have been chosen yet as it may use the next available + const portsAreCompatible = !hmrPort || hmrPort === config.server.port + const wsServer = hmrServer || (portsAreCompatible && server) if (wsServer) { wss = new WebSocket({ noServer: true }) @@ -43,7 +45,7 @@ export function createWebSocketServer( }) } else { const websocketServerOptions: ServerOptions = {} - const port = (hmr && hmr.port) || 24678 + const port = hmrPort || 24678 const host = (hmr && hmr.host) || undefined if (httpsOptions) { // if we're serving the middlewares over https, the ws library doesn't support automatically creating an https server, so we need to do it ourselves From 968a011338054822867846df47c303e0e260aeb8 Mon Sep 17 00:00:00 2001 From: spencer17x <1253478653@qq.com> Date: Thu, 24 Mar 2022 04:30:10 +0800 Subject: [PATCH 08/92] fix: release --lerna-package use variable pkgName (#7399) --- scripts/release.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release.ts b/scripts/release.ts index d536eca1f70586..5c32c13b5cf5c8 100644 --- a/scripts/release.ts +++ b/scripts/release.ts @@ -88,7 +88,7 @@ async function main(): Promise { '--commit-path', '.' ] - if (pkgName !== 'vite') changelogArgs.push('--lerna-package', 'plugin-vue') + if (pkgName !== 'vite') changelogArgs.push('--lerna-package', pkgName) await run('npx', changelogArgs, { cwd: pkgDir }) const { stdout } = await run('git', ['diff'], { stdio: 'pipe' }) From a90b03ba6a765248395a4fbd2158e2930ca95b92 Mon Sep 17 00:00:00 2001 From: Wenlu Wang Date: Thu, 24 Mar 2022 13:51:10 +0800 Subject: [PATCH 09/92] fix: update tsconfck to 1.2.1 (#7424) --- packages/vite/package.json | 2 +- pnpm-lock.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/vite/package.json b/packages/vite/package.json index d749419cc745a3..8d82d89b19dcf8 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -113,7 +113,7 @@ "source-map-support": "^0.5.21", "strip-ansi": "^6.0.1", "terser": "^5.12.1", - "tsconfck": "^1.2.0", + "tsconfck": "^1.2.1", "tslib": "^2.3.1", "types": "link:./types", "ws": "^8.5.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a9145995c4bcf..4451cc9c87e636 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -862,7 +862,7 @@ importers: source-map-support: ^0.5.21 strip-ansi: ^6.0.1 terser: ^5.12.1 - tsconfck: ^1.2.0 + tsconfck: ^1.2.1 tslib: ^2.3.1 types: link:./types ws: ^8.5.0 @@ -935,7 +935,7 @@ importers: source-map-support: 0.5.21 strip-ansi: 6.0.1 terser: 5.12.1 - tsconfck: 1.2.0_typescript@4.5.4 + tsconfck: 1.2.1_typescript@4.5.4 tslib: 2.3.1 types: link:types ws: 8.5.0 @@ -9079,8 +9079,8 @@ packages: yn: 3.1.1 dev: true - /tsconfck/1.2.0_typescript@4.5.4: - resolution: {integrity: sha512-uKBM6x6i5e7Tfof5Zhll2ypgYKwWZhsxsHOoCH/7enlcHjjlcZ8FnxFrqgVFtpN6ymzZX3zoDaVGIlkh/g4x6w==} + /tsconfck/1.2.1_typescript@4.5.4: + resolution: {integrity: sha512-x28dvgpazY0+Gdpheb+D47NiaepLoueunDXlNQ6gVruu9HJbUj3M07ORgjmOQBUpPbXUAQXyfACc8Mi/jlLDVw==} engines: {node: ^12.20 || ^14.13.1 || >= 16} hasBin: true peerDependencies: From f4934e8778f2fe1d3e71e6b8e49d8f16c191cfef Mon Sep 17 00:00:00 2001 From: patak Date: Thu, 24 Mar 2022 09:59:57 +0100 Subject: [PATCH 10/92] feat: hide optimized deps found during scan phase logs (#7419) --- packages/vite/src/node/optimizer/index.ts | 2 + .../src/node/optimizer/registerMissing.ts | 59 ++++++++++--------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index cefa24133db529..37294aea459c15 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -253,6 +253,8 @@ export function loadCachedDepOptimizationMetadata( // need to resolve the processing promise so requests can move on return cachedMetadata } + } else { + config.logger.info('Forced re-optimization of dependencies') } // Start with a fresh cache diff --git a/packages/vite/src/node/optimizer/registerMissing.ts b/packages/vite/src/node/optimizer/registerMissing.ts index 65508ac5ae5155..ee4824389c202b 100644 --- a/packages/vite/src/node/optimizer/registerMissing.ts +++ b/packages/vite/src/node/optimizer/registerMissing.ts @@ -47,14 +47,18 @@ export function createOptimizedDeps(server: ViteDevServer): OptimizedDeps { let newDepsToLog: string[] = [] let newDepsToLogHandle: NodeJS.Timeout | undefined - const logNewDeps = () => { - config.logger.info( - colors.green(`✨ dependencies optimized: ${depsLogString(newDepsToLog)}`), - { - timestamp: true - } - ) - newDepsToLog = [] + const logNewlyDiscoveredDeps = () => { + if (newDepsToLog.length) { + config.logger.info( + colors.green( + `✨ new dependencies optimized: ${depsLogString(newDepsToLog)}` + ), + { + timestamp: true + } + ) + newDepsToLog = [] + } } let depOptimizationProcessing = newDepOptimizationProcessing() @@ -124,7 +128,7 @@ export function createOptimizedDeps(server: ViteDevServer): OptimizedDeps { setTimeout(warmUp, 0) } - async function runOptimizer() { + async function runOptimizer(isRerun = false) { // Ensure that rerun is called sequentially enqueuedRerun = undefined currentlyProcessing = true @@ -223,11 +227,13 @@ export function createOptimizedDeps(server: ViteDevServer): OptimizedDeps { } } - newDepsToLog.push( - ...Object.keys(newData.optimized).filter( - (dep) => !metadata.optimized[dep] + if (isRerun) { + newDepsToLog.push( + ...Object.keys(newData.optimized).filter( + (dep) => !metadata.optimized[dep] + ) ) - ) + } metadata = optimizedDeps.metadata = newData resolveEnqueuedProcessingPromises() @@ -236,17 +242,16 @@ export function createOptimizedDeps(server: ViteDevServer): OptimizedDeps { if (!needsReload) { commitProcessing() - if (isDebugEnabled) { - logNewDeps() - debug(colors.green(`✨ previous optimized dependencies unchanged`), { - timestamp: true - }) - } else { + if (!isDebugEnabled) { if (newDepsToLogHandle) clearTimeout(newDepsToLogHandle) newDepsToLogHandle = setTimeout(() => { newDepsToLogHandle = undefined - logNewDeps() + logNewlyDiscoveredDeps() }, 2 * debounceMs) + } else { + debug(colors.green(`✨ optimized dependencies unchanged`), { + timestamp: true + }) } } else { if (newDepsDiscovered) { @@ -267,14 +272,14 @@ export function createOptimizedDeps(server: ViteDevServer): OptimizedDeps { } else { commitProcessing() - if (newDepsToLogHandle) clearTimeout(newDepsToLogHandle) - newDepsToLogHandle = undefined - logNewDeps() + if (!isDebugEnabled) { + if (newDepsToLogHandle) clearTimeout(newDepsToLogHandle) + newDepsToLogHandle = undefined + logNewlyDiscoveredDeps() + } logger.info( - colors.green( - `✨ previous optimized dependencies have changed, reloading page` - ), + colors.green(`✨ optimized dependencies changed. reloading`), { timestamp: true } @@ -320,7 +325,7 @@ export function createOptimizedDeps(server: ViteDevServer): OptimizedDeps { debug(colors.green(`new dependencies found: ${depsString}`), { timestamp: true }) - runOptimizer() + runOptimizer(true) } function getDiscoveredBrowserHash( From 90e812a5d36d7bfafbce099b9d69b9c8c9348bd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Zhou=20=20=28=E6=AF=93=E6=9D=B0=29?= Date: Thu, 24 Mar 2022 18:12:56 +0800 Subject: [PATCH 11/92] fix(plugin-vue): respect __VUE_PROD_DEVTOOLS__ setting (#4984) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 周毓杰 Co-authored-by: Haoqun Jiang --- packages/plugin-vue/src/index.ts | 8 ++++++-- packages/plugin-vue/src/main.ts | 9 ++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/plugin-vue/src/index.ts b/packages/plugin-vue/src/index.ts index 28a15996363285..28280ed79188b8 100644 --- a/packages/plugin-vue/src/index.ts +++ b/packages/plugin-vue/src/index.ts @@ -64,6 +64,7 @@ export interface ResolvedOptions extends Options { root: string sourceMap: boolean devServer?: ViteDevServer + devToolsEnabled?: boolean } export default function vuePlugin(rawOptions: Options = {}): Plugin { @@ -97,7 +98,8 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { customElement, reactivityTransform, root: process.cwd(), - sourceMap: true + sourceMap: true, + devToolsEnabled: process.env.NODE_ENV !== 'production' } // Temporal handling for 2.7 breaking change @@ -135,7 +137,9 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { ...options, root: config.root, sourceMap: config.command === 'build' ? !!config.build.sourcemap : true, - isProduction: config.isProduction + isProduction: config.isProduction, + devToolsEnabled: + !!config.define!.__VUE_PROD_DEVTOOLS__ || !config.isProduction } }, diff --git a/packages/plugin-vue/src/main.ts b/packages/plugin-vue/src/main.ts index 7fb26fd39c6c03..44b1de74721efd 100644 --- a/packages/plugin-vue/src/main.ts +++ b/packages/plugin-vue/src/main.ts @@ -27,7 +27,7 @@ export async function transformMain( ssr: boolean, asCustomElement: boolean ) { - const { devServer, isProduction } = options + const { devServer, isProduction, devToolsEnabled } = options // prev descriptor is only set and used for hmr const prevDescriptor = getPrevDescriptor(filename) @@ -102,9 +102,12 @@ export async function transformMain( if (hasScoped) { attachedProps.push([`__scopeId`, JSON.stringify(`data-v-${descriptor.id}`)]) } - if (devServer && !isProduction) { + if (devToolsEnabled || (devServer && !isProduction)) { // expose filename during serve for devtools to pickup - attachedProps.push([`__file`, JSON.stringify(filename)]) + attachedProps.push([ + `__file`, + JSON.stringify(isProduction ? path.basename(filename) : filename) + ]) } // HMR From fe4c1edbe009e1df340b4d879c02d2e57a6cd7f0 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Thu, 24 Mar 2022 21:02:17 +0800 Subject: [PATCH 12/92] fix(resolve): skip `module` field when the importer is a `require` call (#7438) --- .../playground/resolve/__tests__/resolve.spec.ts | 6 ++++++ packages/playground/resolve/index.html | 6 ++++++ packages/playground/resolve/package.json | 1 + .../resolve/require-pkg-with-module-field/dep.cjs | 5 +++++ .../resolve/require-pkg-with-module-field/index.cjs | 8 ++++++++ .../require-pkg-with-module-field/package.json | 9 +++++++++ packages/playground/resolve/vite.config.js | 5 ++++- packages/vite/src/node/plugins/resolve.ts | 6 +++++- pnpm-lock.yaml | 12 ++++++++++++ 9 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 packages/playground/resolve/require-pkg-with-module-field/dep.cjs create mode 100644 packages/playground/resolve/require-pkg-with-module-field/index.cjs create mode 100644 packages/playground/resolve/require-pkg-with-module-field/package.json diff --git a/packages/playground/resolve/__tests__/resolve.spec.ts b/packages/playground/resolve/__tests__/resolve.spec.ts index b64da138033fc0..c8c85d8df9b806 100644 --- a/packages/playground/resolve/__tests__/resolve.spec.ts +++ b/packages/playground/resolve/__tests__/resolve.spec.ts @@ -58,6 +58,12 @@ test('dont add extension to directory name (./dir-with-ext.js/index.js)', async expect(await page.textContent('.dir-with-ext')).toMatch('[success]') }) +test('do not resolve to the `module` field if the importer is a `require` call', async () => { + expect(await page.textContent('.require-pkg-with-module-field')).toMatch( + '[success]' + ) +}) + test('a ts module can import another ts module using its corresponding js file name', async () => { expect(await page.textContent('.ts-extension')).toMatch('[success]') }) diff --git a/packages/playground/resolve/index.html b/packages/playground/resolve/index.html index c0569345d86837..2c4ed7b9aa760c 100644 --- a/packages/playground/resolve/index.html +++ b/packages/playground/resolve/index.html @@ -58,6 +58,9 @@

Resolve file name containing dot

Browser Field

fail

+

Don't resolve to the `module` field if the importer is a `require` call

+

fail

+

CSS Entry

@@ -181,6 +184,9 @@

resolve package that contains # in path

text('.browser', main) } + import { msg as requireButWithModuleFieldMsg } from 'require-pkg-with-module-field' + text('.require-pkg-with-module-field', requireButWithModuleFieldMsg) + import { msg as customExtMsg } from './custom-ext' text('.custom-ext', customExtMsg) diff --git a/packages/playground/resolve/package.json b/packages/playground/resolve/package.json index 5e0f53b4c8468a..dda4476bc6ae82 100644 --- a/packages/playground/resolve/package.json +++ b/packages/playground/resolve/package.json @@ -12,6 +12,7 @@ "@babel/runtime": "^7.16.0", "es5-ext": "0.10.53", "normalize.css": "^8.0.1", + "require-pkg-with-module-field": "link:./require-pkg-with-module-field", "resolve-browser-field": "link:./browser-field", "resolve-custom-condition": "link:./custom-condition", "resolve-custom-main-field": "link:./custom-main-field", diff --git a/packages/playground/resolve/require-pkg-with-module-field/dep.cjs b/packages/playground/resolve/require-pkg-with-module-field/dep.cjs new file mode 100644 index 00000000000000..3fb20b76d48b79 --- /dev/null +++ b/packages/playground/resolve/require-pkg-with-module-field/dep.cjs @@ -0,0 +1,5 @@ +const BigNumber = require('bignumber.js') + +const x = new BigNumber('1111222233334444555566') + +module.exports = x.toString() diff --git a/packages/playground/resolve/require-pkg-with-module-field/index.cjs b/packages/playground/resolve/require-pkg-with-module-field/index.cjs new file mode 100644 index 00000000000000..da215f306d1ac1 --- /dev/null +++ b/packages/playground/resolve/require-pkg-with-module-field/index.cjs @@ -0,0 +1,8 @@ +const dep = require('./dep.cjs') + +const msg = + dep === '1.111222233334444555566e+21' + ? '[success] require-pkg-with-module-field' + : '[failed] require-pkg-with-module-field' + +exports.msg = msg diff --git a/packages/playground/resolve/require-pkg-with-module-field/package.json b/packages/playground/resolve/require-pkg-with-module-field/package.json new file mode 100644 index 00000000000000..e409343a7567d5 --- /dev/null +++ b/packages/playground/resolve/require-pkg-with-module-field/package.json @@ -0,0 +1,9 @@ +{ + "name": "require-pkg-with-module-field", + "private": true, + "version": "1.0.0", + "main": "./index.cjs", + "dependencies": { + "bignumber.js": "9.0.2" + } +} diff --git a/packages/playground/resolve/vite.config.js b/packages/playground/resolve/vite.config.js index be1b75e431383a..0550d1ecf6f044 100644 --- a/packages/playground/resolve/vite.config.js +++ b/packages/playground/resolve/vite.config.js @@ -40,5 +40,8 @@ module.exports = { } } } - ] + ], + optimizeDeps: { + include: ['require-pkg-with-module-field'] + } } diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 8f6a48a28da463..686c08b12c3248 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -738,7 +738,11 @@ export function resolvePackageEntry( : isObject(data.browser) && data.browser['.'] if (browserEntry) { // check if the package also has a "module" field. - if (typeof data.module === 'string' && data.module !== browserEntry) { + if ( + !options.isRequire && + typeof data.module === 'string' && + data.module !== browserEntry + ) { // if both are present, we may have a problem: some package points both // to ESM, with "module" targeting Node.js, while some packages points // "module" to browser ESM and "browser" to UMD. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4451cc9c87e636..22de761586a2f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -456,6 +456,7 @@ importers: '@babel/runtime': ^7.16.0 es5-ext: 0.10.53 normalize.css: ^8.0.1 + require-pkg-with-module-field: link:./require-pkg-with-module-field resolve-browser-field: link:./browser-field resolve-custom-condition: link:./custom-condition resolve-custom-main-field: link:./custom-main-field @@ -466,6 +467,7 @@ importers: '@babel/runtime': 7.16.5 es5-ext: 0.10.53 normalize.css: 8.0.1 + require-pkg-with-module-field: link:require-pkg-with-module-field resolve-browser-field: link:browser-field resolve-custom-condition: link:custom-condition resolve-custom-main-field: link:custom-main-field @@ -497,6 +499,12 @@ importers: packages/playground/resolve/inline-package: specifiers: {} + packages/playground/resolve/require-pkg-with-module-field: + specifiers: + bignumber.js: 9.0.2 + dependencies: + bignumber.js: 9.0.2 + packages/playground/ssr-deps: specifiers: bcrypt: ^5.0.1 @@ -3365,6 +3373,10 @@ packages: - supports-color dev: false + /bignumber.js/9.0.2: + resolution: {integrity: sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==} + dev: false + /binary-extensions/2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} From 47668b541989c4abd48a4b232654b4e33c795714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Thu, 24 Mar 2022 22:51:30 +0900 Subject: [PATCH 13/92] fix: inline style css sourcemap (#7434) --- .../css-sourcemap/__tests__/serve.spec.ts | 66 +++++++++++++++++++ packages/playground/css-sourcemap/index.html | 8 +++ packages/vite/src/node/plugins/html.ts | 31 ++++----- .../src/node/server/middlewares/indexHtml.ts | 23 ++++--- .../vite/src/node/server/pluginContainer.ts | 2 +- 5 files changed, 103 insertions(+), 27 deletions(-) diff --git a/packages/playground/css-sourcemap/__tests__/serve.spec.ts b/packages/playground/css-sourcemap/__tests__/serve.spec.ts index 50c256298143ab..fb2dd97967d91f 100644 --- a/packages/playground/css-sourcemap/__tests__/serve.spec.ts +++ b/packages/playground/css-sourcemap/__tests__/serve.spec.ts @@ -30,6 +30,66 @@ if (!isBuild) { return m } + test('inline css', async () => { + const css = await getStyleTagContentIncluding('.inline ') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AAGO;AACP,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACX,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACf,CAAC,CAAC,CAAC;", + "sources": Array [ + "/root/index.html", + ], + "sourcesContent": Array [ + " + + + + +
+

CSS Sourcemap

+ +

<inline>

+ +

<linked>: no import

+

<linked>: with import

+ +

<imported>: no import

+

<imported>: with import

+ +

<imported sass>

+

<imported sass> with module

+ +

<imported less> with string additionalData

+ +

<imported stylus>

+
+ + + ", + ], + "version": 3, + } + `) + }) + test('linked css', async () => { const res = await page.request.get( new URL('./linked.css', page.url()).href, @@ -207,6 +267,12 @@ if (!isBuild) { } `) }) + + test('should not output missing source file warning', () => { + serverLogs.forEach((log) => { + expect(log).not.toMatch(/Sourcemap for .+ points to missing source files/) + }) + }) } else { test('this file only includes test for serve', () => { expect(true).toBe(true) diff --git a/packages/playground/css-sourcemap/index.html b/packages/playground/css-sourcemap/index.html index 2fedceb8f2be44..d0e9980b926125 100644 --- a/packages/playground/css-sourcemap/index.html +++ b/packages/playground/css-sourcemap/index.html @@ -1,9 +1,17 @@ + +

CSS Sourcemap

+

<inline>

+

<linked>: no import

<linked>: with import

diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index c8a5617a92b253..733b2e4829f12c 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -5,7 +5,8 @@ import type { OutputAsset, OutputBundle, OutputChunk, - RollupError + RollupError, + SourceMapInput } from 'rollup' import { cleanUrl, @@ -54,7 +55,7 @@ export const isHTMLRequest = (request: string): boolean => // HTML Proxy Caches are stored by config -> filePath -> index export const htmlProxyMap = new WeakMap< ResolvedConfig, - Map> + Map> >() // HTML Proxy Transform result are stored by config @@ -83,7 +84,7 @@ export function htmlInlineProxyPlugin(config: ResolvedConfig): Plugin { const file = cleanUrl(id) const url = file.replace(normalizePath(config.root), '') const result = htmlProxyMap.get(config)!.get(url)![index] - if (typeof result === 'string') { + if (result) { return result } else { throw new Error(`No matching HTML proxy module found from ${id}`) @@ -97,7 +98,7 @@ export function addToHTMLProxyCache( config: ResolvedConfig, filePath: string, index: number, - code: string + result: { code: string; map?: SourceMapInput } ): void { if (!htmlProxyMap.get(config)) { htmlProxyMap.set(config, new Map()) @@ -105,7 +106,7 @@ export function addToHTMLProxyCache( if (!htmlProxyMap.get(config)!.get(filePath)) { htmlProxyMap.get(config)!.set(filePath, []) } - htmlProxyMap.get(config)!.get(filePath)![index] = code + htmlProxyMap.get(config)!.get(filePath)![index] = result } export function addToHTMLProxyTransformResult( @@ -284,12 +285,9 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { .join('') // const filePath = id.replace(normalizePath(config.root), '') - addToHTMLProxyCache( - config, - filePath, - inlineModuleIndex, - contents - ) + addToHTMLProxyCache(config, filePath, inlineModuleIndex, { + code: contents + }) js += `\nimport "${id}?html-proxy&index=${inlineModuleIndex}.js"` shouldRemove = true } @@ -364,7 +362,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { const styleNode = inlineStyle.value! const code = styleNode.content! const filePath = id.replace(normalizePath(config.root), '') - addToHTMLProxyCache(config, filePath, inlineModuleIndex, code) + addToHTMLProxyCache(config, filePath, inlineModuleIndex, { code }) // will transform with css plugin and cache result with css-post plugin js += `\nimport "${id}?html-proxy&inline-css&index=${inlineModuleIndex}.css"` @@ -382,12 +380,9 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { const styleNode = node.children.pop() as TextNode const filePath = id.replace(normalizePath(config.root), '') inlineModuleIndex++ - addToHTMLProxyCache( - config, - filePath, - inlineModuleIndex, - styleNode.content - ) + addToHTMLProxyCache(config, filePath, inlineModuleIndex, { + code: styleNode.content + }) js += `\nimport "${id}?html-proxy&index=${inlineModuleIndex}.css"` shouldRemove = true } diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 07b2693c07f995..ca2538bd9507ed 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -1,7 +1,7 @@ import fs from 'fs' import path from 'path' import MagicString from 'magic-string' -import type { AttributeNode, ElementNode } from '@vue/compiler-dom' +import type { AttributeNode, ElementNode, TextNode } from '@vue/compiler-dom' import { NodeTypes } from '@vue/compiler-dom' import type { Connect } from 'types/connect' import type { IndexHtmlTransformHook } from '../../plugins/html' @@ -38,7 +38,9 @@ function getHtmlFilename(url: string, server: ViteDevServer) { if (url.startsWith(FS_PREFIX)) { return decodeURIComponent(fsPathFromId(url)) } else { - return decodeURIComponent(path.join(server.config.root, url.slice(1))) + return decodeURIComponent( + normalizePath(path.join(server.config.root, url.slice(1))) + ) } } @@ -90,7 +92,7 @@ const processNodeUrl = ( } const devHtmlHook: IndexHtmlTransformHook = async ( html, - { path: htmlPath, server, originalUrl } + { path: htmlPath, filename, server, originalUrl } ) => { const { config, moduleGraph } = server! const base = config.base || '/' @@ -104,12 +106,17 @@ const devHtmlHook: IndexHtmlTransformHook = async ( const url = filePath.replace(normalizePath(config.root), '') - const contents = node.children - .map((child: any) => child.content || '') - .join('') + const contentNode = node.children[0] as TextNode + + const code = contentNode.content + const map = new MagicString(html) + .snip(contentNode.loc.start.offset, contentNode.loc.end.offset) + .generateMap({ hires: true }) + map.sources = [filename] + map.file = filename // add HTML Proxy to Map - addToHTMLProxyCache(config, url, inlineModuleIndex, contents) + addToHTMLProxyCache(config, url, inlineModuleIndex, { code, map }) // inline js module. convert to src="proxy" const modulePath = `${ @@ -141,7 +148,7 @@ const devHtmlHook: IndexHtmlTransformHook = async ( if (src) { processNodeUrl(src, s, config, htmlPath, originalUrl, moduleGraph) - } else if (isModule) { + } else if (isModule && node.children.length) { addInlineModule(node, 'js') } } diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index 590648ed58d164..5efc2670c0f81a 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -442,7 +442,7 @@ export async function createPluginContainer( ? new MagicString(this.originalCode).generateMap({ includeContent: true, hires: true, - source: this.filename + source: cleanUrl(this.filename) }) : null } From 476786b9039d8e020b06cfff3d3ecd5a17621553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Fri, 25 Mar 2022 03:59:32 +0900 Subject: [PATCH 14/92] fix: virtual html sourcemap warning (#7440) --- .../css-sourcemap/__tests__/serve.spec.ts | 2 ++ packages/playground/css-sourcemap/index.html | 2 ++ .../playground/css-sourcemap/vite.config.js | 22 ++++++++++++++++++- packages/vite/src/node/plugins/html.ts | 4 ++++ packages/vite/src/node/server/sourcemap.ts | 4 +++- 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/playground/css-sourcemap/__tests__/serve.spec.ts b/packages/playground/css-sourcemap/__tests__/serve.spec.ts index fb2dd97967d91f..0c6696b0dff7a2 100644 --- a/packages/playground/css-sourcemap/__tests__/serve.spec.ts +++ b/packages/playground/css-sourcemap/__tests__/serve.spec.ts @@ -83,6 +83,8 @@ if (!isBuild) { import './imported.styl' + + ", ], "version": 3, diff --git a/packages/playground/css-sourcemap/index.html b/packages/playground/css-sourcemap/index.html index d0e9980b926125..a943c1d113a9b4 100644 --- a/packages/playground/css-sourcemap/index.html +++ b/packages/playground/css-sourcemap/index.html @@ -41,3 +41,5 @@

CSS Sourcemap

import './imported.styl' + + diff --git a/packages/playground/css-sourcemap/vite.config.js b/packages/playground/css-sourcemap/vite.config.js index 2e70a4a0894406..f743687ddd9b0e 100644 --- a/packages/playground/css-sourcemap/vite.config.js +++ b/packages/playground/css-sourcemap/vite.config.js @@ -36,5 +36,25 @@ module.exports = { }, build: { sourcemap: true - } + }, + plugins: [ + { + name: 'virtual-html', + configureServer(server) { + server.middlewares.use(async (req, res, next) => { + if (req.url === '/virtual.html') { + const t = await server.transformIndexHtml( + '/virtual.html', + '

virtual html

' + ) + res.setHeader('Content-Type', 'text/html') + res.statusCode = 200 + res.end(t) + return + } + next() + }) + } + } + ] } diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 733b2e4829f12c..30e65a36f83bbe 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -705,6 +705,8 @@ export function resolveHtmlTransforms( return [preHooks, postHooks] } +export const maybeVirtualHtmlSet = new Set() + export async function applyHtmlTransforms( html: string, hooks: IndexHtmlTransformHook[], @@ -715,6 +717,8 @@ export async function applyHtmlTransforms( const bodyTags: HtmlTagDescriptor[] = [] const bodyPrependTags: HtmlTagDescriptor[] = [] + maybeVirtualHtmlSet.add(ctx.filename) + for (const hook of hooks) { const res = await hook(html, ctx) if (!res) { diff --git a/packages/vite/src/node/server/sourcemap.ts b/packages/vite/src/node/server/sourcemap.ts index 68684a3c2d6f2a..dc77c4a4714298 100644 --- a/packages/vite/src/node/server/sourcemap.ts +++ b/packages/vite/src/node/server/sourcemap.ts @@ -1,8 +1,9 @@ import path from 'path' import { promises as fs } from 'fs' import type { Logger } from '../logger' -import { createDebugger } from '../utils' +import { createDebugger, normalizePath } from '../utils' import type { SourceMap } from 'rollup' +import { maybeVirtualHtmlSet } from '../plugins/html' const isDebug = !!process.env.DEBUG const debug = createDebugger('vite:sourcemap', { @@ -42,6 +43,7 @@ export async function injectSourcesContent( sourcePath = path.resolve(sourceRoot, sourcePath) } return fs.readFile(sourcePath, 'utf-8').catch(() => { + if (maybeVirtualHtmlSet.has(normalizePath(sourcePath))) return null missingSources.push(sourcePath) return null }) From a2ce20dcdf546d05bcfe533db2bfbd4b8ddfe8d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Fri, 25 Mar 2022 04:00:37 +0900 Subject: [PATCH 15/92] fix: sourcemap missing source files warning with cached vue (#7442) --- packages/vite/src/node/utils.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 4e0f230fd7edfd..edb3410751868a 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -615,13 +615,16 @@ export function combineSourcemaps( // hack for parse broken with normalized absolute paths on windows (C:/path/to/something). // escape them to linux like paths - sourcemapList.forEach((sourcemap) => { - sourcemap.sources = sourcemap.sources.map((source) => + // also avoid mutation here to prevent breaking plugin's using cache to generate sourcemaps like vue (see #7442) + sourcemapList = sourcemapList.map((sourcemap) => { + const newSourcemaps = { ...sourcemap } + newSourcemaps.sources = sourcemap.sources.map((source) => source ? escapeToLinuxLikePath(source) : null ) if (sourcemap.sourceRoot) { - sourcemap.sourceRoot = escapeToLinuxLikePath(sourcemap.sourceRoot) + newSourcemaps.sourceRoot = escapeToLinuxLikePath(sourcemap.sourceRoot) } + return newSourcemaps }) const escapedFilename = escapeToLinuxLikePath(filename) From d649daba7682791178b711d9a3e44a6b5d00990c Mon Sep 17 00:00:00 2001 From: patak-dev Date: Thu, 24 Mar 2022 20:37:00 +0100 Subject: [PATCH 16/92] release: v2.9.0-beta.8 --- packages/vite/CHANGELOG.md | 13 +++++++++++++ packages/vite/package.json | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/vite/CHANGELOG.md b/packages/vite/CHANGELOG.md index a90e6ef2a35369..f337d529d5c4c6 100644 --- a/packages/vite/CHANGELOG.md +++ b/packages/vite/CHANGELOG.md @@ -1,3 +1,16 @@ +## 2.9.0-beta.8 (2022-03-24) + +* fix: consider undefined port when checking port (#7318) ([c7fc7c3](https://github.com/vitejs/vite/commit/c7fc7c3)), closes [#7318](https://github.com/vitejs/vite/issues/7318) +* fix: inline style css sourcemap (#7434) ([47668b5](https://github.com/vitejs/vite/commit/47668b5)), closes [#7434](https://github.com/vitejs/vite/issues/7434) +* fix: sourcemap missing source files warning with cached vue (#7442) ([a2ce20d](https://github.com/vitejs/vite/commit/a2ce20d)), closes [#7442](https://github.com/vitejs/vite/issues/7442) +* fix: update tsconfck to 1.2.1 (#7424) ([a90b03b](https://github.com/vitejs/vite/commit/a90b03b)), closes [#7424](https://github.com/vitejs/vite/issues/7424) +* fix: virtual html sourcemap warning (#7440) ([476786b](https://github.com/vitejs/vite/commit/476786b)), closes [#7440](https://github.com/vitejs/vite/issues/7440) +* fix(less): empty less file error (#7412) ([0535c70](https://github.com/vitejs/vite/commit/0535c70)), closes [#7412](https://github.com/vitejs/vite/issues/7412) +* fix(resolve): skip `module` field when the importer is a `require` call (#7438) ([fe4c1ed](https://github.com/vitejs/vite/commit/fe4c1ed)), closes [#7438](https://github.com/vitejs/vite/issues/7438) +* feat: hide optimized deps found during scan phase logs (#7419) ([f4934e8](https://github.com/vitejs/vite/commit/f4934e8)), closes [#7419](https://github.com/vitejs/vite/issues/7419) + + + ## 2.9.0-beta.7 (2022-03-23) * feat: non-blocking scanning of dependencies (#7379) ([676f545](https://github.com/vitejs/vite/commit/676f545)), closes [#7379](https://github.com/vitejs/vite/issues/7379) diff --git a/packages/vite/package.json b/packages/vite/package.json index 8d82d89b19dcf8..37faf42ccf875b 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -1,6 +1,6 @@ { "name": "vite", - "version": "2.9.0-beta.7", + "version": "2.9.0-beta.8", "license": "MIT", "author": "Evan You", "description": "Native-ESM powered web dev build tool", From 77dc1a19b4f6f079a3861773b63e36177f32504d Mon Sep 17 00:00:00 2001 From: yoho <907415276@qq.com> Date: Sat, 26 Mar 2022 02:33:45 +0800 Subject: [PATCH 17/92] fix: errors in worker handling (#7236) --- .../worker/__tests__/es/es-worker.spec.ts | 102 +++++++++++ .../worker/__tests__/es/vite.config.js | 1 + .../worker/__tests__/worker.spec.ts | 22 +-- .../{newUrl => }/classic-shared-worker.js | 2 +- packages/playground/worker/classic-worker.js | 30 +--- .../emit-chunk-dynamic-import-worker.js | 3 + .../worker/emit-chunk-nested-worker.js | 7 + .../worker/emit-chunk-sub-worker.js | 6 + packages/playground/worker/index.html | 165 ++++++------------ .../worker/{newUrl => modules}/module.js | 0 packages/playground/worker/modules/module1.js | 1 + packages/playground/worker/modules/module2.js | 3 + packages/playground/worker/modules/module3.js | 2 + .../worker/{ => modules}/test-plugin.tsx | 0 .../worker/{ => modules}/workerImport.js | 0 packages/playground/worker/my-worker.ts | 4 +- .../worker/newUrl/classic-worker.js | 5 - .../playground/worker/newUrl/url-worker.js | 3 - packages/playground/worker/package.json | 7 +- .../worker/possible-ts-output-worker.mjs | 2 +- packages/playground/worker/sub-worker.js | 8 - .../worker/{newUrl => }/url-shared-worker.js | 2 +- packages/playground/worker/url-worker.js | 1 + packages/playground/worker/vite.config-es.js | 29 +++ packages/playground/worker/vite.config.js | 13 ++ packages/playground/worker/vite.config.ts | 9 - .../playground/worker/worker/main-classic.js | 28 +++ .../worker/worker/main-format-es.js | 23 +++ .../playground/worker/worker/main-module.js | 84 +++++++++ packages/playground/worker/worker/main.js | 3 + packages/vite/src/node/build.ts | 1 + packages/vite/src/node/config.ts | 4 +- .../src/node/plugins/importAnalysisBuild.ts | 9 +- .../src/node/plugins/workerImportMetaUrl.ts | 18 +- scripts/jestPerTestSetup.ts | 37 ++-- 35 files changed, 438 insertions(+), 196 deletions(-) create mode 100644 packages/playground/worker/__tests__/es/es-worker.spec.ts create mode 100644 packages/playground/worker/__tests__/es/vite.config.js rename packages/playground/worker/{newUrl => }/classic-shared-worker.js (58%) create mode 100644 packages/playground/worker/emit-chunk-dynamic-import-worker.js create mode 100644 packages/playground/worker/emit-chunk-nested-worker.js create mode 100644 packages/playground/worker/emit-chunk-sub-worker.js rename packages/playground/worker/{newUrl => modules}/module.js (100%) create mode 100644 packages/playground/worker/modules/module1.js create mode 100644 packages/playground/worker/modules/module2.js create mode 100644 packages/playground/worker/modules/module3.js rename packages/playground/worker/{ => modules}/test-plugin.tsx (100%) rename packages/playground/worker/{ => modules}/workerImport.js (100%) delete mode 100644 packages/playground/worker/newUrl/classic-worker.js delete mode 100644 packages/playground/worker/newUrl/url-worker.js rename packages/playground/worker/{newUrl => }/url-shared-worker.js (69%) create mode 100644 packages/playground/worker/url-worker.js create mode 100644 packages/playground/worker/vite.config-es.js create mode 100644 packages/playground/worker/vite.config.js delete mode 100644 packages/playground/worker/vite.config.ts create mode 100644 packages/playground/worker/worker/main-classic.js create mode 100644 packages/playground/worker/worker/main-format-es.js create mode 100644 packages/playground/worker/worker/main-module.js create mode 100644 packages/playground/worker/worker/main.js diff --git a/packages/playground/worker/__tests__/es/es-worker.spec.ts b/packages/playground/worker/__tests__/es/es-worker.spec.ts new file mode 100644 index 00000000000000..51497a0f5ebadd --- /dev/null +++ b/packages/playground/worker/__tests__/es/es-worker.spec.ts @@ -0,0 +1,102 @@ +import fs from 'fs' +import path from 'path' +import { untilUpdated, isBuild, testDir } from '../../../testUtils' +import type { Page } from 'playwright-chromium' + +test('normal', async () => { + await page.click('.ping') + await untilUpdated(() => page.textContent('.pong'), 'pong') + await untilUpdated( + () => page.textContent('.mode'), + isBuild ? 'production' : 'development' + ) + await untilUpdated( + () => page.textContent('.bundle-with-plugin'), + 'worker bundle with plugin success!' + ) +}) + +test('TS output', async () => { + await page.click('.ping-ts-output') + await untilUpdated(() => page.textContent('.pong-ts-output'), 'pong') +}) + +test('inlined', async () => { + await page.click('.ping-inline') + await untilUpdated(() => page.textContent('.pong-inline'), 'pong') +}) + +const waitSharedWorkerTick = ( + (resolvedSharedWorkerCount: number) => async (page: Page) => { + await untilUpdated(async () => { + const count = await page.textContent('.tick-count') + // ignore the initial 0 + return count === '1' ? 'page loaded' : '' + }, 'page loaded') + // test.concurrent sequential is not guaranteed + // force page to wait to ensure two pages overlap in time + resolvedSharedWorkerCount++ + if (resolvedSharedWorkerCount < 2) return + + await untilUpdated(() => { + return resolvedSharedWorkerCount === 2 ? 'all pages loaded' : '' + }, 'all pages loaded') + } +)(0) + +test.concurrent.each([[true], [false]])('shared worker', async (doTick) => { + if (doTick) { + await page.click('.tick-shared') + } + await waitSharedWorkerTick(page) +}) + +test('worker emitted', async () => { + await untilUpdated(() => page.textContent('.nested-worker'), 'pong') +}) + +if (isBuild) { + const assetsDir = path.resolve(testDir, 'dist/es/assets') + // assert correct files + test('inlined code generation', async () => { + const files = fs.readdirSync(assetsDir) + expect(files.length).toBe(20) + 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')) + const workerContent = fs.readFileSync( + path.resolve(assetsDir, worker), + 'utf-8' + ) + + // worker should have all imports resolved and no exports + expect(workerContent).not.toMatch(`import`) + expect(workerContent).not.toMatch(`export`) + // chunk + expect(content).toMatch(`new Worker("/es/assets`) + expect(content).toMatch(`new SharedWorker("/es/assets`) + // inlined + expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`) + expect(content).toMatch(`window.Blob`) + }) +} + +test('module worker', async () => { + expect(await page.textContent('.shared-worker-import-meta-url')).toMatch( + 'A string' + ) +}) + +test('classic worker', async () => { + expect(await page.textContent('.classic-worker')).toMatch('A classic') + expect(await page.textContent('.classic-shared-worker')).toMatch('A classic') +}) + +test('emit chunk', async () => { + expect(await page.textContent('.emti-chunk-worker')).toMatch( + '{"msg1":"module1","msg2":"module2","msg3":"module3"}' + ) + expect(await page.textContent('.emti-chunk-dynamic-import-worker')).toMatch( + '"A string/es/"' + ) +}) diff --git a/packages/playground/worker/__tests__/es/vite.config.js b/packages/playground/worker/__tests__/es/vite.config.js new file mode 100644 index 00000000000000..931d457792c4f9 --- /dev/null +++ b/packages/playground/worker/__tests__/es/vite.config.js @@ -0,0 +1 @@ +module.exports = require('../../vite.config-es') diff --git a/packages/playground/worker/__tests__/worker.spec.ts b/packages/playground/worker/__tests__/worker.spec.ts index 6d93e810c0c510..fc381467f6a4d2 100644 --- a/packages/playground/worker/__tests__/worker.spec.ts +++ b/packages/playground/worker/__tests__/worker.spec.ts @@ -53,19 +53,15 @@ test.concurrent.each([[true], [false]])('shared worker', async (doTick) => { test('worker emitted', async () => { await untilUpdated(() => page.textContent('.nested-worker'), 'pong') - await untilUpdated( - () => page.textContent('.nested-worker-dynamic-import'), - '"msg":"pong"' - ) }) if (isBuild) { - const assetsDir = path.resolve(testDir, 'dist/assets') + const assetsDir = path.resolve(testDir, 'dist/iife/assets') // assert correct files test('inlined code generation', async () => { const files = fs.readdirSync(assetsDir) - expect(files.length).toBe(11) - const index = files.find((f) => f.includes('index')) + expect(files.length).toBe(13) + 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')) const workerContent = fs.readFileSync( @@ -77,15 +73,21 @@ if (isBuild) { expect(workerContent).not.toMatch(`import`) expect(workerContent).not.toMatch(`export`) // chunk - expect(content).toMatch(`new Worker("/assets`) - expect(content).toMatch(`new SharedWorker("/assets`) + expect(content).toMatch(`new Worker("/iife/assets`) + expect(content).toMatch(`new SharedWorker("/iife/assets`) // inlined expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`) expect(content).toMatch(`window.Blob`) }) } -test('classic worker is run', async () => { +test('module worker', async () => { + expect(await page.textContent('.shared-worker-import-meta-url')).toMatch( + 'A string' + ) +}) + +test('classic worker', async () => { expect(await page.textContent('.classic-worker')).toMatch('A classic') expect(await page.textContent('.classic-shared-worker')).toMatch('A classic') }) diff --git a/packages/playground/worker/newUrl/classic-shared-worker.js b/packages/playground/worker/classic-shared-worker.js similarity index 58% rename from packages/playground/worker/newUrl/classic-shared-worker.js rename to packages/playground/worker/classic-shared-worker.js index 462e49dfa8847f..8bd39e194f0618 100644 --- a/packages/playground/worker/newUrl/classic-shared-worker.js +++ b/packages/playground/worker/classic-shared-worker.js @@ -1,4 +1,4 @@ -importScripts('/classic.js') +importScripts(`/${self.location.pathname.split('/')[1]}/classic.js`) self.onconnect = (event) => { const port = event.ports[0] diff --git a/packages/playground/worker/classic-worker.js b/packages/playground/worker/classic-worker.js index bb6f9c3f49fc84..0700428ee0c80b 100644 --- a/packages/playground/worker/classic-worker.js +++ b/packages/playground/worker/classic-worker.js @@ -1,29 +1,5 @@ -// prettier-ignore -function text(el, text) { - document.querySelector(el).textContent = text -} +importScripts(`/${self.location.pathname.split("/")[1]}/classic.js`) -const classicWorker = new Worker( - new URL('./newUrl/classic-worker.js', import.meta.url) /* , */ , - // test comment - -) - -classicWorker.addEventListener('message', ({ data }) => { - text('.classic-worker', data) -}) -classicWorker.postMessage('ping') - -const classicSharedWorker = new SharedWorker( - new URL('./newUrl/classic-shared-worker.js', import.meta.url), - { - type: 'classic' - } -) -classicSharedWorker.port.addEventListener('message', (ev) => { - text( - '.classic-shared-worker', - ev.data - ) +self.addEventListener('message', () => { + self.postMessage(self.constant) }) -classicSharedWorker.port.start() diff --git a/packages/playground/worker/emit-chunk-dynamic-import-worker.js b/packages/playground/worker/emit-chunk-dynamic-import-worker.js new file mode 100644 index 00000000000000..f96e0b15d26497 --- /dev/null +++ b/packages/playground/worker/emit-chunk-dynamic-import-worker.js @@ -0,0 +1,3 @@ +import('./modules/module').then((module) => { + self.postMessage(module.default + import.meta.env.BASE_URL) +}) diff --git a/packages/playground/worker/emit-chunk-nested-worker.js b/packages/playground/worker/emit-chunk-nested-worker.js new file mode 100644 index 00000000000000..dff0f5bc64c5ad --- /dev/null +++ b/packages/playground/worker/emit-chunk-nested-worker.js @@ -0,0 +1,7 @@ +import SubWorker from './emit-chunk-sub-worker?worker' + +const subWorker = new SubWorker() + +subWorker.onmessage = (event) => { + self.postMessage(event.data) +} diff --git a/packages/playground/worker/emit-chunk-sub-worker.js b/packages/playground/worker/emit-chunk-sub-worker.js new file mode 100644 index 00000000000000..bd6b1f6e4f7419 --- /dev/null +++ b/packages/playground/worker/emit-chunk-sub-worker.js @@ -0,0 +1,6 @@ +Promise.all([import('./modules/module2'), import('./modules/module3')]).then( + (data) => { + const _data = { ...data[0], ...data[1] } + self.postMessage(_data) + } +) diff --git a/packages/playground/worker/index.html b/packages/playground/worker/index.html index b3525da299ff5a..60289ff84d6a06 100644 --- a/packages/playground/worker/index.html +++ b/packages/playground/worker/index.html @@ -1,3 +1,4 @@ +

format iife:

Expected values:
@@ -20,113 +21,59 @@ 0
-

new Worker(new Url('path', import.meta.url), { type: 'module' })

-
- -

new SharedWorker(new Url('path', import.meta.url), { type: 'module' })

-
- -

nested worker

-
-
-

new Worker(new Url('path', import.meta.url))

-
- -

new Worker(new Url('path', import.meta.url), { type: 'classic' })

-
- - + .classname { + color: green; + } + + diff --git a/packages/playground/worker/newUrl/module.js b/packages/playground/worker/modules/module.js similarity index 100% rename from packages/playground/worker/newUrl/module.js rename to packages/playground/worker/modules/module.js diff --git a/packages/playground/worker/modules/module1.js b/packages/playground/worker/modules/module1.js new file mode 100644 index 00000000000000..191db09d29c44f --- /dev/null +++ b/packages/playground/worker/modules/module1.js @@ -0,0 +1 @@ +export const msg1 = 'module1' diff --git a/packages/playground/worker/modules/module2.js b/packages/playground/worker/modules/module2.js new file mode 100644 index 00000000000000..60447933b8b16e --- /dev/null +++ b/packages/playground/worker/modules/module2.js @@ -0,0 +1,3 @@ +export * from './module' +export * from './module1' +export const msg2 = 'module2' diff --git a/packages/playground/worker/modules/module3.js b/packages/playground/worker/modules/module3.js new file mode 100644 index 00000000000000..33355423bc030e --- /dev/null +++ b/packages/playground/worker/modules/module3.js @@ -0,0 +1,2 @@ +export * from './module' +export const msg3 = 'module3' diff --git a/packages/playground/worker/test-plugin.tsx b/packages/playground/worker/modules/test-plugin.tsx similarity index 100% rename from packages/playground/worker/test-plugin.tsx rename to packages/playground/worker/modules/test-plugin.tsx diff --git a/packages/playground/worker/workerImport.js b/packages/playground/worker/modules/workerImport.js similarity index 100% rename from packages/playground/worker/workerImport.js rename to packages/playground/worker/modules/workerImport.js diff --git a/packages/playground/worker/my-worker.ts b/packages/playground/worker/my-worker.ts index 550382be72c331..dd6061885128c7 100644 --- a/packages/playground/worker/my-worker.ts +++ b/packages/playground/worker/my-worker.ts @@ -1,5 +1,5 @@ -import { msg, mode } from './workerImport' -import { bundleWithPlugin } from './test-plugin' +import { msg, mode } from './modules/workerImport' +import { bundleWithPlugin } from './modules/test-plugin' self.onmessage = (e) => { if (e.data === 'ping') { diff --git a/packages/playground/worker/newUrl/classic-worker.js b/packages/playground/worker/newUrl/classic-worker.js deleted file mode 100644 index 865810c76fbf85..00000000000000 --- a/packages/playground/worker/newUrl/classic-worker.js +++ /dev/null @@ -1,5 +0,0 @@ -importScripts('/classic.js') - -self.addEventListener('message', () => { - self.postMessage(self.constant) -}) diff --git a/packages/playground/worker/newUrl/url-worker.js b/packages/playground/worker/newUrl/url-worker.js deleted file mode 100644 index afd91bfe613dc2..00000000000000 --- a/packages/playground/worker/newUrl/url-worker.js +++ /dev/null @@ -1,3 +0,0 @@ -import constant from './module' - -self.postMessage(constant) diff --git a/packages/playground/worker/package.json b/packages/playground/worker/package.json index 131df8c4cbf336..54b737ca2d9cc4 100644 --- a/packages/playground/worker/package.json +++ b/packages/playground/worker/package.json @@ -5,8 +5,11 @@ "scripts": { "dev": "vite", "build": "vite build", - "debug": "node --inspect-brk ../../vite/bin/vite", - "preview": "vite preview" + "preview": "vite preview", + "dev:es": "vite --config ./vite.config-es.js dev", + "build:es": "vite --config ./vite.config-es.js build", + "preview:es": "vite --config ./vite.config-es.js preview", + "debug": "node --inspect-brk ../../vite/bin/vite" }, "devDependencies": { "@vitejs/plugin-vue-jsx": "workspace:*" diff --git a/packages/playground/worker/possible-ts-output-worker.mjs b/packages/playground/worker/possible-ts-output-worker.mjs index 2bcce3faa8a50e..25f1a447617cd9 100644 --- a/packages/playground/worker/possible-ts-output-worker.mjs +++ b/packages/playground/worker/possible-ts-output-worker.mjs @@ -1,4 +1,4 @@ -import { msg, mode } from './workerImport' +import { msg, mode } from './modules/workerImport' self.onmessage = (e) => { if (e.data === 'ping') { diff --git a/packages/playground/worker/sub-worker.js b/packages/playground/worker/sub-worker.js index ab64b3667099bb..eec65b86a0382a 100644 --- a/packages/playground/worker/sub-worker.js +++ b/packages/playground/worker/sub-worker.js @@ -3,11 +3,3 @@ self.onmessage = (event) => { self.postMessage('pong') } } -const data = import('./workerImport') -data.then((data) => { - const { mode, msg } = data - self.postMessage({ - mode, - msg - }) -}) diff --git a/packages/playground/worker/newUrl/url-shared-worker.js b/packages/playground/worker/url-shared-worker.js similarity index 69% rename from packages/playground/worker/newUrl/url-shared-worker.js rename to packages/playground/worker/url-shared-worker.js index f52de169243056..3535d5c277ec84 100644 --- a/packages/playground/worker/newUrl/url-shared-worker.js +++ b/packages/playground/worker/url-shared-worker.js @@ -1,4 +1,4 @@ -import constant from './module' +import constant from './modules/module' self.onconnect = (event) => { const port = event.ports[0] diff --git a/packages/playground/worker/url-worker.js b/packages/playground/worker/url-worker.js new file mode 100644 index 00000000000000..79d05da0f06082 --- /dev/null +++ b/packages/playground/worker/url-worker.js @@ -0,0 +1 @@ +self.postMessage('A string' + import.meta.env.BASE_URL) diff --git a/packages/playground/worker/vite.config-es.js b/packages/playground/worker/vite.config-es.js new file mode 100644 index 00000000000000..a65dece2d0db21 --- /dev/null +++ b/packages/playground/worker/vite.config-es.js @@ -0,0 +1,29 @@ +const vueJsx = require('@vitejs/plugin-vue-jsx') +const vite = require('vite') +const path = require('path') + +module.exports = vite.defineConfig({ + base: '/es/', + enforce: 'pre', + worker: { + format: 'es', + plugins: [vueJsx()] + }, + build: { + outDir: 'dist/es' + }, + plugins: [ + { + name: 'resolve-format-es', + + transform(code, id) { + if (id.includes('main.js')) { + return code.replace( + `/* flag: will replace in vite config import("./format-es.js") */`, + `import("./main-format-es")` + ) + } + } + } + ] +}) diff --git a/packages/playground/worker/vite.config.js b/packages/playground/worker/vite.config.js new file mode 100644 index 00000000000000..b7760bc4d7a240 --- /dev/null +++ b/packages/playground/worker/vite.config.js @@ -0,0 +1,13 @@ +const vueJsx = require('@vitejs/plugin-vue-jsx') +const vite = require('vite') + +module.exports = vite.defineConfig({ + base: '/iife/', + worker: { + format: 'iife', + plugins: [vueJsx()] + }, + build: { + outDir: 'dist/iife' + } +}) diff --git a/packages/playground/worker/vite.config.ts b/packages/playground/worker/vite.config.ts deleted file mode 100644 index 6cef7d9cea0bed..00000000000000 --- a/packages/playground/worker/vite.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import vueJsx from '@vitejs/plugin-vue-jsx' -import { defineConfig } from 'vite' - -export default defineConfig({ - worker: { - format: 'es', - plugins: [vueJsx()] - } -}) diff --git a/packages/playground/worker/worker/main-classic.js b/packages/playground/worker/worker/main-classic.js new file mode 100644 index 00000000000000..4ef7776a56876e --- /dev/null +++ b/packages/playground/worker/worker/main-classic.js @@ -0,0 +1,28 @@ +// prettier-ignore +function text(el, text) { + document.querySelector(el).textContent = text +} + +let classicWorker = new Worker( + new URL('../classic-worker.js', import.meta.url) /* , */ + // test comment +) + +// just test for case: ') ... ,' mean no worker options parmas +classicWorker = new Worker(new URL('../classic-worker.js', import.meta.url)) + +classicWorker.addEventListener('message', ({ data }) => { + text('.classic-worker', JSON.stringify(data)) +}) +classicWorker.postMessage('ping') + +const classicSharedWorker = new SharedWorker( + new URL('../classic-shared-worker.js', import.meta.url), + { + type: 'classic' + } +) +classicSharedWorker.port.addEventListener('message', (ev) => { + text('.classic-shared-worker', JSON.stringify(ev.data)) +}) +classicSharedWorker.port.start() diff --git a/packages/playground/worker/worker/main-format-es.js b/packages/playground/worker/worker/main-format-es.js new file mode 100644 index 00000000000000..aa16636c34c73a --- /dev/null +++ b/packages/playground/worker/worker/main-format-es.js @@ -0,0 +1,23 @@ +// run when format es +import NestedWorker from '../emit-chunk-nested-worker?worker' + +function text(el, text) { + document.querySelector(el).textContent = text +} + +text('.format-es', 'format es:') + +const nestedWorker = new NestedWorker() +nestedWorker.addEventListener('message', (ev) => { + text('.emti-chunk-worker', JSON.stringify(ev.data)) +}) + +const dynamicImportWorker = new Worker( + new URL('../emit-chunk-dynamic-import-worker.js', import.meta.url), + { + type: 'module' + } +) +dynamicImportWorker.addEventListener('message', (ev) => { + text('.emti-chunk-dynamic-import-worker', JSON.stringify(ev.data)) +}) diff --git a/packages/playground/worker/worker/main-module.js b/packages/playground/worker/worker/main-module.js new file mode 100644 index 00000000000000..b66c7299cdf690 --- /dev/null +++ b/packages/playground/worker/worker/main-module.js @@ -0,0 +1,84 @@ +import myWorker from '../my-worker?worker' +import InlineWorker from '../my-worker?worker&inline' +import mySharedWorker from '../my-shared-worker?sharedworker&name=shared' +import TSOutputWorker from '../possible-ts-output-worker?worker' +import NestedWorker from '../worker-nested-worker?worker' +import { mode } from '../modules/workerImport' + +function text(el, text) { + document.querySelector(el).textContent = text +} + +document.querySelector('.mode-true').textContent = mode + +const worker = new myWorker() +worker.addEventListener('message', (e) => { + text('.pong', e.data.msg) + text('.mode', e.data.mode) + text('.bundle-with-plugin', e.data.bundleWithPlugin) +}) + +document.querySelector('.ping').addEventListener('click', () => { + worker.postMessage('ping') +}) + +const inlineWorker = new InlineWorker() +inlineWorker.addEventListener('message', (e) => { + text('.pong-inline', e.data.msg) +}) + +document.querySelector('.ping-inline').addEventListener('click', () => { + inlineWorker.postMessage('ping') +}) + +const sharedWorker = new mySharedWorker() +document.querySelector('.tick-shared').addEventListener('click', () => { + sharedWorker.port.postMessage('tick') +}) + +sharedWorker.port.addEventListener('message', (event) => { + text('.tick-count', event.data) +}) + +sharedWorker.port.start() + +const tsOutputWorker = new TSOutputWorker() +tsOutputWorker.addEventListener('message', (e) => { + text('.pong-ts-output', e.data.msg) +}) + +document.querySelector('.ping-ts-output').addEventListener('click', () => { + tsOutputWorker.postMessage('ping') +}) + +const nestedWorker = new NestedWorker() +nestedWorker.addEventListener('message', (ev) => { + if (typeof ev.data === 'string') { + text('.nested-worker', JSON.stringify(ev.data)) + } +}) +nestedWorker.postMessage('ping') + +const workerOptions = { type: 'module' } +// url import worker +const w = new Worker( + new URL('../url-worker.js', import.meta.url), + /* @vite-ignore */ workerOptions +) +w.addEventListener('message', (ev) => + text('.worker-import-meta-url', JSON.stringify(ev.data)) +) + +const genWorkerName = () => 'module' +const w2 = new SharedWorker( + new URL('../url-shared-worker.js', import.meta.url), + { + /* @vite-ignore */ + name: genWorkerName(), + type: 'module' + } +) +w2.port.addEventListener('message', (ev) => { + text('.shared-worker-import-meta-url', JSON.stringify(ev.data)) +}) +w2.port.start() diff --git a/packages/playground/worker/worker/main.js b/packages/playground/worker/worker/main.js new file mode 100644 index 00000000000000..953b5ef1bf3b53 --- /dev/null +++ b/packages/playground/worker/worker/main.js @@ -0,0 +1,3 @@ +/* flag: will replace in vite config import("./format-es.js") */ +import('./main-module') +import('./main-classic') diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 7f0b014659b221..6b4d38836b6c51 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -306,6 +306,7 @@ export function resolveBuildPlugins(config: ResolvedConfig): { post: Plugin[] } { const options = config.build + return { pre: [ watchPackageDataPlugin(config), diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 68fe8abe85fc6d..9910cbb3a8b004 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -248,6 +248,7 @@ export type ResolvedConfig = Readonly< cacheDir: string command: 'build' | 'serve' mode: string + isWorker: boolean isProduction: boolean env: Record resolve: ResolveOptions & { @@ -476,6 +477,7 @@ export async function resolveConfig( cacheDir, command, mode, + isWorker: false, isProduction, plugins: userPlugins, server, @@ -508,7 +510,7 @@ export async function resolveConfig( // flat config.worker.plugin const [workerPrePlugins, workerNormalPlugins, workerPostPlugins] = sortUserPlugins(config.worker?.plugins as Plugin[]) - const workerResolved = { ...resolved } + const workerResolved: ResolvedConfig = { ...resolved, isWorker: true } resolved.worker.plugins = await resolvePlugins( workerResolved, workerPrePlugins, diff --git a/packages/vite/src/node/plugins/importAnalysisBuild.ts b/packages/vite/src/node/plugins/importAnalysisBuild.ts index c8bef0231af757..91ce663b9f8111 100644 --- a/packages/vite/src/node/plugins/importAnalysisBuild.ts +++ b/packages/vite/src/node/plugins/importAnalysisBuild.ts @@ -86,6 +86,7 @@ function preload(baseModule: () => Promise<{}>, deps?: string[]) { export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { const ssr = !!config.build.ssr const insertPreload = !(ssr || !!config.build.lib) + const isWorker = config.isWorker const scriptRel = config.build.polyfillModulePreload ? `'modulepreload'` @@ -120,6 +121,11 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { return } + if (isWorker) { + // preload method use `document` and can't run in the worker + return + } + await init let imports: readonly ImportSpecifier[] = [] @@ -132,7 +138,6 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { if (!imports.length) { return null } - let s: MagicString | undefined const str = () => s || (s = new MagicString(source)) let needPreloadHelper = false @@ -241,7 +246,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { }, generateBundle({ format }, bundle) { - if (format !== 'es' || ssr) { + if (format !== 'es' || ssr || isWorker) { return } diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index f3ed3cf1b8cbe0..233d83d066bcb7 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -15,6 +15,7 @@ import { parseRequest } from '../utils' import { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants' import MagicString from 'magic-string' import type { ViteDevServer } from '..' +import type { RollupError } from 'rollup' type WorkerType = 'classic' | 'module' | 'ignore' @@ -25,14 +26,26 @@ function getWorkerType( noCommentsCode: string, i: number ): WorkerType { + function err(e: string, pos: number) { + const error = new Error(e) as RollupError + error.pos = pos + throw error + } + const commaIndex = noCommentsCode.indexOf(',', i) if (commaIndex === -1) { return 'classic' } const endIndex = noCommentsCode.indexOf(')', i) + // case: ') ... ,' mean no worker options params + if (commaIndex > endIndex) { + return 'classic' + } + // need to find in comment code let workerOptsString = code.substring(commaIndex + 1, endIndex) + const hasViteIgnore = /\/\*\s*@vite-ignore\s*\*\//.test(workerOptsString) if (hasViteIgnore) { return 'ignore' @@ -49,9 +62,10 @@ function getWorkerType( workerOpts = JSON5.parse(workerOptsString) } catch (e) { // can't parse by JSON5, so the worker options had unexpect char. - throw new Error( + err( 'Vite is unable to parse the worker options as the value is not static.' + - 'To ignore this error, please use /* @vite-ignore */ in the worker options.' + 'To ignore this error, please use /* @vite-ignore */ in the worker options.', + commaIndex + 1 ) } diff --git a/scripts/jestPerTestSetup.ts b/scripts/jestPerTestSetup.ts index 312d254a3b11d2..150c02eed5b76c 100644 --- a/scripts/jestPerTestSetup.ts +++ b/scripts/jestPerTestSetup.ts @@ -4,12 +4,12 @@ import { resolve, dirname } from 'path' import sirv from 'sirv' import type { ViteDevServer, - UserConfig, + InlineConfig, PluginOption, ResolvedConfig, Logger } from 'vite' -import { createServer, build } from 'vite' +import { createServer, build, mergeConfig } from 'vite' import type { Page, ConsoleMessage } from 'playwright-chromium' import type { RollupError, RollupWatcher, RollupWatcherEvent } from 'rollup' @@ -90,9 +90,16 @@ beforeAll(async () => { } } + const testCustomConfig = resolve(dirname(testPath), 'vite.config.js') + let config: InlineConfig | undefined + if (fs.existsSync(testCustomConfig)) { + // test has custom server configuration. + config = require(testCustomConfig) + } + const serverLogs: string[] = [] - const options: UserConfig = { + const options: InlineConfig = { root: rootDir, logLevel: 'silent', server: { @@ -120,7 +127,9 @@ beforeAll(async () => { if (!isBuildTest) { process.env.VITE_INLINE = 'inline-serve' - server = await (await createServer(options)).listen() + server = await ( + await createServer(mergeConfig(options, config || {})) + ).listen() // use resolved port/base from server const base = server.config.base === '/' ? '' : server.config.base const url = @@ -137,14 +146,14 @@ beforeAll(async () => { } }) options.plugins = [resolvedPlugin()] - const rollupOutput = await build(options) + const rollupOutput = await build(mergeConfig(options, config || {})) const isWatch = !!resolvedConfig!.build.watch // in build watch,call startStaticServer after the build is complete if (isWatch) { global.watcher = rollupOutput as RollupWatcher await notifyRebuildComplete(global.watcher) } - const url = (global.viteTestUrl = await startStaticServer()) + const url = (global.viteTestUrl = await startStaticServer(config)) await page.goto(url) } } @@ -172,13 +181,15 @@ afterAll(async () => { } }) -function startStaticServer(): Promise { - // check if the test project has base config - const configFile = resolve(rootDir, 'vite.config.js') - let config: UserConfig | undefined - try { - config = require(configFile) - } catch (e) {} +function startStaticServer(config?: InlineConfig): Promise { + if (!config) { + // check if the test project has base config + const configFile = resolve(rootDir, 'vite.config.js') + try { + config = require(configFile) + } catch (e) {} + } + // fallback internal base to '' const base = (config?.base ?? '/') === '/' ? '' : config?.base ?? '' From d451435fb8db776e302c18b74717131685bb6048 Mon Sep 17 00:00:00 2001 From: Julien Papini Date: Fri, 25 Mar 2022 22:23:30 +0100 Subject: [PATCH 18/92] feat(plugin-react): adding jsxPure option (#7088) --- packages/plugin-react/src/index.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/plugin-react/src/index.ts b/packages/plugin-react/src/index.ts index afa1e2c5422da2..76f6c6c2a799f3 100644 --- a/packages/plugin-react/src/index.ts +++ b/packages/plugin-react/src/index.ts @@ -32,6 +32,12 @@ export interface Options { * @default "react" */ jsxImportSource?: string + /** + * Set this to `true` to annotate the JSX factory with `\/* @__PURE__ *\/`. + * This option is ignored when `jsxRuntime` is not `"automatic"`. + * @default true + */ + jsxPure?: boolean /** * Babel configuration applied in both dev and prod. @@ -195,7 +201,8 @@ export default function viteReact(opts: Options = {}): PluginOption[] { ), { runtime: 'automatic', - importSource: opts.jsxImportSource + importSource: opts.jsxImportSource, + pure: opts.jsxPure !== false } ]) From fedb106a4d0d613001230b9e757bcb8f0afe9909 Mon Sep 17 00:00:00 2001 From: stygian-desolator <73412177+stygian-desolator@users.noreply.github.com> Date: Sat, 26 Mar 2022 06:47:44 +0800 Subject: [PATCH 19/92] feat: support importing css with ?raw (#5796) --- .../playground/assets/__tests__/assets.spec.ts | 10 ++++++++++ packages/playground/assets/index.html | 6 ++++++ packages/playground/css/__tests__/css.spec.ts | 8 ++++++++ packages/playground/css/index.html | 3 +++ packages/playground/css/main.js | 3 +++ packages/playground/css/raw-imported.css | 3 +++ packages/vite/src/node/plugins/css.ts | 14 +++++++++++--- 7 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 packages/playground/css/raw-imported.css diff --git a/packages/playground/assets/__tests__/assets.spec.ts b/packages/playground/assets/__tests__/assets.spec.ts index 0635236bd7369e..f53c783c52b606 100644 --- a/packages/playground/assets/__tests__/assets.spec.ts +++ b/packages/playground/assets/__tests__/assets.spec.ts @@ -194,6 +194,16 @@ test('?url import', async () => { ) }) +test('?url import on css', async () => { + const src = readFile('css/icons.css') + const txt = await page.textContent('.url-css') + expect(txt).toEqual( + isBuild + ? `data:text/css;base64,${Buffer.from(src).toString('base64')}` + : '/foo/css/icons.css' + ) +}) + describe('unicode url', () => { test('from js import', async () => { const src = readFile('テスト-測試-white space.js') diff --git a/packages/playground/assets/index.html b/packages/playground/assets/index.html index 25c889a445b771..7534ecbe1677bf 100644 --- a/packages/playground/assets/index.html +++ b/packages/playground/assets/index.html @@ -134,6 +134,9 @@

?raw import

?url import

+

?url import with css

+ +

new URL('...', import.meta.url)

@@ -242,6 +245,9 @@

import unicodeUrl from './テスト-測試-white space.js?url' text('.unicode-url', unicodeUrl) + import cssUrl from './css/icons.css?url' + text('.url-css', cssUrl) + // const url = new URL('non_existent_file.png', import.meta.url) const metaUrl = new URL('./nested/asset.png', import.meta.url) text('.import-meta-url', metaUrl) diff --git a/packages/playground/css/__tests__/css.spec.ts b/packages/playground/css/__tests__/css.spec.ts index 360e46dbbba150..34858ab34bc09d 100644 --- a/packages/playground/css/__tests__/css.spec.ts +++ b/packages/playground/css/__tests__/css.spec.ts @@ -365,6 +365,14 @@ test('minify css', async () => { expect(cssFile).not.toMatch('#ffff00b3') }) +test('?raw', async () => { + const rawImportCss = await page.$('.raw-imported-css') + + expect(await rawImportCss.textContent()).toBe( + require('fs').readFileSync(require.resolve('../raw-imported.css'), 'utf-8') + ) +}) + test('import css in less', async () => { expect(await getColor('.css-in-less')).toBe('yellow') expect(await getColor('.css-in-less-2')).toBe('blue') diff --git a/packages/playground/css/index.html b/packages/playground/css/index.html index acbbe44a7f8a60..a09d8e6e7c46aa 100644 --- a/packages/playground/css/index.html +++ b/packages/playground/css/index.html @@ -118,6 +118,9 @@

CSS


+
+  

Raw Support

+

 
 
 
diff --git a/packages/playground/css/main.js b/packages/playground/css/main.js
index 3599ed0d60562c..6edd840a87c5e7 100644
--- a/packages/playground/css/main.js
+++ b/packages/playground/css/main.js
@@ -12,6 +12,9 @@ text('.imported-less', less)
 import stylus from './stylus.styl'
 text('.imported-stylus', stylus)
 
+import rawCss from './raw-imported.css?raw'
+text('.raw-imported-css', rawCss)
+
 import mod from './mod.module.css'
 document.querySelector('.modules').classList.add(mod['apply-color'])
 text('.modules-code', JSON.stringify(mod, null, 2))
diff --git a/packages/playground/css/raw-imported.css b/packages/playground/css/raw-imported.css
new file mode 100644
index 00000000000000..ac0aee96390c33
--- /dev/null
+++ b/packages/playground/css/raw-imported.css
@@ -0,0 +1,3 @@
+.raw-imported {
+  color: yellow;
+}
diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts
index f45a1ac01fb6f8..af9f689cfecab8 100644
--- a/packages/vite/src/node/plugins/css.ts
+++ b/packages/vite/src/node/plugins/css.ts
@@ -27,7 +27,7 @@ import type {
 } from 'rollup'
 import { dataToEsm } from '@rollup/pluginutils'
 import colors from 'picocolors'
-import { CLIENT_PUBLIC_PATH } from '../constants'
+import { CLIENT_PUBLIC_PATH, SPECIAL_QUERY_RE } from '../constants'
 import type { ResolveFn, ViteDevServer } from '../'
 import {
   getAssetFilename,
@@ -163,7 +163,11 @@ export function cssPlugin(config: ResolvedConfig): Plugin {
     },
 
     async transform(raw, id, options) {
-      if (!isCSSRequest(id) || commonjsProxyRE.test(id)) {
+      if (
+        !isCSSRequest(id) ||
+        commonjsProxyRE.test(id) ||
+        SPECIAL_QUERY_RE.test(id)
+      ) {
         return
       }
       const ssr = options?.ssr === true
@@ -280,7 +284,11 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
     },
 
     async transform(css, id, options) {
-      if (!isCSSRequest(id) || commonjsProxyRE.test(id)) {
+      if (
+        !isCSSRequest(id) ||
+        commonjsProxyRE.test(id) ||
+        SPECIAL_QUERY_RE.test(id)
+      ) {
         return
       }
 

From 80afb873311b5c3ef07ae26b92966ab3a49607bf Mon Sep 17 00:00:00 2001
From: Johannes Maas 
Date: Sat, 26 Mar 2022 09:42:16 +0100
Subject: [PATCH 20/92] docs: point out symlink issues and solution on Windows
 (#7465)

---
 CONTRIBUTING.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fdae54c968c964..a87561a8e4146c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -69,7 +69,9 @@ And re-run `pnpm install` to link the package.
 
 Each package under `packages/playground/` contains a `__tests__` directory. The tests are run using [Jest](https://jestjs.io/) + [Playwright](https://playwright.dev/) with custom integrations to make writing tests simple. The detailed setup is inside `jest.config.js` and `scripts/jest*` files.
 
-Each test can be run under either dev server mode or build mode. Make sure that [Vite has been built](#repo-setup).
+Before running the tests, make sure that [Vite has been built](#repo-setup). On Windows, you may want to [activate Developer Mode](https://docs.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development) to solve [issues with symlink creation for non-admins](https://github.com/vitejs/vite/issues/7390).
+
+Each test can be run under either dev server mode or build mode.
 
 - `pnpm test` by default runs every test in both serve and build mode.
 

From 0d7ef43984ce62efca7db4ca10eb93a6998eb9be Mon Sep 17 00:00:00 2001
From: Johannes Maas 
Date: Sat, 26 Mar 2022 12:19:14 +0100
Subject: [PATCH 21/92] test: instructions when encountering symlink error on
 Windows (#7470)

---
 scripts/jestGlobalSetup.cjs | 24 +++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)

diff --git a/scripts/jestGlobalSetup.cjs b/scripts/jestGlobalSetup.cjs
index 9640f70c5a8291..7341cba40968d9 100644
--- a/scripts/jestGlobalSetup.cjs
+++ b/scripts/jestGlobalSetup.cjs
@@ -21,11 +21,21 @@ module.exports = async () => {
 
   const tempDir = path.resolve(__dirname, '../packages/temp')
   await fs.remove(tempDir)
-  await fs.copy(path.resolve(__dirname, '../packages/playground'), tempDir, {
-    dereference: false,
-    filter(file) {
-      file = file.replace(/\\/g, '/')
-      return !file.includes('__tests__') && !file.match(/dist(\/|$)/)
-    }
-  })
+  await fs
+    .copy(path.resolve(__dirname, '../packages/playground'), tempDir, {
+      dereference: false,
+      filter(file) {
+        file = file.replace(/\\/g, '/')
+        return !file.includes('__tests__') && !file.match(/dist(\/|$)/)
+      }
+    })
+    .catch(async (error) => {
+      if (error.code === 'EPERM' && error.syscall === 'symlink') {
+        throw new Error(
+          'Could not create symlinks. On Windows, consider activating Developer Mode to allow non-admin users to create symlinks by following the instructions at https://docs.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development.'
+        )
+      } else {
+        throw error
+      }
+    })
 }

From e29ea8ea37c295bc500a2321b831f65a893ec750 Mon Sep 17 00:00:00 2001
From: Anthony Fu 
Date: Sat, 26 Mar 2022 22:42:07 +0800
Subject: [PATCH 22/92] feat(dev): expose APIs for client-server communication
 (#7437)

---
 docs/guide/api-hmr.md                         |   8 +
 docs/guide/api-plugin.md                      |  70 ++++++++-
 packages/playground/hmr/__tests__/hmr.spec.ts |   7 +-
 packages/playground/hmr/hmr.js                |   6 +
 packages/playground/hmr/index.html            |   1 +
 packages/playground/hmr/vite.config.js        |  13 +-
 packages/vite/src/client/client.ts            |  16 +-
 packages/vite/src/node/index.ts               |   6 +-
 packages/vite/src/node/server/ws.ts           | 144 ++++++++++++++++--
 packages/vite/types/importMeta.d.ts           |   2 +
 10 files changed, 250 insertions(+), 23 deletions(-)

diff --git a/docs/guide/api-hmr.md b/docs/guide/api-hmr.md
index f4ddf59d8abcd1..46eabab04e8868 100644
--- a/docs/guide/api-hmr.md
+++ b/docs/guide/api-hmr.md
@@ -123,3 +123,11 @@ The following HMR events are dispatched by Vite automatically:
 - `'vite:error'` when an error occurs (e.g. syntax error)
 
 Custom HMR events can also be sent from plugins. See [handleHotUpdate](./api-plugin#handlehotupdate) for more details.
+
+## `hot.send(event, data)`
+
+Send custom events back to Vite's dev server.
+
+If called before connected, the data will be buffered and sent once the connection is established.
+
+See [Client-server Communication](/guide/api-plugin.html#client-server-communication) for more details.
diff --git a/docs/guide/api-plugin.md b/docs/guide/api-plugin.md
index b3888b0fd7009b..13767c45dd3103 100644
--- a/docs/guide/api-plugin.md
+++ b/docs/guide/api-plugin.md
@@ -480,7 +480,7 @@ export default defineConfig({
 
 Check out [Vite Rollup Plugins](https://vite-rollup-plugins.patak.dev) for a list of compatible official Rollup plugins with usage instructions.
 
-## Path normalization
+## Path Normalization
 
 Vite normalizes paths while resolving ids to use POSIX separators ( / ) while preserving the volume in Windows. On the other hand, Rollup keeps resolved paths untouched by default, so resolved ids have win32 separators ( \\ ) in Windows. However, Rollup plugins use a [`normalizePath` utility function](https://github.com/rollup/plugins/tree/master/packages/pluginutils#normalizepath) from `@rollup/pluginutils` internally, which converts separators to POSIX before performing comparisons. This means that when these plugins are used in Vite, the `include` and `exclude` config pattern and other similar paths against resolved ids comparisons work correctly.
 
@@ -492,3 +492,71 @@ import { normalizePath } from 'vite'
 normalizePath('foo\\bar') // 'foo/bar'
 normalizePath('foo/bar') // 'foo/bar'
 ```
+
+## Client-server Communication
+
+Since Vite 2.9, we provide some utilities for plugins to help handle the communication with clients.
+
+### Server to Client
+
+On the plugin side, we could use `server.ws.send` to boardcast events to all the clients:
+
+```js
+// vite.config.js
+export default defineConfig({
+  plugins: [
+    {
+      // ...
+      configureServer(server) {
+        server.ws.send('my:greetings', { msg: 'hello' })
+      }
+    }
+  ]
+})
+```
+
+::: tip NOTE
+We recommend **alway prefixing** your event names to avoid collisions with other plugins.
+:::
+
+On the client side, use [`hot.on`](/guide/api-hmr.html#hot-on-event-cb) to listen to the events:
+
+```ts
+// client side
+if (import.meta.hot) {
+  import.meta.hot.on('my:greetings', (data) => {
+    console.log(data.msg) // hello
+  })
+}
+```
+
+### Client to Server
+
+To send events from the client to the server, we can use [`hot.send`](/guide/api-hmr.html#hot-send-event-payload):
+
+```ts
+// client side
+if (import.meta.hot) {
+  import.meta.hot.send('my:from-client', { msg: 'Hey!' })
+}
+```
+
+Then use `server.ws.on` and listen to the events on the server side:
+
+```js
+// vite.config.js
+export default defineConfig({
+  plugins: [
+    {
+      // ...
+      configureServer(server) {
+        server.ws.on('my:from-client', (data, client) => {
+          console.log('Message from client:', data.msg) // Hey!
+          // reply only to the client (if needed)
+          client.send('my:ack', { msg: 'Hi! I got your message!' })
+        })
+      }
+    }
+  ]
+})
+```
diff --git a/packages/playground/hmr/__tests__/hmr.spec.ts b/packages/playground/hmr/__tests__/hmr.spec.ts
index 1f28763a90df94..4d0491af91a69e 100644
--- a/packages/playground/hmr/__tests__/hmr.spec.ts
+++ b/packages/playground/hmr/__tests__/hmr.spec.ts
@@ -123,11 +123,16 @@ if (!isBuild) {
     await untilUpdated(() => el.textContent(), 'edited')
   })
 
+  test('plugin client-server communication', async () => {
+    const el = await page.$('.custom-communication')
+    await untilUpdated(() => el.textContent(), '3')
+  })
+
   test('full-reload encodeURI path', async () => {
     await page.goto(
       viteTestUrl + '/unicode-path/中文-にほんご-한글-🌕🌖🌗/index.html'
     )
-    let el = await page.$('#app')
+    const el = await page.$('#app')
     expect(await el.textContent()).toBe('title')
     await editFile(
       'unicode-path/中文-にほんご-한글-🌕🌖🌗/index.html',
diff --git a/packages/playground/hmr/hmr.js b/packages/playground/hmr/hmr.js
index 01dca20f5dd71c..e80b517e6449dc 100644
--- a/packages/playground/hmr/hmr.js
+++ b/packages/playground/hmr/hmr.js
@@ -57,6 +57,12 @@ if (import.meta.hot) {
   import.meta.hot.on('foo', ({ msg }) => {
     text('.custom', msg)
   })
+
+  // send custom event to server to calculate 1 + 2
+  import.meta.hot.send('remote-add', { a: 1, b: 2 })
+  import.meta.hot.on('remote-add-result', ({ result }) => {
+    text('.custom-communication', result)
+  })
 }
 
 function text(el, text) {
diff --git a/packages/playground/hmr/index.html b/packages/playground/hmr/index.html
index 766338598e51ad..fc398c60c4cadf 100644
--- a/packages/playground/hmr/index.html
+++ b/packages/playground/hmr/index.html
@@ -5,5 +5,6 @@
 
+
diff --git a/packages/playground/hmr/vite.config.js b/packages/playground/hmr/vite.config.js index c34637844e2170..57252c91be410b 100644 --- a/packages/playground/hmr/vite.config.js +++ b/packages/playground/hmr/vite.config.js @@ -9,14 +9,13 @@ module.exports = { if (file.endsWith('customFile.js')) { const content = await read() const msg = content.match(/export const msg = '(\w+)'/)[1] - server.ws.send({ - type: 'custom', - event: 'foo', - data: { - msg - } - }) + server.ws.send('foo', { msg }) } + }, + configureServer(server) { + server.ws.on('remote-add', ({ a, b }, client) => { + client.send('remote-add-result', { result: a + b }) + }) } } ] diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index a9e8fb639de958..40f0bb0418f365 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -30,6 +30,7 @@ const socketHost = __HMR_PORT__ const socket = new WebSocket(`${socketProtocol}://${socketHost}`, 'vite-hmr') const base = __BASE__ || '/' +const messageBuffer: string[] = [] function warnFailedFetch(err: Error, path: string | string[]) { if (!err.message.match('fetch')) { @@ -59,9 +60,10 @@ async function handleMessage(payload: HMRPayload) { switch (payload.type) { case 'connected': console.log(`[vite] connected.`) + sendMessageBuffer() // proxy(nginx, docker) hmr ws maybe caused timeout, // so send ping package let ws keep alive. - setInterval(() => socket.send('ping'), __HMR_TIMEOUT__) + setInterval(() => socket.send('{"type":"ping"}'), __HMR_TIMEOUT__) break case 'update': notifyListeners('vite:beforeUpdate', payload) @@ -361,6 +363,13 @@ async function fetchUpdate({ path, acceptedPath, timestamp }: Update) { } } +function sendMessageBuffer() { + if (socket.readyState === 1) { + messageBuffer.forEach((msg) => socket.send(msg)) + messageBuffer.length = 0 + } +} + interface HotModule { id: string callbacks: HotCallback[] @@ -478,6 +487,11 @@ export const createHotContext = (ownerPath: string) => { } addToMap(customListenersMap) addToMap(newListeners) + }, + + send: (event: string, data?: any) => { + messageBuffer.push(JSON.stringify({ type: 'custom', event, data })) + sendMessageBuffer() } } diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index 027a715c454a74..2b59da8737029b 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -73,7 +73,11 @@ export type { TransformOptions as EsbuildTransformOptions } from 'esbuild' export type { ESBuildOptions, ESBuildTransformResult } from './plugins/esbuild' export type { Manifest, ManifestChunk } from './plugins/manifest' export type { ResolveOptions, InternalResolveOptions } from './plugins/resolve' -export type { WebSocketServer } from './server/ws' +export type { + WebSocketServer, + WebSocketClient, + WebSocketCustomListener +} from './server/ws' export type { PluginContainer } from './server/pluginContainer' export type { ModuleGraph, ModuleNode, ResolvedUrl } from './server/moduleGraph' export type { SendOptions } from './server/send' diff --git a/packages/vite/src/node/server/ws.ts b/packages/vite/src/node/server/ws.ts index ffbfd7a56eca97..3c6875e2475c64 100644 --- a/packages/vite/src/node/server/ws.ts +++ b/packages/vite/src/node/server/ws.ts @@ -3,28 +3,80 @@ import type { Server } from 'http' import { STATUS_CODES } from 'http' import type { ServerOptions as HttpsServerOptions } from 'https' import { createServer as createHttpsServer } from 'https' -import type { ServerOptions } from 'ws' -import { WebSocketServer as WebSocket } from 'ws' -import type { WebSocket as WebSocketTypes } from 'types/ws' -import type { ErrorPayload, HMRPayload } from 'types/hmrPayload' +import type { ServerOptions, WebSocket as WebSocketRaw } from 'ws' +import { WebSocketServer as WebSocketServerRaw } from 'ws' +import type { CustomPayload, ErrorPayload, HMRPayload } from 'types/hmrPayload' import type { ResolvedConfig } from '..' import { isObject } from '../utils' import type { Socket } from 'net' export const HMR_HEADER = 'vite-hmr' +export type WebSocketCustomListener = ( + data: T, + client: WebSocketClient +) => void + export interface WebSocketServer { - on: WebSocketTypes.Server['on'] - off: WebSocketTypes.Server['off'] + /** + * Get all connected clients. + */ + clients: Set + /** + * Boardcast events to all clients + */ send(payload: HMRPayload): void + /** + * Send custom event + */ + send(event: string, payload?: CustomPayload['data']): void + /** + * Disconnect all clients and terminate the server. + */ close(): Promise + /** + * Handle custom event emitted by `import.meta.hot.send` + */ + on: WebSocketServerRaw['on'] & { + (event: string, listener: WebSocketCustomListener): void + } + /** + * Unregister event listener. + */ + off: WebSocketServerRaw['off'] & { + (event: string, listener: WebSocketCustomListener): void + } +} + +export interface WebSocketClient { + /** + * Send event to the client + */ + send(payload: HMRPayload): void + /** + * Send custom event + */ + send(event: string, payload?: CustomPayload['data']): void + /** + * The raw WebSocket instance + * @advanced + */ + socket: WebSocketRaw } +const wsServerEvents = [ + 'connection', + 'error', + 'headers', + 'listening', + 'message' +] + export function createWebSocketServer( server: Server | null, config: ResolvedConfig, httpsOptions?: HttpsServerOptions ): WebSocketServer { - let wss: WebSocket + let wss: WebSocketServerRaw let httpsServer: Server | undefined = undefined const hmr = isObject(config.server.hmr) && config.server.hmr @@ -33,9 +85,11 @@ export function createWebSocketServer( // TODO: the main server port may not have been chosen yet as it may use the next available const portsAreCompatible = !hmrPort || hmrPort === config.server.port const wsServer = hmrServer || (portsAreCompatible && server) + const customListeners = new Map>>() + const clientsMap = new WeakMap() if (wsServer) { - wss = new WebSocket({ noServer: true }) + wss = new WebSocketServerRaw({ noServer: true }) wsServer.on('upgrade', (req, socket, head) => { if (req.headers['sec-websocket-protocol'] === HMR_HEADER) { wss.handleUpgrade(req, socket as Socket, head, (ws) => { @@ -76,10 +130,22 @@ export function createWebSocketServer( } // vite dev server in middleware mode - wss = new WebSocket(websocketServerOptions) + wss = new WebSocketServerRaw(websocketServerOptions) } wss.on('connection', (socket) => { + socket.on('message', (raw) => { + if (!customListeners.size) return + let parsed: any + try { + parsed = JSON.parse(String(raw)) + } catch {} + if (!parsed || parsed.type !== 'custom' || !parsed.event) return + const listeners = customListeners.get(parsed.event) + if (!listeners?.size) return + const client = getSocketClent(socket) + listeners.forEach((listener) => listener(parsed.data, client)) + }) socket.send(JSON.stringify({ type: 'connected' })) if (bufferedError) { socket.send(JSON.stringify(bufferedError)) @@ -96,6 +162,30 @@ export function createWebSocketServer( } }) + // Provide a wrapper to the ws client so we can send messages in JSON format + // To be consistent with server.ws.send + function getSocketClent(socket: WebSocketRaw) { + if (!clientsMap.has(socket)) { + clientsMap.set(socket, { + send: (...args) => { + let payload: HMRPayload + if (typeof args[0] === 'string') { + payload = { + type: 'custom', + event: args[0], + data: args[1] + } + } else { + payload = args[0] + } + socket.send(JSON.stringify(payload)) + }, + socket + }) + } + return clientsMap.get(socket)! + } + // On page reloads, if a file fails to compile and returns 500, the server // sends the error payload before the client connection is established. // If we have no open clients, buffer the error and send it to the next @@ -103,9 +193,39 @@ export function createWebSocketServer( let bufferedError: ErrorPayload | null = null return { - on: wss.on.bind(wss), - off: wss.off.bind(wss), - send(payload: HMRPayload) { + on: ((event: string, fn: () => void) => { + if (wsServerEvents.includes(event)) wss.on(event, fn) + else { + if (!customListeners.has(event)) { + customListeners.set(event, new Set()) + } + customListeners.get(event)!.add(fn) + } + }) as WebSocketServer['on'], + off: ((event: string, fn: () => void) => { + if (wsServerEvents.includes(event)) { + wss.off(event, fn) + } else { + customListeners.get(event)?.delete(fn) + } + }) as WebSocketServer['off'], + + get clients() { + return new Set(Array.from(wss.clients).map(getSocketClent)) + }, + + send(...args: any[]) { + let payload: HMRPayload + if (typeof args[0] === 'string') { + payload = { + type: 'custom', + event: args[0], + data: args[1] + } + } else { + payload = args[0] + } + if (payload.type === 'error' && !wss.clients.size) { bufferedError = payload return diff --git a/packages/vite/types/importMeta.d.ts b/packages/vite/types/importMeta.d.ts index 56b82125381e20..1fd39b993d5142 100644 --- a/packages/vite/types/importMeta.d.ts +++ b/packages/vite/types/importMeta.d.ts @@ -59,6 +59,8 @@ interface ImportMeta { cb: (data: any) => void ): void } + + send(event: string, data?: any): void } readonly env: ImportMetaEnv From 57f14cbc09db5fa8ebbfe0fae2d7f2ae9f0b4e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Sat, 26 Mar 2022 23:49:55 +0900 Subject: [PATCH 23/92] feat(css): css.devSourcemap option (#7471) --- .../playground/css-sourcemap/vite.config.js | 1 + .../playground/vue-sourcemap/vite.config.js | 1 + packages/plugin-vue/src/index.ts | 3 + packages/plugin-vue/src/style.ts | 18 ++-- packages/vite/src/node/plugins/css.ts | 88 ++++++++++++++----- 5 files changed, 82 insertions(+), 29 deletions(-) diff --git a/packages/playground/css-sourcemap/vite.config.js b/packages/playground/css-sourcemap/vite.config.js index f743687ddd9b0e..4fbeaa9a13aeb0 100644 --- a/packages/playground/css-sourcemap/vite.config.js +++ b/packages/playground/css-sourcemap/vite.config.js @@ -10,6 +10,7 @@ module.exports = { } }, css: { + devSourcemap: true, preprocessorOptions: { less: { additionalData: '@color: red;' diff --git a/packages/playground/vue-sourcemap/vite.config.js b/packages/playground/vue-sourcemap/vite.config.js index 045410259fe590..2a48cad3cb00d4 100644 --- a/packages/playground/vue-sourcemap/vite.config.js +++ b/packages/playground/vue-sourcemap/vite.config.js @@ -5,6 +5,7 @@ const vuePlugin = require('@vitejs/plugin-vue') */ module.exports = { css: { + devSourcemap: true, preprocessorOptions: { less: { additionalData: '@color: red;' diff --git a/packages/plugin-vue/src/index.ts b/packages/plugin-vue/src/index.ts index 28280ed79188b8..7bdf63b700fa15 100644 --- a/packages/plugin-vue/src/index.ts +++ b/packages/plugin-vue/src/index.ts @@ -63,6 +63,7 @@ export interface ResolvedOptions extends Options { compiler: typeof _compiler root: string sourceMap: boolean + cssDevSourcemap: boolean devServer?: ViteDevServer devToolsEnabled?: boolean } @@ -99,6 +100,7 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { reactivityTransform, root: process.cwd(), sourceMap: true, + cssDevSourcemap: false, devToolsEnabled: process.env.NODE_ENV !== 'production' } @@ -137,6 +139,7 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { ...options, root: config.root, sourceMap: config.command === 'build' ? !!config.build.sourcemap : true, + cssDevSourcemap: config.css?.devSourcemap ?? false, isProduction: config.isProduction, devToolsEnabled: !!config.define!.__VUE_PROD_DEVTOOLS__ || !config.isProduction diff --git a/packages/plugin-vue/src/style.ts b/packages/plugin-vue/src/style.ts index c124f3ec744e4f..cab75791bd54ec 100644 --- a/packages/plugin-vue/src/style.ts +++ b/packages/plugin-vue/src/style.ts @@ -23,13 +23,17 @@ export async function transformStyle( isProd: options.isProduction, source: code, scoped: block.scoped, - postcssOptions: { - map: { - from: filename, - inline: false, - annotation: false - } - } + ...(options.cssDevSourcemap + ? { + postcssOptions: { + map: { + from: filename, + inline: false, + annotation: false + } + } + } + : {}) }) if (result.errors.length) { diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index af9f689cfecab8..87de63ef3ec306 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -62,6 +62,12 @@ export interface CSSOptions { | (Postcss.ProcessOptions & { plugins?: Postcss.Plugin[] }) + /** + * Enables css sourcemaps during dev + * @default false + * @experimental + */ + devSourcemap?: boolean } export interface CSSModulesOptions { @@ -309,9 +315,12 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { return `export default ${JSON.stringify(css)}` } - const sourcemap = this.getCombinedSourcemap() - await injectSourcesContent(sourcemap, cleanUrl(id), config.logger) - const cssContent = getCodeWithSourcemap('css', css, sourcemap) + let cssContent = css + if (config.css?.devSourcemap) { + const sourcemap = this.getCombinedSourcemap() + await injectSourcesContent(sourcemap, cleanUrl(id), config.logger) + cssContent = getCodeWithSourcemap('css', css, sourcemap) + } return [ `import { updateStyle as __vite__updateStyle, removeStyle as __vite__removeStyle } from ${JSON.stringify( @@ -612,7 +621,11 @@ async function compileCSS( modules?: Record deps?: Set }> { - const { modules: modulesOptions, preprocessorOptions } = config.css || {} + const { + modules: modulesOptions, + preprocessorOptions, + devSourcemap + } = config.css || {} const isModule = modulesOptions !== false && cssModuleRE.test(id) // although at serve time it can work without processing, we do need to // crawl them in order to register watch dependencies. @@ -661,6 +674,7 @@ async function compileCSS( } // important: set this for relative import resolving opts.filename = cleanUrl(id) + opts.enableSourcemap = devSourcemap ?? false const preprocessResult = await preProcessor( code, @@ -815,6 +829,16 @@ async function compileCSS( } } + if (!devSourcemap) { + return { + ast: postcssResult, + code: postcssResult.css, + map: { mappings: '' }, + modules, + deps + } + } + const rawPostcssMap = postcssResult.map.toJSON() const postcssMap = formatPostcssSourceMap( @@ -1086,6 +1110,7 @@ type StylePreprocessorOptions = { additionalData?: PreprocessorAdditionalData filename: string alias: Alias[] + enableSourcemap: boolean } type SassStylePreprocessorOptions = StylePreprocessorOptions & Sass.Options @@ -1175,7 +1200,8 @@ const scss: SassStylePreprocessor = async ( const { content: data, map: additionalMap } = await getSource( source, options.filename, - options.additionalData + options.additionalData, + options.enableSourcemap ) const finalOptions: Sass.Options = { ...options, @@ -1183,9 +1209,13 @@ const scss: SassStylePreprocessor = async ( file: options.filename, outFile: options.filename, importer, - sourceMap: true, - omitSourceMapUrl: true, - sourceMapRoot: path.dirname(options.filename) + ...(options.enableSourcemap + ? { + sourceMap: true, + omitSourceMapUrl: true, + sourceMapRoot: path.dirname(options.filename) + } + : {}) } try { @@ -1299,7 +1329,8 @@ const less: StylePreprocessor = async (source, root, options, resolvers) => { const { content, map: additionalMap } = await getSource( source, options.filename, - options.additionalData + options.additionalData, + options.enableSourcemap ) let result: Less.RenderOutput | undefined @@ -1307,10 +1338,14 @@ const less: StylePreprocessor = async (source, root, options, resolvers) => { result = await nodeLess.render(content, { ...options, plugins: [viteResolverPlugin, ...(options.plugins || [])], - sourceMap: { - outputSourceFiles: true, - sourceMapFileInline: false - } + ...(options.enableSourcemap + ? { + sourceMap: { + outputSourceFiles: true, + sourceMapFileInline: false + } + } + : {}) }) } catch (e) { const error = e as Less.RenderError @@ -1418,6 +1453,7 @@ const styl: StylePreprocessor = async (source, root, options) => { source, options.filename, options.additionalData, + options.enableSourcemap, '\n' ) // Get preprocessor options.imports dependencies as stylus @@ -1427,11 +1463,13 @@ const styl: StylePreprocessor = async (source, root, options) => { ) try { const ref = nodeStylus(content, options) - ref.set('sourcemap', { - comment: false, - inline: false, - basePath: root - }) + if (options.enableSourcemap) { + ref.set('sourcemap', { + comment: false, + inline: false, + basePath: root + }) + } const result = ref.render() @@ -1439,7 +1477,7 @@ const styl: StylePreprocessor = async (source, root, options) => { const deps = [...ref.deps(), ...importsDeps] // @ts-expect-error sourcemap exists - const map: ExistingRawSourceMap = ref.sourcemap + const map: ExistingRawSourceMap | undefined = ref.sourcemap return { code: result, @@ -1454,9 +1492,10 @@ const styl: StylePreprocessor = async (source, root, options) => { } function formatStylusSourceMap( - mapBefore: ExistingRawSourceMap, + mapBefore: ExistingRawSourceMap | undefined, root: string -): ExistingRawSourceMap { +): ExistingRawSourceMap | undefined { + if (!mapBefore) return undefined const map = { ...mapBefore } const resolveFromRoot = (p: string) => normalizePath(path.resolve(root, p)) @@ -1472,7 +1511,8 @@ function formatStylusSourceMap( async function getSource( source: string, filename: string, - additionalData?: PreprocessorAdditionalData, + additionalData: PreprocessorAdditionalData | undefined, + enableSourcemap: boolean, sep: string = '' ): Promise<{ content: string; map?: ExistingRawSourceMap }> { if (!additionalData) return { content: source } @@ -1485,6 +1525,10 @@ async function getSource( return newContent } + if (!enableSourcemap) { + return { content: additionalData + sep + source } + } + const ms = new MagicString(source) ms.appendLeft(0, sep) ms.appendLeft(0, additionalData) From dd33d9c78bb6f694b5869177fbf290b9d727090c Mon Sep 17 00:00:00 2001 From: patak-dev Date: Sat, 26 Mar 2022 15:59:44 +0100 Subject: [PATCH 24/92] release: v2.9.0-beta.9 --- packages/vite/CHANGELOG.md | 9 +++++++++ packages/vite/package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/vite/CHANGELOG.md b/packages/vite/CHANGELOG.md index f337d529d5c4c6..b79c75db3260f0 100644 --- a/packages/vite/CHANGELOG.md +++ b/packages/vite/CHANGELOG.md @@ -1,3 +1,12 @@ +## 2.9.0-beta.9 (2022-03-26) + +* feat: support importing css with ?raw (#5796) ([fedb106](https://github.com/vitejs/vite/commit/fedb106)), closes [#5796](https://github.com/vitejs/vite/issues/5796) +* feat(css): css.devSourcemap option (#7471) ([57f14cb](https://github.com/vitejs/vite/commit/57f14cb)), closes [#7471](https://github.com/vitejs/vite/issues/7471) +* feat(dev): expose APIs for client-server communication (#7437) ([e29ea8e](https://github.com/vitejs/vite/commit/e29ea8e)), closes [#7437](https://github.com/vitejs/vite/issues/7437) +* fix: errors in worker handling (#7236) ([77dc1a1](https://github.com/vitejs/vite/commit/77dc1a1)), closes [#7236](https://github.com/vitejs/vite/issues/7236) + + + ## 2.9.0-beta.8 (2022-03-24) * fix: consider undefined port when checking port (#7318) ([c7fc7c3](https://github.com/vitejs/vite/commit/c7fc7c3)), closes [#7318](https://github.com/vitejs/vite/issues/7318) diff --git a/packages/vite/package.json b/packages/vite/package.json index 37faf42ccf875b..ddb89d3970fc26 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -1,6 +1,6 @@ { "name": "vite", - "version": "2.9.0-beta.8", + "version": "2.9.0-beta.9", "license": "MIT", "author": "Evan You", "description": "Native-ESM powered web dev build tool", From 64ddff03f7ff693e8a9ed1217d401b628a940852 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sun, 27 Mar 2022 00:23:30 +0800 Subject: [PATCH 25/92] refactor(types): share hot context type (#7475) --- packages/vite/src/client/client.ts | 16 +++++------ packages/vite/types/hot.d.ts | 43 +++++++++++++++++++++++++++++ packages/vite/types/importMeta.d.ts | 43 +---------------------------- 3 files changed, 52 insertions(+), 50 deletions(-) create mode 100644 packages/vite/types/hot.d.ts diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index 40f0bb0418f365..1fe461f5b04caf 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -6,6 +6,7 @@ import type { Update, UpdatePayload } from 'types/hmrPayload' +import type { ViteHotContext } from 'types/hot' import type { CustomEventName } from 'types/customEvent' import { ErrorOverlay, overlayId } from './overlay' // eslint-disable-next-line node/no-missing-import @@ -391,9 +392,7 @@ const ctxToListenersMap = new Map< Map void)[]> >() -// Just infer the return type for now -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export const createHotContext = (ownerPath: string) => { +export function createHotContext(ownerPath: string): ViteHotContext { if (!dataMap.has(ownerPath)) { dataMap.set(ownerPath, {}) } @@ -434,12 +433,12 @@ export const createHotContext = (ownerPath: string) => { hotModulesMap.set(ownerPath, mod) } - const hot = { + const hot: ViteHotContext = { get data() { return dataMap.get(ownerPath) }, - accept(deps: any, callback?: any) { + accept(deps?: any, callback?: any) { if (typeof deps === 'function' || !deps) { // self-accept: hot.accept(() => {}) acceptDeps([ownerPath], ([mod]) => deps && deps(mod)) @@ -460,10 +459,11 @@ export const createHotContext = (ownerPath: string) => { ) }, - dispose(cb: (data: any) => void) { + dispose(cb) { disposeMap.set(ownerPath, cb) }, + // @ts-expect-error untyped prune(cb: (data: any) => void) { pruneMap.set(ownerPath, cb) }, @@ -479,7 +479,7 @@ export const createHotContext = (ownerPath: string) => { }, // custom events - on: (event: string, cb: (data: any) => void) => { + on(event, cb) { const addToMap = (map: Map) => { const existing = map.get(event) || [] existing.push(cb) @@ -489,7 +489,7 @@ export const createHotContext = (ownerPath: string) => { addToMap(newListeners) }, - send: (event: string, data?: any) => { + send(event, data) { messageBuffer.push(JSON.stringify({ type: 'custom', event, data })) sendMessageBuffer() } diff --git a/packages/vite/types/hot.d.ts b/packages/vite/types/hot.d.ts new file mode 100644 index 00000000000000..ee7c660056d086 --- /dev/null +++ b/packages/vite/types/hot.d.ts @@ -0,0 +1,43 @@ +import type { + ErrorPayload, + FullReloadPayload, + PrunePayload, + UpdatePayload +} from './hmrPayload' + +export interface ViteHotContext { + readonly data: any + + accept(): void + accept(cb: (mod: any) => void): void + accept(dep: string, cb: (mod: any) => void): void + accept(deps: readonly string[], cb: (mods: any[]) => void): void + + /** + * @deprecated + */ + acceptDeps(): never + + dispose(cb: (data: any) => void): void + decline(): void + invalidate(): void + + on: { + (event: 'vite:beforeUpdate', cb: (payload: UpdatePayload) => void): void + (event: 'vite:beforePrune', cb: (payload: PrunePayload) => void): void + ( + event: 'vite:beforeFullReload', + cb: (payload: FullReloadPayload) => void + ): void + (event: 'vite:error', cb: (payload: ErrorPayload) => void): void + (event: string, cb: (data: any) => void): void + } + + send(event: string, data?: any): void +} + +// See https://stackoverflow.com/a/63549561. +export type CustomEventName = (T extends `vite:${T}` + ? never + : T) & + (`vite:${T}` extends T ? never : T) diff --git a/packages/vite/types/importMeta.d.ts b/packages/vite/types/importMeta.d.ts index 1fd39b993d5142..9b57fd120a7ba9 100644 --- a/packages/vite/types/importMeta.d.ts +++ b/packages/vite/types/importMeta.d.ts @@ -20,48 +20,7 @@ interface GlobOptions { interface ImportMeta { url: string - readonly hot?: { - readonly data: any - - accept(): void - accept(cb: (mod: any) => void): void - accept(dep: string, cb: (mod: any) => void): void - accept(deps: readonly string[], cb: (mods: any[]) => void): void - - /** - * @deprecated - */ - acceptDeps(): never - - dispose(cb: (data: any) => void): void - decline(): void - invalidate(): void - - on: { - ( - event: 'vite:beforeUpdate', - cb: (payload: import('./hmrPayload').UpdatePayload) => void - ): void - ( - event: 'vite:beforePrune', - cb: (payload: import('./hmrPayload').PrunePayload) => void - ): void - ( - event: 'vite:beforeFullReload', - cb: (payload: import('./hmrPayload').FullReloadPayload) => void - ): void - ( - event: 'vite:error', - cb: (payload: import('./hmrPayload').ErrorPayload) => void - ): void - ( - event: import('./customEvent').CustomEventName, - cb: (data: any) => void - ): void - } - - send(event: string, data?: any): void - } + readonly hot?: import('./hot').ViteHotContext readonly env: ImportMetaEnv From 81d7dd256aa08a8de257fd5bc397980a99d279a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Sun, 27 Mar 2022 01:29:46 +0900 Subject: [PATCH 26/92] docs: add css.devSourcemap option (#7478) --- docs/config/index.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/config/index.md b/docs/config/index.md index 905ffae7a89cea..b0d2f59d4a62af 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -327,6 +327,14 @@ export default defineConfig(({ command, mode }) => { }) ``` +### css.devSourcemap + +- **Experimental** +- **Type:** `boolean` +- **Default:** `false` + + Whether to enable sourcemaps during dev. + ### json.namedExports - **Type:** `boolean` From 50a876537cc7b934ec5c1d11171b5ce02e3891a8 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sun, 27 Mar 2022 01:08:36 +0800 Subject: [PATCH 27/92] feat(type): support typing for custom events (#7476) --- docs/guide/api-plugin.md | 16 ++++++++++ packages/playground/hmr/__tests__/hmr.spec.ts | 16 +++++----- packages/playground/hmr/event.d.ts | 9 ++++++ packages/playground/hmr/{hmr.js => hmr.ts} | 8 ++--- packages/playground/hmr/index.html | 2 +- packages/playground/hmr/tsconfig.json | 15 ++++++++++ .../hmr/{vite.config.js => vite.config.ts} | 15 +++++----- packages/vite/src/client/client.ts | 27 ++++------------- packages/vite/src/node/index.ts | 1 + packages/vite/src/node/server/ws.ts | 11 +++++-- packages/vite/types/customEvent.d.ts | 21 +++++++++---- packages/vite/types/hot.d.ts | 30 ++++--------------- 12 files changed, 96 insertions(+), 75 deletions(-) create mode 100644 packages/playground/hmr/event.d.ts rename packages/playground/hmr/{hmr.js => hmr.ts} (87%) create mode 100644 packages/playground/hmr/tsconfig.json rename packages/playground/hmr/{vite.config.js => vite.config.ts} (56%) diff --git a/docs/guide/api-plugin.md b/docs/guide/api-plugin.md index 13767c45dd3103..228755dd6f85c2 100644 --- a/docs/guide/api-plugin.md +++ b/docs/guide/api-plugin.md @@ -560,3 +560,19 @@ export default defineConfig({ ] }) ``` + +### TypeScript for Custom Events + +It is possible to type custom events by extending the `CustomEventMap` interface: + +```ts +// events.d.ts +import 'vite/types/customEvent' + +declare module 'vite/types/customEvent' { + interface CustomEventMap { + 'custom:foo': { msg: string } + // 'event-key': payload + } +} +``` diff --git a/packages/playground/hmr/__tests__/hmr.spec.ts b/packages/playground/hmr/__tests__/hmr.spec.ts index 4d0491af91a69e..6ddc2345ae4fb4 100644 --- a/packages/playground/hmr/__tests__/hmr.spec.ts +++ b/packages/playground/hmr/__tests__/hmr.spec.ts @@ -16,7 +16,7 @@ if (!isBuild) { test('self accept', async () => { const el = await page.$('.app') - editFile('hmr.js', (code) => code.replace('const foo = 1', 'const foo = 2')) + editFile('hmr.ts', (code) => code.replace('const foo = 1', 'const foo = 2')) await untilUpdated(() => el.textContent(), '2') expect(browserLogs).toMatchObject([ @@ -24,11 +24,11 @@ if (!isBuild) { 'foo was: 1', '(self-accepting 1) foo is now: 2', '(self-accepting 2) foo is now: 2', - '[vite] hot updated: /hmr.js' + '[vite] hot updated: /hmr.ts' ]) browserLogs.length = 0 - editFile('hmr.js', (code) => code.replace('const foo = 2', 'const foo = 3')) + editFile('hmr.ts', (code) => code.replace('const foo = 2', 'const foo = 3')) await untilUpdated(() => el.textContent(), '3') expect(browserLogs).toMatchObject([ @@ -36,7 +36,7 @@ if (!isBuild) { 'foo was: 2', '(self-accepting 1) foo is now: 3', '(self-accepting 2) foo is now: 3', - '[vite] hot updated: /hmr.js' + '[vite] hot updated: /hmr.ts' ]) browserLogs.length = 0 }) @@ -57,7 +57,7 @@ if (!isBuild) { '(single dep) nested foo is now: 1', '(multi deps) foo is now: 2', '(multi deps) nested foo is now: 1', - '[vite] hot updated: /hmrDep.js via /hmr.js' + '[vite] hot updated: /hmrDep.js via /hmr.ts' ]) browserLogs.length = 0 @@ -74,7 +74,7 @@ if (!isBuild) { '(single dep) nested foo is now: 1', '(multi deps) foo is now: 3', '(multi deps) nested foo is now: 1', - '[vite] hot updated: /hmrDep.js via /hmr.js' + '[vite] hot updated: /hmrDep.js via /hmr.ts' ]) browserLogs.length = 0 }) @@ -95,7 +95,7 @@ if (!isBuild) { '(single dep) nested foo is now: 2', '(multi deps) foo is now: 3', '(multi deps) nested foo is now: 2', - '[vite] hot updated: /hmrDep.js via /hmr.js' + '[vite] hot updated: /hmrDep.js via /hmr.ts' ]) browserLogs.length = 0 @@ -112,7 +112,7 @@ if (!isBuild) { '(single dep) nested foo is now: 3', '(multi deps) foo is now: 3', '(multi deps) nested foo is now: 3', - '[vite] hot updated: /hmrDep.js via /hmr.js' + '[vite] hot updated: /hmrDep.js via /hmr.ts' ]) browserLogs.length = 0 }) diff --git a/packages/playground/hmr/event.d.ts b/packages/playground/hmr/event.d.ts new file mode 100644 index 00000000000000..151a9cc3b861cd --- /dev/null +++ b/packages/playground/hmr/event.d.ts @@ -0,0 +1,9 @@ +import 'vite/types/customEvent' + +declare module 'vite/types/customEvent' { + interface CustomEventMap { + 'custom:foo': { msg: string } + 'custom:remote-add': { a: number; b: number } + 'custom:remote-add-result': { result: string } + } +} diff --git a/packages/playground/hmr/hmr.js b/packages/playground/hmr/hmr.ts similarity index 87% rename from packages/playground/hmr/hmr.js rename to packages/playground/hmr/hmr.ts index e80b517e6449dc..113b87bc5865d4 100644 --- a/packages/playground/hmr/hmr.js +++ b/packages/playground/hmr/hmr.ts @@ -41,7 +41,7 @@ if (import.meta.hot) { update.type === 'css-update' && update.path.match('global.css') ) if (cssUpdate) { - const el = document.querySelector('#global-css') + const el = document.querySelector('#global-css') as HTMLLinkElement text('.css-prev', el.href) // We don't have a vite:afterUpdate event, but updates are currently sync setTimeout(() => { @@ -54,13 +54,13 @@ if (import.meta.hot) { console.log(`>>> vite:error -- ${event.type}`) }) - import.meta.hot.on('foo', ({ msg }) => { + import.meta.hot.on('custom:foo', ({ msg }) => { text('.custom', msg) }) // send custom event to server to calculate 1 + 2 - import.meta.hot.send('remote-add', { a: 1, b: 2 }) - import.meta.hot.on('remote-add-result', ({ result }) => { + import.meta.hot.send('custom:remote-add', { a: 1, b: 2 }) + import.meta.hot.on('custom:remote-add-result', ({ result }) => { text('.custom-communication', result) }) } diff --git a/packages/playground/hmr/index.html b/packages/playground/hmr/index.html index fc398c60c4cadf..0add7c26011a01 100644 --- a/packages/playground/hmr/index.html +++ b/packages/playground/hmr/index.html @@ -1,5 +1,5 @@ - +
diff --git a/packages/playground/hmr/tsconfig.json b/packages/playground/hmr/tsconfig.json new file mode 100644 index 00000000000000..41b16fdc65ec8c --- /dev/null +++ b/packages/playground/hmr/tsconfig.json @@ -0,0 +1,15 @@ +{ + "include": ["."], + "exclude": ["**/dist/**"], + "compilerOptions": { + "target": "es2019", + "module": "esnext", + "outDir": "dist", + "allowJs": true, + "esModuleInterop": true, + "moduleResolution": "node", + "baseUrl": ".", + "jsx": "preserve", + "types": ["vite/client", "jest", "node"] + } +} diff --git a/packages/playground/hmr/vite.config.js b/packages/playground/hmr/vite.config.ts similarity index 56% rename from packages/playground/hmr/vite.config.js rename to packages/playground/hmr/vite.config.ts index 57252c91be410b..ef5d3cf36a2fcb 100644 --- a/packages/playground/hmr/vite.config.js +++ b/packages/playground/hmr/vite.config.ts @@ -1,7 +1,6 @@ -/** - * @type {import('vite').UserConfig} - */ -module.exports = { +import { defineConfig } from 'vite' + +export default defineConfig({ plugins: [ { name: 'mock-custom', @@ -9,14 +8,14 @@ module.exports = { if (file.endsWith('customFile.js')) { const content = await read() const msg = content.match(/export const msg = '(\w+)'/)[1] - server.ws.send('foo', { msg }) + server.ws.send('custom:foo', { msg }) } }, configureServer(server) { - server.ws.on('remote-add', ({ a, b }, client) => { - client.send('remote-add-result', { result: a + b }) + server.ws.on('custom:remote-add', ({ a, b }, client) => { + client.send('custom:remote-add-result', { result: a + b }) }) } } ] -} +}) diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index 1fe461f5b04caf..c180714f5a69bf 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -1,13 +1,6 @@ -import type { - ErrorPayload, - FullReloadPayload, - HMRPayload, - PrunePayload, - Update, - UpdatePayload -} from 'types/hmrPayload' +import type { ErrorPayload, HMRPayload, Update } from 'types/hmrPayload' import type { ViteHotContext } from 'types/hot' -import type { CustomEventName } from 'types/customEvent' +import type { InferCustomEventPayload } from 'types/customEvent' import { ErrorOverlay, overlayId } from './overlay' // eslint-disable-next-line node/no-missing-import import '@vite/env' @@ -104,7 +97,7 @@ async function handleMessage(payload: HMRPayload) { }) break case 'custom': { - notifyListeners(payload.event as CustomEventName, payload.data) + notifyListeners(payload.event, payload.data) break } case 'full-reload': @@ -157,19 +150,9 @@ async function handleMessage(payload: HMRPayload) { } } -function notifyListeners( - event: 'vite:beforeUpdate', - payload: UpdatePayload -): void -function notifyListeners(event: 'vite:beforePrune', payload: PrunePayload): void -function notifyListeners( - event: 'vite:beforeFullReload', - payload: FullReloadPayload -): void -function notifyListeners(event: 'vite:error', payload: ErrorPayload): void function notifyListeners( - event: CustomEventName, - data: any + event: T, + data: InferCustomEventPayload ): void function notifyListeners(event: string, data: any): void { const cbs = customListenersMap.get(event) diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index 2b59da8737029b..2e849d846527ca 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -108,6 +108,7 @@ export type { export type { Terser } from 'types/terser' export type { RollupCommonJSOptions } from 'types/commonjs' export type { RollupDynamicImportVarsOptions } from 'types/dynamicImportVars' +export type { CustomEventMap, InferCustomEventPayload } from 'types/customEvent' export type { Matcher, AnymatchPattern, AnymatchFn } from 'types/anymatch' export type { SplitVendorChunkCache } from './plugins/splitVendorChunk' diff --git a/packages/vite/src/node/server/ws.ts b/packages/vite/src/node/server/ws.ts index 3c6875e2475c64..a01d0bd3225571 100644 --- a/packages/vite/src/node/server/ws.ts +++ b/packages/vite/src/node/server/ws.ts @@ -6,9 +6,11 @@ import { createServer as createHttpsServer } from 'https' import type { ServerOptions, WebSocket as WebSocketRaw } from 'ws' import { WebSocketServer as WebSocketServerRaw } from 'ws' import type { CustomPayload, ErrorPayload, HMRPayload } from 'types/hmrPayload' +import type { InferCustomEventPayload } from 'types/customEvent' import type { ResolvedConfig } from '..' import { isObject } from '../utils' import type { Socket } from 'net' + export const HMR_HEADER = 'vite-hmr' export type WebSocketCustomListener = ( @@ -28,7 +30,7 @@ export interface WebSocketServer { /** * Send custom event */ - send(event: string, payload?: CustomPayload['data']): void + send(event: T, payload?: InferCustomEventPayload): void /** * Disconnect all clients and terminate the server. */ @@ -37,13 +39,16 @@ export interface WebSocketServer { * Handle custom event emitted by `import.meta.hot.send` */ on: WebSocketServerRaw['on'] & { - (event: string, listener: WebSocketCustomListener): void + ( + event: T, + listener: WebSocketCustomListener> + ): void } /** * Unregister event listener. */ off: WebSocketServerRaw['off'] & { - (event: string, listener: WebSocketCustomListener): void + (event: string, listener: Function): void } } diff --git a/packages/vite/types/customEvent.d.ts b/packages/vite/types/customEvent.d.ts index c38a4ac940ff6e..aacf1554b0de75 100644 --- a/packages/vite/types/customEvent.d.ts +++ b/packages/vite/types/customEvent.d.ts @@ -1,5 +1,16 @@ -// See https://stackoverflow.com/a/63549561. -export type CustomEventName = (T extends `vite:${T}` - ? never - : T) & - (`vite:${T}` extends T ? never : T) +import type { + ErrorPayload, + FullReloadPayload, + PrunePayload, + UpdatePayload +} from './hmrPayload' + +export interface CustomEventMap { + 'vite:beforeUpdate': UpdatePayload + 'vite:beforePrune': PrunePayload + 'vite:beforeFullReload': FullReloadPayload + 'vite:error': ErrorPayload +} + +export type InferCustomEventPayload = + T extends keyof CustomEventMap ? CustomEventMap[T] : unknown diff --git a/packages/vite/types/hot.d.ts b/packages/vite/types/hot.d.ts index ee7c660056d086..f06846ff59d530 100644 --- a/packages/vite/types/hot.d.ts +++ b/packages/vite/types/hot.d.ts @@ -1,9 +1,4 @@ -import type { - ErrorPayload, - FullReloadPayload, - PrunePayload, - UpdatePayload -} from './hmrPayload' +import type { InferCustomEventPayload } from './customEvent' export interface ViteHotContext { readonly data: any @@ -22,22 +17,9 @@ export interface ViteHotContext { decline(): void invalidate(): void - on: { - (event: 'vite:beforeUpdate', cb: (payload: UpdatePayload) => void): void - (event: 'vite:beforePrune', cb: (payload: PrunePayload) => void): void - ( - event: 'vite:beforeFullReload', - cb: (payload: FullReloadPayload) => void - ): void - (event: 'vite:error', cb: (payload: ErrorPayload) => void): void - (event: string, cb: (data: any) => void): void - } - - send(event: string, data?: any): void + on( + event: T, + cb: (payload: InferCustomEventPayload) => void + ): void + send(event: T, data?: InferCustomEventPayload): void } - -// See https://stackoverflow.com/a/63549561. -export type CustomEventName = (T extends `vite:${T}` - ? never - : T) & - (`vite:${T}` extends T ? never : T) From f10c7cc97a835af0d6e87e1f818ec4ba429d714d Mon Sep 17 00:00:00 2001 From: Rom Date: Sun, 27 Mar 2022 06:59:51 +0200 Subject: [PATCH 28/92] chore: also run CI on `fix/*` and `perf/*` branches (#7435) --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dfccbea368c449..cde7dfb03825ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,8 @@ on: - main - release/* - feat/* + - fix/* + - perf/* pull_request: workflow_dispatch: From 4c95e9953a5c2790cdf54dbb27bfdf3dbb352eed Mon Sep 17 00:00:00 2001 From: patak Date: Sun, 27 Mar 2022 07:00:45 +0200 Subject: [PATCH 29/92] fix: optimizeDeps.entries default ignore paths (#7469) --- docs/config/index.md | 4 ++-- packages/vite/src/node/optimizer/scan.ts | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/config/index.md b/docs/config/index.md index b0d2f59d4a62af..a8d8cb36079f0a 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -973,9 +973,9 @@ export default defineConfig({ - **Type:** `string | string[]` - By default, Vite will crawl your `index.html` to detect dependencies that need to be pre-bundled. If `build.rollupOptions.input` is specified, Vite will crawl those entry points instead. + By default, Vite will crawl all your `.html` files to detect dependencies that need to be pre-bundled (ignoring `node_modules`, `build.outDir`, `__tests__` and `coverage`). If `build.rollupOptions.input` is specified, Vite will crawl those entry points instead. - If neither of these fit your needs, you can specify custom entries using this option - the value should be a [fast-glob pattern](https://github.com/mrmlnc/fast-glob#basic-syntax) or array of patterns that are relative from Vite project root. This will overwrite default entries inference. + If neither of these fit your needs, you can specify custom entries using this option - the value should be a [fast-glob pattern](https://github.com/mrmlnc/fast-glob#basic-syntax) or array of patterns that are relative from Vite project root. This will overwrite default entries inference. Only `node_modules` and `build.outDir` folders will be ignored by default when `optimizeDeps.entries` is explicitily defined. If other folders needs to be ignored, you can use an ignore pattern as part of the entries list, marked with an initial `!`. ### optimizeDeps.exclude diff --git a/packages/vite/src/node/optimizer/scan.ts b/packages/vite/src/node/optimizer/scan.ts index 549f748d9a2b58..e940617386eb35 100644 --- a/packages/vite/src/node/optimizer/scan.ts +++ b/packages/vite/src/node/optimizer/scan.ts @@ -142,7 +142,10 @@ function globEntries(pattern: string | string[], config: ResolvedConfig) { ignore: [ '**/node_modules/**', `**/${config.build.outDir}/**`, - `**/__tests__/**` + // if there aren't explicit entries, also ignore other common folders + ...(config.optimizeDeps.entries + ? [] + : [`**/__tests__/**`, `**/coverage/**`]) ], absolute: true }) From 9ee2cf6ccee473eaa62fbf2a0cb84797bfdf2201 Mon Sep 17 00:00:00 2001 From: Artur Date: Sun, 27 Mar 2022 08:04:45 +0300 Subject: [PATCH 30/92] fix: Correctly process urls when they are rewritten to contain space (#7452) --- packages/playground/css/__tests__/css.spec.ts | 6 ++++++ packages/playground/css/folder with space/ok.png | Bin 0 -> 4986 bytes .../playground/css/folder with space/space.css | 5 +++++ packages/playground/css/imported.css | 1 + packages/playground/css/index.html | 4 ++++ packages/playground/css/vite.config.js | 3 ++- packages/vite/src/node/plugins/css.ts | 7 ++++++- 7 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 packages/playground/css/folder with space/ok.png create mode 100644 packages/playground/css/folder with space/space.css diff --git a/packages/playground/css/__tests__/css.spec.ts b/packages/playground/css/__tests__/css.spec.ts index 34858ab34bc09d..3fe4a09bcc94c8 100644 --- a/packages/playground/css/__tests__/css.spec.ts +++ b/packages/playground/css/__tests__/css.spec.ts @@ -54,6 +54,12 @@ test('css import from js', async () => { await untilUpdated(() => getColor(atImport), 'blue') }) +test('css import asset with space', async () => { + const importedWithSpace = await page.$('.import-with-space') + + expect(await getBg(importedWithSpace)).toMatch(/.*ok\..*png/) +}) + test('postcss config', async () => { const imported = await page.$('.postcss .nesting') expect(await getColor(imported)).toBe('pink') diff --git a/packages/playground/css/folder with space/ok.png b/packages/playground/css/folder with space/ok.png new file mode 100644 index 0000000000000000000000000000000000000000..a8d1e52510c41ceeaf3515ee2f594a82557d1685 GIT binary patch literal 4986 zcmZ`*2RK|?*B+e_JxHPrqC^`dI%D+CL?i|g27@8m=!PJK=%R!nQKA!~B)aHPqD6@= zdX1Xs?IXGWz4!UQ|3BwBXT4{y^{#jAwa>H935DsXk^yf40RRA*x|)*SrIo+D1c?bR z?>eFMtN;LT2BoM7Q&&_3!O)IYs3%AOKrJ*Okx2i(;Vu6y%xWZ*nhnJ9i&KruKfWK2 zyFN@q4VQ$YH7MOol~C15pOUIc*gumE7!~CoK~QR@T#8_ck(W0lA{Eo$$I(o}%GP4` z{gywSytz0#03S?ZdM^O!LEBja*Wk4NTIv;iY>g?j^l6RFdwA68IJmpt=64qLbl`Ae zsHN?BVO;}h`p(g-;i~t37w4wWIVro20MJlkF4Bclt)PjRoiK0$63<^MVudWxQnUR? ztoho_SWq+G{A;OB)6bG#&%PGWh~1-t5HsNTbN%!z(+6~jy+}=H(N#W;X`!rmN!&^u zWWfHTiGBy4nBBSQU3d38XJ&=bYo&>}Pz0!3=RB@&sNqoe;qmg0UV@C_NYIRL8yZCj zKDd}nzTn5+49ma4*#Qc-44~@hR@&cs?ESDLmZS5hLN1&ysU*$pC7Ymk*bzD{=nmbs zPhf(b$kKBy=djVV!e4Pck;lVEfh(vYePen*(4_xexEL1)_4uNw7g9S@HCXq4M@ zoTDVHrmue)WDm@>_lft$vMFF2VAM0ROe3a-n4DAHy|C)8k7IB1pvaB7)zjzHy&rVF zUJHu{^p7}>a~h$*J^K$o$65v5*?4{vN26YbORrjtLGtyWT}wPB=`$%X#e9bJ6iU2X z0=-*8A4PbXeAZR z0kwH*@;Bs$eAF&xp*LP@J@yO0MgSi=-}Ir^h8{443rHS60vh&ak;|Sp1K84W3H$MD z8Yz=-cyCK{%@EebLIt^;PVmLL0P#8Q<@&aNZbRBN>~_iRiPph zIKBR)Gr%gGP5+Q|qF;fDW;g1HGAZ$AK|rODyR5PuH_C#3fW#JXj^tTk_}3{oRuxPPfcU$Yax& zEFc{Aw^epacgXODDdy?g@FhoCWfb*0tUz7wCPzPxcpN(IU#zsHDxT4o;h2H>+Opz! z1E|7a5$fFLB;=f=-%YK>n!{L5?{dwB*@b8@+AF%v91FkyPDG57AzHgdpv9qavsKJ| zqQqnr@`IQ>WU$q6ZqhkJf?0sU;Kf9g^b6_8+R*iO-`2QoZ%^quIS3B8X?gz34$*e* zw)jrpPA2|?XF^R0lp2(jXp$6S3~^D=deGBm?t76o8m?R_RD&<YJhyi6i@G1>fr2=#kL(>V|RoR808IB-F{LdAHGCc25bC1PG#!Gb> zcF}aH($<811R5x(muX~cWUDXcbn)^8*lI1p==FZ)Ee>T4tq-w2cvcWv5Y40OmTjA` z<~-~$OeI>ATx67Ilrv8F5zjVph|oqNnWQJ0Go7~I7G;g{b-g|nv#Gc|I5Ke1-#2)2 z6CQjg@Qa$obrEtQshcfpcio8gZtP{RJy_!!#w2m)ki>oIHghm%Moc50re=r)iYQ80 zoJ9oR6u0*2Ps*RoW*y=luo}wKENoc8gM~X3(8HF-}S}_+#k^t*RIq$h~G?SHtAE; zeP@v4VYEe{`KAqlEa8DK!&{EWj{}Z@XSQcLXZP^#2igSs1gZqK2P-L7Ai@w>MD1+J z%ha!rG8a|$R45`yK0mQ|xg$1DK2O1!!&%C?u9d7!s3odZo#U0WmlNI1ZIx;j-ecSC zkYdp@wzRq=-Qx&vq>G|s3+D-6e^rtlmpzKkMUL=w^CcR_8j6jgEMzS7E_`g4 zw_E_MhwvHyETzr9YV@Oxa_?>Ko2@ zPUucX$X}5wgqV;YFs??qFpWr7NI!K$yT?k3N{~r0NODPTIc-{rj3R!J{RkJhLGPIO zK!EEJq*|)ls+QULtfi-me`>>jqLFb>vRM+d^sAbo>gQzP6h;Qltj;Xw{mXL=vnyjK zljeQXo1|{vTXs3gWk7;R@_n6s9df$8PVTVk_}w<@r}7cuA>EPt@z>M0XFO-AhYW|g z$0H{cL=uF2q&B3j_=H5S@ZDY9s(c)nJR~|KZIt_~&CpJkPRQ*+%5X|Uwe?4y4*iq- zE-XDFsa)6*Mxa7TOx{y@h~|gL~8WQr(cePqVN!8Q&~M)?Gto5wg-AEyAUYS<`Fi{82!57b+o4W zD@K!o3jMNQw#CYu$CJxm=U#IBir{&$ooR7{PtWc&?Q7a$-i(A!-U)-7#XRug1}U?> zbJ14ORlDNFiG{?a24jZWijUA-GdKC~9ys5J=?kuR0c%I9izd!wk1;z3Z`LSywJN-PPZYbsV2I|wJZ^I=<`y4-hJYM-n#?h~69@aKjf z5-M^Jhww!g#mL0NlQBkf#x+qyOpcCjcC!10d+$$lR?_mM@}Hsy@66>tyKj;EQz!a^ z`b2PVR_dyq>CpN1lgY7(<>o%OR0@dS&a;c>zJ9JugL347yeR;I6Pc_k}$v6Md_Wa|X@ph%E1=hks zFEv8^D=<9pNAN#MXH$e=^Q0EJ?l zl6q7QM>gt#Fz6BbIba1h8bmhu4s0RRWrBV8DSGcy8l!c?c0K(GI94X*w?{q~0 z$aqR!LVKhu0_17`#KA?%Q6%3t)$Cs{TdS63$~FxbPxL%>5sz!7Z?7Lt^d1Pcm-g@ySqBlums99$8e z{0=Vcf0F!99wnrU1sdh#igI)SUFAiXJG!~bvawwW{eAqY6X}WiN6EqEuW4OQ5PanU z3ke8<|1X*=%Ig22U3vb{e$VTVoXk}+DHzHV`NU8OWq;Y#%hDisC1if9{FmdOI{y$K zxFFGrj`o*4SI9rB@)!Bv&i_sPtqA`|QB3sTivM!_i+D8wDP1(`QVem`L5PqH_`k;f zikAUjwfA4`{0+a#&{bE3XOap7%002R{x{|!U4Z#K+rKN9h9fHTfT6Qa) z4{Y6ddS0CkX-^>( zxOjLJxD?HkmW|f9X(T?J-K>8B5U!8p_M(61HW1B-4GmSN3{~nM*yVCP_|+K4b!KLB z$dic+l{Nn~6Rv(AC z1FKTm+S}V%*C$6{`uey*iK-|21TA0l)Zn!b6Cd>K^JCW9zdDONx>MU-aB#R|I%m~eo6M=u^BIkm z)j>ayAomTE7{2gmxhQ+2kmf5zJ3HR0DsMBH-6mGnlP2pv2SGtW=ux>fyVIU6?ZVQc zqMx6fue;}F&=${^rluz5y(;0X9||qa2pgLh$)jUq^4{J&c?AV%wviuOw zl@44MJUqPcU-29<#^lxwU9BXcLz`{Qj`Z1|?oN)6kH?(#NRED*!n(hFC*idl{~>wO z-PTBIvWDZWRMtXRI8ffy)U>!qL&{JdvX-rHG@yA*_I1o}%g&6K7Of>-& zJPsz)BO@Xt`UuJhtx}x-~LVnovYU1icaewG;TPzZv)Jd+hNg@vGls}^HS%XRuS z^M|k4sqW=@pTZf}f$T7<^l6_Z8-4uI@o^YcwWgX5M6|&;Uxt_XHc+yL5Z^}uFE%vn zHD_V5NyylkAvO?1gV65Lotd3|o-0Q|;j#VmXF@($H!mlL&@?AELqn+i`TL@xmI`4k z2lzop7xdiL_pI+m;uEW9&v0kSmIUODjY&VJ%7yIjlXD9Q1XNVG53!@(q@}g2*ko!o zJJst~!}YJJtDC|^%7VTPf8&#A);uj*sJ~6zDruM_EL;v^XAgJbL)XSTu`zeBBq%?U z2N_mgK)ENYf0DW>E=AYXF@hEg_+pL8V?hgS>|&g^9L_!QaPdw#%(0FvX|hg51=O(+ zT}uN08)f#XS#M%#R#yi*tImpx3PaW|qQ(YlzSz5T;iHzjk)*PuF-%h(9o+mRG&D3N zC4$SYoje;`%9V)(&Ed=+%H~~oh>%2Q4M+3xX6M9L8{i|vF824%=R&;w2fP57lKV)S b37?V{t|cgkE9S{uec08Nb(D$~%%1%R{RRHc literal 0 HcmV?d00001 diff --git a/packages/playground/css/folder with space/space.css b/packages/playground/css/folder with space/space.css new file mode 100644 index 00000000000000..55a8532da32a94 --- /dev/null +++ b/packages/playground/css/folder with space/space.css @@ -0,0 +1,5 @@ +.import-with-space { + color: green; + background: url(spacefolder/ok.png); + background-position: center; +} diff --git a/packages/playground/css/imported.css b/packages/playground/css/imported.css index 65743d08b932a7..7d582995fab9fd 100644 --- a/packages/playground/css/imported.css +++ b/packages/playground/css/imported.css @@ -1,4 +1,5 @@ @import './imported-at-import.css'; +@import 'spacefolder/space.css'; .imported { color: green; diff --git a/packages/playground/css/index.html b/packages/playground/css/index.html index a09d8e6e7c46aa..060aed7fb1b27f 100644 --- a/packages/playground/css/index.html +++ b/packages/playground/css/index.html @@ -10,6 +10,10 @@

CSS

@import in import from js: This should be purple

+

+ @import from file with space: This should be green and have a background + image +

Imported css string:


   

diff --git a/packages/playground/css/vite.config.js b/packages/playground/css/vite.config.js
index 53d001d8387989..639a1302debb88 100644
--- a/packages/playground/css/vite.config.js
+++ b/packages/playground/css/vite.config.js
@@ -9,7 +9,8 @@ module.exports = {
   },
   resolve: {
     alias: {
-      '@': __dirname
+      '@': __dirname,
+      spacefolder: __dirname + '/folder with space'
     }
   },
   css: {
diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts
index 87de63ef3ec306..4933f8455931c1 100644
--- a/packages/vite/src/node/plugins/css.ts
+++ b/packages/vite/src/node/plugins/css.ts
@@ -1029,7 +1029,12 @@ async function doUrlReplace(
     return matched
   }
 
-  return `url(${wrap}${await replacer(rawUrl)}${wrap})`
+  const newUrl = await replacer(rawUrl)
+  if (wrap === '' && newUrl !== encodeURI(newUrl)) {
+    // The new url might need wrapping even if the original did not have it, e.g. if a space was added during replacement
+    wrap = "'"
+  }
+  return `url(${wrap}${newUrl}${wrap})`
 }
 
 async function doImportCSSReplace(

From 8ac4b12fefb42c748e92f0ea5f93374a0739748e Mon Sep 17 00:00:00 2001
From: yoho 
Date: Sun, 27 Mar 2022 15:46:49 +0800
Subject: [PATCH 31/92] fix: import.meta.url in worker (#7464)

---
 packages/playground/worker/__tests__/worker.spec.ts | 7 +++++--
 packages/playground/worker/sub-worker.js            | 2 +-
 packages/playground/worker/url-worker.js            | 2 +-
 packages/vite/src/node/plugins/define.ts            | 8 +++++++-
 4 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/packages/playground/worker/__tests__/worker.spec.ts b/packages/playground/worker/__tests__/worker.spec.ts
index fc381467f6a4d2..263c49cff52fb9 100644
--- a/packages/playground/worker/__tests__/worker.spec.ts
+++ b/packages/playground/worker/__tests__/worker.spec.ts
@@ -51,8 +51,11 @@ test.concurrent.each([[true], [false]])('shared worker', async (doTick) => {
   await waitSharedWorkerTick(page)
 })
 
-test('worker emitted', async () => {
-  await untilUpdated(() => page.textContent('.nested-worker'), 'pong')
+test('worker emitted and import.meta.url in nested worker', async () => {
+  await untilUpdated(
+    () => page.textContent('.nested-worker'),
+    'pong http://localhost:3000/iife/sub-worker.js?worker_file'
+  )
 })
 
 if (isBuild) {
diff --git a/packages/playground/worker/sub-worker.js b/packages/playground/worker/sub-worker.js
index eec65b86a0382a..eff49dfbb46ba6 100644
--- a/packages/playground/worker/sub-worker.js
+++ b/packages/playground/worker/sub-worker.js
@@ -1,5 +1,5 @@
 self.onmessage = (event) => {
   if (event.data === 'ping') {
-    self.postMessage('pong')
+    self.postMessage(`pong ${import.meta.url}`)
   }
 }
diff --git a/packages/playground/worker/url-worker.js b/packages/playground/worker/url-worker.js
index 79d05da0f06082..c25cbefdff89ec 100644
--- a/packages/playground/worker/url-worker.js
+++ b/packages/playground/worker/url-worker.js
@@ -1 +1 @@
-self.postMessage('A string' + import.meta.env.BASE_URL)
+self.postMessage('A string' + import.meta.env.BASE_URL + import.meta.url)
diff --git a/packages/vite/src/node/plugins/define.ts b/packages/vite/src/node/plugins/define.ts
index 19ca28b34433a0..024cf32d41f72a 100644
--- a/packages/vite/src/node/plugins/define.ts
+++ b/packages/vite/src/node/plugins/define.ts
@@ -10,6 +10,7 @@ const isNonJsRequest = (request: string): boolean => nonJsRe.test(request)
 
 export function definePlugin(config: ResolvedConfig): Plugin {
   const isBuild = config.command === 'build'
+  const isWorker = config.isWorker
 
   const processNodeEnv: Record = {
     'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || config.mode),
@@ -40,7 +41,12 @@ export function definePlugin(config: ResolvedConfig): Plugin {
     Object.assign(importMetaKeys, {
       'import.meta.env.': `({}).`,
       'import.meta.env': JSON.stringify(config.env),
-      'import.meta.hot': `false`
+      'import.meta.hot': `false`,
+      ...(isWorker
+        ? {
+            'import.meta.url': 'self.location.href'
+          }
+        : {})
     })
   }
 

From 7f8dc58daa4a9f07c71d069ecf1f2b055521f23f Mon Sep 17 00:00:00 2001
From: toshify <4579559+toshify@users.noreply.github.com>
Date: Sun, 27 Mar 2022 11:49:17 +0200
Subject: [PATCH 32/92] fix: handle relative path glob raw import, fix #7307
 (#7371)

---
 .../glob-import/__tests__/glob-import.spec.ts | 12 ++++++++++++
 packages/playground/glob-import/index.html    | 19 +++++++++++++++++++
 packages/vite/src/node/importGlob.ts          |  2 +-
 3 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/packages/playground/glob-import/__tests__/glob-import.spec.ts b/packages/playground/glob-import/__tests__/glob-import.spec.ts
index fff8d9fe202ebc..ebdf6c0ab29193 100644
--- a/packages/playground/glob-import/__tests__/glob-import.spec.ts
+++ b/packages/playground/glob-import/__tests__/glob-import.spec.ts
@@ -66,6 +66,12 @@ const rawResult = {
   }
 }
 
+const relativeRawResult = {
+  '../glob-import/dir/baz.json': {
+    msg: 'baz'
+  }
+}
+
 test('should work', async () => {
   expect(await page.textContent('.result')).toBe(
     JSON.stringify(allResult, null, 2)
@@ -81,6 +87,12 @@ test('import glob raw', async () => {
   )
 })
 
+test('import relative glob raw', async () => {
+  expect(await page.textContent('.relative-glob-raw')).toBe(
+    JSON.stringify(relativeRawResult, null, 2)
+  )
+})
+
 if (!isBuild) {
   test('hmr for adding/removing files', async () => {
     addFile('dir/a.js', '')
diff --git a/packages/playground/glob-import/index.html b/packages/playground/glob-import/index.html
index 52d41b817a169c..64f456aeb4d6a2 100644
--- a/packages/playground/glob-import/index.html
+++ b/packages/playground/glob-import/index.html
@@ -1,6 +1,7 @@
 

 

 

+

 
 
 
+
+
diff --git a/packages/vite/src/node/importGlob.ts b/packages/vite/src/node/importGlob.ts
index 8ed3ba66d09744..a759bee2b5fa59 100644
--- a/packages/vite/src/node/importGlob.ts
+++ b/packages/vite/src/node/importGlob.ts
@@ -147,7 +147,7 @@ export async function transformImportGlob(
         )
       }
       entries += ` ${JSON.stringify(file)}: ${JSON.stringify(
-        await fsp.readFile(path.join(base, file), 'utf-8')
+        await fsp.readFile(path.join(base, files[i]), 'utf-8')
       )},`
     } else {
       const importeeUrl = isCSSRequest(importee) ? `${importee}?used` : importee

From 42c15f6bad06f98962bc058c69a9f1e1a0f23440 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 28 Mar 2022 05:10:21 +0200
Subject: [PATCH 33/92] chore(deps): update all non-major dependencies (#7490)

---
 package.json                      |  10 +-
 packages/create-vite/package.json |   2 +-
 packages/vite/package.json        |   6 +-
 pnpm-lock.yaml                    | 175 ++++++++++++++----------------
 4 files changed, 93 insertions(+), 100 deletions(-)

diff --git a/package.json b/package.json
index 858e7502d53324..878fd5c7de506f 100644
--- a/package.json
+++ b/package.json
@@ -45,26 +45,26 @@
     "conventional-changelog-cli": "^2.2.2",
     "cross-env": "^7.0.3",
     "esbuild": "^0.14.27",
-    "eslint": "^8.11.0",
+    "eslint": "^8.12.0",
     "eslint-define-config": "^1.3.0",
     "eslint-plugin-node": "^11.1.0",
     "execa": "^5.1.1",
     "fs-extra": "^10.0.1",
     "jest": "^27.5.1",
     "lint-staged": "^12.3.7",
-    "minimist": "^1.2.5",
+    "minimist": "^1.2.6",
     "node-fetch": "^2.6.6",
     "npm-run-all": "^4.1.5",
     "picocolors": "^1.0.0",
-    "playwright-chromium": "^1.20.0",
-    "prettier": "2.6.0",
+    "playwright-chromium": "^1.20.1",
+    "prettier": "2.6.1",
     "prompts": "^2.4.2",
     "rimraf": "^3.0.2",
     "rollup": "^2.59.0",
     "semver": "^7.3.5",
     "simple-git-hooks": "^2.7.0",
     "sirv": "^2.0.2",
-    "ts-jest": "^27.1.3",
+    "ts-jest": "^27.1.4",
     "ts-node": "^10.4.0",
     "typescript": "~4.5.4",
     "vite": "workspace:*",
diff --git a/packages/create-vite/package.json b/packages/create-vite/package.json
index 5d873b83fe515b..ec8199455ef86f 100644
--- a/packages/create-vite/package.json
+++ b/packages/create-vite/package.json
@@ -26,7 +26,7 @@
   "homepage": "https://github.com/vitejs/vite/tree/main/packages/create-vite#readme",
   "dependencies": {
     "kolorist": "^1.5.1",
-    "minimist": "^1.2.5",
+    "minimist": "^1.2.6",
     "prompts": "^2.4.2"
   }
 }
diff --git a/packages/vite/package.json b/packages/vite/package.json
index ddb89d3970fc26..f6c177b248289f 100644
--- a/packages/vite/package.json
+++ b/packages/vite/package.json
@@ -57,7 +57,7 @@
     "@babel/types": "^7.17.0",
     "@jridgewell/trace-mapping": "^0.3.4",
     "@rollup/plugin-alias": "^3.1.9",
-    "@rollup/plugin-commonjs": "^21.0.2",
+    "@rollup/plugin-commonjs": "^21.0.3",
     "@rollup/plugin-dynamic-import-vars": "^1.4.2",
     "@rollup/plugin-json": "^4.1.0",
     "@rollup/plugin-node-resolve": "13.1.3",
@@ -96,14 +96,14 @@
     "json5": "^2.2.1",
     "launch-editor-middleware": "^2.3.0",
     "magic-string": "^0.26.1",
-    "micromatch": "^4.0.4",
+    "micromatch": "^4.0.5",
     "mrmime": "^1.0.0",
     "node-forge": "^1.3.0",
     "okie": "^1.0.1",
     "open": "^8.4.0",
     "periscopic": "^2.0.3",
     "picocolors": "^1.0.0",
-    "postcss-import": "^14.0.2",
+    "postcss-import": "^14.1.0",
     "postcss-load-config": "^3.1.3",
     "postcss-modules": "^4.3.1",
     "resolve.exports": "^1.1.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 22de761586a2f2..a09304a6c8f50d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -19,26 +19,26 @@ importers:
       conventional-changelog-cli: ^2.2.2
       cross-env: ^7.0.3
       esbuild: ^0.14.27
-      eslint: ^8.11.0
+      eslint: ^8.12.0
       eslint-define-config: ^1.3.0
       eslint-plugin-node: ^11.1.0
       execa: ^5.1.1
       fs-extra: ^10.0.1
       jest: ^27.5.1
       lint-staged: ^12.3.7
-      minimist: ^1.2.5
+      minimist: ^1.2.6
       node-fetch: ^2.6.6
       npm-run-all: ^4.1.5
       picocolors: ^1.0.0
-      playwright-chromium: ^1.20.0
-      prettier: 2.6.0
+      playwright-chromium: ^1.20.1
+      prettier: 2.6.1
       prompts: ^2.4.2
       rimraf: ^3.0.2
       rollup: ^2.59.0
       semver: ^7.3.5
       simple-git-hooks: ^2.7.0
       sirv: ^2.0.2
-      ts-jest: ^27.1.3
+      ts-jest: ^27.1.4
       ts-node: ^10.4.0
       typescript: ~4.5.4
       vite: workspace:*
@@ -50,31 +50,31 @@ importers:
       '@types/node': 16.11.26
       '@types/prompts': 2.0.14
       '@types/semver': 7.3.9
-      '@typescript-eslint/eslint-plugin': 5.16.0_1324a086488ab25887f801dfb12f1aba
-      '@typescript-eslint/parser': 5.16.0_eslint@8.11.0+typescript@4.5.4
+      '@typescript-eslint/eslint-plugin': 5.16.0_e8f8f41828c0dd0ac8e32e1859a7f4f7
+      '@typescript-eslint/parser': 5.16.0_eslint@8.12.0+typescript@4.5.4
       conventional-changelog-cli: 2.2.2
       cross-env: 7.0.3
       esbuild: 0.14.27
-      eslint: 8.11.0
+      eslint: 8.12.0
       eslint-define-config: 1.3.0
-      eslint-plugin-node: 11.1.0_eslint@8.11.0
+      eslint-plugin-node: 11.1.0_eslint@8.12.0
       execa: 5.1.1
       fs-extra: 10.0.1
       jest: 27.5.1_ts-node@10.4.0
       lint-staged: 12.3.7
-      minimist: 1.2.5
+      minimist: 1.2.6
       node-fetch: 2.6.6
       npm-run-all: 4.1.5
       picocolors: 1.0.0
-      playwright-chromium: 1.20.0
-      prettier: 2.6.0
+      playwright-chromium: 1.20.1
+      prettier: 2.6.1
       prompts: 2.4.2
       rimraf: 3.0.2
       rollup: 2.62.0
       semver: 7.3.5
       simple-git-hooks: 2.7.0
       sirv: 2.0.2
-      ts-jest: 27.1.3_4dfe14e0e8266437469ae0475a5c09ac
+      ts-jest: 27.1.4_4dfe14e0e8266437469ae0475a5c09ac
       ts-node: 10.4.0_44ef5af6cbbc24239b4e70b5c7b0d7a6
       typescript: 4.5.4
       vite: link:packages/vite
@@ -83,11 +83,11 @@ importers:
   packages/create-vite:
     specifiers:
       kolorist: ^1.5.1
-      minimist: ^1.2.5
+      minimist: ^1.2.6
       prompts: ^2.4.2
     dependencies:
       kolorist: 1.5.1
-      minimist: 1.2.5
+      minimist: 1.2.6
       prompts: 2.4.2
 
   packages/playground:
@@ -809,7 +809,7 @@ importers:
       '@babel/types': ^7.17.0
       '@jridgewell/trace-mapping': ^0.3.4
       '@rollup/plugin-alias': ^3.1.9
-      '@rollup/plugin-commonjs': ^21.0.2
+      '@rollup/plugin-commonjs': ^21.0.3
       '@rollup/plugin-dynamic-import-vars': ^1.4.2
       '@rollup/plugin-json': ^4.1.0
       '@rollup/plugin-node-resolve': 13.1.3
@@ -850,7 +850,7 @@ importers:
       json5: ^2.2.1
       launch-editor-middleware: ^2.3.0
       magic-string: ^0.26.1
-      micromatch: ^4.0.4
+      micromatch: ^4.0.5
       mrmime: ^1.0.0
       node-forge: ^1.3.0
       okie: ^1.0.1
@@ -858,7 +858,7 @@ importers:
       periscopic: ^2.0.3
       picocolors: ^1.0.0
       postcss: ^8.4.12
-      postcss-import: ^14.0.2
+      postcss-import: ^14.1.0
       postcss-load-config: ^3.1.3
       postcss-modules: ^4.3.1
       resolve: ^1.22.0
@@ -887,7 +887,7 @@ importers:
       '@babel/types': 7.17.0
       '@jridgewell/trace-mapping': 0.3.4
       '@rollup/plugin-alias': 3.1.9_rollup@2.62.0
-      '@rollup/plugin-commonjs': 21.0.2_rollup@2.62.0
+      '@rollup/plugin-commonjs': 21.0.3_rollup@2.62.0
       '@rollup/plugin-dynamic-import-vars': 1.4.2_rollup@2.62.0
       '@rollup/plugin-json': 4.1.0_rollup@2.62.0
       '@rollup/plugin-node-resolve': 13.1.3_rollup@2.62.0
@@ -926,14 +926,14 @@ importers:
       json5: 2.2.1
       launch-editor-middleware: 2.3.0
       magic-string: 0.26.1
-      micromatch: 4.0.4
+      micromatch: 4.0.5
       mrmime: 1.0.0
       node-forge: 1.3.0
       okie: 1.0.1
       open: 8.4.0
       periscopic: 2.0.3
       picocolors: 1.0.0
-      postcss-import: 14.0.2_postcss@8.4.12
+      postcss-import: 14.1.0_postcss@8.4.12
       postcss-load-config: 3.1.3_ts-node@10.4.0
       postcss-modules: 4.3.1_postcss@8.4.12
       resolve.exports: 1.1.0
@@ -1112,7 +1112,7 @@ packages:
       convert-source-map: 1.8.0
       debug: 4.3.4
       gensync: 1.0.0-beta.2
-      json5: 2.2.0
+      json5: 2.2.1
       semver: 6.3.0
     transitivePeerDependencies:
       - supports-color
@@ -2165,17 +2165,6 @@ packages:
       - supports-color
     dev: true
 
-  /@jest/types/27.4.2:
-    resolution: {integrity: sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==}
-    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
-    dependencies:
-      '@types/istanbul-lib-coverage': 2.0.4
-      '@types/istanbul-reports': 3.0.1
-      '@types/node': 16.11.26
-      '@types/yargs': 16.0.4
-      chalk: 4.1.2
-    dev: true
-
   /@jest/types/27.5.1:
     resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==}
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
@@ -2325,8 +2314,8 @@ packages:
       slash: 3.0.0
     dev: true
 
-  /@rollup/plugin-commonjs/21.0.2_rollup@2.62.0:
-    resolution: {integrity: sha512-d/OmjaLVO4j/aQX69bwpWPpbvI3TJkQuxoAk7BH8ew1PyoMBLTOuvJTjzG8oEoW7drIIqB0KCJtfFLu/2GClWg==}
+  /@rollup/plugin-commonjs/21.0.3_rollup@2.62.0:
+    resolution: {integrity: sha512-ThGfwyvcLc6cfP/MWxA5ACF+LZCvsuhUq7V5134Az1oQWsiC7lNpLT4mJI86WQunK7BYmpUiHmMk2Op6OAHs0g==}
     engines: {node: '>= 8.0.0'}
     peerDependencies:
       rollup: ^2.38.3
@@ -2698,7 +2687,7 @@ packages:
     dev: true
     optional: true
 
-  /@typescript-eslint/eslint-plugin/5.16.0_1324a086488ab25887f801dfb12f1aba:
+  /@typescript-eslint/eslint-plugin/5.16.0_e8f8f41828c0dd0ac8e32e1859a7f4f7:
     resolution: {integrity: sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
@@ -2709,12 +2698,12 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 5.16.0_eslint@8.11.0+typescript@4.5.4
+      '@typescript-eslint/parser': 5.16.0_eslint@8.12.0+typescript@4.5.4
       '@typescript-eslint/scope-manager': 5.16.0
-      '@typescript-eslint/type-utils': 5.16.0_eslint@8.11.0+typescript@4.5.4
-      '@typescript-eslint/utils': 5.16.0_eslint@8.11.0+typescript@4.5.4
+      '@typescript-eslint/type-utils': 5.16.0_eslint@8.12.0+typescript@4.5.4
+      '@typescript-eslint/utils': 5.16.0_eslint@8.12.0+typescript@4.5.4
       debug: 4.3.4
-      eslint: 8.11.0
+      eslint: 8.12.0
       functional-red-black-tree: 1.0.1
       ignore: 5.2.0
       regexpp: 3.2.0
@@ -2725,7 +2714,7 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser/5.16.0_eslint@8.11.0+typescript@4.5.4:
+  /@typescript-eslint/parser/5.16.0_eslint@8.12.0+typescript@4.5.4:
     resolution: {integrity: sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
@@ -2739,7 +2728,7 @@ packages:
       '@typescript-eslint/types': 5.16.0
       '@typescript-eslint/typescript-estree': 5.16.0_typescript@4.5.4
       debug: 4.3.4
-      eslint: 8.11.0
+      eslint: 8.12.0
       typescript: 4.5.4
     transitivePeerDependencies:
       - supports-color
@@ -2753,7 +2742,7 @@ packages:
       '@typescript-eslint/visitor-keys': 5.16.0
     dev: true
 
-  /@typescript-eslint/type-utils/5.16.0_eslint@8.11.0+typescript@4.5.4:
+  /@typescript-eslint/type-utils/5.16.0_eslint@8.12.0+typescript@4.5.4:
     resolution: {integrity: sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
@@ -2763,9 +2752,9 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/utils': 5.16.0_eslint@8.11.0+typescript@4.5.4
+      '@typescript-eslint/utils': 5.16.0_eslint@8.12.0+typescript@4.5.4
       debug: 4.3.4
-      eslint: 8.11.0
+      eslint: 8.12.0
       tsutils: 3.21.0_typescript@4.5.4
       typescript: 4.5.4
     transitivePeerDependencies:
@@ -2798,7 +2787,7 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/utils/5.16.0_eslint@8.11.0+typescript@4.5.4:
+  /@typescript-eslint/utils/5.16.0_eslint@8.12.0+typescript@4.5.4:
     resolution: {integrity: sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
@@ -2808,9 +2797,9 @@ packages:
       '@typescript-eslint/scope-manager': 5.16.0
       '@typescript-eslint/types': 5.16.0
       '@typescript-eslint/typescript-estree': 5.16.0_typescript@4.5.4
-      eslint: 8.11.0
+      eslint: 8.12.0
       eslint-scope: 5.1.1
-      eslint-utils: 3.0.0_eslint@8.11.0
+      eslint-utils: 3.0.0_eslint@8.12.0
     transitivePeerDependencies:
       - supports-color
       - typescript
@@ -3075,7 +3064,7 @@ packages:
     resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
     engines: {node: '>= 6.0.0'}
     dependencies:
-      debug: 4.3.3
+      debug: 4.3.4
     transitivePeerDependencies:
       - supports-color
 
@@ -3158,7 +3147,7 @@ packages:
     engines: {node: '>= 8'}
     dependencies:
       normalize-path: 3.0.0
-      picomatch: 2.3.0
+      picomatch: 2.3.1
 
   /aproba/2.0.0:
     resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
@@ -4177,7 +4166,6 @@ packages:
         optional: true
     dependencies:
       ms: 2.1.2
-    dev: true
 
   /debug/4.3.4_supports-color@9.2.1:
     resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
@@ -4700,25 +4688,25 @@ packages:
     engines: {node: '>= 16.9.0', npm: '>= 7.0.0', pnpm: '>= 6.32.2'}
     dev: true
 
-  /eslint-plugin-es/3.0.1_eslint@8.11.0:
+  /eslint-plugin-es/3.0.1_eslint@8.12.0:
     resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==}
     engines: {node: '>=8.10.0'}
     peerDependencies:
       eslint: '>=4.19.1'
     dependencies:
-      eslint: 8.11.0
+      eslint: 8.12.0
       eslint-utils: 2.1.0
       regexpp: 3.2.0
     dev: true
 
-  /eslint-plugin-node/11.1.0_eslint@8.11.0:
+  /eslint-plugin-node/11.1.0_eslint@8.12.0:
     resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==}
     engines: {node: '>=8.10.0'}
     peerDependencies:
       eslint: '>=5.16.0'
     dependencies:
-      eslint: 8.11.0
-      eslint-plugin-es: 3.0.1_eslint@8.11.0
+      eslint: 8.12.0
+      eslint-plugin-es: 3.0.1_eslint@8.12.0
       eslint-utils: 2.1.0
       ignore: 5.2.0
       minimatch: 3.0.4
@@ -4749,13 +4737,13 @@ packages:
       eslint-visitor-keys: 1.3.0
     dev: true
 
-  /eslint-utils/3.0.0_eslint@8.11.0:
+  /eslint-utils/3.0.0_eslint@8.12.0:
     resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
     engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
     peerDependencies:
       eslint: '>=5'
     dependencies:
-      eslint: 8.11.0
+      eslint: 8.12.0
       eslint-visitor-keys: 2.1.0
     dev: true
 
@@ -4774,8 +4762,8 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
-  /eslint/8.11.0:
-    resolution: {integrity: sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==}
+  /eslint/8.12.0:
+    resolution: {integrity: sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     hasBin: true
     dependencies:
@@ -4784,11 +4772,11 @@ packages:
       ajv: 6.12.6
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      debug: 4.3.3
+      debug: 4.3.4
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.1.1
-      eslint-utils: 3.0.0_eslint@8.11.0
+      eslint-utils: 3.0.0_eslint@8.12.0
       eslint-visitor-keys: 3.3.0
       espree: 9.3.1
       esquery: 1.4.0
@@ -5338,7 +5326,7 @@ packages:
     engines: {node: '>=0.4.7'}
     hasBin: true
     dependencies:
-      minimist: 1.2.5
+      minimist: 1.2.6
       neo-async: 2.6.2
       source-map: 0.6.1
       wordwrap: 1.0.0
@@ -5495,7 +5483,7 @@ packages:
     engines: {node: '>= 6'}
     dependencies:
       agent-base: 6.0.2
-      debug: 4.3.3
+      debug: 4.3.4
     transitivePeerDependencies:
       - supports-color
 
@@ -6294,18 +6282,6 @@ packages:
       - supports-color
     dev: true
 
-  /jest-util/27.4.2:
-    resolution: {integrity: sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA==}
-    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
-    dependencies:
-      '@jest/types': 27.4.2
-      '@types/node': 16.11.26
-      chalk: 4.1.2
-      ci-info: 3.3.0
-      graceful-fs: 4.2.9
-      picomatch: 2.3.0
-    dev: true
-
   /jest-util/27.5.1:
     resolution: {integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==}
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
@@ -6475,6 +6451,7 @@ packages:
     hasBin: true
     dependencies:
       minimist: 1.2.5
+    dev: false
 
   /json5/2.2.1:
     resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==}
@@ -6824,6 +6801,14 @@ packages:
       braces: 3.0.2
       picomatch: 2.3.0
 
+  /micromatch/4.0.5:
+    resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+    engines: {node: '>=8.6'}
+    dependencies:
+      braces: 3.0.2
+      picomatch: 2.3.1
+    dev: true
+
   /mime-db/1.46.0:
     resolution: {integrity: sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==}
     engines: {node: '>= 0.6'}
@@ -6937,6 +6922,10 @@ packages:
 
   /minimist/1.2.5:
     resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==}
+    dev: false
+
+  /minimist/1.2.6:
+    resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==}
 
   /minipass/3.1.6:
     resolution: {integrity: sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==}
@@ -7411,6 +7400,10 @@ packages:
     resolution: {integrity: sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==}
     engines: {node: '>=8.6'}
 
+  /picomatch/2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+
   /pidtree/0.3.1:
     resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==}
     engines: {node: '>=0.10'}
@@ -7458,21 +7451,21 @@ packages:
       find-up: 4.1.0
     dev: true
 
-  /playwright-chromium/1.20.0:
-    resolution: {integrity: sha512-5qyHaW2kFguX4eAne2MgS7AxZBfQ+hN2X0t5SD5mxOTAgwciEAhMNc06o9NxILnRcmF8QSXGg19OSb8VMUBLzg==}
+  /playwright-chromium/1.20.1:
+    resolution: {integrity: sha512-zDJm59GEOWrHqpByblzaKI+bpswd3nrheCoAGOTBDX7MsQYwAV/ZBS2aCJzeGxDt/67brq/fQuZse3GwnCk+6A==}
     engines: {node: '>=12'}
     hasBin: true
     requiresBuild: true
     dependencies:
-      playwright-core: 1.20.0
+      playwright-core: 1.20.1
     transitivePeerDependencies:
       - bufferutil
       - supports-color
       - utf-8-validate
     dev: true
 
-  /playwright-core/1.20.0:
-    resolution: {integrity: sha512-d25IRcdooS278Cijlp8J8A5fLQZ+/aY3dKRJvgX5yjXA69N0huIUdnh3xXSgn+LsQ9DCNmB7Ngof3eY630jgdA==}
+  /playwright-core/1.20.1:
+    resolution: {integrity: sha512-A8ZsZ09gaSbxP0UijoLyzp3LJc0kWMxDooLPi+mm4/5iYnTbd6PF5nKjoFw1a7KwjZIEgdhJduah4BcUIh+IPA==}
     engines: {node: '>=12'}
     hasBin: true
     dependencies:
@@ -7510,8 +7503,8 @@ packages:
     engines: {node: '>=12.13.0'}
     dev: true
 
-  /postcss-import/14.0.2_postcss@8.4.12:
-    resolution: {integrity: sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==}
+  /postcss-import/14.1.0_postcss@8.4.12:
+    resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
     engines: {node: '>=10.0.0'}
     peerDependencies:
       postcss: ^8.0.0
@@ -7668,8 +7661,8 @@ packages:
     engines: {node: '>= 0.8.0'}
     dev: true
 
-  /prettier/2.6.0:
-    resolution: {integrity: sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A==}
+  /prettier/2.6.1:
+    resolution: {integrity: sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==}
     engines: {node: '>=10.13.0'}
     hasBin: true
     dev: true
@@ -8065,7 +8058,7 @@ packages:
     resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
     engines: {node: '>=8.10.0'}
     dependencies:
-      picomatch: 2.3.0
+      picomatch: 2.3.1
 
   /redent/3.0.0:
     resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
@@ -9026,15 +9019,15 @@ packages:
       utf8-byte-length: 1.0.4
     dev: true
 
-  /ts-jest/27.1.3_4dfe14e0e8266437469ae0475a5c09ac:
-    resolution: {integrity: sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==}
+  /ts-jest/27.1.4_4dfe14e0e8266437469ae0475a5c09ac:
+    resolution: {integrity: sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==}
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     hasBin: true
     peerDependencies:
       '@babel/core': '>=7.0.0-beta.0 <8'
       '@types/jest': ^27.0.0
       babel-jest: '>=27.0.0 <28'
-      esbuild: ~0.14.0
+      esbuild: '*'
       jest: ^27.0.0
       typescript: '>=3.8 <5.0'
     peerDependenciesMeta:
@@ -9052,8 +9045,8 @@ packages:
       esbuild: 0.14.27
       fast-json-stable-stringify: 2.1.0
       jest: 27.5.1_ts-node@10.4.0
-      jest-util: 27.4.2
-      json5: 2.2.0
+      jest-util: 27.5.1
+      json5: 2.2.1
       lodash.memoize: 4.1.2
       make-error: 1.3.6
       semver: 7.3.5

From 9707f0e9e37e64eeb07fcd96d3e4fee6d19cc3a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=BF=A0=20/=20green?= 
Date: Mon, 28 Mar 2022 21:41:00 +0900
Subject: [PATCH 34/92] test: add js sourcemap tests (#7494)

---
 .../css-sourcemap/__tests__/serve.spec.ts     | 23 ++----
 .../playground/css-sourcemap/package.json     |  1 -
 .../js-sourcemap/__tests__/build.spec.ts      | 13 ++++
 .../js-sourcemap/__tests__/serve.spec.ts      | 44 +++++++++++
 packages/playground/js-sourcemap/bar.ts       |  1 +
 packages/playground/js-sourcemap/foo.js       |  1 +
 packages/playground/js-sourcemap/index.html   |  6 ++
 packages/playground/js-sourcemap/package.json | 11 +++
 .../playground/js-sourcemap/vite.config.js    |  8 ++
 packages/playground/package.json              |  1 +
 packages/playground/testUtils.ts              | 17 +++-
 packages/playground/vue-sourcemap/Js.vue      | 11 +++
 packages/playground/vue-sourcemap/Main.vue    |  4 +
 packages/playground/vue-sourcemap/Ts.vue      | 11 +++
 .../vue-sourcemap/__tests__/serve.spec.ts     | 78 +++++++++++++++----
 .../playground/vue-sourcemap/package.json     |  1 -
 pnpm-lock.yaml                                |  9 ++-
 17 files changed, 199 insertions(+), 41 deletions(-)
 create mode 100644 packages/playground/js-sourcemap/__tests__/build.spec.ts
 create mode 100644 packages/playground/js-sourcemap/__tests__/serve.spec.ts
 create mode 100644 packages/playground/js-sourcemap/bar.ts
 create mode 100644 packages/playground/js-sourcemap/foo.js
 create mode 100644 packages/playground/js-sourcemap/index.html
 create mode 100644 packages/playground/js-sourcemap/package.json
 create mode 100644 packages/playground/js-sourcemap/vite.config.js
 create mode 100644 packages/playground/vue-sourcemap/Js.vue
 create mode 100644 packages/playground/vue-sourcemap/Ts.vue

diff --git a/packages/playground/css-sourcemap/__tests__/serve.spec.ts b/packages/playground/css-sourcemap/__tests__/serve.spec.ts
index 0c6696b0dff7a2..11e33a78af8424 100644
--- a/packages/playground/css-sourcemap/__tests__/serve.spec.ts
+++ b/packages/playground/css-sourcemap/__tests__/serve.spec.ts
@@ -1,11 +1,11 @@
-import { fromComment } from 'convert-source-map'
 import { URL } from 'url'
-import { normalizePath } from 'vite'
-import { isBuild, testDir } from 'testUtils'
+import {
+  extractSourcemap,
+  formatSourcemapForSnapshot,
+  isBuild
+} from 'testUtils'
 
 if (!isBuild) {
-  const root = normalizePath(testDir)
-
   const getStyleTagContentIncluding = async (content: string) => {
     const styles = await page.$$('style')
     for (const style of styles) {
@@ -17,19 +17,6 @@ if (!isBuild) {
     throw new Error('Not found')
   }
 
-  const extractSourcemap = (content: string) => {
-    const lines = content.trim().split('\n')
-    return fromComment(lines[lines.length - 1]).toObject()
-  }
-
-  const formatSourcemapForSnapshot = (map: any) => {
-    const m = { ...map }
-    delete m.file
-    delete m.names
-    m.sources = m.sources.map((source) => source.replace(root, '/root'))
-    return m
-  }
-
   test('inline css', async () => {
     const css = await getStyleTagContentIncluding('.inline ')
     const map = extractSourcemap(css)
diff --git a/packages/playground/css-sourcemap/package.json b/packages/playground/css-sourcemap/package.json
index c29f18d4dee0d7..c7e9e61372cefa 100644
--- a/packages/playground/css-sourcemap/package.json
+++ b/packages/playground/css-sourcemap/package.json
@@ -9,7 +9,6 @@
     "preview": "vite preview"
   },
   "devDependencies": {
-    "convert-source-map": "^1.8.0",
     "less": "^4.1.2",
     "magic-string": "^0.25.7",
     "sass": "^1.43.4",
diff --git a/packages/playground/js-sourcemap/__tests__/build.spec.ts b/packages/playground/js-sourcemap/__tests__/build.spec.ts
new file mode 100644
index 00000000000000..e36c1f52d2c1f8
--- /dev/null
+++ b/packages/playground/js-sourcemap/__tests__/build.spec.ts
@@ -0,0 +1,13 @@
+import { isBuild } from 'testUtils'
+
+if (isBuild) {
+  test('should not output sourcemap warning (#4939)', () => {
+    serverLogs.forEach((log) => {
+      expect(log).not.toMatch('Sourcemap is likely to be incorrect')
+    })
+  })
+} else {
+  test('this file only includes test for build', () => {
+    expect(true).toBe(true)
+  })
+}
diff --git a/packages/playground/js-sourcemap/__tests__/serve.spec.ts b/packages/playground/js-sourcemap/__tests__/serve.spec.ts
new file mode 100644
index 00000000000000..a1ffdddc37ecd5
--- /dev/null
+++ b/packages/playground/js-sourcemap/__tests__/serve.spec.ts
@@ -0,0 +1,44 @@
+import { URL } from 'url'
+import {
+  extractSourcemap,
+  formatSourcemapForSnapshot,
+  isBuild
+} from 'testUtils'
+
+if (!isBuild) {
+  test('js', async () => {
+    const res = await page.request.get(new URL('./foo.js', page.url()).href)
+    const js = await res.text()
+    const lines = js.split('\n')
+    expect(lines[lines.length - 1].includes('//')).toBe(false) // expect no sourcemap
+  })
+
+  test('ts', async () => {
+    const res = await page.request.get(new URL('./bar.ts', page.url()).href)
+    const js = await res.text()
+    const map = extractSourcemap(js)
+    expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(`
+      Object {
+        "mappings": "AAAO,aAAM,MAAM;",
+        "sources": Array [
+          "/root/bar.ts",
+        ],
+        "sourcesContent": Array [
+          "export const bar = 'bar'
+      ",
+        ],
+        "version": 3,
+      }
+    `)
+  })
+
+  test('should not output missing source file warning', () => {
+    serverLogs.forEach((log) => {
+      expect(log).not.toMatch(/Sourcemap for .+ points to missing source files/)
+    })
+  })
+} else {
+  test('this file only includes test for serve', () => {
+    expect(true).toBe(true)
+  })
+}
diff --git a/packages/playground/js-sourcemap/bar.ts b/packages/playground/js-sourcemap/bar.ts
new file mode 100644
index 00000000000000..1fc11814f22e80
--- /dev/null
+++ b/packages/playground/js-sourcemap/bar.ts
@@ -0,0 +1 @@
+export const bar = 'bar'
diff --git a/packages/playground/js-sourcemap/foo.js b/packages/playground/js-sourcemap/foo.js
new file mode 100644
index 00000000000000..cb356468240d50
--- /dev/null
+++ b/packages/playground/js-sourcemap/foo.js
@@ -0,0 +1 @@
+export const foo = 'foo'
diff --git a/packages/playground/js-sourcemap/index.html b/packages/playground/js-sourcemap/index.html
new file mode 100644
index 00000000000000..025b161011a3fa
--- /dev/null
+++ b/packages/playground/js-sourcemap/index.html
@@ -0,0 +1,6 @@
+
+

JS Sourcemap

+
+ + + diff --git a/packages/playground/js-sourcemap/package.json b/packages/playground/js-sourcemap/package.json new file mode 100644 index 00000000000000..e5a97aea80830f --- /dev/null +++ b/packages/playground/js-sourcemap/package.json @@ -0,0 +1,11 @@ +{ + "name": "test-js-sourcemap", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "debug": "node --inspect-brk ../../vite/bin/vite", + "preview": "vite preview" + } +} diff --git a/packages/playground/js-sourcemap/vite.config.js b/packages/playground/js-sourcemap/vite.config.js new file mode 100644 index 00000000000000..bc9d1748cab964 --- /dev/null +++ b/packages/playground/js-sourcemap/vite.config.js @@ -0,0 +1,8 @@ +/** + * @type {import('vite').UserConfig} + */ +module.exports = { + build: { + sourcemap: true + } +} diff --git a/packages/playground/package.json b/packages/playground/package.json index 58ef368099e82f..75b1d15d299319 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -3,6 +3,7 @@ "private": true, "version": "1.0.0", "devDependencies": { + "convert-source-map": "^1.8.0", "css-color-names": "^1.0.1" } } diff --git a/packages/playground/testUtils.ts b/packages/playground/testUtils.ts index 0c8186d4ed121d..7d21625dacf874 100644 --- a/packages/playground/testUtils.ts +++ b/packages/playground/testUtils.ts @@ -6,7 +6,8 @@ import fs from 'fs' import path from 'path' import colors from 'css-color-names' import type { ElementHandle } from 'playwright-chromium' -import type { Manifest } from 'vite' +import { Manifest, normalizePath } from 'vite' +import { fromComment } from 'convert-source-map' export function slash(p: string): string { return p.replace(/\\/g, '/') @@ -138,3 +139,17 @@ export async function untilUpdated( * Send the rebuild complete message in build watch */ export { notifyRebuildComplete } from '../../scripts/jestPerTestSetup' + +export const extractSourcemap = (content: string) => { + const lines = content.trim().split('\n') + return fromComment(lines[lines.length - 1]).toObject() +} + +export const formatSourcemapForSnapshot = (map: any) => { + const root = normalizePath(testDir) + const m = { ...map } + delete m.file + delete m.names + m.sources = m.sources.map((source) => source.replace(root, '/root')) + return m +} diff --git a/packages/playground/vue-sourcemap/Js.vue b/packages/playground/vue-sourcemap/Js.vue new file mode 100644 index 00000000000000..3a5577099f67d3 --- /dev/null +++ b/packages/playground/vue-sourcemap/Js.vue @@ -0,0 +1,11 @@ + + + + + diff --git a/packages/playground/vue-sourcemap/Main.vue b/packages/playground/vue-sourcemap/Main.vue index 04ddf50071ccb3..b9b03596f5aea5 100644 --- a/packages/playground/vue-sourcemap/Main.vue +++ b/packages/playground/vue-sourcemap/Main.vue @@ -1,5 +1,7 @@ + + diff --git a/packages/playground/vue-sourcemap/__tests__/serve.spec.ts b/packages/playground/vue-sourcemap/__tests__/serve.spec.ts index 193b0afb9ba73f..08b4c04face111 100644 --- a/packages/playground/vue-sourcemap/__tests__/serve.spec.ts +++ b/packages/playground/vue-sourcemap/__tests__/serve.spec.ts @@ -1,10 +1,11 @@ -import { fromComment } from 'convert-source-map' -import { normalizePath } from 'vite' -import { isBuild, testDir } from 'testUtils' +import { + extractSourcemap, + formatSourcemapForSnapshot, + isBuild +} from 'testUtils' +import { URL } from 'url' if (!isBuild) { - const root = normalizePath(testDir) - const getStyleTagContentIncluding = async (content: string) => { const styles = await page.$$('style') for (const style of styles) { @@ -16,18 +17,63 @@ if (!isBuild) { throw new Error('Not found') } - const extractSourcemap = (content: string) => { - const lines = content.trim().split('\n') - return fromComment(lines[lines.length - 1]).toObject() - } + test('js', async () => { + const res = await page.request.get(new URL('./Js.vue', page.url()).href) + const js = await res.text() + const map = extractSourcemap(js) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AAKA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;;;;;AAGP;AACd,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;;;;;;;;;;;wBARlB,oBAAiB,WAAd,MAAU", + "sources": Array [ + "/root/Js.vue", + ], + "sourcesContent": Array [ + " - const formatSourcemapForSnapshot = (map: any) => { - const m = { ...map } - delete m.file - delete m.names - m.sources = m.sources.map((source) => source.replace(root, '/root')) - return m - } + + + + ", + ], + "version": 3, + } + `) + }) + + test('ts', async () => { + const res = await page.request.get(new URL('./Ts.vue', page.url()).href) + const js = await res.text() + const map = extractSourcemap(js) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": ";AAKA,QAAQ,IAAI,WAAW;;;;AAIvB,YAAQ,IAAI,UAAU;;;;;;;;uBARpB,oBAAiB,WAAd,MAAU", + "sources": Array [ + "/root/Ts.vue", + ], + "sourcesContent": Array [ + " + + + + + ", + ], + "version": 3, + } + `) + }) test('css', async () => { const css = await getStyleTagContentIncluding('.css ') diff --git a/packages/playground/vue-sourcemap/package.json b/packages/playground/vue-sourcemap/package.json index 5672b5e3d9d57d..286940b01efa58 100644 --- a/packages/playground/vue-sourcemap/package.json +++ b/packages/playground/vue-sourcemap/package.json @@ -10,7 +10,6 @@ }, "devDependencies": { "@vitejs/plugin-vue": "workspace:*", - "convert-source-map": "^1.8.0", "less": "^4.1.2", "sass": "^1.43.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a09304a6c8f50d..def6f1a526907f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,8 +92,10 @@ importers: packages/playground: specifiers: + convert-source-map: ^1.8.0 css-color-names: ^1.0.1 devDependencies: + convert-source-map: 1.8.0 css-color-names: 1.0.1 packages/playground/alias: @@ -152,13 +154,11 @@ importers: packages/playground/css-sourcemap: specifiers: - convert-source-map: ^1.8.0 less: ^4.1.2 magic-string: ^0.25.7 sass: ^1.43.4 stylus: ^0.55.0 devDependencies: - convert-source-map: 1.8.0 less: 4.1.2 magic-string: 0.25.7 sass: 1.45.1 @@ -223,6 +223,9 @@ importers: packages/playground/html: specifiers: {} + packages/playground/js-sourcemap: + specifiers: {} + packages/playground/json: specifiers: json-module: file:./json-module @@ -711,7 +714,6 @@ importers: packages/playground/vue-sourcemap: specifiers: '@vitejs/plugin-vue': workspace:* - convert-source-map: ^1.8.0 less: ^4.1.2 sass: ^1.43.4 vue: ^3.2.31 @@ -719,7 +721,6 @@ importers: vue: 3.2.31 devDependencies: '@vitejs/plugin-vue': link:../../plugin-vue - convert-source-map: 1.8.0 less: 4.1.2 sass: 1.45.1 From 01bc415289d747f2db048578ad23d1e995996301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Wouts?= Date: Tue, 29 Mar 2022 02:14:31 +1100 Subject: [PATCH 35/92] fix: add workaround to fix build-temp-types on Netlify (#7495) --- package.json | 7 +++++++ pnpm-lock.yaml | 9 +++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 878fd5c7de506f..f6166c2985a30c 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,13 @@ "overrides": { "vite": "workspace:*", "@vitejs/plugin-vue": "workspace:*" + }, + "packageExtensions": { + "postcss-load-config": { + "peerDependencies": { + "postcss": "*" + } + } } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index def6f1a526907f..b0574f04c53b86 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,8 @@ overrides: vite: workspace:* '@vitejs/plugin-vue': workspace:* +packageExtensionsChecksum: 696422bac84dd936748019990f84746e + importers: .: @@ -935,7 +937,7 @@ importers: periscopic: 2.0.3 picocolors: 1.0.0 postcss-import: 14.1.0_postcss@8.4.12 - postcss-load-config: 3.1.3_ts-node@10.4.0 + postcss-load-config: 3.1.3_postcss@8.4.12+ts-node@10.4.0 postcss-modules: 4.3.1_postcss@8.4.12 resolve.exports: 1.1.0 rollup-plugin-license: 2.6.1_rollup@2.62.0 @@ -7528,6 +7530,7 @@ packages: resolution: {integrity: sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==} engines: {node: '>= 10'} peerDependencies: + postcss: '*' ts-node: '>=9.0.0' peerDependenciesMeta: ts-node: @@ -7539,16 +7542,18 @@ packages: yaml: 1.10.2 dev: false - /postcss-load-config/3.1.3_ts-node@10.4.0: + /postcss-load-config/3.1.3_postcss@8.4.12+ts-node@10.4.0: resolution: {integrity: sha512-5EYgaM9auHGtO//ljHH+v/aC/TQ5LHXtL7bQajNAUBKUVKiYE8rYpFms7+V26D9FncaGe2zwCoPQsFKb5zF/Hw==} engines: {node: '>= 10'} peerDependencies: + postcss: '*' ts-node: '>=9.0.0' peerDependenciesMeta: ts-node: optional: true dependencies: lilconfig: 2.0.4 + postcss: 8.4.12 ts-node: 10.4.0_44ef5af6cbbc24239b4e70b5c7b0d7a6 yaml: 1.10.2 dev: true From 28b0660ee471828af0aa0bc29ca5b647fa51ef2e Mon Sep 17 00:00:00 2001 From: patak Date: Mon, 28 Mar 2022 17:18:45 +0200 Subject: [PATCH 36/92] fix: custom event payload type (#7498) --- packages/vite/types/customEvent.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/types/customEvent.d.ts b/packages/vite/types/customEvent.d.ts index aacf1554b0de75..af4db5d14fbe97 100644 --- a/packages/vite/types/customEvent.d.ts +++ b/packages/vite/types/customEvent.d.ts @@ -13,4 +13,4 @@ export interface CustomEventMap { } export type InferCustomEventPayload = - T extends keyof CustomEventMap ? CustomEventMap[T] : unknown + T extends keyof CustomEventMap ? CustomEventMap[T] : any From 4ffd82d2d4759bc3efa368bca54613aaa1f45acc Mon Sep 17 00:00:00 2001 From: patak-dev Date: Mon, 28 Mar 2022 20:38:19 +0200 Subject: [PATCH 37/92] release: v2.9.0-beta.10 --- packages/vite/CHANGELOG.md | 13 +++++++++++++ packages/vite/package.json | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/vite/CHANGELOG.md b/packages/vite/CHANGELOG.md index b79c75db3260f0..1a9b06abefcf66 100644 --- a/packages/vite/CHANGELOG.md +++ b/packages/vite/CHANGELOG.md @@ -1,3 +1,16 @@ +## 2.9.0-beta.10 (2022-03-28) + +* fix: Correctly process urls when they are rewritten to contain space (#7452) ([9ee2cf6](https://github.com/vitejs/vite/commit/9ee2cf6)), closes [#7452](https://github.com/vitejs/vite/issues/7452) +* fix: custom event payload type (#7498) ([28b0660](https://github.com/vitejs/vite/commit/28b0660)), closes [#7498](https://github.com/vitejs/vite/issues/7498) +* fix: handle relative path glob raw import, fix #7307 (#7371) ([7f8dc58](https://github.com/vitejs/vite/commit/7f8dc58)), closes [#7307](https://github.com/vitejs/vite/issues/7307) [#7371](https://github.com/vitejs/vite/issues/7371) +* fix: import.meta.url in worker (#7464) ([8ac4b12](https://github.com/vitejs/vite/commit/8ac4b12)), closes [#7464](https://github.com/vitejs/vite/issues/7464) +* fix: optimizeDeps.entries default ignore paths (#7469) ([4c95e99](https://github.com/vitejs/vite/commit/4c95e99)), closes [#7469](https://github.com/vitejs/vite/issues/7469) +* chore(deps): update all non-major dependencies (#7490) ([42c15f6](https://github.com/vitejs/vite/commit/42c15f6)), closes [#7490](https://github.com/vitejs/vite/issues/7490) +* feat(type): support typing for custom events (#7476) ([50a8765](https://github.com/vitejs/vite/commit/50a8765)), closes [#7476](https://github.com/vitejs/vite/issues/7476) +* refactor(types): share hot context type (#7475) ([64ddff0](https://github.com/vitejs/vite/commit/64ddff0)), closes [#7475](https://github.com/vitejs/vite/issues/7475) + + + ## 2.9.0-beta.9 (2022-03-26) * feat: support importing css with ?raw (#5796) ([fedb106](https://github.com/vitejs/vite/commit/fedb106)), closes [#5796](https://github.com/vitejs/vite/issues/5796) diff --git a/packages/vite/package.json b/packages/vite/package.json index f6c177b248289f..918b00aaa46912 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -1,6 +1,6 @@ { "name": "vite", - "version": "2.9.0-beta.9", + "version": "2.9.0-beta.10", "license": "MIT", "author": "Evan You", "description": "Native-ESM powered web dev build tool", From e3c7c7ace9da3d4c38e0ac49dd4b737cdd0c9f75 Mon Sep 17 00:00:00 2001 From: Johannes Maas Date: Tue, 29 Mar 2022 05:53:36 +0200 Subject: [PATCH 38/92] fix: build path error on Windows (#7383) --- packages/vite/src/node/plugins/html.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 30e65a36f83bbe..5f7dae623b0cb4 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -594,7 +594,6 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { } } - const shortEmitName = path.posix.relative(config.root, id) // no use assets plugin because it will emit file let match: RegExpExecArray | null let s: MagicString | undefined @@ -612,8 +611,9 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { if (s) { result = s.toString() } + const relativeUrlPath = path.posix.relative(config.root, id) result = await applyHtmlTransforms(result, postHooks, { - path: '/' + shortEmitName, + path: '/' + relativeUrlPath, filename: id, bundle, chunk @@ -628,6 +628,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { delete bundle[chunk.fileName] } + const shortEmitName = path.relative(config.root, id) this.emitFile({ type: 'asset', fileName: shortEmitName, From 93ae8e5a053f637ff291a76cb0755e2c42cb5d8c Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 29 Mar 2022 09:05:30 +0200 Subject: [PATCH 39/92] fix: infer client port from page location (#7463) --- docs/config/index.md | 4 +--- packages/vite/src/client/client.ts | 13 +++++++------ .../vite/src/node/plugins/clientInjections.ts | 19 +++++++++---------- packages/vite/src/node/server/hmr.ts | 2 +- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/docs/config/index.md b/docs/config/index.md index a8d8cb36079f0a..f9a1a81dc77dbf 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -550,14 +550,12 @@ export default defineConfig(({ command, mode }) => { ### server.hmr -- **Type:** `boolean | { protocol?: string, host?: string, port?: number | false, path?: string, timeout?: number, overlay?: boolean, clientPort?: number, server?: Server }` +- **Type:** `boolean | { protocol?: string, host?: string, port?: number, path?: string, timeout?: number, overlay?: boolean, clientPort?: number, server?: Server }` Disable or configure HMR connection (in cases where the HMR websocket must use a different address from the http server). Set `server.hmr.overlay` to `false` to disable the server error overlay. - Set `server.hmr.port` to `false` when connecting to a domain without a port. - `clientPort` is an advanced option that overrides the port only on the client side, allowing you to serve the websocket on a different port than the client code looks for it on. Useful if you're using an SSL proxy in front of your dev server. If specifying `server.hmr.server`, Vite will process HMR connection requests through the provided server. If not in middleware mode, Vite will attempt to process HMR connection requests through the existing server. This can be helpful when using self-signed certificates or when you want to expose Vite over a network on a single port. diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index c180714f5a69bf..0f5929904a73c3 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -7,9 +7,10 @@ import '@vite/env' // injected by the hmr plugin when served declare const __BASE__: string -declare const __HMR_PROTOCOL__: string -declare const __HMR_HOSTNAME__: string -declare const __HMR_PORT__: string | false +declare const __HMR_PROTOCOL__: string | null +declare const __HMR_HOSTNAME__: string | null +declare const __HMR_PORT__: string | null +declare const __HMR_BASE__: string declare const __HMR_TIMEOUT__: number declare const __HMR_ENABLE_OVERLAY__: boolean @@ -18,9 +19,9 @@ console.log('[vite] connecting...') // use server configuration, then fallback to inference const socketProtocol = __HMR_PROTOCOL__ || (location.protocol === 'https:' ? 'wss' : 'ws') -const socketHost = __HMR_PORT__ - ? `${__HMR_HOSTNAME__ || location.hostname}:${__HMR_PORT__}` - : `${__HMR_HOSTNAME__ || location.hostname}` +const socketHost = `${__HMR_HOSTNAME__ || location.hostname}:${ + __HMR_PORT__ || location.port +}${__HMR_BASE__}` const socket = new WebSocket(`${socketProtocol}://${socketHost}`, 'vite-hmr') const base = __BASE__ || '/' diff --git a/packages/vite/src/node/plugins/clientInjections.ts b/packages/vite/src/node/plugins/clientInjections.ts index e86bec0826d72f..92a2ea93284eb1 100644 --- a/packages/vite/src/node/plugins/clientInjections.ts +++ b/packages/vite/src/node/plugins/clientInjections.ts @@ -2,7 +2,7 @@ import path from 'path' import type { Plugin } from '../plugin' import type { ResolvedConfig } from '../config' import { CLIENT_ENTRY, ENV_ENTRY } from '../constants' -import { normalizePath, isObject } from '../utils' +import { normalizePath } from '../utils' // ids in transform are normalized to unix style const normalizedClientEntry = normalizePath(CLIENT_ENTRY) @@ -23,21 +23,19 @@ export function clientInjectionsPlugin(config: ResolvedConfig): Plugin { const protocol = options.protocol || null const timeout = options.timeout || 30000 const overlay = options.overlay !== false - let port: number | string | false | undefined - if (isObject(config.server.hmr)) { - port = config.server.hmr.clientPort || config.server.hmr.port - } + let port: number | string | undefined | null + port = options.clientPort || options.port if (config.server.middlewareMode) { - port = String(port || 24678) - } else { - port = String(port || options.port || config.server.port!) + port = port || 24678 } + port = port ? String(port) : null + let hmrBase = config.base if (options.path) { hmrBase = path.posix.join(hmrBase, options.path) } - if (hmrBase !== '/') { - port = path.posix.normalize(`${port}${hmrBase}`) + if (hmrBase === '/') { + hmrBase = '' } return code @@ -47,6 +45,7 @@ export function clientInjectionsPlugin(config: ResolvedConfig): Plugin { .replace(/__HMR_PROTOCOL__/g, JSON.stringify(protocol)) .replace(/__HMR_HOSTNAME__/g, JSON.stringify(host)) .replace(/__HMR_PORT__/g, JSON.stringify(port)) + .replace(/__HMR_BASE__/g, JSON.stringify(hmrBase)) .replace(/__HMR_TIMEOUT__/g, JSON.stringify(timeout)) .replace(/__HMR_ENABLE_OVERLAY__/g, JSON.stringify(overlay)) } else if (!options?.ssr && code.includes('process.env.NODE_ENV')) { diff --git a/packages/vite/src/node/server/hmr.ts b/packages/vite/src/node/server/hmr.ts index edb73785247b6f..fc18b0aa91c5cb 100644 --- a/packages/vite/src/node/server/hmr.ts +++ b/packages/vite/src/node/server/hmr.ts @@ -18,7 +18,7 @@ const normalizedClientDir = normalizePath(CLIENT_DIR) export interface HmrOptions { protocol?: string host?: string - port?: number | false + port?: number clientPort?: number path?: string timeout?: number From b7cc0dcdf41c09c147356b0ecafa98b69491261c Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Tue, 29 Mar 2022 15:10:23 +0800 Subject: [PATCH 40/92] docs: note url string must be static (#7508) --- docs/guide/assets.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/guide/assets.md b/docs/guide/assets.md index fd5aa46f49467f..53ac153f9465bd 100644 --- a/docs/guide/assets.md +++ b/docs/guide/assets.md @@ -103,8 +103,13 @@ function getImageUrl(name) { } ``` -During the production build, Vite will perform necessary transforms so that the URLs still point to the correct location even after bundling and asset hashing. +During the production build, Vite will perform necessary transforms so that the URLs still point to the correct location even after bundling and asset hashing. However, the URL string must be static so it can be analyzed, otherwise the code will be left as is, which can cause runtime errors if `build.target` does not support `import.meta.url` -::: warning Note: Does not work with SSR +```js +// Vite will not transform this +const imgUrl = new URL(imagePath, import.meta.url).href +``` + +::: warning Does not work with SSR This pattern does not work if you are using Vite for Server-Side Rendering, because `import.meta.url` have different semantics in browsers vs. Node.js. The server bundle also cannot determine the client host URL ahead of time. ::: From 884e994b7322eb4ff96f457fe536affcd0b596d0 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Tue, 29 Mar 2022 15:15:51 +0800 Subject: [PATCH 41/92] docs: note env in command line (#7507) --- docs/guide/env-and-mode.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/env-and-mode.md b/docs/guide/env-and-mode.md index d3a6a575bce64c..a2dee2b0d9d76e 100644 --- a/docs/guide/env-and-mode.md +++ b/docs/guide/env-and-mode.md @@ -37,7 +37,7 @@ Vite uses [dotenv](https://github.com/motdotla/dotenv) to load additional enviro An env file for a specific mode (e.g. `.env.production`) will take higher priority than a generic one (e.g. `.env`). -In addition, environment variables that already exist when Vite is executed have the highest priority and will not be overwritten by `.env` files. +In addition, environment variables that already exist when Vite is executed have the highest priority and will not be overwritten by `.env` files. For example, when running `VITE_SOME_KEY=123 vite build`. `.env` files are loaded at the start of Vite. Restart the server after making changes. ::: From 9ce673202d7cd3853710080f593241973c58d837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Tue, 29 Mar 2022 16:19:09 +0900 Subject: [PATCH 42/92] fix: import with query with exports/browser field (#7098) --- .../resolve/__tests__/resolve.spec.ts | 5 +- packages/playground/resolve/index.html | 3 +- packages/vite/src/node/plugins/resolve.ts | 48 +++++++++++-------- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/packages/playground/resolve/__tests__/resolve.spec.ts b/packages/playground/resolve/__tests__/resolve.spec.ts index c8c85d8df9b806..2deb2fab7f8d40 100644 --- a/packages/playground/resolve/__tests__/resolve.spec.ts +++ b/packages/playground/resolve/__tests__/resolve.spec.ts @@ -17,7 +17,10 @@ test('deep import with exports field', async () => { }) test('deep import with query with exports field', async () => { - expect(await page.textContent('.exports-deep-query')).not.toMatch('fail') + // since it is imported with `?url` it should return a url + expect(await page.textContent('.exports-deep-query')).toMatch( + isBuild ? /base64/ : '/exports-path/deep.json' + ) }) test('deep import with exports field + exposed dir', async () => { diff --git a/packages/playground/resolve/index.html b/packages/playground/resolve/index.html index 2c4ed7b9aa760c..1920ebb675d24c 100644 --- a/packages/playground/resolve/index.html +++ b/packages/playground/resolve/index.html @@ -171,10 +171,11 @@

resolve package that contains # in path

import e from 'resolve-browser-field/ext-index/index.js' import f from 'resolve-browser-field/ext-index' import g from 'resolve-browser-field/no-ext-index/index.js' // no substitution + import h from 'resolve-browser-field/no-ext?query' import { ra, rb, rc, rd, re, rf, rg } from 'resolve-browser-field/relative' - const success = [main, a, c, d, e, f, ra, rc, rd, re, rf] + const success = [main, a, c, d, e, f, h, ra, rc, rd, re, rf] const noSuccess = [b, g, rb, rg] if ( diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 686c08b12c3248..1b59503a9d43ed 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -332,23 +332,28 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { } } -function tryFsResolve( - fsPath: string, - options: InternalResolveOptions, - tryIndex = true, - targetWeb = true -): string | undefined { - let file = fsPath +function splitFileAndPostfix(path: string) { + let file = path let postfix = '' - let postfixIndex = fsPath.indexOf('?') + let postfixIndex = path.indexOf('?') if (postfixIndex < 0) { - postfixIndex = fsPath.indexOf('#') + postfixIndex = path.indexOf('#') } if (postfixIndex > 0) { - file = fsPath.slice(0, postfixIndex) - postfix = fsPath.slice(postfixIndex) + file = path.slice(0, postfixIndex) + postfix = path.slice(postfixIndex) } + return { file, postfix } +} + +function tryFsResolve( + fsPath: string, + options: InternalResolveOptions, + tryIndex = true, + targetWeb = true +): string | undefined { + const { file, postfix } = splitFileAndPostfix(fsPath) let res: string | undefined @@ -842,6 +847,7 @@ function resolveExports( if (options.conditions) { conditions.push(...options.conditions) } + return _resolveExports(pkg, key, { browser: targetWeb, require: options.isRequire, @@ -872,12 +878,14 @@ function resolveDeepImport( // map relative based on exports data if (exportsField) { if (isObject(exportsField) && !Array.isArray(exportsField)) { - relativeId = resolveExports( - data, - cleanUrl(relativeId), - options, - targetWeb - ) + // resolve without postfix (see #7098) + const { file, postfix } = splitFileAndPostfix(relativeId) + const exportsId = resolveExports(data, file, options, targetWeb) + if (exportsId !== undefined) { + relativeId = exportsId + postfix + } else { + relativeId = undefined + } } else { // not exposed relativeId = undefined @@ -889,9 +897,11 @@ function resolveDeepImport( ) } } else if (targetWeb && isObject(browserField)) { - const mapped = mapWithBrowserField(relativeId, browserField) + // resolve without postfix (see #7098) + const { file, postfix } = splitFileAndPostfix(relativeId) + const mapped = mapWithBrowserField(file, browserField) if (mapped) { - relativeId = mapped + relativeId = mapped + postfix } else if (mapped === false) { return (webResolvedImports[id] = browserExternalId) } From 2b7dad1ea1d78d7977e0569fcca4c585b4014e85 Mon Sep 17 00:00:00 2001 From: Artur Date: Tue, 29 Mar 2022 12:16:52 +0300 Subject: [PATCH 43/92] fix: make @fs URLs work with special characters (#7510) --- .../fs-serve/__tests__/fs-serve.spec.ts | 16 +++++++++++ .../playground/fs-serve/root/src/index.html | 28 +++++++++++++++++++ .../safe.json" | 3 ++ .../safe.txt" | 1 + .../src/node/server/middlewares/static.ts | 2 +- 5 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 "packages/playground/fs-serve/root/src/special characters \303\245\303\244\303\266/safe.json" create mode 100644 "packages/playground/fs-serve/root/src/special characters \303\245\303\244\303\266/safe.txt" diff --git a/packages/playground/fs-serve/__tests__/fs-serve.spec.ts b/packages/playground/fs-serve/__tests__/fs-serve.spec.ts index c618186b9bcd64..eba1e441881710 100644 --- a/packages/playground/fs-serve/__tests__/fs-serve.spec.ts +++ b/packages/playground/fs-serve/__tests__/fs-serve.spec.ts @@ -23,6 +23,15 @@ describe('main', () => { expect(await page.textContent('.safe-fetch-status')).toBe('200') }) + test('safe fetch with special characters', async () => { + expect( + await page.textContent('.safe-fetch-subdir-special-characters') + ).toMatch('KEY=safe') + expect( + await page.textContent('.safe-fetch-subdir-special-characters-status') + ).toBe('200') + }) + test('unsafe fetch', async () => { expect(await page.textContent('.unsafe-fetch')).toMatch('403 Restricted') expect(await page.textContent('.unsafe-fetch-status')).toBe('403') @@ -33,6 +42,13 @@ describe('main', () => { expect(await page.textContent('.safe-fs-fetch-status')).toBe('200') }) + test('safe fs fetch with special characters', async () => { + expect(await page.textContent('.safe-fs-fetch-special-characters')).toBe( + stringified + ) + expect(await page.textContent('.safe-fs-fetch-status')).toBe('200') + }) + test('unsafe fs fetch', async () => { expect(await page.textContent('.unsafe-fs-fetch')).toBe('') expect(await page.textContent('.unsafe-fs-fetch-status')).toBe('403') diff --git a/packages/playground/fs-serve/root/src/index.html b/packages/playground/fs-serve/root/src/index.html index 9e4f728a593a91..951e14ad2cce91 100644 --- a/packages/playground/fs-serve/root/src/index.html +++ b/packages/playground/fs-serve/root/src/index.html @@ -11,6 +11,8 @@

Safe Fetch

Safe Fetch Subdirectory


 

+

+

 
 

Unsafe Fetch


@@ -19,6 +21,8 @@ 

Unsafe Fetch

Safe /@fs/ Fetch


 

+

+

 
 

Unsafe /@fs/ Fetch


@@ -56,6 +60,16 @@ 

Denied

text('.safe-fetch-subdir', JSON.stringify(data)) }) + // inside allowed dir, with special characters, safe fetch + fetch('/src/special%20characters%20%C3%A5%C3%A4%C3%B6/safe.txt') + .then((r) => { + text('.safe-fetch-subdir-special-characters-status', r.status) + return r.text() + }) + .then((data) => { + text('.safe-fetch-subdir-special-characters', JSON.stringify(data)) + }) + // outside of allowed dir, treated as unsafe fetch('/unsafe.txt') .then((r) => { @@ -92,6 +106,20 @@

Denied

console.error(e) }) + // not imported before, inside root with special characters, treated as safe + fetch( + '/@fs/' + + ROOT + + '/root/src/special%20characters%20%C3%A5%C3%A4%C3%B6/safe.json' + ) + .then((r) => { + text('.safe-fs-fetch-special-characters-status', r.status) + return r.json() + }) + .then((data) => { + text('.safe-fs-fetch-special-characters', JSON.stringify(data)) + }) + // .env, denied by default fetch('/@fs/' + ROOT + '/root/.env') .then((r) => { diff --git "a/packages/playground/fs-serve/root/src/special characters \303\245\303\244\303\266/safe.json" "b/packages/playground/fs-serve/root/src/special characters \303\245\303\244\303\266/safe.json" new file mode 100644 index 00000000000000..84f96593c10bad --- /dev/null +++ "b/packages/playground/fs-serve/root/src/special characters \303\245\303\244\303\266/safe.json" @@ -0,0 +1,3 @@ +{ + "msg": "safe" +} diff --git "a/packages/playground/fs-serve/root/src/special characters \303\245\303\244\303\266/safe.txt" "b/packages/playground/fs-serve/root/src/special characters \303\245\303\244\303\266/safe.txt" new file mode 100644 index 00000000000000..3f3d0607101642 --- /dev/null +++ "b/packages/playground/fs-serve/root/src/special characters \303\245\303\244\303\266/safe.txt" @@ -0,0 +1 @@ +KEY=safe diff --git a/packages/vite/src/node/server/middlewares/static.ts b/packages/vite/src/node/server/middlewares/static.ts index a6623338783cc8..5fb4f7fad2e055 100644 --- a/packages/vite/src/node/server/middlewares/static.ts +++ b/packages/vite/src/node/server/middlewares/static.ts @@ -111,7 +111,7 @@ export function serveRawFsMiddleware( // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` return function viteServeRawFsMiddleware(req, res, next) { - let url = req.url! + let url = decodeURI(req.url!) // In some cases (e.g. linked monorepos) files outside of root will // reference assets that are also out of served root. In such cases // the paths are rewritten to `/@fs/` prefixed paths and must be served by From 90df0bb7c610834f875dbfdb8dd3452f44f15627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Tue, 29 Mar 2022 20:08:34 +0900 Subject: [PATCH 44/92] fix: tailwind css sourcemap warning (#7480) --- .../__tests__/build.spec.ts | 13 ++++ .../__tests__/serve.spec.ts | 13 ++++ .../playground/tailwind-sourcemap/index.html | 9 +++ .../tailwind-sourcemap/package.json | 14 ++++ .../tailwind-sourcemap/postcss.config.js | 5 ++ .../tailwind-sourcemap/tailwind.config.js | 7 ++ .../tailwind-sourcemap/tailwind.css | 3 + .../tailwind-sourcemap/vite.config.js | 11 +++ packages/vite/src/node/plugins/css.ts | 30 ++++++-- pnpm-lock.yaml | 68 ++++++++++++++++++- 10 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 packages/playground/tailwind-sourcemap/__tests__/build.spec.ts create mode 100644 packages/playground/tailwind-sourcemap/__tests__/serve.spec.ts create mode 100644 packages/playground/tailwind-sourcemap/index.html create mode 100644 packages/playground/tailwind-sourcemap/package.json create mode 100644 packages/playground/tailwind-sourcemap/postcss.config.js create mode 100644 packages/playground/tailwind-sourcemap/tailwind.config.js create mode 100644 packages/playground/tailwind-sourcemap/tailwind.css create mode 100644 packages/playground/tailwind-sourcemap/vite.config.js diff --git a/packages/playground/tailwind-sourcemap/__tests__/build.spec.ts b/packages/playground/tailwind-sourcemap/__tests__/build.spec.ts new file mode 100644 index 00000000000000..e36c1f52d2c1f8 --- /dev/null +++ b/packages/playground/tailwind-sourcemap/__tests__/build.spec.ts @@ -0,0 +1,13 @@ +import { isBuild } from 'testUtils' + +if (isBuild) { + test('should not output sourcemap warning (#4939)', () => { + serverLogs.forEach((log) => { + expect(log).not.toMatch('Sourcemap is likely to be incorrect') + }) + }) +} else { + test('this file only includes test for build', () => { + expect(true).toBe(true) + }) +} diff --git a/packages/playground/tailwind-sourcemap/__tests__/serve.spec.ts b/packages/playground/tailwind-sourcemap/__tests__/serve.spec.ts new file mode 100644 index 00000000000000..d961f75e4536e5 --- /dev/null +++ b/packages/playground/tailwind-sourcemap/__tests__/serve.spec.ts @@ -0,0 +1,13 @@ +import { isBuild } from 'testUtils' + +if (!isBuild) { + test('should not output missing source file warning', () => { + serverLogs.forEach((log) => { + expect(log).not.toMatch(/Sourcemap for .+ points to missing source files/) + }) + }) +} else { + test('this file only includes test for serve', () => { + expect(true).toBe(true) + }) +} diff --git a/packages/playground/tailwind-sourcemap/index.html b/packages/playground/tailwind-sourcemap/index.html new file mode 100644 index 00000000000000..95c8c5da7716d1 --- /dev/null +++ b/packages/playground/tailwind-sourcemap/index.html @@ -0,0 +1,9 @@ +
+

Tailwind Sourcemap

+ +

foo

+
+ + diff --git a/packages/playground/tailwind-sourcemap/package.json b/packages/playground/tailwind-sourcemap/package.json new file mode 100644 index 00000000000000..5c374f3bf47f1b --- /dev/null +++ b/packages/playground/tailwind-sourcemap/package.json @@ -0,0 +1,14 @@ +{ + "name": "test-tailwind-sourcemap", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "debug": "node --inspect-brk ../../vite/bin/vite", + "preview": "vite preview" + }, + "dependencies": { + "tailwindcss": "^3.0.23" + } +} diff --git a/packages/playground/tailwind-sourcemap/postcss.config.js b/packages/playground/tailwind-sourcemap/postcss.config.js new file mode 100644 index 00000000000000..eab3760cbc7b42 --- /dev/null +++ b/packages/playground/tailwind-sourcemap/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + tailwindcss: { config: __dirname + '/tailwind.config.js' } + } +} diff --git a/packages/playground/tailwind-sourcemap/tailwind.config.js b/packages/playground/tailwind-sourcemap/tailwind.config.js new file mode 100644 index 00000000000000..f89a536ccd742f --- /dev/null +++ b/packages/playground/tailwind-sourcemap/tailwind.config.js @@ -0,0 +1,7 @@ +module.exports = { + content: ['./index.html'], + theme: { + extend: {} + }, + plugins: [] +} diff --git a/packages/playground/tailwind-sourcemap/tailwind.css b/packages/playground/tailwind-sourcemap/tailwind.css new file mode 100644 index 00000000000000..b5c61c956711f9 --- /dev/null +++ b/packages/playground/tailwind-sourcemap/tailwind.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/packages/playground/tailwind-sourcemap/vite.config.js b/packages/playground/tailwind-sourcemap/vite.config.js new file mode 100644 index 00000000000000..70fea77247bcd4 --- /dev/null +++ b/packages/playground/tailwind-sourcemap/vite.config.js @@ -0,0 +1,11 @@ +/** + * @type {import('vite').UserConfig} + */ +module.exports = { + css: { + devSourcemap: true + }, + build: { + sourcemap: true + } +} diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 4933f8455931c1..afd9e7849b7432 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -783,7 +783,9 @@ async function compileCSS( map: { inline: false, annotation: false, - sourcesContent: false + // postcss may return virtual files + // we cannot obtain content of them, so this needs to be enabled + sourcesContent: true // when "prev: preprocessorMap", the result map may include duplicate filename in `postcssResult.map.sources` // prev: preprocessorMap, } @@ -862,19 +864,33 @@ export function formatPostcssSourceMap( file: string ): ExistingRawSourceMap { const inputFileDir = path.dirname(file) - const sources = rawMap.sources + + const sources: string[] = [] + const sourcesContent: string[] = [] + for (const [i, source] of rawMap.sources.entries()) { // remove from sources, to prevent source map to be combined incorrectly - .filter((source) => source !== '') - .map((source) => { - const cleanSource = cleanUrl(decodeURIComponent(source)) - return normalizePath(path.resolve(inputFileDir, cleanSource)) - }) + if (source === '') continue + + const cleanSource = cleanUrl(decodeURIComponent(source)) + + // postcss returns virtual files + if (/^<.+>$/.test(cleanSource)) { + sources.push(`\0${cleanSource}`) + } else { + sources.push(normalizePath(path.resolve(inputFileDir, cleanSource))) + } + + if (rawMap.sourcesContent) { + sourcesContent.push(rawMap.sourcesContent[i]) + } + } return { file, mappings: rawMap.mappings, names: rawMap.names, sources, + sourcesContent, version: rawMap.version } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b0574f04c53b86..461ca065e2e18d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -666,6 +666,12 @@ importers: devDependencies: '@vitejs/plugin-vue': link:../../plugin-vue + packages/playground/tailwind-sourcemap: + specifiers: + tailwindcss: ^3.0.23 + dependencies: + tailwindcss: 3.0.23_ts-node@10.4.0 + packages/playground/tsconfig-json: specifiers: {} @@ -3553,7 +3559,6 @@ packages: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.2 - dev: true /chownr/2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} @@ -7526,6 +7531,16 @@ packages: postcss: 8.4.5 dev: false + /postcss-js/4.0.0_postcss@8.4.12: + resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.3.3 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.12 + dev: false + /postcss-load-config/3.1.0_ts-node@10.4.0: resolution: {integrity: sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==} engines: {node: '>= 10'} @@ -7556,7 +7571,6 @@ packages: postcss: 8.4.12 ts-node: 10.4.0_44ef5af6cbbc24239b4e70b5c7b0d7a6 yaml: 1.10.2 - dev: true /postcss-modules-extract-imports/3.0.0_postcss@8.4.12: resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} @@ -7623,6 +7637,16 @@ packages: dependencies: postcss-selector-parser: 6.0.8 + /postcss-nested/5.0.6_postcss@8.4.12: + resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.12 + postcss-selector-parser: 6.0.8 + dev: false + /postcss-selector-parser/6.0.8: resolution: {integrity: sha512-D5PG53d209Z1Uhcc0qAZ5U3t5HagH3cxu+WLZ22jt3gLUpXM4eXXfiO14jiDWST3NNooX/E8wISfOhZ9eIjGTQ==} engines: {node: '>=4'} @@ -7630,6 +7654,14 @@ packages: cssesc: 3.0.0 util-deprecate: 1.0.2 + /postcss-selector-parser/6.0.9: + resolution: {integrity: sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: false + /postcss-value-parser/3.3.1: resolution: {integrity: sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==} dev: false @@ -8861,6 +8893,38 @@ packages: - ts-node dev: false + /tailwindcss/3.0.23_ts-node@10.4.0: + resolution: {integrity: sha512-+OZOV9ubyQ6oI2BXEhzw4HrqvgcARY38xv3zKcjnWtMIZstEsXdI9xftd1iB7+RbOnj2HOEzkA0OyB5BaSxPQA==} + engines: {node: '>=12.13.0'} + hasBin: true + peerDependencies: + autoprefixer: ^10.0.2 + dependencies: + arg: 5.0.1 + chalk: 4.1.2 + chokidar: 3.5.3 + color-name: 1.1.4 + cosmiconfig: 7.0.1 + detective: 5.2.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.2.11 + glob-parent: 6.0.2 + is-glob: 4.0.3 + normalize-path: 3.0.0 + object-hash: 2.2.0 + postcss: 8.4.12 + postcss-js: 4.0.0_postcss@8.4.12 + postcss-load-config: 3.1.3_postcss@8.4.12+ts-node@10.4.0 + postcss-nested: 5.0.6_postcss@8.4.12 + postcss-selector-parser: 6.0.9 + postcss-value-parser: 4.2.0 + quick-lru: 5.1.1 + resolve: 1.22.0 + transitivePeerDependencies: + - ts-node + dev: false + /tar/6.1.11: resolution: {integrity: sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==} engines: {node: '>= 10'} From 4d5521854c0b7213ec7d061dedd7f34e54919944 Mon Sep 17 00:00:00 2001 From: ygj6 <7699524+ygj6@users.noreply.github.com> Date: Tue, 29 Mar 2022 20:44:59 +0800 Subject: [PATCH 45/92] docs: declare the scope of define expressions (#7511) --- docs/config/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/config/index.md b/docs/config/index.md index f9a1a81dc77dbf..9fc9273c691f56 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -156,6 +156,8 @@ export default defineConfig(({ command, mode }) => { - Starting from `2.0.0-beta.70`, string values will be used as raw expressions, so if defining a string constant, it needs to be explicitly quoted (e.g. with `JSON.stringify`). + - To be consistent with [esbuild behavior](https://esbuild.github.io/api/#define), expressions must either be a JSON object (null, boolean, number, string, array, or object) or a single identifier. + - Replacements are performed only when the match is surrounded by word boundaries (`\b`). ::: warning From 9481c7d1dca1155f73d21e913bd97ddd8d92a428 Mon Sep 17 00:00:00 2001 From: yoho Date: Tue, 29 Mar 2022 23:00:41 +0800 Subject: [PATCH 46/92] fix: worker match only run in js (#7500) --- packages/playground/vue/Main.vue | 3 ++- packages/playground/vue/__tests__/vue.spec.ts | 6 ++++++ packages/playground/vue/worker.vue | 17 +++++++++++++++++ packages/playground/vue/workerTest.js | 1 + packages/playground/worker/index.html | 5 +++++ packages/vite/src/node/build.ts | 2 -- .../src/node/plugins/assetImportMetaUrl.ts | 19 +++++++++++++++---- packages/vite/src/node/plugins/index.ts | 5 +++-- .../src/node/plugins/workerImportMetaUrl.ts | 16 +++++++++++++--- packages/vite/src/node/utils.ts | 1 + 10 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 packages/playground/vue/worker.vue create mode 100644 packages/playground/vue/workerTest.js diff --git a/packages/playground/vue/Main.vue b/packages/playground/vue/Main.vue index d10ae401f7aa8e..87319acdf6f736 100644 --- a/packages/playground/vue/Main.vue +++ b/packages/playground/vue/Main.vue @@ -20,6 +20,7 @@ + diff --git a/packages/playground/vue/workerTest.js b/packages/playground/vue/workerTest.js new file mode 100644 index 00000000000000..fcde5e19b30677 --- /dev/null +++ b/packages/playground/vue/workerTest.js @@ -0,0 +1 @@ +self.postMessage('worker load!') diff --git a/packages/playground/worker/index.html b/packages/playground/worker/index.html index 60289ff84d6a06..fb4b3e9e85bfc8 100644 --- a/packages/playground/worker/index.html +++ b/packages/playground/worker/index.html @@ -1,3 +1,8 @@ +

worker template error match:

+ + const worker = new Worker(new URL('./worker.js', import.meta.url)) + +

format iife:

Expected values:
diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 6b4d38836b6c51..d898b090fcdf6c 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -19,7 +19,6 @@ import type { } from 'rollup' import type Rollup from 'rollup' import { buildReporterPlugin } from './plugins/reporter' -import { buildHtmlPlugin } from './plugins/html' import { buildEsbuildPlugin } from './plugins/esbuild' import { terserPlugin } from './plugins/terser' import type { Terser } from 'types/terser' @@ -310,7 +309,6 @@ export function resolveBuildPlugins(config: ResolvedConfig): { return { pre: [ watchPackageDataPlugin(config), - buildHtmlPlugin(config), commonjsPlugin(options.commonjsOptions), dataURIPlugin(), dynamicImportVars(options.dynamicImportVarsOptions), diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index b0c59bed808604..a3f8e441b0f933 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -3,7 +3,12 @@ import MagicString from 'magic-string' import path from 'path' import { fileToUrl } from './asset' import type { ResolvedConfig } from '../config' -import { multilineCommentsRE, singlelineCommentsRE } from '../utils' +import { + multilineCommentsRE, + singlelineCommentsRE, + stringsRE, + blankReplacer +} from '../utils' /** * Convert `new URL('./foo.png', import.meta.url)` to its resolved built URL @@ -27,12 +32,18 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { const importMetaUrlRE = /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*,?\s*\)/g const noCommentsCode = code - .replace(multilineCommentsRE, (m) => ' '.repeat(m.length)) - .replace(singlelineCommentsRE, (m) => ' '.repeat(m.length)) + .replace(multilineCommentsRE, blankReplacer) + .replace(singlelineCommentsRE, blankReplacer) + .replace(stringsRE, (m) => `'${'\0'.repeat(m.length - 2)}'`) + let s: MagicString | null = null let match: RegExpExecArray | null while ((match = importMetaUrlRE.exec(noCommentsCode))) { - const { 0: exp, 1: rawUrl, index } = match + const { 0: exp, 1: emptyUrl, index } = match + + const urlStart = exp.indexOf(emptyUrl) + index + const urlEnd = urlStart + emptyUrl.length + const rawUrl = code.slice(urlStart, urlEnd) if (!s) s = new MagicString(code) diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index 825798a73c8493..d294233b92ae17 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -9,7 +9,7 @@ import { importAnalysisPlugin } from './importAnalysis' import { cssPlugin, cssPostPlugin } from './css' import { assetPlugin } from './asset' import { clientInjectionsPlugin } from './clientInjections' -import { htmlInlineProxyPlugin } from './html' +import { buildHtmlPlugin, htmlInlineProxyPlugin } from './html' import { wasmPlugin } from './wasm' import { modulePreloadPolyfillPlugin } from './modulePreloadPolyfill' import { webWorkerPlugin } from './worker' @@ -61,12 +61,13 @@ export async function resolvePlugins( ), wasmPlugin(config), webWorkerPlugin(config), - workerImportMetaUrlPlugin(config), assetPlugin(config), ...normalPlugins, definePlugin(config), cssPostPlugin(config), config.build.ssr ? ssrRequireHookPlugin(config) : null, + isBuild && buildHtmlPlugin(config), + workerImportMetaUrlPlugin(config), ...buildPlugins.pre, ...postPlugins, ...buildPlugins.post, diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index 233d83d066bcb7..4b5711cf3a6248 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -7,7 +7,8 @@ import { cleanUrl, injectQuery, multilineCommentsRE, - singlelineCommentsRE + singlelineCommentsRE, + stringsRE } from '../utils' import path from 'path' import { bundleWorkerEntry } from './worker' @@ -122,12 +123,21 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { const noCommentsCode = code .replace(multilineCommentsRE, blankReplacer) .replace(singlelineCommentsRE, blankReplacer) + + const noStringCode = noCommentsCode.replace( + stringsRE, + (m) => `'${' '.repeat(m.length - 2)}'` + ) let match: RegExpExecArray | null let s: MagicString | null = null - while ((match = importMetaUrlRE.exec(noCommentsCode))) { - const { 0: allExp, 2: exp, 3: rawUrl, index } = match + while ((match = importMetaUrlRE.exec(noStringCode))) { + const { 0: allExp, 2: exp, 3: emptyUrl, index } = match const urlIndex = allExp.indexOf(exp) + index + const urlStart = allExp.indexOf(emptyUrl) + index + const urlEnd = urlStart + emptyUrl.length + const rawUrl = code.slice(urlStart, urlEnd) + if (options?.ssr) { this.error( `\`new URL(url, import.meta.url)\` is not supported in SSR.`, diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index edb3410751868a..0e3fe49c9cb584 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -732,3 +732,4 @@ export function parseRequest(id: string): Record | null { } export const blankReplacer = (match: string) => ' '.repeat(match.length) +export const stringsRE = /"[^"]*"|'[^']*'|`[^`]*`/g From b16b8964e633cbf1ee2b051044af2660f1636558 Mon Sep 17 00:00:00 2001 From: Dany Castillo <31006608+dcastil@users.noreply.github.com> Date: Tue, 29 Mar 2022 18:05:54 +0200 Subject: [PATCH 47/92] docs: add wildcard to suggested .gitignore entry in docs (#7512) --- docs/guide/env-and-mode.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/env-and-mode.md b/docs/guide/env-and-mode.md index a2dee2b0d9d76e..1649feda8c7501 100644 --- a/docs/guide/env-and-mode.md +++ b/docs/guide/env-and-mode.md @@ -57,7 +57,7 @@ If you want to customize env variables prefix, see [envPrefix](/config/index#env :::warning SECURITY NOTES -- `.env.*.local` files are local-only and can contain sensitive variables. You should add `.local` to your `.gitignore` to avoid them being checked into git. +- `.env.*.local` files are local-only and can contain sensitive variables. You should add `*.local` to your `.gitignore` to avoid them being checked into git. - Since any variables exposed to your Vite source code will end up in your client bundle, `VITE_*` variables should _not_ contain any sensitive information. ::: From f05a81387b6901622c86c2833f42420b8e18e95e Mon Sep 17 00:00:00 2001 From: yoho Date: Wed, 30 Mar 2022 00:11:54 +0800 Subject: [PATCH 48/92] fix: import url worker two times (#7468) --- .../worker/__tests__/es/es-worker.spec.ts | 4 +- .../worker/emit-chunk-nested-worker.js | 25 +++- .../worker/emit-chunk-sub-worker.js | 14 +- packages/playground/worker/index.html | 10 +- .../playground/worker/module-and-worker.js | 5 + .../worker/worker/main-format-es.js | 20 ++- .../playground/worker/worker/main-module.js | 1 + packages/vite/src/node/plugins/define.ts | 8 +- packages/vite/src/node/plugins/worker.ts | 127 +++++++++++++++--- .../src/node/plugins/workerImportMetaUrl.ts | 17 +-- 10 files changed, 181 insertions(+), 50 deletions(-) create mode 100644 packages/playground/worker/module-and-worker.js diff --git a/packages/playground/worker/__tests__/es/es-worker.spec.ts b/packages/playground/worker/__tests__/es/es-worker.spec.ts index 51497a0f5ebadd..c7fd0d6c19e4bc 100644 --- a/packages/playground/worker/__tests__/es/es-worker.spec.ts +++ b/packages/playground/worker/__tests__/es/es-worker.spec.ts @@ -60,7 +60,7 @@ if (isBuild) { // assert correct files test('inlined code generation', async () => { const files = fs.readdirSync(assetsDir) - expect(files.length).toBe(20) + expect(files.length).toBe(22) 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')) @@ -94,7 +94,7 @@ test('classic worker', async () => { test('emit chunk', async () => { expect(await page.textContent('.emti-chunk-worker')).toMatch( - '{"msg1":"module1","msg2":"module2","msg3":"module3"}' + '["A string",{"type":"emit-chunk-sub-worker","data":"A string"},{"type":"module-and-worker:worker","data":"A string"},{"type":"module-and-worker:module","data":"module and worker"},{"type":"emit-chunk-sub-worker","data":{"module":"module and worker","msg1":"module1","msg2":"module2","msg3":"module3"}}]' ) expect(await page.textContent('.emti-chunk-dynamic-import-worker')).toMatch( '"A string/es/"' diff --git a/packages/playground/worker/emit-chunk-nested-worker.js b/packages/playground/worker/emit-chunk-nested-worker.js index dff0f5bc64c5ad..6cb72b9488cfaf 100644 --- a/packages/playground/worker/emit-chunk-nested-worker.js +++ b/packages/playground/worker/emit-chunk-nested-worker.js @@ -1,7 +1,28 @@ import SubWorker from './emit-chunk-sub-worker?worker' - const subWorker = new SubWorker() subWorker.onmessage = (event) => { - self.postMessage(event.data) + self.postMessage({ + type: 'emit-chunk-sub-worker', + data: event.data + }) } + +const moduleWorker = new Worker( + new URL('./module-and-worker.js', import.meta.url), + { type: 'module' } +) + +moduleWorker.onmessage = (event) => { + self.postMessage({ + type: 'module-and-worker:worker', + data: event.data + }) +} + +import('./module-and-worker').then((res) => { + self.postMessage({ + type: 'module-and-worker:module', + data: res.module + }) +}) diff --git a/packages/playground/worker/emit-chunk-sub-worker.js b/packages/playground/worker/emit-chunk-sub-worker.js index bd6b1f6e4f7419..5d20becc781dd7 100644 --- a/packages/playground/worker/emit-chunk-sub-worker.js +++ b/packages/playground/worker/emit-chunk-sub-worker.js @@ -1,6 +1,8 @@ -Promise.all([import('./modules/module2'), import('./modules/module3')]).then( - (data) => { - const _data = { ...data[0], ...data[1] } - self.postMessage(_data) - } -) +Promise.all([ + import('./module-and-worker'), + import('./modules/module2'), + import('./modules/module3') +]).then((data) => { + const _data = { ...data[0], ...data[1], ...data[2] } + self.postMessage(_data) +}) diff --git a/packages/playground/worker/index.html b/packages/playground/worker/index.html index fb4b3e9e85bfc8..aec3995b60a06e 100644 --- a/packages/playground/worker/index.html +++ b/packages/playground/worker/index.html @@ -62,7 +62,9 @@

format iife:

- worker emit chunk + worker emit chunk
+ module and worker:worker in worker file
+ module and worker:module in worker file
.emti-chunk-worker

@@ -73,6 +75,12 @@

+

+ module and worker:worker in simple file + .module-and-worker-worker +

+ +