mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-30 09:27:13 +00:00
Merge branch '0.10.0'
This commit is contained in:
commit
d1550f914b
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,6 +1,6 @@
|
|||||||
# dependencies
|
# dependencies
|
||||||
yarn.lock
|
|
||||||
node_modules
|
node_modules
|
||||||
|
examples/**/*/yarn.lock
|
||||||
|
|
||||||
# logs
|
# logs
|
||||||
*.log
|
*.log
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data ({ req }, callback) {
|
asyncData ({ req }, callback) {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
// callback(err, data)
|
// callback(err, data)
|
||||||
callback(null, {
|
callback(null, {
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async data ({ params }) {
|
async asyncData ({ params }) {
|
||||||
// We can use async/await ES6 feature
|
// We can use async/await ES6 feature
|
||||||
let { data } = await axios.get(`https://jsonplaceholder.typicode.com/posts/${params.id}`)
|
let { data } = await axios.get(`https://jsonplaceholder.typicode.com/posts/${params.id}`)
|
||||||
return { post: data }
|
return { post: data }
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data ({ req, params }) {
|
asyncData ({ req, params }) {
|
||||||
// We can return a Promise instead of calling the callback
|
// We can return a Promise instead of calling the callback
|
||||||
return axios.get('https://jsonplaceholder.typicode.com/posts')
|
return axios.get('https://jsonplaceholder.typicode.com/posts')
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
layout: 'dark',
|
layout: 'dark',
|
||||||
data ({ req }) {
|
asyncData ({ req }) {
|
||||||
return {
|
return {
|
||||||
name: req ? 'server' : 'client'
|
name: req ? 'server' : 'client'
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data () {
|
asyncData () {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
resolve({})
|
resolve({})
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data () {
|
asyncData () {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
resolve({ name: 'world' })
|
resolve({ name: 'world' })
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
asyncData () {
|
||||||
return axios.get('https://jsonplaceholder.typicode.com/users')
|
return axios.get('https://jsonplaceholder.typicode.com/users')
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return { users: res.data }
|
return { users: res.data }
|
||||||
|
@ -14,7 +14,7 @@ export default {
|
|||||||
validate ({ params }) {
|
validate ({ params }) {
|
||||||
return !isNaN(+params.id)
|
return !isNaN(+params.id)
|
||||||
},
|
},
|
||||||
data ({ params, error }) {
|
asyncData ({ params, error }) {
|
||||||
return axios.get(`https://jsonplaceholder.typicode.com/users/${+params.id}`)
|
return axios.get(`https://jsonplaceholder.typicode.com/users/${+params.id}`)
|
||||||
.then((res) => res.data)
|
.then((res) => res.data)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
3
examples/dynamic-layouts/README.md
Normal file
3
examples/dynamic-layouts/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Dynamic Layouts
|
||||||
|
|
||||||
|
https://nuxtjs.org/examples/layouts
|
25
examples/dynamic-layouts/layouts/error.vue
Normal file
25
examples/dynamic-layouts/layouts/error.vue
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<h1 v-if="error.statusCode === 404">Page not found</h1>
|
||||||
|
<h1 v-else>An error occured</h1>
|
||||||
|
<nuxt-link to="/">Home page</nuxt-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
layout: ({ isMobile }) => isMobile ? 'mobile' : 'default',
|
||||||
|
props: ['error']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
font-family: sans-serif;
|
||||||
|
padding-top: 10%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
37
examples/dynamic-layouts/layouts/mobile.vue
Normal file
37
examples/dynamic-layouts/layouts/mobile.vue
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<div class="dark">
|
||||||
|
<div class="mobile-banner">Mobile website</div>
|
||||||
|
<nuxt/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.dark {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
background: black;
|
||||||
|
color: white;
|
||||||
|
padding: 10px;
|
||||||
|
padding-top: 40px;
|
||||||
|
}
|
||||||
|
.dark a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.mobile-banner {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
background: #333;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
</style>
|
4
examples/dynamic-layouts/middleware/mobile.js
Normal file
4
examples/dynamic-layouts/middleware/mobile.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default function (ctx) {
|
||||||
|
let userAgent = ctx.req ? ctx.req.headers['user-agent'] : navigator.userAgent
|
||||||
|
ctx.isMobile = /mobile/i.test(userAgent)
|
||||||
|
}
|
10
examples/dynamic-layouts/nuxt.config.js
Normal file
10
examples/dynamic-layouts/nuxt.config.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module.exports = {
|
||||||
|
head: {
|
||||||
|
meta: [
|
||||||
|
{ content: 'width=device-width,initial-scale=1', name: 'viewport' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
router: {
|
||||||
|
middleware: ['mobile']
|
||||||
|
}
|
||||||
|
}
|
11
examples/dynamic-layouts/package.json
Normal file
11
examples/dynamic-layouts/package.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "nuxt-dynamic-layouts",
|
||||||
|
"dependencies": {
|
||||||
|
"nuxt": "latest"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nuxt",
|
||||||
|
"build": "nuxt build",
|
||||||
|
"start": "nuxt start"
|
||||||
|
}
|
||||||
|
}
|
17
examples/dynamic-layouts/pages/about.vue
Normal file
17
examples/dynamic-layouts/pages/about.vue
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p>Hi from {{ name }}</p>
|
||||||
|
<nuxt-link to="/">Home page</nuxt-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
layout: ({ isMobile }) => isMobile ? 'mobile' : 'default',
|
||||||
|
asyncData ({ req }) {
|
||||||
|
return {
|
||||||
|
name: req ? 'server' : 'client'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
12
examples/dynamic-layouts/pages/index.vue
Normal file
12
examples/dynamic-layouts/pages/index.vue
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Welcome!</h1>
|
||||||
|
<nuxt-link to="/about">About page</nuxt-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
layout: ({ isMobile }) => isMobile ? 'mobile' : 'default',
|
||||||
|
}
|
||||||
|
</script>
|
BIN
examples/dynamic-layouts/static/logo.png
Normal file
BIN
examples/dynamic-layouts/static/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
@ -20,6 +20,3 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css">
|
|
||||||
</style>
|
|
||||||
|
11
examples/hello-world-jsx/package.json
Normal file
11
examples/hello-world-jsx/package.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "hello-nuxt-jsx",
|
||||||
|
"dependencies": {
|
||||||
|
"nuxt": "latest"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nuxt",
|
||||||
|
"build": "nuxt build",
|
||||||
|
"start": "nuxt"
|
||||||
|
}
|
||||||
|
}
|
15
examples/hello-world-jsx/pages/about.vue
Normal file
15
examples/hello-world-jsx/pages/about.vue
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
asyncData ({ req }) {
|
||||||
|
return {
|
||||||
|
name: req ? 'server' : 'client'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render (h) {
|
||||||
|
return <div>
|
||||||
|
<p>Hi from {this.name}</p>
|
||||||
|
<nuxt-link to="/">Home page</nuxt-link>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
10
examples/hello-world-jsx/pages/index.vue
Normal file
10
examples/hello-world-jsx/pages/index.vue
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
render (h) {
|
||||||
|
return <div>
|
||||||
|
<h1>Welcome!</h1>
|
||||||
|
<nuxt-link to="/about">About page</nuxt-link>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data ({ req }) {
|
asyncData ({ req }) {
|
||||||
return {
|
return {
|
||||||
name: req ? 'server' : 'client'
|
name: req ? 'server' : 'client'
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data ({ store, route, userAgent }) {
|
asyncData ({ store, route, userAgent }) {
|
||||||
return {
|
return {
|
||||||
userAgent,
|
userAgent,
|
||||||
slugs: [
|
slugs: [
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data ({ env }) {
|
asyncData ({ env }) {
|
||||||
return { users: env.users }
|
return { users: env.users }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ export default {
|
|||||||
validate ({ params }) {
|
validate ({ params }) {
|
||||||
return !isNaN(+params.id)
|
return !isNaN(+params.id)
|
||||||
},
|
},
|
||||||
data ({ params, env, error }) {
|
asyncData ({ params, env, error }) {
|
||||||
const user = env.users.find((user) => String(user.id) === params.id)
|
const user = env.users.find((user) => String(user.id) === params.id)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return error({ message: 'User not found', statusCode: 404 })
|
return error({ message: 'User not found', statusCode: 404 })
|
||||||
|
@ -3,6 +3,7 @@ module.exports = {
|
|||||||
vendor: ['axios', 'mini-toastr', 'vue-notifications']
|
vendor: ['axios', 'mini-toastr', 'vue-notifications']
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
'~plugins/vue-notifications.js'
|
// ssr: false to only include it on client-side
|
||||||
|
{ src: '~plugins/vue-notifications.js', ssr: false }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
asyncData () {
|
||||||
return axios.get('https://jsonplaceholder.typicode.com/photos/4').then(res => res.data)
|
return axios.get('https://jsonplaceholder.typicode.com/photos/4').then(res => res.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
let miniToastr
|
let miniToastr
|
||||||
if (process.BROWSER_BUILD) {
|
if (process.browser) {
|
||||||
miniToastr = require('mini-toastr')
|
miniToastr = require('mini-toastr')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +1,23 @@
|
|||||||
// This code will be injected before initializing the root App
|
// This code will be injected before initializing the root App
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import VueNotifications from 'vue-notifications'
|
import VueNotifications from 'vue-notifications'
|
||||||
|
// Include mini-toaster (or any other UI-notification library
|
||||||
|
import miniToastr from 'mini-toastr'
|
||||||
|
|
||||||
if (process.BROWSER_BUILD) {
|
// Here we setup messages output to `mini-toastr`
|
||||||
// Include mini-toaster (or any other UI-notification library
|
const toast = function ({ title, message, type, timeout, cb }) {
|
||||||
const miniToastr = require('mini-toastr')
|
|
||||||
|
|
||||||
// Here we setup messages output to `mini-toastr`
|
|
||||||
const toast = function ({ title, message, type, timeout, cb }) {
|
|
||||||
return miniToastr[type](message, title, timeout, cb)
|
return miniToastr[type](message, title, timeout, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Binding for methods .success(), .error() and etc. You can specify and map your own methods here.
|
// Binding for methods .success(), .error() and etc. You can specify and map your own methods here.
|
||||||
// Required to pipe our outout to UI library (mini-toastr in example here)
|
// Required to pipe our outout to UI library (mini-toastr in example here)
|
||||||
// All not-specified events (types) would be piped to output in console.
|
// All not-specified events (types) would be piped to output in console.
|
||||||
const options = {
|
const options = {
|
||||||
success: toast,
|
success: toast,
|
||||||
error: toast,
|
error: toast,
|
||||||
info: toast,
|
info: toast,
|
||||||
warn: toast
|
warn: toast
|
||||||
}
|
|
||||||
|
|
||||||
// Activate plugin
|
|
||||||
Vue.use(VueNotifications, options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Activate plugin
|
||||||
|
Vue.use(VueNotifications, options)
|
||||||
|
@ -23,7 +23,7 @@ export default {
|
|||||||
if (!from) return 'slide-left'
|
if (!from) return 'slide-left'
|
||||||
return +to.query.page < +from.query.page ? 'slide-right' : 'slide-left'
|
return +to.query.page < +from.query.page ? 'slide-right' : 'slide-left'
|
||||||
},
|
},
|
||||||
data ({ query }) {
|
asyncData ({ query }) {
|
||||||
const page = +query.page || 1
|
const page = +query.page || 1
|
||||||
return axios.get('https://reqres.in/api/users?page=' + page)
|
return axios.get('https://reqres.in/api/users?page=' + page)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data ({ req }) {
|
asyncData ({ req }) {
|
||||||
return {
|
return {
|
||||||
name: req ? 'server' : 'client'
|
name: req ? 'server' : 'client'
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data ({ req, isServer }) {
|
asyncData ({ req, isServer }) {
|
||||||
return {
|
return {
|
||||||
name: req ? 'server' : 'client'
|
name: req ? 'server' : 'client'
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import socket from '~plugins/socket.io.js'
|
import socket from '~plugins/socket.io.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data (context, callback) {
|
asyncData (context, callback) {
|
||||||
socket.emit('last-messages', function (messages) {
|
socket.emit('last-messages', function (messages) {
|
||||||
callback(null, {
|
callback(null, {
|
||||||
messages,
|
messages,
|
||||||
|
3
index.js
3
index.js
@ -4,6 +4,9 @@
|
|||||||
** @Atinux & @alexchopin
|
** @Atinux & @alexchopin
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Until babel-loader 7 is released
|
||||||
|
process.noDeprecation = true
|
||||||
|
|
||||||
var Nuxt = require('./dist/nuxt.js')
|
var Nuxt = require('./dist/nuxt.js')
|
||||||
|
|
||||||
module.exports = Nuxt.default ? Nuxt.default : Nuxt
|
module.exports = Nuxt.default ? Nuxt.default : Nuxt
|
||||||
|
@ -12,7 +12,7 @@ let layouts = {
|
|||||||
<%
|
<%
|
||||||
var layoutsKeys = Object.keys(layouts);
|
var layoutsKeys = Object.keys(layouts);
|
||||||
layoutsKeys.forEach(function (key, i) { %>
|
layoutsKeys.forEach(function (key, i) { %>
|
||||||
"_<%= key %>": process.BROWSER_BUILD ? () => System.import('<%= layouts[key] %>') : require('<%= layouts[key] %>')<%= (i + 1) < layoutsKeys.length ? ',' : '' %>
|
"_<%= key %>": () => import('<%= layouts[key] %>')<%= (i + 1) < layoutsKeys.length ? ',' : '' %>
|
||||||
<% }) %>
|
<% }) %>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,18 +33,19 @@ export default {
|
|||||||
if (!layout || !layouts['_' + layout]) layout = 'default'
|
if (!layout || !layouts['_' + layout]) layout = 'default'
|
||||||
this.layoutName = layout
|
this.layoutName = layout
|
||||||
let _layout = '_' + layout
|
let _layout = '_' + layout
|
||||||
if (typeof layouts[_layout] === 'function') {
|
|
||||||
return this.loadLayout(_layout)
|
|
||||||
}
|
|
||||||
this.layout = layouts[_layout]
|
this.layout = layouts[_layout]
|
||||||
return Promise.resolve(this.layout)
|
return this.layout
|
||||||
},
|
},
|
||||||
loadLayout (_layout) {
|
loadLayout (layout) {
|
||||||
|
if (!layout || !layouts['_' + layout]) layout = 'default'
|
||||||
|
let _layout = '_' + layout
|
||||||
|
if (typeof layouts[_layout] !== 'function') {
|
||||||
|
return Promise.resolve(layouts[_layout])
|
||||||
|
}
|
||||||
return layouts[_layout]()
|
return layouts[_layout]()
|
||||||
.then((Component) => {
|
.then((Component) => {
|
||||||
layouts[_layout] = Component
|
layouts[_layout] = Component
|
||||||
this.layout = layouts[_layout]
|
return layouts[_layout]
|
||||||
return this.layout
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
if (this.$nuxt) {
|
if (this.$nuxt) {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import middleware from './middleware'
|
import middleware from './middleware'
|
||||||
import { app, router<%= (store ? ', store' : '') %> } from './index'
|
import { app, router<%= (store ? ', store' : '') %>, NuxtError } from './index'
|
||||||
import { getMatchedComponents, getMatchedComponentsInstances, flatMapComponents, getContext, promiseSeries, promisify, getLocation, compile } from './utils'
|
import { getMatchedComponents, getMatchedComponentsInstances, flatMapComponents, getContext, promiseSeries, promisify, getLocation, compile } from './utils'
|
||||||
const noopData = () => { return {} }
|
const noopData = () => { return {} }
|
||||||
const noopFetch = () => {}
|
const noopFetch = () => {}
|
||||||
@ -55,8 +55,11 @@ function loadAsyncComponents (to, from, next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function callMiddleware (Components, context, layout) {
|
function callMiddleware (Components, context, layout) {
|
||||||
// Call middleware
|
// if layout is undefined, only call global middleware
|
||||||
let midd = <%= serialize(router.middleware, { isJSON: true }) %>
|
let midd = <%= serialize(router.middleware, { isJSON: true }) %>
|
||||||
|
let unknownMiddleware = false
|
||||||
|
if (typeof layout !== 'undefined') {
|
||||||
|
midd = [] // exclude global middleware if layout defined (already called before)
|
||||||
if (layout.middleware) {
|
if (layout.middleware) {
|
||||||
midd = midd.concat(layout.middleware)
|
midd = midd.concat(layout.middleware)
|
||||||
}
|
}
|
||||||
@ -65,13 +68,15 @@ function callMiddleware (Components, context, layout) {
|
|||||||
midd = midd.concat(Component.options.middleware)
|
midd = midd.concat(Component.options.middleware)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
midd = midd.map((name) => {
|
midd = midd.map((name) => {
|
||||||
if (typeof middleware[name] !== 'function') {
|
if (typeof middleware[name] !== 'function') {
|
||||||
|
unknownMiddleware = true
|
||||||
this.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
this.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
||||||
}
|
}
|
||||||
return middleware[name]
|
return middleware[name]
|
||||||
})
|
})
|
||||||
if (this.$options._nuxt.err) return
|
if (unknownMiddleware) return
|
||||||
return promiseSeries(midd, context)
|
return promiseSeries(midd, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,13 +87,15 @@ function render (to, from, next) {
|
|||||||
nextCalled = true
|
nextCalled = true
|
||||||
next(path)
|
next(path)
|
||||||
}
|
}
|
||||||
const context = getContext({ to<%= (store ? ', store' : '') %>, isClient: true, next: _next.bind(this), error: this.error.bind(this) })
|
let context = getContext({ to<%= (store ? ', store' : '') %>, isClient: true, next: _next.bind(this), error: this.error.bind(this) })
|
||||||
let Components = getMatchedComponents(to)
|
let Components = getMatchedComponents(to)
|
||||||
|
this._context = context
|
||||||
this._dateLastError = this.$options._nuxt.dateErr
|
this._dateLastError = this.$options._nuxt.dateErr
|
||||||
this._hadError = !!this.$options._nuxt.err
|
this._hadError = !!this.$options._nuxt.err
|
||||||
if (!Components.length) {
|
if (!Components.length) {
|
||||||
// Default layout
|
// Default layout
|
||||||
this.setLayout()
|
callMiddleware.call(this, Components, context)
|
||||||
|
.then(() => this.loadLayout(typeof NuxtError.layout === 'function' ? NuxtError.layout(context) : NuxtError.layout))
|
||||||
.then(callMiddleware.bind(this, Components, context))
|
.then(callMiddleware.bind(this, Components, context))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.error({ statusCode: 404, message: 'This page could not be found.' })
|
this.error({ statusCode: 404, message: 'This page could not be found.' })
|
||||||
@ -98,26 +105,22 @@ function render (to, from, next) {
|
|||||||
}
|
}
|
||||||
// Update ._data and other properties if hot reloaded
|
// Update ._data and other properties if hot reloaded
|
||||||
Components.forEach(function (Component) {
|
Components.forEach(function (Component) {
|
||||||
if (!Component._data) {
|
|
||||||
Component._data = Component.options.data || noopData
|
|
||||||
}
|
|
||||||
if (Component._Ctor && Component._Ctor.options) {
|
if (Component._Ctor && Component._Ctor.options) {
|
||||||
|
Component.options.asyncData = Component._Ctor.options.asyncData
|
||||||
Component.options.fetch = Component._Ctor.options.fetch
|
Component.options.fetch = Component._Ctor.options.fetch
|
||||||
if (Component._dataFn) {
|
|
||||||
const originalDataFn = Component._data.toString().replace(/\s/g, '')
|
|
||||||
const dataFn = Component._dataFn
|
|
||||||
const newDataFn = (Component._Ctor.options.data || noopData).toString().replace(/\s/g, '')
|
|
||||||
// If component data method changed
|
|
||||||
if (newDataFn !== originalDataFn && newDataFn !== dataFn) {
|
|
||||||
Component._data = Component._Ctor.options.data || noopData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.setTransitions(mapTransitions(Components, to, from))
|
this.setTransitions(mapTransitions(Components, to, from))
|
||||||
let nextCalled = false
|
let nextCalled = false
|
||||||
// Set layout
|
// Set layout
|
||||||
this.setLayout(Components[0].options.layout)
|
callMiddleware.call(this, Components, context)
|
||||||
|
.then(() => {
|
||||||
|
let layout = Components[0].options.layout
|
||||||
|
if (typeof layout === 'function') {
|
||||||
|
layout = layout(context)
|
||||||
|
}
|
||||||
|
return this.loadLayout(layout)
|
||||||
|
})
|
||||||
.then(callMiddleware.bind(this, Components, context))
|
.then(callMiddleware.bind(this, Components, context))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Pass validation?
|
// Pass validation?
|
||||||
@ -141,13 +144,19 @@ function render (to, from, next) {
|
|||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
let promises = []
|
let promises = []
|
||||||
// Validate method
|
// asyncData method
|
||||||
if (Component._data && typeof Component._data === 'function') {
|
if (Component.options.asyncData && typeof Component.options.asyncData === 'function') {
|
||||||
var promise = promisify(Component._data, context)
|
var promise = promisify(Component.options.asyncData, context)
|
||||||
promise.then((data) => {
|
promise.then((asyncDataResult) => {
|
||||||
Component._cData = () => data || {}
|
let data = {}
|
||||||
Component.options.data = Component._cData
|
// Call data() if defined
|
||||||
Component._dataFn = Component.options.data.toString().replace(/\s/g, '')
|
if (Component.options.data && typeof Component.options.data === 'function') {
|
||||||
|
data = Component.options.data()
|
||||||
|
}
|
||||||
|
// Merge data() and asyncData() results
|
||||||
|
data = Object.assign(data, asyncDataResult)
|
||||||
|
// Overwrite .data() method with merged data
|
||||||
|
Component.options.data = () => data
|
||||||
if (Component._Ctor && Component._Ctor.options) {
|
if (Component._Ctor && Component._Ctor.options) {
|
||||||
Component._Ctor.options.data = Component.options.data
|
Component._Ctor.options.data = Component.options.data
|
||||||
}
|
}
|
||||||
@ -175,9 +184,16 @@ function render (to, from, next) {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
_lastPaths = []
|
_lastPaths = []
|
||||||
error.statusCode = error.statusCode || error.status || (error.response && error.response.status) || 500
|
error.statusCode = error.statusCode || error.status || (error.response && error.response.status) || 500
|
||||||
|
let layout = NuxtError.layout
|
||||||
|
if (typeof layout === 'function') {
|
||||||
|
layout = layout(context)
|
||||||
|
}
|
||||||
|
this.loadLayout(layout)
|
||||||
|
.then(() => {
|
||||||
this.error(error)
|
this.error(error)
|
||||||
next(false)
|
next(false)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix components format in matched, it's due to code-splitting of vue-router
|
// Fix components format in matched, it's due to code-splitting of vue-router
|
||||||
@ -213,73 +229,104 @@ function fixPrepatch (to, ___) {
|
|||||||
if (this._hadError && this._dateLastError === this.$options._nuxt.dateErr) {
|
if (this._hadError && this._dateLastError === this.$options._nuxt.dateErr) {
|
||||||
this.error()
|
this.error()
|
||||||
}
|
}
|
||||||
|
// Set layout
|
||||||
|
let layout = this.$options._nuxt.err ? NuxtError.layout : to.matched[0].components.default.options.layout
|
||||||
|
if (typeof layout === 'function') {
|
||||||
|
layout = layout(this._context)
|
||||||
|
}
|
||||||
|
this.setLayout(layout)
|
||||||
// hot reloading
|
// hot reloading
|
||||||
hotReloadAPI(this)
|
setTimeout(() => hotReloadAPI(this), 100)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special hot reload with data(context)
|
// Special hot reload with asyncData(context)
|
||||||
function hotReloadAPI (_app) {
|
function hotReloadAPI (_app) {
|
||||||
if (!module.hot) return
|
if (!module.hot) return
|
||||||
const $nuxt = _app.$nuxt
|
let $components = []
|
||||||
var _forceUpdate = $nuxt.$forceUpdate.bind($nuxt)
|
let $nuxt = _app.$nuxt
|
||||||
$nuxt.$forceUpdate = function () {
|
while ($nuxt && $nuxt.$children && $nuxt.$children.length) {
|
||||||
let Component = getMatchedComponents(router.currentRoute)[0]
|
$nuxt.$children.forEach(function (child, i) {
|
||||||
|
if (child.$vnode.data.nuxtChild) {
|
||||||
|
let hasAlready = false
|
||||||
|
$components.forEach(function (component) {
|
||||||
|
if (component.$options.__file === child.$options.__file) {
|
||||||
|
hasAlready = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!hasAlready) {
|
||||||
|
$components.push(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$nuxt = child
|
||||||
|
})
|
||||||
|
}
|
||||||
|
$components.forEach(addHotReload.bind(_app))
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHotReload ($component, depth) {
|
||||||
|
if ($component.$vnode.data._hasHotReload) return
|
||||||
|
$component.$vnode.data._hasHotReload = true
|
||||||
|
var _forceUpdate = $component.$forceUpdate.bind($component.$parent)
|
||||||
|
$component.$vnode.context.$forceUpdate = () => {
|
||||||
|
let Components = getMatchedComponents(router.currentRoute)
|
||||||
|
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)
|
||||||
Component._Ctor = Component
|
Component._Ctor = Component
|
||||||
}
|
}
|
||||||
|
this.error()
|
||||||
let promises = []
|
let promises = []
|
||||||
// If layout changed
|
|
||||||
if (_app.layoutName !== Component.options.layout) {
|
|
||||||
let promise = _app.setLayout(Component.options.layout)
|
|
||||||
promise.then(() => {
|
|
||||||
hotReloadAPI(_app)
|
|
||||||
})
|
|
||||||
promises.push(promise)
|
|
||||||
}
|
|
||||||
const next = function (path) {
|
const next = function (path) {
|
||||||
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
||||||
router.push(path)
|
router.push(path)
|
||||||
}
|
}
|
||||||
const context = getContext({ route: router.currentRoute<%= (store ? ', store' : '') %>, isClient: true, next: next.bind(this), error: _app.error })
|
let context = getContext({ route: router.currentRoute<%= (store ? ', store' : '') %>, isClient: true, next: next.bind(this), error: this.error })
|
||||||
// Check if data has been updated
|
<%= (loading ? 'this.$loading.start && this.$loading.start()' : '') %>
|
||||||
const originalDataFn = (Component._data || noopData).toString().replace(/\s/g, '')
|
callMiddleware.call(this, Components, context)
|
||||||
const newDataFn = (Component._Ctor.options.data || noopData).toString().replace(/\s/g, '')
|
.then(() => {
|
||||||
if (originalDataFn !== newDataFn) {
|
// If layout changed
|
||||||
Component._data = Component._Ctor.options.data || noopData
|
if (depth !== 0) return Promise.resolve()
|
||||||
let p = promisify(Component._data, context)
|
let layout = Component.options.layout || 'default'
|
||||||
p.then((data) => {
|
if (typeof layout === 'function') {
|
||||||
Component._cData = () => data || {}
|
layout = layout(context)
|
||||||
Component.options.data = Component._cData
|
}
|
||||||
Component._dataFn = Component.options.data.toString().replace(/\s/g, '')
|
if (this.layoutName === layout) return Promise.resolve()
|
||||||
|
let promise = this.loadLayout(layout)
|
||||||
|
promise.then(() => {
|
||||||
|
this.setLayout(layout)
|
||||||
|
Vue.nextTick(() => hotReloadAPI(this))
|
||||||
|
})
|
||||||
|
return promise
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return callMiddleware.call(this, Components, context, this.layout)
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Call asyncData()
|
||||||
|
let pAsyncData = promisify(Component.options.asyncData || noopData, context)
|
||||||
|
pAsyncData.then((asyncDataResult) => {
|
||||||
|
let data = (typeof Component.options.data === 'function' ? Component.options.data() : noopData())
|
||||||
|
data = Object.assign(data, asyncDataResult)
|
||||||
|
Component.options.data = () => data
|
||||||
Component._Ctor.options.data = Component.options.data
|
Component._Ctor.options.data = Component.options.data
|
||||||
<%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %>
|
<%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %>
|
||||||
})
|
})
|
||||||
promises.push(p)
|
promises.push(pAsyncData)
|
||||||
} else if (Component._cData) {
|
// Call fetch()
|
||||||
Component._data = Component.options.data
|
Component.options.fetch = Component.options.fetch || noopFetch
|
||||||
Component.options.data = Component._cData
|
let pFetch = Component.options.fetch(context)
|
||||||
Component._Ctor.options.data = Component.options.data
|
if (!(pFetch instanceof Promise)) { pFetch = Promise.resolve(pFetch) }
|
||||||
}
|
<%= (loading ? 'pFetch.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %>
|
||||||
// Check if fetch has been updated
|
promises.push(pFetch)
|
||||||
const originalFetchFn = (Component.options.fetch || noopFetch).toString().replace(/\s/g, '')
|
return Promise.all(promises)
|
||||||
const newFetchFn = (Component._Ctor.options.fetch || noopFetch).toString().replace(/\s/g, '')
|
})
|
||||||
// Fetch has been updated, we call it to update the store
|
.then(() => {
|
||||||
if (originalFetchFn !== newFetchFn) {
|
|
||||||
Component.options.fetch = Component._Ctor.options.fetch || noopFetch
|
|
||||||
let p = Component.options.fetch(context)
|
|
||||||
if (!(p instanceof Promise)) { p = Promise.resolve(p) }
|
|
||||||
<%= (loading ? 'p.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %>
|
|
||||||
promises.push(p)
|
|
||||||
}
|
|
||||||
if (!promises.length) return;
|
|
||||||
<%= (loading ? 'this.$loading.start && this.$loading.start()' : '') %>
|
|
||||||
return Promise.all(promises).then(() => {
|
|
||||||
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
||||||
_forceUpdate()
|
_forceUpdate()
|
||||||
|
setTimeout(() => hotReloadAPI(this), 100)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,13 +349,14 @@ const resolveComponents = flatMapComponents(router.match(path), (Component, _, m
|
|||||||
Component._Ctor = Component
|
Component._Ctor = Component
|
||||||
Component.extendOptions = Component.options
|
Component.extendOptions = Component.options
|
||||||
}
|
}
|
||||||
if (Component.options.data && typeof Component.options.data === 'function') {
|
|
||||||
Component._data = Component.options.data
|
|
||||||
if (NUXT.serverRendered) {
|
if (NUXT.serverRendered) {
|
||||||
Component._cData = () => NUXT.data[index] || {}
|
let data = {}
|
||||||
Component.options.data = Component._cData
|
if (Component.options.data && typeof Component.options.data === 'function') {
|
||||||
Component._dataFn = Component.options.data.toString().replace(/\s/g, '')
|
data = Component.options.data()
|
||||||
}
|
}
|
||||||
|
// Merge data() and asyncData() results
|
||||||
|
data = Object.assign(data, NUXT.data[index])
|
||||||
|
Component.options.data = () => data
|
||||||
if (Component._Ctor && Component._Ctor.options) {
|
if (Component._Ctor && Component._Ctor.options) {
|
||||||
Component._Ctor.options.data = Component.options.data
|
Component._Ctor.options.data = Component.options.data
|
||||||
}
|
}
|
||||||
@ -338,25 +386,31 @@ Promise.all(resolveComponents)
|
|||||||
.then((Components) => {
|
.then((Components) => {
|
||||||
const _app = new Vue(app)
|
const _app = new Vue(app)
|
||||||
|
|
||||||
return _app.setLayout(Components.length ? Components[0].options.layout : '')
|
let context = getContext({ to: router.currentRoute, isClient: true })
|
||||||
|
let layoutName = 'default'
|
||||||
|
return callMiddleware.call(_app, Components, context)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
layoutName = Components.length ? Components[0].options.layout : NuxtError.layout
|
||||||
|
if (typeof layoutName === 'function') {
|
||||||
|
layoutName = layoutName(context)
|
||||||
|
}
|
||||||
|
return _app.loadLayout(layoutName)
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
_app.setLayout(layoutName)
|
||||||
return { _app, Components }
|
return { _app, Components }
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(({ _app, Components }) => {
|
.then(({ _app, Components }) => {
|
||||||
const mountApp = () => {
|
const mountApp = () => {
|
||||||
_app.$mount('#__nuxt')
|
_app.$mount('#__nuxt')
|
||||||
|
Vue.nextTick(() => {
|
||||||
// Hot reloading
|
// Hot reloading
|
||||||
hotReloadAPI(_app)
|
hotReloadAPI(_app)
|
||||||
// Call window.onNuxtReady callbacks
|
// Call window.onNuxtReady callbacks
|
||||||
Vue.nextTick(() => nuxtReady(_app))
|
nuxtReady(_app)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
<% if (store) { %>
|
|
||||||
// Replace store state
|
|
||||||
if (NUXT.state) {
|
|
||||||
store.replaceState(NUXT.state)
|
|
||||||
}
|
|
||||||
<% } %>
|
|
||||||
_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))
|
||||||
|
@ -5,6 +5,7 @@ const transitionsKeys = [
|
|||||||
'mode',
|
'mode',
|
||||||
'css',
|
'css',
|
||||||
'type',
|
'type',
|
||||||
|
'duration',
|
||||||
'enterClass',
|
'enterClass',
|
||||||
'leaveClass',
|
'leaveClass',
|
||||||
'enterActiveClass',
|
'enterActiveClass',
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import NuxtChild from './nuxt-child'
|
import NuxtChild from './nuxt-child'
|
||||||
import NuxtError from '<%= components.ErrorPage %>'
|
import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./nuxt-error.vue" %>'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'nuxt',
|
name: 'nuxt',
|
||||||
|
@ -6,6 +6,7 @@ import router from './router.js'
|
|||||||
<% if (store) { %>import store from './store.js'<% } %>
|
<% if (store) { %>import store from './store.js'<% } %>
|
||||||
import NuxtChild from './components/nuxt-child.js'
|
import NuxtChild from './components/nuxt-child.js'
|
||||||
import NuxtLink from './components/nuxt-link.js'
|
import NuxtLink from './components/nuxt-link.js'
|
||||||
|
import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./components/nuxt-error.vue" %>'
|
||||||
import Nuxt from './components/nuxt.vue'
|
import Nuxt from './components/nuxt.vue'
|
||||||
import App from '<%= appPath %>'
|
import App from '<%= appPath %>'
|
||||||
|
|
||||||
@ -19,12 +20,18 @@ Vue.component(Nuxt.name, Nuxt)
|
|||||||
// 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.
|
||||||
attribute: 'n-head', // the attribute name vue-meta adds to the tags it observes
|
attribute: 'data-n-head', // the attribute name vue-meta adds to the tags it observes
|
||||||
ssrAttribute: '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 (process.BROWSER_BUILD) {
|
if (process.browser) {
|
||||||
|
<% if (store) { %>
|
||||||
|
// Replace store state before calling plugins
|
||||||
|
if (window.__NUXT__ && window.__NUXT__.state) {
|
||||||
|
store.replaceState(window.__NUXT__.state)
|
||||||
|
}
|
||||||
|
<% } %>
|
||||||
// window.onNuxtReady(() => console.log('Ready')) hook
|
// window.onNuxtReady(() => console.log('Ready')) hook
|
||||||
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
|
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
|
||||||
window._nuxtReadyCbs = []
|
window._nuxtReadyCbs = []
|
||||||
@ -34,8 +41,16 @@ if (process.BROWSER_BUILD) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Includes external plugins
|
// Includes external plugins
|
||||||
<% plugins.forEach(function (pluginPath) { %>
|
<% plugins.forEach(function (plugin) {
|
||||||
require('<%= pluginPath %>')
|
if (typeof plugin === 'string') { %>
|
||||||
|
require('<%= plugin %>')
|
||||||
|
<% } else if (plugin.src && plugin.ssr !== false) { %>
|
||||||
|
require('<%= plugin.src %>')
|
||||||
|
<% } else if (plugin.src) { %>
|
||||||
|
if (process.browser) {
|
||||||
|
require('<%= plugin.src %>')
|
||||||
|
}
|
||||||
|
<% } %>
|
||||||
<% }) %>
|
<% }) %>
|
||||||
|
|
||||||
// root instance
|
// root instance
|
||||||
@ -85,4 +100,4 @@ const app = {
|
|||||||
...App
|
...App
|
||||||
}
|
}
|
||||||
|
|
||||||
export { app, router<%= (store ? ', store' : '') %> }
|
export { app, router<%= (store ? ', store' : '') %>, NuxtError }
|
||||||
|
@ -23,7 +23,7 @@ function recursiveRoutes(routes, tab, components) {
|
|||||||
var _components = []
|
var _components = []
|
||||||
var _routes = recursiveRoutes(router.routes, '\t\t', _components)
|
var _routes = recursiveRoutes(router.routes, '\t\t', _components)
|
||||||
uniqBy(_components, '_name').forEach((route) => { %>
|
uniqBy(_components, '_name').forEach((route) => { %>
|
||||||
const <%= route._name %> = process.BROWSER_BUILD ? () => System.import('<%= route.component %>') : require('<%= route.component %>')
|
const <%= route._name %> = () => import('<%= route.component %>')
|
||||||
<% }) %>
|
<% }) %>
|
||||||
|
|
||||||
<% if (router.scrollBehavior) { %>
|
<% if (router.scrollBehavior) { %>
|
||||||
|
@ -3,10 +3,11 @@
|
|||||||
const debug = require('debug')('nuxt:render')
|
const debug = require('debug')('nuxt:render')
|
||||||
debug.color = 4 // force blue color
|
debug.color = 4 // force blue color
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
|
||||||
import { stringify } from 'querystring'
|
import { stringify } from 'querystring'
|
||||||
import { omit } from 'lodash'
|
import { omit } from 'lodash'
|
||||||
import middleware from './middleware'
|
import middleware from './middleware'
|
||||||
import { app, router<%= (store ? ', store' : '') %> } from './index'
|
import { app, router<%= (store ? ', store' : '') %>, NuxtError } from './index'
|
||||||
import { getMatchedComponents, getContext, promiseSeries, promisify, urlJoin } from './utils'
|
import { getMatchedComponents, getContext, promiseSeries, promisify, urlJoin } from './utils'
|
||||||
|
|
||||||
const isDev = <%= isDev %>
|
const isDev = <%= isDev %>
|
||||||
@ -25,9 +26,9 @@ export default context => {
|
|||||||
// create context.next for simulate next() of beforeEach() when wanted to redirect
|
// create context.next for simulate next() of beforeEach() when wanted to redirect
|
||||||
context.redirected = false
|
context.redirected = false
|
||||||
context.next = function (opts) {
|
context.next = function (opts) {
|
||||||
|
context.redirected = opts
|
||||||
// if nuxt generate
|
// if nuxt generate
|
||||||
if (!context.res) {
|
if (!context.res) {
|
||||||
context.redirected = opts
|
|
||||||
context.nuxt.serverRendered = false
|
context.nuxt.serverRendered = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -39,18 +40,45 @@ export default context => {
|
|||||||
})
|
})
|
||||||
context.res.end()
|
context.res.end()
|
||||||
}
|
}
|
||||||
// set router's location
|
|
||||||
router.push(context.url)
|
|
||||||
// Add route to the context
|
|
||||||
context.route = router.currentRoute
|
|
||||||
// Add meta infos
|
// Add meta infos
|
||||||
context.meta = _app.$meta()
|
context.meta = _app.$meta()
|
||||||
// Error function
|
// Error function
|
||||||
context.error = _app.$options._nuxt.error.bind(_app)
|
context.error = _app.$options._nuxt.error.bind(_app)
|
||||||
|
|
||||||
<%= (isDev ? 'const s = isDev && Date.now()' : '') %>
|
<%= (isDev ? 'const s = isDev && Date.now()' : '') %>
|
||||||
const ctx = getContext(context)
|
let ctx = null
|
||||||
let Components = getMatchedComponents(context.route)
|
let componentsLoaded = false
|
||||||
|
let Components = []
|
||||||
|
let promises = getMatchedComponents(router.match(context.url)).map((Component) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const _resolve = (Component) => {
|
||||||
|
if (!Component.options) {
|
||||||
|
Component = Vue.extend(Component) // fix issue #6
|
||||||
|
Component._Ctor = Component
|
||||||
|
} else {
|
||||||
|
Component._Ctor = Component
|
||||||
|
Component.extendOptions = Component.options
|
||||||
|
}
|
||||||
|
resolve(Component)
|
||||||
|
}
|
||||||
|
Component().then(_resolve).catch(reject)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return Promise.all(promises)
|
||||||
|
.then((_Components) => {
|
||||||
|
componentsLoaded = true
|
||||||
|
Components = _Components
|
||||||
|
// set router's location
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
router.push(context.url, resolve)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Add route to the context
|
||||||
|
context.route = router.currentRoute
|
||||||
|
// Update context
|
||||||
|
ctx = getContext(context)
|
||||||
|
// nuxtServerInit
|
||||||
<% if (store) { %>
|
<% if (store) { %>
|
||||||
let promise = (store._actions && store._actions.nuxtServerInit ? store.dispatch('nuxtServerInit', omit(getContext(context), 'redirect', 'error')) : null)
|
let promise = (store._actions && store._actions.nuxtServerInit ? store.dispatch('nuxtServerInit', omit(getContext(context), 'redirect', 'error')) : null)
|
||||||
if (!(promise instanceof Promise)) promise = Promise.resolve()
|
if (!(promise instanceof Promise)) promise = Promise.resolve()
|
||||||
@ -58,6 +86,7 @@ export default context => {
|
|||||||
let promise = Promise.resolve()
|
let promise = Promise.resolve()
|
||||||
<% } %>
|
<% } %>
|
||||||
return promise
|
return promise
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Sanitize Components
|
// Sanitize Components
|
||||||
Components = Components.map((Component) => {
|
Components = Components.map((Component) => {
|
||||||
@ -71,12 +100,28 @@ export default context => {
|
|||||||
}
|
}
|
||||||
return Component
|
return Component
|
||||||
})
|
})
|
||||||
|
// Call global middleware (nuxt.config.js)
|
||||||
|
let midd = <%= serialize(router.middleware, { isJSON: true }) %>
|
||||||
|
midd = midd.map((name) => {
|
||||||
|
if (typeof middleware[name] !== 'function') {
|
||||||
|
context.nuxt.error = context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
||||||
|
}
|
||||||
|
return middleware[name]
|
||||||
|
})
|
||||||
|
if (context.nuxt.error) return
|
||||||
|
return promiseSeries(midd, ctx)
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
// Set layout
|
// Set layout
|
||||||
return _app.setLayout(Components.length ? Components[0].options.layout : '')
|
let layout = Components.length ? Components[0].options.layout : NuxtError.layout
|
||||||
|
if (typeof layout === 'function') {
|
||||||
|
layout = layout(ctx)
|
||||||
|
}
|
||||||
|
return _app.loadLayout(layout).then(() => _app.setLayout(layout))
|
||||||
})
|
})
|
||||||
.then((layout) => {
|
.then((layout) => {
|
||||||
// Call middleware
|
// Call middleware (layout + pages)
|
||||||
let midd = <%= serialize(router.middleware, { isJSON: true }) %>
|
let midd = []
|
||||||
if (layout.middleware) {
|
if (layout.middleware) {
|
||||||
midd = midd.concat(layout.middleware)
|
midd = midd.concat(layout.middleware)
|
||||||
}
|
}
|
||||||
@ -114,13 +159,20 @@ export default context => {
|
|||||||
Components = []
|
Components = []
|
||||||
return _app
|
return _app
|
||||||
}
|
}
|
||||||
// Call data & fetch hooks on components matched by the route.
|
// Call asyncData & fetch hooks on components matched by the route.
|
||||||
return Promise.all(Components.map((Component) => {
|
return Promise.all(Components.map((Component) => {
|
||||||
let promises = []
|
let promises = []
|
||||||
|
if (Component.options.asyncData && typeof Component.options.asyncData === 'function') {
|
||||||
|
let promise = promisify(Component.options.asyncData, ctx)
|
||||||
|
// Call asyncData(context)
|
||||||
|
promise.then((asyncDataResult) => {
|
||||||
|
let data = {}
|
||||||
|
// Call data() if defined
|
||||||
if (Component.options.data && typeof Component.options.data === 'function') {
|
if (Component.options.data && typeof Component.options.data === 'function') {
|
||||||
Component._data = Component.options.data
|
data = Component.options.data()
|
||||||
let promise = promisify(Component._data, ctx)
|
}
|
||||||
promise.then((data) => {
|
// Merge data() and asyncData() results
|
||||||
|
data = Object.assign(data, asyncDataResult)
|
||||||
Component.options.data = () => data
|
Component.options.data = () => data
|
||||||
Component._Ctor.options.data = Component.options.data
|
Component._Ctor.options.data = Component.options.data
|
||||||
})
|
})
|
||||||
@ -130,6 +182,8 @@ export default context => {
|
|||||||
}
|
}
|
||||||
if (Component.options.fetch) {
|
if (Component.options.fetch) {
|
||||||
promises.push(Component.options.fetch(ctx))
|
promises.push(Component.options.fetch(ctx))
|
||||||
|
} else {
|
||||||
|
promises.push(null)
|
||||||
}
|
}
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
}))
|
}))
|
||||||
@ -141,16 +195,19 @@ export default context => {
|
|||||||
return _app
|
return _app
|
||||||
}
|
}
|
||||||
<% if (isDev) { %>
|
<% if (isDev) { %>
|
||||||
debug('Data fetching ' + context.req.url + ': ' + (Date.now() - s) + 'ms')
|
debug('Data fetching ' + context.url + ': ' + (Date.now() - s) + 'ms')
|
||||||
<% } %>
|
<% } %>
|
||||||
// datas are the first row of each
|
// datas are the first row of each
|
||||||
context.nuxt.data = res.map((tab) => tab[0])
|
context.nuxt.data = res.map((r) => (r[0] || {}))
|
||||||
context.nuxt.error = _app.$options._nuxt.err
|
context.nuxt.error = _app.$options._nuxt.err
|
||||||
<%= (store ? '// Add the state from the vuex store' : '') %>
|
<%= (store ? '// Add the state from the vuex store' : '') %>
|
||||||
<%= (store ? 'context.nuxt.state = store.state' : '') %>
|
<%= (store ? 'context.nuxt.state = store.state' : '') %>
|
||||||
return _app
|
return _app
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
|
if (!componentsLoaded && error instanceof Error) {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
if (error && (error instanceof Error || error.constructor.toString().indexOf('Error()') !== -1)) {
|
if (error && (error instanceof Error || error.constructor.toString().indexOf('Error()') !== -1)) {
|
||||||
let statusCode = error.statusCode || error.status || (error.response && error.response.status) || 500
|
let statusCode = error.statusCode || error.status || (error.response && error.response.status) || 500
|
||||||
error = { statusCode, message: error.message }
|
error = { statusCode, message: error.message }
|
||||||
|
119
lib/build.js
119
lib/build.js
@ -1,6 +1,5 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const debug = require('debug')('nuxt:build')
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import co from 'co'
|
import co from 'co'
|
||||||
import chokidar from 'chokidar'
|
import chokidar from 'chokidar'
|
||||||
@ -11,10 +10,10 @@ import webpack from 'webpack'
|
|||||||
import serialize from 'serialize-javascript'
|
import serialize from 'serialize-javascript'
|
||||||
import { createBundleRenderer } from 'vue-server-renderer'
|
import { createBundleRenderer } from 'vue-server-renderer'
|
||||||
import { join, resolve, sep } from 'path'
|
import { join, resolve, sep } from 'path'
|
||||||
|
import { isUrl } from './utils'
|
||||||
import clientWebpackConfig from './webpack/client.config.js'
|
import clientWebpackConfig from './webpack/client.config.js'
|
||||||
import serverWebpackConfig from './webpack/server.config.js'
|
import serverWebpackConfig from './webpack/server.config.js'
|
||||||
import chalk from 'chalk'
|
const debug = require('debug')('nuxt:build')
|
||||||
import PostCompilePlugin from 'post-compile-webpack-plugin'
|
|
||||||
const remove = pify(fs.remove)
|
const remove = pify(fs.remove)
|
||||||
const readFile = pify(fs.readFile)
|
const readFile = pify(fs.readFile)
|
||||||
const writeFile = pify(fs.writeFile)
|
const writeFile = pify(fs.writeFile)
|
||||||
@ -38,21 +37,16 @@ const r = function () {
|
|||||||
args = args.map(normalize)
|
args = args.map(normalize)
|
||||||
return wp(resolve.apply(null, args))
|
return wp(resolve.apply(null, args))
|
||||||
}
|
}
|
||||||
const webpackStats = {
|
let webpackStats = 'none'
|
||||||
chunks: false,
|
|
||||||
children: false,
|
|
||||||
modules: false,
|
|
||||||
colors: true
|
|
||||||
}
|
|
||||||
// force green color
|
// force green color
|
||||||
debug.color = 2
|
debug.color = 2
|
||||||
|
|
||||||
const defaults = {
|
const defaults = {
|
||||||
analyze: false,
|
analyze: false,
|
||||||
|
publicPath: '/_nuxt/',
|
||||||
filenames: {
|
filenames: {
|
||||||
css: 'style.css',
|
vendor: 'vendor.bundle.[hash].js',
|
||||||
vendor: 'vendor.bundle.js',
|
app: 'nuxt.bundle.[chunkhash].js'
|
||||||
app: 'nuxt.bundle.js'
|
|
||||||
},
|
},
|
||||||
vendor: [],
|
vendor: [],
|
||||||
loaders: [],
|
loaders: [],
|
||||||
@ -90,13 +84,24 @@ export function options () {
|
|||||||
if (this.options.build && !Array.isArray(this.options.build.loaders)) extraDefaults.loaders = defaultsLoaders
|
if (this.options.build && !Array.isArray(this.options.build.loaders)) extraDefaults.loaders = defaultsLoaders
|
||||||
if (this.options.build && !Array.isArray(this.options.build.postcss)) extraDefaults.postcss = defaultsPostcss
|
if (this.options.build && !Array.isArray(this.options.build.postcss)) extraDefaults.postcss = defaultsPostcss
|
||||||
this.options.build = _.defaultsDeep(this.options.build, defaults, extraDefaults)
|
this.options.build = _.defaultsDeep(this.options.build, defaults, extraDefaults)
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (this.dev && isUrl(this.options.build.publicPath)) {
|
||||||
|
this.options.build.publicPath = defaults.publicPath
|
||||||
|
}
|
||||||
// Production, create server-renderer
|
// Production, create server-renderer
|
||||||
if (!this.dev) {
|
if (!this.dev) {
|
||||||
|
webpackStats = {
|
||||||
|
chunks: false,
|
||||||
|
children: false,
|
||||||
|
modules: false,
|
||||||
|
colors: true
|
||||||
|
}
|
||||||
const serverConfig = getWebpackServerConfig.call(this)
|
const serverConfig = getWebpackServerConfig.call(this)
|
||||||
const bundlePath = join(serverConfig.output.path, serverConfig.output.filename)
|
const bundlePath = join(serverConfig.output.path, 'server-bundle.json')
|
||||||
if (fs.existsSync(bundlePath)) {
|
if (fs.existsSync(bundlePath)) {
|
||||||
const bundle = fs.readFileSync(bundlePath, 'utf8')
|
const bundle = fs.readFileSync(bundlePath, 'utf8')
|
||||||
createRenderer.call(this, bundle)
|
createRenderer.call(this, JSON.parse(bundle))
|
||||||
|
addAppTemplate.call(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,6 +143,16 @@ function * buildFiles () {
|
|||||||
webpackRunClient.call(this),
|
webpackRunClient.call(this),
|
||||||
webpackRunServer.call(this)
|
webpackRunServer.call(this)
|
||||||
]
|
]
|
||||||
|
addAppTemplate.call(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAppTemplate () {
|
||||||
|
let templatePath = resolve(this.dir, '.nuxt', 'dist', 'index.html')
|
||||||
|
if (fs.existsSync(templatePath)) {
|
||||||
|
this.appTemplate = _.template(fs.readFileSync(templatePath, 'utf8'), {
|
||||||
|
interpolate: /{{([\s\S]+?)}}/g
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,17 +166,8 @@ function * generateRoutesAndFiles () {
|
|||||||
if (name === 'error') return
|
if (name === 'error') return
|
||||||
layouts[name] = r(this.srcDir, file)
|
layouts[name] = r(this.srcDir, file)
|
||||||
})
|
})
|
||||||
// Generate routes based on files
|
|
||||||
const files = yield glob('pages/**/*.vue', { cwd: this.srcDir })
|
const files = yield glob('pages/**/*.vue', { cwd: this.srcDir })
|
||||||
this.routes = _.uniq(_.map(files, (file) => {
|
|
||||||
return file.replace(/^pages/, '').replace(/\.vue$/, '').replace(/\/index/g, '').replace(/_/g, ':').replace('', '/').replace(/\/{2,}/g, '/')
|
|
||||||
}))
|
|
||||||
if (typeof this.options.router.extendRoutes === 'function') {
|
|
||||||
// let the user extend the routes
|
|
||||||
this.options.router.extendRoutes(this.routes)
|
|
||||||
}
|
|
||||||
// Interpret and move template files to .nuxt/
|
// Interpret and move template files to .nuxt/
|
||||||
debug('Generating files...')
|
|
||||||
let templatesFiles = [
|
let templatesFiles = [
|
||||||
'App.vue',
|
'App.vue',
|
||||||
'client.js',
|
'client.js',
|
||||||
@ -191,21 +197,29 @@ function * generateRoutesAndFiles () {
|
|||||||
middleware: this.options.middleware,
|
middleware: this.options.middleware,
|
||||||
store: this.options.store,
|
store: this.options.store,
|
||||||
css: this.options.css,
|
css: this.options.css,
|
||||||
plugins: this.options.plugins.map((p) => r(this.srcDir, p)),
|
plugins: this.options.plugins.map((p) => {
|
||||||
|
if (typeof p === 'string') {
|
||||||
|
return { src: r(this.srcDir, p), ssr: true }
|
||||||
|
}
|
||||||
|
return { src: r(this.srcDir, p.src), ssr: !!p.ssr }
|
||||||
|
}),
|
||||||
appPath: './App.vue',
|
appPath: './App.vue',
|
||||||
layouts: layouts,
|
layouts: layouts,
|
||||||
loading: (typeof this.options.loading === 'string' ? r(this.srcDir, this.options.loading) : this.options.loading),
|
loading: (typeof this.options.loading === 'string' ? r(this.srcDir, this.options.loading) : this.options.loading),
|
||||||
transition: this.options.transition,
|
transition: this.options.transition,
|
||||||
components: {
|
components: {
|
||||||
ErrorPage: './nuxt-error.vue'
|
ErrorPage: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Format routes for the lib/app/router.js template
|
// Format routes for the lib/app/router.js template
|
||||||
templateVars.router.routes = createRoutes(files, this.srcDir)
|
templateVars.router.routes = createRoutes(files, this.srcDir)
|
||||||
if (typeof this.options.router.extendRoutes === 'function') {
|
if (typeof this.options.router.extendRoutes === 'function') {
|
||||||
// let the user extend the routes
|
// let the user extend the routes
|
||||||
this.options.router.extendRoutes(templateVars.router.routes)
|
this.options.router.extendRoutes(templateVars.router.routes, r)
|
||||||
}
|
}
|
||||||
|
// Routes for Generate command
|
||||||
|
this.routes = flatRoutes(templateVars.router.routes)
|
||||||
|
debug('Generating files...')
|
||||||
if (layoutsFiles.includes('layouts/error.vue')) {
|
if (layoutsFiles.includes('layouts/error.vue')) {
|
||||||
templateVars.components.ErrorPage = r(this.srcDir, 'layouts/error.vue')
|
templateVars.components.ErrorPage = r(this.srcDir, 'layouts/error.vue')
|
||||||
}
|
}
|
||||||
@ -325,6 +339,19 @@ function cleanChildrenRoutes (routes, isChild = false) {
|
|||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function flatRoutes (router, path = '', routes = []) {
|
||||||
|
router.forEach((r) => {
|
||||||
|
if (!r.path.includes(':') && !r.path.includes('*')) {
|
||||||
|
if (r.children) {
|
||||||
|
flatRoutes(r.children, path + r.path + '/', routes)
|
||||||
|
} else {
|
||||||
|
routes.push((r.path === '' && path[path.length - 1] === '/' ? path.slice(0, -1) : path) + r.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return routes
|
||||||
|
}
|
||||||
|
|
||||||
function getWebpackClientConfig () {
|
function getWebpackClientConfig () {
|
||||||
return clientWebpackConfig.call(this)
|
return clientWebpackConfig.call(this)
|
||||||
}
|
}
|
||||||
@ -335,27 +362,11 @@ function getWebpackServerConfig () {
|
|||||||
|
|
||||||
function createWebpackMiddleware () {
|
function createWebpackMiddleware () {
|
||||||
const clientConfig = getWebpackClientConfig.call(this)
|
const clientConfig = getWebpackClientConfig.call(this)
|
||||||
const host = process.env.HOST || '127.0.0.1'
|
|
||||||
const port = process.env.PORT || '3000'
|
|
||||||
// setup on the fly compilation + hot-reload
|
// setup on the fly compilation + hot-reload
|
||||||
clientConfig.entry.app = _.flatten(['webpack-hot-middleware/client?reload=true', clientConfig.entry.app])
|
clientConfig.entry.app = _.flatten(['webpack-hot-middleware/client?reload=true', clientConfig.entry.app])
|
||||||
clientConfig.plugins.push(
|
clientConfig.plugins.push(
|
||||||
new webpack.HotModuleReplacementPlugin(),
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
new webpack.NoEmitOnErrorsPlugin(),
|
new webpack.NoEmitOnErrorsPlugin()
|
||||||
new PostCompilePlugin(stats => {
|
|
||||||
process.stdout.write('\x1Bc')
|
|
||||||
|
|
||||||
if (stats.hasErrors() || stats.hasWarnings()) {
|
|
||||||
console.log(stats.toString('errors-only')) // eslint-disable-line no-console
|
|
||||||
console.log() // eslint-disable-line no-console
|
|
||||||
console.log(chalk.bgRed.black(' ERROR '), 'Compiling failed!') // eslint-disable-line no-console
|
|
||||||
} else {
|
|
||||||
console.log(stats.toString(webpackStats)) // eslint-disable-line no-console
|
|
||||||
console.log(chalk.bold(`\n> Open http://${host}:${port}\n`)) // eslint-disable-line no-console
|
|
||||||
console.log(chalk.bgGreen.black(' DONE '), 'Compiled successfully!') // eslint-disable-line no-console
|
|
||||||
}
|
|
||||||
console.log() // eslint-disable-line no-console
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
const clientCompiler = webpack(clientConfig)
|
const clientCompiler = webpack(clientConfig)
|
||||||
// Add the middleware to the instance context
|
// Add the middleware to the instance context
|
||||||
@ -368,6 +379,16 @@ function createWebpackMiddleware () {
|
|||||||
this.webpackHotMiddleware = pify(require('webpack-hot-middleware')(clientCompiler, {
|
this.webpackHotMiddleware = pify(require('webpack-hot-middleware')(clientCompiler, {
|
||||||
log: () => {}
|
log: () => {}
|
||||||
}))
|
}))
|
||||||
|
clientCompiler.plugin('done', () => {
|
||||||
|
const fs = this.webpackDevMiddleware.fileSystem
|
||||||
|
const filePath = join(clientConfig.output.path, 'index.html')
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
const template = fs.readFileSync(filePath, 'utf-8')
|
||||||
|
this.appTemplate = _.template(template, {
|
||||||
|
interpolate: /{{([\s\S]+?)}}/g
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function webpackWatchAndUpdate () {
|
function webpackWatchAndUpdate () {
|
||||||
@ -375,11 +396,11 @@ function webpackWatchAndUpdate () {
|
|||||||
const mfs = new MFS()
|
const mfs = new MFS()
|
||||||
const serverConfig = getWebpackServerConfig.call(this)
|
const serverConfig = getWebpackServerConfig.call(this)
|
||||||
const serverCompiler = webpack(serverConfig)
|
const serverCompiler = webpack(serverConfig)
|
||||||
const outputPath = join(serverConfig.output.path, serverConfig.output.filename)
|
const outputPath = join(serverConfig.output.path, 'server-bundle.json')
|
||||||
serverCompiler.outputFileSystem = mfs
|
serverCompiler.outputFileSystem = mfs
|
||||||
this.webpackServerWatcher = serverCompiler.watch({}, (err) => {
|
this.webpackServerWatcher = serverCompiler.watch({}, (err) => {
|
||||||
if (err) throw err
|
if (err) throw err
|
||||||
createRenderer.call(this, mfs.readFileSync(outputPath, 'utf-8'))
|
createRenderer.call(this, JSON.parse(mfs.readFileSync(outputPath, 'utf-8')))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,7 +411,7 @@ function webpackRunClient () {
|
|||||||
clientCompiler.run((err, stats) => {
|
clientCompiler.run((err, stats) => {
|
||||||
if (err) return reject(err)
|
if (err) return reject(err)
|
||||||
console.log('[nuxt:build:client]\n', stats.toString(webpackStats)) // eslint-disable-line no-console
|
console.log('[nuxt:build:client]\n', stats.toString(webpackStats)) // eslint-disable-line no-console
|
||||||
if (stats.hasErrors()) return reject('Webpack build exited with errors')
|
if (stats.hasErrors()) return reject(new Error('Webpack build exited with errors'))
|
||||||
resolve()
|
resolve()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -403,11 +424,11 @@ function webpackRunServer () {
|
|||||||
serverCompiler.run((err, stats) => {
|
serverCompiler.run((err, stats) => {
|
||||||
if (err) return reject(err)
|
if (err) return reject(err)
|
||||||
console.log('[nuxt:build:server]\n', stats.toString(webpackStats)) // eslint-disable-line no-console
|
console.log('[nuxt:build:server]\n', stats.toString(webpackStats)) // eslint-disable-line no-console
|
||||||
if (stats.hasErrors()) return reject('Webpack build exited with errors')
|
if (stats.hasErrors()) return reject(new Error('Webpack build exited with errors'))
|
||||||
const bundlePath = join(serverConfig.output.path, serverConfig.output.filename)
|
const bundlePath = join(serverConfig.output.path, 'server-bundle.json')
|
||||||
readFile(bundlePath, 'utf8')
|
readFile(bundlePath, 'utf8')
|
||||||
.then((bundle) => {
|
.then((bundle) => {
|
||||||
createRenderer.call(this, bundle)
|
createRenderer.call(this, JSON.parse(bundle))
|
||||||
resolve()
|
resolve()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
107
lib/generate.js
107
lib/generate.js
@ -1,14 +1,13 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const debug = require('debug')('nuxt:generate')
|
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import co from 'co'
|
import co from 'co'
|
||||||
import pify from 'pify'
|
import pify from 'pify'
|
||||||
import pathToRegexp from 'path-to-regexp'
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { resolve, join, dirname, sep } from 'path'
|
import { resolve, join, dirname, sep } from 'path'
|
||||||
import { promisifyRouteParams } from './utils'
|
import { isUrl, promisifyRoute } from './utils'
|
||||||
import { minify } from 'html-minifier'
|
import { minify } from 'html-minifier'
|
||||||
|
const debug = require('debug')('nuxt:generate')
|
||||||
const copy = pify(fs.copy)
|
const copy = pify(fs.copy)
|
||||||
const remove = pify(fs.remove)
|
const remove = pify(fs.remove)
|
||||||
const writeFile = pify(fs.writeFile)
|
const writeFile = pify(fs.writeFile)
|
||||||
@ -16,21 +15,32 @@ const mkdirp = pify(fs.mkdirp)
|
|||||||
|
|
||||||
const defaults = {
|
const defaults = {
|
||||||
dir: 'dist',
|
dir: 'dist',
|
||||||
routeParams: {}
|
routes: [],
|
||||||
|
minify: {
|
||||||
|
collapseBooleanAttributes: true,
|
||||||
|
collapseWhitespace: true,
|
||||||
|
decodeEntities: true,
|
||||||
|
minifyCSS: true,
|
||||||
|
minifyJS: true,
|
||||||
|
processConditionalComments: true,
|
||||||
|
removeAttributeQuotes: false,
|
||||||
|
removeComments: false,
|
||||||
|
removeEmptyAttributes: true,
|
||||||
|
removeOptionalTags: true,
|
||||||
|
removeRedundantAttributes: true,
|
||||||
|
removeScriptTypeAttributes: false,
|
||||||
|
removeStyleLinkTypeAttributes: false,
|
||||||
|
removeTagWhitespace: false,
|
||||||
|
sortAttributes: true,
|
||||||
|
sortClassName: true,
|
||||||
|
trimCustomFragments: true,
|
||||||
|
useShortDoctype: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
const s = Date.now()
|
const s = Date.now()
|
||||||
/*
|
/*
|
||||||
** Update loaders config to add router.base path
|
|
||||||
*/
|
|
||||||
// this.options.build.loaders.forEach((config) => {
|
|
||||||
// if (['file', 'url', 'file-loader', 'url-loader'].includes(config.loader)) {
|
|
||||||
// config.query = config.query || {}
|
|
||||||
// config.query.publicPath = urlJoin(this.options.router.base, '/_nuxt/')
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
/*
|
|
||||||
** Set variables
|
** Set variables
|
||||||
*/
|
*/
|
||||||
this.options.generate = _.defaultsDeep(this.options.generate, defaults)
|
this.options.generate = _.defaultsDeep(this.options.generate, defaults)
|
||||||
@ -38,7 +48,7 @@ export default function () {
|
|||||||
var srcStaticPath = resolve(this.srcDir, 'static')
|
var srcStaticPath = resolve(this.srcDir, 'static')
|
||||||
var srcBuiltPath = resolve(this.dir, '.nuxt', 'dist')
|
var srcBuiltPath = resolve(this.dir, '.nuxt', 'dist')
|
||||||
var distPath = resolve(this.dir, this.options.generate.dir)
|
var distPath = resolve(this.dir, this.options.generate.dir)
|
||||||
var distNuxtPath = resolve(distPath, '_nuxt')
|
var distNuxtPath = join(distPath, (isUrl(this.options.build.publicPath) ? '_nuxt' : this.options.build.publicPath))
|
||||||
return co(function * () {
|
return co(function * () {
|
||||||
/*
|
/*
|
||||||
** Launch build process
|
** Launch build process
|
||||||
@ -61,55 +71,31 @@ export default function () {
|
|||||||
debug('Static & build files copied')
|
debug('Static & build files copied')
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Resolve config.generate.routesParams promises before generating the routes
|
// Resolve config.generate.routes promises before generating the routes
|
||||||
return resolveRouteParams(this.options.generate.routeParams)
|
return promisifyRoute(this.options.generate.routes || [])
|
||||||
|
.catch((e) => {
|
||||||
|
console.error('Could not resolve routes') // eslint-disable-line no-console
|
||||||
|
console.error(e) // eslint-disable-line no-console
|
||||||
|
process.exit(1)
|
||||||
|
throw e // eslint-disable-line no-unreachable
|
||||||
})
|
})
|
||||||
.then(() => {
|
})
|
||||||
|
.then((generateRoutes) => {
|
||||||
/*
|
/*
|
||||||
** Generate html files from routes
|
** Generate html files from routes
|
||||||
*/
|
*/
|
||||||
let routes = []
|
generateRoutes.forEach((route) => {
|
||||||
this.routes.forEach((route) => {
|
if (this.routes.indexOf(route) < 0) {
|
||||||
if (route.includes(':') || route.includes('*')) {
|
this.routes.push(route)
|
||||||
const routeParams = this.options.generate.routeParams[route]
|
|
||||||
if (!routeParams) {
|
|
||||||
console.error(`Could not generate the dynamic route ${route}, please add the mapping params in nuxt.config.js (generate.routeParams).`) // eslint-disable-line no-console
|
|
||||||
return process.exit(1)
|
|
||||||
}
|
|
||||||
route = route + '?'
|
|
||||||
const toPath = pathToRegexp.compile(route)
|
|
||||||
routes = routes.concat(routeParams.map((params) => {
|
|
||||||
return toPath(params)
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
routes.push(route)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
let routes = this.routes
|
||||||
return co(function * () {
|
return co(function * () {
|
||||||
while (routes.length) {
|
while (routes.length) {
|
||||||
yield routes.splice(0, 500).map((route) => {
|
yield routes.splice(0, 500).map((route) => {
|
||||||
return co(function * () {
|
return co(function * () {
|
||||||
var { html } = yield self.renderRoute(route, { _generate: true })
|
var { html } = yield self.renderRoute(route, { _generate: true })
|
||||||
html = minify(html, {
|
html = minify(html, self.options.generate.minify)
|
||||||
collapseBooleanAttributes: true,
|
|
||||||
collapseWhitespace: true,
|
|
||||||
decodeEntities: true,
|
|
||||||
minifyCSS: true,
|
|
||||||
minifyJS: true,
|
|
||||||
processConditionalComments: true,
|
|
||||||
removeAttributeQuotes: false,
|
|
||||||
removeComments: false,
|
|
||||||
removeEmptyAttributes: true,
|
|
||||||
removeOptionalTags: true,
|
|
||||||
removeRedundantAttributes: true,
|
|
||||||
removeScriptTypeAttributes: true,
|
|
||||||
removeStyleLinkTypeAttributes: true,
|
|
||||||
removeTagWhitespace: true,
|
|
||||||
sortAttributes: true,
|
|
||||||
sortClassName: true,
|
|
||||||
trimCustomFragments: true,
|
|
||||||
useShortDoctype: true
|
|
||||||
})
|
|
||||||
var path = join(route, sep, 'index.html') // /about -> /about/index.html
|
var path = join(route, sep, 'index.html') // /about -> /about/index.html
|
||||||
debug('Generate file: ' + path)
|
debug('Generate file: ' + path)
|
||||||
path = join(distPath, path)
|
path = join(distPath, path)
|
||||||
@ -133,20 +119,3 @@ export default function () {
|
|||||||
return this
|
return this
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveRouteParams (routeParams) {
|
|
||||||
let promises = []
|
|
||||||
Object.keys(routeParams).forEach(function (routePath) {
|
|
||||||
let promise = promisifyRouteParams(routeParams[routePath])
|
|
||||||
promise.then((routeParamsData) => {
|
|
||||||
routeParams[routePath] = routeParamsData
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.error(`Could not resolve routeParams[${routePath}]`) // eslint-disable-line no-console
|
|
||||||
console.error(e) // eslint-disable-line no-console
|
|
||||||
process.exit(1)
|
|
||||||
})
|
|
||||||
promises.push(promise)
|
|
||||||
})
|
|
||||||
return Promise.all(promises)
|
|
||||||
}
|
|
||||||
|
40
lib/nuxt.js
40
lib/nuxt.js
@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import co from 'co'
|
import co from 'co'
|
||||||
|
import compression from 'compression'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import pify from 'pify'
|
import pify from 'pify'
|
||||||
import ansiHTML from 'ansi-html'
|
|
||||||
import serialize from 'serialize-javascript'
|
|
||||||
import Server from './server'
|
import Server from './server'
|
||||||
import * as build from './build'
|
import * as build from './build'
|
||||||
import * as render from './render'
|
import * as render from './render'
|
||||||
@ -13,10 +12,8 @@ import generate from './generate'
|
|||||||
import serveStatic from 'serve-static'
|
import serveStatic from 'serve-static'
|
||||||
import { resolve, join } from 'path'
|
import { resolve, join } from 'path'
|
||||||
import * as utils from './utils'
|
import * as utils from './utils'
|
||||||
utils.setAnsiColors(ansiHTML)
|
|
||||||
|
|
||||||
class Nuxt {
|
class Nuxt {
|
||||||
|
|
||||||
constructor (options = {}) {
|
constructor (options = {}) {
|
||||||
var defaults = {
|
var defaults = {
|
||||||
dev: true,
|
dev: true,
|
||||||
@ -43,6 +40,10 @@ class Nuxt {
|
|||||||
extendRoutes: null,
|
extendRoutes: null,
|
||||||
scrollBehavior: null
|
scrollBehavior: null
|
||||||
},
|
},
|
||||||
|
performance: {
|
||||||
|
gzip: true,
|
||||||
|
prefetch: true
|
||||||
|
},
|
||||||
build: {}
|
build: {}
|
||||||
}
|
}
|
||||||
// Sanitization
|
// Sanitization
|
||||||
@ -63,28 +64,34 @@ class Nuxt {
|
|||||||
if (fs.existsSync(join(this.srcDir, 'middleware'))) {
|
if (fs.existsSync(join(this.srcDir, 'middleware'))) {
|
||||||
this.options.middleware = true
|
this.options.middleware = true
|
||||||
}
|
}
|
||||||
// Template
|
// If app.html is defined, set the template path to the user template
|
||||||
this.appTemplate = _.template(fs.readFileSync(resolve(__dirname, 'views', 'app.html'), 'utf8'), {
|
this.options.appTemplatePath = resolve(__dirname, 'views/app.template.html')
|
||||||
imports: { serialize }
|
if (fs.existsSync(join(this.srcDir, 'app.html'))) {
|
||||||
})
|
this.options.appTemplatePath = join(this.srcDir, 'app.html')
|
||||||
this.errorTemplate = _.template(fs.readFileSync(resolve(__dirname, 'views', 'error.html'), 'utf8'), {
|
|
||||||
imports: {
|
|
||||||
ansiHTML,
|
|
||||||
encodeHtml: utils.encodeHtml
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
// renderer used by Vue.js (via createBundleRenderer)
|
// renderer used by Vue.js (via createBundleRenderer)
|
||||||
this.renderer = null
|
this.renderer = null
|
||||||
// For serving static/ files to /
|
// For serving static/ files to /
|
||||||
this.serveStatic = pify(serveStatic(resolve(this.srcDir, 'static')))
|
this.serveStatic = pify(serveStatic(resolve(this.srcDir, 'static')))
|
||||||
// For serving .nuxt/dist/ files
|
// For serving .nuxt/dist/ files (only when build.publicPath is not an URL)
|
||||||
this._nuxtRegexp = /^\/_nuxt\//
|
this.serveStaticNuxt = pify(serveStatic(resolve(this.dir, '.nuxt', 'dist'), {
|
||||||
this.serveStaticNuxt = pify(serveStatic(resolve(this.dir, '.nuxt', 'dist')))
|
maxAge: (this.dev ? 0 : '1y') // 1 year in production
|
||||||
|
}))
|
||||||
|
// gzip for production
|
||||||
|
if (!this.dev) {
|
||||||
|
this.gzipMiddleware = pify(compression({
|
||||||
|
threshold: 0
|
||||||
|
}))
|
||||||
|
}
|
||||||
// Add this.Server Class
|
// Add this.Server Class
|
||||||
this.Server = Server
|
this.Server = Server
|
||||||
// Add this.build
|
// Add this.build
|
||||||
build.options.call(this) // Add build options
|
build.options.call(this) // Add build options
|
||||||
this.build = () => co(build.build.bind(this))
|
this.build = () => co(build.build.bind(this))
|
||||||
|
// Error template
|
||||||
|
this.errorTemplate = _.template(fs.readFileSync(resolve(__dirname, 'views', 'error.html'), 'utf8'), {
|
||||||
|
interpolate: /{{([\s\S]+?)}}/g
|
||||||
|
})
|
||||||
// Add this.render and this.renderRoute
|
// Add this.render and this.renderRoute
|
||||||
this.render = render.render.bind(this)
|
this.render = render.render.bind(this)
|
||||||
this.renderRoute = render.renderRoute.bind(this)
|
this.renderRoute = render.renderRoute.bind(this)
|
||||||
@ -123,7 +130,6 @@ class Nuxt {
|
|||||||
if (typeof callback === 'function') callback()
|
if (typeof callback === 'function') callback()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Nuxt
|
export default Nuxt
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const debug = require('debug')('nuxt:render')
|
import ansiHTML from 'ansi-html'
|
||||||
import co from 'co'
|
import co from 'co'
|
||||||
import { urlJoin, getContext } from './utils'
|
import serialize from 'serialize-javascript'
|
||||||
|
import { getContext, setAnsiColors, encodeHtml } from './utils'
|
||||||
|
const debug = require('debug')('nuxt:render')
|
||||||
// force blue color
|
// force blue color
|
||||||
debug.color = 4
|
debug.color = 4
|
||||||
|
setAnsiColors(ansiHTML)
|
||||||
|
|
||||||
export function render (req, res) {
|
export function render (req, res) {
|
||||||
if (!this.renderer && !this.dev) {
|
if (!this.renderer && !this.dev) {
|
||||||
@ -12,20 +15,25 @@ export function render (req, res) {
|
|||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
if (!this.renderer) {
|
if (!this.renderer || !this.appTemplate) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.render(req, res)
|
resolve(this.render(req, res))
|
||||||
}, 1000)
|
}, 1000)
|
||||||
return
|
})
|
||||||
}
|
}
|
||||||
const self = this
|
const self = this
|
||||||
const context = getContext(req, res)
|
const context = getContext(req, res)
|
||||||
return co(function * () {
|
return co(function * () {
|
||||||
|
res.statusCode = 200
|
||||||
if (self.dev) {
|
if (self.dev) {
|
||||||
// Call webpack middleware only in development
|
// Call webpack middleware only in development
|
||||||
yield self.webpackDevMiddleware(req, res)
|
yield self.webpackDevMiddleware(req, res)
|
||||||
yield self.webpackHotMiddleware(req, res)
|
yield self.webpackHotMiddleware(req, res)
|
||||||
}
|
}
|
||||||
|
if (!self.dev && self.options.performance.gzip === true) {
|
||||||
|
yield self.gzipMiddleware(req, res)
|
||||||
|
}
|
||||||
// If base in req.url, remove it for the middleware and vue-router
|
// If base in req.url, remove it for the middleware and vue-router
|
||||||
if (self.options.router.base !== '/' && req.url.indexOf(self.options.router.base) === 0) {
|
if (self.options.router.base !== '/' && req.url.indexOf(self.options.router.base) === 0) {
|
||||||
// Compatibility with base url for dev server
|
// Compatibility with base url for dev server
|
||||||
@ -34,9 +42,9 @@ export function render (req, res) {
|
|||||||
// Serve static/ files
|
// Serve static/ files
|
||||||
yield self.serveStatic(req, res)
|
yield self.serveStatic(req, res)
|
||||||
// Serve .nuxt/dist/ files (only for production)
|
// Serve .nuxt/dist/ files (only for production)
|
||||||
if (!self.dev && self._nuxtRegexp.test(req.url)) {
|
if (!self.dev && req.url.indexOf(self.options.build.publicPath) === 0) {
|
||||||
const url = req.url
|
const url = req.url
|
||||||
req.url = req.url.replace(self._nuxtRegexp, '/')
|
req.url = req.url.replace(self.options.build.publicPath, '/')
|
||||||
yield self.serveStaticNuxt(req, res)
|
yield self.serveStaticNuxt(req, res)
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
req.url = url
|
req.url = url
|
||||||
@ -44,23 +52,34 @@ export function render (req, res) {
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
if (this.dev && this._nuxtRegexp.test(req.url) && req.url.includes('.hot-update.json')) {
|
if (this.dev && req.url.indexOf(self.options.build.publicPath) === 0 && req.url.includes('.hot-update.json')) {
|
||||||
res.statusCode = 404
|
res.statusCode = 404
|
||||||
return res.end()
|
return { html: '' }
|
||||||
}
|
}
|
||||||
return this.renderRoute(req.url, context)
|
return this.renderRoute(req.url, context)
|
||||||
})
|
})
|
||||||
.then(({ html, error }) => {
|
.then(({ html, error, redirected }) => {
|
||||||
|
if (redirected) {
|
||||||
|
return html
|
||||||
|
}
|
||||||
if (error) {
|
if (error) {
|
||||||
res.statusCode = context.nuxt.error.statusCode || 500
|
res.statusCode = context.nuxt.error.statusCode || 500
|
||||||
}
|
}
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
||||||
res.setHeader('Content-Length', Buffer.byteLength(html))
|
res.setHeader('Content-Length', Buffer.byteLength(html))
|
||||||
res.end(html, 'utf8')
|
res.end(html, 'utf8')
|
||||||
|
return html
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
const html = this.errorTemplate({
|
||||||
|
error: err,
|
||||||
|
stack: ansiHTML(encodeHtml(err.stack))
|
||||||
|
})
|
||||||
res.statusCode = 500
|
res.statusCode = 500
|
||||||
res.end(this.errorTemplate({ err }), 'utf8')
|
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
||||||
|
res.setHeader('Content-Length', Buffer.byteLength(html))
|
||||||
|
res.end(html, 'utf8')
|
||||||
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,20 +91,22 @@ export function renderRoute (url, context = {}) {
|
|||||||
// Call rendertoSting from the bundleRenderer and generate the HTML (will update the context as well)
|
// Call rendertoSting from the bundleRenderer and generate the HTML (will update the context as well)
|
||||||
const self = this
|
const self = this
|
||||||
return co(function * () {
|
return co(function * () {
|
||||||
let app = yield self.renderToString(context)
|
let APP = yield self.renderToString(context)
|
||||||
if (!context.nuxt.serverRendered) {
|
if (!context.nuxt.serverRendered) {
|
||||||
app = '<div id="__nuxt"></div>'
|
APP = '<div id="__nuxt"></div>'
|
||||||
}
|
}
|
||||||
|
const m = context.meta.inject()
|
||||||
|
let HEAD = m.meta.text() + m.title.text() + m.link.text() + m.style.text() + m.script.text() + m.noscript.text()
|
||||||
|
if (self.options.router.base !== '/') {
|
||||||
|
HEAD += `<base href="${self.options.router.base}">`
|
||||||
|
}
|
||||||
|
HEAD += context.styles
|
||||||
|
APP += `<script type="text/javascript">window.__NUXT__=${serialize(context.nuxt, { isJSON: true })}</script>`
|
||||||
const html = self.appTemplate({
|
const html = self.appTemplate({
|
||||||
dev: self.dev, // Use to add the extracted CSS <link> in production
|
HTML_ATTRS: 'data-n-head-ssr ' + m.htmlAttrs.text(),
|
||||||
baseUrl: self.options.router.base,
|
BODY_ATTRS: m.bodyAttrs.text(),
|
||||||
APP: app,
|
HEAD,
|
||||||
context: context,
|
APP
|
||||||
files: {
|
|
||||||
css: urlJoin(self.options.router.base, '/_nuxt/', self.options.build.filenames.css),
|
|
||||||
vendor: urlJoin(self.options.router.base, '/_nuxt/', self.options.build.filenames.vendor),
|
|
||||||
app: urlJoin(self.options.router.base, '/_nuxt/', self.options.build.filenames.app)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
html,
|
html,
|
||||||
@ -128,10 +149,9 @@ export function renderAndGetWindow (url, opts = {}) {
|
|||||||
window.scrollTo = function () {}
|
window.scrollTo = function () {}
|
||||||
// If Nuxt could not be loaded (error from the server-side)
|
// If Nuxt could not be loaded (error from the server-side)
|
||||||
if (!window.__NUXT__) {
|
if (!window.__NUXT__) {
|
||||||
return reject({
|
let error = new Error('Could not load the nuxt app')
|
||||||
message: 'Could not load the nuxt app',
|
error.body = window.document.getElementsByTagName('body')[0].innerHTML
|
||||||
body: window.document.getElementsByTagName('body')[0].innerHTML
|
return reject(error)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
// Used by nuxt.js to say when the components are loaded and the app ready
|
// Used by nuxt.js to say when the components are loaded and the app ready
|
||||||
window.onNuxtReady(() => {
|
window.onNuxtReady(() => {
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
const http = require('http')
|
const http = require('http')
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
|
|
||||||
constructor (nuxt) {
|
constructor (nuxt) {
|
||||||
this.nuxt = nuxt
|
this.nuxt = nuxt
|
||||||
this.server = http.createServer(this.render.bind(this))
|
this.server = http.createServer(this.render.bind(this))
|
||||||
@ -27,7 +26,6 @@ class Server {
|
|||||||
close (cb) {
|
close (cb) {
|
||||||
return this.server.close(cb)
|
return this.server.close(cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Server
|
export default Server
|
||||||
|
12
lib/utils.js
12
lib/utils.js
@ -28,15 +28,19 @@ export function * waitFor (ms) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function urlJoin () {
|
export function urlJoin () {
|
||||||
return [].slice.call(arguments).join('/').replace(/\/+/g, '/')
|
return [].slice.call(arguments).join('/').replace(/\/+/g, '/').replace(':/', '://')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function promisifyRouteParams (fn) {
|
export function isUrl (url) {
|
||||||
// If routeParams[route] is an array
|
return (url.indexOf('http') === 0 || url.indexOf('//') === 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function promisifyRoute (fn) {
|
||||||
|
// If routes is an array
|
||||||
if (Array.isArray(fn)) {
|
if (Array.isArray(fn)) {
|
||||||
return Promise.resolve(fn)
|
return Promise.resolve(fn)
|
||||||
}
|
}
|
||||||
// If routeParams[route] is a function expecting a callback
|
// If routes is a function expecting a callback
|
||||||
if (fn.length === 1) {
|
if (fn.length === 1) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fn(function (err, routeParams) {
|
fn(function (err, routeParams) {
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
<% var m = context.meta.inject() %><!DOCTYPE html>
|
|
||||||
<html n-head-ssr <%= m.htmlAttrs.text() %>>
|
|
||||||
<head>
|
|
||||||
<%= m.meta.text() %>
|
|
||||||
<%= m.title.text() %>
|
|
||||||
<%= m.link.text() %>
|
|
||||||
<%= m.style.text() %>
|
|
||||||
<%= m.script.text() %>
|
|
||||||
<%= m.noscript.text() %>
|
|
||||||
<% if (baseUrl !== '/') { %><base href="<%= baseUrl %>"><% } %>
|
|
||||||
<% if (!dev) { %><link rel="stylesheet" href="<%= files.css %>"><% } %>
|
|
||||||
</head>
|
|
||||||
<body <%= m.bodyAttrs.text() %>>
|
|
||||||
<%= APP %>
|
|
||||||
<script type="text/javascript" defer>window.__NUXT__=<%= serialize(context.nuxt, { isJSON: true }) %></script>
|
|
||||||
<script src="<%= files.vendor %>" defer></script>
|
|
||||||
<script src="<%= files.app %>" defer></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
9
lib/views/app.template.html
Normal file
9
lib/views/app.template.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html {{ HTML_ATTRS }}>
|
||||||
|
<head>
|
||||||
|
{{ HEAD }}
|
||||||
|
</head>
|
||||||
|
<body {{ BODY_ATTRS }}>
|
||||||
|
{{ APP }}
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -2,10 +2,10 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Vue.js error</title>
|
<title>Nuxt.js error</title>
|
||||||
</head>
|
</head>
|
||||||
<body style="background-color: #a6004c;color: #efe;font-family: monospace;">
|
<body style="background-color: #a6004c;color: #efe;font-family: monospace;">
|
||||||
<h2>Vue.js error</h2>
|
<h2>Nuxt.js error</h2>
|
||||||
<pre><%= ansiHTML(encodeHtml(err.stack)) %></pre>
|
<pre>{{ stack }}</pre>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import vueLoaderConfig from './vue-loader.config'
|
import vueLoaderConfig from './vue-loader.config'
|
||||||
import { defaults } from 'lodash'
|
import { defaults } from 'lodash'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { urlJoin } from '../utils'
|
import { isUrl, urlJoin } from '../utils'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@ -16,14 +16,16 @@ import { urlJoin } from '../utils'
|
|||||||
export default function ({ isClient, isServer }) {
|
export default function ({ isClient, isServer }) {
|
||||||
const nodeModulesDir = join(__dirname, '..', 'node_modules')
|
const nodeModulesDir = join(__dirname, '..', 'node_modules')
|
||||||
let config = {
|
let config = {
|
||||||
devtool: 'source-map',
|
devtool: (this.dev ? 'cheap-module-eval-source-map' : false),
|
||||||
entry: {
|
entry: {
|
||||||
vendor: ['vue', 'vue-router', 'vue-meta']
|
vendor: ['vue', 'vue-router', 'vue-meta']
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
publicPath: urlJoin(this.options.router.base, '/_nuxt/')
|
publicPath: (isUrl(this.options.build.publicPath) ? this.options.build.publicPath : urlJoin(this.options.router.base, this.options.build.publicPath))
|
||||||
},
|
},
|
||||||
performance: {
|
performance: {
|
||||||
|
maxEntrypointSize: 300000,
|
||||||
|
maxAssetSize: 300000,
|
||||||
hints: (this.dev ? false : 'warning')
|
hints: (this.dev ? false : 'warning')
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
@ -64,16 +66,13 @@ export default function ({ isClient, isServer }) {
|
|||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
query: defaults(this.options.build.babel, {
|
query: defaults(this.options.build.babel, {
|
||||||
plugins: [
|
presets: ['vue-app'],
|
||||||
'transform-async-to-generator',
|
|
||||||
'transform-runtime'
|
|
||||||
],
|
|
||||||
presets: [
|
|
||||||
['es2015', { modules: false }],
|
|
||||||
'stage-2'
|
|
||||||
],
|
|
||||||
cacheDirectory: !!this.dev
|
cacheDirectory: !!this.dev
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
loader: 'vue-style-loader!css-loader'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
import { each } from 'lodash'
|
import { each } from 'lodash'
|
||||||
import webpack from 'webpack'
|
import webpack from 'webpack'
|
||||||
import ExtractTextPlugin from 'extract-text-webpack-plugin'
|
import HTMLPlugin from 'html-webpack-plugin'
|
||||||
|
import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin'
|
||||||
|
import ScriptExtHtmlWebpackPlugin from 'script-ext-html-webpack-plugin'
|
||||||
|
import PreloadWebpackPlugin from 'preload-webpack-plugin'
|
||||||
import ProgressBarPlugin from 'progress-bar-webpack-plugin'
|
import ProgressBarPlugin from 'progress-bar-webpack-plugin'
|
||||||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
|
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
|
||||||
import base from './base.config.js'
|
import base from './base.config.js'
|
||||||
@ -40,38 +43,55 @@ export default function () {
|
|||||||
})
|
})
|
||||||
// Webpack plugins
|
// Webpack plugins
|
||||||
config.plugins = (config.plugins || []).concat([
|
config.plugins = (config.plugins || []).concat([
|
||||||
// strip comments in Vue code
|
// Strip comments in Vue code
|
||||||
new webpack.DefinePlugin(Object.assign(env, {
|
new webpack.DefinePlugin(Object.assign(env, {
|
||||||
'process.env.NODE_ENV': JSON.stringify(this.dev ? 'development' : 'production'),
|
'process.env.NODE_ENV': JSON.stringify(this.dev ? 'development' : 'production'),
|
||||||
'process.BROWSER_BUILD': true,
|
'process.BROWSER_BUILD': true,
|
||||||
'process.SERVER_BUILD': false
|
'process.SERVER_BUILD': false,
|
||||||
|
'process.browser': true,
|
||||||
|
'process.server': true
|
||||||
})),
|
})),
|
||||||
// Extract vendor chunks for better caching
|
// Extract vendor chunks for better caching
|
||||||
new webpack.optimize.CommonsChunkPlugin({
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
name: 'vendor',
|
name: 'vendor',
|
||||||
filename: this.options.build.filenames.vendor
|
filename: this.options.build.filenames.vendor
|
||||||
|
}),
|
||||||
|
// Generate output HTML
|
||||||
|
new HTMLPlugin({
|
||||||
|
template: this.options.appTemplatePath
|
||||||
|
}),
|
||||||
|
// Add defer to scripts
|
||||||
|
new ScriptExtHtmlWebpackPlugin({
|
||||||
|
defaultAttribute: 'defer'
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
|
|
||||||
|
if (!this.dev && this.options.performance.prefetch === true) {
|
||||||
|
// Add prefetch code-splitted routes
|
||||||
|
config.plugins.push(
|
||||||
|
new PreloadWebpackPlugin({
|
||||||
|
rel: 'prefetch'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
// client bundle progress bar
|
// client bundle progress bar
|
||||||
config.plugins.push(
|
config.plugins.push(
|
||||||
new ProgressBarPlugin()
|
new ProgressBarPlugin()
|
||||||
)
|
)
|
||||||
|
// Add friendly error plugin
|
||||||
|
if (this.dev) {
|
||||||
|
config.plugins.push(new FriendlyErrorsWebpackPlugin())
|
||||||
|
}
|
||||||
// Production client build
|
// Production client build
|
||||||
if (!this.dev) {
|
if (!this.dev) {
|
||||||
config.plugins.push(
|
config.plugins.push(
|
||||||
// Use ExtractTextPlugin to extract CSS into a single file
|
|
||||||
new ExtractTextPlugin({
|
|
||||||
filename: this.options.build.filenames.css,
|
|
||||||
allChunks: true
|
|
||||||
}),
|
|
||||||
// This is needed in webpack 2 for minifying CSS
|
// This is needed in webpack 2 for minifying CSS
|
||||||
new webpack.LoaderOptionsPlugin({
|
new webpack.LoaderOptionsPlugin({
|
||||||
minimize: true
|
minimize: true
|
||||||
}),
|
}),
|
||||||
// Minify JS
|
// Minify JS
|
||||||
new webpack.optimize.UglifyJsPlugin({
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
|
sourceMap: true,
|
||||||
compress: {
|
compress: {
|
||||||
warnings: false
|
warnings: false
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
import webpack from 'webpack'
|
import webpack from 'webpack'
|
||||||
|
import VueSSRPlugin from 'vue-ssr-webpack-plugin'
|
||||||
import base from './base.config.js'
|
import base from './base.config.js'
|
||||||
import { each, uniq } from 'lodash'
|
import { each, uniq } from 'lodash'
|
||||||
import { existsSync, readFileSync } from 'fs'
|
import { existsSync, readFileSync } from 'fs'
|
||||||
@ -22,7 +23,7 @@ export default function () {
|
|||||||
|
|
||||||
config = Object.assign(config, {
|
config = Object.assign(config, {
|
||||||
target: 'node',
|
target: 'node',
|
||||||
devtool: false,
|
devtool: 'source-map',
|
||||||
entry: resolve(this.dir, '.nuxt', 'server.js'),
|
entry: resolve(this.dir, '.nuxt', 'server.js'),
|
||||||
output: Object.assign({}, config.output, {
|
output: Object.assign({}, config.output, {
|
||||||
path: resolve(this.dir, '.nuxt', 'dist'),
|
path: resolve(this.dir, '.nuxt', 'dist'),
|
||||||
@ -30,14 +31,26 @@ export default function () {
|
|||||||
libraryTarget: 'commonjs2'
|
libraryTarget: 'commonjs2'
|
||||||
}),
|
}),
|
||||||
plugins: (config.plugins || []).concat([
|
plugins: (config.plugins || []).concat([
|
||||||
|
new VueSSRPlugin({
|
||||||
|
filename: 'server-bundle.json'
|
||||||
|
}),
|
||||||
new webpack.DefinePlugin(Object.assign(env, {
|
new webpack.DefinePlugin(Object.assign(env, {
|
||||||
'process.env.NODE_ENV': JSON.stringify(this.dev ? 'development' : 'production'),
|
'process.env.NODE_ENV': JSON.stringify(this.dev ? 'development' : 'production'),
|
||||||
'process.BROWSER_BUILD': false,
|
'process.BROWSER_BUILD': false, // deprecated
|
||||||
'process.SERVER_BUILD': true
|
'process.SERVER_BUILD': true, // deprecated
|
||||||
|
'process.browser': false,
|
||||||
|
'process.server': true
|
||||||
}))
|
}))
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
// This is needed in webpack 2 for minifying CSS
|
||||||
|
if (!this.dev) {
|
||||||
|
config.plugins.push(
|
||||||
|
new webpack.LoaderOptionsPlugin({
|
||||||
|
minimize: true
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
// Externals
|
// Externals
|
||||||
const nuxtPackageJson = require('../../package.json')
|
const nuxtPackageJson = require('../../package.json')
|
||||||
const projectPackageJsonPath = resolve(this.dir, 'package.json')
|
const projectPackageJsonPath = resolve(this.dir, 'package.json')
|
||||||
@ -48,6 +61,7 @@ export default function () {
|
|||||||
config.externals = config.externals.concat(Object.keys(projectPackageJson.dependencies || {}))
|
config.externals = config.externals.concat(Object.keys(projectPackageJson.dependencies || {}))
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
config.externals = config.externals.concat(this.options.build.vendor)
|
||||||
config.externals = uniq(config.externals)
|
config.externals = uniq(config.externals)
|
||||||
|
|
||||||
// Extend config
|
// Extend config
|
||||||
|
@ -4,19 +4,14 @@ import { defaults } from 'lodash'
|
|||||||
|
|
||||||
export default function ({ isClient }) {
|
export default function ({ isClient }) {
|
||||||
let babelOptions = JSON.stringify(defaults(this.options.build.babel, {
|
let babelOptions = JSON.stringify(defaults(this.options.build.babel, {
|
||||||
plugins: [
|
presets: ['vue-app'],
|
||||||
'transform-async-to-generator',
|
cacheDirectory: !!this.dev
|
||||||
'transform-runtime'
|
|
||||||
],
|
|
||||||
presets: [
|
|
||||||
['es2015', { modules: false }],
|
|
||||||
'stage-2'
|
|
||||||
]
|
|
||||||
}))
|
}))
|
||||||
let config = {
|
let config = {
|
||||||
postcss: this.options.build.postcss,
|
postcss: this.options.build.postcss,
|
||||||
loaders: {
|
loaders: {
|
||||||
'js': 'babel-loader?' + babelOptions,
|
'js': 'babel-loader?' + babelOptions,
|
||||||
|
'css': 'vue-style-loader!css-loader',
|
||||||
'less': 'vue-style-loader!css-loader!less-loader',
|
'less': 'vue-style-loader!css-loader!less-loader',
|
||||||
'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax',
|
'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax',
|
||||||
'scss': 'vue-style-loader!css-loader!sass-loader',
|
'scss': 'vue-style-loader!css-loader!sass-loader',
|
||||||
@ -25,17 +20,6 @@ export default function ({ isClient }) {
|
|||||||
},
|
},
|
||||||
preserveWhitespace: false
|
preserveWhitespace: false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.dev && isClient) {
|
|
||||||
// Use ExtractTextPlugin to extract CSS into a single file
|
|
||||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
|
||||||
config.loaders.css = ExtractTextPlugin.extract({ loader: 'css-loader' })
|
|
||||||
config.loaders.scss = ExtractTextPlugin.extract({ loader: 'css-loader!sass-loader', fallbackLoader: 'vue-style-loader' })
|
|
||||||
config.loaders.sass = ExtractTextPlugin.extract({ loader: 'css-loader!sass-loader?indentedSyntax', fallbackLoader: 'vue-style-loader' })
|
|
||||||
config.loaders.stylus = ExtractTextPlugin.extract({ loader: 'css-loader!stylus-loader', fallbackLoader: 'vue-style-loader' })
|
|
||||||
config.loaders.less = ExtractTextPlugin.extract({ loader: 'css-loader!less-loader', fallbackLoader: 'vue-style-loader' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the config
|
// Return the config
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
91
package.json
91
package.json
@ -38,7 +38,7 @@
|
|||||||
"nuxt": "./bin/nuxt"
|
"nuxt": "./bin/nuxt"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "nyc ava --serial test/",
|
"test": "nyc ava --verbose --serial test/",
|
||||||
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
||||||
"lint": "eslint --ext .js,.vue bin lib pages test/*.js --ignore-pattern lib/app",
|
"lint": "eslint --ext .js,.vue bin lib pages test/*.js --ignore-pattern lib/app",
|
||||||
"build": "webpack",
|
"build": "webpack",
|
||||||
@ -52,63 +52,68 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-html": "^0.0.7",
|
"ansi-html": "^0.0.7",
|
||||||
"autoprefixer": "^6.7.2",
|
"autoprefixer": "^6.7.7",
|
||||||
"babel-core": "^6.22.1",
|
"babel-core": "^6.24.0",
|
||||||
"babel-loader": "^6.2.10",
|
"babel-loader": "^6.4.1",
|
||||||
"babel-plugin-array-includes": "^2.0.3",
|
"babel-preset-vue-app": "^0.5.1",
|
||||||
"babel-plugin-transform-async-to-generator": "^6.22.0",
|
|
||||||
"babel-plugin-transform-runtime": "^6.22.0",
|
|
||||||
"babel-preset-es2015": "^6.22.0",
|
|
||||||
"babel-preset-stage-2": "^6.22.0",
|
|
||||||
"chalk": "^1.1.3",
|
|
||||||
"chokidar": "^1.6.1",
|
"chokidar": "^1.6.1",
|
||||||
"co": "^4.6.0",
|
"co": "^4.6.0",
|
||||||
"css-loader": "^0.26.1",
|
"compression": "^1.6.2",
|
||||||
"debug": "^2.6.1",
|
"css-loader": "^0.27.3",
|
||||||
"extract-text-webpack-plugin": "2.0.0-beta.4",
|
"debug": "^2.6.3",
|
||||||
"file-loader": "^0.10.0",
|
"file-loader": "^0.10.1",
|
||||||
"fs-extra": "^2.0.0",
|
"friendly-errors-webpack-plugin": "^1.6.1",
|
||||||
|
"fs-extra": "^2.1.2",
|
||||||
"glob": "^7.1.1",
|
"glob": "^7.1.1",
|
||||||
"hash-sum": "^1.0.2",
|
"hash-sum": "^1.0.2",
|
||||||
"html-minifier": "^3.3.1",
|
"html-minifier": "^3.4.2",
|
||||||
|
"html-webpack-plugin": "^2.28.0",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
"lru-cache": "^4.0.2",
|
"lru-cache": "^4.0.2",
|
||||||
"memory-fs": "^0.4.1",
|
"memory-fs": "^0.4.1",
|
||||||
"path-to-regexp": "^1.7.0",
|
|
||||||
"pify": "^2.3.0",
|
"pify": "^2.3.0",
|
||||||
"post-compile-webpack-plugin": "^0.1.1",
|
"preload-webpack-plugin": "^1.2.1",
|
||||||
"progress-bar-webpack-plugin": "^1.9.3",
|
"progress-bar-webpack-plugin": "^1.9.3",
|
||||||
|
"script-ext-html-webpack-plugin": "^1.7.1",
|
||||||
"serialize-javascript": "^1.3.0",
|
"serialize-javascript": "^1.3.0",
|
||||||
"serve-static": "^1.11.2",
|
"serve-static": "^1.12.1",
|
||||||
"url-loader": "^0.5.7",
|
"url-loader": "^0.5.8",
|
||||||
"vue": "^2.1.10",
|
"vue": "^2.2.5",
|
||||||
"vue-loader": "^10.3.0",
|
"vue-loader": "^11.3.3",
|
||||||
"vue-meta": "^0.5.3",
|
"vue-meta": "^0.5.5",
|
||||||
"vue-router": "^2.2.0",
|
"vue-router": "^2.3.0",
|
||||||
"vue-server-renderer": "^2.1.10",
|
"vue-server-renderer": "^2.2.5",
|
||||||
"vue-ssr-html-stream": "^2.2.0",
|
"vue-ssr-html-stream": "^2.2.0",
|
||||||
"vue-template-compiler": "^2.1.10",
|
"vue-ssr-webpack-plugin": "^1.0.2",
|
||||||
"vuex": "^2.1.2",
|
"vue-template-compiler": "^2.2.5",
|
||||||
"webpack": "^2.2.1",
|
"vuex": "^2.2.1",
|
||||||
"webpack-bundle-analyzer": "^2.2.3",
|
"webpack": "^2.3.1",
|
||||||
"webpack-dev-middleware": "^1.10.0",
|
"webpack-bundle-analyzer": "^2.3.1",
|
||||||
"webpack-hot-middleware": "^2.16.1"
|
"webpack-dev-middleware": "^1.10.1",
|
||||||
|
"webpack-hot-middleware": "^2.17.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ava": "^0.18.1",
|
"ava": "^0.18.2",
|
||||||
"babel-eslint": "^7.1.1",
|
"babel-eslint": "^7.2.1",
|
||||||
"codecov": "^1.0.1",
|
"babel-plugin-array-includes": "^2.0.3",
|
||||||
|
"babel-plugin-transform-async-to-generator": "^6.22.0",
|
||||||
|
"babel-plugin-transform-runtime": "^6.23.0",
|
||||||
|
"babel-preset-es2015": "^6.24.0",
|
||||||
|
"babel-preset-stage-2": "^6.22.0",
|
||||||
|
"codecov": "^2.1.0",
|
||||||
"copy-webpack-plugin": "^4.0.1",
|
"copy-webpack-plugin": "^4.0.1",
|
||||||
"eslint": "^3.15.0",
|
"eslint": "^3.18.0",
|
||||||
"eslint-config-standard": "^6.2.1",
|
"eslint-config-standard": "^8.0.0-beta.2",
|
||||||
"eslint-plugin-html": "^2.0.0",
|
"eslint-plugin-html": "^2.0.1",
|
||||||
"eslint-plugin-promise": "^3.4.1",
|
"eslint-plugin-import": "^2.2.0",
|
||||||
"eslint-plugin-standard": "^2.0.1",
|
"eslint-plugin-node": "^4.2.1",
|
||||||
"finalhandler": "^0.5.1",
|
"eslint-plugin-promise": "^3.5.0",
|
||||||
"jsdom": "^9.10.0",
|
"eslint-plugin-standard": "^2.1.1",
|
||||||
|
"finalhandler": "^1.0.1",
|
||||||
|
"jsdom": "^9.12.0",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"nyc": "^10.1.2",
|
"nyc": "^10.2.0-candidate.0",
|
||||||
"request": "^2.79.0",
|
"request": "^2.81.0",
|
||||||
"request-promise-native": "^1.0.3",
|
"request-promise-native": "^1.0.3",
|
||||||
"webpack-node-externals": "^1.5.4"
|
"webpack-node-externals": "^1.5.4"
|
||||||
}
|
}
|
||||||
|
@ -1,45 +1,19 @@
|
|||||||
import test from 'ava'
|
import test from 'ava'
|
||||||
import { resolve } from 'path'
|
import { resolve } from 'path'
|
||||||
|
|
||||||
test('Fail to generate without routeParams', t => {
|
test('Fail with routes() which throw an error', t => {
|
||||||
const Nuxt = require('../')
|
|
||||||
const options = {
|
|
||||||
rootDir: resolve(__dirname, 'fixtures/basic'),
|
|
||||||
dev: false
|
|
||||||
// no generate.routeParams
|
|
||||||
}
|
|
||||||
const nuxt = new Nuxt(options)
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
var oldExit = process.exit
|
|
||||||
var oldCE = console.error // eslint-disable-line no-console
|
|
||||||
var _log = ''
|
|
||||||
console.error = (s) => { _log += s } // eslint-disable-line no-console
|
|
||||||
process.exit = (code) => {
|
|
||||||
process.exit = oldExit
|
|
||||||
console.error = oldCE // eslint-disable-line no-console
|
|
||||||
t.is(code, 1)
|
|
||||||
t.true(_log.includes('Could not generate the dynamic route /users/:id'))
|
|
||||||
resolve()
|
|
||||||
}
|
|
||||||
nuxt.generate()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('Fail with routeParams which throw an error', t => {
|
|
||||||
const Nuxt = require('../')
|
const Nuxt = require('../')
|
||||||
const options = {
|
const options = {
|
||||||
rootDir: resolve(__dirname, 'fixtures/basic'),
|
rootDir: resolve(__dirname, 'fixtures/basic'),
|
||||||
dev: false,
|
dev: false,
|
||||||
generate: {
|
generate: {
|
||||||
routeParams: {
|
routes: function () {
|
||||||
'/users/:id': function () {
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
reject('Not today!')
|
reject(new Error('Not today!'))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
const nuxt = new Nuxt(options)
|
const nuxt = new Nuxt(options)
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
var oldExit = process.exit
|
var oldExit = process.exit
|
||||||
@ -50,12 +24,12 @@ test('Fail with routeParams which throw an error', t => {
|
|||||||
process.exit = oldExit
|
process.exit = oldExit
|
||||||
console.error = oldCE // eslint-disable-line no-console
|
console.error = oldCE // eslint-disable-line no-console
|
||||||
t.is(code, 1)
|
t.is(code, 1)
|
||||||
t.true(_log.includes('Could not resolve routeParams[/users/:id]'))
|
t.true(_log.includes('Could not resolve routes'))
|
||||||
resolve()
|
resolve()
|
||||||
}
|
}
|
||||||
nuxt.generate()
|
nuxt.generate()
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
t.true(e === 'Not today!')
|
t.true(e.message === 'Not today!')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
10
test/fixtures/basic/nuxt.config.js
vendored
10
test/fixtures/basic/nuxt.config.js
vendored
@ -1,11 +1,9 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
generate: {
|
generate: {
|
||||||
routeParams: {
|
routes: [
|
||||||
'/users/:id': [
|
'/users/1',
|
||||||
{ id: 1 },
|
'/users/2',
|
||||||
{ id: 2 },
|
'/users/3'
|
||||||
{ id: 3 }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/basic/pages/async-data.vue
vendored
2
test/fixtures/basic/pages/async-data.vue
vendored
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data () {
|
asyncData () {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(() => resolve({ name: 'Nuxt.js' }), 10)
|
setTimeout(() => resolve({ name: 'Nuxt.js' }), 10)
|
||||||
})
|
})
|
||||||
|
@ -10,7 +10,7 @@ const fetchData = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async data () {
|
async asyncData () {
|
||||||
return await fetchData()
|
return await fetchData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
async data (context, callback) {
|
async asyncData (context, callback) {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
callback(null, { name: 'Callback Nuxt.js' })
|
callback(null, { name: 'Callback Nuxt.js' })
|
||||||
}, 10)
|
}, 10)
|
||||||
|
2
test/fixtures/basic/pages/error.vue
vendored
2
test/fixtures/basic/pages/error.vue
vendored
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data () {
|
asyncData () {
|
||||||
throw new Error('Error mouahahah')
|
throw new Error('Error mouahahah')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/basic/pages/error2.vue
vendored
2
test/fixtures/basic/pages/error2.vue
vendored
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data ({ error }) {
|
asyncData ({ error }) {
|
||||||
error({ message: 'Custom error' })
|
error({ message: 'Custom error' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/basic/pages/users/_id.vue
vendored
2
test/fixtures/basic/pages/users/_id.vue
vendored
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data ({ params }) {
|
asyncData ({ params }) {
|
||||||
return { id: params.id }
|
return { id: params.id }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
test/fixtures/with-config/app.html
vendored
Normal file
10
test/fixtures/with-config/app.html
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html {{ HTML_ATTRS }}>
|
||||||
|
<head>
|
||||||
|
{{ HEAD }}
|
||||||
|
</head>
|
||||||
|
<body {{ BODY_ATTRS }}>
|
||||||
|
{{ APP }}
|
||||||
|
<p>Made by Nuxt.js team</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
6
test/fixtures/with-config/nuxt.config.js
vendored
6
test/fixtures/with-config/nuxt.config.js
vendored
@ -10,7 +10,10 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
cache: true,
|
cache: true,
|
||||||
plugins: ['~plugins/test.js'],
|
plugins: [
|
||||||
|
'~plugins/test.js',
|
||||||
|
{ src: '~plugins/only-client.js', ssr: false }
|
||||||
|
],
|
||||||
loading: '~components/loading',
|
loading: '~components/loading',
|
||||||
env: {
|
env: {
|
||||||
bool: true,
|
bool: true,
|
||||||
@ -18,6 +21,7 @@ module.exports = {
|
|||||||
string: 'Nuxt.js'
|
string: 'Nuxt.js'
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
|
publicPath: '/orion/',
|
||||||
analyze: {
|
analyze: {
|
||||||
analyzerMode: 'disabled',
|
analyzerMode: 'disabled',
|
||||||
generateStatsFile: true
|
generateStatsFile: true
|
||||||
|
2
test/fixtures/with-config/pages/env.vue
vendored
2
test/fixtures/with-config/pages/env.vue
vendored
@ -5,7 +5,7 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
layout: 'custom-env',
|
layout: 'custom-env',
|
||||||
data ({ env }) {
|
asyncData ({ env }) {
|
||||||
return { env }
|
return { env }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
middleware: 'user-agent',
|
middleware: 'user-agent',
|
||||||
data ({ userAgent }) {
|
asyncData ({ userAgent }) {
|
||||||
return { userAgent }
|
return { userAgent }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
test/fixtures/with-config/plugins/only-client.js
vendored
Normal file
1
test/fixtures/with-config/plugins/only-client.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
console.log('Only called in client-side!')
|
@ -37,9 +37,9 @@ test('urlJoin', t => {
|
|||||||
t.is(utils.urlJoin('test', '/about'), 'test/about')
|
t.is(utils.urlJoin('test', '/about'), 'test/about')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('promisifyRouteParams (array)', t => {
|
test('promisifyRoute (array)', t => {
|
||||||
const array = [1]
|
const array = [1]
|
||||||
const promise = utils.promisifyRouteParams(array)
|
const promise = utils.promisifyRoute(array)
|
||||||
t.is(typeof promise, 'object')
|
t.is(typeof promise, 'object')
|
||||||
return promise
|
return promise
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
@ -47,12 +47,12 @@ test('promisifyRouteParams (array)', t => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('promisifyRouteParams (fn => array)', t => {
|
test('promisifyRoute (fn => array)', t => {
|
||||||
const array = [1, 2]
|
const array = [1, 2]
|
||||||
const fn = function () {
|
const fn = function () {
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
const promise = utils.promisifyRouteParams(fn)
|
const promise = utils.promisifyRoute(fn)
|
||||||
t.is(typeof promise, 'object')
|
t.is(typeof promise, 'object')
|
||||||
return promise
|
return promise
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
@ -60,14 +60,14 @@ test('promisifyRouteParams (fn => array)', t => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('promisifyRouteParams (fn => promise)', t => {
|
test('promisifyRoute (fn => promise)', t => {
|
||||||
const array = [1, 2, 3]
|
const array = [1, 2, 3]
|
||||||
const fn = function () {
|
const fn = function () {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
resolve(array)
|
resolve(array)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const promise = utils.promisifyRouteParams(fn)
|
const promise = utils.promisifyRoute(fn)
|
||||||
t.is(typeof promise, 'object')
|
t.is(typeof promise, 'object')
|
||||||
return promise
|
return promise
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
@ -75,24 +75,24 @@ test('promisifyRouteParams (fn => promise)', t => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('promisifyRouteParams (fn(cb) with error)', t => {
|
test('promisifyRoute (fn(cb) with error)', t => {
|
||||||
const fn = function (cb) {
|
const fn = function (cb) {
|
||||||
cb('Error here')
|
cb(new Error('Error here'))
|
||||||
}
|
}
|
||||||
const promise = utils.promisifyRouteParams(fn)
|
const promise = utils.promisifyRoute(fn)
|
||||||
t.is(typeof promise, 'object')
|
t.is(typeof promise, 'object')
|
||||||
return promise
|
return promise
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
t.is(e, 'Error here')
|
t.is(e.message, 'Error here')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('promisifyRouteParams (fn(cb) with result)', t => {
|
test('promisifyRoute (fn(cb) with result)', t => {
|
||||||
const array = [1, 2, 3, 4]
|
const array = [1, 2, 3, 4]
|
||||||
const fn = function (cb) {
|
const fn = function (cb) {
|
||||||
cb(null, array)
|
cb(null, array)
|
||||||
}
|
}
|
||||||
const promise = utils.promisifyRouteParams(fn)
|
const promise = utils.promisifyRoute(fn)
|
||||||
t.is(typeof promise, 'object')
|
t.is(typeof promise, 'object')
|
||||||
return promise
|
return promise
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -24,6 +24,16 @@ test('/', async t => {
|
|||||||
t.true(html.includes('<h1>I have custom configurations</h1>'))
|
t.true(html.includes('<h1>I have custom configurations</h1>'))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('/ (custom app.html)', async t => {
|
||||||
|
const { html } = await nuxt.renderRoute('/')
|
||||||
|
t.true(html.includes('<p>Made by Nuxt.js team</p>'))
|
||||||
|
})
|
||||||
|
|
||||||
|
test('/ (custom build.publicPath)', async t => {
|
||||||
|
const { html } = await nuxt.renderRoute('/')
|
||||||
|
t.true(html.includes('src="/test/orion/vendor.bundle'))
|
||||||
|
})
|
||||||
|
|
||||||
test('/test/ (router base)', async t => {
|
test('/test/ (router base)', async t => {
|
||||||
const window = await nuxt.renderAndGetWindow(url('/test/'))
|
const window = await nuxt.renderAndGetWindow(url('/test/'))
|
||||||
const html = window.document.body.innerHTML
|
const html = window.document.body.innerHTML
|
||||||
@ -62,7 +72,7 @@ test('/test/about-bis (added with extendRoutes)', async t => {
|
|||||||
|
|
||||||
test('Check stats.json generated by build.analyze', t => {
|
test('Check stats.json generated by build.analyze', t => {
|
||||||
const stats = require(resolve(__dirname, 'fixtures/with-config/.nuxt/dist/stats.json'))
|
const stats = require(resolve(__dirname, 'fixtures/with-config/.nuxt/dist/stats.json'))
|
||||||
t.is(stats.assets.length, 11)
|
t.is(stats.assets.length, 19)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Close server and ask nuxt to stop listening to file changes
|
// Close server and ask nuxt to stop listening to file changes
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
|
||||||
|
// Until babel-loader 7 is released
|
||||||
|
process.noDeprecation = true
|
||||||
|
|
||||||
var nodeExternals = require('webpack-node-externals')
|
var nodeExternals = require('webpack-node-externals')
|
||||||
var ProgressBarPlugin = require('progress-bar-webpack-plugin')
|
var ProgressBarPlugin = require('progress-bar-webpack-plugin')
|
||||||
var CopyWebpackPlugin = require('copy-webpack-plugin')
|
var CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||||
|
Loading…
Reference in New Issue
Block a user