mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-18 01:15:58 +00:00
feat: improve base url options (#2655)
This commit is contained in:
parent
05e75426ce
commit
d07d572263
@ -15,6 +15,13 @@ export function setupNitroBridge () {
|
||||
throw new Error('[nitro] Please use `nuxt generate` for static target')
|
||||
}
|
||||
|
||||
// Handle legacy property name `assetsPath`
|
||||
nuxt.options.app.buildAssetsDir = nuxt.options.app.buildAssetsDir || nuxt.options.app.assetsPath
|
||||
nuxt.options.app.assetsPath = nuxt.options.app.buildAssetsDir
|
||||
// Nitro expects app config on `config.app` rather than `config._app`
|
||||
nuxt.options.publicRuntimeConfig.app = nuxt.options.publicRuntimeConfig.app || {}
|
||||
Object.assign(nuxt.options.publicRuntimeConfig.app, nuxt.options.publicRuntimeConfig._app)
|
||||
|
||||
// Disable loading-screen
|
||||
// @ts-ignore
|
||||
nuxt.options.build.loadingScreen = false
|
||||
|
@ -3,6 +3,7 @@ import * as vite from 'vite'
|
||||
import { createVuePlugin } from 'vite-plugin-vue2'
|
||||
import PluginLegacy from '@vitejs/plugin-legacy'
|
||||
import consola from 'consola'
|
||||
import { joinURL } from 'ufo'
|
||||
import { devStyleSSRPlugin } from '../../../vite/src/plugins/dev-ssr-css'
|
||||
import { jsxPlugin } from './plugins/jsx'
|
||||
import { ViteBuildContext, ViteOptions } from './types'
|
||||
@ -28,7 +29,6 @@ export async function buildClient (ctx: ViteBuildContext) {
|
||||
},
|
||||
build: {
|
||||
outDir: resolve(ctx.nuxt.options.buildDir, 'dist/client'),
|
||||
assetsDir: '.',
|
||||
rollupOptions: {
|
||||
input: resolve(ctx.nuxt.options.buildDir, 'client.js')
|
||||
},
|
||||
@ -39,7 +39,10 @@ export async function buildClient (ctx: ViteBuildContext) {
|
||||
jsxPlugin(),
|
||||
createVuePlugin(ctx.config.vue),
|
||||
PluginLegacy(),
|
||||
devStyleSSRPlugin(ctx.nuxt.options.rootDir)
|
||||
devStyleSSRPlugin({
|
||||
rootDir: ctx.nuxt.options.rootDir,
|
||||
buildAssetsURL: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir)
|
||||
})
|
||||
],
|
||||
server: {
|
||||
middlewareMode: true
|
||||
|
@ -46,7 +46,6 @@ export async function prepareManifests (ctx: ViteBuildContext) {
|
||||
export async function generateBuildManifest (ctx: ViteBuildContext) {
|
||||
const rDist = (...args: string[]): string => resolve(ctx.nuxt.options.buildDir, 'dist', ...args)
|
||||
|
||||
const publicPath = ctx.nuxt.options.app.assetsPath // Default: /nuxt/
|
||||
const viteClientManifest = await fse.readJSON(rDist('client/manifest.json'))
|
||||
const clientEntries = Object.entries(viteClientManifest)
|
||||
|
||||
@ -59,12 +58,13 @@ export async function generateBuildManifest (ctx: ViteBuildContext) {
|
||||
const polyfillName = initialEntries.find(id => id.startsWith('polyfills-legacy.'))
|
||||
|
||||
// @vitejs/plugin-legacy uses SystemJS which need to call `System.import` to load modules
|
||||
const clientImports = initialJs.filter(id => id !== polyfillName).map(id => publicPath + id)
|
||||
const clientImports = initialJs.filter(id => id !== polyfillName)
|
||||
const clientEntryCode = `var imports = ${JSON.stringify(clientImports)}\nimports.reduce((p, id) => p.then(() => System.import(id)), Promise.resolve())`
|
||||
const clientEntryName = 'entry-legacy.' + hash(clientEntryCode) + '.js'
|
||||
|
||||
const clientManifest = {
|
||||
publicPath,
|
||||
// This publicPath will be ignored by Nitro and computed dynamically
|
||||
publicPath: ctx.nuxt.options.app.buildAssetsDir,
|
||||
all: uniq([
|
||||
polyfillName,
|
||||
clientEntryName,
|
||||
@ -74,12 +74,12 @@ export async function generateBuildManifest (ctx: ViteBuildContext) {
|
||||
polyfillName,
|
||||
clientEntryName,
|
||||
...initialAssets
|
||||
],
|
||||
].filter(Boolean),
|
||||
async: [
|
||||
// We move initial entries to the client entry
|
||||
...initialJs,
|
||||
...asyncEntries
|
||||
],
|
||||
].filter(Boolean),
|
||||
modules: {},
|
||||
assetsMapping: {}
|
||||
}
|
||||
|
@ -51,7 +51,6 @@ export async function buildServer (ctx: ViteBuildContext) {
|
||||
},
|
||||
build: {
|
||||
outDir: resolve(ctx.nuxt.options.buildDir, 'dist/server'),
|
||||
assetsDir: ctx.nuxt.options.app.assetsPath.replace(/^\/|\/$/, ''),
|
||||
ssr: true,
|
||||
ssrManifest: true,
|
||||
rollupOptions: {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { resolve } from 'pathe'
|
||||
import * as vite from 'vite'
|
||||
import consola from 'consola'
|
||||
import { withoutLeadingSlash } from 'ufo'
|
||||
import { distDir } from '../dirs'
|
||||
import { warmupViteServer } from '../../../vite/src/utils/warmup'
|
||||
import { buildClient } from './client'
|
||||
@ -73,6 +74,7 @@ async function bundle (nuxt: Nuxt, builder: any) {
|
||||
publicDir: resolve(nuxt.options.srcDir, nuxt.options.dir.static),
|
||||
clearScreen: false,
|
||||
build: {
|
||||
assetsDir: withoutLeadingSlash(nuxt.options.app.buildAssetsDir),
|
||||
emptyOutDir: false
|
||||
},
|
||||
plugins: [
|
||||
|
@ -4,7 +4,7 @@ import * as rollup from 'rollup'
|
||||
import fse from 'fs-extra'
|
||||
import { printFSTree } from './utils/tree'
|
||||
import { getRollupConfig } from './rollup/config'
|
||||
import { hl, prettyPath, serializeTemplate, writeFile, isDirectory, readDirRecursively, replaceAll } from './utils'
|
||||
import { hl, prettyPath, serializeTemplate, writeFile, isDirectory, replaceAll } from './utils'
|
||||
import { NitroContext } from './context'
|
||||
import { scanMiddleware } from './server/middleware'
|
||||
|
||||
@ -30,20 +30,17 @@ async function cleanupDir (dir: string) {
|
||||
export async function generate (nitroContext: NitroContext) {
|
||||
consola.start('Generating public...')
|
||||
|
||||
await nitroContext._internal.hooks.callHook('nitro:generate', nitroContext)
|
||||
|
||||
const publicDir = nitroContext._nuxt.publicDir
|
||||
let publicFiles: string[] = []
|
||||
if (await isDirectory(publicDir)) {
|
||||
publicFiles = readDirRecursively(publicDir).map(r => r.replace(publicDir, ''))
|
||||
await fse.copy(publicDir, nitroContext.output.publicDir)
|
||||
}
|
||||
|
||||
const clientDist = resolve(nitroContext._nuxt.buildDir, 'dist/client')
|
||||
if (await isDirectory(clientDist)) {
|
||||
await fse.copy(clientDist, join(nitroContext.output.publicDir, nitroContext._nuxt.publicPath), {
|
||||
// TODO: Workaround vite's issue that duplicates public files
|
||||
// https://github.com/nuxt/framework/issues/1192
|
||||
filter: src => !publicFiles.includes(src.replace(clientDist, ''))
|
||||
})
|
||||
const buildAssetsDir = join(nitroContext.output.publicDir, nitroContext._nuxt.buildAssetsDir)
|
||||
await fse.copy(clientDist, buildAssetsDir)
|
||||
}
|
||||
|
||||
consola.success('Generated public ' + prettyPath(nitroContext.output.publicDir))
|
||||
|
@ -19,6 +19,7 @@ export interface NitroHooks {
|
||||
'nitro:document': (htmlTemplate: { src: string, contents: string, dst: string }) => void
|
||||
'nitro:rollup:before': (context: NitroContext) => void | Promise<void>
|
||||
'nitro:compiled': (context: NitroContext) => void
|
||||
'nitro:generate': (context: NitroContext) => void | Promise<void>
|
||||
'close': () => void
|
||||
}
|
||||
|
||||
@ -71,8 +72,8 @@ export interface NitroContext {
|
||||
generateDir: string
|
||||
publicDir: string
|
||||
serverDir: string
|
||||
routerBase: string
|
||||
publicPath: string
|
||||
baseURL: string
|
||||
buildAssetsDir: string
|
||||
isStatic: boolean
|
||||
fullStatic: boolean
|
||||
staticAssets: any
|
||||
@ -139,8 +140,8 @@ export function getNitroContext (nuxtOptions: NuxtOptions, input: NitroInput): N
|
||||
generateDir: nuxtOptions.generate.dir,
|
||||
publicDir: resolve(nuxtOptions.srcDir, nuxtOptions.dir.public || nuxtOptions.dir.static),
|
||||
serverDir: resolve(nuxtOptions.srcDir, (nuxtOptions.dir as any).server || 'server'),
|
||||
routerBase: nuxtOptions.router.base,
|
||||
publicPath: nuxtOptions.app.assetsPath,
|
||||
baseURL: nuxtOptions.app.baseURL,
|
||||
buildAssetsDir: nuxtOptions.app.buildAssetsDir,
|
||||
isStatic: nuxtOptions.target === 'static' && !nuxtOptions.dev,
|
||||
fullStatic: nuxtOptions.target === 'static' && !nuxtOptions._legacyGenerate,
|
||||
staticAssets: nuxtOptions.generate.staticAssets,
|
||||
|
@ -1,17 +1,19 @@
|
||||
import { existsSync, promises as fsp } from 'fs'
|
||||
import { resolve } from 'pathe'
|
||||
import consola from 'consola'
|
||||
import { joinURL } from 'ufo'
|
||||
import { extendPreset, prettyPath } from '../utils'
|
||||
import { NitroPreset, NitroContext, NitroInput } from '../context'
|
||||
import { worker } from './worker'
|
||||
|
||||
export const browser: NitroPreset = extendPreset(worker, (input: NitroInput) => {
|
||||
const routerBase = input._nuxt.routerBase
|
||||
// TODO: Join base at runtime
|
||||
const baseURL = input._nuxt.baseURL
|
||||
|
||||
const script = `<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function () {
|
||||
navigator.serviceWorker.register('${routerBase}sw.js');
|
||||
navigator.serviceWorker.register('${joinURL(baseURL, 'sw.js')}');
|
||||
});
|
||||
}
|
||||
</script>`
|
||||
@ -21,11 +23,11 @@ if ('serviceWorker' in navigator) {
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="prefetch" href="${routerBase}sw.js">
|
||||
<link rel="prefetch" href="${routerBase}_server/index.mjs">
|
||||
<link rel="prefetch" href="${joinURL(baseURL, 'sw.js')}">
|
||||
<link rel="prefetch" href="${joinURL(baseURL, '_server/index.mjs')}">
|
||||
<script>
|
||||
async function register () {
|
||||
const registration = await navigator.serviceWorker.register('${routerBase}sw.js')
|
||||
const registration = await navigator.serviceWorker.register('${joinURL(baseURL, 'sw.js')}')
|
||||
await navigator.serviceWorker.ready
|
||||
registration.active.addEventListener('statechange', (event) => {
|
||||
if (event.target.state === 'activated') {
|
||||
@ -62,7 +64,7 @@ if ('serviceWorker' in navigator) {
|
||||
tmpl.contents = tmpl.contents.replace('</body>', script + '</body>')
|
||||
},
|
||||
async 'nitro:compiled' ({ output }: NitroContext) {
|
||||
await fsp.writeFile(resolve(output.publicDir, 'sw.js'), `self.importScripts('${input._nuxt.routerBase}_server/index.mjs');`, 'utf8')
|
||||
await fsp.writeFile(resolve(output.publicDir, 'sw.js'), `self.importScripts('${joinURL(baseURL, '_server/index.mjs')}');`, 'utf8')
|
||||
|
||||
// Temp fix
|
||||
if (!existsSync(resolve(output.publicDir, 'index.html'))) {
|
||||
|
@ -159,8 +159,6 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
||||
'process.server': 'true',
|
||||
'process.client': 'false',
|
||||
'process.env.NUXT_NO_SSR': JSON.stringify(!nitroContext._nuxt.ssr),
|
||||
'process.env.ROUTER_BASE': JSON.stringify(nitroContext._nuxt.routerBase),
|
||||
'process.env.PUBLIC_PATH': JSON.stringify(nitroContext._nuxt.publicPath),
|
||||
'process.env.NUXT_STATIC_BASE': JSON.stringify(nitroContext._nuxt.staticAssets.base),
|
||||
'process.env.NUXT_STATIC_VERSION': JSON.stringify(nitroContext._nuxt.staticAssets.version),
|
||||
'process.env.NUXT_FULL_STATIC': nitroContext._nuxt.fullStatic as unknown as string,
|
||||
@ -227,6 +225,7 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
||||
entries: {
|
||||
'#nitro': nitroContext._internal.runtimeDir,
|
||||
'#nitro-renderer': resolve(nitroContext._internal.runtimeDir, 'app', renderer),
|
||||
'#paths': resolve(nitroContext._internal.runtimeDir, 'app/paths'),
|
||||
'#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)
|
||||
|
@ -11,6 +11,12 @@ for (const type of ['private', 'public']) {
|
||||
}
|
||||
}
|
||||
|
||||
// Load dynamic app configuration
|
||||
const appConfig = _runtimeConfig.public.app
|
||||
appConfig.baseURL = process.env.NUXT_APP_BASE_URL || appConfig.baseURL
|
||||
appConfig.cdnURL = process.env.NUXT_APP_CDN_URL || appConfig.cdnURL
|
||||
appConfig.buildAssetsDir = process.env.NUXT_APP_BUILD_ASSETS_DIR || appConfig.buildAssetsDir
|
||||
|
||||
// Named exports
|
||||
export const privateConfig = deepFreeze(defu(_runtimeConfig.private, _runtimeConfig.public))
|
||||
export const publicConfig = deepFreeze(_runtimeConfig.public)
|
||||
|
19
packages/nitro/src/runtime/app/paths.ts
Normal file
19
packages/nitro/src/runtime/app/paths.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { joinURL } from 'ufo'
|
||||
import config from '#config'
|
||||
|
||||
export function baseURL () {
|
||||
return config.app.baseURL
|
||||
}
|
||||
|
||||
export function buildAssetsDir () {
|
||||
return config.app.buildAssetsDir
|
||||
}
|
||||
|
||||
export function buildAssetsURL (...path: string[]) {
|
||||
return joinURL(publicAssetsURL(), config.app.buildAssetsDir, ...path)
|
||||
}
|
||||
|
||||
export function publicAssetsURL (...path: string[]) {
|
||||
const publicBase = config.app.cdnURL || config.app.baseURL
|
||||
return path.length ? joinURL(publicBase, ...path) : publicBase
|
||||
}
|
@ -2,6 +2,7 @@ import type { ServerResponse } from 'http'
|
||||
import { createRenderer } from 'vue-bundle-renderer'
|
||||
import devalue from '@nuxt/devalue'
|
||||
import { privateConfig, publicConfig } from './config'
|
||||
import { buildAssetsURL } from './paths'
|
||||
// @ts-ignore
|
||||
import htmlTemplate from '#build/views/document.template.mjs'
|
||||
|
||||
@ -12,8 +13,6 @@ const PAYLOAD_JS = '/payload.js'
|
||||
const getClientManifest = cachedImport(() => import('#build/dist/server/client.manifest.mjs'))
|
||||
const getSSRApp = !process.env.NUXT_NO_SSR && cachedImport(() => import('#build/dist/server/server.mjs'))
|
||||
|
||||
const publicPath = (publicConfig.app && publicConfig.app.assetsPath) || process.env.PUBLIC_PATH || '/_nuxt'
|
||||
|
||||
const getSSRRenderer = cachedResult(async () => {
|
||||
// Load client manifest
|
||||
const clientManifest = await getClientManifest()
|
||||
@ -23,7 +22,7 @@ const getSSRRenderer = cachedResult(async () => {
|
||||
if (!createSSRApp) { throw new Error('Server bundle is not available') }
|
||||
// Create renderer
|
||||
const { renderToString } = await import('#nitro-renderer')
|
||||
return createRenderer((createSSRApp), { clientManifest, renderToString, publicPath }).renderToString
|
||||
return createRenderer((createSSRApp), { clientManifest, renderToString, publicPath: buildAssetsURL() }).renderToString
|
||||
})
|
||||
|
||||
const getSPARenderer = cachedResult(async () => {
|
||||
@ -50,13 +49,13 @@ const getSPARenderer = cachedResult(async () => {
|
||||
entryFiles
|
||||
.flatMap(({ css }) => css)
|
||||
.filter(css => css != null)
|
||||
.map(file => `<link rel="stylesheet" href="${publicPath}${file}">`)
|
||||
.map(file => `<link rel="stylesheet" href="${buildAssetsURL(file)}">`)
|
||||
.join(''),
|
||||
renderScripts: () =>
|
||||
entryFiles
|
||||
.map(({ file }) => {
|
||||
const isMJS = !file.endsWith('.js')
|
||||
return `<script ${isMJS ? 'type="module"' : ''} src="${publicPath}${file}"></script>`
|
||||
return `<script ${isMJS ? 'type="module"' : ''} src="${buildAssetsURL(file)}"></script>`
|
||||
})
|
||||
.join('')
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import '#polyfill'
|
||||
import { getAssetFromKV } from '@cloudflare/kv-asset-handler'
|
||||
import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler'
|
||||
import { withoutBase } from 'ufo'
|
||||
import { localCall } from '../server'
|
||||
import { requestHasBody, useRequestBody } from '../server/utils'
|
||||
|
||||
const PUBLIC_PATH = process.env.PUBLIC_PATH // Default: /_nuxt/
|
||||
import { buildAssetsURL, baseURL } from '#paths'
|
||||
|
||||
addEventListener('fetch', (event: any) => {
|
||||
event.respondWith(handleEvent(event))
|
||||
@ -11,7 +11,7 @@ addEventListener('fetch', (event: any) => {
|
||||
|
||||
async function handleEvent (event) {
|
||||
try {
|
||||
return await getAssetFromKV(event, { cacheControl: assetsCacheControl })
|
||||
return await getAssetFromKV(event, { cacheControl: assetsCacheControl, mapRequestToAsset: baseURLModifier })
|
||||
} catch (_err) {
|
||||
// Ignore
|
||||
}
|
||||
@ -42,7 +42,7 @@ async function handleEvent (event) {
|
||||
}
|
||||
|
||||
function assetsCacheControl (request) {
|
||||
if (request.url.includes(PUBLIC_PATH) /* TODO: Check with routerBase */) {
|
||||
if (request.url.startsWith(buildAssetsURL())) {
|
||||
return {
|
||||
browserTTL: 31536000,
|
||||
edgeTTL: 31536000
|
||||
@ -50,3 +50,8 @@ function assetsCacheControl (request) {
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
const baseURLModifier = (request: Request) => {
|
||||
const url = withoutBase(request.url, baseURL())
|
||||
return mapRequestToAsset(new Request(url, request))
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { Server as HttpServer } from 'http'
|
||||
import { Server as HttpsServer } from 'https'
|
||||
import destr from 'destr'
|
||||
import { handle } from '../server'
|
||||
import { baseURL } from '#paths'
|
||||
|
||||
const cert = process.env.NITRO_SSL_CERT
|
||||
const key = process.env.NITRO_SSL_KEY
|
||||
@ -19,7 +20,7 @@ server.listen(port, hostname, (err) => {
|
||||
process.exit(1)
|
||||
}
|
||||
const protocol = cert && key ? 'https' : 'http'
|
||||
console.log(`Listening on ${protocol}://${hostname}:${port}`)
|
||||
console.log(`Listening on ${protocol}://${hostname}:${port}${baseURL()}`)
|
||||
})
|
||||
|
||||
export default {}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import '../app/config'
|
||||
import { createApp, useBase } from 'h3'
|
||||
import { createFetch, Headers } from 'ohmyfetch'
|
||||
import destr from 'destr'
|
||||
import { createCall, createFetch as createLocalFetch } from 'unenv/runtime/fetch/index'
|
||||
import { baseURL } from '../app/paths'
|
||||
import { timingMiddleware } from './timing'
|
||||
import { handleError } from './error'
|
||||
// @ts-ignore
|
||||
@ -18,7 +18,7 @@ app.use(serverMiddleware)
|
||||
app.use(() => import('../app/render').then(e => e.renderMiddleware), { lazy: true })
|
||||
|
||||
export const stack = app.stack
|
||||
export const handle = useBase(process.env.ROUTER_BASE, app)
|
||||
export const handle = useBase(baseURL(), app)
|
||||
export const localCall = createCall(handle)
|
||||
export const localFetch = createLocalFetch(localCall, globalThis.fetch)
|
||||
|
||||
|
@ -2,9 +2,10 @@ import { createError } from 'h3'
|
||||
import { withoutTrailingSlash, withLeadingSlash, parseURL } from 'ufo'
|
||||
// @ts-ignore
|
||||
import { getAsset, readAsset } from '#static'
|
||||
import { buildAssetsDir } from '#paths'
|
||||
|
||||
const METHODS = ['HEAD', 'GET']
|
||||
const PUBLIC_PATH = process.env.PUBLIC_PATH // Default: /_nuxt/
|
||||
|
||||
const TWO_DAYS = 2 * 60 * 60 * 24
|
||||
const STATIC_ASSETS_BASE = process.env.NUXT_STATIC_BASE + '/' + process.env.NUXT_STATIC_VERSION
|
||||
|
||||
@ -26,8 +27,10 @@ export default async function serveStatic (req, res) {
|
||||
}
|
||||
}
|
||||
|
||||
const isBuildAsset = id.startsWith(buildAssetsDir())
|
||||
|
||||
if (!asset) {
|
||||
if (id.startsWith(PUBLIC_PATH) && !id.startsWith(STATIC_ASSETS_BASE)) {
|
||||
if (isBuildAsset && !id.startsWith(STATIC_ASSETS_BASE)) {
|
||||
throw createError({
|
||||
statusMessage: 'Cannot find static asset ' + id,
|
||||
statusCode: 404
|
||||
@ -62,7 +65,7 @@ export default async function serveStatic (req, res) {
|
||||
res.setHeader('Last-Modified', asset.mtime)
|
||||
}
|
||||
|
||||
if (id.startsWith(PUBLIC_PATH)) {
|
||||
if (isBuildAsset) {
|
||||
res.setHeader('Cache-Control', `max-age=${TWO_DAYS}, immutable`)
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ import servePlaceholder from 'serve-placeholder'
|
||||
import serveStatic from 'serve-static'
|
||||
import { resolve } from 'pathe'
|
||||
import connect from 'connect'
|
||||
import { joinURL } from 'ufo'
|
||||
import type { NitroContext } from '../context'
|
||||
import { handleVfs } from './vfs'
|
||||
|
||||
@ -76,8 +77,9 @@ export function createDevServer (nitroContext: NitroContext) {
|
||||
const app = createApp()
|
||||
|
||||
// _nuxt and static
|
||||
app.use(nitroContext._nuxt.publicPath, serveStatic(resolve(nitroContext._nuxt.buildDir, 'dist/client')))
|
||||
app.use(nitroContext._nuxt.routerBase, serveStatic(resolve(nitroContext._nuxt.publicDir)))
|
||||
const buildAssetsURL = joinURL(nitroContext._nuxt.baseURL, nitroContext._nuxt.buildAssetsDir)
|
||||
app.use(buildAssetsURL, serveStatic(resolve(nitroContext._nuxt.buildDir, 'dist/client')))
|
||||
app.use(nitroContext._nuxt.baseURL, serveStatic(resolve(nitroContext._nuxt.publicDir)))
|
||||
|
||||
// debugging endpoint to view vfs
|
||||
app.use('/_vfs', useBase('/_vfs', handleVfs(nitroContext)))
|
||||
@ -89,7 +91,7 @@ export function createDevServer (nitroContext: NitroContext) {
|
||||
app.use(devMiddleware.middleware)
|
||||
|
||||
// serve placeholder 404 assets instead of hitting SSR
|
||||
app.use(nitroContext._nuxt.publicPath, servePlaceholder())
|
||||
app.use(buildAssetsURL, servePlaceholder())
|
||||
|
||||
// SSR Proxy
|
||||
const proxy = httpProxy.createProxy()
|
||||
|
@ -23,10 +23,10 @@ export interface ServerMiddleware {
|
||||
promisify?: boolean // Default is true
|
||||
}
|
||||
|
||||
function filesToMiddleware (files: string[], baseDir: string, basePath: string, overrides?: Partial<ServerMiddleware>): ServerMiddleware[] {
|
||||
function filesToMiddleware (files: string[], baseDir: string, baseURL: string, overrides?: Partial<ServerMiddleware>): ServerMiddleware[] {
|
||||
return files.map((file) => {
|
||||
const route = joinURL(
|
||||
basePath,
|
||||
baseURL,
|
||||
file
|
||||
.slice(0, file.length - extname(file).length)
|
||||
.replace(/\/index$/, '')
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createRequire } from 'module'
|
||||
import { relative, dirname, join, resolve } from 'pathe'
|
||||
import { relative, dirname, resolve } from 'pathe'
|
||||
import fse from 'fs-extra'
|
||||
import jiti from 'jiti'
|
||||
import defu from 'defu'
|
||||
@ -152,11 +152,3 @@ export function readPackageJson (
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export function readDirRecursively (dir: string) {
|
||||
return fse.readdirSync(dir).reduce((files, file) => {
|
||||
const name = join(dir, file)
|
||||
const isDirectory = fse.statSync(name).isDirectory()
|
||||
return isDirectory ? [...files, ...readDirRecursively(name)] : [...files, name]
|
||||
}, [])
|
||||
}
|
||||
|
8
packages/nitro/types/shims.d.ts
vendored
8
packages/nitro/types/shims.d.ts
vendored
@ -21,3 +21,11 @@ declare module '#config' {
|
||||
const runtimeConfig: PrivateRuntimeConfig & PublicRuntimeConfig
|
||||
export default runtimeConfig
|
||||
}
|
||||
|
||||
declare module '#paths' {
|
||||
export const baseURL: () => string
|
||||
export const buildAssetsDir: () => string
|
||||
|
||||
export const buildAssetsURL: (...path: string[]) => string
|
||||
export const publicAssetsURL: (...path: string[]) => string
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ export function initNitro (nuxt: Nuxt) {
|
||||
nuxt.hooks.addHooks(nitroContext.nuxtHooks)
|
||||
nuxt.hook('close', () => nitroContext._internal.hooks.callHook('close'))
|
||||
nitroContext._internal.hooks.hook('nitro:document', template => nuxt.callHook('nitro:document', template))
|
||||
nitroContext._internal.hooks.hook('nitro:generate', ctx => nuxt.callHook('nitro:generate', ctx))
|
||||
|
||||
// @ts-ignore
|
||||
nuxt.hooks.addHooks(nitroDevContext.nuxtHooks)
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
import NuxtNestedPage from './nested-page.vue'
|
||||
import NuxtPage from './page.vue'
|
||||
import NuxtLayout from './layout'
|
||||
import { defineNuxtPlugin } from '#app'
|
||||
import { defineNuxtPlugin, useRuntimeConfig } from '#app'
|
||||
// @ts-ignore
|
||||
import routes from '#build/routes'
|
||||
|
||||
@ -29,9 +29,10 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
// TODO: remove before release - present for backwards compatibility & intentionally undocumented
|
||||
nuxtApp.vueApp.component('NuxtChild', NuxtNestedPage)
|
||||
|
||||
const { baseURL } = useRuntimeConfig().app
|
||||
const routerHistory = process.client
|
||||
? createWebHistory()
|
||||
: createMemoryHistory()
|
||||
? createWebHistory(baseURL)
|
||||
: createMemoryHistory(baseURL)
|
||||
|
||||
const router = createRouter({
|
||||
history: routerHistory,
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { resolve, join } from 'pathe'
|
||||
import { existsSync, readdirSync } from 'fs'
|
||||
import defu from 'defu'
|
||||
import { isRelative, joinURL, hasProtocol } from 'ufo'
|
||||
|
||||
export default {
|
||||
/** Vue.js config */
|
||||
@ -29,16 +28,40 @@ export default {
|
||||
/**
|
||||
* Nuxt App configuration.
|
||||
* @version 2
|
||||
* @version 3
|
||||
*/
|
||||
app: {
|
||||
$resolve: (val, get) => {
|
||||
const useCDN = hasProtocol(get('build.publicPath'), true) && !get('dev')
|
||||
const isRelativePublicPath = isRelative(get('build.publicPath'))
|
||||
return defu(val, {
|
||||
basePath: get('router.base'),
|
||||
assetsPath: isRelativePublicPath ? get('build.publicPath') : useCDN ? '/' : joinURL(get('router.base'), get('build.publicPath')),
|
||||
cdnURL: useCDN ? get('build.publicPath') : null
|
||||
})
|
||||
/**
|
||||
* The base path of your Nuxt application.
|
||||
*
|
||||
* This can be set at runtime by setting the BASE_PATH environment variable.
|
||||
* @example
|
||||
* ```bash
|
||||
* BASE_PATH=/prefix/ node .output/server/index.mjs
|
||||
* ```
|
||||
*/
|
||||
baseURL: '/',
|
||||
/** The folder name for the built site assets, relative to `baseURL` (or `cdnURL` if set). This is set at build time and should not be customized at runtime. */
|
||||
buildAssetsDir: '/_nuxt/',
|
||||
/**
|
||||
* The folder name for the built site assets, relative to `baseURL` (or `cdnURL` if set).
|
||||
* @deprecated - use `buildAssetsDir` instead
|
||||
* @version 2
|
||||
*/
|
||||
assetsPath: {
|
||||
$resolve: (val, get) => val ?? get('buildAssetsDir')
|
||||
},
|
||||
/**
|
||||
* An absolute URL to serve the public folder from (production-only).
|
||||
*
|
||||
* This can be set to a different value at runtime by setting the CDN_URL environment variable.
|
||||
* @example
|
||||
* ```bash
|
||||
* CDN_URL=https://mycdn.org/ node .output/server/index.mjs
|
||||
* ```
|
||||
*/
|
||||
cdnURL: {
|
||||
$resolve: (val, get) => get('dev') ? null : val || null
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { isCI, isTest } from 'std-env'
|
||||
import { hasProtocol } from 'ufo'
|
||||
import { normalizeURL, withTrailingSlash } from 'ufo'
|
||||
|
||||
export default {
|
||||
/**
|
||||
@ -153,13 +153,9 @@ export default {
|
||||
* }
|
||||
* ```
|
||||
* @version 2
|
||||
* @version 3
|
||||
*/
|
||||
publicPath: {
|
||||
$resolve: (val, get) => {
|
||||
if (hasProtocol(val, true) && get('dev')) { val = null }
|
||||
return (val || '/_nuxt/').replace(/([^/])$/, '$1/')
|
||||
}
|
||||
$resolve: (val, get) => val ? withTrailingSlash(normalizeURL(val)) : get('app.buildAssetsDir')
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -160,7 +160,7 @@ export default {
|
||||
* The full path to the directory underneath `/_nuxt/` where static assets
|
||||
* (payload, state and manifest files) will live.
|
||||
*/
|
||||
base: { $resolve: (val, get) => val || joinURL(get('app.assetsPath'), get('generate.dir')) },
|
||||
base: { $resolve: (val, get) => val || joinURL(get('app.buildAssetsDir'), get('generate.dir')) },
|
||||
/** The full path to the versioned directory where static assets for the current buidl are located. */
|
||||
versionBase: { $resolve: (val, get) => val || joinURL(get('generate.base'), get('generate.version')) },
|
||||
/** A unique string to uniquely identify payload versions (defaults to the current timestamp). */
|
||||
|
@ -16,10 +16,9 @@ export default {
|
||||
* This can be useful if you need to serve Nuxt as a different context root, from
|
||||
* within a bigger web site.
|
||||
* @version 2
|
||||
* @version 3
|
||||
*/
|
||||
base: {
|
||||
$resolve: (val = '/') => withTrailingSlash(normalizeURL(val))
|
||||
$resolve: (val, get) => val ? withTrailingSlash(normalizeURL(val)) : get('app.baseURL')
|
||||
},
|
||||
|
||||
/** @private */
|
||||
|
@ -80,6 +80,7 @@ export interface NuxtHooks {
|
||||
// @nuxt/nitro
|
||||
'nitro:document': (template: { src: string, contents: string }) => HookResult
|
||||
'nitro:context': (context: any) => HookResult
|
||||
'nitro:generate': (context: any) => HookResult
|
||||
|
||||
// @nuxt/cli
|
||||
'generate:cache:ignore': (ignore: string[]) => HookResult
|
||||
|
@ -5,12 +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 { 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'
|
||||
|
||||
export async function buildClient (ctx: ViteBuildContext) {
|
||||
const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
|
||||
@ -25,6 +27,7 @@ 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',
|
||||
@ -38,7 +41,11 @@ export async function buildClient (ctx: ViteBuildContext) {
|
||||
cacheDirPlugin(ctx.nuxt.options.rootDir, 'client'),
|
||||
vuePlugin(ctx.config.vue),
|
||||
viteJsxPlugin(),
|
||||
devStyleSSRPlugin(ctx.nuxt.options.rootDir)
|
||||
DynamicBasePlugin.vite({ env: 'client', devAppConfig: ctx.nuxt.options.app }),
|
||||
devStyleSSRPlugin({
|
||||
rootDir: ctx.nuxt.options.rootDir,
|
||||
buildAssetsURL: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir)
|
||||
})
|
||||
],
|
||||
server: {
|
||||
middlewareMode: true
|
||||
|
@ -1,5 +1,6 @@
|
||||
import fse from 'fs-extra'
|
||||
import { resolve } from 'pathe'
|
||||
import { joinURL } from 'ufo'
|
||||
import type { ViteBuildContext } from './vite'
|
||||
|
||||
export async function writeManifest (ctx: ViteBuildContext, extraEntries: string[] = []) {
|
||||
@ -15,7 +16,7 @@ export async function writeManifest (ctx: ViteBuildContext, extraEntries: string
|
||||
|
||||
// Legacy dev manifest
|
||||
const devClientManifest = {
|
||||
publicPath: ctx.nuxt.options.build.publicPath,
|
||||
publicPath: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir),
|
||||
all: entries,
|
||||
initial: entries,
|
||||
async: [],
|
||||
|
@ -1,7 +1,13 @@
|
||||
import { joinURL } from 'ufo'
|
||||
import { Plugin } from 'vite'
|
||||
import { isCSS } from '../utils'
|
||||
|
||||
export function devStyleSSRPlugin (rootDir: string): Plugin {
|
||||
export interface DevStyleSSRPluginOptions {
|
||||
rootDir: string
|
||||
buildAssetsURL: string
|
||||
}
|
||||
|
||||
export function devStyleSSRPlugin (options: DevStyleSSRPluginOptions): Plugin {
|
||||
return {
|
||||
name: 'nuxt:dev-style-ssr',
|
||||
apply: 'serve',
|
||||
@ -12,13 +18,13 @@ export function devStyleSSRPlugin (rootDir: string): Plugin {
|
||||
}
|
||||
|
||||
let moduleId = id
|
||||
if (moduleId.startsWith(rootDir)) {
|
||||
moduleId = moduleId.slice(rootDir.length)
|
||||
if (moduleId.startsWith(options.rootDir)) {
|
||||
moduleId = moduleId.slice(options.rootDir.length)
|
||||
}
|
||||
|
||||
// When dev `<style>` is injected, remove the `<link>` styles from manifest
|
||||
// TODO: Use `app.assetsPath` or unique hash
|
||||
return code + `\ndocument.querySelectorAll(\`link[href="/_nuxt${moduleId}"]\`).forEach(i=>i.remove())`
|
||||
const selector = joinURL(options.buildAssetsURL, moduleId)
|
||||
return code + `\ndocument.querySelectorAll(\`link[href="${selector}"]\`).forEach(i=>i.remove())`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
57
packages/vite/src/plugins/dynamic-base.ts
Normal file
57
packages/vite/src/plugins/dynamic-base.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { createUnplugin } from 'unplugin'
|
||||
|
||||
interface DynamicBasePluginOptions {
|
||||
env: 'dev' | 'server' | 'client'
|
||||
devAppConfig?: Record<string, any>
|
||||
globalPublicPath?: string
|
||||
}
|
||||
|
||||
export const DynamicBasePlugin = createUnplugin(function (options: DynamicBasePluginOptions) {
|
||||
return {
|
||||
name: 'nuxt:dynamic-base-path',
|
||||
resolveId (id) {
|
||||
if (id.startsWith('/__NUXT_BASE__')) {
|
||||
return id.replace('/__NUXT_BASE__', '')
|
||||
}
|
||||
},
|
||||
enforce: 'post',
|
||||
transform (code, id) {
|
||||
if (options.globalPublicPath && id.includes('entry.ts')) {
|
||||
code = 'import { joinURL } from "ufo";' +
|
||||
`${options.globalPublicPath} = joinURL(NUXT_BASE, NUXT_CONFIG.app.buildAssetsDir);` + code
|
||||
}
|
||||
|
||||
if (code.includes('NUXT_BASE') && !code.includes('const NUXT_BASE =')) {
|
||||
code = 'const NUXT_BASE = NUXT_CONFIG.app.cdnURL || NUXT_CONFIG.app.baseURL;' + code
|
||||
|
||||
if (options.env === 'dev') {
|
||||
code = `const NUXT_CONFIG = { app: ${JSON.stringify(options.devAppConfig)} };` + code
|
||||
} else if (options.env === 'server') {
|
||||
code = 'import NUXT_CONFIG from "#config";' + code
|
||||
} else {
|
||||
code = 'const NUXT_CONFIG = __NUXT__.config;' + code
|
||||
}
|
||||
}
|
||||
|
||||
if (id === 'vite/preload-helper') {
|
||||
// Define vite base path as buildAssetsUrl (i.e. including _nuxt/)
|
||||
code = code.replace(
|
||||
/const base = ['"]\/__NUXT_BASE__\/['"]/,
|
||||
'import { joinURL } from "ufo";' +
|
||||
'const base = joinURL(NUXT_BASE, NUXT_CONFIG.app.buildAssetsDir);')
|
||||
}
|
||||
|
||||
// Sanitize imports
|
||||
code = code.replace(/from *['"]\/__NUXT_BASE__(\/[^'"]*)['"]/g, 'from "$1"')
|
||||
|
||||
// Dynamically compute string URLs featuring baseURL
|
||||
for (const delimiter of ['`', '"', "'"]) {
|
||||
const delimiterRE = new RegExp(`${delimiter}([^${delimiter}]*)\\/__NUXT_BASE__\\/([^${delimiter}]*)${delimiter}`, 'g')
|
||||
/* eslint-disable-next-line no-template-curly-in-string */
|
||||
code = code.replace(delimiterRE, '`$1${NUXT_BASE}$2`')
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
}
|
||||
})
|
@ -1,4 +1,4 @@
|
||||
import { resolve, normalize } from 'pathe'
|
||||
import { join, resolve, normalize } from 'pathe'
|
||||
import * as vite from 'vite'
|
||||
import vuePlugin from '@vitejs/plugin-vue'
|
||||
import viteJsxPlugin from '@vitejs/plugin-vue-jsx'
|
||||
@ -6,12 +6,14 @@ import fse from 'fs-extra'
|
||||
import pDebounce from 'p-debounce'
|
||||
import consola from 'consola'
|
||||
import { resolveModule } from '@nuxt/kit'
|
||||
import { withoutTrailingSlash } from 'ufo'
|
||||
import { ViteBuildContext, ViteOptions } from './vite'
|
||||
import { wpfs } from './utils/wpfs'
|
||||
import { cacheDirPlugin } from './plugins/cache-dir'
|
||||
import { DynamicBasePlugin } from './plugins/dynamic-base'
|
||||
import { bundleRequest } from './dev-bundler'
|
||||
import { writeManifest } from './manifest'
|
||||
import { isCSS } from './utils'
|
||||
import { isCSS, isDirectory, readDirRecursively } from './utils'
|
||||
|
||||
export async function buildServer (ctx: ViteBuildContext) {
|
||||
const _resolve = id => resolveModule(id, { paths: ctx.nuxt.options.modulesDir })
|
||||
@ -38,7 +40,7 @@ export async function buildServer (ctx: ViteBuildContext) {
|
||||
}
|
||||
},
|
||||
ssr: {
|
||||
external: [],
|
||||
external: ['#config'],
|
||||
noExternal: [
|
||||
...ctx.nuxt.options.build.transpile,
|
||||
// TODO: Use externality for production (rollup) build
|
||||
@ -75,12 +77,41 @@ export async function buildServer (ctx: ViteBuildContext) {
|
||||
plugins: [
|
||||
cacheDirPlugin(ctx.nuxt.options.rootDir, 'server'),
|
||||
vuePlugin(ctx.config.vue),
|
||||
DynamicBasePlugin.vite({ env: ctx.nuxt.options.dev ? 'dev' : 'server', devAppConfig: ctx.nuxt.options.app }),
|
||||
viteJsxPlugin()
|
||||
]
|
||||
} as ViteOptions)
|
||||
|
||||
await ctx.nuxt.callHook('vite:extendConfig', serverConfig, { isClient: false, isServer: true })
|
||||
|
||||
ctx.nuxt.hook('nitro:generate', async () => {
|
||||
const clientDist = resolve(ctx.nuxt.options.buildDir, 'dist/client')
|
||||
|
||||
// Remove public files that have been duplicated into buildAssetsDir
|
||||
// TODO: Add option to configure this behaviour in vite
|
||||
const publicDir = join(ctx.nuxt.options.srcDir, ctx.nuxt.options.dir.public)
|
||||
let publicFiles: string[] = []
|
||||
if (await isDirectory(publicDir)) {
|
||||
publicFiles = readDirRecursively(publicDir).map(r => r.replace(publicDir, ''))
|
||||
for (const file of publicFiles) {
|
||||
try {
|
||||
fse.rmSync(join(clientDist, file))
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy doubly-nested /_nuxt/_nuxt files into buildAssetsDir
|
||||
// TODO: Workaround vite issue
|
||||
if (await isDirectory(clientDist)) {
|
||||
const nestedAssetsPath = withoutTrailingSlash(join(clientDist, ctx.nuxt.options.app.buildAssetsDir))
|
||||
|
||||
if (await isDirectory(nestedAssetsPath)) {
|
||||
await fse.copy(nestedAssetsPath, clientDist, { recursive: true })
|
||||
await fse.remove(nestedAssetsPath)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const onBuild = () => ctx.nuxt.callHook('build:resources', wpfs)
|
||||
|
||||
// Production build
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { createHash } from 'crypto'
|
||||
import { promises as fsp, readdirSync, statSync } from 'fs'
|
||||
import { join } from 'pathe'
|
||||
|
||||
export function uniq<T> (arr: T[]): T[] {
|
||||
return Array.from(new Set(arr))
|
||||
@ -32,3 +34,19 @@ export function hash (input: string, length = 8) {
|
||||
.digest('hex')
|
||||
.slice(0, length)
|
||||
}
|
||||
|
||||
export function readDirRecursively (dir: string) {
|
||||
return readdirSync(dir).reduce((files, file) => {
|
||||
const name = join(dir, file)
|
||||
const isDirectory = statSync(name).isDirectory()
|
||||
return isDirectory ? [...files, ...readDirRecursively(name)] : [...files, name]
|
||||
}, [])
|
||||
}
|
||||
|
||||
export async function isDirectory (path: string) {
|
||||
try {
|
||||
return (await fsp.stat(path)).isDirectory()
|
||||
} catch (_err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import type { Nuxt } from '@nuxt/schema'
|
||||
import type { InlineConfig, SSROptions } from 'vite'
|
||||
import type { Options } from '@vitejs/plugin-vue'
|
||||
import { sanitizeFilePath } from 'mlly'
|
||||
import { joinURL, withoutLeadingSlash } from 'ufo'
|
||||
import { buildClient } from './client'
|
||||
import { buildServer } from './server'
|
||||
import virtual from './plugins/virtual'
|
||||
@ -47,7 +48,9 @@ export async function bundle (nuxt: Nuxt) {
|
||||
'abort-controller': 'unenv/runtime/mock/empty'
|
||||
}
|
||||
},
|
||||
base: nuxt.options.build.publicPath,
|
||||
base: nuxt.options.dev
|
||||
? joinURL(nuxt.options.app.baseURL, nuxt.options.app.buildAssetsDir)
|
||||
: '/__NUXT_BASE__/',
|
||||
publicDir: resolve(nuxt.options.srcDir, nuxt.options.dir.public),
|
||||
// TODO: move to kit schema when it exists
|
||||
vue: {
|
||||
@ -71,6 +74,7 @@ export async function bundle (nuxt: Nuxt) {
|
||||
},
|
||||
clearScreen: false,
|
||||
build: {
|
||||
assetsDir: withoutLeadingSlash(nuxt.options.app.buildAssetsDir),
|
||||
emptyOutDir: false,
|
||||
rollupOptions: {
|
||||
input: resolve(nuxt.options.appDir, 'entry'),
|
||||
|
@ -4,6 +4,7 @@ import webpack from 'webpack'
|
||||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
|
||||
|
||||
import type { ClientOptions } from 'webpack-hot-middleware'
|
||||
import { joinURL } from 'ufo'
|
||||
import { applyPresets, WebpackConfigContext } from '../utils/config'
|
||||
import { nuxt } from '../presets/nuxt'
|
||||
|
||||
@ -53,7 +54,7 @@ function clientHMR (ctx: WebpackConfigContext) {
|
||||
const hotMiddlewareClientOptions = {
|
||||
reload: true,
|
||||
timeout: 30000,
|
||||
path: `${options.router.base}/__webpack_hmr/${ctx.name}`.replace(/\/\//g, '/'),
|
||||
path: joinURL(options.app.baseURL, '__webpack_hmr', ctx.name),
|
||||
...clientOptions,
|
||||
ansiColors: JSON.stringify(clientOptions.ansiColors || {}),
|
||||
overlayStyles: JSON.stringify(clientOptions.overlayStyles || {}),
|
||||
|
@ -45,9 +45,13 @@ function serverStandalone (ctx: WebpackConfigContext) {
|
||||
'#',
|
||||
...ctx.options.build.transpile
|
||||
]
|
||||
const external = ['#config']
|
||||
|
||||
if (!Array.isArray(ctx.config.externals)) { return }
|
||||
ctx.config.externals.push(({ request }, cb) => {
|
||||
if (external.includes(request)) {
|
||||
return cb(null, true)
|
||||
}
|
||||
if (
|
||||
request[0] === '.' ||
|
||||
isAbsolute(request) ||
|
||||
|
@ -5,7 +5,7 @@ import consola from 'consola'
|
||||
import webpack from 'webpack'
|
||||
import FriendlyErrorsWebpackPlugin from '@nuxt/friendly-errors-webpack-plugin'
|
||||
import { escapeRegExp } from 'lodash-es'
|
||||
import { hasProtocol, joinURL } from 'ufo'
|
||||
import { joinURL } from 'ufo'
|
||||
import WarningIgnorePlugin from '../plugins/warning-ignore'
|
||||
import { WebpackConfigContext, applyPresets, fileName } from '../utils/config'
|
||||
|
||||
@ -194,9 +194,7 @@ function getOutput (ctx: WebpackConfigContext): webpack.Configuration['output']
|
||||
path: resolve(options.buildDir, 'dist', ctx.isServer ? 'server' : 'client'),
|
||||
filename: fileName(ctx, 'app'),
|
||||
chunkFilename: fileName(ctx, 'chunk'),
|
||||
publicPath: hasProtocol(options.build.publicPath, true)
|
||||
? options.build.publicPath
|
||||
: joinURL(options.router.base, options.build.publicPath)
|
||||
publicPath: joinURL(options.app.baseURL, options.app.buildAssetsDir)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,8 @@ import type { Context as WebpackDevMiddlewareContext, Options as WebpackDevMiddl
|
||||
import type { MiddlewareOptions as WebpackHotMiddlewareOptions } from 'webpack-hot-middleware'
|
||||
|
||||
import type { Nuxt } from '@nuxt/schema'
|
||||
import { joinURL } from 'ufo'
|
||||
import { DynamicBasePlugin } from '../../vite/src/plugins/dynamic-base'
|
||||
import { createMFS } from './utils/mfs'
|
||||
import { client, server } from './configs'
|
||||
import { createWebpackConfigContext, applyPresets, getWebpackConfig } from './utils/config'
|
||||
@ -113,6 +115,11 @@ class WebpackBundler {
|
||||
this.compilers = webpackConfigs.map((config) => {
|
||||
// Support virtual modules (input)
|
||||
config.plugins.push(this.virtualModules)
|
||||
config.plugins.push(DynamicBasePlugin.webpack({
|
||||
env: this.nuxt.options.dev ? 'dev' : config.name as 'client',
|
||||
devAppConfig: this.nuxt.options.app,
|
||||
globalPublicPath: '__webpack_public_path__'
|
||||
}))
|
||||
|
||||
// Create compiler
|
||||
const compiler = webpack(config)
|
||||
@ -210,7 +217,7 @@ class WebpackBundler {
|
||||
// @ts-ignore
|
||||
compiler,
|
||||
{
|
||||
publicPath: buildOptions.publicPath,
|
||||
publicPath: joinURL(this.nuxt.options.app.baseURL, this.nuxt.options.app.buildAssetsDir),
|
||||
outputFileSystem: this.mfs,
|
||||
stats: 'none',
|
||||
...buildOptions.devMiddleware
|
||||
@ -229,7 +236,7 @@ class WebpackBundler {
|
||||
{
|
||||
log: false,
|
||||
heartbeat: 10000,
|
||||
path: `/__webpack_hmr/${name}`,
|
||||
path: joinURL(this.nuxt.options.app.baseURL, '__webpack_hmr', name),
|
||||
...hotMiddlewareOptions
|
||||
} as WebpackHotMiddlewareOptions
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user