diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index aea96d14d758be..a6d74e5dfd46fc 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -58,6 +58,7 @@ export type { HtmlTagDescriptor } from './plugins/html' export type { CSSOptions, CSSModulesOptions } from './plugins/css' +export type { ChunkMetadata } from './plugins/metadata' export type { JsonOptions } from './plugins/json' export type { TransformOptions as EsbuildTransformOptions } from 'esbuild' export type { ESBuildOptions, ESBuildTransformResult } from './plugins/esbuild' @@ -95,3 +96,11 @@ export type { Terser } from 'types/terser' export type { RollupCommonJSOptions } from 'types/commonjs' export type { RollupDynamicImportVarsOptions } from 'types/dynamicImportVars' export type { Matcher, AnymatchPattern, AnymatchFn } from 'types/anymatch' + +import type { ChunkMetadata } from './plugins/metadata' + +declare module 'rollup' { + export interface RenderedChunk { + viteMetadata: ChunkMetadata + } +} diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 3c7681ad6f0bb8..d3426094f32e53 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -6,7 +6,7 @@ import type { Plugin } from '../plugin' import type { ResolvedConfig } from '../config' import { cleanUrl } from '../utils' import { FS_PREFIX } from '../constants' -import type { OutputOptions, PluginContext, RenderedChunk } from 'rollup' +import type { OutputOptions, PluginContext } from 'rollup' import MagicString from 'magic-string' import { createHash } from 'crypto' import { normalizePath } from '../utils' @@ -16,8 +16,6 @@ export const assetUrlRE = /__VITE_ASSET__([a-z\d]{8})__(?:\$_(.*?)__)?/g const rawRE = /(\?|&)raw(?:&|$)/ const urlRE = /(\?|&)url(?:&|$)/ -export const chunkToEmittedAssetsMap = new WeakMap>() - const assetCache = new WeakMap>() const assetHashToFilenameMap = new WeakMap< @@ -100,7 +98,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin { // some internal plugins may still need to emit chunks (e.g. worker) so // fallback to this.getFileName for that. const file = getAssetFilename(hash, config) || this.getFileName(hash) - registerAssetToChunk(chunk, file) + chunk.viteMetadata.importedAssets.add(cleanUrl(file)) const outputFilepath = config.base + file + postfix s.overwrite(match.index, match.index + full.length, outputFilepath) } @@ -131,15 +129,6 @@ export function assetPlugin(config: ResolvedConfig): Plugin { } } -export function registerAssetToChunk(chunk: RenderedChunk, file: string): void { - let emitted = chunkToEmittedAssetsMap.get(chunk) - if (!emitted) { - emitted = new Set() - chunkToEmittedAssetsMap.set(chunk, emitted) - } - emitted.add(cleanUrl(file)) -} - export function checkPublicFile( url: string, { publicDir }: ResolvedConfig diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index cb62b785732e0d..85eec565ebb86a 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -30,7 +30,6 @@ import type { ResolveFn, ViteDevServer } from '../' import { getAssetFilename, assetUrlRE, - registerAssetToChunk, fileToUrl, checkPublicFile } from './asset' @@ -120,11 +119,6 @@ const cssModulesCache = new WeakMap< Map> >() -export const chunkToEmittedCssFileMap = new WeakMap< - RenderedChunk, - Set ->() - export const removedPureCssFilesCache = new WeakMap< ResolvedConfig, Map @@ -390,7 +384,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { const isRelativeBase = config.base === '' || config.base.startsWith('.') css = css.replace(assetUrlRE, (_, fileHash, postfix = '') => { const filename = getAssetFilename(fileHash, config) + postfix - registerAssetToChunk(chunk, filename) + chunk.viteMetadata.importedAssets.add(cleanUrl(filename)) if (!isRelativeBase || inlined) { // absolute base or relative base but inlined (injected as style tag into // index.html) use the base as-is @@ -427,10 +421,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { type: 'asset', source: chunkCSS }) - chunkToEmittedCssFileMap.set( - chunk, - new Set([this.getFileName(fileHandle)]) - ) + chunk.viteMetadata.importedCss.add(this.getFileName(fileHandle)) } else if (!config.build.ssr) { // legacy build, inline css chunkCSS = await processChunkCSS(chunkCSS, { @@ -493,17 +484,12 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { // chunks instead. chunk.imports = chunk.imports.filter((file) => { if (pureCssChunks.has(file)) { - const css = chunkToEmittedCssFileMap.get( - bundle[file] as OutputChunk + const { + viteMetadata: { importedCss } + } = bundle[file] as OutputChunk + importedCss.forEach((file) => + chunk.viteMetadata.importedCss.add(file) ) - if (css) { - let existing = chunkToEmittedCssFileMap.get(chunk) - if (!existing) { - existing = new Set() - } - css.forEach((file) => existing!.add(file)) - chunkToEmittedCssFileMap.set(chunk, existing) - } return false } return true diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 7f8a750c72bf22..f2fd4aca98ff2f 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -24,7 +24,7 @@ import { urlToBuiltUrl, getAssetFilename } from './asset' -import { isCSSRequest, chunkToEmittedCssFileMap } from './css' +import { isCSSRequest } from './css' import { modulePreloadPolyfillId } from './modulePreloadPolyfill' import type { AttributeNode, @@ -511,21 +511,19 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { }) } - const cssFiles = chunkToEmittedCssFileMap.get(chunk) - if (cssFiles) { - cssFiles.forEach((file) => { - if (!seen.has(file)) { - seen.add(file) - tags.push({ - tag: 'link', - attrs: { - rel: 'stylesheet', - href: toPublicPath(file, config) - } - }) - } - }) - } + chunk.viteMetadata.importedCss.forEach((file) => { + if (!seen.has(file)) { + seen.add(file) + tags.push({ + tag: 'link', + attrs: { + rel: 'stylesheet', + href: toPublicPath(file, config) + } + }) + } + }) + return tags } diff --git a/packages/vite/src/node/plugins/importAnalysisBuild.ts b/packages/vite/src/node/plugins/importAnalysisBuild.ts index d816d4c1995fd8..41d9671a853cdd 100644 --- a/packages/vite/src/node/plugins/importAnalysisBuild.ts +++ b/packages/vite/src/node/plugins/importAnalysisBuild.ts @@ -5,11 +5,7 @@ import MagicString from 'magic-string' import type { ImportSpecifier } from 'es-module-lexer' import { init, parse as parseImports } from 'es-module-lexer' import type { OutputChunk } from 'rollup' -import { - chunkToEmittedCssFileMap, - isCSSRequest, - removedPureCssFilesCache -} from './css' +import { isCSSRequest, removedPureCssFilesCache } from './css' import { transformImportGlob } from '../importGlob' import { bareImportRE } from '../utils' @@ -283,21 +279,17 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { const chunk = bundle[filename] as OutputChunk | undefined if (chunk) { deps.add(chunk.fileName) - const cssFiles = chunkToEmittedCssFileMap.get(chunk) - if (cssFiles) { - cssFiles.forEach((file) => { - deps.add(file) - }) - } + chunk.viteMetadata.importedCss.forEach((file) => { + deps.add(file) + }) chunk.imports.forEach(addDeps) } else { const removedPureCssFiles = removedPureCssFilesCache.get(config)! const chunk = removedPureCssFiles.get(filename) if (chunk) { - const cssFiles = chunkToEmittedCssFileMap.get(chunk) - if (cssFiles && cssFiles.size > 0) { - cssFiles.forEach((file) => { + if (chunk.viteMetadata.importedCss.size) { + chunk.viteMetadata.importedCss.forEach((file) => { deps.add(file) }) hasRemovedPureCssChunk = true diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index e4f3d453527af3..5213714febb12b 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -16,6 +16,7 @@ import { preAliasPlugin } from './preAlias' import { definePlugin } from './define' import { ssrRequireHookPlugin } from './ssrRequireHook' import { workerImportMetaUrlPlugin } from './workerImportMetaUrl' +import { metadataPlugin } from './metadata' export async function resolvePlugins( config: ResolvedConfig, @@ -30,6 +31,7 @@ export async function resolvePlugins( : { pre: [], post: [] } return [ + isBuild ? metadataPlugin() : null, isBuild ? null : preAliasPlugin(), aliasPlugin({ entries: config.resolve.alias }), ...prePlugins, diff --git a/packages/vite/src/node/plugins/manifest.ts b/packages/vite/src/node/plugins/manifest.ts index d6dceb507ca946..02fbdce63eec78 100644 --- a/packages/vite/src/node/plugins/manifest.ts +++ b/packages/vite/src/node/plugins/manifest.ts @@ -2,8 +2,6 @@ import path from 'path' import type { OutputChunk } from 'rollup' import type { ResolvedConfig } from '..' import type { Plugin } from '../plugin' -import { chunkToEmittedCssFileMap } from './css' -import { chunkToEmittedAssetsMap } from './asset' import { normalizePath } from '../utils' export type Manifest = Record @@ -90,13 +88,12 @@ export function manifestPlugin(config: ResolvedConfig): Plugin { } } - const cssFiles = chunkToEmittedCssFileMap.get(chunk) - if (cssFiles) { - manifestChunk.css = [...cssFiles] + if (chunk.viteMetadata.importedCss.size) { + manifestChunk.css = [...chunk.viteMetadata.importedCss] + } + if (chunk.viteMetadata.importedAssets.size) { + manifestChunk.assets = [...chunk.viteMetadata.importedAssets] } - - const assets = chunkToEmittedAssetsMap.get(chunk) - if (assets) [(manifestChunk.assets = [...assets])] return manifestChunk } diff --git a/packages/vite/src/node/plugins/metadata.ts b/packages/vite/src/node/plugins/metadata.ts new file mode 100644 index 00000000000000..2b617aca4c89c8 --- /dev/null +++ b/packages/vite/src/node/plugins/metadata.ts @@ -0,0 +1,23 @@ +import type { Plugin } from '../plugin' + +export interface ChunkMetadata { + importedAssets: Set + importedCss: Set +} + +/** + * Prepares the rendered chunks to contain additional metadata during build. + */ +export function metadataPlugin(): Plugin { + return { + name: 'vite:build-metadata', + + async renderChunk(_code, chunk) { + chunk.viteMetadata = { + importedAssets: new Set(), + importedCss: new Set() + } + return null + } + } +} diff --git a/packages/vite/src/node/ssr/ssrManifestPlugin.ts b/packages/vite/src/node/ssr/ssrManifestPlugin.ts index 351c91349a3ca3..1e00853402ba5d 100644 --- a/packages/vite/src/node/ssr/ssrManifestPlugin.ts +++ b/packages/vite/src/node/ssr/ssrManifestPlugin.ts @@ -4,8 +4,6 @@ import type { ImportSpecifier } from 'es-module-lexer' import type { OutputChunk } from 'rollup' import type { ResolvedConfig } from '..' import type { Plugin } from '../plugin' -import { chunkToEmittedCssFileMap } from '../plugins/css' -import { chunkToEmittedAssetsMap } from '../plugins/asset' import { preloadMethod } from '../plugins/importAnalysisBuild' import { normalizePath } from '../utils' @@ -20,29 +18,21 @@ export function ssrManifestPlugin(config: ResolvedConfig): Plugin { for (const file in bundle) { const chunk = bundle[file] if (chunk.type === 'chunk') { - // links for certain entry chunks are already generated in static HTML - // in those cases we only need to record info for non-entry chunks - const cssFiles = chunk.isEntry - ? null - : chunkToEmittedCssFileMap.get(chunk) - const assetFiles = chunkToEmittedAssetsMap.get(chunk) for (const id in chunk.modules) { const normalizedId = normalizePath(relative(config.root, id)) const mappedChunks = ssrManifest[normalizedId] ?? (ssrManifest[normalizedId] = []) if (!chunk.isEntry) { mappedChunks.push(base + chunk.fileName) - } - if (cssFiles) { - cssFiles.forEach((file) => { - mappedChunks.push(base + file) - }) - } - if (assetFiles) { - assetFiles.forEach((file) => { + // tags for entry chunks are already generated in static HTML, + // so we only need to record info for non-entry chunks. + chunk.viteMetadata.importedCss.forEach((file) => { mappedChunks.push(base + file) }) } + chunk.viteMetadata.importedAssets.forEach((file) => { + mappedChunks.push(base + file) + }) } if (chunk.code.includes(preloadMethod)) { // generate css deps map @@ -74,12 +64,9 @@ export function ssrManifestPlugin(config: ResolvedConfig): Plugin { analyzed.add(filename) const chunk = bundle[filename] as OutputChunk | undefined if (chunk) { - const cssFiles = chunkToEmittedCssFileMap.get(chunk) - if (cssFiles) { - cssFiles.forEach((file) => { - deps.push(`/${file}`) - }) - } + chunk.viteMetadata.importedCss.forEach((file) => { + deps.push(`/${file}`) + }) chunk.imports.forEach(addDeps) } }