From 1d34d51c249bedc3d6e91333092e0355f3e39f4d Mon Sep 17 00:00:00 2001 From: saurabhdaware Date: Mon, 2 May 2022 22:41:15 +0530 Subject: [PATCH 01/10] fix(importAnalysis): early null return on undefined --- packages/vite/src/node/plugins/importAnalysis.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index d8bf59225fd49a..cf86e74e7f7721 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -53,7 +53,7 @@ import { optimizedDepNeedsInterop } from '../optimizer' -const isDebug = !!process.env.DEBUG +const isDebug = true const debug = createDebugger('vite:import-analysis') const clientDir = normalizePath(CLIENT_DIR) @@ -167,9 +167,10 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { } const { moduleGraph } = server - // since we are already in the transform phase of the importer, it must - // have been loaded so its entry is guaranteed in the module graph. - const importerModule = moduleGraph.getModuleById(importer)! + const importerModule = moduleGraph.getModuleById(importer) + if (!importerModule) { + return null + } if (!imports.length) { importerModule.isSelfAccepting = false From ac05f2565f00b093c8f1c5f23d7d6f0411fcfb43 Mon Sep 17 00:00:00 2001 From: saurabhdaware Date: Mon, 2 May 2022 22:45:45 +0530 Subject: [PATCH 02/10] chore: revert isDebug change --- packages/vite/src/node/plugins/importAnalysis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index cf86e74e7f7721..d7cb243b36ea08 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -53,7 +53,7 @@ import { optimizedDepNeedsInterop } from '../optimizer' -const isDebug = true +const isDebug = !!process.env.DEBUG const debug = createDebugger('vite:import-analysis') const clientDir = normalizePath(CLIENT_DIR) From 2a764af54f2ef4af068a748ae82d57f5ff42e47e Mon Sep 17 00:00:00 2001 From: yoho Date: Tue, 3 May 2022 12:21:45 +0800 Subject: [PATCH 03/10] fix: url may be '/' run resolveId get real path --- packages/vite/src/node/plugins/importAnalysis.ts | 5 +---- packages/vite/src/node/server/middlewares/indexHtml.ts | 5 ++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index d7cb243b36ea08..d3db9c64eb5f5a 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -167,10 +167,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { } const { moduleGraph } = server - const importerModule = moduleGraph.getModuleById(importer) - if (!importerModule) { - return null - } + const importerModule = moduleGraph.getModuleById(importer)! if (!imports.length) { importerModule.isSelfAccepting = false diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 8638492b1c2001..6ee5b052a8c26b 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -190,7 +190,10 @@ const devHtmlHook: IndexHtmlTransformHook = async ( await Promise.all( styleUrl.map(async ({ start, end, code }, index) => { - const url = filename + `?html-proxy&${index}.css` + // NOTE: ssr url may be '/' run resolveId to get the real path + let url = + (await server!.pluginContainer.resolveId(filename))?.id || filename + url += `?html-proxy&${index}.css` // ensure module in graph after successful load const mod = await moduleGraph.ensureEntryFromUrl(url, false) From ccda81b8a47541e521766121fc370035964327bb Mon Sep 17 00:00:00 2001 From: saurabhdaware Date: Tue, 3 May 2022 12:33:24 +0530 Subject: [PATCH 04/10] feat: add try catch to resolveId --- packages/vite/src/node/plugins/importAnalysis.ts | 2 ++ .../vite/src/node/server/middlewares/indexHtml.ts | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index d3db9c64eb5f5a..d8bf59225fd49a 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -167,6 +167,8 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { } const { moduleGraph } = server + // since we are already in the transform phase of the importer, it must + // have been loaded so its entry is guaranteed in the module graph. const importerModule = moduleGraph.getModuleById(importer)! if (!imports.length) { diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 6ee5b052a8c26b..68b8674edfbf89 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -191,9 +191,18 @@ const devHtmlHook: IndexHtmlTransformHook = async ( await Promise.all( styleUrl.map(async ({ start, end, code }, index) => { // NOTE: ssr url may be '/' run resolveId to get the real path - let url = - (await server!.pluginContainer.resolveId(filename))?.id || filename - url += `?html-proxy&${index}.css` + let resolvedId: string | undefined + try { + resolvedId = (await server!.pluginContainer.resolveId(filename))?.id + } catch (err) { + // If fails to resolve filename, try resolving /index.html + resolvedId = ( + await server!.pluginContainer.resolveId( + path.join(filename, 'index.html') + ) + )?.id + } + const url = `${resolvedId || filename}?html-proxy&${index}.css` // ensure module in graph after successful load const mod = await moduleGraph.ensureEntryFromUrl(url, false) From a2a0d4f56494c2e81076885fe37995db26a45740 Mon Sep 17 00:00:00 2001 From: saurabhdaware Date: Tue, 3 May 2022 12:50:21 +0530 Subject: [PATCH 05/10] feat: handle html name in getHtmlFilename --- .../src/node/server/middlewares/indexHtml.ts | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 68b8674edfbf89..3f2847e5885d25 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -49,8 +49,12 @@ function getHtmlFilename(url: string, server: ViteDevServer) { if (url.startsWith(FS_PREFIX)) { return decodeURIComponent(fsPathFromId(url)) } else { + let htmlFileName = url.slice(1) + if (!htmlFileName) { + htmlFileName = 'index.html' + } return decodeURIComponent( - normalizePath(path.join(server.config.root, url.slice(1))) + normalizePath(path.join(server.config.root, htmlFileName)) ) } } @@ -191,18 +195,18 @@ const devHtmlHook: IndexHtmlTransformHook = async ( await Promise.all( styleUrl.map(async ({ start, end, code }, index) => { // NOTE: ssr url may be '/' run resolveId to get the real path - let resolvedId: string | undefined - try { - resolvedId = (await server!.pluginContainer.resolveId(filename))?.id - } catch (err) { - // If fails to resolve filename, try resolving /index.html - resolvedId = ( - await server!.pluginContainer.resolveId( - path.join(filename, 'index.html') - ) - )?.id - } - const url = `${resolvedId || filename}?html-proxy&${index}.css` + // let resolvedId: string | undefined + // try { + // resolvedId = (await server!.pluginContainer.resolveId(filename))?.id + // } catch (err) { + // // If fails to resolve filename, try resolving /index.html + // resolvedId = ( + // await server!.pluginContainer.resolveId( + // path.join(filename, 'index.html') + // ) + // )?.id + // } + const url = `${filename}?html-proxy&${index}.css` // ensure module in graph after successful load const mod = await moduleGraph.ensureEntryFromUrl(url, false) From eb6bfc734a29b20ca1976c39440ac4409cff2f7d Mon Sep 17 00:00:00 2001 From: saurabhdaware Date: Tue, 3 May 2022 12:57:56 +0530 Subject: [PATCH 06/10] feat: update ssr-html example with SSR docs code --- packages/playground/ssr-html/server.js | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/playground/ssr-html/server.js b/packages/playground/ssr-html/server.js index ad115f1be01163..beeb4d4d8f9daa 100644 --- a/packages/playground/ssr-html/server.js +++ b/packages/playground/ssr-html/server.js @@ -14,6 +14,14 @@ const DYNAMIC_SCRIPTS = ` ` +const DYNAMIC_STYLES = ` + +` + async function createServer( root = process.cwd(), isProd = process.env.NODE_ENV === 'production' @@ -44,13 +52,18 @@ async function createServer( app.use('*', async (req, res) => { try { - let [url] = req.originalUrl.split('?') - if (url.endsWith('/')) url += 'index.html' + const url = req.originalUrl + if (url.startsWith('/favicon.ico')) { + return res.status(404).end('404') + } + const htmlLoc = url === '/' ? resolve('index.html') : `${url}.html` + let template = fs.readFileSync(htmlLoc, 'utf-8') - const htmlLoc = resolve(`.${url}`) - let html = fs.readFileSync(htmlLoc, 'utf8') - html = html.replace('', `${DYNAMIC_SCRIPTS}`) - html = await vite.transformIndexHtml(url, html) + template = template.replace( + '', + `${DYNAMIC_SCRIPTS}${DYNAMIC_STYLES}` + ) + const html = await vite.transformIndexHtml(url, template) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) } catch (e) { From e5eedeb357066e170cb4c5e0dd2e4175e4018087 Mon Sep 17 00:00:00 2001 From: yoho Date: Tue, 3 May 2022 17:48:59 +0800 Subject: [PATCH 07/10] fix: error --- packages/playground/ssr-html/server.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/playground/ssr-html/server.js b/packages/playground/ssr-html/server.js index beeb4d4d8f9daa..9d34756cccb984 100644 --- a/packages/playground/ssr-html/server.js +++ b/packages/playground/ssr-html/server.js @@ -52,11 +52,14 @@ async function createServer( app.use('*', async (req, res) => { try { - const url = req.originalUrl + let [url] = req.originalUrl.split('?') + if (url.endsWith('/')) url += 'index.html' + if (url.startsWith('/favicon.ico')) { return res.status(404).end('404') } - const htmlLoc = url === '/' ? resolve('index.html') : `${url}.html` + const htmlLoc = + url === '/index.html' ? resolve('index.html') : `${url}.html` let template = fs.readFileSync(htmlLoc, 'utf-8') template = template.replace( From cced917afc134ee08ce58f378b38f5960fb36154 Mon Sep 17 00:00:00 2001 From: yoho Date: Tue, 3 May 2022 21:24:51 +0800 Subject: [PATCH 08/10] fix: pass for hmr --- .../vite/src/node/server/middlewares/indexHtml.ts | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 3f2847e5885d25..450a2c80711af7 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -194,18 +194,9 @@ const devHtmlHook: IndexHtmlTransformHook = async ( await Promise.all( styleUrl.map(async ({ start, end, code }, index) => { - // NOTE: ssr url may be '/' run resolveId to get the real path - // let resolvedId: string | undefined - // try { - // resolvedId = (await server!.pluginContainer.resolveId(filename))?.id - // } catch (err) { - // // If fails to resolve filename, try resolving /index.html - // resolvedId = ( - // await server!.pluginContainer.resolveId( - // path.join(filename, 'index.html') - // ) - // )?.id - // } + if (filename.endsWith('/')) { + return + } const url = `${filename}?html-proxy&${index}.css` // ensure module in graph after successful load From b650fd6d96b4de35bcc5e89b423291b1062500d3 Mon Sep 17 00:00:00 2001 From: patak-dev Date: Tue, 3 May 2022 19:14:09 +0200 Subject: [PATCH 09/10] chore: update, wip --- packages/playground/ssr-html/server.js | 5 ++- .../src/node/server/middlewares/indexHtml.ts | 41 +++++++++++-------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/packages/playground/ssr-html/server.js b/packages/playground/ssr-html/server.js index 9d34756cccb984..443bc751e9b7dc 100644 --- a/packages/playground/ssr-html/server.js +++ b/packages/playground/ssr-html/server.js @@ -58,14 +58,15 @@ async function createServer( if (url.startsWith('/favicon.ico')) { return res.status(404).end('404') } - const htmlLoc = - url === '/index.html' ? resolve('index.html') : `${url}.html` + + const htmlLoc = resolve(`.${url}`) let template = fs.readFileSync(htmlLoc, 'utf-8') template = template.replace( '', `${DYNAMIC_SCRIPTS}${DYNAMIC_STYLES}` ) + const html = await vite.transformIndexHtml(url, template) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 450a2c80711af7..6c2a3c25bc368e 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -1,6 +1,7 @@ import fs from 'fs' import path from 'path' import MagicString from 'magic-string' +import type { SourceMapInput } from 'rollup' import type { AttributeNode, ElementNode, TextNode } from '@vue/compiler-dom' import { NodeTypes } from '@vue/compiler-dom' import type { Connect } from 'types/connect' @@ -49,12 +50,8 @@ function getHtmlFilename(url: string, server: ViteDevServer) { if (url.startsWith(FS_PREFIX)) { return decodeURIComponent(fsPathFromId(url)) } else { - let htmlFileName = url.slice(1) - if (!htmlFileName) { - htmlFileName = 'index.html' - } return decodeURIComponent( - normalizePath(path.join(server.config.root, htmlFileName)) + normalizePath(path.join(server.config.root, url.slice(1))) ) } } @@ -112,6 +109,15 @@ const devHtmlHook: IndexHtmlTransformHook = async ( const { config, moduleGraph, watcher } = server! const base = config.base || '/' + // There are users of vite.transformIndexHtml calling it with url '/' + // for SSR integrations #7993 + const proxyModulePath = + config.base + + htmlPath.slice(1) + + (htmlPath.endsWith('/') ? 'index.html' : '') + const proxyModuleUrl = (index: number, ext: string) => + `${proxyModulePath}?html-proxy&index=${index}.${ext}` + const s = new MagicString(html) let inlineModuleIndex = -1 const filePath = cleanUrl(htmlPath) @@ -125,19 +131,21 @@ const devHtmlHook: IndexHtmlTransformHook = async ( 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 + + let map: SourceMapInput | undefined + if (!htmlPath.endsWith('/')) { + 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, { code, map }) // inline js module. convert to src="proxy" - const modulePath = `${ - config.base + htmlPath.slice(1) - }?html-proxy&index=${inlineModuleIndex}.${ext}` + const modulePath = proxyModuleUrl(inlineModuleIndex, ext) // invalidate the module so the newly cached contents will be served const module = server?.moduleGraph.getModuleById(modulePath) @@ -194,16 +202,13 @@ const devHtmlHook: IndexHtmlTransformHook = async ( await Promise.all( styleUrl.map(async ({ start, end, code }, index) => { - if (filename.endsWith('/')) { - return - } - const url = `${filename}?html-proxy&${index}.css` + const url = proxyModuleUrl(index, 'css') // ensure module in graph after successful load const mod = await moduleGraph.ensureEntryFromUrl(url, false) ensureWatchedFile(watcher, mod.file, config.root) - const result = await server!.pluginContainer.transform(code, url) + const result = await server!.pluginContainer.transform(code, mod.id!) s.overwrite(start, end, result?.code || '') }) ) From 1bf08ff6731acb056232994b4cc3433f4c7aaa18 Mon Sep 17 00:00:00 2001 From: patak-dev Date: Tue, 3 May 2022 23:46:07 +0200 Subject: [PATCH 10/10] fix: handle js and css proxies for virtual html --- packages/playground/ssr-html/server.js | 10 +++- .../src/node/server/middlewares/indexHtml.ts | 47 ++++++++++++------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/packages/playground/ssr-html/server.js b/packages/playground/ssr-html/server.js index 443bc751e9b7dc..1f2cd5331c0157 100644 --- a/packages/playground/ssr-html/server.js +++ b/packages/playground/ssr-html/server.js @@ -50,7 +50,7 @@ async function createServer( // use vite's connect instance as middleware app.use(vite.middlewares) - app.use('*', async (req, res) => { + app.use('*', async (req, res, next) => { try { let [url] = req.originalUrl.split('?') if (url.endsWith('/')) url += 'index.html' @@ -58,6 +58,9 @@ async function createServer( if (url.startsWith('/favicon.ico')) { return res.status(404).end('404') } + if (url.startsWith('/@id/__x00__')) { + return next() + } const htmlLoc = resolve(`.${url}`) let template = fs.readFileSync(htmlLoc, 'utf-8') @@ -67,7 +70,10 @@ async function createServer( `${DYNAMIC_SCRIPTS}${DYNAMIC_STYLES}` ) - const html = await vite.transformIndexHtml(url, template) + // Force calling transformIndexHtml with url === '/', to simulate + // usage by ecosystem that was recommended in the SSR documentation + // as `const url = req.originalUrl` + const html = await vite.transformIndexHtml('/', template) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) } catch (e) { diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 6c2a3c25bc368e..955ee6b708f54d 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -16,7 +16,12 @@ import { } from '../../plugins/html' import type { ResolvedConfig, ViteDevServer } from '../..' import { send } from '../send' -import { CLIENT_PUBLIC_PATH, FS_PREFIX } from '../../constants' +import { + CLIENT_PUBLIC_PATH, + FS_PREFIX, + VALID_ID_PREFIX, + NULL_BYTE_PLACEHOLDER +} from '../../constants' import { cleanUrl, fsPathFromId, @@ -109,31 +114,41 @@ const devHtmlHook: IndexHtmlTransformHook = async ( const { config, moduleGraph, watcher } = server! const base = config.base || '/' - // There are users of vite.transformIndexHtml calling it with url '/' - // for SSR integrations #7993 - const proxyModulePath = - config.base + - htmlPath.slice(1) + - (htmlPath.endsWith('/') ? 'index.html' : '') - const proxyModuleUrl = (index: number, ext: string) => - `${proxyModulePath}?html-proxy&index=${index}.${ext}` + let proxyModulePath: string + let proxyModuleUrl: string + + const trailingSlash = htmlPath.endsWith('/') + if (!trailingSlash && fs.existsSync(filename)) { + proxyModulePath = htmlPath + proxyModuleUrl = base + htmlPath.slice(1) + } else { + // There are users of vite.transformIndexHtml calling it with url '/' + // for SSR integrations #7993, filename is root for this case + // A user may also use a valid name for a virtual html file + // Mark the path as virtual in both cases so sourcemaps aren't processed + // and ids are properly handled + const validPath = `${htmlPath}${trailingSlash ? 'index.html' : ''}` + proxyModulePath = `\0${validPath}` + proxyModuleUrl = `${VALID_ID_PREFIX}${NULL_BYTE_PLACEHOLDER}${validPath}` + } const s = new MagicString(html) let inlineModuleIndex = -1 - const filePath = cleanUrl(htmlPath) + const proxyCacheUrl = cleanUrl(proxyModulePath).replace( + normalizePath(config.root), + '' + ) const styleUrl: AssetNode[] = [] const addInlineModule = (node: ElementNode, ext: 'js') => { inlineModuleIndex++ - const url = filePath.replace(normalizePath(config.root), '') - const contentNode = node.children[0] as TextNode const code = contentNode.content let map: SourceMapInput | undefined - if (!htmlPath.endsWith('/')) { + if (!proxyModulePath.startsWith('\0')) { map = new MagicString(html) .snip(contentNode.loc.start.offset, contentNode.loc.end.offset) .generateMap({ hires: true }) @@ -142,10 +157,10 @@ const devHtmlHook: IndexHtmlTransformHook = async ( } // add HTML Proxy to Map - addToHTMLProxyCache(config, url, inlineModuleIndex, { code, map }) + addToHTMLProxyCache(config, proxyCacheUrl, inlineModuleIndex, { code, map }) // inline js module. convert to src="proxy" - const modulePath = proxyModuleUrl(inlineModuleIndex, ext) + const modulePath = `${proxyModuleUrl}?html-proxy&index=${inlineModuleIndex}.${ext}` // invalidate the module so the newly cached contents will be served const module = server?.moduleGraph.getModuleById(modulePath) @@ -202,7 +217,7 @@ const devHtmlHook: IndexHtmlTransformHook = async ( await Promise.all( styleUrl.map(async ({ start, end, code }, index) => { - const url = proxyModuleUrl(index, 'css') + const url = `${proxyModulePath}?html-proxy&index=${index}.css` // ensure module in graph after successful load const mod = await moduleGraph.ensureEntryFromUrl(url, false)