style: format with prettier

This commit is contained in:
Pooya Parsa 2018-01-13 08:52:11 +03:30
parent b3e9952976
commit 10d1b5974c
41 changed files with 958 additions and 541 deletions

View File

@ -11,7 +11,15 @@ const webpackDevMiddleware = require('webpack-dev-middleware')
const webpackHotMiddleware = require('webpack-hot-middleware') const webpackHotMiddleware = require('webpack-hot-middleware')
const Debug = require('debug') const Debug = require('debug')
const Glob = require('glob') const Glob = require('glob')
const { r, wp, wChunk, createRoutes, sequence, relativeTo, waitFor } = require('../common/utils') const {
r,
wp,
wChunk,
createRoutes,
sequence,
relativeTo,
waitFor
} = require('../common/utils')
const { Options } = require('../common') const { Options } = require('../common')
const clientWebpackConfig = require('./webpack/client.config.js') const clientWebpackConfig = require('./webpack/client.config.js')
const serverWebpackConfig = require('./webpack/server.config.js') const serverWebpackConfig = require('./webpack/server.config.js')
@ -41,7 +49,8 @@ module.exports = class Builder {
this.webpackStats = this.options.dev ? false : this.options.build.stats this.webpackStats = this.options.dev ? false : this.options.build.stats
// Helper to resolve build paths // Helper to resolve build paths
this.relativeToBuild = (...args) => relativeTo(this.options.buildDir, ...args) this.relativeToBuild = (...args) =>
relativeTo(this.options.buildDir, ...args)
this._buildStatus = STATUS.INITIAL this._buildStatus = STATUS.INITIAL
@ -56,28 +65,34 @@ module.exports = class Builder {
} }
get plugins() { get plugins() {
return _.uniqBy(this.options.plugins.map((p, i) => { return _.uniqBy(
this.options.plugins.map((p, i) => {
if (typeof p === 'string') p = { src: p } if (typeof p === 'string') p = { src: p }
const pluginBaseName = basename(p.src, extname(p.src)).replace(/[^a-zA-Z?\d\s:]/g, '') const pluginBaseName = basename(p.src, extname(p.src)).replace(
/[^a-zA-Z?\d\s:]/g,
''
)
return { return {
src: this.nuxt.resolveAlias(p.src), src: this.nuxt.resolveAlias(p.src),
ssr: (p.ssr !== false), ssr: p.ssr !== false,
name: 'nuxt_plugin_' + pluginBaseName + '_' + hash(p.src) name: 'nuxt_plugin_' + pluginBaseName + '_' + hash(p.src)
} }
}), p => p.name) }),
p => p.name
)
} }
vendor() { vendor() {
return [ return ['vue', 'vue-router', 'vue-meta', this.options.store && 'vuex']
'vue', .concat(
'vue-router', this.options.build.extractCSS && [
'vue-meta',
this.options.store && 'vuex'
].concat(this.options.build.extractCSS && [
// https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/456 // https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/456
'vue-style-loader/lib/addStylesClient', 'vue-style-loader/lib/addStylesClient',
'css-loader/lib/css-base' 'css-loader/lib/css-base'
]).concat(this.options.build.vendor).filter(v => v) ]
)
.concat(this.options.build.vendor)
.filter(v => v)
} }
vendorEntries() { vendorEntries() {
@ -87,7 +102,7 @@ module.exports = class Builder {
vendor.forEach(v => { vendor.forEach(v => {
try { try {
require.resolve(v) require.resolve(v)
vendorEntries[v] = [ v ] vendorEntries[v] = [v]
} catch (e) { } catch (e) {
// Ignore // Ignore
} }
@ -125,9 +140,13 @@ module.exports = class Builder {
if (!existsSync(join(this.options.srcDir, 'pages'))) { if (!existsSync(join(this.options.srcDir, 'pages'))) {
let dir = this.options.srcDir let dir = this.options.srcDir
if (existsSync(join(this.options.srcDir, '..', 'pages'))) { if (existsSync(join(this.options.srcDir, '..', 'pages'))) {
throw new Error(`No \`pages\` directory found in ${dir}. Did you mean to run \`nuxt\` in the parent (\`../\`) directory?`) throw new Error(
`No \`pages\` directory found in ${dir}. Did you mean to run \`nuxt\` in the parent (\`../\`) directory?`
)
} else { } else {
throw new Error(`Couldn't find a \`pages\` directory in ${dir}. Please create one under the project root`) throw new Error(
`Couldn't find a \`pages\` directory in ${dir}. Please create one under the project root`
)
} }
} }
} }
@ -158,10 +177,14 @@ module.exports = class Builder {
} }
getBabelOptions({ isServer }) { getBabelOptions({ isServer }) {
const options = _.defaults({}, { const options = _.defaults(
{},
{
babelrc: false, babelrc: false,
cacheDirectory: !!this.options.dev cacheDirectory: !!this.options.dev
}, this.options.build.babel) },
this.options.build.babel
)
if (typeof options.presets === 'function') { if (typeof options.presets === 'function') {
options.presets = options.presets({ isServer }) options.presets = options.presets({ isServer })
@ -216,7 +239,9 @@ module.exports = class Builder {
] ]
const templateVars = { const templateVars = {
options: this.options, options: this.options,
extensions: this.options.extensions.map((ext) => ext.replace(/^\./, '')).join('|'), extensions: this.options.extensions
.map(ext => ext.replace(/^\./, ''))
.join('|'),
messages: this.options.messages, messages: this.options.messages,
uniqBy: _.uniqBy, uniqBy: _.uniqBy,
isDev: this.options.dev, isDev: this.options.dev,
@ -232,30 +257,48 @@ module.exports = class Builder {
appPath: './App.js', appPath: './App.js',
ignorePrefix: this.options.ignorePrefix, ignorePrefix: this.options.ignorePrefix,
layouts: Object.assign({}, this.options.layouts), layouts: Object.assign({}, this.options.layouts),
loading: typeof this.options.loading === 'string' ? this.relativeToBuild(this.options.srcDir, this.options.loading) : this.options.loading, loading:
typeof this.options.loading === 'string'
? this.relativeToBuild(this.options.srcDir, this.options.loading)
: this.options.loading,
transition: this.options.transition, transition: this.options.transition,
layoutTransition: this.options.layoutTransition, layoutTransition: this.options.layoutTransition,
components: { components: {
ErrorPage: this.options.ErrorPage ? this.relativeToBuild(this.options.ErrorPage) : null ErrorPage: this.options.ErrorPage
? this.relativeToBuild(this.options.ErrorPage)
: null
} }
} }
// -- Layouts -- // -- Layouts --
if (existsSync(resolve(this.options.srcDir, 'layouts'))) { if (existsSync(resolve(this.options.srcDir, 'layouts'))) {
const layoutsFiles = await glob('layouts/**/*.{vue,js}', { cwd: this.options.srcDir, ignore: [`layouts/**/${this.options.ignorePrefix}*.{vue,js}`] }) const layoutsFiles = await glob('layouts/**/*.{vue,js}', {
cwd: this.options.srcDir,
ignore: [`layouts/**/${this.options.ignorePrefix}*.{vue,js}`]
})
let hasErrorLayout = false let hasErrorLayout = false
layoutsFiles.forEach((file) => { layoutsFiles.forEach(file => {
let name = file.split('/').slice(1).join('/').replace(/\.(vue|js)$/, '') let name = file
.split('/')
.slice(1)
.join('/')
.replace(/\.(vue|js)$/, '')
if (name === 'error') { if (name === 'error') {
hasErrorLayout = true hasErrorLayout = true
return return
} }
if (!templateVars.layouts[name] || /\.vue$/.test(file)) { if (!templateVars.layouts[name] || /\.vue$/.test(file)) {
templateVars.layouts[name] = this.relativeToBuild(this.options.srcDir, file) templateVars.layouts[name] = this.relativeToBuild(
this.options.srcDir,
file
)
} }
}) })
if (!templateVars.components.ErrorPage && hasErrorLayout) { if (!templateVars.components.ErrorPage && hasErrorLayout) {
templateVars.components.ErrorPage = this.relativeToBuild(this.options.srcDir, 'layouts/error.vue') templateVars.components.ErrorPage = this.relativeToBuild(
this.options.srcDir,
'layouts/error.vue'
)
} }
} }
// If no default layout, create its folder and add the default folder // If no default layout, create its folder and add the default folder
@ -270,24 +313,39 @@ module.exports = class Builder {
// If user defined a custom method to create routes // If user defined a custom method to create routes
if (this._nuxtPages) { if (this._nuxtPages) {
// Use nuxt.js createRoutes bases on pages/ // Use nuxt.js createRoutes bases on pages/
const files = {}; const files = {}
(await glob('pages/**/*.{vue,js}', { cwd: this.options.srcDir, ignore: [`pages/**/${this.options.ignorePrefix}*.{vue,js}`] })).forEach(f => { ;(await glob('pages/**/*.{vue,js}', {
cwd: this.options.srcDir,
ignore: [`pages/**/${this.options.ignorePrefix}*.{vue,js}`]
})).forEach(f => {
const key = f.replace(/\.(js|vue)$/, '') const key = f.replace(/\.(js|vue)$/, '')
if (/\.vue$/.test(f) || !files[key]) { if (/\.vue$/.test(f) || !files[key]) {
files[key] = f files[key] = f
} }
}) })
templateVars.router.routes = createRoutes(Object.values(files), this.options.srcDir) templateVars.router.routes = createRoutes(
Object.values(files),
this.options.srcDir
)
} else { } else {
templateVars.router.routes = this.options.build.createRoutes(this.options.srcDir) templateVars.router.routes = this.options.build.createRoutes(
this.options.srcDir
)
} }
await this.nuxt.callHook('build:extendRoutes', templateVars.router.routes, r) await this.nuxt.callHook(
'build:extendRoutes',
templateVars.router.routes,
r
)
// router.extendRoutes method // router.extendRoutes method
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
const extendedRoutes = this.options.router.extendRoutes(templateVars.router.routes, r) const extendedRoutes = this.options.router.extendRoutes(
templateVars.router.routes,
r
)
// Only overwrite routes when something is returned for backwards compatibility // Only overwrite routes when something is returned for backwards compatibility
if (extendedRoutes !== undefined) { if (extendedRoutes !== undefined) {
templateVars.router.routes = extendedRoutes templateVars.router.routes = extendedRoutes
@ -304,9 +362,12 @@ module.exports = class Builder {
} }
// Resolve template files // Resolve template files
const customTemplateFiles = this.options.build.templates.map(t => t.dst || basename(t.src || t)) const customTemplateFiles = this.options.build.templates.map(
t => t.dst || basename(t.src || t)
)
templatesFiles = templatesFiles.map(file => { templatesFiles = templatesFiles
.map(file => {
// Skip if custom file was already provided in build.templates[] // Skip if custom file was already provided in build.templates[]
if (customTemplateFiles.indexOf(file) !== -1) { if (customTemplateFiles.indexOf(file) !== -1) {
return return
@ -316,29 +377,41 @@ module.exports = class Builder {
const customFileExists = existsSync(customPath) const customFileExists = existsSync(customPath)
return { return {
src: customFileExists src: customFileExists ? customPath : r(this.options.nuxtAppDir, file),
? customPath
: r(this.options.nuxtAppDir, file),
dst: file, dst: file,
custom: customFileExists custom: customFileExists
} }
}).filter(i => !!i) })
.filter(i => !!i)
// -- Custom templates -- // -- Custom templates --
// Add custom template files // Add custom template files
templatesFiles = templatesFiles.concat(this.options.build.templates.map(t => { templatesFiles = templatesFiles.concat(
return Object.assign({ this.options.build.templates.map(t => {
return Object.assign(
{
src: r(this.options.srcDir, t.src || t), src: r(this.options.srcDir, t.src || t),
dst: t.dst || basename(t.src || t), dst: t.dst || basename(t.src || t),
custom: true custom: true
}, t) },
})) t
)
})
)
// -- Loading indicator -- // -- Loading indicator --
if (this.options.loadingIndicator.name) { if (this.options.loadingIndicator.name) {
const indicatorPath1 = resolve(this.options.nuxtAppDir, 'views/loading', this.options.loadingIndicator.name + '.html') const indicatorPath1 = resolve(
const indicatorPath2 = this.nuxt.resolveAlias(this.options.loadingIndicator.name) this.options.nuxtAppDir,
const indicatorPath = existsSync(indicatorPath1) ? indicatorPath1 : (existsSync(indicatorPath2) ? indicatorPath2 : null) 'views/loading',
this.options.loadingIndicator.name + '.html'
)
const indicatorPath2 = this.nuxt.resolveAlias(
this.options.loadingIndicator.name
)
const indicatorPath = existsSync(indicatorPath1)
? indicatorPath1
: existsSync(indicatorPath2) ? indicatorPath2 : null
if (indicatorPath) { if (indicatorPath) {
templatesFiles.push({ templatesFiles.push({
src: indicatorPath, src: indicatorPath,
@ -347,14 +420,24 @@ module.exports = class Builder {
}) })
} else { } else {
/* istanbul ignore next */ /* istanbul ignore next */
console.error(`Could not fetch loading indicator: ${this.options.loadingIndicator.name}`) // eslint-disable-line no-console // eslint-disable-next-line no-console
console.error(
`Could not fetch loading indicator: ${
this.options.loadingIndicator.name
}`
)
} }
} }
await this.nuxt.callHook('build:templates', { templatesFiles, templateVars, resolve: r }) await this.nuxt.callHook('build:templates', {
templatesFiles,
templateVars,
resolve: r
})
// Interpret and move template files to .nuxt/ // Interpret and move template files to .nuxt/
await Promise.all(templatesFiles.map(async ({ src, dst, options, custom }) => { await Promise.all(
templatesFiles.map(async ({ src, dst, options, custom }) => {
// Add template to watchers // Add template to watchers
this.options.build.watch.push(src) this.options.build.watch.push(src)
// Render template to dst // Render template to dst
@ -373,12 +456,14 @@ module.exports = class Builder {
relativeToBuild: this.relativeToBuild relativeToBuild: this.relativeToBuild
} }
}) })
content = template(Object.assign({}, templateVars, { content = template(
Object.assign({}, templateVars, {
options: options || {}, options: options || {},
custom, custom,
src, src,
dst dst
})) })
)
} catch (err) { } catch (err) {
/* istanbul ignore next */ /* istanbul ignore next */
throw new Error(`Could not compile template ${src}: ${err.message}`) throw new Error(`Could not compile template ${src}: ${err.message}`)
@ -388,7 +473,8 @@ module.exports = class Builder {
await mkdirp(dirname(path)) await mkdirp(dirname(path))
// Write file // Write file
await writeFile(path, content, 'utf8') await writeFile(path, content, 'utf8')
})) })
)
} }
async webpackBuild() { async webpackBuild() {
@ -443,13 +529,20 @@ module.exports = class Builder {
}) })
// Start Builds // Start Builds
await sequence(this.compilers, (compiler) => new Promise(async (resolve, reject) => { await sequence(
this.compilers,
compiler =>
new Promise(async (resolve, reject) => {
const name = compiler.options.name const name = compiler.options.name
await this.nuxt.callHook('build:compile', { name, compiler }) await this.nuxt.callHook('build:compile', { name, compiler })
// Resolve only when compiler emit done event // Resolve only when compiler emit done event
compiler.plugin('done', async (stats) => { compiler.plugin('done', async stats => {
await this.nuxt.callHook('build:compiled', { name, compiler, stats }) await this.nuxt.callHook('build:compiled', {
name,
compiler,
stats
})
// Reload renderer if available // Reload renderer if available
this.nuxt.renderer.loadResources(sharedFS || require('fs')) this.nuxt.renderer.loadResources(sharedFS || require('fs'))
// Resolve on next tick // Resolve on next tick
@ -471,7 +564,7 @@ module.exports = class Builder {
} }
// Server, build and watch for changes // Server, build and watch for changes
this.compilersWatching.push( this.compilersWatching.push(
compiler.watch(this.options.watchers.webpack, (err) => { compiler.watch(this.options.watchers.webpack, err => {
/* istanbul ignore if */ /* istanbul ignore if */
if (err) return reject(err) if (err) return reject(err)
}) })
@ -494,26 +587,43 @@ module.exports = class Builder {
return reject(new Error('Webpack build exited with errors')) return reject(new Error('Webpack build exited with errors'))
} }
}) })
})) })
)
} }
webpackDev(compiler) { webpackDev(compiler) {
debug('Adding webpack middleware...') debug('Adding webpack middleware...')
// Create webpack dev middleware // Create webpack dev middleware
this.webpackDevMiddleware = promisify(webpackDevMiddleware(compiler, Object.assign({ this.webpackDevMiddleware = promisify(
webpackDevMiddleware(
compiler,
Object.assign(
{
publicPath: this.options.build.publicPath, publicPath: this.options.build.publicPath,
stats: this.webpackStats, stats: this.webpackStats,
logLevel: 'silent', logLevel: 'silent',
watchOptions: this.options.watchers.webpack watchOptions: this.options.watchers.webpack
}, this.options.build.devMiddleware))) },
this.options.build.devMiddleware
)
)
)
this.webpackDevMiddleware.close = promisify(this.webpackDevMiddleware.close) this.webpackDevMiddleware.close = promisify(this.webpackDevMiddleware.close)
this.webpackHotMiddleware = promisify(webpackHotMiddleware(compiler, Object.assign({ this.webpackHotMiddleware = promisify(
webpackHotMiddleware(
compiler,
Object.assign(
{
log: false, log: false,
heartbeat: 10000 heartbeat: 10000
}, this.options.build.hotMiddleware))) },
this.options.build.hotMiddleware
)
)
)
// Inject to renderer instance // Inject to renderer instance
if (this.nuxt.renderer) { if (this.nuxt.renderer) {
@ -550,13 +660,17 @@ module.exports = class Builder {
const refreshFiles = _.debounce(() => this.generateRoutesAndFiles(), 200) const refreshFiles = _.debounce(() => this.generateRoutesAndFiles(), 200)
// Watch for src Files // Watch for src Files
this.filesWatcher = chokidar.watch(patterns, options) this.filesWatcher = chokidar
.watch(patterns, options)
.on('add', refreshFiles) .on('add', refreshFiles)
.on('unlink', refreshFiles) .on('unlink', refreshFiles)
// Watch for custom provided files // Watch for custom provided files
const watchFiles = _.map(_.uniq(this.options.build.watch), p => upath.normalizeSafe(p)) const watchFiles = _.map(_.uniq(this.options.build.watch), p =>
this.customFilesWatcher = chokidar.watch(watchFiles, options) upath.normalizeSafe(p)
)
this.customFilesWatcher = chokidar
.watch(watchFiles, options)
.on('change', refreshFiles) .on('change', refreshFiles)
} }
@ -578,7 +692,11 @@ module.exports = class Builder {
async generateConfig() { async generateConfig() {
const config = resolve(this.options.buildDir, 'build.config.js') const config = resolve(this.options.buildDir, 'build.config.js')
const options = _.omit(this.options, Options.unsafeKeys) const options = _.omit(this.options, Options.unsafeKeys)
await writeFile(config, `module.exports = ${JSON.stringify(options, null, ' ')}`, 'utf8') await writeFile(
config,
`module.exports = ${JSON.stringify(options, null, ' ')}`,
'utf8'
)
} }
} }

View File

@ -11,6 +11,7 @@ const { resolve } = require('path')
const { existsSync } = require('fs') const { existsSync } = require('fs')
const Debug = require('debug') const Debug = require('debug')
const Chalk = require('chalk') const Chalk = require('chalk')
const { printWarn } = require('../../common/utils')
const base = require('./base.config.js') const base = require('./base.config.js')
const debug = Debug('nuxt:build') const debug = Debug('nuxt:build')
@ -43,7 +44,10 @@ module.exports = function webpackClientConfig() {
// Env object defined in nuxt.config.js // Env object defined in nuxt.config.js
let env = {} let env = {}
each(this.options.env, (value, key) => { each(this.options.env, (value, key) => {
env['process.env.' + key] = (['boolean', 'number'].indexOf(typeof value) !== -1 ? value : JSON.stringify(value)) env['process.env.' + key] =
['boolean', 'number'].indexOf(typeof value) !== -1
? value
: JSON.stringify(value)
}) })
// Generate output HTML for SPA // Generate output HTML for SPA
@ -85,36 +89,47 @@ module.exports = function webpackClientConfig() {
// Define Env // Define Env
config.plugins.push( config.plugins.push(
new webpack.DefinePlugin(Object.assign(env, { new webpack.DefinePlugin(
'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV || (this.options.dev ? 'development' : 'production')), Object.assign(env, {
'process.env.NODE_ENV': JSON.stringify(
env.NODE_ENV || (this.options.dev ? 'development' : 'production')
),
'process.env.VUE_ENV': JSON.stringify('client'), 'process.env.VUE_ENV': JSON.stringify('client'),
'process.mode': JSON.stringify(this.options.mode), 'process.mode': JSON.stringify(this.options.mode),
'process.browser': true, 'process.browser': true,
'process.client': true, 'process.client': true,
'process.server': false, 'process.server': false,
'process.static': this.isStatic 'process.static': this.isStatic
})) })
)
) )
// Build progress bar // Build progress bar
if (this.options.build.profile) { if (this.options.build.profile) {
config.plugins.push(new ProgressPlugin({ config.plugins.push(
new ProgressPlugin({
profile: true profile: true
})) })
)
} else { } else {
config.plugins.push(new ProgressBarPlugin({ config.plugins.push(
new ProgressBarPlugin({
complete: Chalk.green('█'), complete: Chalk.green('█'),
incomplete: Chalk.white('█'), incomplete: Chalk.white('█'),
format: ' :bar ' + Chalk.green.bold(':percent') + ' :msg', format: ' :bar ' + Chalk.green.bold(':percent') + ' :msg',
clear: false clear: false
})) })
)
} }
const shouldClearConsole = this.options.build.stats !== false && const shouldClearConsole =
this.options.build.stats !== false &&
this.options.build.stats !== 'errors-only' this.options.build.stats !== 'errors-only'
// Add friendly error plugin // Add friendly error plugin
config.plugins.push(new FriendlyErrorsWebpackPlugin({ clearConsole: shouldClearConsole })) config.plugins.push(
new FriendlyErrorsWebpackPlugin({ clearConsole: shouldClearConsole })
)
// -------------------------------------- // --------------------------------------
// Dev specific config // Dev specific config
@ -126,7 +141,9 @@ module.exports = function webpackClientConfig() {
// Add HMR support // Add HMR support
config.entry.app = [ config.entry.app = [
// https://github.com/glenjamin/webpack-hot-middleware#config // https://github.com/glenjamin/webpack-hot-middleware#config
`webpack-hot-middleware/client?name=client&reload=true&timeout=30000&path=${this.options.router.base}/__webpack_hmr`.replace(/\/\//g, '/'), `webpack-hot-middleware/client?name=client&reload=true&timeout=30000&path=${
this.options.router.base
}/__webpack_hmr`.replace(/\/\//g, '/'),
config.entry.app config.entry.app
] ]
config.plugins.push( config.plugins.push(
@ -156,7 +173,9 @@ module.exports = function webpackClientConfig() {
// https://github.com/webpack-contrib/uglifyjs-webpack-plugin // https://github.com/webpack-contrib/uglifyjs-webpack-plugin
if (this.options.build.uglify !== false) { if (this.options.build.uglify !== false) {
config.plugins.push( config.plugins.push(
new UglifyJSPlugin(Object.assign({ new UglifyJSPlugin(
Object.assign(
{
// cache: true, // cache: true,
sourceMap: true, sourceMap: true,
parallel: true, parallel: true,
@ -168,13 +187,18 @@ module.exports = function webpackClientConfig() {
comments: /^\**!|@preserve|@license|@cc_on/ comments: /^\**!|@preserve|@license|@cc_on/
} }
} }
}, this.options.build.uglify)) },
this.options.build.uglify
)
)
) )
} }
// Webpack Bundle Analyzer // Webpack Bundle Analyzer
if (this.options.build.analyze) { if (this.options.build.analyze) {
config.plugins.push(new BundleAnalyzerPlugin(Object.assign({}, this.options.build.analyze))) config.plugins.push(
new BundleAnalyzerPlugin(Object.assign({}, this.options.build.analyze))
)
} }
} }
@ -183,7 +207,7 @@ module.exports = function webpackClientConfig() {
const isDev = this.options.dev const isDev = this.options.dev
const extendedConfig = this.options.build.extend.call(this, config, { const extendedConfig = this.options.build.extend.call(this, config, {
get dev() { get dev() {
console.warn('dev has been deprecated in build.extend(), please use isDev') // eslint-disable-line no-console printWarn('dev has been deprecated in build.extend(), please use isDev')
return isDev return isDev
}, },
isDev, isDev,

View File

@ -2,9 +2,11 @@ module.exports = class WarnFixPlugin {
apply(compiler) /* istanbul ignore next */ { apply(compiler) /* istanbul ignore next */ {
compiler.plugin('done', stats => { compiler.plugin('done', stats => {
stats.compilation.warnings = stats.compilation.warnings.filter(warn => { stats.compilation.warnings = stats.compilation.warnings.filter(warn => {
if (warn.name === 'ModuleDependencyWarning' && if (
warn.name === 'ModuleDependencyWarning' &&
warn.message.includes(`export 'default'`) && warn.message.includes(`export 'default'`) &&
warn.message.includes('nuxt_plugin_')) { warn.message.includes('nuxt_plugin_')
) {
return false return false
} }
return true return true

View File

@ -14,7 +14,13 @@ module.exports = function postcssConfig() {
// Search for postCSS config file and use it if exists // Search for postCSS config file and use it if exists
// https://github.com/michael-ciniawsky/postcss-load-config // https://github.com/michael-ciniawsky/postcss-load-config
for (let dir of [this.options.srcDir, this.options.rootDir]) { for (let dir of [this.options.srcDir, this.options.rootDir]) {
for (let file of ['postcss.config.js', '.postcssrc.js', '.postcssrc', '.postcssrc.json', '.postcssrc.yaml']) { for (let file of [
'postcss.config.js',
'.postcssrc.js',
'.postcssrc',
'.postcssrc.json',
'.postcssrc.yaml'
]) {
if (existsSync(resolve(dir, file))) { if (existsSync(resolve(dir, file))) {
const postcssConfigPath = resolve(dir, file) const postcssConfigPath = resolve(dir, file)
return { return {
@ -34,7 +40,8 @@ module.exports = function postcssConfig() {
// Apply default plugins // Apply default plugins
if (isPureObject(config)) { if (isPureObject(config)) {
config = Object.assign({ config = Object.assign(
{
useConfigFile: false, useConfigFile: false,
sourceMap: this.options.build.cssSourceMap, sourceMap: this.options.build.cssSourceMap,
plugins: { plugins: {
@ -54,7 +61,9 @@ module.exports = function postcssConfig() {
// http://cssnext.io/postcss // http://cssnext.io/postcss
'postcss-cssnext': {} 'postcss-cssnext': {}
} }
}, config) },
config
)
} }
// Map postcss plugins into instances on object mode once // Map postcss plugins into instances on object mode once
@ -66,7 +75,8 @@ module.exports = function postcssConfig() {
if (opts === false) return // Disabled if (opts === false) return // Disabled
const instance = plugin(opts) const instance = plugin(opts)
return instance return instance
}).filter(e => e) })
.filter(e => e)
} }
return config return config

View File

@ -4,6 +4,7 @@ const nodeExternals = require('webpack-node-externals')
const { each } = require('lodash') const { each } = require('lodash')
const { resolve } = require('path') const { resolve } = require('path')
const { existsSync } = require('fs') const { existsSync } = require('fs')
const { printWarn } = require('../../common/utils')
const base = require('./base.config.js') const base = require('./base.config.js')
/* /*
@ -17,7 +18,10 @@ module.exports = function webpackServerConfig() {
// Env object defined in nuxt.config.js // Env object defined in nuxt.config.js
let env = {} let env = {}
each(this.options.env, (value, key) => { each(this.options.env, (value, key) => {
env['process.env.' + key] = (['boolean', 'number'].indexOf(typeof value) !== -1 ? value : JSON.stringify(value)) env['process.env.' + key] =
['boolean', 'number'].indexOf(typeof value) !== -1
? value
: JSON.stringify(value)
}) })
// Config devtool // Config devtool
@ -40,15 +44,19 @@ module.exports = function webpackServerConfig() {
new VueSSRServerPlugin({ new VueSSRServerPlugin({
filename: 'server-bundle.json' filename: 'server-bundle.json'
}), }),
new webpack.DefinePlugin(Object.assign(env, { new webpack.DefinePlugin(
'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV || (this.options.dev ? 'development' : 'production')), Object.assign(env, {
'process.env.NODE_ENV': JSON.stringify(
env.NODE_ENV || (this.options.dev ? 'development' : 'production')
),
'process.env.VUE_ENV': JSON.stringify('server'), 'process.env.VUE_ENV': JSON.stringify('server'),
'process.mode': JSON.stringify(this.options.mode), 'process.mode': JSON.stringify(this.options.mode),
'process.browser': false, 'process.browser': false,
'process.client': false, 'process.client': false,
'process.server': true, 'process.server': true,
'process.static': this.isStatic 'process.static': this.isStatic
})) })
)
]) ])
}) })
@ -56,11 +64,13 @@ module.exports = function webpackServerConfig() {
// https://github.com/liady/webpack-node-externals // https://github.com/liady/webpack-node-externals
this.options.modulesDir.forEach(dir => { this.options.modulesDir.forEach(dir => {
if (existsSync(dir)) { if (existsSync(dir)) {
config.externals.push(nodeExternals({ config.externals.push(
nodeExternals({
// load non-javascript files with extensions, presumably via loaders // load non-javascript files with extensions, presumably via loaders
whitelist: [/es6-promise|\.(?!(?:js|json)$).{1,5}$/i], whitelist: [/es6-promise|\.(?!(?:js|json)$).{1,5}$/i],
modulesDir: dir modulesDir: dir
})) })
)
} }
}) })
@ -69,7 +79,7 @@ module.exports = function webpackServerConfig() {
const isDev = this.options.dev const isDev = this.options.dev
const extendedConfig = this.options.build.extend.call(this, config, { const extendedConfig = this.options.build.extend.call(this, config, {
get dev() { get dev() {
console.warn('dev has been deprecated in build.extend(), please use isDev') // eslint-disable-line no-console printWarn('dev has been deprecated in build.extend(), please use isDev')
return isDev return isDev
}, },
isDev, isDev,

View File

@ -6,8 +6,12 @@ module.exports = function styleLoader(ext, loaders = [], isVueLoader = false) {
const sourceMap = Boolean(this.options.build.cssSourceMap) const sourceMap = Boolean(this.options.build.cssSourceMap)
// Normalize loaders // Normalize loaders
loaders = (Array.isArray(loaders) ? loaders : [loaders]) loaders = (Array.isArray(loaders) ? loaders : [loaders]).map(loader =>
.map(loader => Object.assign({ options: { sourceMap } }, typeof loader === 'string' ? { loader } : loader)) Object.assign(
{ options: { sourceMap } },
typeof loader === 'string' ? { loader } : loader
)
)
// Prepare vue-style-loader // Prepare vue-style-loader
// https://github.com/vuejs/vue-style-loader // https://github.com/vuejs/vue-style-loader
@ -21,8 +25,14 @@ module.exports = function styleLoader(ext, loaders = [], isVueLoader = false) {
// style-resources-loader // style-resources-loader
// https://github.com/yenshih/style-resources-loader // https://github.com/yenshih/style-resources-loader
if (this.options.build.styleResources[ext]) { if (this.options.build.styleResources[ext]) {
const patterns = Array.isArray(this.options.build.styleResources[ext]) ? this.options.build.styleResources[ext] : [ this.options.build.styleResources[ext] ] const patterns = Array.isArray(this.options.build.styleResources[ext])
const options = Object.assign({}, (this.options.build.styleResources.options || {}), { patterns }) ? this.options.build.styleResources[ext]
: [this.options.build.styleResources[ext]]
const options = Object.assign(
{},
this.options.build.styleResources.options || {},
{ patterns }
)
loaders.push({ loaders.push({
loader: 'style-resources-loader', loader: 'style-resources-loader',
@ -77,9 +87,9 @@ module.exports = function styleLoader(ext, loaders = [], isVueLoader = false) {
options: { sourceMap } options: { sourceMap }
} }
return this.options.dev ? [ hotLoader ].concat(extractLoader) : extractLoader return this.options.dev ? [hotLoader].concat(extractLoader) : extractLoader
} }
// -- Without extractCSS -- // -- Without extractCSS --
return [ vueStyleLoader ].concat(loaders) return [vueStyleLoader].concat(loaders)
} }

View File

@ -9,17 +9,22 @@ module.exports = function vueLoader({ isServer }) {
cssSourceMap: this.options.build.cssSourceMap, cssSourceMap: this.options.build.cssSourceMap,
preserveWhitespace: false, preserveWhitespace: false,
loaders: { loaders: {
'js': { js: {
loader: 'babel-loader', loader: 'babel-loader',
options: this.getBabelOptions({ isServer }) options: this.getBabelOptions({ isServer })
}, },
// Note: do not nest the `postcss` option under `loaders` // Note: do not nest the `postcss` option under `loaders`
'css': styleLoader.call(this, 'css', [], true), css: styleLoader.call(this, 'css', [], true),
'less': styleLoader.call(this, 'less', 'less-loader', true), less: styleLoader.call(this, 'less', 'less-loader', true),
'scss': styleLoader.call(this, 'scss', 'sass-loader', true), scss: styleLoader.call(this, 'scss', 'sass-loader', true),
'sass': styleLoader.call(this, 'sass', {loader: 'sass-loader', options: { indentedSyntax: true }}, true), sass: styleLoader.call(
'stylus': styleLoader.call(this, 'stylus', 'stylus-loader', true), this,
'styl': styleLoader.call(this, 'stylus', 'stylus-loader', true) 'sass',
{ loader: 'sass-loader', options: { indentedSyntax: true } },
true
),
stylus: styleLoader.call(this, 'stylus', 'stylus-loader', true),
styl: styleLoader.call(this, 'stylus', 'stylus-loader', true)
}, },
template: { template: {
doctype: 'html' // For pug, see https://github.com/vuejs/vue-loader/issues/55 doctype: 'html' // For pug, see https://github.com/vuejs/vue-loader/issues/55

View File

@ -19,7 +19,11 @@ Options.from = function (_options) {
if (options.loading === true) { if (options.loading === true) {
delete options.loading delete options.loading
} }
if (options.router && options.router.middleware && !Array.isArray(options.router.middleware)) { if (
options.router &&
options.router.middleware &&
!Array.isArray(options.router.middleware)
) {
options.router.middleware = [options.router.middleware] options.router.middleware = [options.router.middleware]
} }
if (options.router && typeof options.router.base === 'string') { if (options.router && typeof options.router.base === 'string') {
@ -32,7 +36,7 @@ Options.from = function (_options) {
options.layoutTransition = { name: options.layoutTransition } options.layoutTransition = { name: options.layoutTransition }
} }
if (typeof options.extensions === 'string') { if (typeof options.extensions === 'string') {
options.extensions = [ options.extensions ] options.extensions = [options.extensions]
} }
const hasValue = v => typeof v === 'string' && v const hasValue = v => typeof v === 'string' && v
@ -50,7 +54,9 @@ Options.from = function (_options) {
_.defaultsDeep(options, Options.defaults) _.defaultsDeep(options, Options.defaults)
// Resolve dirs // Resolve dirs
options.srcDir = hasValue(options.srcDir) ? resolve(options.rootDir, options.srcDir) : options.rootDir options.srcDir = hasValue(options.srcDir)
? resolve(options.rootDir, options.srcDir)
: options.rootDir
options.buildDir = resolve(options.rootDir, options.buildDir) options.buildDir = resolve(options.rootDir, options.buildDir)
options.cacheDir = resolve(options.rootDir, options.cacheDir) options.cacheDir = resolve(options.rootDir, options.cacheDir)
@ -94,11 +100,14 @@ Options.from = function (_options) {
} }
// Apply defaults to loadingIndicator // Apply defaults to loadingIndicator
options.loadingIndicator = Object.assign({ options.loadingIndicator = Object.assign(
{
name: 'pulse', name: 'pulse',
color: '#dbe1ec', color: '#dbe1ec',
background: 'white' background: 'white'
}, options.loadingIndicator) },
options.loadingIndicator
)
// cssSourceMap // cssSourceMap
if (options.build.cssSourceMap === undefined) { if (options.build.cssSourceMap === undefined) {
@ -111,7 +120,8 @@ Options.from = function (_options) {
} }
// Apply mode preset // Apply mode preset
let modePreset = Options.modes[options.mode || 'universal'] || Options.modes['universal'] let modePreset =
Options.modes[options.mode || 'universal'] || Options.modes['universal']
_.defaultsDeep(options, modePreset) _.defaultsDeep(options, modePreset)
// If no server-side rendering, add appear true transition // If no server-side rendering, add appear true transition
@ -299,9 +309,11 @@ Options.defaults = {
server_error: 'Server error', server_error: 'Server error',
nuxtjs: 'Nuxt.js', nuxtjs: 'Nuxt.js',
back_to_home: 'Back to the home page', back_to_home: 'Back to the home page',
server_error_details: 'An error occurred in the application and your page could not be served. If you are the application owner, check your logs for details.', server_error_details:
'An error occurred in the application and your page could not be served. If you are the application owner, check your logs for details.',
client_error: 'Error', client_error: 'Error',
client_error_details: 'An error occurred while rendering the page. Check developer tools console for details.', client_error_details:
'An error occurred while rendering the page. Check developer tools console for details.',
redirect: 'Redirecting to external page.' redirect: 'Redirecting to external page.'
} }
} }

View File

@ -1,4 +1,3 @@
const Vue = require('vue') const Vue = require('vue')
const VueMeta = require('vue-meta') const VueMeta = require('vue-meta')
const VueServerRenderer = require('vue-server-renderer') const VueServerRenderer = require('vue-server-renderer')
@ -51,7 +50,13 @@ module.exports = class MetaRenderer {
// BODY_ATTRS // BODY_ATTRS
meta.BODY_ATTRS = m.bodyAttrs.text() meta.BODY_ATTRS = m.bodyAttrs.text()
// HEAD tags // HEAD tags
meta.HEAD = m.meta.text() + m.title.text() + m.link.text() + m.style.text() + m.script.text() + m.noscript.text() meta.HEAD =
m.meta.text() +
m.title.text() +
m.link.text() +
m.style.text() +
m.script.text() +
m.noscript.text()
// BODY_SCRIPTS // BODY_SCRIPTS
meta.BODY_SCRIPTS = m.script.text({ body: true }) meta.BODY_SCRIPTS = m.script.text({ body: true })
// Resources Hints // Resources Hints
@ -62,11 +67,17 @@ module.exports = class MetaRenderer {
const publicPath = clientManifest.publicPath || '/_nuxt/' const publicPath = clientManifest.publicPath || '/_nuxt/'
// Pre-Load initial resources // Pre-Load initial resources
if (Array.isArray(clientManifest.initial)) { if (Array.isArray(clientManifest.initial)) {
meta.resourceHints += clientManifest.initial.map(r => `<link rel="preload" href="${publicPath}${r}" as="script" />`).join('') meta.resourceHints += clientManifest.initial
.map(
r => `<link rel="preload" href="${publicPath}${r}" as="script" />`
)
.join('')
} }
// Pre-Fetch async resources // Pre-Fetch async resources
if (Array.isArray(clientManifest.async)) { if (Array.isArray(clientManifest.async)) {
meta.resourceHints += clientManifest.async.map(r => `<link rel="prefetch" href="${publicPath}${r}" />`).join('') meta.resourceHints += clientManifest.async
.map(r => `<link rel="prefetch" href="${publicPath}${r}" />`)
.join('')
} }
// Add them to HEAD // Add them to HEAD
if (meta.resourceHints) { if (meta.resourceHints) {
@ -75,7 +86,8 @@ module.exports = class MetaRenderer {
} }
// Emulate getPreloadFiles from vue-server-renderer (works for JS chunks only) // Emulate getPreloadFiles from vue-server-renderer (works for JS chunks only)
meta.getPreloadFiles = () => clientManifest.initial.map(r => ({ meta.getPreloadFiles = () =>
clientManifest.initial.map(r => ({
file: r, file: r,
fileWithoutQuery: r, fileWithoutQuery: r,
asType: 'script', asType: 'script',

View File

@ -6,7 +6,7 @@ module.exports = function errorMiddleware(err, req, res, next) {
// ensure statusCode, message and name fields // ensure statusCode, message and name fields
err.statusCode = err.statusCode || 500 err.statusCode = err.statusCode || 500
err.message = err.message || 'Nuxt Server Error' err.message = err.message || 'Nuxt Server Error'
err.name = (!err.name || err.name === 'Error') ? 'NuxtServerError' : err.name err.name = !err.name || err.name === 'Error' ? 'NuxtServerError' : err.name
// We hide actual errors from end users, so show them on server logs // We hide actual errors from end users, so show them on server logs
if (err.statusCode !== 404) { if (err.statusCode !== 404) {
@ -25,8 +25,11 @@ module.exports = function errorMiddleware(err, req, res, next) {
} }
// Check if request accepts JSON // Check if request accepts JSON
const hasReqHeader = (header, includes) => req.headers[header] && req.headers[header].toLowerCase().includes(includes) const hasReqHeader = (header, includes) =>
const isJson = hasReqHeader('accept', 'application/json') || hasReqHeader('user-agent', 'curl/') req.headers[header] && req.headers[header].toLowerCase().includes(includes)
const isJson =
hasReqHeader('accept', 'application/json') ||
hasReqHeader('user-agent', 'curl/')
// Use basic errors when debug mode is disabled // Use basic errors when debug mode is disabled
if (!this.options.debug) { if (!this.options.debug) {
@ -46,17 +49,28 @@ module.exports = function errorMiddleware(err, req, res, next) {
} }
// Show stack trace // Show stack trace
const youch = new Youch(err, req, readSource.bind(this), this.options.router.base, true) const youch = new Youch(
err,
req,
readSource.bind(this),
this.options.router.base,
true
)
if (isJson) { if (isJson) {
youch.toJSON().then(json => { sendResponse(JSON.stringify(json, undefined, 2), 'text/json') }) youch.toJSON().then(json => {
sendResponse(JSON.stringify(json, undefined, 2), 'text/json')
})
} else { } else {
youch.toHTML().then(html => { sendResponse(html) }) youch.toHTML().then(html => {
sendResponse(html)
})
} }
} }
async function readSource(frame) { async function readSource(frame) {
// Remove webpack:/// & query string from the end // Remove webpack:/// & query string from the end
const sanitizeName = name => name ? name.replace('webpack:///', '').split('?')[0] : null const sanitizeName = name =>
name ? name.replace('webpack:///', '').split('?')[0] : null
frame.fileName = sanitizeName(frame.fileName) frame.fileName = sanitizeName(frame.fileName)
// Return if fileName is unknown // Return if fileName is unknown

View File

@ -11,7 +11,13 @@ module.exports = async function nuxtMiddleware(req, res, next) {
try { try {
const result = await this.renderRoute(req.url, context) const result = await this.renderRoute(req.url, context)
await this.nuxt.callHook('render:route', req.url, result) await this.nuxt.callHook('render:route', req.url, result)
const { html, cspScriptSrcHashes, error, redirected, getPreloadFiles } = result const {
html,
cspScriptSrcHashes,
error,
redirected,
getPreloadFiles
} = result
if (redirected) { if (redirected) {
return html return html
@ -62,7 +68,10 @@ module.exports = async function nuxtMiddleware(req, res, next) {
} }
if (this.options.render.csp) { if (this.options.render.csp) {
res.setHeader('Content-Security-Policy', `script-src 'self' ${(cspScriptSrcHashes || []).join(' ')}`) res.setHeader(
'Content-Security-Policy',
`script-src 'self' ${(cspScriptSrcHashes || []).join(' ')}`
)
} }
// Send response // Send response

View File

@ -94,14 +94,19 @@ module.exports = class Renderer {
// Reload error template // Reload error template
const errorTemplatePath = resolve(this.options.buildDir, 'views/error.html') const errorTemplatePath = resolve(this.options.buildDir, 'views/error.html')
if (fs.existsSync(errorTemplatePath)) { if (fs.existsSync(errorTemplatePath)) {
this.resources.errorTemplate = parseTemplate(fs.readFileSync(errorTemplatePath, 'utf8')) this.resources.errorTemplate = parseTemplate(
fs.readFileSync(errorTemplatePath, 'utf8')
)
} }
// Load loading template // Load loading template
const loadingHTMLPath = resolve(this.options.buildDir, 'loading.html') const loadingHTMLPath = resolve(this.options.buildDir, 'loading.html')
if (fs.existsSync(loadingHTMLPath)) { if (fs.existsSync(loadingHTMLPath)) {
this.resources.loadingHTML = fs.readFileSync(loadingHTMLPath, 'utf8') this.resources.loadingHTML = fs.readFileSync(loadingHTMLPath, 'utf8')
this.resources.loadingHTML = this.resources.loadingHTML.replace(/[\r|\n]/g, '') this.resources.loadingHTML = this.resources.loadingHTML.replace(
/[\r|\n]/g,
''
)
} else { } else {
this.resources.loadingHTML = '' this.resources.loadingHTML = ''
} }
@ -157,11 +162,17 @@ module.exports = class Renderer {
} }
// Create bundle renderer for SSR // Create bundle renderer for SSR
this.bundleRenderer = createBundleRenderer(this.resources.serverBundle, Object.assign({ this.bundleRenderer = createBundleRenderer(
this.resources.serverBundle,
Object.assign(
{
clientManifest: this.resources.clientManifest, clientManifest: this.resources.clientManifest,
runInNewContext: false, runInNewContext: false,
basedir: this.options.rootDir basedir: this.options.rootDir
}, this.options.render.bundleRenderer)) },
this.options.render.bundleRenderer
)
)
} }
useMiddleware(m) { useMiddleware(m) {
@ -178,7 +189,10 @@ module.exports = class Renderer {
} }
const handler = m.handler || m const handler = m.handler || m
const path = (((m.prefix !== false) ? this.options.router.base : '') + (typeof m.path === 'string' ? m.path : '')).replace(/\/\//g, '/') const path = (
(m.prefix !== false ? this.options.router.base : '') +
(typeof m.path === 'string' ? m.path : '')
).replace(/\/\//g, '/')
// Inject $src and $m to final handler // Inject $src and $m to final handler
if (src) handler.$src = src if (src) handler.$src = src
@ -189,7 +203,9 @@ module.exports = class Renderer {
} }
get publicPath() { get publicPath() {
return isUrl(this.options.build.publicPath) ? Options.defaults.build.publicPath : this.options.build.publicPath return isUrl(this.options.build.publicPath)
? Options.defaults.build.publicPath
: this.options.build.publicPath
} }
async setupMiddleware() { async setupMiddleware() {
@ -233,7 +249,12 @@ module.exports = class Renderer {
} }
// For serving static/ files to / // For serving static/ files to /
this.useMiddleware(serveStatic(resolve(this.options.srcDir, 'static'), this.options.render.static)) this.useMiddleware(
serveStatic(
resolve(this.options.srcDir, 'static'),
this.options.render.static
)
)
// Serve .nuxt/dist/ files only for production // Serve .nuxt/dist/ files only for production
// For dev they will be served with devMiddleware // For dev they will be served with devMiddleware
@ -280,12 +301,26 @@ module.exports = class Renderer {
const ENV = this.options.env const ENV = this.options.env
if (this.noSSR || spa) { if (this.noSSR || spa) {
const { HTML_ATTRS, BODY_ATTRS, HEAD, BODY_SCRIPTS, getPreloadFiles } = await this.metaRenderer.render(context) const {
const APP = `<div id="__nuxt">${this.resources.loadingHTML}</div>` + BODY_SCRIPTS HTML_ATTRS,
BODY_ATTRS,
HEAD,
BODY_SCRIPTS,
getPreloadFiles
} = await this.metaRenderer.render(context)
const APP =
`<div id="__nuxt">${this.resources.loadingHTML}</div>` + BODY_SCRIPTS
// Detect 404 errors // Detect 404 errors
if (url.includes(this.options.build.publicPath) || url.includes('__webpack')) { if (
const err = { statusCode: 404, message: this.options.messages.error_404, name: 'ResourceNotFound' } url.includes(this.options.build.publicPath) ||
url.includes('__webpack')
) {
const err = {
statusCode: 404,
message: this.options.messages.error_404,
name: 'ResourceNotFound'
}
throw err throw err
} }
@ -307,7 +342,13 @@ module.exports = class Renderer {
APP = '<div id="__nuxt"></div>' APP = '<div id="__nuxt"></div>'
} }
const m = context.meta.inject() 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() let HEAD =
m.meta.text() +
m.title.text() +
m.link.text() +
m.style.text() +
m.script.text() +
m.noscript.text()
if (this.options._routerBaseSpecified) { if (this.options._routerBaseSpecified) {
HEAD += `<base href="${this.options.router.base}">` HEAD += `<base href="${this.options.router.base}">`
} }
@ -316,12 +357,16 @@ module.exports = class Renderer {
HEAD += context.renderResourceHints() HEAD += context.renderResourceHints()
} }
let serializedSession = `window.__NUXT__=${serialize(context.nuxt, { isJSON: true })};` let serializedSession = `window.__NUXT__=${serialize(context.nuxt, {
isJSON: true
})};`
let cspScriptSrcHashes = [] let cspScriptSrcHashes = []
if (this.options.render.csp) { if (this.options.render.csp) {
let hash = crypto.createHash(this.options.render.csp.hashAlgorithm) let hash = crypto.createHash(this.options.render.csp.hashAlgorithm)
hash.update(serializedSession) hash.update(serializedSession)
cspScriptSrcHashes.push(`'${this.options.render.csp.hashAlgorithm}-${hash.digest('base64')}'`) cspScriptSrcHashes.push(
`'${this.options.render.csp.hashAlgorithm}-${hash.digest('base64')}'`
)
} }
APP += `<script type="text/javascript">${serializedSession}</script>` APP += `<script type="text/javascript">${serializedSession}</script>`
@ -375,7 +420,9 @@ module.exports = class Renderer {
url = url || 'http://localhost:3000' url = url || 'http://localhost:3000'
const { window } = await jsdom.JSDOM.fromURL(url, options) const { window } = await jsdom.JSDOM.fromURL(url, options)
// If Nuxt could not be loaded (error from the server-side) // If Nuxt could not be loaded (error from the server-side)
const nuxtExists = window.document.body.innerHTML.includes(this.options.render.ssr ? 'window.__NUXT__' : '<div id="__nuxt">') const nuxtExists = window.document.body.innerHTML.includes(
this.options.render.ssr ? 'window.__NUXT__' : '<div id="__nuxt">'
)
/* istanbul ignore if */ /* istanbul ignore if */
if (!nuxtExists) { if (!nuxtExists) {
let error = new Error('Could not load the nuxt app') let error = new Error('Could not load the nuxt app')
@ -383,7 +430,7 @@ module.exports = class Renderer {
throw error throw 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
await new Promise((resolve) => { await new Promise(resolve => {
window._onNuxtLoaded = () => resolve(window) window._onNuxtLoaded = () => resolve(window)
}) })
// Send back window object // Send back window object
@ -391,9 +438,10 @@ module.exports = class Renderer {
} }
} }
const parseTemplate = templateStr => _.template(templateStr, { const parseTemplate = templateStr =>
_.template(templateStr, {
interpolate: /{{([\s\S]+?)}}/g interpolate: /{{([\s\S]+?)}}/g
}) })
const resourceMap = [ const resourceMap = [
{ {
@ -419,4 +467,7 @@ const resourceMap = [
] ]
// Protector utility against request to SSR bundle files // Protector utility against request to SSR bundle files
const ssrResourceRegex = new RegExp(resourceMap.map(resource => resource.fileName).join('|'), 'i') const ssrResourceRegex = new RegExp(
resourceMap.map(resource => resource.fileName).join('|'),
'i'
)

View File

@ -20,7 +20,9 @@ const p = JSON.parse(originalPackage)
p.name = 'nuxt-edge' p.name = 'nuxt-edge'
// Get latest git commit id // Get latest git commit id
const gitCommit = String(spawnSync('git', 'rev-parse --short HEAD'.split(' ')).stdout).trim() const gitCommit = String(
spawnSync('git', 'rev-parse --short HEAD'.split(' ')).stdout
).trim()
// Version with latest git commit id // Version with latest git commit id
// Using date.now() so latest push will be always choosen by npm/yarn // Using date.now() so latest push will be always choosen by npm/yarn
@ -32,7 +34,9 @@ p.version = `${baseVersion}-${date}.${gitCommit}`
writeFileSync(packagePath, JSON.stringify(p, null, 2) + '\r\n') writeFileSync(packagePath, JSON.stringify(p, null, 2) + '\r\n')
// Parse git branch to decide npm tag // Parse git branch to decide npm tag
let tag = String(spawnSync('git', 'rev-parse --abbrev-ref HEAD'.split(' ')).stdout).trim() let tag = String(
spawnSync('git', 'rev-parse --abbrev-ref HEAD'.split(' ')).stdout
).trim()
// dev ~> latest // dev ~> latest
if (tag === 'dev') { if (tag === 'dev') {
@ -45,4 +49,6 @@ console.log(`publishing ${p.name}@${p.version} with tag ${tag}`)
// Do publish // Do publish
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(String(spawnSync('npm', `publish --tag ${tag}`.split(' ')).stdout).trim()) console.log(
String(spawnSync('npm', `publish --tag ${tag}`.split(' ')).stdout).trim()
)

View File

@ -2,7 +2,13 @@
const now = Date.now() const now = Date.now()
const { readFileSync, readJSONSync, writeFileSync, copySync, removeSync } = require('fs-extra') const {
readFileSync,
readJSONSync,
writeFileSync,
copySync,
removeSync
} = require('fs-extra')
const { resolve } = require('path') const { resolve } = require('path')
// Dirs // Dirs
@ -13,18 +19,11 @@ const startDir = resolve(rootDir, 'start')
const packageJSON = readJSONSync(resolve(rootDir, 'package.json')) const packageJSON = readJSONSync(resolve(rootDir, 'package.json'))
// Required and Excluded packages for start // Required and Excluded packages for start
let requires = [ let requires = ['source-map-support', 'pretty-error', 'minimist']
'source-map-support',
'pretty-error',
'minimist'
]
const excludes = [ const excludes = ['path', 'fs', 'http', 'module'].concat(
'path', Object.keys(packageJSON.devDependencies)
'fs', )
'http',
'module'
].concat(Object.keys(packageJSON.devDependencies))
// Parse dist/core.js for all external dependencies // Parse dist/core.js for all external dependencies
const requireRegex = /require\('([-@/\w]+)'\)/g const requireRegex = /require\('([-@/\w]+)'\)/g
@ -66,13 +65,13 @@ packageJSON.bin = {
} }
// Update package.json // Update package.json
writeFileSync(resolve(startDir, 'package.json'), JSON.stringify(packageJSON, null, 2)) writeFileSync(
resolve(startDir, 'package.json'),
JSON.stringify(packageJSON, null, 2)
)
// Copy required files // Copy required files
const excludeFiles = [ const excludeFiles = ['README.md', '.gitignore']
'README.md',
'.gitignore'
]
packageJSON.files.forEach(file => { packageJSON.files.forEach(file => {
if (excludeFiles.indexOf(file) !== -1) { if (excludeFiles.indexOf(file) !== -1) {
return return
@ -99,11 +98,20 @@ extraFiles.forEach(file => {
// Patch index.js // Patch index.js
const startIndexjs = resolve(startDir, 'index.js') const startIndexjs = resolve(startDir, 'index.js')
writeFileSync(startIndexjs, String(readFileSync(startIndexjs)).replace('./dist/nuxt', './dist/core')) writeFileSync(
startIndexjs,
String(readFileSync(startIndexjs)).replace('./dist/nuxt', './dist/core')
)
// Patch bin/nuxt-start // Patch bin/nuxt-start
const binStart = resolve(startDir, 'bin/nuxt-start') const binStart = resolve(startDir, 'bin/nuxt-start')
writeFileSync(binStart, String(readFileSync(binStart)).replace(/nuxt start/g, 'nuxt-start')) writeFileSync(
binStart,
String(readFileSync(binStart)).replace(/nuxt start/g, 'nuxt-start')
)
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(`Generated ${packageJSON.name}@${packageJSON.version} in ${Date.now() - now}ms`) console.log(
`Generated ${packageJSON.name}@${packageJSON.version} in ${Date.now() -
now}ms`
)

View File

@ -5,7 +5,7 @@ import * as browser from './helpers/browser'
import { interceptLog } from './helpers/console' import { interceptLog } from './helpers/console'
const port = 4003 const port = 4003
const url = (route) => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
let nuxt = null let nuxt = null
let page = null let page = null
@ -63,7 +63,10 @@ test.serial('/css', async t => {
await page.nuxt.navigate('/css') await page.nuxt.navigate('/css')
t.is(await page.$text('.red'), 'This is red') t.is(await page.$text('.red'), 'This is red')
t.is(await page.$eval('.red', (red) => window.getComputedStyle(red).color), 'rgb(255, 0, 0)') t.is(
await page.$eval('.red', red => window.getComputedStyle(red).color),
'rgb(255, 0, 0)'
)
}) })
test.serial('/stateful', async t => { test.serial('/stateful', async t => {
@ -80,7 +83,9 @@ test.serial('/store', async t => {
}) })
test.serial('/head', async t => { test.serial('/head', async t => {
const msg = new Promise((resolve) => page.on('console', (msg) => resolve(msg.text))) const msg = new Promise(resolve =>
page.on('console', msg => resolve(msg.text))
)
await page.nuxt.navigate('/head') await page.nuxt.navigate('/head')
const metas = await page.$$attr('meta', 'content') const metas = await page.$$attr('meta', 'content')
@ -158,7 +163,9 @@ test.serial('/redirect-external', async t => {
// New page for redirecting to external link. // New page for redirecting to external link.
const page = await browser.page(url('/')) const page = await browser.page(url('/'))
await page.nuxt.navigate('/redirect-external', false) await page.nuxt.navigate('/redirect-external', false)
await page.waitForFunction(() => window.location.href === 'https://nuxtjs.org/') await page.waitForFunction(
() => window.location.href === 'https://nuxtjs.org/'
)
page.close() page.close()
t.pass() t.pass()
}) })
@ -186,7 +193,10 @@ test.serial('/fn-midd', async t => {
await page.nuxt.navigate('/fn-midd') await page.nuxt.navigate('/fn-midd')
t.is(await page.$text('.title'), 'You need to ask the permission') t.is(await page.$text('.title'), 'You need to ask the permission')
t.deepEqual(await page.nuxt.errorData(), { message: 'You need to ask the permission', statusCode: 403 }) t.deepEqual(await page.nuxt.errorData(), {
message: 'You need to ask the permission',
statusCode: 403
})
}) })
test.serial('/fn-midd?please=true', async t => { test.serial('/fn-midd?please=true', async t => {

View File

@ -4,7 +4,7 @@ import { intercept, release } from './helpers/console'
import { Nuxt, Builder } from '..' import { Nuxt, Builder } from '..'
const port = 4001 const port = 4001
const url = (route) => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
const rootDir = resolve(__dirname, 'fixtures/basic') const rootDir = resolve(__dirname, 'fixtures/basic')
let nuxt = null let nuxt = null

View File

@ -22,8 +22,7 @@ test('Fail with routes() which throw an error', async t => {
const builder = new Builder(nuxt) const builder = new Builder(nuxt)
const generator = new Generator(nuxt, builder) const generator = new Generator(nuxt, builder)
return generator.generate() return generator.generate().catch(e => {
.catch((e) => {
t.true(e.message === 'Not today!') t.true(e.message === 'Not today!')
}) })
}) })

View File

@ -10,7 +10,7 @@ import { interceptLog, release } from './helpers/console'
import { Nuxt, Builder, Generator } from '..' import { Nuxt, Builder, Generator } from '..'
const port = 4002 const port = 4002
const url = (route) => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
const rootDir = resolve(__dirname, 'fixtures/basic') const rootDir = resolve(__dirname, 'fixtures/basic')
let nuxt = null let nuxt = null
@ -91,7 +91,9 @@ test.serial('/async-data', async t => {
test.serial('/users/1/index.html', async t => { test.serial('/users/1/index.html', async t => {
const html = await rp(url('/users/1/index.html')) const html = await rp(url('/users/1/index.html'))
t.true(html.includes('<h1>User: 1</h1>')) t.true(html.includes('<h1>User: 1</h1>'))
t.true(existsSync(resolve(__dirname, 'fixtures/basic/dist', 'users/1/index.html'))) t.true(
existsSync(resolve(__dirname, 'fixtures/basic/dist', 'users/1/index.html'))
)
t.false(existsSync(resolve(__dirname, 'fixtures/basic/dist', 'users/1.html'))) t.false(existsSync(resolve(__dirname, 'fixtures/basic/dist', 'users/1.html')))
}) })
@ -160,7 +162,9 @@ test.serial('/users/1.html', async t => {
const html = await rp(url('/users/1.html')) const html = await rp(url('/users/1.html'))
t.true(html.includes('<h1>User: 1</h1>')) t.true(html.includes('<h1>User: 1</h1>'))
t.true(existsSync(resolve(__dirname, 'fixtures/basic/dist', 'users/1.html'))) t.true(existsSync(resolve(__dirname, 'fixtures/basic/dist', 'users/1.html')))
t.false(existsSync(resolve(__dirname, 'fixtures/basic/dist', 'users/1/index.html'))) t.false(
existsSync(resolve(__dirname, 'fixtures/basic/dist', 'users/1/index.html'))
)
}) })
test.serial('/-ignored', async t => { test.serial('/-ignored', async t => {

View File

@ -5,7 +5,7 @@ import { Nuxt, Builder } from '..'
import { interceptLog, interceptError, release } from './helpers/console' import { interceptLog, interceptError, release } from './helpers/console'
const port = 4004 const port = 4004
const url = (route) => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
let nuxt = null let nuxt = null
@ -83,12 +83,16 @@ test('/store', async t => {
test.serial('/head', async t => { test.serial('/head', async t => {
const logSpy = await interceptLog() const logSpy = await interceptLog()
const window = await nuxt.renderAndGetWindow(url('/head'), { virtualConsole: false }) const window = await nuxt.renderAndGetWindow(url('/head'), {
virtualConsole: false
})
t.is(window.document.title, 'My title - Nuxt.js') t.is(window.document.title, 'My title - Nuxt.js')
const html = window.document.body.innerHTML const html = window.document.body.innerHTML
t.true(html.includes('<div><h1>I can haz meta tags</h1></div>')) t.true(html.includes('<div><h1>I can haz meta tags</h1></div>'))
t.true(html.includes('<script data-n-head="true" src="/body.js" data-body="true">')) t.true(
html.includes('<script data-n-head="true" src="/body.js" data-body="true">')
)
const metas = window.document.getElementsByTagName('meta') const metas = window.document.getElementsByTagName('meta')
t.is(metas[0].getAttribute('content'), 'my meta') t.is(metas[0].getAttribute('content'), 'my meta')
@ -169,7 +173,11 @@ test.serial('/error status code', async t => {
const errorSpy = await interceptError() const errorSpy = await interceptError()
const err = await t.throws(rp(url('/error'))) const err = await t.throws(rp(url('/error')))
t.true(err.statusCode === 500) t.true(err.statusCode === 500)
t.true(err.response.body.includes('An error occurred in the application and your page could not be served')) t.true(
err.response.body.includes(
'An error occurred in the application and your page could not be served'
)
)
release() release()
t.true(errorSpy.calledOnce) t.true(errorSpy.calledOnce)
t.true(errorSpy.args[0][0].message.includes('Error mouahahah')) t.true(errorSpy.args[0][0].message.includes('Error mouahahah'))
@ -215,7 +223,11 @@ test('/redirect-name', async t => {
test('/no-ssr', async t => { test('/no-ssr', async t => {
const { html } = await nuxt.renderRoute('/no-ssr') const { html } = await nuxt.renderRoute('/no-ssr')
t.true(html.includes('<div class="no-ssr-placeholder">&lt;p&gt;Loading...&lt;/p&gt;</div>')) t.true(
html.includes(
'<div class="no-ssr-placeholder">&lt;p&gt;Loading...&lt;/p&gt;</div>'
)
)
}) })
test('/no-ssr (client-side)', async t => { test('/no-ssr (client-side)', async t => {
@ -225,25 +237,38 @@ test('/no-ssr (client-side)', async t => {
}) })
test('ETag Header', async t => { test('ETag Header', async t => {
const { headers: { etag } } = await rp(url('/stateless'), { resolveWithFullResponse: true }) const { headers: { etag } } = await rp(url('/stateless'), {
resolveWithFullResponse: true
})
// Verify functionality // Verify functionality
const error = await t.throws(rp(url('/stateless'), { headers: { 'If-None-Match': etag } })) const error = await t.throws(
rp(url('/stateless'), { headers: { 'If-None-Match': etag } })
)
t.is(error.statusCode, 304) t.is(error.statusCode, 304)
}) })
test('Content-Security-Policy Header', async t => { test('Content-Security-Policy Header', async t => {
const { headers } = await rp(url('/stateless'), { resolveWithFullResponse: true }) const { headers } = await rp(url('/stateless'), {
resolveWithFullResponse: true
})
// Verify functionality // Verify functionality
t.is(headers['content-security-policy'], "script-src 'self' 'sha256-BBvfKxDOoRM/gnFwke9u60HBZX3HUss/0lSI1sBRvOU='") t.is(
headers['content-security-policy'],
"script-src 'self' 'sha256-BBvfKxDOoRM/gnFwke9u60HBZX3HUss/0lSI1sBRvOU='"
)
}) })
test('/_nuxt/server-bundle.json should return 404', async t => { test('/_nuxt/server-bundle.json should return 404', async t => {
const err = await t.throws(rp(url('/_nuxt/server-bundle.json'), { resolveWithFullResponse: true })) const err = await t.throws(
rp(url('/_nuxt/server-bundle.json'), { resolveWithFullResponse: true })
)
t.is(err.statusCode, 404) t.is(err.statusCode, 404)
}) })
test('/_nuxt/ should return 404', async t => { test('/_nuxt/ should return 404', async t => {
const err = await t.throws(rp(url('/_nuxt/'), { resolveWithFullResponse: true })) const err = await t.throws(
rp(url('/_nuxt/'), { resolveWithFullResponse: true })
)
t.is(err.statusCode, 404) t.is(err.statusCode, 404)
}) })
@ -253,7 +278,9 @@ test('/meta', async t => {
}) })
test('/fn-midd', async t => { test('/fn-midd', async t => {
const err = await t.throws(rp(url('/fn-midd'), { resolveWithFullResponse: true })) const err = await t.throws(
rp(url('/fn-midd'), { resolveWithFullResponse: true })
)
t.is(err.statusCode, 403) t.is(err.statusCode, 403)
t.true(err.response.body.includes('You need to ask the permission')) t.true(err.response.body.includes('You need to ask the permission'))
}) })

View File

@ -5,7 +5,7 @@ import * as browser from './helpers/browser'
import { interceptLog } from './helpers/console' import { interceptLog } from './helpers/console'
const port = 4014 const port = 4014
const url = (route) => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
let nuxt = null let nuxt = null
let page let page

View File

@ -5,7 +5,7 @@ import { Nuxt, Builder } from '..'
import { interceptLog, interceptError, release } from './helpers/console' import { interceptLog, interceptError, release } from './helpers/console'
const port = 4009 const port = 4009
const url = (route) => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
let nuxt = null let nuxt = null
@ -26,19 +26,32 @@ test.serial('Init Nuxt.js', async t => {
}) })
test.serial('/test/__open-in-editor (open-in-editor)', async t => { test.serial('/test/__open-in-editor (open-in-editor)', async t => {
const { body } = await rp(url('/test/__open-in-editor?file=pages/index.vue'), { resolveWithFullResponse: true }) const { body } = await rp(
url('/test/__open-in-editor?file=pages/index.vue'),
{ resolveWithFullResponse: true }
)
t.is(body, '') t.is(body, '')
}) })
test.serial('/test/__open-in-editor should return error (open-in-editor)', async t => { test.serial(
const { error, statusCode } = await t.throws(rp(url('/test/__open-in-editor?file='), { resolveWithFullResponse: true })) '/test/__open-in-editor should return error (open-in-editor)',
async t => {
const { error, statusCode } = await t.throws(
rp(url('/test/__open-in-editor?file='), { resolveWithFullResponse: true })
)
t.is(statusCode, 500) t.is(statusCode, 500)
t.is(error, 'launch-editor-middleware: required query param "file" is missing.') t.is(
}) error,
'launch-editor-middleware: required query param "file" is missing.'
)
}
)
test.serial('/test/error should return error stack trace (Youch)', async t => { test.serial('/test/error should return error stack trace (Youch)', async t => {
const errorSpy = await interceptError() const errorSpy = await interceptError()
const { response, error } = await t.throws(nuxt.renderAndGetWindow(url('/test/error'))) const { response, error } = await t.throws(
nuxt.renderAndGetWindow(url('/test/error'))
)
t.is(response.statusCode, 500) t.is(response.statusCode, 500)
t.is(response.statusMessage, 'NuxtServerError') t.is(response.statusMessage, 'NuxtServerError')
t.true(error.includes('test youch !')) t.true(error.includes('test youch !'))
@ -54,7 +67,9 @@ test.serial('/test/error no source-map (Youch)', async t => {
nuxt.renderer.resources.serverBundle.maps = {} nuxt.renderer.resources.serverBundle.maps = {}
const errorSpy = await interceptError() const errorSpy = await interceptError()
const { response, error } = await t.throws(nuxt.renderAndGetWindow(url('/test/error'))) const { response, error } = await t.throws(
nuxt.renderAndGetWindow(url('/test/error'))
)
t.is(response.statusCode, 500) t.is(response.statusCode, 500)
t.is(response.statusMessage, 'NuxtServerError') t.is(response.statusMessage, 'NuxtServerError')
t.true(error.includes('test youch !')) t.true(error.includes('test youch !'))
@ -70,7 +85,7 @@ test.serial('/test/error no source-map (Youch)', async t => {
test.serial('/test/error should return json format error (Youch)', async t => { test.serial('/test/error should return json format error (Youch)', async t => {
const opts = { const opts = {
headers: { headers: {
'accept': 'application/json' accept: 'application/json'
}, },
resolveWithFullResponse: true resolveWithFullResponse: true
} }

View File

@ -5,7 +5,7 @@ import { Nuxt, Builder } from '..'
import { intercept, interceptWarn, release } from './helpers/console' import { intercept, interceptWarn, release } from './helpers/console'
const port = 4010 const port = 4010
const url = (route) => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
let nuxt = null let nuxt = null
let builder = null let builder = null
@ -32,14 +32,26 @@ test.serial('Init Nuxt.js', async t => {
test.serial('Deprecated: context.isServer and context.isClient', async t => { test.serial('Deprecated: context.isServer and context.isClient', async t => {
const warnSpy = await interceptWarn() const warnSpy = await interceptWarn()
await rp(url('/')) await rp(url('/'))
t.true(warnSpy.calledWith('context.isServer has been deprecated, please use process.server instead.')) t.true(
t.true(warnSpy.calledWith('context.isClient has been deprecated, please use process.client instead.')) warnSpy.calledWith(
'context.isServer has been deprecated, please use process.server instead.'
)
)
t.true(
warnSpy.calledWith(
'context.isClient has been deprecated, please use process.client instead.'
)
)
t.true(warnSpy.calledTwice) t.true(warnSpy.calledTwice)
release() release()
}) })
test.serial('Deprecated: dev in build.extend()', async t => { test.serial('Deprecated: dev in build.extend()', async t => {
t.true(buildSpies.warn.withArgs('dev has been deprecated in build.extend(), please use isDev').calledTwice) t.true(
buildSpies.warn.withArgs(
'dev has been deprecated in build.extend(), please use isDev'
).calledTwice
)
}) })
test.serial('Deprecated: nuxt.plugin()', async t => { test.serial('Deprecated: nuxt.plugin()', async t => {

View File

@ -9,9 +9,12 @@ const readFile = promisify(fs.readFile)
const rootDir = resolve(__dirname, 'fixtures/dll') const rootDir = resolve(__dirname, 'fixtures/dll')
const dllDir = resolve(rootDir, '.cache/client-dll') const dllDir = resolve(rootDir, '.cache/client-dll')
const checkCache = (lib) => { const checkCache = lib => {
return async (t) => { return async t => {
const manifest = await readFile(resolve(dllDir, `./${lib}-manifest.json`), 'utf-8') const manifest = await readFile(
resolve(dllDir, `./${lib}-manifest.json`),
'utf-8'
)
t.truthy(JSON.parse(manifest).name) t.truthy(JSON.parse(manifest).name)
t.true(fs.existsSync(resolve(dllDir, `./${JSON.parse(manifest).name}.js`))) t.true(fs.existsSync(resolve(dllDir, `./${JSON.parse(manifest).name}.js`)))
} }

View File

@ -24,13 +24,18 @@ test.serial('Init Nuxt.js', async t => {
}) })
test('Check .nuxt/router.js', t => { test('Check .nuxt/router.js', t => {
return readFile(resolve(__dirname, './fixtures/dynamic-routes/.nuxt/router.js'), 'utf-8') return readFile(
.then((routerFile) => { resolve(__dirname, './fixtures/dynamic-routes/.nuxt/router.js'),
'utf-8'
).then(routerFile => {
routerFile = routerFile routerFile = routerFile
.slice(routerFile.indexOf('routes: [')) .slice(routerFile.indexOf('routes: ['))
.replace('routes: [', '[') .replace('routes: [', '[')
.replace(/ _[0-9A-z]+,/g, ' "",') .replace(/ _[0-9A-z]+,/g, ' "",')
routerFile = routerFile.substr(routerFile.indexOf('['), routerFile.lastIndexOf(']') + 1) routerFile = routerFile.substr(
routerFile.indexOf('['),
routerFile.lastIndexOf(']') + 1
)
let routes = eval('( ' + routerFile + ')') // eslint-disable-line no-eval let routes = eval('( ' + routerFile + ')') // eslint-disable-line no-eval
// pages/index.vue // pages/index.vue
t.is(routes[0].path, '/') t.is(routes[0].path, '/')
@ -50,8 +55,12 @@ test('Check .nuxt/router.js', t => {
t.falsy(routes[3].name) // parent route has no name t.falsy(routes[3].name) // parent route has no name
// pages/parent/*.vue // pages/parent/*.vue
t.is(routes[3].children.length, 3) // parent has 3 children t.is(routes[3].children.length, 3) // parent has 3 children
t.deepEqual(routes[3].children.map((r) => r.path), ['', 'teub', 'child']) t.deepEqual(routes[3].children.map(r => r.path), ['', 'teub', 'child'])
t.deepEqual(routes[3].children.map((r) => r.name), ['parent', 'parent-teub', 'parent-child']) t.deepEqual(routes[3].children.map(r => r.name), [
'parent',
'parent-teub',
'parent-child'
])
// pages/test/projects/index.vue // pages/test/projects/index.vue
t.is(routes[4].path, '/test/projects') t.is(routes[4].path, '/test/projects')
t.is(routes[4].name, 'test-projects') t.is(routes[4].name, 'test-projects')
@ -60,8 +69,20 @@ test('Check .nuxt/router.js', t => {
t.falsy(routes[5].name) // parent route has no name t.falsy(routes[5].name) // parent route has no name
// pages/test/users/*.vue // pages/test/users/*.vue
t.is(routes[5].children.length, 5) // parent has 5 children t.is(routes[5].children.length, 5) // parent has 5 children
t.deepEqual(routes[5].children.map((r) => r.path), ['', 'projects', 'projects/:category', ':id', ':index/teub']) t.deepEqual(routes[5].children.map(r => r.path), [
t.deepEqual(routes[5].children.map((r) => r.name), ['test-users', 'test-users-projects', 'test-users-projects-category', 'test-users-id', 'test-users-index-teub']) '',
'projects',
'projects/:category',
':id',
':index/teub'
])
t.deepEqual(routes[5].children.map(r => r.name), [
'test-users',
'test-users-projects',
'test-users-projects-category',
'test-users-id',
'test-users-index-teub'
])
// pages/test/songs/toto.vue // pages/test/songs/toto.vue
t.is(routes[6].path, '/test/songs/toto') t.is(routes[6].path, '/test/songs/toto')
t.is(routes[6].name, 'test-songs-toto') t.is(routes[6].name, 'test-songs-toto')

View File

@ -5,7 +5,7 @@ import { Nuxt, Builder } from '..'
import { interceptLog, interceptError, release } from './helpers/console' import { interceptLog, interceptError, release } from './helpers/console'
const port = 4005 const port = 4005
const url = (route) => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
let nuxt = null let nuxt = null
@ -46,13 +46,19 @@ test.serial('/ with renderAndGetWindow()', async t => {
t.is(err.response.statusMessage, 'NuxtServerError') t.is(err.response.statusMessage, 'NuxtServerError')
release() release()
t.true(errorSpy.calledOnce) t.true(errorSpy.calledOnce)
t.true(errorSpy.getCall(0).args[0].message.includes('render function or template not defined in component: anonymous')) t.true(
errorSpy
.getCall(0)
.args[0].message.includes(
'render function or template not defined in component: anonymous'
)
)
}) })
test.serial('/ with text/json content', async t => { test.serial('/ with text/json content', async t => {
const opts = { const opts = {
headers: { headers: {
'accept': 'application/json' accept: 'application/json'
}, },
resolveWithFullResponse: true resolveWithFullResponse: true
} }
@ -61,7 +67,13 @@ test.serial('/ with text/json content', async t => {
t.is(headers['content-type'], 'text/json; charset=utf-8') t.is(headers['content-type'], 'text/json; charset=utf-8')
release() release()
t.true(errorSpy.calledOnce) t.true(errorSpy.calledOnce)
t.true(errorSpy.getCall(0).args[0].message.includes('render function or template not defined in component: anonymous')) t.true(
errorSpy
.getCall(0)
.args[0].message.includes(
'render function or template not defined in component: anonymous'
)
)
}) })
// Close server and ask nuxt to stop listening to file changes // Close server and ask nuxt to stop listening to file changes

View File

@ -6,7 +6,7 @@ import rp from 'request-promise-native'
import { interceptLog } from './helpers/console' import { interceptLog } from './helpers/console'
const port = 4000 const port = 4000
const url = (route) => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
let nuxt let nuxt
let app let app

View File

@ -21,8 +21,6 @@ module.exports = {
transition: false, transition: false,
build: { build: {
scopeHoisting: true, scopeHoisting: true,
postcss: [ postcss: [require('postcss-cssnext')()]
require('postcss-cssnext')()
]
} }
} }

View File

@ -1,7 +1,5 @@
module.exports = { module.exports = {
modules: [ modules: ['~/modules/hooks'],
'~/modules/hooks'
],
build: { build: {
stats: false, stats: false,
extend(config, options) { extend(config, options) {

View File

@ -4,7 +4,9 @@ module.exports = {
dll: true, dll: true,
extend(config, options) { extend(config, options) {
if (options.isClient) { if (options.isClient) {
const dlls = config.plugins.filter(plugin => plugin.constructor.name === 'DllReferencePlugin') const dlls = config.plugins.filter(
plugin => plugin.constructor.name === 'DllReferencePlugin'
)
console.log('Using dll for ' + dlls.length + ' libs') // eslint-disable-line no-console console.log('Using dll for ' + dlls.length + ' libs') // eslint-disable-line no-console
} }
return config return config

View File

@ -9,11 +9,9 @@ module.exports = {
foo: 'bar' foo: 'bar'
} }
}, },
['./modules/template', {baz: 'ping'}] ['./modules/template', { baz: 'ping' }]
],
serverMiddleware: [
'./modules/middleware/midd2'
], ],
serverMiddleware: ['./modules/middleware/midd2'],
hooks(hook) { hooks(hook) {
hook('ready', nuxt => { hook('ready', nuxt => {
nuxt.__ready_called__ = true nuxt.__ready_called__ = true
@ -22,7 +20,7 @@ module.exports = {
builder.__build_done__ = true builder.__build_done__ = true
}) })
// Add hook for renderer // Add hook for renderer
hook('render:before', (renderer) => { hook('render:before', renderer => {
renderer.useMiddleware({ renderer.useMiddleware({
path: '/use-middleware', path: '/use-middleware',
handler: '~/modules/middleware/use-middleware' handler: '~/modules/middleware/use-middleware'

View File

@ -16,9 +16,7 @@ module.exports = {
] ]
} }
}, },
modulesDir: [ modulesDir: [path.join(__dirname, '..', '..', '..', 'node_modules')],
path.join(__dirname, '..', '..', '..', 'node_modules')
],
transition: 'test', transition: 'test',
layoutTransition: 'test', layoutTransition: 'test',
loadingIndicator: 'circle', loadingIndicator: 'circle',
@ -63,9 +61,7 @@ module.exports = {
}) })
} }
}, },
css: [ css: [{ src: '~/assets/app.css' }],
{ src: '~/assets/app.css' }
],
render: { render: {
http2: { http2: {
push: true, push: true,

View File

@ -6,10 +6,7 @@ module.exports = {
plugins: { plugins: {
'postcss-import': { 'postcss-import': {
root: rootDir, root: rootDir,
path: [ path: [rootDir, modulesDir]
rootDir,
modulesDir
]
}, },
'postcss-url': {}, 'postcss-url': {},
'postcss-cssnext': {} 'postcss-cssnext': {}

View File

@ -60,7 +60,7 @@ test('initRoutes with routes (fn(cb, args))', async t => {
const config = { const config = {
generate: { generate: {
routes(cb, arg1, arg2, arg3, arg4) { routes(cb, arg1, arg2, arg3, arg4) {
cb(null, [ arg1, arg2, arg3, arg4 ]) cb(null, [arg1, arg2, arg3, arg4])
} }
} }
} }

View File

@ -4,9 +4,14 @@ let browser = null
export async function start(options = {}) { export async function start(options = {}) {
// https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions // https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions
browser = await puppeteer.launch(Object.assign({ browser = await puppeteer.launch(
Object.assign(
{
args: ['--no-sandbox', '--disable-setuid-sandbox'] args: ['--no-sandbox', '--disable-setuid-sandbox']
}, options)) },
options
)
)
} }
export async function stop() { export async function stop() {
@ -19,25 +24,38 @@ export async function page(url) {
const page = await browser.newPage() const page = await browser.newPage()
await page.goto(url) await page.goto(url)
await page.waitForFunction('!!window.$nuxt') await page.waitForFunction('!!window.$nuxt')
page.html = () => page.evaluate(() => window.document.documentElement.outerHTML) page.html = () =>
page.$text = (selector) => page.$eval(selector, (el) => el.textContent) page.evaluate(() => window.document.documentElement.outerHTML)
page.$$text = (selector) => page.$$eval(selector, (els) => els.map((el) => el.textContent)) page.$text = selector => page.$eval(selector, el => el.textContent)
page.$attr = (selector, attr) => page.$eval(selector, (el, attr) => el.getAttribute(attr), attr) page.$$text = selector =>
page.$$attr = (selector, attr) => page.$$eval(selector, (els, attr) => els.map((el) => el.getAttribute(attr)), attr) page.$$eval(selector, els => els.map(el => el.textContent))
page.$attr = (selector, attr) =>
page.$eval(selector, (el, attr) => el.getAttribute(attr), attr)
page.$$attr = (selector, attr) =>
page.$$eval(
selector,
(els, attr) => els.map(el => el.getAttribute(attr)),
attr
)
page.$nuxt = await page.evaluateHandle('window.$nuxt') page.$nuxt = await page.evaluateHandle('window.$nuxt')
page.nuxt = { page.nuxt = {
async navigate(path, waitEnd = true) { async navigate(path, waitEnd = true) {
const hook = page.evaluate(() => { const hook = page.evaluate(() => {
return new Promise((resolve) => window.$nuxt.$once('routeChanged', resolve)) return new Promise(resolve =>
.then(() => new Promise((resolve) => setTimeout(resolve, 50))) window.$nuxt.$once('routeChanged', resolve)
).then(() => new Promise(resolve => setTimeout(resolve, 50)))
}) })
await page.evaluate(($nuxt, path) => $nuxt.$router.push(path), page.$nuxt, path) await page.evaluate(
($nuxt, path) => $nuxt.$router.push(path),
page.$nuxt,
path
)
if (waitEnd) await hook if (waitEnd) await hook
return { hook } return { hook }
}, },
routeData() { routeData() {
return page.evaluate(($nuxt) => { return page.evaluate($nuxt => {
return { return {
path: $nuxt.$route.path, path: $nuxt.$route.path,
query: $nuxt.$route.query query: $nuxt.$route.query
@ -45,13 +63,13 @@ export async function page(url) {
}, page.$nuxt) }, page.$nuxt)
}, },
loadingData() { loadingData() {
return page.evaluate(($nuxt) => $nuxt.$loading.$data, page.$nuxt) return page.evaluate($nuxt => $nuxt.$loading.$data, page.$nuxt)
}, },
errorData() { errorData() {
return page.evaluate(($nuxt) => $nuxt.nuxt.err, page.$nuxt) return page.evaluate($nuxt => $nuxt.nuxt.err, page.$nuxt)
}, },
storeState() { storeState() {
return page.evaluate(($nuxt) => $nuxt.$store.state, page.$nuxt) return page.evaluate($nuxt => $nuxt.$store.state, page.$nuxt)
} }
} }
return page return page

View File

@ -4,7 +4,9 @@ let context = null
export function release() { export function release() {
if (context === null) { if (context === null) {
process.stderr.write('Console spy context was empty, did a previous test already release it?\n') process.stderr.write(
'Console spy context was empty, did a previous test already release it?\n'
)
return return
} }
@ -32,7 +34,9 @@ export function release() {
export async function intercept(levels, msg, cb) { export async function intercept(levels, msg, cb) {
if (context !== null) { if (context !== null) {
process.stderr.write('Console spy context was not empty, did a previous test not release it?\n') process.stderr.write(
'Console spy context was not empty, did a previous test not release it?\n'
)
} }
context = {} context = {}

View File

@ -25,7 +25,9 @@ test('Fail to build when no pages/ directory but is in the parent', t => {
return new Builder(nuxt).build().catch(err => { return new Builder(nuxt).build().catch(err => {
let s = String(err) let s = String(err)
t.true(s.includes('No `pages` directory found')) t.true(s.includes('No `pages` directory found'))
t.true(s.includes('Did you mean to run `nuxt` in the parent (`../`) directory?')) t.true(
s.includes('Did you mean to run `nuxt` in the parent (`../`) directory?')
)
}) })
}) })
@ -36,7 +38,7 @@ test('Fail to build when no pages/ directory', t => {
}) })
return new Builder(nuxt).build().catch(err => { return new Builder(nuxt).build().catch(err => {
let s = String(err) let s = String(err)
t.true(s.includes('Couldn\'t find a `pages` directory')) t.true(s.includes("Couldn't find a `pages` directory"))
t.true(s.includes('Please create one under the project root')) t.true(s.includes('Please create one under the project root'))
}) })
}) })

View File

@ -5,7 +5,7 @@ import { Nuxt, Builder } from '..'
import { intercept } from './helpers/console' import { intercept } from './helpers/console'
const port = 4006 const port = 4006
const url = (route) => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
let nuxt = null let nuxt = null
let builder = null let builder = null
@ -30,12 +30,19 @@ test.serial('Init Nuxt.js', async t => {
}) })
test.serial('Vendor', async t => { test.serial('Vendor', async t => {
t.true(nuxt.options.build.vendor.indexOf('lodash') !== -1, 'lodash added to config') t.true(
nuxt.options.build.vendor.indexOf('lodash') !== -1,
'lodash added to config'
)
}) })
test.serial('Plugin', async t => { test.serial('Plugin', async t => {
t.true(normalize(nuxt.options.plugins[0].src) t.true(
.includes(normalize('fixtures/module/.nuxt/basic.reverse.')), 'plugin added to config') normalize(nuxt.options.plugins[0].src).includes(
normalize('fixtures/module/.nuxt/basic.reverse.')
),
'plugin added to config'
)
const { html } = await nuxt.renderRoute('/') const { html } = await nuxt.renderRoute('/')
t.true(html.includes('<h1>TXUN</h1>'), 'plugin works') t.true(html.includes('<h1>TXUN</h1>'), 'plugin works')
}) })

View File

@ -6,7 +6,7 @@ import { interceptLog, release } from './helpers/console'
let nuxt = null let nuxt = null
const port = 4012 const port = 4012
const url = (route) => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
const renderRoute = async _url => { const renderRoute = async _url => {
const window = await nuxt.renderAndGetWindow(url(_url)) const window = await nuxt.renderAndGetWindow(url(_url))
@ -73,7 +73,9 @@ test.serial('/mounted', async t => {
}) })
test('/_nuxt/ (access publicPath in spa mode)', async t => { test('/_nuxt/ (access publicPath in spa mode)', async t => {
const { response: { statusCode, statusMessage } } = await t.throws(renderRoute('/_nuxt/')) const { response: { statusCode, statusMessage } } = await t.throws(
renderRoute('/_nuxt/')
)
t.is(statusCode, 404) t.is(statusCode, 404)
t.is(statusMessage, 'ResourceNotFound') t.is(statusMessage, 'ResourceNotFound')
}) })

View File

@ -13,7 +13,7 @@ const range = n => [...Array(n).keys()]
const FOOBAR_REGEX = /<foobar>([\s\S]*)<\/foobar>/ const FOOBAR_REGEX = /<foobar>([\s\S]*)<\/foobar>/
const match = (regex, text) => (regex.exec(text) || [])[1] const match = (regex, text) => (regex.exec(text) || [])[1]
const url = (route) => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
const isWindows = /^win/.test(process.platform) const isWindows = /^win/.test(process.platform)
@ -93,7 +93,7 @@ test('unique responses with fetch', async t => {
// Making 16K requests by default // Making 16K requests by default
// Related issue: https://github.com/nuxt/nuxt.js/issues/1354 // Related issue: https://github.com/nuxt/nuxt.js/issues/1354
const stressTest = async (t, _url, concurrency = 64, steps = 256) => { const stressTest = async (t, _url, concurrency = 64, steps = 256) => {
let statusCodes = { } let statusCodes = {}
// appveyor memory limit! // appveyor memory limit!
if (isWindows) { if (isWindows) {

View File

@ -21,7 +21,7 @@ test('setAnsiColors', t => {
t.pass() t.pass()
}) })
test('waitFor', async (t) => { test('waitFor', async t => {
let s = Date.now() let s = Date.now()
await Utils.waitFor(100) await Utils.waitFor(100)
t.true(Date.now() - s >= 100) t.true(Date.now() - s >= 100)
@ -36,8 +36,7 @@ test('promisifyRoute (array)', t => {
const array = [1] const array = [1]
const promise = Utils.promisifyRoute(array) const promise = Utils.promisifyRoute(array)
t.is(typeof promise, 'object') t.is(typeof promise, 'object')
return promise return promise.then(res => {
.then((res) => {
t.is(res, array) t.is(res, array)
}) })
}) })
@ -49,8 +48,7 @@ test('promisifyRoute (fn => array)', t => {
} }
const promise = Utils.promisifyRoute(fn) const promise = Utils.promisifyRoute(fn)
t.is(typeof promise, 'object') t.is(typeof promise, 'object')
return promise return promise.then(res => {
.then((res) => {
t.is(res, array) t.is(res, array)
}) })
}) })
@ -58,29 +56,27 @@ test('promisifyRoute (fn => array)', t => {
test('promisifyRoute (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.promisifyRoute(fn) const promise = Utils.promisifyRoute(fn)
t.is(typeof promise, 'object') t.is(typeof promise, 'object')
return promise return promise.then(res => {
.then((res) => {
t.is(res, array) t.is(res, array)
}) })
}) })
test('promisifyRoute ((fn(args) => promise))', t => { test('promisifyRoute ((fn(args) => promise))', t => {
const fn = function (array) { const fn = function (array) {
return new Promise((resolve) => { return new Promise(resolve => {
resolve(array) resolve(array)
}) })
} }
const array = [1, 2, 3] const array = [1, 2, 3]
const promise = Utils.promisifyRoute(fn, array) const promise = Utils.promisifyRoute(fn, array)
t.is(typeof promise, 'object') t.is(typeof promise, 'object')
return promise return promise.then(res => {
.then((res) => {
t.is(res, array) t.is(res, array)
}) })
}) })
@ -91,8 +87,7 @@ test('promisifyRoute (fn(cb) with error)', t => {
} }
const promise = Utils.promisifyRoute(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.message, 'Error here') t.is(e.message, 'Error here')
}) })
}) })
@ -104,8 +99,7 @@ test('promisifyRoute (fn(cb, args) with error)', t => {
const array = [1, 2, 3, 4] const array = [1, 2, 3, 4]
const promise = Utils.promisifyRoute(fn, array) const promise = Utils.promisifyRoute(fn, array)
t.is(typeof promise, 'object') t.is(typeof promise, 'object')
return promise return promise.catch(e => {
.catch((e) => {
t.is(e.message, 'Error here: ' + array.join()) t.is(e.message, 'Error here: ' + array.join())
}) })
}) })
@ -117,8 +111,7 @@ test('promisifyRoute (fn(cb) with result)', t => {
} }
const promise = Utils.promisifyRoute(fn) const promise = Utils.promisifyRoute(fn)
t.is(typeof promise, 'object') t.is(typeof promise, 'object')
return promise return promise.then(res => {
.then((res) => {
t.is(res, array) t.is(res, array)
}) })
}) })
@ -131,8 +124,7 @@ test('promisifyRoute (fn(cb, args) with result)', t => {
const object = { a: 1 } const object = { a: 1 }
const promise = Utils.promisifyRoute(fn, array, object) const promise = Utils.promisifyRoute(fn, array, object)
t.is(typeof promise, 'object') t.is(typeof promise, 'object')
return promise return promise.then(res => {
.then((res) => {
t.is(res.array, array) t.is(res.array, array)
t.is(res.object, object) t.is(res.object, object)
}) })

View File

@ -6,7 +6,7 @@ import styleLoader from '../lib/builder/webpack/style-loader'
import { interceptLog, release } from './helpers/console' import { interceptLog, release } from './helpers/console'
const port = 4007 const port = 4007
const url = (route) => 'http://localhost:' + port + route const url = route => 'http://localhost:' + port + route
let nuxt = null let nuxt = null
let builder = null let builder = null
@ -45,7 +45,11 @@ test('/ (global styles inlined)', async t => {
test('/ (preload fonts)', async t => { test('/ (preload fonts)', async t => {
const { html } = await nuxt.renderRoute('/') const { html } = await nuxt.renderRoute('/')
t.true(html.includes('<link rel="preload" href="/test/orion/fonts/roboto.7cf5d7c.woff2" as="font" type="font/woff2" crossorigin')) t.true(
html.includes(
'<link rel="preload" href="/test/orion/fonts/roboto.7cf5d7c.woff2" as="font" type="font/woff2" crossorigin'
)
)
}) })
test('/ (custom app.html)', async t => { test('/ (custom app.html)', async t => {
@ -168,12 +172,17 @@ test.serial('/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, 35) t.is(stats.assets.length, 35)
}) })
test('Check /test/test.txt with custom serve-static options', async t => { test('Check /test/test.txt with custom serve-static options', async t => {
const { headers } = await rp(url('/test/test.txt'), { resolveWithFullResponse: true }) const { headers } = await rp(url('/test/test.txt'), {
resolveWithFullResponse: true
})
t.is(headers['cache-control'], 'public, max-age=31536000') t.is(headers['cache-control'], 'public, max-age=31536000')
}) })