From 4ab16591215578b6c0ff2a6fe7725efa5f1f5fda Mon Sep 17 00:00:00 2001
From: sapphi-red <49056869+sapphi-red@users.noreply.github.com>
Date: Thu, 12 Oct 2023 14:16:52 +0900
Subject: [PATCH] fix(html): inline style attribute not working in dev
---
packages/vite/src/node/plugins/css.ts | 32 ++++++++-------
packages/vite/src/node/plugins/html.ts | 36 ++++++++++-------
.../src/node/server/middlewares/indexHtml.ts | 39 +++++++++++++++++--
playground/assets/__tests__/assets.spec.ts | 5 +--
4 files changed, 76 insertions(+), 36 deletions(-)
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 b879d4066874a6..0488c9bb185ee9 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 c9d9228a0d79ef..b17f045ada57a4 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 {
@@ -177,6 +186,7 @@ const devHtmlHook: IndexHtmlTransformHook = async (
'',
)
const styleUrl: AssetNode[] = []
+ const inlineStyles: InlineStyleAttribute[] = []
const addInlineModule = (
node: DefaultTreeAdapterMap['element'],
@@ -243,6 +253,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({
@@ -271,8 +291,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
@@ -297,7 +317,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 43a93a19276440..7c7ea779fca0b2 100644
--- a/playground/assets/__tests__/assets.spec.ts
+++ b/playground/assets/__tests__/assets.spec.ts
@@ -462,10 +462,7 @@ test('url() contains file in publicDir, in