From ea47b8fa51d958bc3fa9d27dc23c0bae2b6066d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Thu, 12 Oct 2023 20:08:47 +0900 Subject: [PATCH] fix(html): import expression in classic script for dev (#14595) --- packages/vite/src/node/plugins/html.ts | 42 +++++++----- .../src/node/server/middlewares/indexHtml.ts | 67 +++++++++++++------ playground/vitestSetup.ts | 9 ++- 3 files changed, 77 insertions(+), 41 deletions(-) diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index a132654a5b4e60..0657539c516e85 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -432,22 +432,9 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { } else if (node.childNodes.length) { const scriptNode = node.childNodes.pop() as DefaultTreeAdapterMap['textNode'] - const cleanCode = stripLiteral(scriptNode.value) - - let match: RegExpExecArray | null - inlineImportRE.lastIndex = 0 - while ((match = inlineImportRE.exec(cleanCode))) { - const { 1: url, index } = match - const startUrl = cleanCode.indexOf(url, index) - const start = startUrl + 1 - const end = start + url.length - 2 - const startOffset = scriptNode.sourceCodeLocation!.startOffset - scriptUrls.push({ - start: start + startOffset, - end: end + startOffset, - url: scriptNode.value.slice(start, end), - }) - } + scriptUrls.push( + ...extractImportExpressionFromClassicScript(scriptNode), + ) } } @@ -889,6 +876,29 @@ export function findNeedTransformStyleAttribute( return { attr, location } } +export function extractImportExpressionFromClassicScript( + scriptTextNode: DefaultTreeAdapterMap['textNode'], +): ScriptAssetsUrl[] { + const startOffset = scriptTextNode.sourceCodeLocation!.startOffset + const cleanCode = stripLiteral(scriptTextNode.value) + + const scriptUrls: ScriptAssetsUrl[] = [] + let match: RegExpExecArray | null + inlineImportRE.lastIndex = 0 + while ((match = inlineImportRE.exec(cleanCode))) { + const { 1: url, index } = match + const startUrl = cleanCode.indexOf(url, index) + const start = startUrl + 1 + const end = start + url.length - 2 + scriptUrls.push({ + start: start + startOffset, + end: end + startOffset, + url: scriptTextNode.value.slice(start, end), + }) + } + return scriptUrls +} + 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 6cbaf727db4cfb..07b9b905fd251f 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -10,6 +10,7 @@ import { addToHTMLProxyCache, applyHtmlTransforms, assetAttrsConfig, + extractImportExpressionFromClassicScript, findNeedTransformStyleAttribute, getAttrKey, getScriptInfo, @@ -103,17 +104,16 @@ function shouldPreTransform(url: string, config: ResolvedConfig) { const startsWithWordCharRE = /^\w/ +const isSrcSet = (attr: Token.Attribute) => + attr.name === 'srcset' && attr.prefix === undefined const processNodeUrl = ( - attr: Token.Attribute, - sourceCodeLocation: Token.Location, - s: MagicString, + url: string, + useSrcSetReplacer: boolean, config: ResolvedConfig, htmlPath: string, originalUrl?: string, server?: ViteDevServer, -) => { - let url = attr.value || '' - +): string | undefined => { if (server?.moduleGraph) { const mod = server.moduleGraph.urlToModuleMap.get(url) if (mod && mod.lastHMRTimestamp > 0) { @@ -124,10 +124,10 @@ const processNodeUrl = ( if (url[0] === '/' && url[1] !== '/') { // prefix with base (dev only, base is never relative) const fullUrl = path.posix.join(devBase, url) - overwriteAttrValue(s, sourceCodeLocation, fullUrl) if (server && shouldPreTransform(url, config)) { preTransformRequest(server, fullUrl, devBase) } + return fullUrl } else if ( (url[0] === '.' || startsWithWordCharRE.test(url)) && originalUrl && @@ -148,11 +148,10 @@ const processNodeUrl = ( // rewrite before `./index.js` -> `localhost:5173/a/index.js`. // rewrite after `../index.js` -> `localhost:5173/index.js`. - const processedUrl = - attr.name === 'srcset' && attr.prefix === undefined - ? processSrcSetSync(url, ({ url }) => replacer(url)) - : replacer(url) - overwriteAttrValue(s, sourceCodeLocation, processedUrl) + const processedUrl = useSrcSetReplacer + ? processSrcSetSync(url, ({ url }) => replacer(url)) + : replacer(url) + return processedUrl } } const devHtmlHook: IndexHtmlTransformHook = async ( @@ -241,17 +240,39 @@ const devHtmlHook: IndexHtmlTransformHook = async ( const { src, sourceCodeLocation, isModule } = getScriptInfo(node) if (src) { - processNodeUrl( - src, - sourceCodeLocation!, - s, + const processedUrl = processNodeUrl( + src.value, + isSrcSet(src), config, htmlPath, originalUrl, server, ) + if (processedUrl) { + overwriteAttrValue(s, sourceCodeLocation!, processedUrl) + } } else if (isModule && node.childNodes.length) { addInlineModule(node, 'js') + } else if (node.childNodes.length) { + const scriptNode = node.childNodes[ + node.childNodes.length - 1 + ] as DefaultTreeAdapterMap['textNode'] + for (const { + url, + start, + end, + } of extractImportExpressionFromClassicScript(scriptNode)) { + const processedUrl = processNodeUrl( + url, + false, + config, + htmlPath, + originalUrl, + ) + if (processedUrl) { + s.update(start, end, processedUrl) + } + } } } @@ -280,14 +301,20 @@ const devHtmlHook: IndexHtmlTransformHook = async ( for (const p of node.attrs) { const attrKey = getAttrKey(p) if (p.value && assetAttrs.includes(attrKey)) { - processNodeUrl( - p, - node.sourceCodeLocation!.attrs![attrKey], - s, + const processedUrl = processNodeUrl( + p.value, + isSrcSet(p), config, htmlPath, originalUrl, ) + if (processedUrl) { + overwriteAttrValue( + s, + node.sourceCodeLocation!.attrs![attrKey], + processedUrl, + ) + } } } } diff --git a/playground/vitestSetup.ts b/playground/vitestSetup.ts index 465518a3150fd0..b7e43395a99479 100644 --- a/playground/vitestSetup.ts +++ b/playground/vitestSetup.ts @@ -239,11 +239,10 @@ export async function startDefaultServe(): Promise { process.env.VITE_INLINE = 'inline-serve' const testConfig = mergeConfig(options, config || {}) viteServer = server = await (await createServer(testConfig)).listen() - // use resolved port/base from server - const devBase = server.config.base - viteTestUrl = `http://localhost:${server.config.server.port}${ - devBase === '/' ? '' : devBase - }` + viteTestUrl = server.resolvedUrls.local[0] + if (server.config.base === '/') { + viteTestUrl = viteTestUrl.replace(/\/$/, '') + } await page.goto(viteTestUrl) } else { process.env.VITE_INLINE = 'inline-build'