mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-30 15:22:39 +00:00
commit
ded7de0e72
@ -2,8 +2,6 @@ language: node_js
|
||||
node_js:
|
||||
- "7.2"
|
||||
- "6.9"
|
||||
- "5.12"
|
||||
- "4.7"
|
||||
before_install:
|
||||
- if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi
|
||||
install:
|
||||
|
@ -1,6 +1,7 @@
|
||||
module.exports = {
|
||||
build: {
|
||||
filenames: {
|
||||
css: 'styles.[chunkhash].css', // default: common.[chunkhash].css
|
||||
manifest: 'manifest.[hash].js', // default: manifest.[hash].js
|
||||
vendor: 'vendor.[hash].js', // default: vendor.bundle.[hash].js
|
||||
app: 'app.[chunkhash].js' // default: nuxt.bundle.[chunkhash].js
|
||||
|
@ -2,13 +2,15 @@
|
||||
|
||||
import Vue from 'vue'
|
||||
import middleware from './middleware'
|
||||
import { app, router<%= (store ? ', store' : '') %>, NuxtError } from './index'
|
||||
import { createApp, NuxtError } from './index'
|
||||
import { applyAsyncData, getMatchedComponents, getMatchedComponentsInstances, flatMapComponents, getContext, promiseSeries, promisify, getLocation, compile } from './utils'
|
||||
const noopData = () => { return {} }
|
||||
const noopFetch = () => {}
|
||||
let _lastPaths = []
|
||||
let _lastComponentsFiles = []
|
||||
|
||||
const { app, router<%= (store ? ', store' : '') %> } = createApp()
|
||||
|
||||
function mapTransitions(Components, to, from) {
|
||||
return Components.map((Component) => {
|
||||
let transition = Component.options.transition
|
||||
|
163
lib/app/index.js
163
lib/app/index.js
@ -2,8 +2,8 @@
|
||||
|
||||
import Vue from 'vue'
|
||||
import Meta from 'vue-meta'
|
||||
import router from './router.js'
|
||||
<% if (store) { %>import store from './store.js'<% } %>
|
||||
import { createRouter } from './router.js'
|
||||
<% if (store) { %>import { createStore } from './store.js'<% } %>
|
||||
import NuxtChild from './components/nuxt-child.js'
|
||||
import NuxtLink from './components/nuxt-link.js'
|
||||
import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./components/nuxt-error.vue" %>'
|
||||
@ -25,86 +25,97 @@ Vue.use(Meta, {
|
||||
tagIDKeyName: 'hid' // the property name that vue-meta uses to determine whether to overwrite or append a tag
|
||||
})
|
||||
|
||||
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
|
||||
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
|
||||
window._nuxtReadyCbs = []
|
||||
window.onNuxtReady = function (cb) {
|
||||
window._nuxtReadyCbs.push(cb)
|
||||
}
|
||||
}
|
||||
|
||||
// root instance
|
||||
// here we inject the router and store to all child components,
|
||||
// making them available everywhere as `this.$router` and `this.$store`.
|
||||
const defaultTransition = <%=
|
||||
serialize(transition)
|
||||
.replace('beforeEnter(', 'function(').replace('enter(', 'function(').replace('afterEnter(', 'function(')
|
||||
.replace('enterCancelled(', 'function(').replace('beforeLeave(', 'function(').replace('leave(', 'function(')
|
||||
.replace('afterLeave(', 'function(').replace('leaveCancelled(', 'function(')
|
||||
%>
|
||||
let app = {
|
||||
router,
|
||||
<%= (store ? 'store,' : '') %>
|
||||
_nuxt: {
|
||||
defaultTransition: defaultTransition,
|
||||
transitions: [ defaultTransition ],
|
||||
setTransitions (transitions) {
|
||||
if (!Array.isArray(transitions)) {
|
||||
transitions = [ transitions ]
|
||||
}
|
||||
transitions = transitions.map((transition) => {
|
||||
if (!transition) {
|
||||
transition = defaultTransition
|
||||
} else if (typeof transition === 'string') {
|
||||
transition = Object.assign({}, defaultTransition, { name: transition })
|
||||
} else {
|
||||
transition = Object.assign({}, defaultTransition, transition)
|
||||
}
|
||||
return transition
|
||||
})
|
||||
this.$options._nuxt.transitions = transitions
|
||||
return transitions
|
||||
},
|
||||
err: null,
|
||||
dateErr: null,
|
||||
error (err) {
|
||||
err = err || null
|
||||
if (typeof err === 'string') {
|
||||
err = { statusCode: 500, message: err }
|
||||
}
|
||||
this.$options._nuxt.dateErr = Date.now()
|
||||
this.$options._nuxt.err = err;
|
||||
return err
|
||||
serialize(transition)
|
||||
.replace('beforeEnter(', 'function(').replace('enter(', 'function(').replace('afterEnter(', 'function(')
|
||||
.replace('enterCancelled(', 'function(').replace('beforeLeave(', 'function(').replace('leave(', 'function(')
|
||||
.replace('afterLeave(', 'function(').replace('leaveCancelled(', 'function(')
|
||||
%>
|
||||
|
||||
export { NuxtError }
|
||||
|
||||
export function createApp (ssrContext) {
|
||||
<% if (store) { %>
|
||||
const store = createStore()
|
||||
<% } %>
|
||||
const router = createRouter()
|
||||
|
||||
if (process.browser) {
|
||||
<% if (store) { %>
|
||||
// Replace store state before calling plugins
|
||||
if (window.__NUXT__ && window.__NUXT__.state) {
|
||||
store.replaceState(window.__NUXT__.state)
|
||||
}
|
||||
},
|
||||
...App
|
||||
}
|
||||
<% } %>
|
||||
// window.onNuxtReady(() => console.log('Ready')) hook
|
||||
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
|
||||
window._nuxtReadyCbs = []
|
||||
window.onNuxtReady = function (cb) {
|
||||
window._nuxtReadyCbs.push(cb)
|
||||
}
|
||||
}
|
||||
|
||||
// root instance
|
||||
// here we inject the router and store to all child components,
|
||||
// making them available everywhere as `this.$router` and `this.$store`.
|
||||
let app = {
|
||||
router,
|
||||
<%= (store ? 'store,' : '') %>
|
||||
ssrContext,
|
||||
_nuxt: {
|
||||
defaultTransition: defaultTransition,
|
||||
transitions: [ defaultTransition ],
|
||||
setTransitions (transitions) {
|
||||
if (!Array.isArray(transitions)) {
|
||||
transitions = [ transitions ]
|
||||
}
|
||||
transitions = transitions.map((transition) => {
|
||||
if (!transition) {
|
||||
transition = defaultTransition
|
||||
} else if (typeof transition === 'string') {
|
||||
transition = Object.assign({}, defaultTransition, { name: transition })
|
||||
} else {
|
||||
transition = Object.assign({}, defaultTransition, transition)
|
||||
}
|
||||
return transition
|
||||
})
|
||||
this.$options._nuxt.transitions = transitions
|
||||
return transitions
|
||||
},
|
||||
err: null,
|
||||
dateErr: null,
|
||||
error (err) {
|
||||
err = err || null
|
||||
if (typeof err === 'string') {
|
||||
err = { statusCode: 500, message: err }
|
||||
}
|
||||
this.$options._nuxt.dateErr = Date.now()
|
||||
this.$options._nuxt.err = err;
|
||||
return err
|
||||
}
|
||||
},
|
||||
...App
|
||||
}
|
||||
|
||||
|
||||
// Includes & Inject external plugins
|
||||
<% plugins.forEach(function (plugin) {
|
||||
if (plugin.ssr) { %>
|
||||
<%= (plugin.injectAs ? 'let ' + plugin.injectAs + ' = ' : '') %>require('<%= plugin.src %>')
|
||||
<% if (plugin.injectAs) { %>
|
||||
<%= plugin.injectAs + ' = ' + plugin.injectAs + '.default || ' + plugin.injectAs %>
|
||||
app['<%= plugin.injectAs %>'] = <%= plugin.injectAs %>
|
||||
<% }
|
||||
} else { %>
|
||||
if (process.browser) {
|
||||
// Includes & Inject external plugins
|
||||
<% plugins.forEach(function (plugin) {
|
||||
if (plugin.ssr) { %>
|
||||
<%= (plugin.injectAs ? 'let ' + plugin.injectAs + ' = ' : '') %>require('<%= plugin.src %>')
|
||||
<% if (plugin.injectAs) { %>
|
||||
<%= plugin.injectAs + ' = ' + plugin.injectAs + '.default || ' + plugin.injectAs %>
|
||||
app['<%= plugin.injectAs %>'] = <%= plugin.injectAs %>
|
||||
<% } %>
|
||||
}
|
||||
<% }
|
||||
}) %>
|
||||
<% }
|
||||
} else { %>
|
||||
if (process.browser) {
|
||||
<%= (plugin.injectAs ? 'let ' + plugin.injectAs + ' = ' : '') %>require('<%= plugin.src %>')
|
||||
<% if (plugin.injectAs) { %>
|
||||
<%= plugin.injectAs + ' = ' + plugin.injectAs + '.default || ' + plugin.injectAs %>
|
||||
app['<%= plugin.injectAs %>'] = <%= plugin.injectAs %>
|
||||
<% } %>
|
||||
}
|
||||
<% }
|
||||
}) %>
|
||||
|
||||
export { app, router<%= (store ? ', store' : '') %>, NuxtError }
|
||||
return { app, router<%= (store ? ', store' : '') %> }
|
||||
}
|
||||
|
@ -53,12 +53,14 @@ const scrollBehavior = (to, from, savedPosition) => {
|
||||
}
|
||||
<% } %>
|
||||
|
||||
export default new Router({
|
||||
mode: '<%= router.mode %>',
|
||||
base: '<%= router.base %>',
|
||||
linkActiveClass: '<%= router.linkActiveClass %>',
|
||||
scrollBehavior,
|
||||
routes: [
|
||||
<%= _routes %>
|
||||
]
|
||||
})
|
||||
export function createRouter () {
|
||||
return new Router({
|
||||
mode: '<%= router.mode %>',
|
||||
base: '<%= router.base %>',
|
||||
linkActiveClass: '<%= router.linkActiveClass %>',
|
||||
scrollBehavior,
|
||||
routes: [
|
||||
<%= _routes %>
|
||||
]
|
||||
})
|
||||
}
|
||||
|
@ -7,11 +7,10 @@ import Vue from 'vue'
|
||||
import { stringify } from 'querystring'
|
||||
import { omit } from 'lodash'
|
||||
import middleware from './middleware'
|
||||
import { app, router<%= (store ? ', store' : '') %>, NuxtError } from './index'
|
||||
import { createApp, NuxtError } from './index'
|
||||
import { applyAsyncData, getMatchedComponents, getContext, promiseSeries, promisify, urlJoin } from './utils'
|
||||
|
||||
const isDev = <%= isDev %>
|
||||
const _app = new Vue(app)
|
||||
|
||||
// This exported function will be called by `bundleRenderer`.
|
||||
// This is where we perform data-prefetching to determine the
|
||||
@ -19,6 +18,8 @@ const _app = new Vue(app)
|
||||
// Since data fetching is async, this function is expected to
|
||||
// return a Promise that resolves to the app instance.
|
||||
export default context => {
|
||||
const { app, router<%= (store ? ', store' : '') %> } = createApp(context)
|
||||
const _app = new Vue(app)
|
||||
// Add store to the context
|
||||
<%= (store ? 'context.store = store' : '') %>
|
||||
// Nuxt object
|
||||
|
@ -30,8 +30,11 @@ let storeData = {}
|
||||
if (filenames.indexOf('./index.js') !== -1) {
|
||||
let mainModule = getModule('./index.js')
|
||||
if (mainModule.commit) {
|
||||
store = mainModule
|
||||
console.error('[nuxt.js] store/index.js should export raw store options instead of an instance.')
|
||||
} else {
|
||||
if (mainModule.state && typeof mainModule.state !== 'function') {
|
||||
console.error('[nuxt.js] store state should be a function.')
|
||||
}
|
||||
storeData = mainModule
|
||||
}
|
||||
}
|
||||
@ -49,8 +52,13 @@ if (store == null) {
|
||||
name = namePath.pop()
|
||||
module[name] = getModule(filename)
|
||||
module[name].namespaced = true
|
||||
|
||||
if (typeof module[name].state !== 'function') {
|
||||
console.error('[nuxt.js] store module state should be a function.')
|
||||
}
|
||||
}
|
||||
store = new Vuex.Store(storeData)
|
||||
}
|
||||
|
||||
export default store
|
||||
export function createStore () {
|
||||
return new Vuex.Store(storeData)
|
||||
}
|
||||
|
61
lib/build.js
61
lib/build.js
@ -47,6 +47,7 @@ const defaults = {
|
||||
analyze: false,
|
||||
publicPath: '/_nuxt/',
|
||||
filenames: {
|
||||
css: 'common.[chunkhash].css',
|
||||
manifest: 'manifest.[hash].js',
|
||||
vendor: 'vendor.bundle.[hash].js',
|
||||
app: 'nuxt.bundle.[chunkhash].js'
|
||||
@ -101,9 +102,11 @@ export function options () {
|
||||
}
|
||||
const serverConfig = getWebpackServerConfig.call(this)
|
||||
const bundlePath = join(serverConfig.output.path, 'server-bundle.json')
|
||||
if (fs.existsSync(bundlePath)) {
|
||||
const manifestPath = join(serverConfig.output.path, 'client-manifest.json')
|
||||
if (fs.existsSync(bundlePath) && fs.existsSync(manifestPath)) {
|
||||
const bundle = fs.readFileSync(bundlePath, 'utf8')
|
||||
createRenderer.call(this, JSON.parse(bundle))
|
||||
const manifest = fs.readFileSync(manifestPath, 'utf8')
|
||||
createRenderer.call(this, JSON.parse(bundle), JSON.parse(manifest))
|
||||
addAppTemplate.call(this)
|
||||
}
|
||||
}
|
||||
@ -142,10 +145,8 @@ function * buildFiles () {
|
||||
watchPages.call(this)
|
||||
} else {
|
||||
debug('Building files...')
|
||||
yield [
|
||||
webpackRunClient.call(this),
|
||||
webpackRunServer.call(this)
|
||||
]
|
||||
yield webpackRunClient.call(this)
|
||||
yield webpackRunServer.call(this)
|
||||
addAppTemplate.call(this)
|
||||
}
|
||||
}
|
||||
@ -386,6 +387,7 @@ function createWebpackMiddleware () {
|
||||
})
|
||||
)
|
||||
const clientCompiler = webpack(clientConfig)
|
||||
this.clientCompiler = clientCompiler
|
||||
// Add the middleware to the instance context
|
||||
this.webpackDevMiddleware = pify(require('webpack-dev-middleware')(clientCompiler, {
|
||||
publicPath: clientConfig.output.publicPath,
|
||||
@ -406,20 +408,35 @@ function createWebpackMiddleware () {
|
||||
interpolate: /{{([\s\S]+?)}}/g
|
||||
})
|
||||
}
|
||||
this.watchHandler()
|
||||
})
|
||||
}
|
||||
|
||||
function webpackWatchAndUpdate () {
|
||||
const MFS = require('memory-fs') // <- dependencies of webpack
|
||||
const mfs = new MFS()
|
||||
const serverFS = new MFS()
|
||||
const clientFS = this.clientCompiler.outputFileSystem
|
||||
const serverConfig = getWebpackServerConfig.call(this)
|
||||
const serverCompiler = webpack(serverConfig)
|
||||
const outputPath = join(serverConfig.output.path, 'server-bundle.json')
|
||||
serverCompiler.outputFileSystem = mfs
|
||||
this.webpackServerWatcher = serverCompiler.watch(this.options.watchers.webpack, (err) => {
|
||||
const bundlePath = join(serverConfig.output.path, 'server-bundle.json')
|
||||
const manifestPath = join(serverConfig.output.path, 'client-manifest.json')
|
||||
serverCompiler.outputFileSystem = serverFS
|
||||
const watchHandler = (err) => {
|
||||
if (err) throw err
|
||||
createRenderer.call(this, JSON.parse(mfs.readFileSync(outputPath, 'utf-8')))
|
||||
})
|
||||
const bundleExists = serverFS.existsSync(bundlePath)
|
||||
const manifestExists = clientFS.existsSync(manifestPath)
|
||||
if (!bundleExists) {
|
||||
debug('Waiting for server bundle...')
|
||||
} else if (!manifestExists) {
|
||||
debug('Waiting for client manifest...')
|
||||
} else {
|
||||
const bundle = serverFS.readFileSync(bundlePath, 'utf8')
|
||||
const manifest = clientFS.readFileSync(manifestPath, 'utf8')
|
||||
createRenderer.call(this, JSON.parse(bundle), JSON.parse(manifest))
|
||||
}
|
||||
}
|
||||
this.watchHandler = watchHandler
|
||||
this.webpackServerWatcher = serverCompiler.watch(this.options.watchers.webpack, watchHandler)
|
||||
}
|
||||
|
||||
function webpackRunClient () {
|
||||
@ -444,16 +461,18 @@ function webpackRunServer () {
|
||||
console.log('[nuxt:build:server]\n', stats.toString(webpackStats)) // eslint-disable-line no-console
|
||||
if (stats.hasErrors()) return reject(new Error('Webpack build exited with errors'))
|
||||
const bundlePath = join(serverConfig.output.path, 'server-bundle.json')
|
||||
const manifestPath = join(serverConfig.output.path, 'client-manifest.json')
|
||||
readFile(bundlePath, 'utf8')
|
||||
.then((bundle) => {
|
||||
createRenderer.call(this, JSON.parse(bundle))
|
||||
resolve()
|
||||
})
|
||||
.then(bundle => readFile(manifestPath, 'utf8')
|
||||
.then(manifest => {
|
||||
createRenderer.call(this, JSON.parse(bundle), JSON.parse(manifest))
|
||||
resolve()
|
||||
}))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function createRenderer (bundle) {
|
||||
function createRenderer (bundle, manifest) {
|
||||
// Create bundle renderer to give a fresh context for every request
|
||||
let cacheConfig = false
|
||||
if (this.options.cache) {
|
||||
@ -463,9 +482,11 @@ function createRenderer (bundle) {
|
||||
maxAge: 1000 * 60 * 15
|
||||
}))
|
||||
}
|
||||
this.renderer = createBundleRenderer(bundle, {
|
||||
cache: cacheConfig
|
||||
})
|
||||
this.renderer = createBundleRenderer(bundle, Object.assign({
|
||||
cache: cacheConfig,
|
||||
clientManifest: manifest,
|
||||
runInNewContext: false
|
||||
}, this.options.ssr))
|
||||
this.renderToString = pify(this.renderer.renderToString)
|
||||
this.renderToStream = this.renderer.renderToStream
|
||||
}
|
||||
|
@ -43,8 +43,7 @@ class Nuxt {
|
||||
performance: {
|
||||
gzip: {
|
||||
threshold: 0
|
||||
},
|
||||
prefetch: true
|
||||
}
|
||||
},
|
||||
watchers: {
|
||||
webpack: {},
|
||||
|
@ -93,7 +93,7 @@ export function renderRoute (url, context = {}) {
|
||||
// Add url and isSever to the context
|
||||
context.url = url
|
||||
context.isServer = true
|
||||
// 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
|
||||
return co(function * () {
|
||||
let APP = yield self.renderToString(context)
|
||||
@ -105,8 +105,9 @@ export function renderRoute (url, context = {}) {
|
||||
if (self.options.router.base !== '/') {
|
||||
HEAD += `<base href="${self.options.router.base}">`
|
||||
}
|
||||
HEAD += context.styles
|
||||
HEAD += context.renderResourceHints() + context.renderStyles()
|
||||
APP += `<script type="text/javascript">window.__NUXT__=${serialize(context.nuxt, { isJSON: true })}</script>`
|
||||
APP += context.renderScripts()
|
||||
const html = self.appTemplate({
|
||||
HTML_ATTRS: 'data-n-head-ssr ' + m.htmlAttrs.text(),
|
||||
BODY_ATTRS: m.bodyAttrs.text(),
|
||||
@ -127,7 +128,8 @@ export function renderAndGetWindow (url, opts = {}) {
|
||||
/* istanbul ignore if */
|
||||
if (!jsdom) {
|
||||
try {
|
||||
jsdom = require('jsdom')
|
||||
// https://github.com/tmpvar/jsdom/blob/master/lib/old-api.md
|
||||
jsdom = require('jsdom/lib/old-api')
|
||||
} catch (e) {
|
||||
console.error('Fail when calling nuxt.renderAndGetWindow(url)') // eslint-disable-line no-console
|
||||
console.error('jsdom module is not installed') // eslint-disable-line no-console
|
||||
@ -136,6 +138,7 @@ export function renderAndGetWindow (url, opts = {}) {
|
||||
}
|
||||
}
|
||||
let virtualConsole = jsdom.createVirtualConsole().sendTo(console)
|
||||
// let virtualConsole = new jsdom.VirtualConsole().sendTo(console)
|
||||
if (opts.virtualConsole === false) {
|
||||
virtualConsole = undefined
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import vueLoaderConfig from './vue-loader.config'
|
||||
import { defaults } from 'lodash'
|
||||
import { join } from 'path'
|
||||
import { isUrl, urlJoin } from '../utils'
|
||||
import { styleLoader, extractStyles } from './helpers'
|
||||
import ExtractTextPlugin from 'extract-text-webpack-plugin'
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@ -71,15 +73,21 @@ export default function ({ isClient, isServer }) {
|
||||
cacheDirectory: !!this.dev
|
||||
})
|
||||
},
|
||||
{ test: /\.css$/, loader: 'vue-style-loader!css-loader' },
|
||||
{ test: /\.less$/, loader: 'vue-style-loader!css-loader!less-loader' },
|
||||
{ test: /\.sass$/, loader: 'vue-style-loader!css-loader!sass-loader?indentedSyntax' },
|
||||
{ test: /\.scss$/, loader: 'vue-style-loader!css-loader!sass-loader' },
|
||||
{ test: /\.styl(us)?$/, loader: 'vue-style-loader!css-loader!stylus-loader' }
|
||||
{ test: /\.css$/, use: styleLoader.call(this, 'css') },
|
||||
{ test: /\.less$/, use: styleLoader.call(this, 'less', 'less-loader') },
|
||||
{ test: /\.sass$/, use: styleLoader.call(this, 'sass', 'sass-loader?indentedSyntax') },
|
||||
{ test: /\.scss$/, use: styleLoader.call(this, 'sass', 'sass-loader') },
|
||||
{ test: /\.styl(us)?$/, use: styleLoader.call(this, 'stylus', 'stylus-loader') }
|
||||
]
|
||||
},
|
||||
plugins: this.options.build.plugins
|
||||
}
|
||||
// CSS extraction
|
||||
if (extractStyles.call(this)) {
|
||||
config.plugins.push(
|
||||
new ExtractTextPlugin({filename: this.options.build.filenames.css})
|
||||
)
|
||||
}
|
||||
// Add nuxt build loaders (can be configured in nuxt.config.js)
|
||||
config.module.rules = config.module.rules.concat(this.options.build.loaders)
|
||||
// Return config
|
||||
|
@ -2,14 +2,15 @@
|
||||
|
||||
import { each, defaults } from 'lodash'
|
||||
import webpack from 'webpack'
|
||||
import VueSSRClientPlugin from 'vue-server-renderer/client-plugin'
|
||||
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 { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
|
||||
import OfflinePlugin from 'offline-plugin'
|
||||
import base from './base.config.js'
|
||||
import { extractStyles } from './helpers'
|
||||
import { resolve } from 'path'
|
||||
|
||||
/*
|
||||
@ -56,31 +57,33 @@ export default function () {
|
||||
// Extract vendor chunks for better caching
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'vendor',
|
||||
filename: this.options.build.filenames.vendor
|
||||
filename: this.options.build.filenames.vendor,
|
||||
minChunks (module) {
|
||||
// A module is extracted into the vendor chunk when...
|
||||
return (
|
||||
// If it's inside node_modules
|
||||
/node_modules/.test(module.context) &&
|
||||
// Do not externalize if the request is a CSS file
|
||||
!/\.css$/.test(module.request)
|
||||
)
|
||||
}
|
||||
}),
|
||||
// Extract manifest
|
||||
// Extract webpack runtime & manifest
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'manifest',
|
||||
minChunks: Infinity,
|
||||
filename: this.options.build.filenames.manifest
|
||||
}),
|
||||
// Generate output HTML
|
||||
new HTMLPlugin({
|
||||
template: this.options.appTemplatePath
|
||||
template: this.options.appTemplatePath,
|
||||
inject: false // <- Resources will be injected using vue server renderer
|
||||
}),
|
||||
// Add defer to scripts
|
||||
new ScriptExtHtmlWebpackPlugin({
|
||||
defaultAttribute: 'defer'
|
||||
// Generate client manifest json
|
||||
new VueSSRClientPlugin({
|
||||
filename: 'client-manifest.json'
|
||||
})
|
||||
])
|
||||
|
||||
if (!this.dev && this.options.performance.prefetch === true) {
|
||||
// Add prefetch code-splitted routes
|
||||
config.plugins.push(
|
||||
new PreloadWebpackPlugin({
|
||||
rel: 'prefetch'
|
||||
})
|
||||
)
|
||||
}
|
||||
// client bundle progress bar
|
||||
config.plugins.push(
|
||||
new ProgressBarPlugin()
|
||||
|
15
lib/webpack/helpers.js
Executable file
15
lib/webpack/helpers.js
Executable file
@ -0,0 +1,15 @@
|
||||
import ExtractTextPlugin from 'extract-text-webpack-plugin'
|
||||
|
||||
export function extractStyles(ext) {
|
||||
return !this.dev && !!this.options.build.extractCSS && this.options.build.extractCSS[ext] !== false
|
||||
}
|
||||
|
||||
export function styleLoader(ext, loader = []) {
|
||||
if (!extractStyles.call(this, ext)) {
|
||||
return ['vue-style-loader', 'css-loader'].concat(loader)
|
||||
}
|
||||
return ExtractTextPlugin.extract({
|
||||
use: ['css-loader?minimize'].concat(loader),
|
||||
fallback: 'vue-style-loader'
|
||||
})
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
import webpack from 'webpack'
|
||||
import VueSSRPlugin from 'vue-ssr-webpack-plugin'
|
||||
import VueSSRServerPlugin from 'vue-server-renderer/server-plugin'
|
||||
import base from './base.config.js'
|
||||
import { each, uniq } from 'lodash'
|
||||
import { existsSync, readFileSync } from 'fs'
|
||||
@ -34,7 +34,7 @@ export default function () {
|
||||
hints: false
|
||||
},
|
||||
plugins: (config.plugins || []).concat([
|
||||
new VueSSRPlugin({
|
||||
new VueSSRServerPlugin({
|
||||
filename: 'server-bundle.json'
|
||||
}),
|
||||
new webpack.DefinePlugin(Object.assign(env, {
|
||||
|
@ -1,6 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
import { defaults } from 'lodash'
|
||||
import { extractStyles, styleLoader } from './helpers'
|
||||
|
||||
export default function ({ isClient }) {
|
||||
let babelOptions = JSON.stringify(defaults(this.options.build.babel, {
|
||||
@ -8,18 +9,21 @@ export default function ({ isClient }) {
|
||||
babelrc: false,
|
||||
cacheDirectory: !!this.dev
|
||||
}))
|
||||
|
||||
// https://github.com/vuejs/vue-loader/blob/master/docs/en/configurations
|
||||
let config = {
|
||||
postcss: this.options.build.postcss,
|
||||
loaders: {
|
||||
'js': 'babel-loader?' + babelOptions,
|
||||
'css': 'vue-style-loader!css-loader',
|
||||
'less': 'vue-style-loader!css-loader!less-loader',
|
||||
'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax',
|
||||
'scss': 'vue-style-loader!css-loader!sass-loader',
|
||||
'stylus': 'vue-style-loader!css-loader!stylus-loader',
|
||||
'styl': 'vue-style-loader!css-loader!stylus-loader'
|
||||
'css': styleLoader.call(this, 'css'),
|
||||
'less': styleLoader.call(this, 'less', 'less-loader'),
|
||||
'sass': styleLoader.call(this, 'sass', 'sass-loader?indentedSyntax'),
|
||||
'scss': styleLoader.call(this, 'sass', 'scss-loader'),
|
||||
'stylus': styleLoader.call(this, 'stylus', 'stylus-loader'),
|
||||
'styl': styleLoader.call(this, 'stylus', 'stylus-loader')
|
||||
},
|
||||
preserveWhitespace: false
|
||||
preserveWhitespace: false,
|
||||
extractCSS: extractStyles.call(this, 'vue')
|
||||
}
|
||||
// Return the config
|
||||
return config
|
||||
|
34
package.json
34
package.json
@ -54,17 +54,18 @@
|
||||
"ansi-html": "^0.0.7",
|
||||
"autoprefixer": "^6.7.7",
|
||||
"babel-core": "^6.24.1",
|
||||
"babel-loader": "^6.4.1",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-vue-app": "^1.2.0",
|
||||
"chokidar": "^1.6.1",
|
||||
"co": "^4.6.0",
|
||||
"compression": "^1.6.2",
|
||||
"css-loader": "^0.28.0",
|
||||
"debug": "^2.6.3",
|
||||
"css-loader": "^0.28.1",
|
||||
"debug": "^2.6.6",
|
||||
"extract-text-webpack-plugin": "^2.1.0",
|
||||
"file-loader": "^0.11.1",
|
||||
"friendly-errors-webpack-plugin": "^1.6.1",
|
||||
"fs-extra": "^2.1.2",
|
||||
"fs-extra": "^3.0.0",
|
||||
"glob": "^7.1.1",
|
||||
"hash-sum": "^1.0.2",
|
||||
"html-minifier": "^3.4.3",
|
||||
@ -79,25 +80,24 @@
|
||||
"progress-bar-webpack-plugin": "^1.9.3",
|
||||
"script-ext-html-webpack-plugin": "^1.7.1",
|
||||
"serialize-javascript": "^1.3.0",
|
||||
"serve-static": "^1.12.1",
|
||||
"serve-static": "^1.12.2",
|
||||
"url-loader": "^0.5.8",
|
||||
"vue": "~2.2.6",
|
||||
"vue-loader": "^11.3.4",
|
||||
"vue": "~2.3.1",
|
||||
"vue-loader": "^12.0.2",
|
||||
"vue-meta": "^0.5.6",
|
||||
"vue-router": "^2.4.0",
|
||||
"vue-server-renderer": "~2.2.6",
|
||||
"vue-router": "^2.5.3",
|
||||
"vue-server-renderer": "~2.3.1",
|
||||
"vue-ssr-html-stream": "^2.2.0",
|
||||
"vue-ssr-webpack-plugin": "^3.0.0",
|
||||
"vue-template-compiler": "~2.2.6",
|
||||
"vuex": "^2.3.0",
|
||||
"vue-template-compiler": "~2.3.1",
|
||||
"vuex": "^2.3.1",
|
||||
"webpack": "^2.4.1",
|
||||
"webpack-bundle-analyzer": "^2.4.0",
|
||||
"webpack-dev-middleware": "^1.10.1",
|
||||
"webpack-dev-middleware": "^1.10.2",
|
||||
"webpack-hot-middleware": "^2.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^0.19.1",
|
||||
"babel-eslint": "^7.2.2",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-plugin-array-includes": "^2.0.3",
|
||||
"babel-plugin-transform-async-to-generator": "^6.24.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
@ -111,10 +111,10 @@
|
||||
"eslint-plugin-node": "^4.2.2",
|
||||
"eslint-plugin-promise": "^3.5.0",
|
||||
"eslint-plugin-standard": "^3.0.1",
|
||||
"finalhandler": "^1.0.1",
|
||||
"jsdom": "^9.12.0",
|
||||
"finalhandler": "^1.0.2",
|
||||
"jsdom": "^10.1.0",
|
||||
"json-loader": "^0.5.4",
|
||||
"nyc": "^10.2.0",
|
||||
"nyc": "^10.3.0",
|
||||
"request": "^2.81.0",
|
||||
"request-promise-native": "^1.0.3",
|
||||
"webpack-node-externals": "^1.5.4"
|
||||
|
@ -17,12 +17,11 @@ test.before('Init Nuxt.js', async t => {
|
||||
test('Check .nuxt/router.js', t => {
|
||||
return readFile(resolve(__dirname, './fixtures/dynamic-routes/.nuxt/router.js'), 'utf-8')
|
||||
.then((routerFile) => {
|
||||
routerFile = routerFile.slice(
|
||||
routerFile.indexOf('routes: ['),
|
||||
-3
|
||||
)
|
||||
.replace('routes: [', '[')
|
||||
.replace(/ _[0-9A-z]+,/g, ' "",')
|
||||
routerFile = routerFile
|
||||
.slice(routerFile.indexOf('routes: ['))
|
||||
.replace('routes: [', '[')
|
||||
.replace(/ _[0-9A-z]+,/g, ' "",')
|
||||
routerFile = routerFile.substr(routerFile.indexOf('['), routerFile.lastIndexOf(']') + 1)
|
||||
let routes = eval('( ' + routerFile + ')') // eslint-disable-line no-eval
|
||||
// pages/index.vue
|
||||
t.is(routes[0].path, '/')
|
||||
|
@ -37,7 +37,7 @@ test('/ with renderAndGetWindow()', async t => {
|
||||
await nuxt.renderAndGetWindow(url('/'))
|
||||
} catch (e) {
|
||||
t.true(e.message.includes('Could not load the nuxt app'))
|
||||
t.true(e.body.includes('not_defined is not defined'))
|
||||
t.true(e.body.includes('render function or template not defined in component'))
|
||||
}
|
||||
})
|
||||
|
||||
|
6
test/fixtures/basic/store/foo/bar.js
vendored
6
test/fixtures/basic/store/foo/bar.js
vendored
@ -1,5 +1,7 @@
|
||||
export const state = {
|
||||
baz: 'Vuex Nested Modules'
|
||||
export const state = () => {
|
||||
return {
|
||||
baz: 'Vuex Nested Modules'
|
||||
}
|
||||
}
|
||||
|
||||
export const getters = {
|
||||
|
2
test/fixtures/with-config/store/index.js
vendored
2
test/fixtures/with-config/store/index.js
vendored
@ -3,7 +3,7 @@ import Vuex from 'vuex'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
const store = new Vuex.Store({
|
||||
const store = () => new Vuex.Store({
|
||||
state: {
|
||||
counter: 0
|
||||
},
|
||||
|
@ -82,7 +82,7 @@ test('/test/about-bis (added with extendRoutes)', async t => {
|
||||
|
||||
test('Check stats.json generated by build.analyze', t => {
|
||||
const stats = require(resolve(__dirname, 'fixtures/with-config/.nuxt/dist/stats.json'))
|
||||
t.is(stats.assets.length, 26)
|
||||
t.is(stats.assets.length, 27)
|
||||
})
|
||||
|
||||
// Close server and ask nuxt to stop listening to file changes
|
||||
|
Loading…
Reference in New Issue
Block a user