Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(nuxt): pass joinRelativeURL to bundle renderer and share paths with nitro #26407

Merged
merged 11 commits into from
Mar 21, 2024
2 changes: 1 addition & 1 deletion packages/nuxt/src/app/composables/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 1 addition & 4 deletions packages/nuxt/src/app/entry.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
2 changes: 1 addition & 1 deletion packages/nuxt/src/core/nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions packages/nuxt/src/core/runtime/nitro/paths.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { joinURL } from 'ufo'
import { joinRelativeURL } from 'ufo'
import { useRuntimeConfig } from '#internal/nitro'

export function baseURL (): string {
Expand All @@ -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
}
2 changes: 1 addition & 1 deletion packages/nuxt/src/core/runtime/nitro/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions packages/nuxt/src/core/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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()',
Expand Down
1 change: 1 addition & 0 deletions packages/vite/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
},
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/src/plugins/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/plugins/public-dirs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)))})`
}
}
},
Expand Down
17 changes: 10 additions & 7 deletions packages/vite/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
}
},
Expand All @@ -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',
Expand All @@ -105,12 +106,14 @@ 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(
// 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
)
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)
Expand Down
3 changes: 3 additions & 0 deletions packages/webpack/src/configs/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
10 changes: 5 additions & 5 deletions packages/webpack/src/plugins/dynamic-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions packages/webpack/src/presets/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions test/bundle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"')
Expand Down Expand Up @@ -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"')
Expand Down
2 changes: 1 addition & 1 deletion vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
}
Expand Down