mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-23 14:15:13 +00:00
feat(vue-app): support configurable features (#6287)
This commit is contained in:
parent
05a6efd1eb
commit
174c13d56c
3
examples/minimal-features/README.md
Normal file
3
examples/minimal-features/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# A minimal Hello World Nuxt.js app
|
||||||
|
|
||||||
|
https://nuxtjs.org/examples
|
28
examples/minimal-features/nuxt.config.js
Normal file
28
examples/minimal-features/nuxt.config.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
export default {
|
||||||
|
loading: false,
|
||||||
|
loadingIndicator: false,
|
||||||
|
fetch: {
|
||||||
|
client: false,
|
||||||
|
server: false
|
||||||
|
},
|
||||||
|
features: {
|
||||||
|
store: false,
|
||||||
|
layouts: false,
|
||||||
|
meta: false,
|
||||||
|
middleware: false,
|
||||||
|
transitions: false,
|
||||||
|
deprecations: false,
|
||||||
|
validate: false,
|
||||||
|
asyncData: false,
|
||||||
|
fetch: false,
|
||||||
|
clientOnline: false,
|
||||||
|
clientPrefetch: false,
|
||||||
|
clientUseUrl: true,
|
||||||
|
componentAliases: false,
|
||||||
|
componentClientOnly: false
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
indicator: false,
|
||||||
|
terser: true
|
||||||
|
}
|
||||||
|
}
|
12
examples/minimal-features/package.json
Normal file
12
examples/minimal-features/package.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "example-minimal-features",
|
||||||
|
"dependencies": {
|
||||||
|
"nuxt": "latest"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nuxt",
|
||||||
|
"build": "nuxt build",
|
||||||
|
"start": "nuxt start",
|
||||||
|
"post-update": "yarn upgrade --latest"
|
||||||
|
}
|
||||||
|
}
|
21
examples/minimal-features/pages/about.vue
Executable file
21
examples/minimal-features/pages/about.vue
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p>Hi from {{ name }}</p>
|
||||||
|
<nuxt-link to="/">
|
||||||
|
Home page
|
||||||
|
</nuxt-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
name: process.static ? 'static' : (process.server ? 'server' : 'client')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
head: {
|
||||||
|
title: 'About page'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
16
examples/minimal-features/pages/index.vue
Executable file
16
examples/minimal-features/pages/index.vue
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>Welcome!</h1>
|
||||||
|
<nuxt-link to="/about">
|
||||||
|
About Page
|
||||||
|
</nuxt-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
head: {
|
||||||
|
title: 'Home page'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -238,6 +238,8 @@ export default class Builder {
|
|||||||
this.resolveMiddleware(templateContext)
|
this.resolveMiddleware(templateContext)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
this.addOptionalTemplates(templateContext)
|
||||||
|
|
||||||
await this.resolveCustomTemplates(templateContext)
|
await this.resolveCustomTemplates(templateContext)
|
||||||
|
|
||||||
await this.resolveLoadingIndicator(templateContext)
|
await this.resolveLoadingIndicator(templateContext)
|
||||||
@ -303,6 +305,16 @@ export default class Builder {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addOptionalTemplates (templateContext) {
|
||||||
|
if (this.options.build.indicator) {
|
||||||
|
templateContext.templateFiles.push('components/nuxt-build-indicator.vue')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.loading !== false) {
|
||||||
|
templateContext.templateFiles.push('components/nuxt-loading.vue')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async resolveFiles (dir, cwd = this.options.srcDir) {
|
async resolveFiles (dir, cwd = this.options.srcDir) {
|
||||||
return this.ignore.filter(await glob(this.globPathWithExtensions(dir), {
|
return this.ignore.filter(await glob(this.globPathWithExtensions(dir), {
|
||||||
cwd,
|
cwd,
|
||||||
@ -316,6 +328,10 @@ export default class Builder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async resolveLayouts ({ templateVars, templateFiles }) {
|
async resolveLayouts ({ templateVars, templateFiles }) {
|
||||||
|
if (!this.options.features.layouts) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (await fsExtra.exists(path.resolve(this.options.srcDir, this.options.dir.layouts))) {
|
if (await fsExtra.exists(path.resolve(this.options.srcDir, this.options.dir.layouts))) {
|
||||||
for (const file of await this.resolveFiles(this.options.dir.layouts)) {
|
for (const file of await this.resolveFiles(this.options.dir.layouts)) {
|
||||||
const name = file
|
const name = file
|
||||||
@ -409,7 +425,7 @@ export default class Builder {
|
|||||||
|
|
||||||
async resolveStore ({ templateVars, templateFiles }) {
|
async resolveStore ({ templateVars, templateFiles }) {
|
||||||
// Add store if needed
|
// Add store if needed
|
||||||
if (!this.options.store) {
|
if (!this.options.features.store || !this.options.store) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,17 +444,20 @@ export default class Builder {
|
|||||||
templateFiles.push('store.js')
|
templateFiles.push('store.js')
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolveMiddleware ({ templateVars }) {
|
async resolveMiddleware ({ templateVars, templateFiles }) {
|
||||||
// -- Middleware --
|
if (!this.options.features.middleware) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const middleware = await this.resolveRelative(this.options.dir.middleware)
|
const middleware = await this.resolveRelative(this.options.dir.middleware)
|
||||||
|
|
||||||
const extRE = new RegExp(`\\.(${this.supportedExtensions.join('|')})$`)
|
const extRE = new RegExp(`\\.(${this.supportedExtensions.join('|')})$`)
|
||||||
|
|
||||||
templateVars.middleware = middleware.map(({ src }) => {
|
templateVars.middleware = middleware.map(({ src }) => {
|
||||||
const name = src.replace(extRE, '')
|
const name = src.replace(extRE, '')
|
||||||
const dst = this.relativeToBuild(this.options.srcDir, this.options.dir.middleware, src)
|
const dst = this.relativeToBuild(this.options.srcDir, this.options.dir.middleware, src)
|
||||||
return { name, src, dst }
|
return { name, src, dst }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
templateFiles.push('middleware.js')
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolveCustomTemplates (templateContext) {
|
async resolveCustomTemplates (templateContext) {
|
||||||
|
@ -11,6 +11,7 @@ export default class TemplateContext {
|
|||||||
this.templateFiles = Array.from(builder.template.files)
|
this.templateFiles = Array.from(builder.template.files)
|
||||||
this.templateVars = {
|
this.templateVars = {
|
||||||
nuxtOptions: options,
|
nuxtOptions: options,
|
||||||
|
features: options.features,
|
||||||
extensions: options.extensions
|
extensions: options.extensions
|
||||||
.map(ext => ext.replace(/^\./, ''))
|
.map(ext => ext.replace(/^\./, ''))
|
||||||
.join('|'),
|
.join('|'),
|
||||||
@ -27,7 +28,7 @@ export default class TemplateContext {
|
|||||||
router: options.router,
|
router: options.router,
|
||||||
env: options.env,
|
env: options.env,
|
||||||
head: options.head,
|
head: options.head,
|
||||||
store: options.store,
|
store: options.features.store ? options.store : false,
|
||||||
globalName: options.globalName,
|
globalName: options.globalName,
|
||||||
globals: builder.globals,
|
globals: builder.globals,
|
||||||
css: options.css,
|
css: options.css,
|
||||||
|
@ -48,12 +48,14 @@ describe('builder: builder generate', () => {
|
|||||||
},
|
},
|
||||||
watch: []
|
watch: []
|
||||||
}
|
}
|
||||||
|
|
||||||
const builder = new Builder(nuxt, BundleBuilder)
|
const builder = new Builder(nuxt, BundleBuilder)
|
||||||
builder.normalizePlugins = jest.fn(() => [{ name: 'test_plugin', src: '/var/somesrc' }])
|
builder.normalizePlugins = jest.fn(() => [{ name: 'test_plugin', src: '/var/somesrc' }])
|
||||||
builder.resolveLayouts = jest.fn(() => 'resolveLayouts')
|
builder.resolveLayouts = jest.fn(() => 'resolveLayouts')
|
||||||
builder.resolveRoutes = jest.fn(() => 'resolveRoutes')
|
builder.resolveRoutes = jest.fn(() => 'resolveRoutes')
|
||||||
builder.resolveStore = jest.fn(() => 'resolveStore')
|
builder.resolveStore = jest.fn(() => 'resolveStore')
|
||||||
builder.resolveMiddleware = jest.fn(() => 'resolveMiddleware')
|
builder.resolveMiddleware = jest.fn(() => 'resolveMiddleware')
|
||||||
|
builder.addOptionalTemplates = jest.fn()
|
||||||
builder.resolveCustomTemplates = jest.fn()
|
builder.resolveCustomTemplates = jest.fn()
|
||||||
builder.resolveLoadingIndicator = jest.fn()
|
builder.resolveLoadingIndicator = jest.fn()
|
||||||
builder.compileTemplates = jest.fn()
|
builder.compileTemplates = jest.fn()
|
||||||
@ -77,6 +79,7 @@ describe('builder: builder generate', () => {
|
|||||||
'resolveStore',
|
'resolveStore',
|
||||||
'resolveMiddleware'
|
'resolveMiddleware'
|
||||||
])
|
])
|
||||||
|
expect(builder.addOptionalTemplates).toBeCalledTimes(1)
|
||||||
expect(builder.resolveCustomTemplates).toBeCalledTimes(1)
|
expect(builder.resolveCustomTemplates).toBeCalledTimes(1)
|
||||||
expect(builder.resolveLoadingIndicator).toBeCalledTimes(1)
|
expect(builder.resolveLoadingIndicator).toBeCalledTimes(1)
|
||||||
expect(builder.options.build.watch).toEqual(['/var/nuxt/src/template/**/*.{vue,js}'])
|
expect(builder.options.build.watch).toEqual(['/var/nuxt/src/template/**/*.{vue,js}'])
|
||||||
@ -124,6 +127,7 @@ describe('builder: builder generate', () => {
|
|||||||
|
|
||||||
test('should resolve store modules', async () => {
|
test('should resolve store modules', async () => {
|
||||||
const nuxt = createNuxt()
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.features = { store: true }
|
||||||
nuxt.options.store = true
|
nuxt.options.store = true
|
||||||
nuxt.options.dir = {
|
nuxt.options.dir = {
|
||||||
store: '/var/nuxt/src/store'
|
store: '/var/nuxt/src/store'
|
||||||
@ -153,8 +157,26 @@ describe('builder: builder generate', () => {
|
|||||||
expect(templateFiles).toEqual(['store.js'])
|
expect(templateFiles).toEqual(['store.js'])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should disable store resolving', async () => {
|
test('should disable store resolving when not set', async () => {
|
||||||
const nuxt = createNuxt()
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.features = { store: false }
|
||||||
|
nuxt.options.dir = {
|
||||||
|
store: '/var/nuxt/src/store'
|
||||||
|
}
|
||||||
|
const builder = new Builder(nuxt, BundleBuilder)
|
||||||
|
|
||||||
|
const templateVars = {}
|
||||||
|
const templateFiles = []
|
||||||
|
await builder.resolveStore({ templateVars, templateFiles })
|
||||||
|
|
||||||
|
expect(templateVars.storeModules).toBeUndefined()
|
||||||
|
expect(templateFiles).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should disable store resolving when feature disabled', async () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.features = { store: false }
|
||||||
|
nuxt.options.store = true
|
||||||
nuxt.options.dir = {
|
nuxt.options.dir = {
|
||||||
store: '/var/nuxt/src/store'
|
store: '/var/nuxt/src/store'
|
||||||
}
|
}
|
||||||
@ -170,6 +192,7 @@ describe('builder: builder generate', () => {
|
|||||||
|
|
||||||
test('should resolve middleware', async () => {
|
test('should resolve middleware', async () => {
|
||||||
const nuxt = createNuxt()
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.features = { middleware: true }
|
||||||
nuxt.options.store = false
|
nuxt.options.store = false
|
||||||
nuxt.options.srcDir = '/var/nuxt/src'
|
nuxt.options.srcDir = '/var/nuxt/src'
|
||||||
nuxt.options.dir = {
|
nuxt.options.dir = {
|
||||||
@ -183,13 +206,31 @@ describe('builder: builder generate', () => {
|
|||||||
builder.relativeToBuild = jest.fn().mockReturnValue(middlewarePath)
|
builder.relativeToBuild = jest.fn().mockReturnValue(middlewarePath)
|
||||||
|
|
||||||
const templateVars = {}
|
const templateVars = {}
|
||||||
await builder.resolveMiddleware({ templateVars })
|
const templateFiles = []
|
||||||
|
await builder.resolveMiddleware({ templateVars, templateFiles })
|
||||||
|
|
||||||
expect(templateVars.middleware).toEqual([{
|
expect(templateVars.middleware).toEqual([
|
||||||
|
{
|
||||||
name: 'subfolder/midd',
|
name: 'subfolder/midd',
|
||||||
src: 'subfolder/midd.js',
|
src: 'subfolder/midd.js',
|
||||||
dst: 'subfolder/midd.js'
|
dst: 'subfolder/midd.js'
|
||||||
}])
|
}
|
||||||
|
])
|
||||||
|
expect(templateFiles).toEqual(['middleware.js'])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should disable middleware when feature disabled', async () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.features = { middleware: false }
|
||||||
|
nuxt.options.store = false
|
||||||
|
nuxt.options.dir = {
|
||||||
|
middleware: '/var/nuxt/src/middleware'
|
||||||
|
}
|
||||||
|
const builder = new Builder(nuxt, BundleBuilder)
|
||||||
|
const templateVars = {}
|
||||||
|
const templateFiles = []
|
||||||
|
await builder.resolveMiddleware({ templateVars, templateFiles })
|
||||||
|
expect(templateFiles).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should custom templates', async () => {
|
test('should custom templates', async () => {
|
||||||
@ -414,6 +455,7 @@ describe('builder: builder generate', () => {
|
|||||||
describe('builder: builder resolveLayouts', () => {
|
describe('builder: builder resolveLayouts', () => {
|
||||||
test('should resolve layouts', async () => {
|
test('should resolve layouts', async () => {
|
||||||
const nuxt = createNuxt()
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.features = { layouts: true }
|
||||||
nuxt.options.srcDir = '/var/nuxt/src'
|
nuxt.options.srcDir = '/var/nuxt/src'
|
||||||
nuxt.options.buildDir = '/var/nuxt/build'
|
nuxt.options.buildDir = '/var/nuxt/build'
|
||||||
nuxt.options.dir = {
|
nuxt.options.dir = {
|
||||||
@ -466,6 +508,7 @@ describe('builder: builder generate', () => {
|
|||||||
|
|
||||||
test('should resolve error layouts', async () => {
|
test('should resolve error layouts', async () => {
|
||||||
const nuxt = createNuxt()
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.features = { layouts: true }
|
||||||
nuxt.options.srcDir = '/var/nuxt/src'
|
nuxt.options.srcDir = '/var/nuxt/src'
|
||||||
nuxt.options.dir = {
|
nuxt.options.dir = {
|
||||||
layouts: '/var/nuxt/src/layouts'
|
layouts: '/var/nuxt/src/layouts'
|
||||||
@ -493,6 +536,7 @@ describe('builder: builder generate', () => {
|
|||||||
|
|
||||||
test('should not resolve layouts if layouts dir does not exist', async () => {
|
test('should not resolve layouts if layouts dir does not exist', async () => {
|
||||||
const nuxt = createNuxt()
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.features = { layouts: true }
|
||||||
nuxt.options.srcDir = '/var/nuxt/src'
|
nuxt.options.srcDir = '/var/nuxt/src'
|
||||||
nuxt.options.dir = {
|
nuxt.options.dir = {
|
||||||
layouts: '/var/nuxt/src/layouts'
|
layouts: '/var/nuxt/src/layouts'
|
||||||
|
@ -20,6 +20,9 @@ TemplateContext {
|
|||||||
],
|
],
|
||||||
"env": "test_env",
|
"env": "test_env",
|
||||||
"extensions": "test|ext",
|
"extensions": "test|ext",
|
||||||
|
"features": Object {
|
||||||
|
"store": true,
|
||||||
|
},
|
||||||
"fetch": undefined,
|
"fetch": undefined,
|
||||||
"globalName": "test_global",
|
"globalName": "test_global",
|
||||||
"globals": Array [
|
"globals": Array [
|
||||||
@ -59,6 +62,9 @@ TemplateContext {
|
|||||||
"test",
|
"test",
|
||||||
"ext",
|
"ext",
|
||||||
],
|
],
|
||||||
|
"features": Object {
|
||||||
|
"store": true,
|
||||||
|
},
|
||||||
"globalName": "test_global",
|
"globalName": "test_global",
|
||||||
"head": "test_head",
|
"head": "test_head",
|
||||||
"layoutTransition": Object {
|
"layoutTransition": Object {
|
||||||
|
@ -16,6 +16,7 @@ describe('builder: buildContext', () => {
|
|||||||
relativeToBuild: jest.fn((...args) => `relativeBuild(${args.join(', ')})`)
|
relativeToBuild: jest.fn((...args) => `relativeBuild(${args.join(', ')})`)
|
||||||
}
|
}
|
||||||
const options = {
|
const options = {
|
||||||
|
features: { store: true },
|
||||||
extensions: [ 'test', 'ext' ],
|
extensions: [ 'test', 'ext' ],
|
||||||
messages: { test: 'test message' },
|
messages: { test: 'test message' },
|
||||||
build: {
|
build: {
|
||||||
|
@ -56,5 +56,22 @@ export default () => ({
|
|||||||
layoutTransition: {
|
layoutTransition: {
|
||||||
name: 'layout',
|
name: 'layout',
|
||||||
mode: 'out-in'
|
mode: 'out-in'
|
||||||
|
},
|
||||||
|
|
||||||
|
features: {
|
||||||
|
store: true,
|
||||||
|
layouts: true,
|
||||||
|
meta: true,
|
||||||
|
middleware: true,
|
||||||
|
transitions: true,
|
||||||
|
deprecations: true,
|
||||||
|
validate: true,
|
||||||
|
asyncData: true,
|
||||||
|
fetch: true,
|
||||||
|
clientOnline: true,
|
||||||
|
clientPrefetch: true,
|
||||||
|
clientUseUrl: false,
|
||||||
|
componentAliases: true,
|
||||||
|
componentClientOnly: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -166,6 +166,22 @@ Object {
|
|||||||
"js",
|
"js",
|
||||||
"mjs",
|
"mjs",
|
||||||
],
|
],
|
||||||
|
"features": Object {
|
||||||
|
"asyncData": true,
|
||||||
|
"clientOnline": true,
|
||||||
|
"clientPrefetch": true,
|
||||||
|
"clientUseUrl": false,
|
||||||
|
"componentAliases": true,
|
||||||
|
"componentClientOnly": true,
|
||||||
|
"deprecations": true,
|
||||||
|
"fetch": true,
|
||||||
|
"layouts": true,
|
||||||
|
"meta": true,
|
||||||
|
"middleware": true,
|
||||||
|
"store": true,
|
||||||
|
"transitions": true,
|
||||||
|
"validate": true,
|
||||||
|
},
|
||||||
"fetch": Object {
|
"fetch": Object {
|
||||||
"client": true,
|
"client": true,
|
||||||
"server": true,
|
"server": true,
|
||||||
|
@ -143,6 +143,22 @@ Object {
|
|||||||
"env": Object {},
|
"env": Object {},
|
||||||
"extendPlugins": null,
|
"extendPlugins": null,
|
||||||
"extensions": Array [],
|
"extensions": Array [],
|
||||||
|
"features": Object {
|
||||||
|
"asyncData": true,
|
||||||
|
"clientOnline": true,
|
||||||
|
"clientPrefetch": true,
|
||||||
|
"clientUseUrl": false,
|
||||||
|
"componentAliases": true,
|
||||||
|
"componentClientOnly": true,
|
||||||
|
"deprecations": true,
|
||||||
|
"fetch": true,
|
||||||
|
"layouts": true,
|
||||||
|
"meta": true,
|
||||||
|
"middleware": true,
|
||||||
|
"store": true,
|
||||||
|
"transitions": true,
|
||||||
|
"validate": true,
|
||||||
|
},
|
||||||
"fetch": Object {
|
"fetch": Object {
|
||||||
"client": true,
|
"client": true,
|
||||||
"server": true,
|
"server": true,
|
||||||
@ -474,6 +490,22 @@ Object {
|
|||||||
"env": Object {},
|
"env": Object {},
|
||||||
"extendPlugins": null,
|
"extendPlugins": null,
|
||||||
"extensions": Array [],
|
"extensions": Array [],
|
||||||
|
"features": Object {
|
||||||
|
"asyncData": true,
|
||||||
|
"clientOnline": true,
|
||||||
|
"clientPrefetch": true,
|
||||||
|
"clientUseUrl": false,
|
||||||
|
"componentAliases": true,
|
||||||
|
"componentClientOnly": true,
|
||||||
|
"deprecations": true,
|
||||||
|
"fetch": true,
|
||||||
|
"layouts": true,
|
||||||
|
"meta": true,
|
||||||
|
"middleware": true,
|
||||||
|
"store": true,
|
||||||
|
"transitions": true,
|
||||||
|
"validate": true,
|
||||||
|
},
|
||||||
"fetch": Object {
|
"fetch": Object {
|
||||||
"client": true,
|
"client": true,
|
||||||
"server": true,
|
"server": true,
|
||||||
|
@ -8,15 +8,12 @@ export const template = {
|
|||||||
'App.js',
|
'App.js',
|
||||||
'client.js',
|
'client.js',
|
||||||
'index.js',
|
'index.js',
|
||||||
'middleware.js',
|
|
||||||
'router.js',
|
'router.js',
|
||||||
'router.scrollBehavior.js',
|
'router.scrollBehavior.js',
|
||||||
'server.js',
|
'server.js',
|
||||||
'utils.js',
|
'utils.js',
|
||||||
'empty.js',
|
'empty.js',
|
||||||
'components/nuxt-build-indicator.vue',
|
|
||||||
'components/nuxt-error.vue',
|
'components/nuxt-error.vue',
|
||||||
'components/nuxt-loading.vue',
|
|
||||||
'components/nuxt-child.js',
|
'components/nuxt-child.js',
|
||||||
'components/nuxt-link.server.js',
|
'components/nuxt-link.server.js',
|
||||||
'components/nuxt-link.client.js',
|
'components/nuxt-link.client.js',
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import { getMatchedComponentsInstances, promisify, globalHandleError } from './utils'
|
<% if (features.asyncData || features.fetch) { %>
|
||||||
|
import {
|
||||||
|
getMatchedComponentsInstances,
|
||||||
|
promisify,
|
||||||
|
globalHandleError
|
||||||
|
} from './utils'
|
||||||
|
<% } %>
|
||||||
<% if (loading) { %>import NuxtLoading from '<%= (typeof loading === "string" ? loading : "./components/nuxt-loading.vue") %>'<% } %>
|
<% if (loading) { %>import NuxtLoading from '<%= (typeof loading === "string" ? loading : "./components/nuxt-loading.vue") %>'<% } %>
|
||||||
<% if (buildIndicator) { %>import NuxtBuildIndicator from './components/nuxt-build-indicator'<% } %>
|
<% if (buildIndicator) { %>import NuxtBuildIndicator from './components/nuxt-build-indicator'<% } %>
|
||||||
<% css.forEach((c) => { %>
|
<% css.forEach((c) => { %>
|
||||||
import '<%= relativeToBuild(resolvePath(c.src || c, { isStyle: true })) %>'
|
import '<%= relativeToBuild(resolvePath(c.src || c, { isStyle: true })) %>'
|
||||||
<% }) %>
|
<% }) %>
|
||||||
|
|
||||||
|
<% if (features.layouts) { %>
|
||||||
<%= Object.keys(layouts).map((key) => {
|
<%= Object.keys(layouts).map((key) => {
|
||||||
if (splitChunks.layouts) {
|
if (splitChunks.layouts) {
|
||||||
return `const _${hash(key)} = () => import('${layouts[key]}' /* webpackChunkName: "${wChunk('layouts/' + key)}" */).then(m => m.default || m)`
|
return `const _${hash(key)} = () => import('${layouts[key]}' /* webpackChunkName: "${wChunk('layouts/' + key)}" */).then(m => m.default || m)`
|
||||||
@ -17,13 +24,17 @@ import '<%= relativeToBuild(resolvePath(c.src || c, { isStyle: true })) %>'
|
|||||||
const layouts = { <%= Object.keys(layouts).map(key => `"_${key}": _${hash(key)}`).join(',') %> }<%= isTest ? '// eslint-disable-line' : '' %>
|
const layouts = { <%= Object.keys(layouts).map(key => `"_${key}": _${hash(key)}`).join(',') %> }<%= isTest ? '// eslint-disable-line' : '' %>
|
||||||
|
|
||||||
<% if (splitChunks.layouts) { %>let resolvedLayouts = {}<% } %>
|
<% if (splitChunks.layouts) { %>let resolvedLayouts = {}<% } %>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
<% if (features.meta) { %>
|
||||||
<%= isTest ? '/* eslint-disable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren */' : '' %>
|
<%= isTest ? '/* eslint-disable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren */' : '' %>
|
||||||
head: <%= serializeFunction(head) %>,
|
head: <%= serializeFunction(head) %>,
|
||||||
<%= isTest ? '/* eslint-enable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren */' : '' %>
|
<%= isTest ? '/* eslint-enable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren */' : '' %>
|
||||||
|
<% } %>
|
||||||
render(h, props) {
|
render(h, props) {
|
||||||
<% if (loading) { %>const loadingEl = h('NuxtLoading', { ref: 'loading' })<% } %>
|
<% if (loading) { %>const loadingEl = h('NuxtLoading', { ref: 'loading' })<% } %>
|
||||||
|
<% if (features.layouts) { %>
|
||||||
const layoutEl = h(this.layout || 'nuxt')
|
const layoutEl = h(this.layout || 'nuxt')
|
||||||
const templateEl = h('div', {
|
const templateEl = h('div', {
|
||||||
domProps: {
|
domProps: {
|
||||||
@ -31,7 +42,11 @@ export default {
|
|||||||
},
|
},
|
||||||
key: this.layoutName
|
key: this.layoutName
|
||||||
}, [ layoutEl ])
|
}, [ layoutEl ])
|
||||||
|
<% } else { %>
|
||||||
|
const templateEl = h('nuxt')
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.transitions) { %>
|
||||||
const transitionEl = h('transition', {
|
const transitionEl = h('transition', {
|
||||||
props: {
|
props: {
|
||||||
name: '<%= layoutTransition.name %>',
|
name: '<%= layoutTransition.name %>',
|
||||||
@ -46,18 +61,29 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [ templateEl ])
|
}, [ templateEl ])
|
||||||
|
<% } %>
|
||||||
|
|
||||||
return h('div', {
|
return h('div', {
|
||||||
domProps: {
|
domProps: {
|
||||||
id: '<%= globals.id %>'
|
id: '<%= globals.id %>'
|
||||||
}
|
}
|
||||||
}, [<% if (loading) { %>loadingEl, <% } %><%if (buildIndicator) { %>h(NuxtBuildIndicator), <% } %>transitionEl])
|
}, [
|
||||||
|
<% if (loading) { %>loadingEl, <% } %>
|
||||||
|
<% if (buildIndicator) { %>h(NuxtBuildIndicator), <% } %>
|
||||||
|
<% if (features.transitions) { %>transitionEl<% } else { %>templateEl<% } %>
|
||||||
|
])
|
||||||
},
|
},
|
||||||
|
<% if (features.clientOnline || features.layouts) { %>
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
<% if (features.clientOnline) { %>
|
||||||
isOnline: true,
|
isOnline: true,
|
||||||
|
<% } %>
|
||||||
|
<% if (features.layouts) { %>
|
||||||
layout: null,
|
layout: null,
|
||||||
layoutName: ''
|
layoutName: ''
|
||||||
|
<% } %>
|
||||||
}),
|
}),
|
||||||
|
<% } %>
|
||||||
beforeCreate() {
|
beforeCreate() {
|
||||||
Vue.util.defineReactive(this, 'nuxt', this.$options.nuxt)
|
Vue.util.defineReactive(this, 'nuxt', this.$options.nuxt)
|
||||||
},
|
},
|
||||||
@ -67,10 +93,12 @@ export default {
|
|||||||
// add to window so we can listen when ready
|
// add to window so we can listen when ready
|
||||||
if (process.client) {
|
if (process.client) {
|
||||||
window.<%= globals.nuxt %> = <%= (globals.nuxt !== '$nuxt' ? 'window.$nuxt = ' : '') %>this
|
window.<%= globals.nuxt %> = <%= (globals.nuxt !== '$nuxt' ? 'window.$nuxt = ' : '') %>this
|
||||||
|
<% if (features.clientOnline) { %>
|
||||||
this.refreshOnlineStatus()
|
this.refreshOnlineStatus()
|
||||||
// Setup the listeners
|
// Setup the listeners
|
||||||
window.addEventListener('online', this.refreshOnlineStatus)
|
window.addEventListener('online', this.refreshOnlineStatus)
|
||||||
window.addEventListener('offline', this.refreshOnlineStatus)
|
window.addEventListener('offline', this.refreshOnlineStatus)
|
||||||
|
<% } %>
|
||||||
}
|
}
|
||||||
// Add $nuxt.error()
|
// Add $nuxt.error()
|
||||||
this.error = this.nuxt.error
|
this.error = this.nuxt.error
|
||||||
@ -85,12 +113,15 @@ export default {
|
|||||||
'nuxt.err': 'errorChanged'
|
'nuxt.err': 'errorChanged'
|
||||||
},
|
},
|
||||||
<% } %>
|
<% } %>
|
||||||
|
<% if (features.clientOnline) { %>
|
||||||
computed: {
|
computed: {
|
||||||
isOffline() {
|
isOffline() {
|
||||||
return !this.isOnline
|
return !this.isOnline
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
<% } %>
|
||||||
methods: {
|
methods: {
|
||||||
|
<% if (features.clientOnline) { %>
|
||||||
refreshOnlineStatus() {
|
refreshOnlineStatus() {
|
||||||
if (process.client) {
|
if (process.client) {
|
||||||
if (typeof window.navigator.onLine === 'undefined') {
|
if (typeof window.navigator.onLine === 'undefined') {
|
||||||
@ -103,19 +134,25 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
<% } %>
|
||||||
async refresh() {
|
async refresh() {
|
||||||
|
<% if (features.asyncData || features.fetch) { %>
|
||||||
const pages = getMatchedComponentsInstances(this.$route)
|
const pages = getMatchedComponentsInstances(this.$route)
|
||||||
|
|
||||||
if (!pages.length) {
|
if (!pages.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
<% if (loading) { %>this.$loading.start()<% } %>
|
<% if (loading) { %>this.$loading.start()<% } %>
|
||||||
|
|
||||||
const promises = pages.map(async (page) => {
|
const promises = pages.map(async (page) => {
|
||||||
const p = []
|
const p = []
|
||||||
|
|
||||||
|
<% if (features.fetch) { %>
|
||||||
if (page.$options.fetch) {
|
if (page.$options.fetch) {
|
||||||
p.push(promisify(page.$options.fetch, this.context))
|
p.push(promisify(page.$options.fetch, this.context))
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
|
<% if (features.asyncData) { %>
|
||||||
if (page.$options.asyncData) {
|
if (page.$options.asyncData) {
|
||||||
p.push(
|
p.push(
|
||||||
promisify(page.$options.asyncData, this.context)
|
promisify(page.$options.asyncData, this.context)
|
||||||
@ -126,6 +163,7 @@ export default {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
return Promise.all(p)
|
return Promise.all(p)
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
@ -136,6 +174,7 @@ export default {
|
|||||||
this.error(error)
|
this.error(error)
|
||||||
}
|
}
|
||||||
<% if (loading) { %>this.$loading.finish()<% } %>
|
<% if (loading) { %>this.$loading.finish()<% } %>
|
||||||
|
<% } %>
|
||||||
},
|
},
|
||||||
<% if (loading) { %>
|
<% if (loading) { %>
|
||||||
errorChanged() {
|
errorChanged() {
|
||||||
@ -145,6 +184,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
<% } %>
|
<% } %>
|
||||||
|
<% if (features.layouts) { %>
|
||||||
<% if (splitChunks.layouts) { %>
|
<% if (splitChunks.layouts) { %>
|
||||||
setLayout(layout) {
|
setLayout(layout) {
|
||||||
<% if (debug) { %>
|
<% if (debug) { %>
|
||||||
@ -193,9 +233,12 @@ export default {
|
|||||||
}
|
}
|
||||||
return Promise.resolve(layouts['_' + layout])
|
return Promise.resolve(layouts['_' + layout])
|
||||||
}
|
}
|
||||||
<% } %>
|
<% } /* splitChunks.layouts */ %>
|
||||||
|
<% } /* features.layouts */ %>
|
||||||
},
|
},
|
||||||
|
<% if (loading) { %>
|
||||||
components: {
|
components: {
|
||||||
<%= (loading ? 'NuxtLoading' : '') %>
|
NuxtLoading
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
<% if (fetch.client) { %>import fetch from 'unfetch'<% } %>
|
<% if (fetch.client) { %>import fetch from 'unfetch'<% } %>
|
||||||
import middleware from './middleware.js'
|
<% if (features.middleware) { %>import middleware from './middleware.js'<% } %>
|
||||||
import {
|
import {
|
||||||
applyAsyncData,
|
<% if (features.asyncData) { %>applyAsyncData,<% } %>
|
||||||
|
<% if (features.middleware) { %>middlewareSeries,<% } %>
|
||||||
sanitizeComponent,
|
sanitizeComponent,
|
||||||
resolveRouteComponents,
|
resolveRouteComponents,
|
||||||
getMatchedComponents,
|
getMatchedComponents,
|
||||||
getMatchedComponentsInstances,
|
getMatchedComponentsInstances,
|
||||||
flatMapComponents,
|
flatMapComponents,
|
||||||
setContext,
|
setContext,
|
||||||
middlewareSeries,
|
|
||||||
promisify,
|
promisify,
|
||||||
getLocation,
|
getLocation,
|
||||||
compile,
|
compile,
|
||||||
@ -17,7 +17,7 @@ import {
|
|||||||
globalHandleError
|
globalHandleError
|
||||||
} from './utils.js'
|
} from './utils.js'
|
||||||
import { createApp, NuxtError } from './index.js'
|
import { createApp, NuxtError } from './index.js'
|
||||||
import NuxtLink from './components/nuxt-link.<%= router.prefetchLinks ? "client" : "server" %>.js' // should be included after ./index.js
|
import NuxtLink from './components/nuxt-link.<%= features.clientPrefetch && router.prefetchLinks ? "client" : "server" %>.js' // should be included after ./index.js
|
||||||
<% if (isDev) { %>import consola from 'consola'<% } %>
|
<% if (isDev) { %>import consola from 'consola'<% } %>
|
||||||
|
|
||||||
<% if (isDev) { %>consola.wrapConsole()
|
<% if (isDev) { %>consola.wrapConsole()
|
||||||
@ -26,7 +26,7 @@ console.log = console.__log
|
|||||||
|
|
||||||
// Component: <NuxtLink>
|
// Component: <NuxtLink>
|
||||||
Vue.component(NuxtLink.name, NuxtLink)
|
Vue.component(NuxtLink.name, NuxtLink)
|
||||||
Vue.component('NLink', NuxtLink)
|
<% if (features.componentAliases) { %>Vue.component('NLink', NuxtLink)<% } %>
|
||||||
|
|
||||||
<% if (fetch.client) { %>if (!global.fetch) { global.fetch = fetch }<% } %>
|
<% if (fetch.client) { %>if (!global.fetch) { global.fetch = fetch }<% } %>
|
||||||
|
|
||||||
@ -94,6 +94,7 @@ const errorHandler = Vue.config.errorHandler || console.error
|
|||||||
// Create and mount App
|
// Create and mount App
|
||||||
createApp().then(mountApp).catch(errorHandler)
|
createApp().then(mountApp).catch(errorHandler)
|
||||||
|
|
||||||
|
<% if (features.transitions) { %>
|
||||||
function componentOption(component, key, ...args) {
|
function componentOption(component, key, ...args) {
|
||||||
if (!component || !component.options || !component.options[key]) {
|
if (!component || !component.options || !component.options[key]) {
|
||||||
return {}
|
return {}
|
||||||
@ -126,7 +127,7 @@ function mapTransitions(Components, to, from) {
|
|||||||
return transitions
|
return transitions
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
async function loadAsyncComponents(to, from, next) {
|
async function loadAsyncComponents(to, from, next) {
|
||||||
// Check if route path changed (this._pathChanged), only if the page is not an error (for validate())
|
// Check if route path changed (this._pathChanged), only if the page is not an error (for validate())
|
||||||
this._pathChanged = Boolean(app.nuxt.err) || from.path !== to.path
|
this._pathChanged = Boolean(app.nuxt.err) || from.path !== to.path
|
||||||
@ -184,9 +185,11 @@ async function loadAsyncComponents(to, from, next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applySSRData(Component, ssrData) {
|
function applySSRData(Component, ssrData) {
|
||||||
|
<% if (features.asyncData) { %>
|
||||||
if (NUXT.serverRendered && ssrData) {
|
if (NUXT.serverRendered && ssrData) {
|
||||||
applyAsyncData(Component, ssrData)
|
applyAsyncData(Component, ssrData)
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
Component._Ctor = Component
|
Component._Ctor = Component
|
||||||
return Component
|
return Component
|
||||||
}
|
}
|
||||||
@ -206,11 +209,12 @@ function resolveComponents(router) {
|
|||||||
return _Component
|
return _Component
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
<% if (features.middleware) { %>
|
||||||
function callMiddleware(Components, context, layout) {
|
function callMiddleware(Components, context, layout) {
|
||||||
let midd = <%= devalue(router.middleware) %><%= isTest ? '// eslint-disable-line' : '' %>
|
let midd = <%= devalue(router.middleware) %><%= isTest ? '// eslint-disable-line' : '' %>
|
||||||
let unknownMiddleware = false
|
let unknownMiddleware = false
|
||||||
|
|
||||||
|
<% if (features.layouts) { %>
|
||||||
// If layout is undefined, only call global middleware
|
// If layout is undefined, only call global middleware
|
||||||
if (typeof layout !== 'undefined') {
|
if (typeof layout !== 'undefined') {
|
||||||
midd = [] // Exclude global middleware if layout defined (already called before)
|
midd = [] // Exclude global middleware if layout defined (already called before)
|
||||||
@ -224,6 +228,7 @@ function callMiddleware(Components, context, layout) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
|
|
||||||
midd = midd.map((name) => {
|
midd = midd.map((name) => {
|
||||||
if (typeof name === 'function') return name
|
if (typeof name === 'function') return name
|
||||||
@ -237,7 +242,14 @@ function callMiddleware(Components, context, layout) {
|
|||||||
if (unknownMiddleware) return
|
if (unknownMiddleware) return
|
||||||
return middlewareSeries(midd, context)
|
return middlewareSeries(midd, context)
|
||||||
}
|
}
|
||||||
|
<% } else if (isDev) {
|
||||||
|
// This is a placeholder function mainly so we dont have to
|
||||||
|
// refactor the promise chain in addHotReload()
|
||||||
|
%>
|
||||||
|
function callMiddleware() {
|
||||||
|
return Promise.resolve(true)
|
||||||
|
}
|
||||||
|
<% } %>
|
||||||
async function render(to, from, next) {
|
async function render(to, from, next) {
|
||||||
if (this._pathChanged === false && this._queryChanged === false) return next()
|
if (this._pathChanged === false && this._queryChanged === false) return next()
|
||||||
// Handle first render on SPA mode
|
// Handle first render on SPA mode
|
||||||
@ -282,51 +294,71 @@ async function render(to, from, next) {
|
|||||||
|
|
||||||
// If no Components matched, generate 404
|
// If no Components matched, generate 404
|
||||||
if (!Components.length) {
|
if (!Components.length) {
|
||||||
|
<% if (features.middleware) { %>
|
||||||
// Default layout
|
// Default layout
|
||||||
await callMiddleware.call(this, Components, app.context)
|
await callMiddleware.call(this, Components, app.context)
|
||||||
if (nextCalled) return
|
if (nextCalled) return
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.layouts) { %>
|
||||||
// Load layout for error page
|
// Load layout for error page
|
||||||
const layout = await this.loadLayout(
|
const layout = await this.loadLayout(
|
||||||
typeof NuxtError.layout === 'function'
|
typeof NuxtError.layout === 'function'
|
||||||
? NuxtError.layout(app.context)
|
? NuxtError.layout(app.context)
|
||||||
: NuxtError.layout
|
: NuxtError.layout
|
||||||
)
|
)
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.middleware) { %>
|
||||||
await callMiddleware.call(this, Components, app.context, layout)
|
await callMiddleware.call(this, Components, app.context, layout)
|
||||||
if (nextCalled) return
|
if (nextCalled) return
|
||||||
|
<% } %>
|
||||||
|
|
||||||
// Show error page
|
// Show error page
|
||||||
app.context.error({ statusCode: 404, message: `<%= messages.error_404 %>` })
|
app.context.error({ statusCode: 404, message: `<%= messages.error_404 %>` })
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<% if (features.asyncData || features.fetch) { %>
|
||||||
// Update ._data and other properties if hot reloaded
|
// Update ._data and other properties if hot reloaded
|
||||||
Components.forEach((Component) => {
|
Components.forEach((Component) => {
|
||||||
if (Component._Ctor && Component._Ctor.options) {
|
if (Component._Ctor && Component._Ctor.options) {
|
||||||
Component.options.asyncData = Component._Ctor.options.asyncData
|
<% if (features.asyncData) { %>Component.options.asyncData = Component._Ctor.options.asyncData<% } %>
|
||||||
Component.options.fetch = Component._Ctor.options.fetch
|
<% if (features.fetch) { %>Component.options.fetch = Component._Ctor.options.fetch<% } %>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.transitions) { %>
|
||||||
// Apply transitions
|
// Apply transitions
|
||||||
this.setTransitions(mapTransitions(Components, to, from))
|
this.setTransitions(mapTransitions(Components, to, from))
|
||||||
|
<% } %>
|
||||||
try {
|
try {
|
||||||
|
<% if (features.middleware) { %>
|
||||||
// Call middleware
|
// Call middleware
|
||||||
await callMiddleware.call(this, Components, app.context)
|
await callMiddleware.call(this, Components, app.context)
|
||||||
if (nextCalled) return
|
if (nextCalled) return
|
||||||
if (app.context._errored) return next()
|
if (app.context._errored) return next()
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.layouts) { %>
|
||||||
// Set layout
|
// Set layout
|
||||||
let layout = Components[0].options.layout
|
let layout = Components[0].options.layout
|
||||||
if (typeof layout === 'function') {
|
if (typeof layout === 'function') {
|
||||||
layout = layout(app.context)
|
layout = layout(app.context)
|
||||||
}
|
}
|
||||||
layout = await this.loadLayout(layout)
|
layout = await this.loadLayout(layout)
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.middleware) { %>
|
||||||
// Call middleware for layout
|
// Call middleware for layout
|
||||||
await callMiddleware.call(this, Components, app.context, layout)
|
await callMiddleware.call(this, Components, app.context, layout)
|
||||||
if (nextCalled) return
|
if (nextCalled) return
|
||||||
if (app.context._errored) return next()
|
if (app.context._errored) return next()
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
|
||||||
|
<% if (features.validate) { %>
|
||||||
// Call .validate()
|
// Call .validate()
|
||||||
let isValid = true
|
let isValid = true
|
||||||
try {
|
try {
|
||||||
@ -355,7 +387,9 @@ async function render(to, from, next) {
|
|||||||
this.error({ statusCode: 404, message: `<%= messages.error_404 %>` })
|
this.error({ statusCode: 404, message: `<%= messages.error_404 %>` })
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.asyncData || features.fetch) { %>
|
||||||
let instances
|
let instances
|
||||||
// Call asyncData & fetch hooks on components matched by the route.
|
// Call asyncData & fetch hooks on components matched by the route.
|
||||||
await Promise.all(Components.map((Component, i) => {
|
await Promise.all(Components.map((Component, i) => {
|
||||||
@ -380,20 +414,31 @@ async function render(to, from, next) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this._hadError && this._isMounted && !Component._dataRefresh) {
|
if (!this._hadError && this._isMounted && !Component._dataRefresh) {
|
||||||
return Promise.resolve()
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const promises = []
|
const promises = []
|
||||||
|
|
||||||
|
<% if (features.asyncData) { %>
|
||||||
const hasAsyncData = (
|
const hasAsyncData = (
|
||||||
Component.options.asyncData &&
|
Component.options.asyncData &&
|
||||||
typeof Component.options.asyncData === 'function'
|
typeof Component.options.asyncData === 'function'
|
||||||
)
|
)
|
||||||
|
<% } else { %>
|
||||||
|
const hasAsyncData = false
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.fetch) { %>
|
||||||
const hasFetch = Boolean(Component.options.fetch)
|
const hasFetch = Boolean(Component.options.fetch)
|
||||||
|
<% } else { %>
|
||||||
|
const hasFetch = false
|
||||||
|
<% } %>
|
||||||
|
|
||||||
<% if (loading) { %>
|
<% if (loading) { %>
|
||||||
const loadingIncrease = (hasAsyncData && hasFetch) ? 30 : 45
|
const loadingIncrease = (hasAsyncData && hasFetch) ? 30 : 45
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.asyncData) { %>
|
||||||
// Call asyncData(context)
|
// Call asyncData(context)
|
||||||
if (hasAsyncData) {
|
if (hasAsyncData) {
|
||||||
const promise = promisify(Component.options.asyncData, app.context)
|
const promise = promisify(Component.options.asyncData, app.context)
|
||||||
@ -407,10 +452,12 @@ async function render(to, from, next) {
|
|||||||
})
|
})
|
||||||
promises.push(promise)
|
promises.push(promise)
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
|
|
||||||
// Check disabled page loading
|
// Check disabled page loading
|
||||||
this.$loading.manual = Component.options.loading === false
|
this.$loading.manual = Component.options.loading === false
|
||||||
|
|
||||||
|
<% if (features.fetch) { %>
|
||||||
// Call fetch(context)
|
// Call fetch(context)
|
||||||
if (hasFetch) {
|
if (hasFetch) {
|
||||||
let p = Component.options.fetch(app.context)
|
let p = Component.options.fetch(app.context)
|
||||||
@ -426,9 +473,11 @@ async function render(to, from, next) {
|
|||||||
})
|
})
|
||||||
promises.push(p)
|
promises.push(p)
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
|
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
}))
|
}))
|
||||||
|
<% } %>
|
||||||
|
|
||||||
// If not redirected
|
// If not redirected
|
||||||
if (!nextCalled) {
|
if (!nextCalled) {
|
||||||
@ -449,12 +498,14 @@ async function render(to, from, next) {
|
|||||||
|
|
||||||
globalHandleError(error)
|
globalHandleError(error)
|
||||||
|
|
||||||
|
<% if (features.layouts) { %>
|
||||||
// Load error layout
|
// Load error layout
|
||||||
let layout = NuxtError.layout
|
let layout = NuxtError.layout
|
||||||
if (typeof layout === 'function') {
|
if (typeof layout === 'function') {
|
||||||
layout = layout(app.context)
|
layout = layout(app.context)
|
||||||
}
|
}
|
||||||
await this.loadLayout(layout)
|
await this.loadLayout(layout)
|
||||||
|
<% } %>
|
||||||
|
|
||||||
this.error(error)
|
this.error(error)
|
||||||
this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error)
|
this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error)
|
||||||
@ -481,6 +532,7 @@ function showNextPage(to) {
|
|||||||
this.error()
|
this.error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<% if (features.layouts) { %>
|
||||||
// Set layout
|
// Set layout
|
||||||
let layout = this.$options.nuxt.err
|
let layout = this.$options.nuxt.err
|
||||||
? NuxtError.layout
|
? NuxtError.layout
|
||||||
@ -490,6 +542,7 @@ function showNextPage(to) {
|
|||||||
layout = layout(app.context)
|
layout = layout(app.context)
|
||||||
}
|
}
|
||||||
this.setLayout(layout)
|
this.setLayout(layout)
|
||||||
|
<% } %>
|
||||||
}
|
}
|
||||||
|
|
||||||
// When navigating on a different route but the same component is used, Vue.js
|
// When navigating on a different route but the same component is used, Vue.js
|
||||||
@ -581,7 +634,9 @@ function addHotReload($component, depth) {
|
|||||||
$component.$vnode.context.$forceUpdate = async () => {
|
$component.$vnode.context.$forceUpdate = async () => {
|
||||||
let Components = getMatchedComponents(router.currentRoute)
|
let Components = getMatchedComponents(router.currentRoute)
|
||||||
let Component = Components[depth]
|
let Component = Components[depth]
|
||||||
if (!Component) return _forceUpdate()
|
if (!Component) {
|
||||||
|
return _forceUpdate()
|
||||||
|
}
|
||||||
if (typeof Component === 'object' && !Component.options) {
|
if (typeof Component === 'object' && !Component.options) {
|
||||||
// Updated via vue-router resolveAsyncComponents()
|
// Updated via vue-router resolveAsyncComponents()
|
||||||
Component = Vue.extend(Component)
|
Component = Vue.extend(Component)
|
||||||
@ -599,29 +654,45 @@ function addHotReload($component, depth) {
|
|||||||
next: next.bind(this)
|
next: next.bind(this)
|
||||||
})
|
})
|
||||||
const context = app.context
|
const context = app.context
|
||||||
|
|
||||||
<% if (loading) { %>
|
<% if (loading) { %>
|
||||||
if (this.$loading.start && !this.$loading.manual) this.$loading.start()
|
if (this.$loading.start && !this.$loading.manual) {
|
||||||
|
this.$loading.start()
|
||||||
|
}
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
callMiddleware.call(this, Components, context)
|
callMiddleware.call(this, Components, context)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
<% if (features.layouts) { %>
|
||||||
// If layout changed
|
// If layout changed
|
||||||
if (depth !== 0) return Promise.resolve()
|
if (depth !== 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let layout = Component.options.layout || 'default'
|
let layout = Component.options.layout || 'default'
|
||||||
if (typeof layout === 'function') {
|
if (typeof layout === 'function') {
|
||||||
layout = layout(context)
|
layout = layout(context)
|
||||||
}
|
}
|
||||||
if (this.layoutName === layout) return Promise.resolve()
|
if (this.layoutName === layout) {
|
||||||
|
return
|
||||||
|
}
|
||||||
let promise = this.loadLayout(layout)
|
let promise = this.loadLayout(layout)
|
||||||
promise.then(() => {
|
promise.then(() => {
|
||||||
this.setLayout(layout)
|
this.setLayout(layout)
|
||||||
Vue.nextTick(() => hotReloadAPI(this))
|
Vue.nextTick(() => hotReloadAPI(this))
|
||||||
})
|
})
|
||||||
return promise
|
return promise
|
||||||
|
<% } else { %>
|
||||||
|
return
|
||||||
|
<% } %>
|
||||||
})
|
})
|
||||||
|
<% if (features.layouts) { %>
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return callMiddleware.call(this, Components, context, this.layout)
|
return callMiddleware.call(this, Components, context, this.layout)
|
||||||
})
|
})
|
||||||
|
<% } %>
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
<% if (features.asyncData) { %>
|
||||||
// Call asyncData(context)
|
// Call asyncData(context)
|
||||||
let pAsyncData = promisify(Component.options.asyncData || noopData, context)
|
let pAsyncData = promisify(Component.options.asyncData || noopData, context)
|
||||||
pAsyncData.then((asyncDataResult) => {
|
pAsyncData.then((asyncDataResult) => {
|
||||||
@ -629,12 +700,16 @@ function addHotReload($component, depth) {
|
|||||||
<%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %>
|
<%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %>
|
||||||
})
|
})
|
||||||
promises.push(pAsyncData)
|
promises.push(pAsyncData)
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.fetch) { %>
|
||||||
// Call fetch()
|
// Call fetch()
|
||||||
Component.options.fetch = Component.options.fetch || noopFetch
|
Component.options.fetch = Component.options.fetch || noopFetch
|
||||||
let pFetch = Component.options.fetch(context)
|
let pFetch = Component.options.fetch(context)
|
||||||
if (!pFetch || (!(pFetch instanceof Promise) && (typeof pFetch.then !== 'function'))) { pFetch = Promise.resolve(pFetch) }
|
if (!pFetch || (!(pFetch instanceof Promise) && (typeof pFetch.then !== 'function'))) { pFetch = Promise.resolve(pFetch) }
|
||||||
<%= (loading ? 'pFetch.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %>
|
<%= (loading ? 'pFetch.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %>
|
||||||
promises.push(pFetch)
|
promises.push(pFetch)
|
||||||
|
<% } %>
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -658,7 +733,7 @@ async function mountApp(__app) {
|
|||||||
// Create Vue instance
|
// Create Vue instance
|
||||||
const _app = new Vue(app)
|
const _app = new Vue(app)
|
||||||
|
|
||||||
<% if (mode !== 'spa') { %>
|
<% if (features.layouts && mode !== 'spa') { %>
|
||||||
// Load layout
|
// Load layout
|
||||||
const layout = NUXT.layout || 'default'
|
const layout = NUXT.layout || 'default'
|
||||||
await _app.loadLayout(layout)
|
await _app.loadLayout(layout)
|
||||||
@ -683,14 +758,14 @@ async function mountApp(__app) {
|
|||||||
<% } %>
|
<% } %>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
<% if (features.transitions) { %>
|
||||||
// Enable transitions
|
// Enable transitions
|
||||||
_app.setTransitions = _app.$options.nuxt.setTransitions.bind(_app)
|
_app.setTransitions = _app.$options.nuxt.setTransitions.bind(_app)
|
||||||
if (Components.length) {
|
if (Components.length) {
|
||||||
_app.setTransitions(mapTransitions(Components, router.currentRoute))
|
_app.setTransitions(mapTransitions(Components, router.currentRoute))
|
||||||
_lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params))
|
_lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params))
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
// Initialize error handler
|
// Initialize error handler
|
||||||
_app.$loading = {} // To avoid error while _app.$nuxt does not exist
|
_app.$loading = {} // To avoid error while _app.$nuxt does not exist
|
||||||
if (NUXT.error) _app.error(NUXT.error)
|
if (NUXT.error) _app.error(NUXT.error)
|
||||||
|
@ -14,6 +14,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
render(h, { parent, data, props }) {
|
render(h, { parent, data, props }) {
|
||||||
|
<% if (features.transitions) { %>
|
||||||
data.nuxtChild = true
|
data.nuxtChild = true
|
||||||
const _parent = parent
|
const _parent = parent
|
||||||
const transitions = parent.<%= globals.nuxt %>.nuxt.transitions
|
const transitions = parent.<%= globals.nuxt %>.nuxt.transitions
|
||||||
@ -69,20 +70,25 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
let routerView = h('routerView', data)
|
let routerView = h('routerView', data)
|
||||||
|
|
||||||
if (props.keepAlive) {
|
if (props.keepAlive) {
|
||||||
routerView = h('keep-alive', { props: props.keepAliveProps }, [routerView])
|
routerView = h('keep-alive', { props: props.keepAliveProps }, [routerView])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<% if (features.transitions) { %>
|
||||||
return h('transition', {
|
return h('transition', {
|
||||||
props: transitionProps,
|
props: transitionProps,
|
||||||
on: listeners
|
on: listeners
|
||||||
}, [routerView])
|
}, [routerView])
|
||||||
|
<% } else { %>
|
||||||
|
return routerView
|
||||||
|
<% } %>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<% if (features.transitions) { %>
|
||||||
const transitionsKeys = [
|
const transitionsKeys = [
|
||||||
'name',
|
'name',
|
||||||
'mode',
|
'mode',
|
||||||
@ -116,3 +122,4 @@ const listenersKeys = [
|
|||||||
'afterAppear',
|
'afterAppear',
|
||||||
'appearCancelled'
|
'appearCancelled'
|
||||||
]
|
]
|
||||||
|
<% } %>
|
||||||
|
@ -9,7 +9,7 @@ import NuxtError from '<%= "../" + components.ErrorPage %>'
|
|||||||
<% } %>
|
<% } %>
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
import NuxtError from './nuxt-error.vue'
|
import NuxtError from './nuxt-error.vue'
|
||||||
<% } %>
|
<% } /* components */ %>
|
||||||
import NuxtChild from './nuxt-child'
|
import NuxtChild from './nuxt-child'
|
||||||
|
|
||||||
<%= isTest ? '// @vue/component' : '' %>
|
<%= isTest ? '// @vue/component' : '' %>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Meta from 'vue-meta'
|
<% if (features.meta) { %>import Meta from 'vue-meta'<% } %>
|
||||||
import ClientOnly from 'vue-client-only'
|
<% if (features.componentClientOnly) { %>import ClientOnly from 'vue-client-only'<% } %>
|
||||||
import NoSsr from 'vue-no-ssr'
|
<% if (features.deprecations) { %>import NoSsr from 'vue-no-ssr'<% } %>
|
||||||
import { createRouter } from './router.js'
|
import { createRouter } from './router.js'
|
||||||
import NuxtChild from './components/nuxt-child.js'
|
import NuxtChild from './components/nuxt-child.js'
|
||||||
import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./components/nuxt-error.vue" %>'
|
import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./components/nuxt-error.vue" %>'
|
||||||
@ -16,8 +16,11 @@ import { setContext, getLocation, getRouteData, normalizeError } from './utils'
|
|||||||
<% }) %>
|
<% }) %>
|
||||||
<%= isTest ? '/* eslint-enable camelcase */' : '' %>
|
<%= isTest ? '/* eslint-enable camelcase */' : '' %>
|
||||||
|
|
||||||
|
<% if (features.componentClientOnly) { %>
|
||||||
// Component: <ClientOnly>
|
// Component: <ClientOnly>
|
||||||
Vue.component(ClientOnly.name, ClientOnly)
|
Vue.component(ClientOnly.name, ClientOnly)
|
||||||
|
<% } %>
|
||||||
|
<% if (features.deprecations) { %>
|
||||||
// TODO: Remove in Nuxt 3: <NoSsr>
|
// TODO: Remove in Nuxt 3: <NoSsr>
|
||||||
Vue.component(NoSsr.name, {
|
Vue.component(NoSsr.name, {
|
||||||
...NoSsr,
|
...NoSsr,
|
||||||
@ -29,16 +32,17 @@ Vue.component(NoSsr.name, {
|
|||||||
return NoSsr.render(h, ctx)
|
return NoSsr.render(h, ctx)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
<% } %>
|
||||||
// Component: <NuxtChild>
|
// Component: <NuxtChild>
|
||||||
Vue.component(NuxtChild.name, NuxtChild)
|
Vue.component(NuxtChild.name, NuxtChild)
|
||||||
Vue.component('NChild', NuxtChild)
|
<% if (features.componentAliases) { %>Vue.component('NChild', NuxtChild)<% } %>
|
||||||
|
|
||||||
// Component NuxtLink is imported in server.js or client.js
|
// Component NuxtLink is imported in server.js or client.js
|
||||||
|
|
||||||
// Component: <Nuxt>`
|
// Component: <Nuxt>`
|
||||||
Vue.component(Nuxt.name, Nuxt)
|
Vue.component(Nuxt.name, Nuxt)
|
||||||
|
|
||||||
|
<% if (features.meta) { %>
|
||||||
// vue-meta configuration
|
// vue-meta configuration
|
||||||
Vue.use(Meta, {
|
Vue.use(Meta, {
|
||||||
keyName: 'head', // the component option name that vue-meta looks for meta info on.
|
keyName: 'head', // the component option name that vue-meta looks for meta info on.
|
||||||
@ -46,7 +50,9 @@ Vue.use(Meta, {
|
|||||||
ssrAttribute: 'data-n-head-ssr', // the attribute name that lets vue-meta know that meta info has already been server-rendered
|
ssrAttribute: 'data-n-head-ssr', // the attribute name that lets vue-meta know that meta info has already been server-rendered
|
||||||
tagIDKeyName: 'hid' // the property name that vue-meta uses to determine whether to overwrite or append a tag
|
tagIDKeyName: 'hid' // the property name that vue-meta uses to determine whether to overwrite or append a tag
|
||||||
})
|
})
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.transitions) { %>
|
||||||
const defaultTransition = <%=
|
const defaultTransition = <%=
|
||||||
serialize(pageTransition)
|
serialize(pageTransition)
|
||||||
.replace('beforeEnter(', 'function(').replace('enter(', 'function(').replace('afterEnter(', 'function(')
|
.replace('beforeEnter(', 'function(').replace('enter(', 'function(').replace('afterEnter(', 'function(')
|
||||||
@ -54,6 +60,7 @@ const defaultTransition = <%=
|
|||||||
.replace('afterLeave(', 'function(').replace('leaveCancelled(', 'function(').replace('beforeAppear(', 'function(')
|
.replace('afterLeave(', 'function(').replace('leaveCancelled(', 'function(').replace('beforeAppear(', 'function(')
|
||||||
.replace('appear(', 'function(').replace('afterAppear(', 'function(').replace('appearCancelled(', 'function(')
|
.replace('appear(', 'function(').replace('afterAppear(', 'function(').replace('appearCancelled(', 'function(')
|
||||||
%><%= isTest ? '// eslint-disable-line' : '' %>
|
%><%= isTest ? '// eslint-disable-line' : '' %>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
async function createApp(ssrContext) {
|
async function createApp(ssrContext) {
|
||||||
const router = await createRouter(ssrContext)
|
const router = await createRouter(ssrContext)
|
||||||
@ -74,9 +81,10 @@ async function createApp(ssrContext) {
|
|||||||
// here we inject the router and store to all child components,
|
// here we inject the router and store to all child components,
|
||||||
// making them available everywhere as `this.$router` and `this.$store`.
|
// making them available everywhere as `this.$router` and `this.$store`.
|
||||||
const app = {
|
const app = {
|
||||||
router,
|
|
||||||
<% if (store) { %>store,<% } %>
|
<% if (store) { %>store,<% } %>
|
||||||
|
router,
|
||||||
nuxt: {
|
nuxt: {
|
||||||
|
<% if (features.transitions) { %>
|
||||||
defaultTransition,
|
defaultTransition,
|
||||||
transitions: [ defaultTransition ],
|
transitions: [ defaultTransition ],
|
||||||
setTransitions(transitions) {
|
setTransitions(transitions) {
|
||||||
@ -96,6 +104,7 @@ async function createApp(ssrContext) {
|
|||||||
this.$options.nuxt.transitions = transitions
|
this.$options.nuxt.transitions = transitions
|
||||||
return transitions
|
return transitions
|
||||||
},
|
},
|
||||||
|
<% } %>
|
||||||
err: null,
|
err: null,
|
||||||
dateErr: null,
|
dateErr: null,
|
||||||
error(err) {
|
error(err) {
|
||||||
@ -128,10 +137,10 @@ async function createApp(ssrContext) {
|
|||||||
|
|
||||||
// Set context to app.context
|
// Set context to app.context
|
||||||
await setContext(app, {
|
await setContext(app, {
|
||||||
|
<% if (store) { %>store,<% } %>
|
||||||
route,
|
route,
|
||||||
next,
|
next,
|
||||||
error: app.nuxt.error.bind(app),
|
error: app.nuxt.error.bind(app),
|
||||||
<% if (store) { %>store,<% } %>
|
|
||||||
payload: ssrContext ? ssrContext.payload : undefined,
|
payload: ssrContext ? ssrContext.payload : undefined,
|
||||||
req: ssrContext ? ssrContext.req : undefined,
|
req: ssrContext ? ssrContext.req : undefined,
|
||||||
res: ssrContext ? ssrContext.res : undefined,
|
res: ssrContext ? ssrContext.res : undefined,
|
||||||
@ -213,8 +222,8 @@ async function createApp(ssrContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
app,
|
|
||||||
<% if(store) { %>store,<% } %>
|
<% if(store) { %>store,<% } %>
|
||||||
|
app,
|
||||||
router
|
router
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,29 @@
|
|||||||
import { stringify } from 'querystring'
|
import { stringify } from 'querystring'
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
<% if (fetch.server) { %>import fetch from 'node-fetch'<% } %>
|
<% if (fetch.server) { %>import fetch from 'node-fetch'<% } %>
|
||||||
import middleware from './middleware.js'
|
<% if (features.middleware) { %>import middleware from './middleware.js'<% } %>
|
||||||
import { applyAsyncData, getMatchedComponents, middlewareSeries, promisify, urlJoin, sanitizeComponent } from './utils.js'
|
import {
|
||||||
|
<% if (features.asyncData) { %>applyAsyncData,<% } %>
|
||||||
|
<% if (features.middleware) { %>middlewareSeries,<% } %>
|
||||||
|
getMatchedComponents,
|
||||||
|
promisify,
|
||||||
|
sanitizeComponent
|
||||||
|
} from './utils.js'
|
||||||
import { createApp, NuxtError } from './index.js'
|
import { createApp, NuxtError } from './index.js'
|
||||||
import NuxtLink from './components/nuxt-link.server.js' // should be included after ./index.js
|
import NuxtLink from './components/nuxt-link.server.js' // should be included after ./index.js
|
||||||
|
|
||||||
// Component: <NuxtLink>
|
// Component: <NuxtLink>
|
||||||
Vue.component(NuxtLink.name, NuxtLink)
|
Vue.component(NuxtLink.name, NuxtLink)
|
||||||
Vue.component('NLink', NuxtLink)
|
<% if (features.componentAliases) { %>Vue.component('NLink', NuxtLink)<% } %>
|
||||||
|
|
||||||
<% if (fetch.server) { %>if (!global.fetch) { global.fetch = fetch }<% } %>
|
<% if (fetch.server) { %>if (!global.fetch) { global.fetch = fetch }<% } %>
|
||||||
|
|
||||||
const noopApp = () => new Vue({ render: h => h('div') })
|
const noopApp = () => new Vue({ render: h => h('div') })
|
||||||
|
|
||||||
|
function urlJoin() {
|
||||||
|
return Array.prototype.slice.call(arguments).join('/').replace(/\/+/g, '/')
|
||||||
|
}
|
||||||
|
|
||||||
const createNext = ssrContext => (opts) => {
|
const createNext = ssrContext => (opts) => {
|
||||||
ssrContext.redirected = opts
|
ssrContext.redirected = opts
|
||||||
// If nuxt generate
|
// If nuxt generate
|
||||||
@ -50,32 +60,39 @@ export default async (ssrContext) => {
|
|||||||
// Used for beforeNuxtRender({ Components, nuxtState })
|
// Used for beforeNuxtRender({ Components, nuxtState })
|
||||||
ssrContext.beforeRenderFns = []
|
ssrContext.beforeRenderFns = []
|
||||||
// Nuxt object (window{{globals.context}}, defaults to window.__NUXT__)
|
// Nuxt object (window{{globals.context}}, defaults to window.__NUXT__)
|
||||||
ssrContext.nuxt = { layout: 'default', data: [], error: null<%= (store ? ', state: null' : '') %>, serverRendered: true }
|
ssrContext.nuxt = { <% if (features.layouts) { %>layout: 'default', <% } %>data: [], error: null<%= (store ? ', state: null' : '') %>, serverRendered: true }
|
||||||
// Create the app definition and the instance (created for each request)
|
// Create the app definition and the instance (created for each request)
|
||||||
const { app, router<%= (store ? ', store' : '') %> } = await createApp(ssrContext)
|
const { app, router<%= (store ? ', store' : '') %> } = await createApp(ssrContext)
|
||||||
const _app = new Vue(app)
|
const _app = new Vue(app)
|
||||||
|
|
||||||
|
<% if (features.meta) { %>
|
||||||
// Add meta infos (used in renderer.js)
|
// Add meta infos (used in renderer.js)
|
||||||
ssrContext.meta = _app.$meta()
|
ssrContext.meta = _app.$meta()
|
||||||
|
<% } %>
|
||||||
|
<% if (features.asyncData) { %>
|
||||||
// Keep asyncData for each matched component in ssrContext (used in app/utils.js via this.$ssrContext)
|
// Keep asyncData for each matched component in ssrContext (used in app/utils.js via this.$ssrContext)
|
||||||
ssrContext.asyncData = {}
|
ssrContext.asyncData = {}
|
||||||
|
<% } %>
|
||||||
|
|
||||||
const beforeRender = async () => {
|
const beforeRender = async () => {
|
||||||
// Call beforeNuxtRender() methods
|
// Call beforeNuxtRender() methods
|
||||||
await Promise.all(ssrContext.beforeRenderFns.map(fn => promisify(fn, { Components, nuxtState: ssrContext.nuxt })))
|
await Promise.all(ssrContext.beforeRenderFns.map(fn => promisify(fn, { Components, nuxtState: ssrContext.nuxt })))
|
||||||
ssrContext.rendered = () => {
|
|
||||||
<% if (store) { %>
|
<% if (store) { %>
|
||||||
|
ssrContext.rendered = () => {
|
||||||
// Add the state from the vuex store
|
// Add the state from the vuex store
|
||||||
ssrContext.nuxt.state = store.state
|
ssrContext.nuxt.state = store.state
|
||||||
|
}
|
||||||
<% } %>
|
<% } %>
|
||||||
}
|
}
|
||||||
}
|
|
||||||
const renderErrorPage = async () => {
|
const renderErrorPage = async () => {
|
||||||
|
<% if (features.layouts) { %>
|
||||||
// Load layout for error page
|
// Load layout for error page
|
||||||
const errLayout = (typeof NuxtError.layout === 'function' ? NuxtError.layout(app.context) : NuxtError.layout)
|
const errLayout = (typeof NuxtError.layout === 'function' ? NuxtError.layout(app.context) : NuxtError.layout)
|
||||||
ssrContext.nuxt.layout = errLayout || 'default'
|
ssrContext.nuxt.layout = errLayout || 'default'
|
||||||
await _app.loadLayout(errLayout)
|
await _app.loadLayout(errLayout)
|
||||||
_app.setLayout(errLayout)
|
_app.setLayout(errLayout)
|
||||||
|
<% } %>
|
||||||
await beforeRender()
|
await beforeRender()
|
||||||
return _app
|
return _app
|
||||||
}
|
}
|
||||||
@ -106,6 +123,7 @@ export default async (ssrContext) => {
|
|||||||
if (ssrContext.nuxt.error) return renderErrorPage()
|
if (ssrContext.nuxt.error) return renderErrorPage()
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.middleware) { %>
|
||||||
/*
|
/*
|
||||||
** Call global middleware (nuxt.config.js)
|
** Call global middleware (nuxt.config.js)
|
||||||
*/
|
*/
|
||||||
@ -121,7 +139,9 @@ export default async (ssrContext) => {
|
|||||||
// ...If there is a redirect or an error, stop the process
|
// ...If there is a redirect or an error, stop the process
|
||||||
if (ssrContext.redirected) return noopApp()
|
if (ssrContext.redirected) return noopApp()
|
||||||
if (ssrContext.nuxt.error) return renderErrorPage()
|
if (ssrContext.nuxt.error) return renderErrorPage()
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.layouts) { %>
|
||||||
/*
|
/*
|
||||||
** Set layout
|
** Set layout
|
||||||
*/
|
*/
|
||||||
@ -131,13 +151,19 @@ export default async (ssrContext) => {
|
|||||||
if (ssrContext.nuxt.error) return renderErrorPage()
|
if (ssrContext.nuxt.error) return renderErrorPage()
|
||||||
layout = _app.setLayout(layout)
|
layout = _app.setLayout(layout)
|
||||||
ssrContext.nuxt.layout = _app.layoutName
|
ssrContext.nuxt.layout = _app.layoutName
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.middleware) { %>
|
||||||
/*
|
/*
|
||||||
** Call middleware (layout + pages)
|
** Call middleware (layout + pages)
|
||||||
*/
|
*/
|
||||||
midd = []
|
midd = []
|
||||||
|
<% if (features.layouts) { %>
|
||||||
layout = sanitizeComponent(layout)
|
layout = sanitizeComponent(layout)
|
||||||
if (layout.options.middleware) midd = midd.concat(layout.options.middleware)
|
if (layout.options.middleware) {
|
||||||
|
midd = midd.concat(layout.options.middleware)
|
||||||
|
}
|
||||||
|
<% } %>
|
||||||
Components.forEach((Component) => {
|
Components.forEach((Component) => {
|
||||||
if (Component.options.middleware) {
|
if (Component.options.middleware) {
|
||||||
midd = midd.concat(Component.options.middleware)
|
midd = midd.concat(Component.options.middleware)
|
||||||
@ -154,7 +180,9 @@ export default async (ssrContext) => {
|
|||||||
// ...If there is a redirect or an error, stop the process
|
// ...If there is a redirect or an error, stop the process
|
||||||
if (ssrContext.redirected) return noopApp()
|
if (ssrContext.redirected) return noopApp()
|
||||||
if (ssrContext.nuxt.error) return renderErrorPage()
|
if (ssrContext.nuxt.error) return renderErrorPage()
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.validate) { %>
|
||||||
/*
|
/*
|
||||||
** Call .validate()
|
** Call .validate()
|
||||||
*/
|
*/
|
||||||
@ -187,14 +215,17 @@ export default async (ssrContext) => {
|
|||||||
// Render a 404 error page
|
// Render a 404 error page
|
||||||
return render404Page()
|
return render404Page()
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
|
|
||||||
// If no Components found, returns 404
|
// If no Components found, returns 404
|
||||||
if (!Components.length) return render404Page()
|
if (!Components.length) return render404Page()
|
||||||
|
|
||||||
|
<% if (features.asyncData || features.fetch) { %>
|
||||||
// Call asyncData & fetch hooks on components matched by the route.
|
// Call asyncData & fetch hooks on components matched by the route.
|
||||||
const asyncDatas = await Promise.all(Components.map((Component) => {
|
const asyncDatas = await Promise.all(Components.map((Component) => {
|
||||||
const promises = []
|
const promises = []
|
||||||
|
|
||||||
|
<% if (features.asyncData) { %>
|
||||||
// Call asyncData(context)
|
// Call asyncData(context)
|
||||||
if (Component.options.asyncData && typeof Component.options.asyncData === 'function') {
|
if (Component.options.asyncData && typeof Component.options.asyncData === 'function') {
|
||||||
const promise = promisify(Component.options.asyncData, app.context)
|
const promise = promisify(Component.options.asyncData, app.context)
|
||||||
@ -207,13 +238,16 @@ export default async (ssrContext) => {
|
|||||||
} else {
|
} else {
|
||||||
promises.push(null)
|
promises.push(null)
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (features.fetch) { %>
|
||||||
// Call fetch(context)
|
// Call fetch(context)
|
||||||
if (Component.options.fetch) {
|
if (Component.options.fetch) {
|
||||||
promises.push(Component.options.fetch(app.context))
|
promises.push(Component.options.fetch(app.context))
|
||||||
} else {
|
} else {
|
||||||
promises.push(null)
|
promises.push(null)
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
|
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
}))
|
}))
|
||||||
@ -222,6 +256,7 @@ export default async (ssrContext) => {
|
|||||||
|
|
||||||
// datas are the first row of each
|
// datas are the first row of each
|
||||||
ssrContext.nuxt.data = asyncDatas.map(r => r[0] || {})
|
ssrContext.nuxt.data = asyncDatas.map(r => r[0] || {})
|
||||||
|
<% } %>
|
||||||
|
|
||||||
// ...If there is a redirect or an error, stop the process
|
// ...If there is a redirect or an error, stop the process
|
||||||
if (ssrContext.redirected) return noopApp()
|
if (ssrContext.redirected) return noopApp()
|
||||||
|
@ -21,6 +21,7 @@ export function interopDefault(promise) {
|
|||||||
return promise.then(m => m.default || m)
|
return promise.then(m => m.default || m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<% if (features.asyncData) { %>
|
||||||
export function applyAsyncData(Component, asyncData) {
|
export function applyAsyncData(Component, asyncData) {
|
||||||
if (
|
if (
|
||||||
// For SSR, we once all this function without second param to just apply asyncData
|
// For SSR, we once all this function without second param to just apply asyncData
|
||||||
@ -47,6 +48,7 @@ export function applyAsyncData(Component, asyncData) {
|
|||||||
Component._Ctor.options.data = Component.options.data
|
Component._Ctor.options.data = Component.options.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
|
|
||||||
export function sanitizeComponent(Component) {
|
export function sanitizeComponent(Component) {
|
||||||
// If Component already sanitized
|
// If Component already sanitized
|
||||||
@ -67,22 +69,17 @@ export function sanitizeComponent(Component) {
|
|||||||
return Component
|
return Component
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMatchedComponents(route, matches = false) {
|
export function getMatchedComponents(route, matches = false, prop = 'components') {
|
||||||
return Array.prototype.concat.apply([], route.matched.map((m, index) => {
|
return Array.prototype.concat.apply([], route.matched.map((m, index) => {
|
||||||
return Object.keys(m.components).map((key) => {
|
return Object.keys(m[prop]).map((key) => {
|
||||||
matches && matches.push(index)
|
matches && matches.push(index)
|
||||||
return m.components[key]
|
return m[prop][key]
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMatchedComponentsInstances(route, matches = false) {
|
export function getMatchedComponentsInstances(route, matches = false) {
|
||||||
return Array.prototype.concat.apply([], route.matched.map((m, index) => {
|
return getMatchedComponents(route, matches, 'instances')
|
||||||
return Object.keys(m.instances).map((key) => {
|
|
||||||
matches && matches.push(index)
|
|
||||||
return m.instances[key]
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function flatMapComponents(route, fn) {
|
export function flatMapComponents(route, fn) {
|
||||||
@ -215,11 +212,11 @@ export async function setContext(app, context) {
|
|||||||
app.context.next = context.next
|
app.context.next = context.next
|
||||||
app.context._redirected = false
|
app.context._redirected = false
|
||||||
app.context._errored = false
|
app.context._errored = false
|
||||||
app.context.isHMR = Boolean(context.isHMR)
|
app.context.isHMR = <% if(isDev) { %>Boolean(context.isHMR)<% } else { %>false<% } %>
|
||||||
app.context.params = app.context.route.params || {}
|
app.context.params = app.context.route.params || {}
|
||||||
app.context.query = app.context.route.query || {}
|
app.context.query = app.context.route.query || {}
|
||||||
}
|
}
|
||||||
|
<% if (features.middleware) { %>
|
||||||
export function middlewareSeries(promises, appContext) {
|
export function middlewareSeries(promises, appContext) {
|
||||||
if (!promises.length || appContext._redirected || appContext._errored) {
|
if (!promises.length || appContext._redirected || appContext._errored) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
@ -229,8 +226,9 @@ export function middlewareSeries(promises, appContext) {
|
|||||||
return middlewareSeries(promises.slice(1), appContext)
|
return middlewareSeries(promises.slice(1), appContext)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
export function promisify(fn, context) {
|
export function promisify(fn, context) {
|
||||||
|
<% if (features.deprecations) { %>
|
||||||
let promise
|
let promise
|
||||||
if (fn.length === 2) {
|
if (fn.length === 2) {
|
||||||
<% if (isDev) { %>
|
<% if (isDev) { %>
|
||||||
@ -251,11 +249,14 @@ export function promisify(fn, context) {
|
|||||||
} else {
|
} else {
|
||||||
promise = fn(context)
|
promise = fn(context)
|
||||||
}
|
}
|
||||||
if (!promise || (!(promise instanceof Promise) && (typeof promise.then !== 'function'))) {
|
<% } else { %>
|
||||||
promise = Promise.resolve(promise)
|
const promise = fn(context)
|
||||||
}
|
<% } %>
|
||||||
|
if (promise && promise instanceof Promise && typeof promise.then === 'function') {
|
||||||
return promise
|
return promise
|
||||||
}
|
}
|
||||||
|
return Promise.resolve(promise)
|
||||||
|
}
|
||||||
|
|
||||||
// Imported from vue-router
|
// Imported from vue-router
|
||||||
export function getLocation(base, mode) {
|
export function getLocation(base, mode) {
|
||||||
@ -269,10 +270,6 @@ export function getLocation(base, mode) {
|
|||||||
return (path || '/') + window.location.search + window.location.hash
|
return (path || '/') + window.location.search + window.location.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
export function urlJoin() {
|
|
||||||
return Array.prototype.slice.call(arguments).join('/').replace(/\/+/g, '/')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Imported from path-to-regexp
|
// Imported from path-to-regexp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -412,8 +409,9 @@ function parse(str, options) {
|
|||||||
* @param {string}
|
* @param {string}
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function encodeURIComponentPretty(str) {
|
function encodeURIComponentPretty(str, slashAllowed) {
|
||||||
return encodeURI(str).replace(/[/?#]/g, (c) => {
|
const re = slashAllowed ? /[?#]/g : /[/?#]/g
|
||||||
|
return encodeURI(str).replace(re, (c) => {
|
||||||
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
|
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -425,9 +423,27 @@ function encodeURIComponentPretty(str) {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function encodeAsterisk(str) {
|
function encodeAsterisk(str) {
|
||||||
return encodeURI(str).replace(/[?#]/g, (c) => {
|
return encodeURIComponentPretty(str, true)
|
||||||
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
|
}
|
||||||
})
|
|
||||||
|
/**
|
||||||
|
* Escape a regular expression string.
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
function escapeString(str) {
|
||||||
|
return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape the capturing group by escaping special characters and meaning.
|
||||||
|
*
|
||||||
|
* @param {string} group
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
function escapeGroup(group) {
|
||||||
|
return group.replace(/([=!:$/()])/g, '\\$1')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -514,26 +530,6 @@ function tokensToFunction(tokens) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Escape a regular expression string.
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
function escapeString(str) {
|
|
||||||
return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Escape the capturing group by escaping special characters and meaning.
|
|
||||||
*
|
|
||||||
* @param {string} group
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
function escapeGroup(group) {
|
|
||||||
return group.replace(/([=!:$/()])/g, '\\$1')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format given url, append query to url query string
|
* Format given url, append query to url query string
|
||||||
*
|
*
|
||||||
@ -542,6 +538,24 @@ function escapeGroup(group) {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function formatUrl(url, query) {
|
function formatUrl(url, query) {
|
||||||
|
<% if (features.clientUseUrl) { %>
|
||||||
|
url = new URL(url, top.location.href)
|
||||||
|
for (const key in query) {
|
||||||
|
const value = query[key]
|
||||||
|
if (value == null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
for (const arrayValue of value) {
|
||||||
|
url.searchParams.append(key, arrayValue)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
url.searchParams.append(key, value)
|
||||||
|
}
|
||||||
|
url.searchParams.sort()
|
||||||
|
return url.toString()
|
||||||
|
<% } else { %>
|
||||||
let protocol
|
let protocol
|
||||||
const index = url.indexOf('://')
|
const index = url.indexOf('://')
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
@ -569,8 +583,9 @@ function formatUrl(url, query) {
|
|||||||
result += hash ? '#' + hash : ''
|
result += hash ? '#' + hash : ''
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
<% } %>
|
||||||
}
|
}
|
||||||
|
<% if (!features.clientUseUrl) { %>
|
||||||
/**
|
/**
|
||||||
* Transform data object to query string
|
* Transform data object to query string
|
||||||
*
|
*
|
||||||
@ -589,3 +604,4 @@ function formatQuery(query) {
|
|||||||
return key + '=' + val
|
return key + '=' + val
|
||||||
}).filter(Boolean).join('&')
|
}).filter(Boolean).join('&')
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
|
@ -47,6 +47,7 @@ export default class SPARenderer extends BaseRenderer {
|
|||||||
BODY_SCRIPTS: ''
|
BODY_SCRIPTS: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.options.features.meta) {
|
||||||
// Get vue-meta context
|
// Get vue-meta context
|
||||||
let head
|
let head
|
||||||
if (typeof this.options.head === 'function') {
|
if (typeof this.options.head === 'function') {
|
||||||
@ -90,6 +91,7 @@ export default class SPARenderer extends BaseRenderer {
|
|||||||
m.style.text({ body: true }) +
|
m.style.text({ body: true }) +
|
||||||
m.script.text({ body: true }) +
|
m.script.text({ body: true }) +
|
||||||
m.noscript.text({ body: true })
|
m.noscript.text({ body: true })
|
||||||
|
}
|
||||||
|
|
||||||
// Resources Hints
|
// Resources Hints
|
||||||
meta.resourceHints = ''
|
meta.resourceHints = ''
|
||||||
|
@ -83,15 +83,19 @@ export default class SSRRenderer extends BaseRenderer {
|
|||||||
APP = `<div id="${this.serverContext.globals.id}"></div>`
|
APP = `<div id="${this.serverContext.globals.id}"></div>`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let HEAD = ''
|
||||||
|
|
||||||
// Inject head meta
|
// Inject head meta
|
||||||
const m = renderContext.meta.inject()
|
// (this is unset when features.meta is false in server template)
|
||||||
let HEAD =
|
const meta = renderContext.meta && renderContext.meta.inject()
|
||||||
m.title.text() +
|
if (meta) {
|
||||||
m.meta.text() +
|
HEAD += meta.title.text() +
|
||||||
m.link.text() +
|
meta.meta.text() +
|
||||||
m.style.text() +
|
meta.link.text() +
|
||||||
m.script.text() +
|
meta.style.text() +
|
||||||
m.noscript.text()
|
meta.script.text() +
|
||||||
|
meta.noscript.text()
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we need to inject scripts and state
|
// Check if we need to inject scripts and state
|
||||||
const shouldInjectScripts = this.options.render.injectScripts !== false
|
const shouldInjectScripts = this.options.render.injectScripts !== false
|
||||||
@ -109,16 +113,18 @@ export default class SSRRenderer extends BaseRenderer {
|
|||||||
// Inject styles
|
// Inject styles
|
||||||
HEAD += renderContext.renderStyles()
|
HEAD += renderContext.renderStyles()
|
||||||
|
|
||||||
|
if (meta) {
|
||||||
const BODY_PREPEND =
|
const BODY_PREPEND =
|
||||||
m.meta.text({ pbody: true }) +
|
meta.meta.text({ pbody: true }) +
|
||||||
m.link.text({ pbody: true }) +
|
meta.link.text({ pbody: true }) +
|
||||||
m.style.text({ pbody: true }) +
|
meta.style.text({ pbody: true }) +
|
||||||
m.script.text({ pbody: true }) +
|
meta.script.text({ pbody: true }) +
|
||||||
m.noscript.text({ pbody: true })
|
meta.noscript.text({ pbody: true })
|
||||||
|
|
||||||
if (BODY_PREPEND) {
|
if (BODY_PREPEND) {
|
||||||
APP = `${BODY_PREPEND}${APP}`
|
APP = `${BODY_PREPEND}${APP}`
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Serialize state
|
// Serialize state
|
||||||
const serializedSession = `window.${this.serverContext.globals.context}=${devalue(renderContext.nuxt)};`
|
const serializedSession = `window.${this.serverContext.globals.context}=${devalue(renderContext.nuxt)};`
|
||||||
@ -152,18 +158,20 @@ export default class SSRRenderer extends BaseRenderer {
|
|||||||
APP += this.renderScripts(renderContext)
|
APP += this.renderScripts(renderContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (meta) {
|
||||||
// Append body scripts
|
// Append body scripts
|
||||||
APP += m.meta.text({ body: true })
|
APP += meta.meta.text({ body: true })
|
||||||
APP += m.link.text({ body: true })
|
APP += meta.link.text({ body: true })
|
||||||
APP += m.style.text({ body: true })
|
APP += meta.style.text({ body: true })
|
||||||
APP += m.script.text({ body: true })
|
APP += meta.script.text({ body: true })
|
||||||
APP += m.noscript.text({ body: true })
|
APP += meta.noscript.text({ body: true })
|
||||||
|
}
|
||||||
|
|
||||||
// Template params
|
// Template params
|
||||||
const templateParams = {
|
const templateParams = {
|
||||||
HTML_ATTRS: m.htmlAttrs.text(true /* addSrrAttribute */),
|
HTML_ATTRS: meta ? meta.htmlAttrs.text(true /* addSrrAttribute */) : '',
|
||||||
HEAD_ATTRS: m.headAttrs.text(),
|
HEAD_ATTRS: meta ? meta.headAttrs.text() : '',
|
||||||
BODY_ATTRS: m.bodyAttrs.text(),
|
BODY_ATTRS: meta ? meta.bodyAttrs.text() : '',
|
||||||
HEAD,
|
HEAD,
|
||||||
APP,
|
APP,
|
||||||
ENV: this.options.env
|
ENV: this.options.env
|
||||||
|
38
test/fixtures/unicode-base/nuxt.config.js
vendored
38
test/fixtures/unicode-base/nuxt.config.js
vendored
@ -1,5 +1,43 @@
|
|||||||
export default {
|
export default {
|
||||||
|
modern: 'server',
|
||||||
router: {
|
router: {
|
||||||
base: '/%C3%B6/'
|
base: '/%C3%B6/'
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
loadingIndicator: false,
|
||||||
|
fetch: {
|
||||||
|
client: false,
|
||||||
|
server: false
|
||||||
|
},
|
||||||
|
features: {
|
||||||
|
store: false,
|
||||||
|
layouts: false,
|
||||||
|
meta: false,
|
||||||
|
middleware: false,
|
||||||
|
transitions: false,
|
||||||
|
deprecations: false,
|
||||||
|
validate: false,
|
||||||
|
asyncData: false,
|
||||||
|
fetch: false,
|
||||||
|
clientOnline: false,
|
||||||
|
clientPrefetch: false,
|
||||||
|
clientUseUrl: true,
|
||||||
|
componentAliases: false,
|
||||||
|
componentClientOnly: false
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
indicator: false,
|
||||||
|
terser: true,
|
||||||
|
optimization: {
|
||||||
|
splitChunks: {
|
||||||
|
cacheGroups: {
|
||||||
|
nuxtApp: {
|
||||||
|
test: /[\\/]\.nuxt[\\/]/,
|
||||||
|
filename: 'vue-app.nuxt.js',
|
||||||
|
enforce: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,8 @@
|
|||||||
|
|
||||||
import { resolve } from 'path'
|
import { resolve } from 'path'
|
||||||
import zlib from 'zlib'
|
import { getResourcesSize } from '../utils'
|
||||||
import fs from 'fs-extra'
|
|
||||||
import pify from 'pify'
|
|
||||||
|
|
||||||
const gzip = pify(zlib.gzip)
|
|
||||||
const brotli = pify(zlib.brotliCompress)
|
|
||||||
const compressSize = (input, compressor) => compressor(input).then(data => data.length)
|
|
||||||
|
|
||||||
const distDir = resolve(__dirname, '../fixtures/async-config/.nuxt/dist')
|
const distDir = resolve(__dirname, '../fixtures/async-config/.nuxt/dist')
|
||||||
|
|
||||||
const getResourcesSize = async (mode) => {
|
|
||||||
const { all } = await import(resolve(distDir, 'server', `${mode}.manifest.json`))
|
|
||||||
const resources = all.filter(filename => filename.endsWith('.js'))
|
|
||||||
const sizes = { uncompressed: 0, gzip: 0, brotli: 0 }
|
|
||||||
for (const resource of resources) {
|
|
||||||
const file = resolve(distDir, 'client', resource)
|
|
||||||
const stat = await fs.stat(file)
|
|
||||||
sizes.uncompressed += stat.size / 1024
|
|
||||||
const fileContent = await fs.readFile(file)
|
|
||||||
sizes.gzip += await compressSize(fileContent, gzip) / 1024
|
|
||||||
sizes.brotli += await compressSize(fileContent, brotli) / 1024
|
|
||||||
}
|
|
||||||
return sizes
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('nuxt basic resources size limit', () => {
|
describe('nuxt basic resources size limit', () => {
|
||||||
expect.extend({
|
expect.extend({
|
||||||
toBeWithinSize (received, size) {
|
toBeWithinSize (received, size) {
|
||||||
@ -40,7 +18,7 @@ describe('nuxt basic resources size limit', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should stay within the size limit range in legacy mode', async () => {
|
it('should stay within the size limit range in legacy mode', async () => {
|
||||||
const legacyResourcesSize = await getResourcesSize('client')
|
const legacyResourcesSize = await getResourcesSize(distDir, 'client', { gzip: true, brotli: true })
|
||||||
|
|
||||||
const LEGACY_JS_RESOURCES_KB_SIZE = 194
|
const LEGACY_JS_RESOURCES_KB_SIZE = 194
|
||||||
expect(legacyResourcesSize.uncompressed).toBeWithinSize(LEGACY_JS_RESOURCES_KB_SIZE)
|
expect(legacyResourcesSize.uncompressed).toBeWithinSize(LEGACY_JS_RESOURCES_KB_SIZE)
|
||||||
@ -53,7 +31,7 @@ describe('nuxt basic resources size limit', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should stay within the size limit range in modern mode', async () => {
|
it('should stay within the size limit range in modern mode', async () => {
|
||||||
const modernResourcesSize = await getResourcesSize('modern')
|
const modernResourcesSize = await getResourcesSize(distDir, 'modern', { gzip: true, brotli: true })
|
||||||
|
|
||||||
const MODERN_JS_RESOURCES_KB_SIZE = 172
|
const MODERN_JS_RESOURCES_KB_SIZE = 172
|
||||||
expect(modernResourcesSize.uncompressed).toBeWithinSize(MODERN_JS_RESOURCES_KB_SIZE)
|
expect(modernResourcesSize.uncompressed).toBeWithinSize(MODERN_JS_RESOURCES_KB_SIZE)
|
||||||
|
27
test/unit/unicode-base.size-limit.test.js
Normal file
27
test/unit/unicode-base.size-limit.test.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { resolve } from 'path'
|
||||||
|
import { getResourcesSize } from '../utils'
|
||||||
|
|
||||||
|
const distDir = resolve(__dirname, '../fixtures/unicode-base/.nuxt/dist')
|
||||||
|
|
||||||
|
describe('nuxt minimal vue-app bundle size limit', () => {
|
||||||
|
expect.extend({
|
||||||
|
toBeWithinSize (received, size) {
|
||||||
|
const maxSize = size * 1.02
|
||||||
|
const minSize = size * 0.98
|
||||||
|
const pass = received >= minSize && received <= maxSize
|
||||||
|
return {
|
||||||
|
pass,
|
||||||
|
message: () =>
|
||||||
|
`expected ${received} to be within range ${minSize} - ${maxSize}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should stay within the size limit range', async () => {
|
||||||
|
const filter = filename => filename === 'vue-app.nuxt.js'
|
||||||
|
const legacyResourcesSize = await getResourcesSize(distDir, 'client', { filter })
|
||||||
|
|
||||||
|
const LEGACY_JS_RESOURCES_KB_SIZE = 15.8
|
||||||
|
expect(legacyResourcesSize.uncompressed).toBeWithinSize(LEGACY_JS_RESOURCES_KB_SIZE)
|
||||||
|
})
|
||||||
|
})
|
@ -1,7 +1,7 @@
|
|||||||
import { getPort, loadFixture, Nuxt } from '../utils'
|
import { getPort, loadFixture, Nuxt, rp } from '../utils'
|
||||||
|
|
||||||
let port
|
let port
|
||||||
const url = route => 'http://localhost:' + port + route
|
const url = route => 'http://localhost:' + port + encodeURI(route)
|
||||||
|
|
||||||
let nuxt = null
|
let nuxt = null
|
||||||
|
|
||||||
@ -16,10 +16,9 @@ describe('unicode-base', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('/ö/ (router base)', async () => {
|
test('/ö/ (router base)', async () => {
|
||||||
const window = await nuxt.server.renderAndGetWindow(url('/ö/'))
|
const response = await rp(url('/ö/'))
|
||||||
|
|
||||||
const html = window.document.body.innerHTML
|
expect(response).toContain('<h1>Unicode base works!</h1>')
|
||||||
expect(html).toContain('<h1>Unicode base works!</h1>')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Close server and ask nuxt to stop listening to file changes
|
// Close server and ask nuxt to stop listening to file changes
|
||||||
|
@ -5,6 +5,7 @@ export { default as getPort } from 'get-port'
|
|||||||
export { default as rp } from 'request-promise-native'
|
export { default as rp } from 'request-promise-native'
|
||||||
|
|
||||||
export * from './nuxt'
|
export * from './nuxt'
|
||||||
|
export * from './resource-size'
|
||||||
|
|
||||||
export const listPaths = function listPaths (dir, pathsBefore = [], options = {}) {
|
export const listPaths = function listPaths (dir, pathsBefore = [], options = {}) {
|
||||||
if (Array.isArray(pathsBefore) && pathsBefore.length) {
|
if (Array.isArray(pathsBefore) && pathsBefore.length) {
|
||||||
|
35
test/utils/resource-size.js
Normal file
35
test/utils/resource-size.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { resolve } from 'path'
|
||||||
|
import zlib from 'zlib'
|
||||||
|
import fs from 'fs-extra'
|
||||||
|
import pify from 'pify'
|
||||||
|
|
||||||
|
const gzipCompressor = pify(zlib.gzip)
|
||||||
|
const brotliCompressor = pify(zlib.brotliCompress)
|
||||||
|
const compressSize = (input, compressor) => compressor(input).then(data => data.length)
|
||||||
|
|
||||||
|
export const getResourcesSize = async (distDir, mode, { filter, gzip, brotli } = {}) => {
|
||||||
|
if (!filter) {
|
||||||
|
filter = filename => filename.endsWith('.js')
|
||||||
|
}
|
||||||
|
const { all } = await import(resolve(distDir, 'server', `${mode}.manifest.json`))
|
||||||
|
const resources = all.filter(filter)
|
||||||
|
const sizes = { uncompressed: 0, gzip: 0, brotli: 0 }
|
||||||
|
for (const resource of resources) {
|
||||||
|
const file = resolve(distDir, 'client', resource)
|
||||||
|
|
||||||
|
const stat = await fs.stat(file)
|
||||||
|
sizes.uncompressed += stat.size / 1024
|
||||||
|
|
||||||
|
if (gzip || brotli) {
|
||||||
|
const fileContent = await fs.readFile(file)
|
||||||
|
|
||||||
|
if (gzip) {
|
||||||
|
sizes.gzip += await compressSize(fileContent, gzipCompressor) / 1024
|
||||||
|
}
|
||||||
|
if (brotli) {
|
||||||
|
sizes.brotli += await compressSize(fileContent, brotliCompressor) / 1024
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sizes
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user