diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 133370be37036d..c7ef8e6608fac6 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -423,6 +423,23 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { css = stripBomTag(css) + // cache css compile result to map + // and then use the cache replace inline-style-flag + // when `generateBundle` in vite:build-html plugin and devHtmlHook + const inlineCSS = inlineCSSRE.test(id) + const isHTMLProxy = htmlProxyRE.test(id) + if (inlineCSS && isHTMLProxy) { + const query = parseRequest(id) + if (styleAttrRE.test(id)) { + css = css.replace(/"/g, '"') + } + addToHTMLProxyTransformResult( + `${getHash(cleanUrl(id))}_${Number.parseInt(query!.index)}`, + css, + ) + return `export default ''` + } + const inlined = inlineRE.test(id) const modules = cssModulesCache.get(config)!.get(id) @@ -475,21 +492,6 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { // build CSS handling ---------------------------------------------------- // record css - // cache css compile result to map - // and then use the cache replace inline-style-flag when `generateBundle` in vite:build-html plugin - const inlineCSS = inlineCSSRE.test(id) - const isHTMLProxy = htmlProxyRE.test(id) - const query = parseRequest(id) - if (inlineCSS && isHTMLProxy) { - if (styleAttrRE.test(id)) { - css = css.replace(/"/g, '"') - } - addToHTMLProxyTransformResult( - `${getHash(cleanUrl(id))}_${Number.parseInt(query!.index)}`, - css, - ) - return `export default ''` - } if (!inlined) { styles.set(id, css) } diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 3620bfbfd850cb..a132654a5b4e60 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -497,31 +497,22 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { } } } - // - // extract inline styles as virtual css and add class attribute to tag for selecting - const inlineStyle = node.attrs.find( - (prop) => - prop.prefix === undefined && - prop.name === 'style' && - // only url(...) or image-set(...) in css need to emit file - (prop.value.includes('url(') || - prop.value.includes('image-set(')), - ) + + const inlineStyle = findNeedTransformStyleAttribute(node) if (inlineStyle) { inlineModuleIndex++ - // replace `inline style` to class + // replace `inline style` with __VITE_INLINE_CSS__**_**__ // and import css in js code - const code = inlineStyle.value + const code = inlineStyle.attr.value const filePath = id.replace(normalizePath(config.root), '') addToHTMLProxyCache(config, filePath, inlineModuleIndex, { code }) // will transform with css plugin and cache result with css-post plugin js += `\nimport "${id}?html-proxy&inline-css&style-attr&index=${inlineModuleIndex}.css"` const hash = getHash(cleanUrl(id)) // will transform in `applyHtmlTransforms` - const sourceCodeLocation = node.sourceCodeLocation!.attrs!['style'] overwriteAttrValue( s, - sourceCodeLocation, + inlineStyle.location!, `__VITE_INLINE_CSS__${hash}_${inlineModuleIndex}__`, ) } @@ -881,6 +872,23 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { } } +// +// extract inline styles as virtual css +export function findNeedTransformStyleAttribute( + node: DefaultTreeAdapterMap['element'], +): { attr: Token.Attribute; location?: Token.Location } | undefined { + const attr = node.attrs.find( + (prop) => + prop.prefix === undefined && + prop.name === 'style' && + // only url(...) or image-set(...) in css need to emit file + (prop.value.includes('url(') || prop.value.includes('image-set(')), + ) + if (!attr) return undefined + const location = node.sourceCodeLocation?.attrs?.['style'] + return { attr, location } +} + export interface HtmlTagDescriptor { tag: string attrs?: Record diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 96d05c418c1ae1..6cbaf727db4cfb 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -10,9 +10,11 @@ import { addToHTMLProxyCache, applyHtmlTransforms, assetAttrsConfig, + findNeedTransformStyleAttribute, getAttrKey, getScriptInfo, htmlEnvHook, + htmlProxyResult, nodeIsElement, overwriteAttrValue, postImportMapHook, @@ -27,6 +29,7 @@ import { cleanUrl, ensureWatchedFile, fsPathFromId, + getHash, injectQuery, isJSRequest, joinUrlSegments, @@ -48,6 +51,12 @@ interface AssetNode { code: string } +interface InlineStyleAttribute { + index: number + location: Token.Location + code: string +} + export function createDevHtmlTransformFn( server: ViteDevServer, ): (url: string, html: string, originalUrl: string) => Promise { @@ -179,6 +188,7 @@ const devHtmlHook: IndexHtmlTransformHook = async ( '', ) const styleUrl: AssetNode[] = [] + const inlineStyles: InlineStyleAttribute[] = [] const addInlineModule = ( node: DefaultTreeAdapterMap['element'], @@ -245,6 +255,16 @@ const devHtmlHook: IndexHtmlTransformHook = async ( } } + const inlineStyle = findNeedTransformStyleAttribute(node) + if (inlineStyle) { + inlineModuleIndex++ + inlineStyles.push({ + index: inlineModuleIndex, + location: inlineStyle.location!, + code: inlineStyle.attr.value, + }) + } + if (node.nodeName === 'style' && node.childNodes.length) { const children = node.childNodes[0] as DefaultTreeAdapterMap['textNode'] styleUrl.push({ @@ -273,8 +293,8 @@ const devHtmlHook: IndexHtmlTransformHook = async ( } }) - await Promise.all( - styleUrl.map(async ({ start, end, code }, index) => { + await Promise.all([ + ...styleUrl.map(async ({ start, end, code }, index) => { const url = `${proxyModulePath}?html-proxy&direct&index=${index}.css` // ensure module in graph after successful load @@ -299,7 +319,20 @@ const devHtmlHook: IndexHtmlTransformHook = async ( } s.overwrite(start, end, content) }), - ) + ...inlineStyles.map(async ({ index, location, code }) => { + // will transform with css plugin and cache result with css-post plugin + const url = `${proxyModulePath}?html-proxy&inline-css&style-attr&index=${index}.css` + + const mod = await moduleGraph.ensureEntryFromUrl(url, false) + ensureWatchedFile(watcher, mod.file, config.root) + + await server?.pluginContainer.transform(code, mod.id!) + + const hash = getHash(cleanUrl(mod.id!)) + const result = htmlProxyResult.get(`${hash}_${index}`) + overwriteAttrValue(s, location, result ?? '') + }), + ]) html = s.toString() diff --git a/playground/assets/__tests__/assets.spec.ts b/playground/assets/__tests__/assets.spec.ts index 77a0ca11c6c810..70d0fcfe4e8e54 100644 --- a/playground/assets/__tests__/assets.spec.ts +++ b/playground/assets/__tests__/assets.spec.ts @@ -467,10 +467,7 @@ test('url() contains file in publicDir, in