fix(vite): handle emitted assets with relative urls and dynamic base (#2851)

This commit is contained in:
Daniel Roe 2022-01-24 12:57:24 +00:00 committed by GitHub
parent 0b0e56ae4f
commit b803fdb4f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 4 deletions

View File

@ -5,14 +5,14 @@ import vuePlugin from '@vitejs/plugin-vue'
import viteJsxPlugin from '@vitejs/plugin-vue-jsx' import viteJsxPlugin from '@vitejs/plugin-vue-jsx'
import type { Connect } from 'vite' import type { Connect } from 'vite'
import { joinURL, withoutLeadingSlash } from 'ufo' import { joinURL } from 'ufo'
import { cacheDirPlugin } from './plugins/cache-dir' import { cacheDirPlugin } from './plugins/cache-dir'
import { analyzePlugin } from './plugins/analyze' import { analyzePlugin } from './plugins/analyze'
import { wpfs } from './utils/wpfs' import { wpfs } from './utils/wpfs'
import type { ViteBuildContext, ViteOptions } from './vite' import type { ViteBuildContext, ViteOptions } from './vite'
import { writeManifest } from './manifest' import { writeManifest } from './manifest'
import { devStyleSSRPlugin } from './plugins/dev-ssr-css' import { devStyleSSRPlugin } from './plugins/dev-ssr-css'
import { DynamicBasePlugin } from './plugins/dynamic-base' import { DynamicBasePlugin, RelativeAssetPlugin } from './plugins/dynamic-base'
export async function buildClient (ctx: ViteBuildContext) { export async function buildClient (ctx: ViteBuildContext) {
const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, { const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
@ -27,7 +27,6 @@ export async function buildClient (ctx: ViteBuildContext) {
} }
}, },
build: { build: {
assetsDir: ctx.nuxt.options.dev ? withoutLeadingSlash(ctx.nuxt.options.app.buildAssetsDir) : '.',
rollupOptions: { rollupOptions: {
output: { output: {
chunkFileNames: ctx.nuxt.options.dev ? undefined : '[name]-[hash].mjs', chunkFileNames: ctx.nuxt.options.dev ? undefined : '[name]-[hash].mjs',
@ -42,6 +41,7 @@ export async function buildClient (ctx: ViteBuildContext) {
vuePlugin(ctx.config.vue), vuePlugin(ctx.config.vue),
viteJsxPlugin(), viteJsxPlugin(),
DynamicBasePlugin.vite({ env: 'client', devAppConfig: ctx.nuxt.options.app }), DynamicBasePlugin.vite({ env: 'client', devAppConfig: ctx.nuxt.options.app }),
RelativeAssetPlugin(),
devStyleSSRPlugin({ devStyleSSRPlugin({
rootDir: ctx.nuxt.options.rootDir, rootDir: ctx.nuxt.options.rootDir,
buildAssetsURL: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir) buildAssetsURL: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir)

View File

@ -1,4 +1,5 @@
import { createUnplugin } from 'unplugin' import { createUnplugin } from 'unplugin'
import type { Plugin } from 'vite'
interface DynamicBasePluginOptions { interface DynamicBasePluginOptions {
env: 'dev' | 'server' | 'client' env: 'dev' | 'server' | 'client'
@ -6,6 +7,31 @@ interface DynamicBasePluginOptions {
globalPublicPath?: string globalPublicPath?: string
} }
const escapeRE = (str: string) => str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
export const RelativeAssetPlugin = function (): Plugin {
return {
name: 'nuxt:vite-relative-asset',
generateBundle (_, bundle) {
const generatedAssets = Object.entries(bundle).filter(([_, asset]) => asset.type === 'asset').map(([key]) => escapeRE(key))
const assetRE = new RegExp(`\\/__NUXT_BASE__\\/(${generatedAssets.join('|')})`, 'g')
for (const file in bundle) {
const asset = bundle[file]
if (asset.type === 'asset') {
const depth = file.split('/').length - 1
const assetBase = depth === 0 ? '.' : Array.from({ length: depth }).map(() => '..').join('/')
asset.source = asset.source.toString().replace(assetRE, r => r.replace(/\/__NUXT_BASE__/g, assetBase))
const publicBase = Array.from({ length: depth + 1 }).map(() => '..').join('/')
asset.source = asset.source.toString().replace(/\/__NUXT_BASE__/g, publicBase)
}
}
}
}
}
const VITE_ASSET_RE = /^export default ["'](__VITE_ASSET.*)["']$/
export const DynamicBasePlugin = createUnplugin(function (options: DynamicBasePluginOptions) { export const DynamicBasePlugin = createUnplugin(function (options: DynamicBasePluginOptions) {
return { return {
name: 'nuxt:dynamic-base-path', name: 'nuxt:dynamic-base-path',
@ -13,6 +39,7 @@ export const DynamicBasePlugin = createUnplugin(function (options: DynamicBasePl
if (id.startsWith('/__NUXT_BASE__')) { if (id.startsWith('/__NUXT_BASE__')) {
return id.replace('/__NUXT_BASE__', '') return id.replace('/__NUXT_BASE__', '')
} }
return null
}, },
enforce: 'post', enforce: 'post',
transform (code, id) { transform (code, id) {
@ -21,6 +48,12 @@ export const DynamicBasePlugin = createUnplugin(function (options: DynamicBasePl
`${options.globalPublicPath} = joinURL(NUXT_BASE, NUXT_CONFIG.app.buildAssetsDir);` + code `${options.globalPublicPath} = joinURL(NUXT_BASE, NUXT_CONFIG.app.buildAssetsDir);` + code
} }
const assetId = code.match(VITE_ASSET_RE)
if (assetId) {
code = 'import { joinURL } from "ufo";' +
`export default joinURL(NUXT_BASE, NUXT_CONFIG.app.buildAssetsDir, "${assetId[1]}".replace("/__NUXT_BASE__", ""));`
}
if (code.includes('NUXT_BASE') && !code.includes('const NUXT_BASE =')) { if (code.includes('NUXT_BASE') && !code.includes('const NUXT_BASE =')) {
code = 'const NUXT_BASE = NUXT_CONFIG.app.cdnURL || NUXT_CONFIG.app.baseURL;' + code code = 'const NUXT_BASE = NUXT_CONFIG.app.cdnURL || NUXT_CONFIG.app.baseURL;' + code

View File

@ -74,7 +74,7 @@ export async function bundle (nuxt: Nuxt) {
}, },
clearScreen: false, clearScreen: false,
build: { build: {
assetsDir: withoutLeadingSlash(nuxt.options.app.buildAssetsDir), assetsDir: nuxt.options.dev ? withoutLeadingSlash(nuxt.options.app.buildAssetsDir) : '.',
emptyOutDir: false, emptyOutDir: false,
rollupOptions: { rollupOptions: {
input: resolve(nuxt.options.appDir, 'entry'), input: resolve(nuxt.options.appDir, 'entry'),