From 7af2a36681d4bb3af9c4d58838620962aab11cfd Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Fri, 6 May 2022 16:30:56 +0200 Subject: [PATCH 1/4] refactor(nuxt): cleanup renderer entry --- packages/nuxt/package.json | 2 +- packages/nuxt/src/core/nitro.ts | 5 +- .../nuxt/src/core/runtime/nitro/renderer.ts | 168 +++++++++--------- packages/nuxt/src/core/runtime/nitro/vue2.ts | 43 ----- packages/nuxt/src/core/runtime/nitro/vue3.ts | 6 - yarn.lock | 10 +- 6 files changed, 88 insertions(+), 146 deletions(-) delete mode 100644 packages/nuxt/src/core/runtime/nitro/vue2.ts delete mode 100644 packages/nuxt/src/core/runtime/nitro/vue3.ts diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index e22a8c2507d..b36e48ce412 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -66,7 +66,7 @@ "unplugin": "^0.6.2", "untyped": "^0.4.4", "vue": "^3.2.33", - "vue-bundle-renderer": "^0.3.7", + "vue-bundle-renderer": "^0.3.8", "vue-router": "^4.0.15" }, "devDependencies": { diff --git a/packages/nuxt/src/core/nitro.ts b/packages/nuxt/src/core/nitro.ts index 4339ca06c18..55fb262d538 100644 --- a/packages/nuxt/src/core/nitro.ts +++ b/packages/nuxt/src/core/nitro.ts @@ -77,9 +77,6 @@ export async function initNitro (nuxt: Nuxt) { '@vue/compiler-ssr': 'unenv/runtime/mock/proxy', '@vue/devtools-api': 'unenv/runtime/mock/proxy', - // Renderer - '#vue-renderer': resolve(distDir, 'core/runtime/nitro/vue3'), - // Paths '#paths': resolve(distDir, 'core/runtime/nitro/paths'), @@ -87,7 +84,7 @@ export async function initNitro (nuxt: Nuxt) { ...nuxt.options.alias }, replace: { - 'process.env.NUXT_NO_SSR': nuxt.options.ssr === false ? true : undefined + 'process.env.NUXT_NO_SSR': nuxt.options.ssr === false }, rollupConfig: { plugins: [] diff --git a/packages/nuxt/src/core/runtime/nitro/renderer.ts b/packages/nuxt/src/core/runtime/nitro/renderer.ts index 91dc6f5e449..9ac8f833b17 100644 --- a/packages/nuxt/src/core/runtime/nitro/renderer.ts +++ b/packages/nuxt/src/core/runtime/nitro/renderer.ts @@ -1,6 +1,9 @@ import { createRenderer } from 'vue-bundle-renderer' -import { eventHandler, useQuery } from 'h3' +import type { SSRContext } from 'vue-bundle-renderer' +import { CompatibilityEvent, eventHandler, useQuery } from 'h3' import devalue from '@nuxt/devalue' +import { RuntimeConfig } from '@nuxt/schema' + // @ts-ignore import { useRuntimeConfig } from '#internal/nitro' // @ts-ignore @@ -8,33 +11,71 @@ import { buildAssetsURL } from '#paths' // @ts-ignore import htmlTemplate from '#build/views/document.template.mjs' -const STATIC_ASSETS_BASE = process.env.NUXT_STATIC_BASE + '/' + process.env.NUXT_STATIC_VERSION -const NUXT_NO_SSR = process.env.NUXT_NO_SSR -const PAYLOAD_JS = '/payload.js' +interface NuxtSSRContext extends SSRContext { + url: string + noSSR: boolean + redirected: boolean + event: CompatibilityEvent + req: CompatibilityEvent['req'] + res: CompatibilityEvent['res'] + runtimeConfig: RuntimeConfig + error?: any + nuxt?: any + payload?: any + teleports?: { body?: string } + renderMeta?: () => Promise +} + +interface RenderResult { + html: any + renderResourceHints: () => string + renderStyles: () => string + renderScripts: () => string + meta?: Partial<{ + htmlAttrs?: string, + bodyAttrs: string, + headAttrs: string, + headTags: string, + bodyScriptsPrepend : string, + bodyScripts : string + }> +} // @ts-ignore -const getClientManifest = cachedImport(() => import('#build/dist/server/client.manifest.mjs')) +const getClientManifest = () => import('#build/dist/server/client.manifest.mjs').then(r => r.default || r) + // @ts-ignore -const getSSRApp = !process.env.NUXT_NO_SSR && cachedImport(() => import('#build/dist/server/server.mjs')) +const getServerEntry = () => process.env.NUXT_NO_SSR ? Promise.resolve(null) : import('#build/dist/server/server.mjs').then(r => r.default || r) -const getSSRRenderer = cachedResult(async () => { +// -- SSR Renderer -- +const getSSRRenderer = lazyCachedFunction(async () => { // Load client manifest const clientManifest = await getClientManifest() if (!clientManifest) { throw new Error('client.manifest is not available') } + // Load server bundle - const createSSRApp = await getSSRApp() + const createSSRApp = await getServerEntry() if (!createSSRApp) { throw new Error('Server bundle is not available') } + // Create renderer - // @ts-ignore - const { renderToString } = await import('#vue-renderer') // Alias to vue2.ts or vue3.ts - return createRenderer((createSSRApp), { clientManifest, renderToString, publicPath: buildAssetsURL() }).renderToString + const { renderToString: _renderToString } = await import('vue/server-renderer') + const renderToString = async (input, context) => { + const html = await _renderToString(input, context) + return `
${html}
` + } + return createRenderer(createSSRApp, { + clientManifest, + renderToString, + publicPath: buildAssetsURL() + }) }) -const getSPARenderer = cachedResult(async () => { +// -- SPA Renderer -- +const getSPARenderer = lazyCachedFunction(async () => { const clientManifest = await getClientManifest() - return (ssrContext) => { + const renderToString = (ssrContext: NuxtSSRContext) => { const config = useRuntimeConfig() - ssrContext.nuxt = { + ssrContext.payload = { serverRendered: false, config: { public: config.public, @@ -42,21 +83,19 @@ const getSPARenderer = cachedResult(async () => { } } - let entryFiles = Object.values(clientManifest).filter( - (fileValue: any) => fileValue.isEntry - ) + let entryFiles = Object.values(clientManifest).filter((fileValue: any) => fileValue.isEntry) if ('all' in clientManifest && 'initial' in clientManifest) { // Upgrade legacy manifest (also see normalizeClientManifest in vue-bundle-renderer) // https://github.com/nuxt-contrib/vue-bundle-renderer/issues/12 entryFiles = clientManifest.initial.map(file => ({ file })) } - return { + return Promise.resolve({ html: '
', renderResourceHints: () => '', renderStyles: () => entryFiles - .flatMap(({ css }) => css) + .flatMap((entry: any) => entry.css) .filter(css => css != null) .map(file => ``) .join(''), @@ -67,36 +106,25 @@ const getSPARenderer = cachedResult(async () => { return `` }) .join('') - } + }) } -}) -function renderToString (ssrContext) { - const getRenderer = (NUXT_NO_SSR || ssrContext.noSSR) ? getSPARenderer : getSSRRenderer - return getRenderer().then(renderToString => renderToString(ssrContext)) -} + return { renderToString } +}) export default eventHandler(async (event) => { // Whether we're rendering an error page const ssrError = event.req.url?.startsWith('/__nuxt_error') ? useQuery(event) : null - let url = ssrError?.url as string || event.req.url! - - // payload.json request detection - let isPayloadReq = false - if (url.startsWith(STATIC_ASSETS_BASE) && url.endsWith(PAYLOAD_JS)) { - isPayloadReq = true - url = url.slice(STATIC_ASSETS_BASE.length, url.length - PAYLOAD_JS.length) || '/' - } + const url = ssrError?.url as string || event.req.url! // Initialize ssr context - const ssrContext = { + const ssrContext: NuxtSSRContext = { url, event, req: event.req, res: event.res, runtimeConfig: useRuntimeConfig(), - noSSR: event.req.headers['x-nuxt-no-ssr'], - + noSSR: !!event.req.headers['x-nuxt-no-ssr'], error: ssrError, redirected: undefined, nuxt: undefined, /* NuxtApp */ @@ -104,9 +132,10 @@ export default eventHandler(async (event) => { } // Render app - const rendered = await renderToString(ssrContext).catch((e) => { + const renderer = (process.env.NUXT_NO_SSR || ssrContext.noSSR) ? await getSPARenderer() : await getSSRRenderer() + const rendered = await renderer.renderToString(ssrContext).catch((e) => { if (!ssrError) { throw e } - }) + }) as RenderResult // If we error on rendering error page, we bail out and directly return to the error handler if (!rendered) { return } @@ -115,76 +144,41 @@ export default eventHandler(async (event) => { return } - const error = ssrContext.error /* nuxt 3 */ || ssrContext.nuxt?.error // Handle errors - if (error && !ssrError) { - throw error + if (ssrContext.error && !ssrError) { + throw ssrContext.error } if (ssrContext.nuxt?.hooks) { await ssrContext.nuxt.hooks.callHook('app:rendered') } - // TODO: nuxt3 should not reuse `nuxt` property for different purpose! - const payload = ssrContext.payload /* nuxt 3 */ || ssrContext.nuxt /* nuxt 2 */ - - if (process.env.NUXT_FULL_STATIC) { - payload.staticAssetsBase = STATIC_ASSETS_BASE - } - - let data - if (isPayloadReq) { - data = renderPayload(payload, url) - event.res.setHeader('Content-Type', 'text/javascript;charset=UTF-8') - } else { - data = await renderHTML(payload, rendered, ssrContext) - event.res.setHeader('Content-Type', 'text/html;charset=UTF-8') - } - - event.res.end(data, 'utf-8') + const html = await renderHTML(ssrContext.payload, rendered, ssrContext) + event.res.setHeader('Content-Type', 'text/html;charset=UTF-8') + return html }) -async function renderHTML (payload, rendered, ssrContext) { +async function renderHTML (payload: any, rendered: RenderResult, ssrContext: NuxtSSRContext) { const state = `` const html = rendered.html - if ('renderMeta' in ssrContext) { + if (ssrContext.renderMeta) { rendered.meta = await ssrContext.renderMeta() } - - const { - htmlAttrs = '', - bodyAttrs = '', - headAttrs = '', - headTags = '', - bodyScriptsPrepend = '', - bodyScripts = '' - } = rendered.meta || {} + rendered.meta = rendered.meta || {} return htmlTemplate({ - HTML_ATTRS: htmlAttrs, - HEAD_ATTRS: headAttrs, - HEAD: headTags + + HTML_ATTRS: rendered.meta.htmlAttrs || '', + HEAD_ATTRS: rendered.meta.headAttrs || '', + HEAD: rendered.meta.headTags || '' + rendered.renderResourceHints() + rendered.renderStyles() + (ssrContext.styles || ''), - BODY_ATTRS: bodyAttrs, + BODY_ATTRS: rendered.meta.bodyAttrs || '', BODY_PREPEND: ssrContext.teleports?.body || '', - APP: bodyScriptsPrepend + html + state + rendered.renderScripts() + bodyScripts + APP: (rendered.meta.bodyScriptsPrepend || '') + html + state + rendered.renderScripts() + (rendered.meta.bodyScripts || '') }) } -function renderPayload (payload, url) { - return `__NUXT_JSONP__("${url}", ${devalue(payload)})` -} - -function _interopDefault (e) { - return e && typeof e === 'object' && 'default' in e ? e.default : e -} - -function cachedImport (importer: () => Promise) { - return cachedResult(() => importer().then(_interopDefault)) as () => Promise -} - -function cachedResult (fn: () => Promise): () => Promise { +function lazyCachedFunction (fn: () => Promise): () => Promise { let res: Promise | null = null return () => { if (res === null) { diff --git a/packages/nuxt/src/core/runtime/nitro/vue2.ts b/packages/nuxt/src/core/runtime/nitro/vue2.ts deleted file mode 100644 index 1a7019bdc14..00000000000 --- a/packages/nuxt/src/core/runtime/nitro/vue2.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * This template is only used for @nuxt/bridge - * - * TODO: Move to bridge once render functions was more reusable - */ - -// @ts-ignore -import { createRenderer } from '#vue2-server-renderer' -const _renderer = createRenderer({}) - -// @ts-ignore -const __VUE_SSR_CONTEXT__ = globalThis.__VUE_SSR_CONTEXT__ = {} - -export function renderToString (component, context) { - return new Promise((resolve, reject) => { - _renderer.renderToString(component, context, (err, result) => { - const styles = [__VUE_SSR_CONTEXT__, context].map(c => c && c._styles && c._styles.default).filter(Boolean) - if (!context._styles) { context._styles = {} } - context._styles.default = { - ids: [...styles.map(s => s.ids)], - css: styles.map(s => s.css).join(''), - media: styles.map(s => s.media).join('') - } - if (err) { - return reject(err) - } - return resolve(result) - }) - }) -} - -// Basic renderer -// import _renderToString from 'vue-server-renderer/basic' -// export function renderToString (component, context) { -// return new Promise((resolve, reject) => { -// _renderToString(component, context, (err, result) => { -// if (err) { -// return reject(err) -// } -// return resolve(result) -// }) -// }) -// } diff --git a/packages/nuxt/src/core/runtime/nitro/vue3.ts b/packages/nuxt/src/core/runtime/nitro/vue3.ts deleted file mode 100644 index 03cea5e7c31..00000000000 --- a/packages/nuxt/src/core/runtime/nitro/vue3.ts +++ /dev/null @@ -1,6 +0,0 @@ -// @ts-ignore -import { renderToString as render } from 'vue/server-renderer' - -export const renderToString: typeof render = (...args) => { - return render(...args).then(result => `
${result}
`) -} diff --git a/yarn.lock b/yarn.lock index 257faaba91b..5a0663ca061 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10089,7 +10089,7 @@ __metadata: unplugin: ^0.6.2 untyped: ^0.4.4 vue: ^3.2.33 - vue-bundle-renderer: ^0.3.7 + vue-bundle-renderer: ^0.3.8 vue-meta: next vue-router: ^4.0.15 bin: @@ -13751,12 +13751,12 @@ __metadata: languageName: node linkType: hard -"vue-bundle-renderer@npm:^0.3.7": - version: 0.3.7 - resolution: "vue-bundle-renderer@npm:0.3.7" +"vue-bundle-renderer@npm:^0.3.8": + version: 0.3.8 + resolution: "vue-bundle-renderer@npm:0.3.8" dependencies: bundle-runner: ^0.0.1 - checksum: 6ef21f019e2b415554dfc1c4ba554a39248a7d8107001de4a3637098787be06cf9784273ec997ee7e2a348949225d2ee534e5523ca8ec9c981e8d7e3965228d0 + checksum: f1cc37913369786e60db6cffcda3f016cb073301db765128cddebdcf8367b50a7525e47b60924788def443f3183b92bcfc72ad248ce28d3fed19ef092ae8b662 languageName: node linkType: hard From 1a313c94e4d225a2a88e2149be384d928dde239a Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Fri, 6 May 2022 17:02:16 +0200 Subject: [PATCH 2/4] add direct aliases for `vue/server-renderer` --- packages/nuxt/src/core/nitro.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/nuxt/src/core/nitro.ts b/packages/nuxt/src/core/nitro.ts index 55fb262d538..629f9b161ce 100644 --- a/packages/nuxt/src/core/nitro.ts +++ b/packages/nuxt/src/core/nitro.ts @@ -64,9 +64,10 @@ export async function initNitro (nuxt: Nuxt) { ] }, alias: { - // TODO: #590 - 'vue/server-renderer': 'vue/server-renderer', 'vue/compiler-sfc': 'vue/compiler-sfc', + 'vue/server-renderer': nuxt.options.dev + ? '@vue/server-renderer/dist/server-renderer.cjs.js' + : '@vue/server-renderer/dist/server-renderer.esm-bundler.js', vue: await resolvePath(`vue/dist/vue.cjs${nuxt.options.dev ? '' : '.prod'}.js`), // Vue 3 mocks From 91caf8038c1e347821ca631c82b4341e99469256 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Fri, 6 May 2022 17:14:56 +0200 Subject: [PATCH 3/4] use normal import for `vue/server-renderer` --- packages/nuxt/src/core/nitro.ts | 4 +--- packages/nuxt/src/core/runtime/nitro/renderer.ts | 11 +++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/nuxt/src/core/nitro.ts b/packages/nuxt/src/core/nitro.ts index 629f9b161ce..1743ed26a75 100644 --- a/packages/nuxt/src/core/nitro.ts +++ b/packages/nuxt/src/core/nitro.ts @@ -65,9 +65,7 @@ export async function initNitro (nuxt: Nuxt) { }, alias: { 'vue/compiler-sfc': 'vue/compiler-sfc', - 'vue/server-renderer': nuxt.options.dev - ? '@vue/server-renderer/dist/server-renderer.cjs.js' - : '@vue/server-renderer/dist/server-renderer.esm-bundler.js', + 'vue/server-renderer': 'vue/server-renderer', vue: await resolvePath(`vue/dist/vue.cjs${nuxt.options.dev ? '' : '.prod'}.js`), // Vue 3 mocks diff --git a/packages/nuxt/src/core/runtime/nitro/renderer.ts b/packages/nuxt/src/core/runtime/nitro/renderer.ts index 9ac8f833b17..d3e95aacb85 100644 --- a/packages/nuxt/src/core/runtime/nitro/renderer.ts +++ b/packages/nuxt/src/core/runtime/nitro/renderer.ts @@ -3,6 +3,7 @@ import type { SSRContext } from 'vue-bundle-renderer' import { CompatibilityEvent, eventHandler, useQuery } from 'h3' import devalue from '@nuxt/devalue' import { RuntimeConfig } from '@nuxt/schema' +import { renderToString as _renderToString } from 'vue/server-renderer' // @ts-ignore import { useRuntimeConfig } from '#internal/nitro' @@ -58,7 +59,6 @@ const getSSRRenderer = lazyCachedFunction(async () => { if (!createSSRApp) { throw new Error('Server bundle is not available') } // Create renderer - const { renderToString: _renderToString } = await import('vue/server-renderer') const renderToString = async (input, context) => { const html = await _renderToString(input, context) return `
${html}
` @@ -95,7 +95,7 @@ const getSPARenderer = lazyCachedFunction(async () => { renderResourceHints: () => '', renderStyles: () => entryFiles - .flatMap((entry: any) => entry.css) + .flatMap(({ css }) => css) .filter(css => css != null) .map(file => ``) .join(''), @@ -160,12 +160,11 @@ export default eventHandler(async (event) => { async function renderHTML (payload: any, rendered: RenderResult, ssrContext: NuxtSSRContext) { const state = `` - const html = rendered.html + rendered.meta = rendered.meta || {} if (ssrContext.renderMeta) { - rendered.meta = await ssrContext.renderMeta() + Object.assign(rendered.meta, await ssrContext.renderMeta()) } - rendered.meta = rendered.meta || {} return htmlTemplate({ HTML_ATTRS: rendered.meta.htmlAttrs || '', @@ -174,7 +173,7 @@ async function renderHTML (payload: any, rendered: RenderResult, ssrContext: Nux rendered.renderResourceHints() + rendered.renderStyles() + (ssrContext.styles || ''), BODY_ATTRS: rendered.meta.bodyAttrs || '', BODY_PREPEND: ssrContext.teleports?.body || '', - APP: (rendered.meta.bodyScriptsPrepend || '') + html + state + rendered.renderScripts() + (rendered.meta.bodyScripts || '') + APP: (rendered.meta.bodyScriptsPrepend || '') + rendered.html + state + rendered.renderScripts() + (rendered.meta.bodyScripts || '') }) } From d4907fb3b0ba7cf39af694bcbdc6727c965a2e32 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Fri, 6 May 2022 17:22:25 +0200 Subject: [PATCH 4/4] fix: typo --- packages/nuxt/src/core/runtime/nitro/renderer.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/nuxt/src/core/runtime/nitro/renderer.ts b/packages/nuxt/src/core/runtime/nitro/renderer.ts index d3e95aacb85..4bf3ab9ecb2 100644 --- a/packages/nuxt/src/core/runtime/nitro/renderer.ts +++ b/packages/nuxt/src/core/runtime/nitro/renderer.ts @@ -167,12 +167,12 @@ async function renderHTML (payload: any, rendered: RenderResult, ssrContext: Nux } return htmlTemplate({ - HTML_ATTRS: rendered.meta.htmlAttrs || '', - HEAD_ATTRS: rendered.meta.headAttrs || '', - HEAD: rendered.meta.headTags || '' + + HTML_ATTRS: (rendered.meta.htmlAttrs || ''), + HEAD_ATTRS: (rendered.meta.headAttrs || ''), + HEAD: (rendered.meta.headTags || '') + rendered.renderResourceHints() + rendered.renderStyles() + (ssrContext.styles || ''), - BODY_ATTRS: rendered.meta.bodyAttrs || '', - BODY_PREPEND: ssrContext.teleports?.body || '', + BODY_ATTRS: (rendered.meta.bodyAttrs || ''), + BODY_PREPEND: (ssrContext.teleports?.body || ''), APP: (rendered.meta.bodyScriptsPrepend || '') + rendered.html + state + rendered.renderScripts() + (rendered.meta.bodyScripts || '') }) }