mirror of
https://github.com/nuxt/nuxt.git
synced 2025-02-17 06:01:34 +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 defu from 'defu'
|
||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
import destr from 'destr'
|
import destr from 'destr'
|
||||||
import { TARGETS, MODES, guardDir, isNonEmptyString, isPureObject, isUrl, getMainModule, urlJoin, getPKG } from '@nuxt/utils'
|
import { TARGETS, MODES, guardDir, isNonEmptyString, isPureObject, isUrl, getMainModule, getPKG } from '@nuxt/utils'
|
||||||
import { normalizeURL, withTrailingSlash } from '@nuxt/ufo'
|
import { joinURL, normalizeURL, withTrailingSlash } from '@nuxt/ufo'
|
||||||
import { defaultNuxtConfigFile, getDefaultNuxtConfig } from './config'
|
import { defaultNuxtConfigFile, getDefaultNuxtConfig } from './config'
|
||||||
|
|
||||||
export function getNuxtConfig (_options) {
|
export function getNuxtConfig (_options) {
|
||||||
@ -450,17 +450,27 @@ export function getNuxtConfig (_options) {
|
|||||||
.map(([path, handler]) => ({ path, handler }))
|
.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
|
// Generate staticAssets
|
||||||
const { staticAssets } = options.generate
|
const { staticAssets } = options.generate
|
||||||
if (!staticAssets.version) {
|
if (!staticAssets.version) {
|
||||||
staticAssets.version = String(Math.round(Date.now() / 1000))
|
staticAssets.version = String(Math.round(Date.now() / 1000))
|
||||||
}
|
}
|
||||||
if (!staticAssets.base) {
|
if (!staticAssets.base) {
|
||||||
const publicPath = isUrl(options.build.publicPath) ? '' : options.build.publicPath // "/_nuxt" or custom CDN URL
|
staticAssets.base = joinURL(options.app.assetsPath, staticAssets.dir)
|
||||||
staticAssets.base = urlJoin(publicPath, staticAssets.dir)
|
|
||||||
}
|
}
|
||||||
if (!staticAssets.versionBase) {
|
if (!staticAssets.versionBase) {
|
||||||
staticAssets.versionBase = urlJoin(staticAssets.base, staticAssets.version)
|
staticAssets.versionBase = joinURL(staticAssets.base, staticAssets.version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createRequire
|
// createRequire
|
||||||
|
@ -19,6 +19,11 @@ Object {
|
|||||||
"~": "/var/nuxt/test",
|
"~": "/var/nuxt/test",
|
||||||
"~~": "/var/nuxt/test",
|
"~~": "/var/nuxt/test",
|
||||||
},
|
},
|
||||||
|
"app": Object {
|
||||||
|
"assetsPath": "/_nuxt/",
|
||||||
|
"basePath": "/",
|
||||||
|
"cdnURL": null,
|
||||||
|
},
|
||||||
"appTemplatePath": "/var/nuxt/test/.nuxt/views/app.template.html",
|
"appTemplatePath": "/var/nuxt/test/.nuxt/views/app.template.html",
|
||||||
"build": Object {
|
"build": Object {
|
||||||
"_publicPath": "/_nuxt/",
|
"_publicPath": "/_nuxt/",
|
||||||
@ -312,7 +317,13 @@ Object {
|
|||||||
},
|
},
|
||||||
"plugins": Array [],
|
"plugins": Array [],
|
||||||
"privateRuntimeConfig": Object {},
|
"privateRuntimeConfig": Object {},
|
||||||
"publicRuntimeConfig": Object {},
|
"publicRuntimeConfig": Object {
|
||||||
|
"app": Object {
|
||||||
|
"assetsPath": "/_nuxt/",
|
||||||
|
"basePath": "/",
|
||||||
|
"cdnURL": null,
|
||||||
|
},
|
||||||
|
},
|
||||||
"render": Object {
|
"render": Object {
|
||||||
"bundleRenderer": Object {
|
"bundleRenderer": Object {
|
||||||
"runInNewContext": false,
|
"runInNewContext": false,
|
||||||
|
@ -29,11 +29,9 @@ export default class Generator {
|
|||||||
)
|
)
|
||||||
// Payloads for full static
|
// Payloads for full static
|
||||||
if (this.isFullStatic) {
|
if (this.isFullStatic) {
|
||||||
const { build: { publicPath: _publicPath }, router: { base } } = this.options
|
|
||||||
const publicPath = isUrl(_publicPath) ? _publicPath : base
|
|
||||||
const { staticAssets, manifest } = this.options.generate
|
const { staticAssets, manifest } = this.options.generate
|
||||||
this.staticAssetsDir = path.resolve(this.distNuxtPath, staticAssets.dir, staticAssets.version)
|
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) {
|
if (manifest) {
|
||||||
this.manifest = defu(manifest, {
|
this.manifest = defu(manifest, {
|
||||||
routes: []
|
routes: []
|
||||||
|
@ -15,7 +15,8 @@ import {
|
|||||||
compile,
|
compile,
|
||||||
getQueryDiff,
|
getQueryDiff,
|
||||||
globalHandleError,
|
globalHandleError,
|
||||||
isSamePath
|
isSamePath,
|
||||||
|
urlJoin
|
||||||
} from './utils.js'
|
} from './utils.js'
|
||||||
import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
|
import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
|
||||||
<% if (features.fetch) { %>import fetchMixin from './mixins/fetch.client'<% } %>
|
<% if (features.fetch) { %>import fetchMixin from './mixins/fetch.client'<% } %>
|
||||||
@ -45,6 +46,11 @@ let router
|
|||||||
// Try to rehydrate SSR data from window
|
// Try to rehydrate SSR data from window
|
||||||
const NUXT = window.<%= globals.context %> || {}
|
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' : '' %>
|
Object.assign(Vue.config, <%= serialize(vue.config) %>)<%= isTest ? '// eslint-disable-line' : '' %>
|
||||||
|
|
||||||
<% if (nuxtOptions.render.ssrLog) { %>
|
<% if (nuxtOptions.render.ssrLog) { %>
|
||||||
|
@ -88,7 +88,7 @@ function registerModule (path, rawModule, options = {}) {
|
|||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
async function createApp(ssrContext, config = {}) {
|
async function createApp(ssrContext, config = {}) {
|
||||||
const router = await createRouter(ssrContext)
|
const router = await createRouter(ssrContext, config)
|
||||||
|
|
||||||
<% if (store) { %>
|
<% if (store) { %>
|
||||||
const store = createStore(ssrContext)
|
const store = createStore(ssrContext)
|
||||||
|
@ -109,8 +109,9 @@ function decodeObj(obj) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createRouter () {
|
export function createRouter (ssrContext, config) {
|
||||||
const router = new Router(routerOptions)
|
const base = (config.app && config.app.basePath) || routerOptions.base
|
||||||
|
const router = new Router({ ...routerOptions, base })
|
||||||
|
|
||||||
// TODO: remove in Nuxt 3
|
// TODO: remove in Nuxt 3
|
||||||
const originalPush = router.push
|
const originalPush = router.push
|
||||||
|
@ -46,7 +46,8 @@ const createNext = ssrContext => (opts) => {
|
|||||||
}
|
}
|
||||||
opts.query = stringify(opts.query)
|
opts.query = stringify(opts.query)
|
||||||
opts.path = opts.path + (opts.query ? '?' + 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))) {
|
if (!opts.path.startsWith('http') && (routerBase !== '/' && !opts.path.startsWith(routerBase))) {
|
||||||
opts.path = urlJoin(routerBase, opts.path)
|
opts.path = urlJoin(routerBase, opts.path)
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ export async function setContext (app, context) {
|
|||||||
<%= (store ? 'store: app.store,' : '') %>
|
<%= (store ? 'store: app.store,' : '') %>
|
||||||
payload: context.payload,
|
payload: context.payload,
|
||||||
error: context.error,
|
error: context.error,
|
||||||
base: '<%= router.base %>',
|
base: app.router.options.base,
|
||||||
env: <%= JSON.stringify(env) %><%= isTest ? '// eslint-disable-line' : '' %>
|
env: <%= JSON.stringify(env) %><%= isTest ? '// eslint-disable-line' : '' %>
|
||||||
}
|
}
|
||||||
// Only set once
|
// Only set once
|
||||||
|
@ -2,7 +2,7 @@ import path from 'path'
|
|||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
import { template } from 'lodash'
|
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 { normalizeURL } from '@nuxt/ufo'
|
||||||
|
|
||||||
import SPARenderer from './renderers/spa'
|
import SPARenderer from './renderers/spa'
|
||||||
@ -310,14 +310,15 @@ export default class VueRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get resourceMap () {
|
get resourceMap () {
|
||||||
|
const publicPath = urlJoin(this.options.app.cdnURL || '/', this.options.app.assetsPath)
|
||||||
return {
|
return {
|
||||||
clientManifest: {
|
clientManifest: {
|
||||||
fileName: 'client.manifest.json',
|
fileName: 'client.manifest.json',
|
||||||
transform: src => JSON.parse(src)
|
transform: src => Object.assign(JSON.parse(src), { publicPath })
|
||||||
},
|
},
|
||||||
modernManifest: {
|
modernManifest: {
|
||||||
fileName: 'modern.manifest.json',
|
fileName: 'modern.manifest.json',
|
||||||
transform: src => JSON.parse(src)
|
transform: src => Object.assign(JSON.parse(src), { publicPath })
|
||||||
},
|
},
|
||||||
serverManifest: {
|
serverManifest: {
|
||||||
fileName: 'server.manifest.json',
|
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