From b803fdb4f94a16cb80df1ca4640b26471e3c6491 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 24 Jan 2022 12:57:24 +0000 Subject: [PATCH] fix(vite): handle emitted assets with relative urls and dynamic base (#2851) --- packages/vite/src/client.ts | 6 ++--- packages/vite/src/plugins/dynamic-base.ts | 33 +++++++++++++++++++++++ packages/vite/src/vite.ts | 2 +- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/vite/src/client.ts b/packages/vite/src/client.ts index 6375097aab..aef4a18aa1 100644 --- a/packages/vite/src/client.ts +++ b/packages/vite/src/client.ts @@ -5,14 +5,14 @@ import vuePlugin from '@vitejs/plugin-vue' import viteJsxPlugin from '@vitejs/plugin-vue-jsx' import type { Connect } from 'vite' -import { joinURL, withoutLeadingSlash } from 'ufo' +import { joinURL } from 'ufo' import { cacheDirPlugin } from './plugins/cache-dir' import { analyzePlugin } from './plugins/analyze' import { wpfs } from './utils/wpfs' import type { ViteBuildContext, ViteOptions } from './vite' import { writeManifest } from './manifest' 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) { const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, { @@ -27,7 +27,6 @@ export async function buildClient (ctx: ViteBuildContext) { } }, build: { - assetsDir: ctx.nuxt.options.dev ? withoutLeadingSlash(ctx.nuxt.options.app.buildAssetsDir) : '.', rollupOptions: { output: { chunkFileNames: ctx.nuxt.options.dev ? undefined : '[name]-[hash].mjs', @@ -42,6 +41,7 @@ export async function buildClient (ctx: ViteBuildContext) { vuePlugin(ctx.config.vue), viteJsxPlugin(), DynamicBasePlugin.vite({ env: 'client', devAppConfig: ctx.nuxt.options.app }), + RelativeAssetPlugin(), devStyleSSRPlugin({ rootDir: ctx.nuxt.options.rootDir, buildAssetsURL: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir) diff --git a/packages/vite/src/plugins/dynamic-base.ts b/packages/vite/src/plugins/dynamic-base.ts index f2aaca0b03..c709f1283b 100644 --- a/packages/vite/src/plugins/dynamic-base.ts +++ b/packages/vite/src/plugins/dynamic-base.ts @@ -1,4 +1,5 @@ import { createUnplugin } from 'unplugin' +import type { Plugin } from 'vite' interface DynamicBasePluginOptions { env: 'dev' | 'server' | 'client' @@ -6,6 +7,31 @@ interface DynamicBasePluginOptions { 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) { return { name: 'nuxt:dynamic-base-path', @@ -13,6 +39,7 @@ export const DynamicBasePlugin = createUnplugin(function (options: DynamicBasePl if (id.startsWith('/__NUXT_BASE__')) { return id.replace('/__NUXT_BASE__', '') } + return null }, enforce: 'post', transform (code, id) { @@ -21,6 +48,12 @@ export const DynamicBasePlugin = createUnplugin(function (options: DynamicBasePl `${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 =')) { code = 'const NUXT_BASE = NUXT_CONFIG.app.cdnURL || NUXT_CONFIG.app.baseURL;' + code diff --git a/packages/vite/src/vite.ts b/packages/vite/src/vite.ts index 3fa502e214..10b59d549e 100644 --- a/packages/vite/src/vite.ts +++ b/packages/vite/src/vite.ts @@ -74,7 +74,7 @@ export async function bundle (nuxt: Nuxt) { }, clearScreen: false, build: { - assetsDir: withoutLeadingSlash(nuxt.options.app.buildAssetsDir), + assetsDir: nuxt.options.dev ? withoutLeadingSlash(nuxt.options.app.buildAssetsDir) : '.', emptyOutDir: false, rollupOptions: { input: resolve(nuxt.options.appDir, 'entry'),