mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-23 06:05:11 +00:00
feat(config, vue-app, vue-renderer): support dynamic base
and publicPath
(#8520)
Co-authored-by: pooya parsa <pyapar@gmail.com>
This commit is contained in:
parent
dec8f99fc3
commit
9681a8937d
@ -4,8 +4,8 @@ import { defaultsDeep, pick, uniq } from 'lodash'
|
||||
import defu from 'defu'
|
||||
import consola from 'consola'
|
||||
import destr from 'destr'
|
||||
import { TARGETS, MODES, guardDir, isNonEmptyString, isPureObject, isUrl, getMainModule, urlJoin, getPKG } from '@nuxt/utils'
|
||||
import { normalizeURL, withTrailingSlash } from '@nuxt/ufo'
|
||||
import { TARGETS, MODES, guardDir, isNonEmptyString, isPureObject, isUrl, getMainModule, getPKG } from '@nuxt/utils'
|
||||
import { joinURL, normalizeURL, withTrailingSlash } from '@nuxt/ufo'
|
||||
import { defaultNuxtConfigFile, getDefaultNuxtConfig } from './config'
|
||||
|
||||
export function getNuxtConfig (_options) {
|
||||
@ -450,17 +450,27 @@ export function getNuxtConfig (_options) {
|
||||
.map(([path, handler]) => ({ path, handler }))
|
||||
}
|
||||
|
||||
// App config (internal for nuxt2 at this stage)
|
||||
const useCDN = isUrl(options.build.publicPath) && !options.dev
|
||||
options.app = defu(options.app, {
|
||||
basePath: options.router.base,
|
||||
assetsPath: useCDN ? '/' : joinURL(options.router.base, options.build.publicPath),
|
||||
cdnURL: useCDN ? options.build.publicPath : null
|
||||
})
|
||||
// Expose app config to $config.app
|
||||
options.publicRuntimeConfig = options.publicRuntimeConfig || {}
|
||||
options.publicRuntimeConfig.app = options.app
|
||||
|
||||
// Generate staticAssets
|
||||
const { staticAssets } = options.generate
|
||||
if (!staticAssets.version) {
|
||||
staticAssets.version = String(Math.round(Date.now() / 1000))
|
||||
}
|
||||
if (!staticAssets.base) {
|
||||
const publicPath = isUrl(options.build.publicPath) ? '' : options.build.publicPath // "/_nuxt" or custom CDN URL
|
||||
staticAssets.base = urlJoin(publicPath, staticAssets.dir)
|
||||
staticAssets.base = joinURL(options.app.assetsPath, staticAssets.dir)
|
||||
}
|
||||
if (!staticAssets.versionBase) {
|
||||
staticAssets.versionBase = urlJoin(staticAssets.base, staticAssets.version)
|
||||
staticAssets.versionBase = joinURL(staticAssets.base, staticAssets.version)
|
||||
}
|
||||
|
||||
// createRequire
|
||||
|
@ -19,6 +19,11 @@ Object {
|
||||
"~": "/var/nuxt/test",
|
||||
"~~": "/var/nuxt/test",
|
||||
},
|
||||
"app": Object {
|
||||
"assetsPath": "/_nuxt/",
|
||||
"basePath": "/",
|
||||
"cdnURL": null,
|
||||
},
|
||||
"appTemplatePath": "/var/nuxt/test/.nuxt/views/app.template.html",
|
||||
"build": Object {
|
||||
"_publicPath": "/_nuxt/",
|
||||
@ -312,7 +317,13 @@ Object {
|
||||
},
|
||||
"plugins": Array [],
|
||||
"privateRuntimeConfig": Object {},
|
||||
"publicRuntimeConfig": Object {},
|
||||
"publicRuntimeConfig": Object {
|
||||
"app": Object {
|
||||
"assetsPath": "/_nuxt/",
|
||||
"basePath": "/",
|
||||
"cdnURL": null,
|
||||
},
|
||||
},
|
||||
"render": Object {
|
||||
"bundleRenderer": Object {
|
||||
"runInNewContext": false,
|
||||
|
@ -29,11 +29,9 @@ export default class Generator {
|
||||
)
|
||||
// Payloads for full static
|
||||
if (this.isFullStatic) {
|
||||
const { build: { publicPath: _publicPath }, router: { base } } = this.options
|
||||
const publicPath = isUrl(_publicPath) ? _publicPath : base
|
||||
const { staticAssets, manifest } = this.options.generate
|
||||
this.staticAssetsDir = path.resolve(this.distNuxtPath, staticAssets.dir, staticAssets.version)
|
||||
this.staticAssetsBase = urlJoin(publicPath, this.options.generate.staticAssets.versionBase)
|
||||
this.staticAssetsBase = urlJoin(this.options.app.cdnURL || '/', this.options.generate.staticAssets.versionBase)
|
||||
if (manifest) {
|
||||
this.manifest = defu(manifest, {
|
||||
routes: []
|
||||
|
@ -15,7 +15,8 @@ import {
|
||||
compile,
|
||||
getQueryDiff,
|
||||
globalHandleError,
|
||||
isSamePath
|
||||
isSamePath,
|
||||
urlJoin
|
||||
} from './utils.js'
|
||||
import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
|
||||
<% if (features.fetch) { %>import fetchMixin from './mixins/fetch.client'<% } %>
|
||||
@ -45,6 +46,11 @@ let router
|
||||
// Try to rehydrate SSR data from window
|
||||
const NUXT = window.<%= globals.context %> || {}
|
||||
|
||||
const $config = NUXT.config || {}
|
||||
if ($config.app) {
|
||||
__webpack_public_path__ = urlJoin($config.app.cdnURL || '/', $config.app.assetsPath)
|
||||
}
|
||||
|
||||
Object.assign(Vue.config, <%= serialize(vue.config) %>)<%= isTest ? '// eslint-disable-line' : '' %>
|
||||
|
||||
<% if (nuxtOptions.render.ssrLog) { %>
|
||||
|
@ -88,7 +88,7 @@ function registerModule (path, rawModule, options = {}) {
|
||||
<% } %>
|
||||
|
||||
async function createApp(ssrContext, config = {}) {
|
||||
const router = await createRouter(ssrContext)
|
||||
const router = await createRouter(ssrContext, config)
|
||||
|
||||
<% if (store) { %>
|
||||
const store = createStore(ssrContext)
|
||||
|
@ -109,8 +109,9 @@ function decodeObj(obj) {
|
||||
}
|
||||
}
|
||||
|
||||
export function createRouter () {
|
||||
const router = new Router(routerOptions)
|
||||
export function createRouter (ssrContext, config) {
|
||||
const base = (config.app && config.app.basePath) || routerOptions.base
|
||||
const router = new Router({ ...routerOptions, base })
|
||||
|
||||
// TODO: remove in Nuxt 3
|
||||
const originalPush = router.push
|
||||
|
@ -46,7 +46,8 @@ const createNext = ssrContext => (opts) => {
|
||||
}
|
||||
opts.query = stringify(opts.query)
|
||||
opts.path = opts.path + (opts.query ? '?' + opts.query : '')
|
||||
const routerBase = '<%= router.base %>'
|
||||
const $config = ssrContext.runtimeConfig || {}
|
||||
const routerBase = ($config.app && $config.app.basePath) || '<%= router.base %>'
|
||||
if (!opts.path.startsWith('http') && (routerBase !== '/' && !opts.path.startsWith(routerBase))) {
|
||||
opts.path = urlJoin(routerBase, opts.path)
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ export async function setContext (app, context) {
|
||||
<%= (store ? 'store: app.store,' : '') %>
|
||||
payload: context.payload,
|
||||
error: context.error,
|
||||
base: '<%= router.base %>',
|
||||
base: app.router.options.base,
|
||||
env: <%= JSON.stringify(env) %><%= isTest ? '// eslint-disable-line' : '' %>
|
||||
}
|
||||
// Only set once
|
||||
|
@ -2,7 +2,7 @@ import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import consola from 'consola'
|
||||
import { template } from 'lodash'
|
||||
import { TARGETS, isModernRequest, waitFor } from '@nuxt/utils'
|
||||
import { TARGETS, isModernRequest, urlJoin, waitFor } from '@nuxt/utils'
|
||||
import { normalizeURL } from '@nuxt/ufo'
|
||||
|
||||
import SPARenderer from './renderers/spa'
|
||||
@ -310,14 +310,15 @@ export default class VueRenderer {
|
||||
}
|
||||
|
||||
get resourceMap () {
|
||||
const publicPath = urlJoin(this.options.app.cdnURL || '/', this.options.app.assetsPath)
|
||||
return {
|
||||
clientManifest: {
|
||||
fileName: 'client.manifest.json',
|
||||
transform: src => JSON.parse(src)
|
||||
transform: src => Object.assign(JSON.parse(src), { publicPath })
|
||||
},
|
||||
modernManifest: {
|
||||
fileName: 'modern.manifest.json',
|
||||
transform: src => JSON.parse(src)
|
||||
transform: src => Object.assign(JSON.parse(src), { publicPath })
|
||||
},
|
||||
serverManifest: {
|
||||
fileName: 'server.manifest.json',
|
||||
|
69
test/dev/basic.dynamic.test.js
Normal file
69
test/dev/basic.dynamic.test.js
Normal file
@ -0,0 +1,69 @@
|
||||
import path from 'path'
|
||||
import { readFileSync } from 'fs'
|
||||
import { ResourceLoader } from 'jsdom'
|
||||
|
||||
import { loadFixture, getPort, Nuxt, Builder } from '../utils'
|
||||
|
||||
let fetchCount = 0
|
||||
|
||||
class ProxyLoader extends ResourceLoader {
|
||||
fetch (url, options) {
|
||||
if (url.startsWith('https://cdn.nuxtjs.org')) {
|
||||
fetchCount++
|
||||
const param = url.slice('https://cdn.nuxtjs.org'.length + 1)
|
||||
const file = path.join(nuxt.options.buildDir, 'dist/client', param)
|
||||
const fileContents = readFileSync(file, 'utf-8')
|
||||
return Promise.resolve(Buffer.from(fileContents))
|
||||
}
|
||||
|
||||
return super.fetch(url, options)
|
||||
}
|
||||
}
|
||||
const resourceLoader = new ProxyLoader()
|
||||
|
||||
let port
|
||||
let nuxt
|
||||
const url = route => 'http://localhost:' + port + route
|
||||
|
||||
describe('basic ssr', () => {
|
||||
beforeAll(async () => {
|
||||
const options = await loadFixture('basic')
|
||||
const builderNuxt = new Nuxt(options)
|
||||
await builderNuxt.ready()
|
||||
|
||||
const builder = new Builder(builderNuxt)
|
||||
|
||||
await builder.build()
|
||||
|
||||
const runOptions = await loadFixture('basic', {
|
||||
router: {
|
||||
base: '/path'
|
||||
},
|
||||
build: {
|
||||
publicPath: 'https://cdn.nuxtjs.org'
|
||||
}
|
||||
})
|
||||
nuxt = new Nuxt(runOptions)
|
||||
await nuxt.ready()
|
||||
|
||||
port = await getPort()
|
||||
await nuxt.server.listen(port, '0.0.0.0')
|
||||
})
|
||||
|
||||
test('dynamic config is injected', async () => {
|
||||
const window = await nuxt.server.renderAndGetWindow(url('/path/'), { resources: resourceLoader })
|
||||
|
||||
expect(window.document.body.innerHTML).toContain('<h1>Index page</h1>')
|
||||
|
||||
expect(window.__NUXT__.config.app.basePath).toBe('/path/')
|
||||
expect(window.__NUXT__.config.app.cdnURL).toBe('https://cdn.nuxtjs.org/')
|
||||
expect(window.__NUXT__.config.app.assetsPath).toBe('/')
|
||||
|
||||
expect(fetchCount).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
// Close server and ask nuxt to stop listening to file changes
|
||||
afterAll(async () => {
|
||||
await nuxt.close()
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user