mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
fix(vite): handle runtime paths in inlined styles (#27327)
This commit is contained in:
parent
6a271e8a56
commit
c054ca084c
@ -3,10 +3,11 @@ import { useNitro } from '@nuxt/kit'
|
||||
import { createUnplugin } from 'unplugin'
|
||||
import { withLeadingSlash, withTrailingSlash } from 'ufo'
|
||||
import { dirname, relative } from 'pathe'
|
||||
import MagicString from 'magic-string'
|
||||
|
||||
const PREFIX = 'virtual:public?'
|
||||
|
||||
export const VitePublicDirsPlugin = createUnplugin(() => {
|
||||
export const VitePublicDirsPlugin = createUnplugin((options: { sourcemap?: boolean }) => {
|
||||
const nitro = useNitro()
|
||||
|
||||
function resolveFromPublicAssets (id: string) {
|
||||
@ -40,14 +41,33 @@ export const VitePublicDirsPlugin = createUnplugin(() => {
|
||||
}
|
||||
},
|
||||
},
|
||||
generateBundle (outputOptions, bundle) {
|
||||
renderChunk (code, chunk) {
|
||||
if (!chunk.facadeModuleId?.includes('?inline&used')) { return }
|
||||
|
||||
const s = new MagicString(code)
|
||||
const q = code.match(/(?<= = )['"`]/)?.[0] || '"'
|
||||
for (const [full, url] of code.matchAll(CSS_URL_RE)) {
|
||||
if (resolveFromPublicAssets(url)) {
|
||||
s.replace(full, `url(${q} + publicAssetsURL(${q}${url}${q}) + ${q})`)
|
||||
}
|
||||
}
|
||||
|
||||
if (s.hasChanged()) {
|
||||
s.prepend(`import { publicAssetsURL } from '#internal/nuxt/paths';`)
|
||||
return {
|
||||
code: s.toString(),
|
||||
map: options.sourcemap ? s.generateMap({ hires: true }) : undefined,
|
||||
}
|
||||
}
|
||||
},
|
||||
generateBundle (_outputOptions, bundle) {
|
||||
for (const file in bundle) {
|
||||
const chunk = bundle[file]
|
||||
if (!file.endsWith('.css') || chunk.type !== 'asset') { continue }
|
||||
|
||||
let css = chunk.source.toString()
|
||||
let wasReplaced = false
|
||||
for (const [full, url] of css.matchAll(/url\((\/[^)]+)\)/g)) {
|
||||
for (const [full, url] of css.matchAll(CSS_URL_RE)) {
|
||||
if (resolveFromPublicAssets(url)) {
|
||||
const relativeURL = relative(withLeadingSlash(dirname(file)), url)
|
||||
css = css.replace(full, `url(${relativeURL})`)
|
||||
@ -62,3 +82,5 @@ export const VitePublicDirsPlugin = createUnplugin(() => {
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const CSS_URL_RE = /url\((\/[^)]+)\)/g
|
||||
|
@ -100,7 +100,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
||||
},
|
||||
plugins: [
|
||||
// add resolver for files in public assets directories
|
||||
VitePublicDirsPlugin.vite(),
|
||||
VitePublicDirsPlugin.vite({ sourcemap: !!nuxt.options.sourcemap.server }),
|
||||
composableKeysPlugin.vite({
|
||||
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
||||
rootDir: nuxt.options.rootDir,
|
||||
|
@ -1915,11 +1915,24 @@ describe('public directories', () => {
|
||||
|
||||
// TODO: dynamic paths in dev
|
||||
describe.skipIf(isDev())('dynamic paths', () => {
|
||||
const publicFiles = ['/public.svg', '/css-only-public-asset.svg']
|
||||
const isPublicFile = (base = '/', file: string) => {
|
||||
if (isWebpack) {
|
||||
// TODO: webpack does not yet support dynamic static paths
|
||||
expect(publicFiles).toContain(file)
|
||||
return true
|
||||
}
|
||||
|
||||
expect(file).toMatch(new RegExp(`^${base.replace(/\//g, '\\/')}`))
|
||||
expect(publicFiles).toContain(file.replace(base, '/'))
|
||||
return true
|
||||
}
|
||||
|
||||
it('should work with no overrides', async () => {
|
||||
const html: string = await $fetch<string>('/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"|url\(([^)]*)\)/g)) {
|
||||
const url = match[2] || match[3]
|
||||
expect(url.startsWith('/_nuxt/') || url === '/public.svg').toBeTruthy()
|
||||
expect(url.startsWith('/_nuxt/') || isPublicFile('/', url)).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
@ -1929,16 +1942,14 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
||||
const urls = Array.from(html.matchAll(/(href|src)="(.*?)"|url\(([^)]*)\)/g)).map(m => m[2] || m[3])
|
||||
const cssURL = urls.find(u => /_nuxt\/assets.*\.css$/.test(u))
|
||||
expect(cssURL).toBeDefined()
|
||||
const css: string = await $fetch<string>(cssURL!)
|
||||
const imageUrls = Array.from(css.matchAll(/url\(([^)]*)\)/g)).map(m => m[1].replace(/[-.]\w{8}\./g, '.'))
|
||||
expect(imageUrls).toMatchInlineSnapshot(`
|
||||
[
|
||||
"./logo.svg",
|
||||
"../public.svg",
|
||||
"../public.svg",
|
||||
"../public.svg",
|
||||
]
|
||||
`)
|
||||
const css = await $fetch<string>(cssURL!)
|
||||
const imageUrls = new Set(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 () => {
|
||||
@ -1952,12 +1963,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
||||
const html = await $fetch<string>('/foo/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"|url\(([^)]*)\)/g)) {
|
||||
const url = match[2] || match[3]
|
||||
expect(
|
||||
url.startsWith('/foo/_other/') ||
|
||||
url === '/foo/public.svg' ||
|
||||
// TODO: webpack does not yet support dynamic static paths
|
||||
(isWebpack && url === '/public.svg'),
|
||||
).toBeTruthy()
|
||||
expect(url.startsWith('/foo/_other/') || isPublicFile('/foo/', url)).toBeTruthy()
|
||||
}
|
||||
|
||||
expect(await $fetch<string>('/foo/url')).toContain('path: /foo/url')
|
||||
@ -1973,12 +1979,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
||||
const html = await $fetch<string>('/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"|url\(([^)]*)\)/g)) {
|
||||
const url = match[2] || match[3]
|
||||
expect(
|
||||
url.startsWith('./_nuxt/') ||
|
||||
url === './public.svg' ||
|
||||
// TODO: webpack does not yet support dynamic static paths
|
||||
(isWebpack && url === '/public.svg'),
|
||||
).toBeTruthy()
|
||||
expect(url.startsWith('./_nuxt/') || isPublicFile('./', url)).toBeTruthy()
|
||||
expect(url.startsWith('./_nuxt/_nuxt')).toBeFalsy()
|
||||
}
|
||||
})
|
||||
@ -2007,12 +2008,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
||||
const html = await $fetch<string>('/foo/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"|url\(([^)]*)\)/g)) {
|
||||
const url = match[2] || match[3]
|
||||
expect(
|
||||
url.startsWith('https://example.com/_cdn/') ||
|
||||
url === 'https://example.com/public.svg' ||
|
||||
// TODO: webpack does not yet support dynamic static paths
|
||||
(isWebpack && url === '/public.svg'),
|
||||
).toBeTruthy()
|
||||
expect(url.startsWith('https://example.com/_cdn/') || isPublicFile('https://example.com/', url)).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
|
1
test/fixtures/basic/assets/global.css
vendored
1
test/fixtures/basic/assets/global.css
vendored
@ -1,4 +1,5 @@
|
||||
:root {
|
||||
--global: 'global';
|
||||
--asset: url('~/assets/css-only-asset.svg');
|
||||
--public-asset: url('/css-only-public-asset.svg');
|
||||
}
|
||||
|
1
test/fixtures/basic/public/css-only-public-asset.svg
vendored
Normal file
1
test/fixtures/basic/public/css-only-public-asset.svg
vendored
Normal file
@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg"></svg>
|
After Width: | Height: | Size: 67 B |
Loading…
Reference in New Issue
Block a user