mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
fix(nuxt3): use shared module for dynamic paths (#3757)
* test: add tests for dynamic paths + relative assets Co-authored-by: Anthony Fu <hi@antfu.me> * fix: rework client config and use shared module for dynamic paths * resolves webpack public path regression * refactor: use more similar names to nitro equivalent * fix: align config exports * refactor: remove `__` prefix within dynamic paths module * refactor: use '#_config' to indicate internal alias * Update packages/nuxt3/src/core/templates.ts Co-authored-by: pooya parsa <pyapar@gmail.com> * refactor: use `#_config` alias and rename to `paths.mjs` Co-authored-by: Anthony Fu <hi@antfu.me> Co-authored-by: pooya parsa <pyapar@gmail.com>
This commit is contained in:
parent
6c4a34a675
commit
7458dd1aa6
@ -231,6 +231,7 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
||||
'#nitro-renderer': resolve(nitroContext._internal.runtimeDir, 'app', renderer),
|
||||
'#paths': resolve(nitroContext._internal.runtimeDir, 'app/paths'),
|
||||
'#config': resolve(nitroContext._internal.runtimeDir, 'app/config'),
|
||||
'#_config': resolve(nitroContext._internal.runtimeDir, 'app/config'),
|
||||
'#nitro-vue-renderer': vue2ServerRenderer,
|
||||
// Only file and data URLs are supported by the default ESM loader on Windows (#427)
|
||||
'#build': nitroContext._nuxt.dev && process.platform === 'win32'
|
||||
|
@ -1,3 +1,5 @@
|
||||
// We set __webpack_public_path via this import with webpack builder
|
||||
import '#build/paths.mjs'
|
||||
import { createSSRApp, createApp, nextTick } from 'vue'
|
||||
import { createNuxtApp, applyPlugins, normalizePlugins, CreateOptions } from '#app'
|
||||
import '#build/css'
|
||||
|
@ -168,3 +168,32 @@ export const layoutTemplate: NuxtTemplate = {
|
||||
].join('\n')
|
||||
}
|
||||
}
|
||||
|
||||
export const clientConfigTemplate: NuxtTemplate = {
|
||||
filename: 'config.client.mjs',
|
||||
getContents: () => 'export default window?.__NUXT__?.config || {}'
|
||||
}
|
||||
|
||||
export const publicPathTemplate: NuxtTemplate = {
|
||||
filename: 'paths.mjs',
|
||||
getContents ({ nuxt }) {
|
||||
return [
|
||||
'import { joinURL } from \'ufo\'',
|
||||
'import config from \'#_config\'',
|
||||
|
||||
nuxt.options.dev
|
||||
? `const appConfig = ${JSON.stringify(nuxt.options.app)}`
|
||||
: 'const appConfig = config.app',
|
||||
|
||||
'export const baseURL = () => appConfig.baseURL',
|
||||
'export const buildAssetsDir = () => appConfig.buildAssetsDir',
|
||||
|
||||
'export const buildAssetsURL = (...path) => joinURL(publicAssetsURL(), buildAssetsDir(), ...path)',
|
||||
|
||||
'export const publicAssetsURL = (...path) => {',
|
||||
' const publicBase = appConfig.cdnURL || appConfig.baseURL',
|
||||
' return path.length ? joinURL(publicBase, ...path) : publicBase',
|
||||
'}'
|
||||
].join('\n')
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,8 @@ export async function buildClient (ctx: ViteBuildContext) {
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'#build/plugins': resolve(ctx.nuxt.options.buildDir, 'plugins/client')
|
||||
'#build/plugins': resolve(ctx.nuxt.options.buildDir, 'plugins/client'),
|
||||
'#_config': resolve(ctx.nuxt.options.buildDir, 'config.client.mjs')
|
||||
}
|
||||
},
|
||||
build: {
|
||||
|
@ -45,35 +45,29 @@ export const DynamicBasePlugin = createUnplugin(function (options: DynamicBasePl
|
||||
enforce: 'post',
|
||||
transform (code, id) {
|
||||
const s = new MagicString(code)
|
||||
let injectUtils = false
|
||||
|
||||
if (options.globalPublicPath && id.includes('entry.ts')) {
|
||||
injectUtils = true
|
||||
s.prepend(`${options.globalPublicPath} = joinURL(NUXT_BASE, NUXT_CONFIG.app.buildAssetsDir);\n`)
|
||||
if (options.globalPublicPath && id.includes('paths.mjs') && code.includes('const appConfig = ')) {
|
||||
s.append(`${options.globalPublicPath} = buildAssetsURL();\n`)
|
||||
}
|
||||
|
||||
const assetId = code.match(VITE_ASSET_RE)
|
||||
if (assetId) {
|
||||
injectUtils = true
|
||||
s.overwrite(0, code.length, `export default joinURL(NUXT_BASE, NUXT_CONFIG.app.buildAssetsDir, "${assetId[1]}".replace("/__NUXT_BASE__", ""));`)
|
||||
s.overwrite(0, code.length,
|
||||
[
|
||||
'import { buildAssetsURL } from \'#build/paths.mjs\';',
|
||||
`export default buildAssetsURL("${assetId[1]}".replace("/__NUXT_BASE__", ""));`
|
||||
].join('\n')
|
||||
)
|
||||
}
|
||||
|
||||
if (injectUtils || (code.includes('NUXT_BASE') && !code.includes('const NUXT_BASE ='))) {
|
||||
s.prepend('const NUXT_BASE = NUXT_CONFIG.app.cdnURL || NUXT_CONFIG.app.baseURL;\n')
|
||||
|
||||
if (options.env === 'dev') {
|
||||
s.prepend(`const NUXT_CONFIG = { app: ${JSON.stringify(options.devAppConfig)} };\n`)
|
||||
} else if (options.env === 'server') {
|
||||
s.prepend('import NUXT_CONFIG from "#config";\n')
|
||||
} else {
|
||||
s.prepend('const NUXT_CONFIG = __NUXT__.config;\n')
|
||||
}
|
||||
if (!id.includes('paths.mjs') && code.includes('NUXT_BASE') && !code.includes('import { publicAssetsURL as __publicAssetsURL }')) {
|
||||
s.prepend('import { publicAssetsURL as __publicAssetsURL } from \'#build/paths.mjs\';\n')
|
||||
}
|
||||
|
||||
if (id === 'vite/preload-helper') {
|
||||
injectUtils = true
|
||||
// Define vite base path as buildAssetsUrl (i.e. including _nuxt/)
|
||||
s.replace(/const base = ['"]\/__NUXT_BASE__\/['"]/, 'const base = joinURL(NUXT_BASE, NUXT_CONFIG.app.buildAssetsDir)')
|
||||
s.prepend('import { buildAssetsDir } from \'#build/paths.mjs\';\n')
|
||||
s.replace(/const base = ['"]\/__NUXT_BASE__\/['"]/, 'const base = buildAssetsDir()')
|
||||
}
|
||||
|
||||
// Sanitize imports
|
||||
@ -83,11 +77,7 @@ export const DynamicBasePlugin = createUnplugin(function (options: DynamicBasePl
|
||||
for (const delimiter of ['`', '"', "'"]) {
|
||||
const delimiterRE = new RegExp(`(?<!const base = )${delimiter}([^${delimiter}]*)\\/__NUXT_BASE__\\/([^${delimiter}]*)${delimiter}`, 'g')
|
||||
/* eslint-disable-next-line no-template-curly-in-string */
|
||||
s.replace(delimiterRE, '`$1${NUXT_BASE}$2`')
|
||||
}
|
||||
|
||||
if (injectUtils) {
|
||||
s.prepend('import { joinURL } from "ufo";\n')
|
||||
s.replace(delimiterRE, '`$1${__publicAssetsURL()}$2`')
|
||||
}
|
||||
|
||||
if (s.hasChanged()) {
|
||||
|
@ -40,6 +40,8 @@ export async function buildServer (ctx: ViteBuildContext) {
|
||||
}
|
||||
},
|
||||
ssr: {
|
||||
// Private nitro alias: packages/nitro/src/rollup/config.ts#L234
|
||||
external: ['#_config'],
|
||||
noExternal: [
|
||||
...ctx.nuxt.options.build.transpile,
|
||||
// TODO: Use externality for production (rollup) build
|
||||
|
@ -46,7 +46,7 @@ function serverStandalone (ctx: WebpackConfigContext) {
|
||||
'#',
|
||||
...ctx.options.build.transpile
|
||||
]
|
||||
const external = ['#config']
|
||||
const external = ['#_config']
|
||||
|
||||
if (!Array.isArray(ctx.config.externals)) { return }
|
||||
ctx.config.externals.push(({ request }, cb) => {
|
||||
|
@ -118,6 +118,9 @@ function baseAlias (ctx: WebpackConfigContext) {
|
||||
...options.alias,
|
||||
...ctx.alias
|
||||
}
|
||||
if (ctx.isClient) {
|
||||
ctx.alias['#_config'] = resolve(ctx.nuxt.options.buildDir, 'config.client.mjs')
|
||||
}
|
||||
}
|
||||
|
||||
function baseResolve (ctx: WebpackConfigContext) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { fileURLToPath } from 'url'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { setup, $fetch } from '@nuxt/test-utils'
|
||||
import { setup, $fetch, startServer } from '@nuxt/test-utils'
|
||||
|
||||
describe('fixtures:basic', async () => {
|
||||
await setup({
|
||||
@ -156,4 +156,59 @@ describe('fixtures:basic', async () => {
|
||||
expect(html).toContain('Sugar Counter 12 x 2 = 24')
|
||||
})
|
||||
})
|
||||
|
||||
describe('dynamic paths', () => {
|
||||
it('should work with no overrides', async () => {
|
||||
const html = await $fetch('/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"/g)) {
|
||||
const url = match[2]
|
||||
expect(url.startsWith('/_nuxt/') || url === '/public.svg').toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('adds relative paths to CSS', async () => {
|
||||
const html = await $fetch('/assets')
|
||||
const urls = Array.from(html.matchAll(/(href|src)="(.*?)"/g)).map(m => m[2])
|
||||
const cssURL = urls.find(u => /_nuxt\/entry.*\.css$/.test(u))
|
||||
if (process.env.TEST_WITH_WEBPACK) {
|
||||
// Webpack injects CSS differently
|
||||
return
|
||||
}
|
||||
const css = await $fetch(cssURL)
|
||||
const imageUrls = Array.from(css.matchAll(/url\(([^)]*)\)/g)).map(m => m[1].replace(/[-.][\w]{8}\./g, '.'))
|
||||
expect(imageUrls).toMatchInlineSnapshot(`
|
||||
[
|
||||
"./logo.svg",
|
||||
"../public.svg",
|
||||
]
|
||||
`)
|
||||
})
|
||||
|
||||
it('should allow setting base URL and build assets directory', async () => {
|
||||
process.env.NUXT_APP_BUILD_ASSETS_DIR = '/_other/'
|
||||
process.env.NUXT_APP_BASE_URL = '/foo/'
|
||||
await startServer()
|
||||
|
||||
const html = await $fetch('/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"/g)) {
|
||||
const url = match[2]
|
||||
// TODO: webpack does not yet support dynamic static paths
|
||||
expect(url.startsWith('/foo/_other/') || url === '/foo/public.svg' || (process.env.TEST_WITH_WEBPACK && url === '/public.svg')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should allow setting CDN URL', async () => {
|
||||
process.env.NUXT_APP_BASE_URL = '/foo/'
|
||||
process.env.NUXT_APP_CDN_URL = 'https://example.com/'
|
||||
process.env.NUXT_APP_BUILD_ASSETS_DIR = '/_cdn/'
|
||||
await startServer()
|
||||
|
||||
const html = await $fetch('/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"/g)) {
|
||||
const url = match[2]
|
||||
// TODO: webpack does not yet support dynamic static paths
|
||||
expect(url.startsWith('https://example.com/_cdn/') || url === 'https://example.com/public.svg' || (process.env.TEST_WITH_WEBPACK && url === '/public.svg')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
18
test/fixtures/basic/assets/logo.svg
vendored
Normal file
18
test/fixtures/basic/assets/logo.svg
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<svg viewBox="0 0 221 65" fill="none" xmlns="http://www.w3.org/2000/svg" class="h-8">
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="currentColor"
|
||||
d="M82.5623 18.5705h7.3017l15.474 24.7415V18.5705h6.741v35.0576h-7.252L89.3025 28.938v24.6901h-6.7402V18.5705ZM142.207 53.628h-6.282v-3.916c-1.429 2.7559-4.339 4.3076-8.015 4.3076-5.822 0-9.603-4.1069-9.603-10.0175V28.3847h6.282v14.3251c0 3.4558 2.146 5.8592 5.362 5.8592 3.524 0 5.974-2.7044 5.974-6.4099V28.3847h6.282V53.628ZM164.064 53.2289l-6.026-8.4144-6.027 8.4144h-6.69l9.296-13.1723-8.58-12.0709h6.843l5.158 7.2641 5.106-7.2641h6.895l-8.632 12.0709 9.295 13.1723h-6.638ZM183.469 20.7726v7.6116h7.149v5.1593h-7.149v12.5311c0 .4208.17.8245.473 1.1223.303.2978.715.4654 1.144.4661h5.532v5.9547h-4.137c-5.617 0-9.293-3.2062-9.293-8.8109V33.5484h-5.056v-5.1642h3.172c1.479 0 2.34-.8639 2.34-2.2932v-5.3184h5.825Z">
|
||||
</path>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M30.1185 11.5456c-1.8853-3.24168-6.5987-3.24169-8.484 0L1.08737 46.8747c-1.885324 3.2417.47133 7.2938 4.24199 7.2938H21.3695c-1.6112-1.4081-2.2079-3.8441-.9886-5.9341l15.5615-26.675-5.8239-10.0138Z"
|
||||
fill="#80EEC0"></path>
|
||||
<path
|
||||
d="M43.1374 19.2952c1.5603-2.6523 5.461-2.6523 7.0212 0l17.0045 28.9057c1.5603 2.6522-.39 5.9676-3.5106 5.9676h-34.009c-3.1206 0-5.0709-3.3154-3.5106-5.9676l17.0045-28.9057ZM209.174 53.8005H198.483c0-1.8514.067-3.4526 0-6.0213h10.641c1.868 0 3.353.1001 4.354-.934 1-1.0341 1.501-2.3351 1.501-3.9029 0-1.8347-.667-3.2191-2.002-4.1532-1.301-.9674-2.985-1.4511-5.054-1.4511h-2.601v-5.2539h2.652c1.701 0 3.119-.4003 4.253-1.2009 1.134-.8006 1.701-1.9849 1.701-3.5527 0-1.301-.434-2.3351-1.301-3.1023-.834-.8007-2.001-1.201-3.503-1.201-1.634 0-2.918.4837-3.853 1.4511-.9.9674-1.401 2.1517-1.501 3.5527h-6.254c.133-3.2358 1.251-5.7877 3.352-7.6558 2.135-1.868 4.887-2.8021 8.256-2.8021 2.402 0 4.42.4337 6.055 1.301 1.668.834 2.919 1.9515 3.753 3.3525.867 1.4011 1.301 2.9523 1.301 4.6536 0 1.9681-.551 3.636-1.651 5.0037-1.068 1.3344-2.402 2.235-4.004 2.7021 1.969.4003 3.57 1.3677 4.804 2.9022 1.234 1.5011 1.852 3.4025 1.852 5.7043 0 1.9347-.468 3.7028-1.402 5.304-.934 1.6012-2.301 2.8855-4.103 3.8529-1.768.9674-3.953 1.4511-6.555 1.4511Z"
|
||||
fill="#00DC82"></path>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill="#fff" d="M0 0h221v65H0z"></path>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
20
test/fixtures/basic/pages/assets.vue
vendored
Normal file
20
test/fixtures/basic/pages/assets.vue
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<img src="~/assets/logo.svg" class="h-20 mb-4">
|
||||
<img src="/public.svg" class="h-20 mb-4">
|
||||
<img :src="logo" class="h-20 mb-4">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import logo from '~/assets/logo.svg'
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#__nuxt {
|
||||
background-image: url('~/assets/logo.svg');
|
||||
}
|
||||
body {
|
||||
background-image: url('/public.svg');
|
||||
}
|
||||
</style>
|
18
test/fixtures/basic/public/public.svg
vendored
Normal file
18
test/fixtures/basic/public/public.svg
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<svg viewBox="0 0 221 65" fill="none" xmlns="http://www.w3.org/2000/svg" class="h-8">
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="currentColor"
|
||||
d="M82.5623 18.5705h7.3017l15.474 24.7415V18.5705h6.741v35.0576h-7.252L89.3025 28.938v24.6901h-6.7402V18.5705ZM142.207 53.628h-6.282v-3.916c-1.429 2.7559-4.339 4.3076-8.015 4.3076-5.822 0-9.603-4.1069-9.603-10.0175V28.3847h6.282v14.3251c0 3.4558 2.146 5.8592 5.362 5.8592 3.524 0 5.974-2.7044 5.974-6.4099V28.3847h6.282V53.628ZM164.064 53.2289l-6.026-8.4144-6.027 8.4144h-6.69l9.296-13.1723-8.58-12.0709h6.843l5.158 7.2641 5.106-7.2641h6.895l-8.632 12.0709 9.295 13.1723h-6.638ZM183.469 20.7726v7.6116h7.149v5.1593h-7.149v12.5311c0 .4208.17.8245.473 1.1223.303.2978.715.4654 1.144.4661h5.532v5.9547h-4.137c-5.617 0-9.293-3.2062-9.293-8.8109V33.5484h-5.056v-5.1642h3.172c1.479 0 2.34-.8639 2.34-2.2932v-5.3184h5.825Z">
|
||||
</path>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M30.1185 11.5456c-1.8853-3.24168-6.5987-3.24169-8.484 0L1.08737 46.8747c-1.885324 3.2417.47133 7.2938 4.24199 7.2938H21.3695c-1.6112-1.4081-2.2079-3.8441-.9886-5.9341l15.5615-26.675-5.8239-10.0138Z"
|
||||
fill="#80EEC0"></path>
|
||||
<path
|
||||
d="M43.1374 19.2952c1.5603-2.6523 5.461-2.6523 7.0212 0l17.0045 28.9057c1.5603 2.6522-.39 5.9676-3.5106 5.9676h-34.009c-3.1206 0-5.0709-3.3154-3.5106-5.9676l17.0045-28.9057ZM209.174 53.8005H198.483c0-1.8514.067-3.4526 0-6.0213h10.641c1.868 0 3.353.1001 4.354-.934 1-1.0341 1.501-2.3351 1.501-3.9029 0-1.8347-.667-3.2191-2.002-4.1532-1.301-.9674-2.985-1.4511-5.054-1.4511h-2.601v-5.2539h2.652c1.701 0 3.119-.4003 4.253-1.2009 1.134-.8006 1.701-1.9849 1.701-3.5527 0-1.301-.434-2.3351-1.301-3.1023-.834-.8007-2.001-1.201-3.503-1.201-1.634 0-2.918.4837-3.853 1.4511-.9.9674-1.401 2.1517-1.501 3.5527h-6.254c.133-3.2358 1.251-5.7877 3.352-7.6558 2.135-1.868 4.887-2.8021 8.256-2.8021 2.402 0 4.42.4337 6.055 1.301 1.668.834 2.919 1.9515 3.753 3.3525.867 1.4011 1.301 2.9523 1.301 4.6536 0 1.9681-.551 3.636-1.651 5.0037-1.068 1.3344-2.402 2.235-4.004 2.7021 1.969.4003 3.57 1.3677 4.804 2.9022 1.234 1.5011 1.852 3.4025 1.852 5.7043 0 1.9347-.468 3.7028-1.402 5.304-.934 1.6012-2.301 2.8855-4.103 3.8529-1.768.9674-3.953 1.4511-6.555 1.4511Z"
|
||||
fill="#00DC82"></path>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill="#fff" d="M0 0h221v65H0z"></path>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
Loading…
Reference in New Issue
Block a user