fix(nuxt): pass `joinRelativeURL` + share paths on server (#26407)

This commit is contained in:
Daniel Roe 2024-03-21 11:57:11 +00:00 committed by GitHub
parent 4647d2f925
commit 8c3159f4ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 38 additions and 33 deletions

View File

@ -6,7 +6,7 @@ import { useRuntimeConfig } from '../nuxt'
// @ts-expect-error virtual file
import { appManifest as isAppManifestEnabled } from '#build/nuxt.config.mjs'
// @ts-expect-error virtual file
import { buildAssetsURL } from '#build/paths.mjs'
import { buildAssetsURL } from '#internal/nuxt/paths'
export interface NuxtAppManifestMeta {
id: string

View File

@ -1,10 +1,7 @@
import { createApp, createSSRApp, nextTick } from 'vue'
import type { App } from 'vue'
// These files must be imported first as they have side effects:
// 1. (we set __webpack_public_path via this import, if using webpack builder)
import '#build/paths.mjs'
// 2. we set globalThis.$fetch via this import
// This file must be imported first as we set globalThis.$fetch via this import
import '#build/fetch.mjs'
import { applyPlugins, createNuxtApp } from './nuxt'

View File

@ -3,7 +3,7 @@ import { getAppManifest } from '../composables/manifest'
import type { NuxtAppManifestMeta } from '../composables/manifest'
import { onNuxtReady } from '../composables/ready'
// @ts-expect-error virtual file
import { buildAssetsURL } from '#build/paths.mjs'
import { buildAssetsURL } from '#internal/nuxt/paths'
export default defineNuxtPlugin((nuxtApp) => {
if (import.meta.test) { return }

View File

@ -206,7 +206,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
'@vue/devtools-api': 'vue-devtools-stub',
// Paths
'#paths': resolve(distDir, 'core/runtime/nitro/paths'),
'#internal/nuxt/paths': resolve(distDir, 'core/runtime/nitro/paths'),
// Nuxt aliases
...nuxt.options.alias

View File

@ -1,4 +1,4 @@
import { joinURL } from 'ufo'
import { joinRelativeURL } from 'ufo'
import { useRuntimeConfig } from '#internal/nitro'
export function baseURL (): string {
@ -12,12 +12,12 @@ export function buildAssetsDir (): string {
}
export function buildAssetsURL (...path: string[]): string {
return joinURL(publicAssetsURL(), buildAssetsDir(), ...path)
return joinRelativeURL(publicAssetsURL(), buildAssetsDir(), ...path)
}
export function publicAssetsURL (...path: string[]): string {
// TODO: support passing event to `useRuntimeConfig`
const app = useRuntimeConfig().app
const publicBase = app.cdnURL as string || app.baseURL
return path.length ? joinURL(publicBase, ...path) : publicBase
return path.length ? joinRelativeURL(publicBase, ...path) : publicBase
}

View File

@ -31,7 +31,7 @@ import type { NuxtPayload, NuxtSSRContext } from '#app'
// @ts-expect-error virtual file
import { appHead, appRootId, appRootTag, appTeleportId, appTeleportTag, componentIslands } from '#internal/nuxt.config.mjs'
// @ts-expect-error virtual file
import { buildAssetsURL, publicAssetsURL } from '#paths'
import { buildAssetsURL, publicAssetsURL } from '#internal/nuxt/paths'
// @ts-expect-error private property consumed by vite-generated url helpers
globalThis.__buildAssetsURL = buildAssetsURL

View File

@ -327,7 +327,7 @@ export const publicPathTemplate: NuxtTemplate = {
filename: 'paths.mjs',
getContents ({ nuxt }) {
return [
'import { joinRelativeURL as joinURL } from \'ufo\'',
'import { joinRelativeURL } from \'ufo\'',
!nuxt.options.dev && 'import { useRuntimeConfig } from \'#internal/nitro\'',
nuxt.options.dev
@ -337,11 +337,11 @@ export const publicPathTemplate: NuxtTemplate = {
'export const baseURL = () => appConfig.baseURL',
'export const buildAssetsDir = () => appConfig.buildAssetsDir',
'export const buildAssetsURL = (...path) => joinURL(publicAssetsURL(), buildAssetsDir(), ...path)',
'export const buildAssetsURL = (...path) => joinRelativeURL(publicAssetsURL(), buildAssetsDir(), ...path)',
'export const publicAssetsURL = (...path) => {',
' const publicBase = appConfig.cdnURL || appConfig.baseURL',
' return path.length ? joinURL(publicBase, ...path) : publicBase',
' return path.length ? joinRelativeURL(publicBase, ...path) : publicBase',
'}',
// On server these are registered directly in packages/nuxt/src/core/runtime/nitro/renderer.ts
@ -358,7 +358,7 @@ export const dollarFetchTemplate: NuxtTemplate = {
getContents () {
return [
'import { $fetch } from \'ofetch\'',
"import { baseURL } from '#build/paths.mjs'",
"import { baseURL } from '#internal/nuxt/paths'",
'if (!globalThis.$fetch) {',
' globalThis.$fetch = $fetch.create({',
' baseURL: baseURL()',

View File

@ -68,6 +68,7 @@ export async function buildClient (ctx: ViteBuildContext) {
alias: {
...nodeCompat.alias,
...ctx.config.resolve?.alias,
'#internal/nuxt/paths': resolve(ctx.nuxt.options.buildDir, 'paths.mjs'),
'#build/plugins': resolve(ctx.nuxt.options.buildDir, 'plugins/client'),
'#internal/nitro': resolve(ctx.nuxt.options.buildDir, 'nitro.client.mjs')
},

View File

@ -27,8 +27,8 @@ export function runtimePathsPlugin (options: RuntimePathsOptions): Plugin {
if (VITE_ASSET_RE.test(code)) {
const s = new MagicString(code)
// Register dependency on paths.mjs, which sets globalThis.__publicAssetsURL
s.prepend('import "#build/paths.mjs";')
// Register dependency on #build/paths.mjs or #internal/nuxt/paths.mjs, which sets globalThis.__publicAssetsURL
s.prepend('import "#internal/nuxt/paths";')
return {
code: s.toString(),

View File

@ -26,7 +26,7 @@ export const VitePublicDirsPlugin = createUnplugin(() => {
enforce: 'pre',
handler (id) {
if (id.startsWith(PREFIX)) {
return `import { publicAssetsURL } from '#build/paths.mjs';export default publicAssetsURL(${JSON.stringify(decodeURIComponent(id.slice(PREFIX.length)))})`
return `import { publicAssetsURL } from '#internal/nuxt/paths';export default publicAssetsURL(${JSON.stringify(decodeURIComponent(id.slice(PREFIX.length)))})`
}
}
},

View File

@ -55,6 +55,7 @@ export async function buildServer (ctx: ViteBuildContext) {
},
resolve: {
alias: {
'#internal/nuxt/paths': resolve(ctx.nuxt.options.buildDir, 'paths.mjs'),
'#build/plugins': resolve(ctx.nuxt.options.buildDir, 'plugins/server')
}
},
@ -79,7 +80,7 @@ export async function buildServer (ctx: ViteBuildContext) {
ssr: true,
rollupOptions: {
input: { server: entry },
external: ['#internal/nitro'],
external: ['#internal/nitro', '#internal/nuxt/paths'],
output: {
entryFileNames: '[name].mjs',
format: 'module',
@ -105,13 +106,15 @@ export async function buildServer (ctx: ViteBuildContext) {
if (!ctx.nuxt.options.dev) {
const nitroDependencies = await tryResolveModule('nitropack/package.json', ctx.nuxt.options.modulesDir)
.then(r => import(r!)).then(r => Object.keys(r.dependencies || {})).catch(() => [])
;(serverConfig.ssr!.external as string[])!.push(
if (Array.isArray(serverConfig.ssr!.external)) {
serverConfig.ssr!.external.push(
// explicit dependencies we use in our ssr renderer - these can be inlined (if necessary) in the nitro build
'unhead', '@unhead/ssr', 'unctx', 'h3', 'devalue', '@nuxt/devalue', 'radix3', 'unstorage', 'hookable',
// dependencies we might share with nitro - these can be inlined (if necessary) in the nitro build
...nitroDependencies
)
}
}
serverConfig.customLogger = createViteLogger(serverConfig)

View File

@ -54,6 +54,9 @@ function serverStandalone (ctx: WebpackConfigContext) {
...ctx.options.build.transpile
]
const external = ['#internal/nitro']
if (!ctx.nuxt.options.dev) {
external.push('#internal/nuxt/paths')
}
if (!Array.isArray(ctx.config.externals)) { return }
ctx.config.externals.push(({ request }, cb) => {

View File

@ -11,17 +11,17 @@ const defaults: DynamicBasePluginOptions = {
sourcemap: true
}
const ENTRY_RE = /import ["']#build\/css["'];/
export const DynamicBasePlugin = createUnplugin((options: DynamicBasePluginOptions = {}) => {
options = { ...defaults, ...options }
return {
name: 'nuxt:dynamic-base-path',
enforce: 'post',
enforce: 'post' as const,
transform (code, id) {
if (!id.includes('paths.mjs') || !code.includes('const appConfig = ')) {
return
}
if (!id.includes('entry') || !ENTRY_RE.test(code)) { return }
const s = new MagicString(code)
s.append(`\n${options.globalPublicPath} = buildAssetsURL();\n`)
s.prepend(`import { buildAssetsURL } from '#internal/nuxt/paths';\n${options.globalPublicPath} = buildAssetsURL();\n`)
return {
code: s.toString(),
map: options.sourcemap

View File

@ -120,6 +120,7 @@ function baseAlias (ctx: WebpackConfigContext) {
'#app': ctx.options.appDir,
'#build/plugins': resolve(ctx.options.buildDir, 'plugins', ctx.isClient ? 'client' : 'server'),
'#build': ctx.options.buildDir,
'#internal/nuxt/paths': resolve(ctx.nuxt.options.buildDir, 'paths.mjs'),
...ctx.options.alias,
...ctx.alias
}

View File

@ -32,7 +32,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
const serverDir = join(rootDir, '.output/server')
const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot('"206k"')
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot('"207k"')
const modules = await analyzeSizes('node_modules/**/*', serverDir)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"1336k"')
@ -72,7 +72,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
const serverDir = join(rootDir, '.output-inline/server')
const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot('"525k"')
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot('"526k"')
const modules = await analyzeSizes('node_modules/**/*', serverDir)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"77.8k"')

View File

@ -8,7 +8,7 @@ export default defineConfig({
resolve: {
alias: {
'#build/nuxt.config.mjs': resolve('./test/mocks/nuxt-config'),
'#build/paths.mjs': resolve('./test/mocks/paths'),
'#internal/nuxt/paths': resolve('./test/mocks/paths'),
'#build/app.config.mjs': resolve('./test/mocks/app-config'),
'#app': resolve('./packages/nuxt/dist/app')
}