mirror of
https://github.com/nuxt/nuxt.git
synced 2024-12-04 19:37:18 +00:00
commit
aed0a217ab
3
.gitignore
vendored
3
.gitignore
vendored
@ -23,3 +23,6 @@ coverage
|
||||
# Intellij idea
|
||||
*.iml
|
||||
.idea
|
||||
|
||||
# Macos
|
||||
.DS_Store
|
||||
|
@ -2,9 +2,7 @@ language: node_js
|
||||
node_js:
|
||||
- "8.0"
|
||||
- "7.2"
|
||||
- "6.9"
|
||||
before_install:
|
||||
- if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi
|
||||
- "6.11"
|
||||
install:
|
||||
- yarn install
|
||||
- yarn run build
|
||||
|
10
README.md
10
README.md
@ -15,7 +15,7 @@
|
||||
|
||||
</p>
|
||||
|
||||
> Nuxt.js is a framework for server-rendered Vue applications (inspired by [Next.js](https://github.com/zeit/next.js))
|
||||
> Nuxt.js is a Versatile Vue.js Framework
|
||||
|
||||
## 🚧 Under active development, [1.0](https://github.com/nuxt/nuxt.js/projects/1) will be released soon :fire:
|
||||
|
||||
@ -98,6 +98,7 @@ Support us with a monthly donation and help us continue our activities. [[Become
|
||||
- 📘 Documentation: [https://nuxtjs.org](https://nuxtjs.org)
|
||||
- 🎬 Video: [1 minute demo](https://www.youtube.com/watch?v=kmf-p-pTi40)
|
||||
- 🐦 Twitter: [@nuxt_js](https://twitter.com/nuxt_js)
|
||||
- 👥 [Nuxt.js Community](https://github.com/nuxt-community)
|
||||
- 👉 [Play with Nuxt.js online](https://glitch.com/edit/#!/nuxt-hello-world)
|
||||
|
||||
## Getting started
|
||||
@ -170,13 +171,8 @@ const Nuxt = require('nuxt')
|
||||
// Launch nuxt build with given options
|
||||
let config = require('./nuxt.config.js')
|
||||
let nuxt = new Nuxt(config)
|
||||
nuxt.build()
|
||||
.then(() => {
|
||||
|
||||
// You can use nuxt.render(req, res) or nuxt.renderRoute(route, context)
|
||||
})
|
||||
.catch((e) => {
|
||||
// An error happened during the build
|
||||
})
|
||||
```
|
||||
|
||||
Learn more: https://nuxtjs.org/api/nuxt
|
||||
|
15
bin/nuxt
15
bin/nuxt
@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var join = require('path').join
|
||||
const join = require('path').join
|
||||
|
||||
var defaultCommand = 'dev'
|
||||
var commands = new Set([
|
||||
const defaultCommand = 'dev'
|
||||
const commands = new Set([
|
||||
defaultCommand,
|
||||
'init',
|
||||
'build',
|
||||
@ -19,6 +19,13 @@ if (commands.has(cmd)) {
|
||||
cmd = defaultCommand
|
||||
}
|
||||
|
||||
var bin = join(__dirname, 'nuxt-' + cmd)
|
||||
const bin = join(__dirname, 'nuxt-' + cmd)
|
||||
|
||||
// Console error unhandled promises
|
||||
process.on('unhandledRejection', function (err) {
|
||||
/* eslint-disable no-console */
|
||||
console.error(err)
|
||||
console.error('[nuxt] Unhandled promise rejection: ' + err)
|
||||
})
|
||||
|
||||
require(bin)
|
||||
|
@ -1,61 +1,71 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Show logs
|
||||
process.env.DEBUG = 'nuxt:*'
|
||||
process.env.DEBUG = process.env.DEBUG || 'nuxt:*'
|
||||
|
||||
var fs = require('fs')
|
||||
var without = require('lodash').without
|
||||
var Nuxt = require('../')
|
||||
var resolve = require('path').resolve
|
||||
const fs = require('fs')
|
||||
const parseArgs = require('minimist')
|
||||
const { Nuxt, Builder } = require('../')
|
||||
const resolve = require('path').resolve
|
||||
const debug = require('debug')('nuxt:build')
|
||||
debug.color = 2 // Force green color
|
||||
|
||||
// --analyze option
|
||||
var analyzeBuild = false
|
||||
if (process.argv.indexOf('--analyze') !== -1 || process.argv.indexOf('-a') !== -1) {
|
||||
analyzeBuild = true
|
||||
process.argv = without(process.argv, '--analyze', '-a')
|
||||
const argv = parseArgs(process.argv.slice(2), {
|
||||
alias: {
|
||||
h: 'help',
|
||||
c: 'config-file',
|
||||
a: 'analyze'
|
||||
},
|
||||
boolean: ['h', 'a'],
|
||||
string: ['c'],
|
||||
default: {
|
||||
c: 'nuxt.config.js'
|
||||
}
|
||||
})
|
||||
|
||||
if (argv.help) {
|
||||
console.log(`
|
||||
Description
|
||||
Compiles the application for production deployment
|
||||
Usage
|
||||
$ nuxt build <dir>
|
||||
Options
|
||||
--analyze, -a Launch webpack-bundle-analyzer to optimize your bundles.
|
||||
--config-file, -c Path to Nuxt.js config file (default: nuxt.config.js)
|
||||
--help, -h Displays this message
|
||||
`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
var nuxtConfigFileName = 'nuxt.config.js'
|
||||
|
||||
// --config-file option
|
||||
var indexOfConfig = false
|
||||
if (process.argv.indexOf('--config-file') !== -1) {
|
||||
indexOfConfig = process.argv.indexOf('--config-file')
|
||||
} else if (process.argv.indexOf('-c') !== -1) {
|
||||
indexOfConfig = process.argv.indexOf('-c')
|
||||
}
|
||||
|
||||
if (indexOfConfig !== false) {
|
||||
nuxtConfigFileName = process.argv.slice(indexOfConfig)[1]
|
||||
process.argv = without(process.argv, '--config-file', '-c', nuxtConfigFileName)
|
||||
}
|
||||
|
||||
// Root directory parameter
|
||||
var rootDir = resolve(process.argv.slice(2)[0] || '.')
|
||||
var nuxtConfigFilePath = resolve(rootDir, nuxtConfigFileName)
|
||||
const rootDir = resolve(argv._[0] || '.')
|
||||
const nuxtConfigFile = resolve(rootDir, argv['config-file'])
|
||||
|
||||
var options = {}
|
||||
if (fs.existsSync(nuxtConfigFilePath)) {
|
||||
options = require(nuxtConfigFilePath)
|
||||
} else {
|
||||
console.log(`Could not locate ${nuxtConfigFilePath}`) // eslint-disable-line no-console
|
||||
if (fs.existsSync(nuxtConfigFile)) {
|
||||
options = require(nuxtConfigFile)
|
||||
} else if (argv['config-file'] !== 'nuxt.config.js') {
|
||||
console.error(`> Could not load config file ${argv['config-file']}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (typeof options.rootDir !== 'string') {
|
||||
options.rootDir = rootDir
|
||||
}
|
||||
options.dev = false // Create production build when calling `nuxt build`
|
||||
// Create production build when calling `nuxt build`
|
||||
options.dev = false
|
||||
|
||||
// Analyze option
|
||||
options.build = options.build || {}
|
||||
if (analyzeBuild) {
|
||||
options.build.analyze = analyzeBuild
|
||||
if (argv.analyze) {
|
||||
options.build.analyze = true
|
||||
}
|
||||
|
||||
console.log('[nuxt] Building...') // eslint-disable-line no-console
|
||||
var nuxt = module.exports = new Nuxt(options)
|
||||
nuxt.build()
|
||||
debug('Building...')
|
||||
const nuxt = new Nuxt(options)
|
||||
const builder = new Builder(nuxt)
|
||||
|
||||
builder.build()
|
||||
.then(() => {
|
||||
console.log('[nuxt] Building done') // eslint-disable-line no-console
|
||||
debug('Building done')
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err) // eslint-disable-line no-console
|
||||
|
151
bin/nuxt-dev
151
bin/nuxt-dev
@ -1,80 +1,113 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Show logs
|
||||
process.env.DEBUG = 'nuxt:*'
|
||||
process.env.DEBUG = process.env.DEBUG || 'nuxt:*'
|
||||
|
||||
var _ = require('lodash')
|
||||
var debug = require('debug')('nuxt:build')
|
||||
const _ = require('lodash')
|
||||
const debug = require('debug')('nuxt:build')
|
||||
debug.color = 2 // force green color
|
||||
var fs = require('fs')
|
||||
var Nuxt = require('../')
|
||||
var chokidar = require('chokidar')
|
||||
var resolve = require('path').resolve
|
||||
var without = require('lodash').without
|
||||
const fs = require('fs')
|
||||
const parseArgs = require('minimist')
|
||||
const { Nuxt, Builder } = require('../')
|
||||
const chokidar = require('chokidar')
|
||||
const resolve = require('path').resolve
|
||||
|
||||
var nuxtConfigFileName = 'nuxt.config.js'
|
||||
const argv = parseArgs(process.argv.slice(2), {
|
||||
alias: {
|
||||
h: 'help',
|
||||
H: 'hostname',
|
||||
p: 'port',
|
||||
c: 'config-file'
|
||||
},
|
||||
boolean: ['h'],
|
||||
string: ['H', 'c'],
|
||||
default: {
|
||||
c: 'nuxt.config.js'
|
||||
}
|
||||
})
|
||||
|
||||
// --config-file option
|
||||
var indexOfConfig = false
|
||||
if (process.argv.indexOf('--config-file') !== -1) {
|
||||
indexOfConfig = process.argv.indexOf('--config-file')
|
||||
} else if (process.argv.indexOf('-c') !== -1) {
|
||||
indexOfConfig = process.argv.indexOf('-c')
|
||||
if (argv.hostname === '') {
|
||||
console.error(`> Provided hostname argument has no value`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (indexOfConfig !== false) {
|
||||
nuxtConfigFileName = process.argv.slice(indexOfConfig)[1]
|
||||
process.argv = without(process.argv, '--config-file', '-c', nuxtConfigFileName)
|
||||
if (argv.help) {
|
||||
console.log(`
|
||||
Description
|
||||
Starts the application in development mode (hot-code reloading, error
|
||||
reporting, etc)
|
||||
Usage
|
||||
$ nuxt dev <dir> -p <port number> -H <hostname>
|
||||
Options
|
||||
--port, -p A port number on which to start the application
|
||||
--hostname, -H Hostname on which to start the application
|
||||
--config-file, -c Path to Nuxt.js config file (default: nuxt.config.js)
|
||||
--help, -h Displays this message
|
||||
`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
var rootDir = resolve(process.argv.slice(2)[0] || '.')
|
||||
var nuxtConfigFile = resolve(rootDir, nuxtConfigFileName)
|
||||
const rootDir = resolve(argv._[0] || '.')
|
||||
const nuxtConfigFile = resolve(rootDir, argv['config-file'])
|
||||
|
||||
// Load config once for chokidar
|
||||
const nuxtConfig = loadNuxtConfig()
|
||||
_.defaultsDeep(nuxtConfig, { watchers: { chokidar: { ignoreInitial: true } } })
|
||||
|
||||
// Start dev
|
||||
let dev = startDev()
|
||||
|
||||
// Start watching for nuxt.config.js changes
|
||||
chokidar
|
||||
.watch(nuxtConfigFile, nuxtConfig.watchers.chokidar)
|
||||
.on('all', _.debounce(() => {
|
||||
debug('[nuxt.config.js] changed')
|
||||
debug('Rebuilding the app...')
|
||||
dev = dev.then(startDev)
|
||||
}), 2500)
|
||||
|
||||
function startDev (oldNuxt) {
|
||||
// Get latest environment variables
|
||||
const port = argv.port || process.env.PORT || process.env.npm_package_config_nuxt_port
|
||||
const host = argv.hostname || process.env.HOST || process.env.npm_package_config_nuxt_host
|
||||
|
||||
// Load options
|
||||
let options = {}
|
||||
try {
|
||||
options = loadNuxtConfig()
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return // Wait for next reload
|
||||
}
|
||||
|
||||
// Create nuxt and builder instance
|
||||
const nuxt = new Nuxt(options)
|
||||
const builder = new Builder(nuxt)
|
||||
|
||||
return Promise.resolve()
|
||||
.then(() => builder.build()) // 1- Start build
|
||||
.then(() => oldNuxt ? oldNuxt.close() : Promise.resolve()) // 2- Close old nuxt after successful build
|
||||
.then(() => nuxt.listen(port, host)) // 3- Start listening
|
||||
.then(() => nuxt) // 4- Pass new nuxt to watch chain
|
||||
}
|
||||
|
||||
function loadNuxtConfig () {
|
||||
let options = {}
|
||||
|
||||
var options = {}
|
||||
if (fs.existsSync(nuxtConfigFile)) {
|
||||
delete require.cache[nuxtConfigFile]
|
||||
options = require(nuxtConfigFile)
|
||||
} else if (argv['config-file'] !== 'nuxt.config.js') {
|
||||
console.error(`> Could not load config file ${argv['config-file']}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (typeof options.rootDir !== 'string') {
|
||||
options.rootDir = rootDir
|
||||
}
|
||||
options.dev = true // Add hot reloading and watching changes
|
||||
|
||||
var nuxt = module.exports = new Nuxt(options)
|
||||
var port = process.env.PORT || process.env.npm_package_config_nuxt_port
|
||||
var host = process.env.HOST || process.env.npm_package_config_nuxt_host
|
||||
var server = nuxt.server = new nuxt.Server(nuxt).listen(port, host)
|
||||
// Force development mode for add hot reloading and watching changes
|
||||
options.dev = true
|
||||
|
||||
listenOnConfigChanges(nuxt, server)
|
||||
|
||||
function listenOnConfigChanges(nuxt, server) {
|
||||
// Listen on nuxt.config.js changes
|
||||
var build = _.debounce(() => {
|
||||
debug('[nuxt.config.js] changed')
|
||||
delete require.cache[nuxtConfigFile]
|
||||
var options = {}
|
||||
if (fs.existsSync(nuxtConfigFile)) {
|
||||
try {
|
||||
options = require(nuxtConfigFile)
|
||||
} catch (e) {
|
||||
return console.error(e) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
options.rootDir = rootDir
|
||||
nuxt.close()
|
||||
.then(() => {
|
||||
nuxt.renderer = null
|
||||
debug('Rebuilding the app...')
|
||||
return new Nuxt(options).build()
|
||||
})
|
||||
.then((nuxt) => {
|
||||
server.nuxt = nuxt
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error while rebuild the app:', error) // eslint-disable-line no-console
|
||||
process.exit(1)
|
||||
})
|
||||
}, 200)
|
||||
var nuxtConfigFile = resolve(rootDir, nuxtConfigFileName)
|
||||
chokidar.watch(nuxtConfigFile, Object.assign({}, nuxt.options.watchers.chokidar, {ignoreInitial: true}))
|
||||
.on('all', build)
|
||||
return options
|
||||
}
|
||||
|
@ -1,29 +1,63 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Show logs
|
||||
process.env.DEBUG = 'nuxt:*'
|
||||
process.env.DEBUG = process.env.DEBUG || 'nuxt:*'
|
||||
|
||||
var fs = require('fs')
|
||||
var Nuxt = require('../')
|
||||
var resolve = require('path').resolve
|
||||
const fs = require('fs')
|
||||
const parseArgs = require('minimist')
|
||||
const debug = require('debug')('nuxt:generate')
|
||||
|
||||
var rootDir = resolve(process.argv.slice(2)[0] || '.')
|
||||
var nuxtConfigFile = resolve(rootDir, 'nuxt.config.js')
|
||||
const { Nuxt, Builder, Generator } = require('../')
|
||||
const resolve = require('path').resolve
|
||||
|
||||
const argv = parseArgs(process.argv.slice(2), {
|
||||
alias: {
|
||||
h: 'help',
|
||||
c: 'config-file'
|
||||
},
|
||||
boolean: ['h'],
|
||||
string: ['c'],
|
||||
default: {
|
||||
c: 'nuxt.config.js'
|
||||
}
|
||||
})
|
||||
|
||||
if (argv.help) {
|
||||
console.log(`
|
||||
Description
|
||||
Generate a static web application (server-rendered)
|
||||
Usage
|
||||
$ nuxt generate <dir>
|
||||
Options
|
||||
--config-file, -c Path to Nuxt.js config file (default: nuxt.config.js)
|
||||
--help, -h Displays this message
|
||||
`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const rootDir = resolve(argv._[0] || '.')
|
||||
const nuxtConfigFile = resolve(rootDir, argv['config-file'])
|
||||
|
||||
var options = {}
|
||||
if (fs.existsSync(nuxtConfigFile)) {
|
||||
options = require(nuxtConfigFile)
|
||||
} else if (argv['config-file'] !== 'nuxt.config.js') {
|
||||
console.error(`> Could not load config file ${argv['config-file']}`)
|
||||
process.exit(1)
|
||||
}
|
||||
if (typeof options.rootDir !== 'string') {
|
||||
options.rootDir = rootDir
|
||||
}
|
||||
options.dev = false // Force production mode (no webpack middleware called)
|
||||
|
||||
console.log('[nuxt] Generating...') // eslint-disable-line no-console
|
||||
var nuxt = module.exports = new Nuxt(options)
|
||||
nuxt.generate()
|
||||
debug('Generating...')
|
||||
const nuxt = new Nuxt(options)
|
||||
const builder = new Builder(nuxt)
|
||||
const generator = new Generator(nuxt, builder)
|
||||
generator.generate()
|
||||
.then(() => {
|
||||
console.log('[nuxt] Generate done') // eslint-disable-line no-console
|
||||
debug('Generate done')
|
||||
process.exit(0)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err) // eslint-disable-line no-console
|
||||
|
@ -1,22 +1,72 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var fs = require('fs')
|
||||
var Nuxt = require('../')
|
||||
var resolve = require('path').resolve
|
||||
const fs = require('fs')
|
||||
const parseArgs = require('minimist')
|
||||
const { Nuxt } = require('../')
|
||||
const { join, resolve } = require('path')
|
||||
|
||||
var rootDir = resolve(process.argv.slice(2)[0] || '.')
|
||||
var nuxtConfigFile = resolve(rootDir, 'nuxt.config.js')
|
||||
const argv = parseArgs(process.argv.slice(2), {
|
||||
alias: {
|
||||
h: 'help',
|
||||
H: 'hostname',
|
||||
p: 'port',
|
||||
c: 'config-file'
|
||||
},
|
||||
boolean: ['h'],
|
||||
string: ['H', 'c'],
|
||||
default: {
|
||||
c: 'nuxt.config.js'
|
||||
}
|
||||
})
|
||||
|
||||
if (argv.hostname === '') {
|
||||
console.error(`> Provided hostname argument has no value`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (argv.help) {
|
||||
console.log(`
|
||||
Description
|
||||
Starts the application in production mode.
|
||||
The application should be compiled with \`nuxt build\` first.
|
||||
Usage
|
||||
$ nuxt start <dir> -p <port number> -H <hostname>
|
||||
Options
|
||||
--port, -p A port number on which to start the application
|
||||
--hostname, -H Hostname on which to start the application
|
||||
--config-file, -c Path to Nuxt.js config file (default: nuxt.config.js)
|
||||
--help, -h Displays this message
|
||||
`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const rootDir = resolve(argv._[0] || '.')
|
||||
const nuxtConfigFile = resolve(rootDir, argv['config-file'])
|
||||
|
||||
let options = {}
|
||||
|
||||
var options = {}
|
||||
if (fs.existsSync(nuxtConfigFile)) {
|
||||
options = require(nuxtConfigFile)
|
||||
} else if (argv['config-file'] !== 'nuxt.config.js') {
|
||||
console.error(`> Could not load config file ${argv['config-file']}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (typeof options.rootDir !== 'string') {
|
||||
options.rootDir = rootDir
|
||||
}
|
||||
options.dev = false // Force production mode (no webpack middleware called)
|
||||
|
||||
var nuxt = module.exports = new Nuxt(options)
|
||||
var port = process.env.PORT || process.env.npm_package_config_nuxt_port
|
||||
var host = process.env.HOST || process.env.npm_package_config_nuxt_host
|
||||
var server = nuxt.server = new nuxt.Server(nuxt).listen(port, host)
|
||||
// Force production mode (no webpack middleware called)
|
||||
options.dev = false
|
||||
|
||||
// Check if project is built for production
|
||||
const distDir = join(options.rootDir, options.buildDir || '.nuxt', 'dist')
|
||||
if (!fs.existsSync(join(distDir, 'server-bundle.json'))) {
|
||||
console.error('> No build files found, please run `nuxt build` before launching `nuxt start`') // eslint-disable-line no-console
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const nuxt = new Nuxt(options)
|
||||
const port = argv.port || process.env.PORT || process.env.npm_package_config_nuxt_port
|
||||
const host = argv.hostname || process.env.HOST || process.env.npm_package_config_nuxt_host
|
||||
nuxt.listen(port, host)
|
||||
|
109
build/rollup.config.js
Executable file
109
build/rollup.config.js
Executable file
@ -0,0 +1,109 @@
|
||||
// Some parts brought from https://github.com/vuejs/vue/blob/dev/build/config.js
|
||||
const { resolve } = require('path')
|
||||
const rollupBabel = require('rollup-plugin-babel')
|
||||
const rollupAlias = require('rollup-plugin-alias')
|
||||
const rollupCommonJS = require('rollup-plugin-commonjs')
|
||||
const rollupReplace = require('rollup-plugin-replace')
|
||||
const rollupNodeResolve = require('rollup-plugin-node-resolve')
|
||||
const packageJson = require('../package.json')
|
||||
|
||||
const dependencies = Object.keys(packageJson.dependencies)
|
||||
const version = packageJson.version || process.env.VERSION
|
||||
|
||||
// -----------------------------
|
||||
// Banner
|
||||
// -----------------------------
|
||||
const banner =
|
||||
'/*!\n' +
|
||||
' * Nuxt.js v' + version + '\n' +
|
||||
' * Released under the MIT License.\n' +
|
||||
' */'
|
||||
|
||||
// -----------------------------
|
||||
// Aliases
|
||||
// -----------------------------
|
||||
const rootDir = resolve(__dirname, '..')
|
||||
const libDir = resolve(rootDir, 'lib')
|
||||
const distDir = resolve(rootDir, 'dist')
|
||||
|
||||
const aliases = {
|
||||
core: resolve(libDir, 'core/index.js'),
|
||||
builder: resolve(libDir, 'builder/index.js'),
|
||||
common: resolve(libDir, 'common/index.js'),
|
||||
utils: resolve(libDir, 'common/utils.js'),
|
||||
app: resolve(libDir, 'app')
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// Builds
|
||||
// -----------------------------
|
||||
const builds = {
|
||||
nuxt: {
|
||||
entry: resolve(libDir, 'index.js'),
|
||||
dest: resolve(distDir, 'nuxt.js')
|
||||
},
|
||||
core: {
|
||||
entry: resolve(libDir, 'core/index.js'),
|
||||
dest: resolve(distDir, 'core.js')
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// Default config
|
||||
// -----------------------------
|
||||
function genConfig (opts) {
|
||||
const config = {
|
||||
entry: opts.entry,
|
||||
dest: opts.dest,
|
||||
external: ['fs', 'path', 'http'].concat(dependencies, opts.external),
|
||||
format: opts.format || 'cjs',
|
||||
banner: opts.banner || banner,
|
||||
moduleName: opts.moduleName || 'Nuxt',
|
||||
sourceMap: true,
|
||||
plugins: [
|
||||
rollupAlias(Object.assign({
|
||||
resolve: ['.js', '.json', '.jsx', '.ts']
|
||||
}, aliases, opts.alias)),
|
||||
|
||||
rollupNodeResolve({ main: true, jsnext: true }),
|
||||
|
||||
rollupCommonJS(),
|
||||
|
||||
rollupBabel(Object.assign({
|
||||
exclude: 'node_modules/**',
|
||||
plugins: [
|
||||
['transform-runtime', { 'helpers': false, 'polyfill': false }],
|
||||
'transform-async-to-generator',
|
||||
'array-includes'
|
||||
],
|
||||
presets: [
|
||||
'babel-preset-es2015-rollup'
|
||||
],
|
||||
'env': {
|
||||
'test': {
|
||||
'plugins': [ 'istanbul' ]
|
||||
}
|
||||
}
|
||||
}, opts.babel)),
|
||||
|
||||
rollupReplace({
|
||||
__VERSION__: version
|
||||
})
|
||||
].concat(opts.plugins || [])
|
||||
}
|
||||
|
||||
if (opts.env) {
|
||||
config.plugins.push(rollupReplace({
|
||||
'process.env.NODE_ENV': JSON.stringify(opts.env)
|
||||
}))
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
if (process.env.TARGET) {
|
||||
module.exports = genConfig(builds[process.env.TARGET])
|
||||
} else {
|
||||
exports.getBuild = name => genConfig(builds[name])
|
||||
exports.getAllBuilds = () => Object.keys(builds).map(name => genConfig(builds[name]))
|
||||
}
|
92
build/start.js
Executable file
92
build/start.js
Executable file
@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { readFileSync, readJSONSync, writeFileSync, copySync, removeSync } = require('fs-extra')
|
||||
const { resolve, relative } = require('path')
|
||||
|
||||
// Dirs
|
||||
const rootDir = resolve(__dirname, '..')
|
||||
const startDir = resolve(rootDir, 'start')
|
||||
|
||||
// Read main package.json
|
||||
const packageJSON = readJSONSync(resolve(rootDir, 'package.json'))
|
||||
|
||||
// Required and Excluded packages for start
|
||||
let requires = [
|
||||
'source-map-support'
|
||||
]
|
||||
const excludes = [
|
||||
'path',
|
||||
'fs'
|
||||
].concat(Object.keys(packageJSON.devDependencies))
|
||||
|
||||
// Parse dist/core.js for all external dependencies
|
||||
const requireRegex = /require\('([-\w]+)'\)/g
|
||||
const rawCore = readFileSync(resolve(rootDir, 'dist/core.js'))
|
||||
let match = requireRegex.exec(rawCore)
|
||||
while (match) {
|
||||
requires.push(match[1])
|
||||
match = requireRegex.exec(rawCore)
|
||||
}
|
||||
|
||||
// Apply Excludes
|
||||
requires = requires.filter(r => excludes.indexOf(r) === -1)
|
||||
|
||||
// Resolve version constrains
|
||||
let dependencies = {}
|
||||
requires.forEach(r => {
|
||||
if (!packageJSON.dependencies[r]) {
|
||||
console.warn('cannot resolve dependency version for ' + r)
|
||||
return
|
||||
}
|
||||
dependencies[r] = packageJSON.dependencies[r]
|
||||
})
|
||||
|
||||
// Drop fields
|
||||
let drops = ['devDependencies', 'scripts', 'nyc', 'types']
|
||||
drops.forEach(k => {
|
||||
delete packageJSON[k]
|
||||
})
|
||||
|
||||
// Update dependencies
|
||||
packageJSON.dependencies = dependencies
|
||||
|
||||
// Update package meta
|
||||
packageJSON.name = 'nuxt-start'
|
||||
packageJSON.description = 'runtime-only build for nuxt'
|
||||
|
||||
// Update package.json
|
||||
writeFileSync(resolve(startDir, 'package.json'), JSON.stringify(packageJSON, null, 2))
|
||||
|
||||
// Copy required files
|
||||
const excludeFiles = [
|
||||
'README.md',
|
||||
'.gitignore'
|
||||
]
|
||||
packageJSON.files.forEach(file => {
|
||||
if (excludeFiles.indexOf(file) !== -1) {
|
||||
return
|
||||
}
|
||||
let src = resolve(rootDir, file)
|
||||
let dst = resolve(startDir, file)
|
||||
// console.log(relative(rootDir, src), '~>', relative(rootDir, dst))
|
||||
removeSync(dst)
|
||||
copySync(src, dst)
|
||||
})
|
||||
|
||||
// Remove extras
|
||||
const extraFiles = [
|
||||
'bin/nuxt-build',
|
||||
'bin/nuxt-generate',
|
||||
'bin/nuxt-dev',
|
||||
'dist/nuxt.js',
|
||||
'dist/nuxt.js.map'
|
||||
]
|
||||
extraFiles.forEach(file => {
|
||||
removeSync(resolve(startDir, file))
|
||||
})
|
||||
|
||||
// Patch index.js
|
||||
const startIndexjs = resolve(startDir, 'index.js')
|
||||
writeFileSync(startIndexjs, String(readFileSync(startIndexjs)).replace('./dist/nuxt', './dist/core'))
|
||||
|
||||
console.log('generated ' + packageJSON.name + '@' + packageJSON.version)
|
69
examples/async-component-injection/assets/css/common.css
Normal file
69
examples/async-component-injection/assets/css/common.css
Normal file
@ -0,0 +1,69 @@
|
||||
body {
|
||||
font-family: "Roboto", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, serif;
|
||||
text-rendering: optimizelegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
word-spacing: 1px;
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
img {
|
||||
border: none;
|
||||
}
|
||||
a {
|
||||
color: #666;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
.header {
|
||||
letter-spacing: 5px;
|
||||
margin: 50px auto 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.header a {
|
||||
font-size: 15px;
|
||||
color: #444;
|
||||
}
|
||||
.links {
|
||||
text-align: center;
|
||||
font-family: "Roboto", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, serif;
|
||||
color: #999;
|
||||
font-size: 24px;
|
||||
margin: 0;
|
||||
}
|
||||
.links a {
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
margin: 0 3px;
|
||||
}
|
||||
.links img {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
.header,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: "Montserrat", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, sans-serif;
|
||||
font-weight: 400;
|
||||
color: #444;
|
||||
}
|
||||
.main {
|
||||
max-width: 600px;
|
||||
margin: 50px auto;
|
||||
padding: 0 30px 50px;
|
||||
position: relative;
|
||||
}
|
||||
@media screen and (max-width: 420px) {
|
||||
.header {
|
||||
margin: 40px auto 10px;
|
||||
}
|
||||
.header a {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
57
examples/async-component-injection/assets/css/index.css
Normal file
57
examples/async-component-injection/assets/css/index.css
Normal file
@ -0,0 +1,57 @@
|
||||
.main > ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
padding-top: 4px;
|
||||
}
|
||||
.main > ul > li {
|
||||
position: relative;
|
||||
padding: 30px 0 30px;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
.main > ul > li:first-child {
|
||||
margin-top: -30px;
|
||||
}
|
||||
.main h2,
|
||||
.main h3 {
|
||||
letter-spacing: 1px;
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.main h2 {
|
||||
font-size: 20px;
|
||||
letter-spacing: 1px;
|
||||
margin-left: 120px;
|
||||
}
|
||||
.main h2 a {
|
||||
color: #444;
|
||||
}
|
||||
.main h2 a:hover {
|
||||
color: #f33;
|
||||
}
|
||||
.main h3 {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 33px;
|
||||
}
|
||||
@media screen and (max-width: 420px) {
|
||||
.main h2 {
|
||||
font-size: 16px;
|
||||
margin-left: 0;
|
||||
}
|
||||
.main h2 a:hover {
|
||||
color: #f66;
|
||||
}
|
||||
.main h3 {
|
||||
font-size: 11px;
|
||||
position: static;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.main ul li {
|
||||
padding: 18px 0 20px;
|
||||
}
|
||||
.main ul li:first-child {
|
||||
margin-top: -35px;
|
||||
}
|
||||
}
|
307
examples/async-component-injection/assets/css/post.css
Normal file
307
examples/async-component-injection/assets/css/post.css
Normal file
@ -0,0 +1,307 @@
|
||||
.gutter pre {
|
||||
color: #999;
|
||||
}
|
||||
pre {
|
||||
color: #525252;
|
||||
}
|
||||
pre .function .keyword,
|
||||
pre .constant {
|
||||
color: #0092db;
|
||||
}
|
||||
pre .keyword,
|
||||
pre .attribute {
|
||||
color: #e96900;
|
||||
}
|
||||
pre .number,
|
||||
pre .literal {
|
||||
color: #ae81ff;
|
||||
}
|
||||
pre .tag,
|
||||
pre .tag .title,
|
||||
pre .change,
|
||||
pre .winutils,
|
||||
pre .flow,
|
||||
pre .lisp .title,
|
||||
pre .clojure .built_in,
|
||||
pre .nginx .title,
|
||||
pre .tex .special {
|
||||
color: #2973b7;
|
||||
}
|
||||
pre .class .title {
|
||||
color: #fff;
|
||||
}
|
||||
pre .symbol,
|
||||
pre .symbol .string,
|
||||
pre .value,
|
||||
pre .regexp {
|
||||
color: #42b983;
|
||||
}
|
||||
pre .title {
|
||||
color: #a6e22e;
|
||||
}
|
||||
pre .tag .value,
|
||||
pre .string,
|
||||
pre .subst,
|
||||
pre .haskell .type,
|
||||
pre .preprocessor,
|
||||
pre .ruby .class .parent,
|
||||
pre .built_in,
|
||||
pre .sql .aggregate,
|
||||
pre .django .template_tag,
|
||||
pre .django .variable,
|
||||
pre .smalltalk .class,
|
||||
pre .javadoc,
|
||||
pre .django .filter .argument,
|
||||
pre .smalltalk .localvars,
|
||||
pre .smalltalk .array,
|
||||
pre .attr_selector,
|
||||
pre .pseudo,
|
||||
pre .addition,
|
||||
pre .stream,
|
||||
pre .envvar,
|
||||
pre .apache .tag,
|
||||
pre .apache .cbracket,
|
||||
pre .tex .command,
|
||||
pre .prompt {
|
||||
color: #42b983;
|
||||
}
|
||||
pre .comment,
|
||||
pre .java .annotation,
|
||||
pre .python .decorator,
|
||||
pre .template_comment,
|
||||
pre .pi,
|
||||
pre .doctype,
|
||||
pre .deletion,
|
||||
pre .shebang,
|
||||
pre .apache .sqbracket,
|
||||
pre .tex .formula {
|
||||
color: #b3b3b3;
|
||||
}
|
||||
pre .coffeescript .javascript,
|
||||
pre .javascript .xml,
|
||||
pre .tex .formula,
|
||||
pre .xml .javascript,
|
||||
pre .xml .vbscript,
|
||||
pre .xml .css,
|
||||
pre .xml .cdata {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.main .post {
|
||||
position: relative;
|
||||
padding-bottom: 30px;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
.main .post h1,
|
||||
.main .post h2 {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0px;
|
||||
}
|
||||
.main .post h1 a:hover,
|
||||
.main .post h2 a:hover {
|
||||
border-bottom: 3px solid #666;
|
||||
}
|
||||
.main .post h1 {
|
||||
font-size: 32px;
|
||||
margin: 0 0 45px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.main .post h2 {
|
||||
font-size: 24px;
|
||||
margin: 60px 0 30px;
|
||||
position: relative;
|
||||
}
|
||||
.main .post h2:before {
|
||||
content: '';
|
||||
border-left: 5px solid #41b883;
|
||||
position: absolute;
|
||||
left: -15px;
|
||||
height: 75%;
|
||||
top: 12%;
|
||||
}
|
||||
.main .post h3 {
|
||||
margin: 30px 0 15px;
|
||||
}
|
||||
.main .post .date {
|
||||
font-family: "Montserrat", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
margin: 0 0 30px;
|
||||
letter-spacing: 1px;
|
||||
position: initial;
|
||||
text-transform: none;
|
||||
}
|
||||
.main .post .content {
|
||||
text-align: left;
|
||||
line-height: 1.8em;
|
||||
}
|
||||
.main .post .content p,
|
||||
.main .post .content ul,
|
||||
.main .post .content ol {
|
||||
margin: 1em 0 1.5em;
|
||||
}
|
||||
.main .post .content strong {
|
||||
font-weight: 600;
|
||||
color: #444;
|
||||
}
|
||||
.main .post .content ol {
|
||||
padding-left: 1.6em;
|
||||
}
|
||||
.main .post .content ul {
|
||||
padding-left: 15px;
|
||||
list-style-type: none;
|
||||
}
|
||||
.main .post .content ul li:before {
|
||||
position: absolute;
|
||||
font-weight: 600;
|
||||
content: " · ";
|
||||
margin: 0;
|
||||
left: 0;
|
||||
}
|
||||
.main .post .content a {
|
||||
color: #41b883;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
.main .post .content a:hover {
|
||||
color: #41b883;
|
||||
border-bottom-color: #41b883;
|
||||
}
|
||||
.main .post .content .highlight,
|
||||
.main .post .content .highlight table {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.main .post .content .highlight {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.main .post .content .highlight table,
|
||||
.main .post .content .highlight tr,
|
||||
.main .post .content .highlight td {
|
||||
padding: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.main .post .content code {
|
||||
font-family: "Roboto Mono", "Menlo", "Consolas", monospace;
|
||||
font-size: 13px;
|
||||
background-color: #f6f6f6;
|
||||
padding: 3px 10px;
|
||||
margin: 0 5px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.main .post .content pre {
|
||||
font-family: "Roboto Mono", "Menlo", "Consolas", monospace;
|
||||
font-size: 13px;
|
||||
overflow-x: auto;
|
||||
text-align: left;
|
||||
padding: 15px 25px;
|
||||
background-color: #f6f6f6;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
.main .post .content .code pre {
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
.main .post .content .gutter pre {
|
||||
padding: 15px 0 15px 15px;
|
||||
color: #75715e;
|
||||
border-top-left-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.main .post .content blockquote {
|
||||
margin: 2em 0;
|
||||
padding-left: 30px;
|
||||
border-left: 5px solid #e6e6e6;
|
||||
}
|
||||
.main .post .content blockquote p {
|
||||
font-size: 17px;
|
||||
font-style: italic;
|
||||
line-height: 1.8em;
|
||||
color: #999;
|
||||
}
|
||||
.main .post img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
.blog-nav {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-family: "Montserrat", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
color: #999;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
letter-spacing: 1px;
|
||||
border-bottom: 3px solid transparent;
|
||||
}
|
||||
.blog-nav:hover {
|
||||
color: #333;
|
||||
border-bottom-color: #333;
|
||||
}
|
||||
#newer {
|
||||
left: 40px;
|
||||
}
|
||||
#older {
|
||||
right: 40px;
|
||||
}
|
||||
.show-comments {
|
||||
font-family: "Montserrat", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
.show-comments a {
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
}
|
||||
.show-comments a:hover {
|
||||
color: #666;
|
||||
}
|
||||
@media screen and (max-width: 900px) {
|
||||
.main .post {
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
.blog-nav {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
}
|
||||
#newer {
|
||||
left: 0;
|
||||
}
|
||||
#older {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 420px) {
|
||||
.main {
|
||||
margin-top: 32px;
|
||||
}
|
||||
.main .post h1 {
|
||||
font-size: 24px;
|
||||
margin: 0 0 30px;
|
||||
}
|
||||
.main .post h2 {
|
||||
font-size: 20px;
|
||||
margin: 30px 0 15px;
|
||||
}
|
||||
.main .post h3 {
|
||||
font-size: 16px;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
.main .post .date {
|
||||
font-size: 12px;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
.main .post .content {
|
||||
font-size: 15px;
|
||||
}
|
||||
.main .post .content pre {
|
||||
font-size: 12px;
|
||||
}
|
||||
.main .post .content blockquote p {
|
||||
font-size: 16px;
|
||||
}
|
||||
.blog-nav {
|
||||
font-size: 14px;
|
||||
color: #444;
|
||||
}
|
||||
}
|
BIN
examples/async-component-injection/assets/img/github.png
Normal file
BIN
examples/async-component-injection/assets/img/github.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
examples/async-component-injection/assets/img/swimmer.jpg
Normal file
BIN
examples/async-component-injection/assets/img/swimmer.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 139 KiB |
BIN
examples/async-component-injection/assets/img/twitter.png
Normal file
BIN
examples/async-component-injection/assets/img/twitter.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
15
examples/async-component-injection/layouts/default.vue
Normal file
15
examples/async-component-injection/layouts/default.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<div class="header">
|
||||
<nuxt-link to="/">NUXT BLOG</nuxt-link>
|
||||
</div>
|
||||
<p class="links">
|
||||
<a href="https://twitter.com/nuxt_js" target="_blank"><img src="~assets/img/twitter.png"></a>
|
||||
<a href="https://github.com/nuxt/nuxt.js/tree/dev/examples/async-component-injection" target="_blank"><img src="~assets/img/github.png"></a>
|
||||
</p>
|
||||
<div class="main">
|
||||
<nuxt/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
22
examples/async-component-injection/nuxt.config.js
Normal file
22
examples/async-component-injection/nuxt.config.js
Normal file
@ -0,0 +1,22 @@
|
||||
module.exports = {
|
||||
head: {
|
||||
title: 'Nuxt Blog',
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' }
|
||||
],
|
||||
link: [
|
||||
{ rel: 'icon', href: '/favicon.ico', type: 'image/x-icon' },
|
||||
{ rel: 'stylesheet', href: 'http://fonts.googleapis.com/css?family=Montserrat|Roboto:400,400italic,600|Roboto+Mono', type: 'text/css' }
|
||||
]
|
||||
},
|
||||
css: [
|
||||
'@/assets/css/common.css'
|
||||
],
|
||||
generate: {
|
||||
routes: [
|
||||
'/deep-dive-into-ocean',
|
||||
'/welcome-to-my-blog'
|
||||
]
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "offline-config-nuxt",
|
||||
"name": "components-injection-nuxt",
|
||||
"dependencies": {
|
||||
"nuxt": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "nuxt",
|
||||
"build": "nuxt build",
|
||||
"start": "nuxt start"
|
||||
},
|
||||
"dependencies": {
|
||||
"nuxt": "latest"
|
||||
"start": "nuxt"
|
||||
}
|
||||
}
|
21
examples/async-component-injection/pages/_slug.vue
Normal file
21
examples/async-component-injection/pages/_slug.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="post">
|
||||
<component :is="component"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// See https://vuejs.org/v2/guide/components.html#Advanced-Async-Components
|
||||
const getPost = (slug) => ({
|
||||
component: import(`@/posts/${slug}`),
|
||||
error: require('@/posts/404')
|
||||
})
|
||||
|
||||
export default {
|
||||
beforeCreate () {
|
||||
this.component = () => getPost(this.$route.params.slug)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style src="@/assets/css/post.css"/>
|
22
examples/async-component-injection/pages/index.vue
Normal file
22
examples/async-component-injection/pages/index.vue
Normal file
@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<ul>
|
||||
<li v-for="(post, index) in posts" :key="index">
|
||||
<h3>{{ post.date }}</h3>
|
||||
<h2><nuxt-link :to="post.link">{{ post.title }}</nuxt-link></h2>
|
||||
</li>
|
||||
<li style="border:none;text-align: center;font-size: 14px;">Design from <a href="http://blog.evanyou.me" target="_blank">EvanYou.me</a></li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: () => ({
|
||||
posts: [
|
||||
{ date: 'Jul 10, 2017', title: 'Deep dive into the Ocean', link: '/deep-dive-into-ocean' },
|
||||
{ date: 'Jul 08, 2017', title: 'Welcome to my blog', link: '/welcome-to-my-blog' }
|
||||
]
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style src="@/assets/css/index.css"/>
|
3
examples/async-component-injection/posts/404.vue
Normal file
3
examples/async-component-injection/posts/404.vue
Normal file
@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<h1 style="text-align: center;">Article not found</h1>
|
||||
</template>
|
@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3 class="date">Jul 10, 2017</h3>
|
||||
<h1>Deep dive into the Ocean</h1>
|
||||
<div class="content">
|
||||
<img src="~assets/img/swimmer.jpg">
|
||||
<h2>Subtitle #1</h2>
|
||||
<p>Far far away, behind the word mountains, far from the countries Vokalia and Consonantia, there live the blind texts. Separated they live in Bookmarksgrove right at the coast of the Semantics, a large language ocean. A small river named Duden flows by their place and supplies it with the necessary regelialia. It is a paradisematic country, in which roasted parts of sentences fly into your mouth. Even the all-powerful Pointing has no control about the blind texts it is an almost unorthographic life One day however a small line of blind text by the name of Lorem Ipsum decided to leave for the far World of Grammar.</p>
|
||||
<h2>Another subtitle</h2>
|
||||
<ul>
|
||||
<li>Vue.js</li>
|
||||
<li>Nuxt.js</li>
|
||||
<li>= <3</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3 class="date">Jul 08, 2017</h3>
|
||||
<h1>Welcome to my blog</h1>
|
||||
<div class="content">
|
||||
<h2>What is Lorem Ipsum?</h2>
|
||||
<p><b>Lorem Ipsum</b> is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
BIN
examples/async-component-injection/static/favicon.ico
Normal file
BIN
examples/async-component-injection/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -1,8 +1,7 @@
|
||||
{
|
||||
"name": "nuxt-async-data",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"axios": "^0.15.2",
|
||||
"axios": "latest",
|
||||
"nuxt": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -9,14 +9,9 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
asyncData ({ req }, callback) {
|
||||
setTimeout(function () {
|
||||
// callback(err, data)
|
||||
callback(null, {
|
||||
asyncData: ({ req }) => ({
|
||||
userAgent: (req ? req.headers['user-agent'] : navigator.userAgent)
|
||||
})
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
36
examples/auth-routes/api/index.js
Normal file
36
examples/auth-routes/api/index.js
Normal file
@ -0,0 +1,36 @@
|
||||
const express = require('express')
|
||||
|
||||
// Create express router
|
||||
const router = express.Router()
|
||||
|
||||
// Transform req & res to have the same API as express
|
||||
// So we can use res.status() & res.json()
|
||||
var app = express()
|
||||
router.use((req, res, next) => {
|
||||
Object.setPrototypeOf(req, app.request)
|
||||
Object.setPrototypeOf(res, app.response)
|
||||
req.res = res
|
||||
res.req = req
|
||||
next()
|
||||
})
|
||||
|
||||
// Add POST - /api/login
|
||||
router.post('/login', (req, res) => {
|
||||
if (req.body.username === 'demo' && req.body.password === 'demo') {
|
||||
req.session.authUser = { username: 'demo' }
|
||||
return res.json({ username: 'demo' })
|
||||
}
|
||||
res.status(401).json({ message: 'Bad credentials' })
|
||||
})
|
||||
|
||||
// Add POST - /api/logout
|
||||
router.post('/logout', (req, res) => {
|
||||
delete req.session.authUser
|
||||
res.json({ ok: true })
|
||||
})
|
||||
|
||||
// Export the server middleware
|
||||
module.exports = {
|
||||
path: '/api',
|
||||
handler: router
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
export default function ({ store, redirect, error }) {
|
||||
// If user not connected, redirect to /
|
||||
export default function ({ store, error }) {
|
||||
if (!store.state.authUser) {
|
||||
// return redirect('/')
|
||||
error({
|
||||
message: 'You are not connected',
|
||||
statusCode: 403
|
||||
|
@ -1,3 +1,6 @@
|
||||
const bodyParser = require('body-parser')
|
||||
const session = require('express-session')
|
||||
|
||||
module.exports = {
|
||||
head: {
|
||||
title: 'Auth Routes',
|
||||
@ -9,5 +12,24 @@ module.exports = {
|
||||
},
|
||||
build: {
|
||||
vendor: ['axios']
|
||||
}
|
||||
},
|
||||
/*
|
||||
** Add server middleware
|
||||
** Nuxt.js uses `connect` module as server
|
||||
** So most of express middleware works with nuxt.js server middleware
|
||||
*/
|
||||
serverMiddleware: [
|
||||
// body-parser middleware
|
||||
bodyParser.json(),
|
||||
// session middleware
|
||||
session({
|
||||
secret: 'super-secret-key',
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: { maxAge: 60000 }
|
||||
}),
|
||||
// Api middleware
|
||||
// We add /api/login & /api/logout routes
|
||||
'~/api'
|
||||
]
|
||||
}
|
||||
|
@ -1,17 +1,15 @@
|
||||
{
|
||||
"name": "auth-routes",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"axios": "^0.16.1",
|
||||
"body-parser": "^1.17.2",
|
||||
"cross-env": "^5.0.0",
|
||||
"express": "^4.15.3",
|
||||
"express-session": "^1.15.3",
|
||||
"nuxt": "^1.0.0-alpha1"
|
||||
"nuxt": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "node server.js",
|
||||
"dev": "nuxt",
|
||||
"build": "nuxt build",
|
||||
"start": "cross-env NODE_ENV=production node server.js"
|
||||
"start": "nuxt start"
|
||||
}
|
||||
}
|
||||
|
@ -28,22 +28,25 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
login () {
|
||||
this.$store.dispatch('login', {
|
||||
async login () {
|
||||
try {
|
||||
await this.$store.dispatch('login', {
|
||||
username: this.formUsername,
|
||||
password: this.formPassword
|
||||
})
|
||||
.then(() => {
|
||||
this.formUsername = ''
|
||||
this.formPassword = ''
|
||||
this.formError = null
|
||||
})
|
||||
.catch((e) => {
|
||||
} catch(e) {
|
||||
this.formError = e.message
|
||||
})
|
||||
}
|
||||
},
|
||||
logout () {
|
||||
this.$store.dispatch('logout')
|
||||
async logout () {
|
||||
try {
|
||||
await this.$store.dispatch('logout')
|
||||
} catch (e) {
|
||||
this.formError = e.message
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Super secret page</h1>
|
||||
<p>If you try to access this URL not connected, you will be redirected to the home page (server-side or client-side)</p>
|
||||
<p>If you try to access this URL not connected, you will see the error page telling your that you are not connected.</p>
|
||||
<nuxt-link to="/">Back to the home page</nuxt-link>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,56 +0,0 @@
|
||||
const Nuxt = require('nuxt')
|
||||
const bodyParser = require('body-parser')
|
||||
const session = require('express-session')
|
||||
const app = require('express')()
|
||||
|
||||
const host = process.env.HOST || '127.0.0.1'
|
||||
const port = process.env.PORT || '3000'
|
||||
|
||||
// Body parser, to access req.body
|
||||
app.use(bodyParser.json())
|
||||
|
||||
// Sessions to create req.session
|
||||
app.use(session({
|
||||
secret: 'super-secret-key',
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: { maxAge: 60000 }
|
||||
}))
|
||||
|
||||
// POST /api/login to log in the user and add him to the req.session.authUser
|
||||
app.post('/api/login', function (req, res) {
|
||||
if (req.body.username === 'demo' && req.body.password === 'demo') {
|
||||
req.session.authUser = { username: 'demo' }
|
||||
return res.json({ username: 'demo' })
|
||||
}
|
||||
res.status(401).json({ message: 'Bad credentials' })
|
||||
})
|
||||
|
||||
// POST /api/logout to log out the user and remove it from the req.session
|
||||
app.post('/api/logout', function (req, res) {
|
||||
delete req.session.authUser
|
||||
res.json({ ok: true })
|
||||
})
|
||||
|
||||
// Import and Set Nuxt.js options
|
||||
let config = require('./nuxt.config.js')
|
||||
config.dev = !(process.env.NODE_ENV === 'production')
|
||||
|
||||
// Init Nuxt.js
|
||||
new Nuxt(config)
|
||||
.then((nuxt) => {
|
||||
// nuxt middlware
|
||||
app.use(nuxt.render)
|
||||
// Build only in dev mode
|
||||
if (config.dev) {
|
||||
nuxt.build()
|
||||
.catch((error) => {
|
||||
console.error(error) // eslint-disable-line no-console
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
// Listen the server
|
||||
app.listen(port, host)
|
||||
console.log('Server listening on ' + host + ':' + port) // eslint-disable-line no-console
|
||||
})
|
||||
|
@ -11,31 +11,27 @@ export const mutations = {
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
// nuxtServerInit is called by Nuxt.js before server-rendering every page
|
||||
nuxtServerInit ({ commit }, { req }) {
|
||||
if (req.session && req.session.authUser) {
|
||||
commit('SET_USER', req.session.authUser)
|
||||
}
|
||||
},
|
||||
login ({ commit }, { username, password }) {
|
||||
return axios.post('/api/login', {
|
||||
username,
|
||||
password
|
||||
})
|
||||
.then((res) => {
|
||||
commit('SET_USER', res.data)
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.response.status === 401) {
|
||||
async login ({ commit }, { username, password }) {
|
||||
try {
|
||||
const { data } = await axios.post('/api/login', { username, password })
|
||||
commit('SET_USER', data)
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status === 401) {
|
||||
throw new Error('Bad credentials')
|
||||
}
|
||||
})
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
logout ({ commit }) {
|
||||
return axios.post('/api/logout')
|
||||
.then(() => {
|
||||
async logout ({ commit }) {
|
||||
await axios.post('/api/logout')
|
||||
commit('SET_USER', null)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
module.exports = {
|
||||
build: {
|
||||
ssr: {
|
||||
render: {
|
||||
bundleRenderer: {
|
||||
cache: require('lru-cache')({
|
||||
max: 1000,
|
||||
maxAge: 1000 * 60 * 15
|
||||
|
@ -7,21 +7,13 @@ module.exports = {
|
||||
app: 'app.[chunkhash].js' // default: nuxt.bundle.[chunkhash].js
|
||||
},
|
||||
vendor: ['lodash'],
|
||||
// Loaders config (Webpack 2)
|
||||
loaders: [
|
||||
{
|
||||
test: /\.(png|jpg|gif|svg)$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 100000, // 100KO
|
||||
name: 'img/[name].[ext]?[hash]'
|
||||
}
|
||||
}
|
||||
],
|
||||
extend (config, { dev }) {
|
||||
if (dev) {
|
||||
config.devtool = (dev ? 'eval-source-map' : false)
|
||||
}
|
||||
const urlLoader = config.module.rules.find((loader) => loader.loader === 'url-loader')
|
||||
// Increase limit to 100KO
|
||||
urlLoader.query.limit = 100000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,9 @@
|
||||
</template>
|
||||
|
||||
<style>
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
.dark {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -1,4 +1,4 @@
|
||||
<template lang="html">
|
||||
<template>
|
||||
<div class="loading-page" v-if="loading">
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
|
@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
loading: 'components/loading.vue'
|
||||
loading: '~/components/loading.vue'
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"name": "nuxt-custom-routes",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"axios": "^0.15.2",
|
||||
"axios": "latest",
|
||||
"nuxt": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -13,11 +13,9 @@
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
asyncData () {
|
||||
return axios.get('https://jsonplaceholder.typicode.com/users')
|
||||
.then((res) => {
|
||||
return { users: res.data }
|
||||
})
|
||||
async asyncData () {
|
||||
const { data } = await axios.get('https://jsonplaceholder.typicode.com/users')
|
||||
return { users: data }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -14,12 +14,13 @@ export default {
|
||||
validate ({ params }) {
|
||||
return !isNaN(+params.id)
|
||||
},
|
||||
asyncData ({ params, error }) {
|
||||
return axios.get(`https://jsonplaceholder.typicode.com/users/${+params.id}`)
|
||||
.then((res) => res.data)
|
||||
.catch(() => {
|
||||
async asyncData ({ params, error }) {
|
||||
try {
|
||||
const { data } = await axios.get(`https://jsonplaceholder.typicode.com/users/${+params.id}`)
|
||||
return data
|
||||
} catch (e) {
|
||||
error({ message: 'User not found', statusCode: 404 })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
5
examples/dynamic-components/README.md
Normal file
5
examples/dynamic-components/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Dynamic Components with Nuxt.js
|
||||
|
||||
Demo: https://nuxt-chat.now.sh
|
||||
|
||||
Video: https://www.youtube.com/watch?v=HzDea5-PFaw
|
20
examples/dynamic-components/components/code.vue
Normal file
20
examples/dynamic-components/components/code.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<pre>{{ data }}</pre>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
data: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
pre {
|
||||
background: #222;
|
||||
color: #eee;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
36
examples/dynamic-components/components/image.vue
Normal file
36
examples/dynamic-components/components/image.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<img v-if="loaded" :src="data" alt="image" />
|
||||
<svg v-else width="60px" height="60px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="uil-ring"><rect x="0" y="0" width="100" height="100" fill="none" class="bk"></rect><defs><filter id="uil-ring-shadow" x="-100%" y="-100%" width="300%" height="300%"><feOffset result="offOut" in="SourceGraphic" dx="0" dy="0"></feOffset><feGaussianBlur result="blurOut" in="offOut" stdDeviation="0"></feGaussianBlur><feBlend in="SourceGraphic" in2="blurOut" mode="normal"></feBlend></filter></defs><path d="M10,50c0,0,0,0.5,0.1,1.4c0,0.5,0.1,1,0.2,1.7c0,0.3,0.1,0.7,0.1,1.1c0.1,0.4,0.1,0.8,0.2,1.2c0.2,0.8,0.3,1.8,0.5,2.8 c0.3,1,0.6,2.1,0.9,3.2c0.3,1.1,0.9,2.3,1.4,3.5c0.5,1.2,1.2,2.4,1.8,3.7c0.3,0.6,0.8,1.2,1.2,1.9c0.4,0.6,0.8,1.3,1.3,1.9 c1,1.2,1.9,2.6,3.1,3.7c2.2,2.5,5,4.7,7.9,6.7c3,2,6.5,3.4,10.1,4.6c3.6,1.1,7.5,1.5,11.2,1.6c4-0.1,7.7-0.6,11.3-1.6 c3.6-1.2,7-2.6,10-4.6c3-2,5.8-4.2,7.9-6.7c1.2-1.2,2.1-2.5,3.1-3.7c0.5-0.6,0.9-1.3,1.3-1.9c0.4-0.6,0.8-1.3,1.2-1.9 c0.6-1.3,1.3-2.5,1.8-3.7c0.5-1.2,1-2.4,1.4-3.5c0.3-1.1,0.6-2.2,0.9-3.2c0.2-1,0.4-1.9,0.5-2.8c0.1-0.4,0.1-0.8,0.2-1.2 c0-0.4,0.1-0.7,0.1-1.1c0.1-0.7,0.1-1.2,0.2-1.7C90,50.5,90,50,90,50s0,0.5,0,1.4c0,0.5,0,1,0,1.7c0,0.3,0,0.7,0,1.1 c0,0.4-0.1,0.8-0.1,1.2c-0.1,0.9-0.2,1.8-0.4,2.8c-0.2,1-0.5,2.1-0.7,3.3c-0.3,1.2-0.8,2.4-1.2,3.7c-0.2,0.7-0.5,1.3-0.8,1.9 c-0.3,0.7-0.6,1.3-0.9,2c-0.3,0.7-0.7,1.3-1.1,2c-0.4,0.7-0.7,1.4-1.2,2c-1,1.3-1.9,2.7-3.1,4c-2.2,2.7-5,5-8.1,7.1 c-0.8,0.5-1.6,1-2.4,1.5c-0.8,0.5-1.7,0.9-2.6,1.3L66,87.7l-1.4,0.5c-0.9,0.3-1.8,0.7-2.8,1c-3.8,1.1-7.9,1.7-11.8,1.8L47,90.8 c-1,0-2-0.2-3-0.3l-1.5-0.2l-0.7-0.1L41.1,90c-1-0.3-1.9-0.5-2.9-0.7c-0.9-0.3-1.9-0.7-2.8-1L34,87.7l-1.3-0.6 c-0.9-0.4-1.8-0.8-2.6-1.3c-0.8-0.5-1.6-1-2.4-1.5c-3.1-2.1-5.9-4.5-8.1-7.1c-1.2-1.2-2.1-2.7-3.1-4c-0.5-0.6-0.8-1.4-1.2-2 c-0.4-0.7-0.8-1.3-1.1-2c-0.3-0.7-0.6-1.3-0.9-2c-0.3-0.7-0.6-1.3-0.8-1.9c-0.4-1.3-0.9-2.5-1.2-3.7c-0.3-1.2-0.5-2.3-0.7-3.3 c-0.2-1-0.3-2-0.4-2.8c-0.1-0.4-0.1-0.8-0.1-1.2c0-0.4,0-0.7,0-1.1c0-0.7,0-1.2,0-1.7C10,50.5,10,50,10,50z" fill="#59ebff" filter="url(#uil-ring-shadow)"><animateTransform attributeName="transform" type="rotate" from="0 50 50" to="360 50 50" repeatCount="indefinite" dur="1s"></animateTransform></path></svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
data: String
|
||||
},
|
||||
data: () => ({
|
||||
loaded: false
|
||||
}),
|
||||
beforeMount () {
|
||||
// Preload image
|
||||
const img = new Image()
|
||||
img.onload = () => {
|
||||
this.loaded = true
|
||||
};
|
||||
img.src = this.data
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
img {
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
svg {
|
||||
margin: 20px;
|
||||
margin-left: 50%;
|
||||
position: relative;
|
||||
left: -30px;
|
||||
}
|
||||
</style>
|
17
examples/dynamic-components/components/text.vue
Normal file
17
examples/dynamic-components/components/text.vue
Normal file
@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<p v-html="data"></p>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
data: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
p {
|
||||
padding: 5px 20px;
|
||||
}
|
||||
</style>
|
16
examples/dynamic-components/js/messages.js
Normal file
16
examples/dynamic-components/js/messages.js
Normal file
@ -0,0 +1,16 @@
|
||||
const messages = [
|
||||
{ component: 'vText', data: 'Welcome to the <b>Dynamic Component</b> demo!' },
|
||||
{ component: 'vText', data: 'Look at this nice picture:' },
|
||||
{ component: 'vImage', data: 'https://placeimg.com/350/200/animals' },
|
||||
{ component: 'vText', data: 'If you prefer, look at this code component:' },
|
||||
{ component: 'vCode', data: 'var a = 1;\nvar b = 2;\nb = a;' },
|
||||
{ component: 'vText', data: 'End of demo 🎉' },
|
||||
]
|
||||
|
||||
function streamMessages (fn, i = 0) {
|
||||
if (i >= messages.length) return
|
||||
fn(messages[i])
|
||||
setTimeout(() => streamMessages(fn, i + 1), 2000)
|
||||
}
|
||||
|
||||
export default streamMessages
|
9
examples/dynamic-components/nuxt.config.js
Normal file
9
examples/dynamic-components/nuxt.config.js
Normal file
@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
head: {
|
||||
titleTemplate: 'Nuxt.js - Dynamic Components',
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
|
||||
]
|
||||
}
|
||||
}
|
11
examples/dynamic-components/package.json
Normal file
11
examples/dynamic-components/package.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "dynamic-components-nuxt",
|
||||
"dependencies": {
|
||||
"nuxt": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "nuxt",
|
||||
"build": "nuxt build",
|
||||
"start": "nuxt"
|
||||
}
|
||||
}
|
70
examples/dynamic-components/pages/index.vue
Executable file
70
examples/dynamic-components/pages/index.vue
Executable file
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Nuxt Chat</h1>
|
||||
<transition-group name="list" tag="ul">
|
||||
<li v-for="(message, index) in messages" :key="index">
|
||||
<component :is="message.component" :data="message.data"></component>
|
||||
</li>
|
||||
</transition-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import streamMessages from '@/js/messages.js'
|
||||
// Dynamic components
|
||||
const components = {
|
||||
vText: () => import('@/components/text.vue').then(m => m.default),
|
||||
vImage: () => import('@/components/image.vue').then(m => m.default),
|
||||
vCode: () => import('@/components/code.vue').then(m => m.default)
|
||||
}
|
||||
|
||||
export default {
|
||||
data: () => ({
|
||||
messages: []
|
||||
}),
|
||||
mounted () {
|
||||
// Listen to new messages
|
||||
streamMessages(async (message) => {
|
||||
// Make sure to wait for async chunk to be loaded before adding the message
|
||||
await components[message.component]()
|
||||
// Add the message to the list
|
||||
this.messages.push(message)
|
||||
})
|
||||
},
|
||||
components
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
with: 100%;
|
||||
max-width: 300px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
ul li {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border-radius: 20px;
|
||||
margin-bottom: 5px;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
background: white;
|
||||
border: 1px #ddd solid;
|
||||
overflow: hidden;
|
||||
opacity: 1;
|
||||
}
|
||||
.list-enter-active, .list-leave-active {
|
||||
transition: all 0.4s;
|
||||
}
|
||||
.list-enter, .list-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
</style>
|
@ -1,12 +1,13 @@
|
||||
const { join } = require('path')
|
||||
|
||||
module.exports = {
|
||||
head: {
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||
{ hid: 'description', name: 'description', content: 'Meta description' }
|
||||
]
|
||||
},
|
||||
css: [
|
||||
'hover.css/css/hover-min.css',
|
||||
'bulma/bulma.sass',
|
||||
join(__dirname, 'css/main.css')
|
||||
],
|
||||
build: {
|
||||
extractCSS: true
|
||||
}
|
||||
'bulma/css/bulma.css',
|
||||
'~/css/main.css'
|
||||
]
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
{
|
||||
"name": "nuxt-global-css",
|
||||
"dependencies": {
|
||||
"bulma": "^0.4.0",
|
||||
"hover.css": "^2.2.0",
|
||||
"node-sass": "^4.5.1",
|
||||
"nuxt": "^0.10.0",
|
||||
"sass-loader": "^6.0.3"
|
||||
"bulma": "^0.4.3",
|
||||
"nuxt": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "nuxt",
|
||||
|
@ -8,7 +8,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TwitterHeadCard from '~components/twitter-head-card.vue'
|
||||
import TwitterHeadCard from '~/components/twitter-head-card.vue'
|
||||
|
||||
export default {
|
||||
head: {
|
||||
|
@ -4,6 +4,8 @@
|
||||
"nuxt": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "nuxt",
|
||||
"build": "nuxt build",
|
||||
"start": "nuxt"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
export default function ({ app, store, route, params, error, redirect, hotReload }) {
|
||||
// Check if middleware called from hot-reloading, ignore
|
||||
// If middleware is called from hot-reloading, ignore it
|
||||
if (hotReload) return
|
||||
// Get locale from params
|
||||
const locale = params.lang || 'en'
|
||||
|
@ -6,7 +6,7 @@ module.exports = {
|
||||
router: {
|
||||
middleware: 'i18n'
|
||||
},
|
||||
plugins: ['~plugins/i18n.js',],
|
||||
plugins: ['~/plugins/i18n.js',],
|
||||
generate: {
|
||||
routes: ['/', '/about', '/fr', '/fr/about']
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "nuxt-i18n",
|
||||
"dependencies": {
|
||||
"nuxt": "1.0.0-alpha.3",
|
||||
"vue-i18n": "^7.0.0"
|
||||
"nuxt": "latest",
|
||||
"vue-i18n": "^7.0.5"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "nuxt",
|
||||
|
@ -1,4 +1,4 @@
|
||||
<script>
|
||||
import About from '~pages/_lang/about'
|
||||
import About from '~/pages/_lang/about'
|
||||
export default About
|
||||
</script>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<script>
|
||||
import Index from '~pages/_lang/index'
|
||||
import Index from '~/pages/_lang/index'
|
||||
export default Index
|
||||
</script>
|
||||
|
@ -6,7 +6,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Visits from '~components/Visits'
|
||||
import Visits from '~/components/Visits'
|
||||
|
||||
export default {
|
||||
components: { Visits }
|
||||
|
@ -1,6 +1,6 @@
|
||||
export const state = {
|
||||
export const state = () => ({
|
||||
visits: []
|
||||
}
|
||||
})
|
||||
|
||||
export const mutations = {
|
||||
ADD_VISIT (state, path) {
|
||||
|
@ -22,8 +22,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Post from '~components/post'
|
||||
import vP from '~components/paragraph'
|
||||
import Post from '~/components/post'
|
||||
import vP from '~/components/paragraph'
|
||||
const vHr = { render: (h) => h('hr', { class: 'hr' }) }
|
||||
|
||||
export default {
|
||||
|
@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
offline: true, // true or https://github.com/NekR/offline-plugin/blob/master/docs/options.md
|
||||
plugins: [
|
||||
{ src: '~plugins/offline.js', ssr: false }
|
||||
]
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
<template>
|
||||
<div>This is offline test</div>
|
||||
</template>
|
@ -1,19 +0,0 @@
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
var OfflinePlugin = require('offline-plugin/runtime')
|
||||
window.onNuxtReady(() => {
|
||||
OfflinePlugin.install({
|
||||
onInstalled: function () {
|
||||
console.log('Offline plugin installed.') // eslint-disable-line no-console
|
||||
},
|
||||
onUpdating: function () {
|
||||
|
||||
},
|
||||
onUpdateReady: function () {
|
||||
OfflinePlugin.applyUpdate()
|
||||
},
|
||||
onUpdated: function () {
|
||||
window.location.reload()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
@ -4,6 +4,6 @@ module.exports = {
|
||||
},
|
||||
plugins: [
|
||||
// ssr: false to only include it on client-side
|
||||
{ src: '~plugins/vue-notifications.js', ssr: false }
|
||||
{ src: '~/plugins/vue-notifications.js', ssr: false }
|
||||
]
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "nuxt-plugins-vendor",
|
||||
"dependencies": {
|
||||
"axios": "^0.15.2",
|
||||
"mini-toastr": "^0.3.10",
|
||||
"axios": "^0.16.2",
|
||||
"mini-toastr": "^0.6.5",
|
||||
"nuxt": "latest",
|
||||
"vue-notifications": "^0.7.0"
|
||||
"vue-notifications": "^0.8.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "nuxt",
|
||||
|
@ -10,7 +10,8 @@ import axios from 'axios'
|
||||
|
||||
export default {
|
||||
asyncData () {
|
||||
return axios.get('https://jsonplaceholder.typicode.com/photos/4').then(res => res.data)
|
||||
const nb = Math.max(1, Math.round(Math.random() * 10))
|
||||
return axios.get(`https://jsonplaceholder.typicode.com/photos/${nb}`).then(res => res.data)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -2,6 +2,5 @@ module.exports = {
|
||||
build: {
|
||||
vendor: ['axios']
|
||||
},
|
||||
css: ['assets/main.css'],
|
||||
loading: false
|
||||
css: ['~/assets/main.css']
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="container" :key="page">
|
||||
<div class="container">
|
||||
<nuxt-link v-if="page > 1" :to="'?page=' + (page - 1)">< Prev</nuxt-link>
|
||||
<a v-else class="disabled">< Prev</a>
|
||||
<span>{{ page }}/{{ totalPages }}</span>
|
||||
@ -23,16 +23,14 @@ export default {
|
||||
if (!from) return 'slide-left'
|
||||
return +to.query.page < +from.query.page ? 'slide-right' : 'slide-left'
|
||||
},
|
||||
asyncData ({ query }) {
|
||||
async asyncData ({ query }) {
|
||||
const page = +query.page || 1
|
||||
return axios.get('https://reqres.in/api/users?page=' + page)
|
||||
.then((res) => {
|
||||
const { data } = await axios.get(`https://reqres.in/api/users?page=${page}`)
|
||||
return {
|
||||
page: +res.data.page,
|
||||
totalPages: res.data.total_pages,
|
||||
users: res.data.data
|
||||
page: +data.page,
|
||||
totalPages: data.total_pages,
|
||||
users: data.data
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,4 +1,4 @@
|
||||
module.exports = function (options, next) {
|
||||
module.exports = function (options) {
|
||||
// Extend build
|
||||
this.extendBuild((config) => {
|
||||
// Add TypeScript loader
|
||||
@ -13,6 +13,4 @@ module.exports = function (options, next) {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
next()
|
||||
}
|
||||
|
@ -23,9 +23,7 @@
|
||||
"~middleware/*": ["./middleware/*"],
|
||||
"~pages/*": ["./pages/*"],
|
||||
"~plugins/*": ["./plugins/*"],
|
||||
"~static/*": ["./static/*"],
|
||||
"~store": ["./.nuxt/store"],
|
||||
"~router": ["./.nuxt/router"]
|
||||
"~static/*": ["./static/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +0,0 @@
|
||||
@require './vendor/material-icons.styl'
|
||||
@require './vendor/vuetify.styl'
|
@ -1,21 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Material Icons'), local('MaterialIcons-Regular'), url(https://fonts.gstatic.com/s/materialicons/v22/2fcrYFNaTjcS6g4U3t-Y5UEw0lE80llgEseQY3FEmqw.woff2) format('woff2');
|
||||
}
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
45
examples/with-vuetify/layouts/default.vue
Normal file
45
examples/with-vuetify/layouts/default.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<v-toolbar>
|
||||
<v-toolbar-side-icon @click.native.stop="sidebar = !sidebar" />
|
||||
<v-toolbar-logo>Toolbar</v-toolbar-logo>
|
||||
</v-toolbar>
|
||||
<v-sidebar left fixed drawer v-model="sidebar">
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<v-list-tile router nuxt href="/">
|
||||
<v-list-tile-title>Home</v-list-tile-title>
|
||||
</v-list-tile>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-list-tile router nuxt href="/about">
|
||||
<v-list-tile-title>About</v-list-tile-title>
|
||||
</v-list-tile>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-sidebar>
|
||||
<main>
|
||||
<v-content>
|
||||
<v-container fluid>
|
||||
<nuxt/>
|
||||
</v-container>
|
||||
</v-content>
|
||||
</main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
sidebar: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.title {
|
||||
padding-left: 20px;
|
||||
}
|
||||
</style>
|
@ -2,16 +2,28 @@
|
||||
const { join } = require('path')
|
||||
|
||||
module.exports = {
|
||||
build: {
|
||||
vendor: ['vuetify']
|
||||
},
|
||||
plugins: ['~plugins/vuetify.js'],
|
||||
css: [
|
||||
{ src: join(__dirname, 'css/app.styl'), lang: 'styl' }
|
||||
],
|
||||
/*
|
||||
** Head elements
|
||||
** Add Roboto font and Material Icons
|
||||
*/
|
||||
head: {
|
||||
link: [
|
||||
{ rel: 'preload', as: 'style', href: 'https://fonts.googleapis.com/css?family=Roboto' }
|
||||
{ rel: 'stylesheet', type: 'text/css', href: 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' }
|
||||
]
|
||||
}
|
||||
},
|
||||
/*
|
||||
** Add Vuetify into vendor.bundle.js
|
||||
*/
|
||||
build: {
|
||||
vendor: ['vuetify'],
|
||||
extractCSS: true
|
||||
},
|
||||
/*
|
||||
** Load Vuetify into the app
|
||||
*/
|
||||
plugins: ['~plugins/vuetify'],
|
||||
/*
|
||||
** Load Vuetify CSS globally
|
||||
*/
|
||||
css: ['~assets/app.styl']
|
||||
}
|
||||
|
@ -1,16 +1,14 @@
|
||||
{
|
||||
"name": "with-vuetify",
|
||||
"dependencies": {
|
||||
"nuxt": "^0.10.7",
|
||||
"vuetify": "^0.11.1"
|
||||
"nuxt": "latest",
|
||||
"vuetify": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "nuxt",
|
||||
"build": "nuxt build",
|
||||
"start": "nuxt start",
|
||||
"generate": "nuxt generate",
|
||||
"predeploy": "yarn run generate",
|
||||
"deploy": "surge --domain nuxt-with-vuetify-example.surge.sh dist"
|
||||
"generate": "nuxt generate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"stylus": "^0.54.5",
|
||||
|
18
examples/with-vuetify/pages/about.vue
Normal file
18
examples/with-vuetify/pages/about.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<v-carousel>
|
||||
<v-carousel-item v-for="(src, i) in images" v-bind:src="src" :key="i"></v-carousel-item>
|
||||
</v-carousel>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: () => ({
|
||||
images: [
|
||||
'https://vuetifyjs.com/static/doc-images/carousel/squirrel.jpg',
|
||||
'https://vuetifyjs.com/static/doc-images/carousel/sky.jpg',
|
||||
'https://vuetifyjs.com/static/doc-images/carousel/bird.jpg',
|
||||
'https://vuetifyjs.com/static/doc-images/carousel/planet.jpg'
|
||||
]
|
||||
})
|
||||
}
|
||||
</script>
|
@ -1,45 +1,8 @@
|
||||
<template>
|
||||
<v-app top-toolbar left-fixed-sidebar>
|
||||
<v-toolbar>
|
||||
<v-toolbar-side-icon @click.native.stop="sidebar = !sidebar" />
|
||||
<v-toolbar-logo>Toolbar</v-toolbar-logo>
|
||||
</v-toolbar>
|
||||
<main>
|
||||
<v-sidebar left fixed drawer v-model="sidebar">
|
||||
<v-list>
|
||||
<v-list-item v-for="i in 3" :key="i">
|
||||
<v-list-tile>
|
||||
<v-list-tile-title>Item {{ i }}</v-list-tile-title>
|
||||
</v-list-tile>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-sidebar>
|
||||
<v-content>
|
||||
<v-container fluid>
|
||||
<div class="title">
|
||||
<h2>Main content</h2>
|
||||
<v-btn primary>Primary button</v-btn>
|
||||
<v-btn secondary>Secondary button</v-btn>
|
||||
<v-btn success>Success button</v-btn>
|
||||
</div>
|
||||
</v-container>
|
||||
</v-content>
|
||||
</main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
asyncData() {
|
||||
return {
|
||||
sidebar: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.title {
|
||||
padding-left: 20px;
|
||||
}
|
||||
</style>
|
||||
|
11
index.js
11
index.js
@ -1,11 +1,16 @@
|
||||
/*!
|
||||
* Nuxt.js
|
||||
* (c) 2016-2017 Chopin Brothers
|
||||
* Core maintainer: Pooya (@pi0)
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
// Node Source Map Support
|
||||
// https://github.com/evanw/node-source-map-support
|
||||
require('source-map-support').install()
|
||||
|
||||
// Fix babel flag
|
||||
/* istanbul ignore else */
|
||||
process.noDeprecation = true
|
||||
|
||||
var Nuxt = require('./dist/nuxt.js')
|
||||
|
||||
module.exports = Nuxt.default ? Nuxt.default : Nuxt
|
||||
module.exports = require('./dist/nuxt')
|
||||
|
@ -15,7 +15,7 @@ let layouts = {
|
||||
<%
|
||||
var layoutsKeys = Object.keys(layouts);
|
||||
layoutsKeys.forEach(function (key, i) { %>
|
||||
"_<%= key %>": () => import('<%= layouts[key] %>' /* webpackChunkName: "layouts/<%= key %>" */)<%= (i + 1) < layoutsKeys.length ? ',' : '' %>
|
||||
"_<%= key %>": () => import('<%= layouts[key] %>' /* webpackChunkName: "layouts/<%= key %>" */).then(m => m.default || m)<%= (i + 1) < layoutsKeys.length ? ',' : '' %>
|
||||
<% }) %>
|
||||
}
|
||||
|
||||
|
@ -1,185 +1,315 @@
|
||||
'use strict'
|
||||
|
||||
import Vue from 'vue'
|
||||
import middleware from './middleware'
|
||||
import { createApp, NuxtError } from './index'
|
||||
import { applyAsyncData, sanitizeComponent, getMatchedComponents, getMatchedComponentsInstances, flatMapComponents, getContext, middlewareSeries, promisify, getLocation, compile } from './utils'
|
||||
import {
|
||||
applyAsyncData,
|
||||
sanitizeComponent,
|
||||
getMatchedComponents,
|
||||
getMatchedComponentsInstances,
|
||||
flatMapComponents,
|
||||
getContext,
|
||||
middlewareSeries,
|
||||
promisify,
|
||||
getLocation,
|
||||
compile
|
||||
} from './utils'
|
||||
|
||||
const noopData = () => { return {} }
|
||||
const noopFetch = () => {}
|
||||
|
||||
// Global shared references
|
||||
let _lastPaths = []
|
||||
let _lastComponentsFiles = []
|
||||
|
||||
let app
|
||||
let router
|
||||
<%= (store ? 'let store' : '') %>
|
||||
<% if (store) { %>let store<% } %>
|
||||
|
||||
// Try to rehydrate SSR data from window
|
||||
const NUXT = window.__NUXT__ || {}
|
||||
NUXT.components = window.__COMPONENTS__ || null
|
||||
|
||||
// Create and mount App
|
||||
createApp()
|
||||
.then(mountApp)
|
||||
.catch(err => {
|
||||
console.error('[nuxt] Error while initializing app', err)
|
||||
})
|
||||
|
||||
function componentOption(component, key, ...args) {
|
||||
if (!component || !component.options || !component.options[key]) {
|
||||
return {}
|
||||
}
|
||||
const option = component.options[key]
|
||||
if (typeof option === 'function') {
|
||||
return option(...args)
|
||||
}
|
||||
return option
|
||||
}
|
||||
|
||||
function mapTransitions(Components, to, from) {
|
||||
return Components.map((Component) => {
|
||||
let transition = Component.options.transition
|
||||
if (typeof transition === 'function') {
|
||||
return transition(to, from)
|
||||
const componentTransitions = component => {
|
||||
const transition = componentOption(component, 'transition', to, from)
|
||||
return (typeof transition === 'string' ? { name: transition } : transition)
|
||||
}
|
||||
return transition
|
||||
|
||||
return Components.map(Component => {
|
||||
// Clone original object to prevent overrides
|
||||
const transitions = Object.assign({}, componentTransitions(Component))
|
||||
|
||||
// Combine transitions & prefer `leave` transitions of 'from' route
|
||||
if (from && from.matched.length && from.matched[0].components.default) {
|
||||
const from_transitions = componentTransitions(from.matched[0].components.default)
|
||||
Object.keys(from_transitions)
|
||||
.filter(key => from_transitions[key] && key.toLowerCase().indexOf('leave') !== -1)
|
||||
.forEach(key => { transitions[key] = from_transitions[key] })
|
||||
}
|
||||
|
||||
return transitions
|
||||
})
|
||||
}
|
||||
|
||||
function loadAsyncComponents (to, from, next) {
|
||||
const resolveComponents = flatMapComponents(to, (Component, _, match, key) => {
|
||||
if (typeof Component === 'function' && !Component.options) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const _resolve = (Component) => {
|
||||
Component = sanitizeComponent(Component)
|
||||
match.components[key] = Component
|
||||
resolve(Component)
|
||||
}
|
||||
Component().then(_resolve).catch(reject)
|
||||
})
|
||||
}
|
||||
Component = sanitizeComponent(Component)
|
||||
match.components[key] = Component
|
||||
return match.components[key]
|
||||
})
|
||||
async function loadAsyncComponents (to, from, next) {
|
||||
// Check if route hash changed
|
||||
const fromPath = from.fullPath.split('#')[0]
|
||||
const toPath = to.fullPath.split('#')[0]
|
||||
this._hashChanged = (fromPath === toPath)
|
||||
if (!this._hashChanged) {
|
||||
<%= (loading ? 'this.$loading.start && this.$loading.start()' : '') %>
|
||||
this._hashChanged = fromPath === toPath
|
||||
|
||||
<% if (loading) { %>
|
||||
if (!this._hashChanged && this.$loading.start) {
|
||||
this.$loading.start()
|
||||
}
|
||||
Promise.all(resolveComponents)
|
||||
.then(() => next())
|
||||
.catch((err) => {
|
||||
let statusCode = err.statusCode || err.status || (err.response && err.response.status) || 500
|
||||
<% } %>
|
||||
|
||||
try {
|
||||
await Promise.all(flatMapComponents(to, (Component, _, match, key) => {
|
||||
// If component already resolved
|
||||
if (typeof Component !== 'function' || Component.options) {
|
||||
const _Component = sanitizeComponent(Component)
|
||||
match.components[key] = _Component
|
||||
return _Component
|
||||
}
|
||||
|
||||
// Resolve component
|
||||
return Component().then(Component => {
|
||||
const _Component = sanitizeComponent(Component)
|
||||
match.components[key] = _Component
|
||||
return _Component
|
||||
})
|
||||
}))
|
||||
|
||||
next()
|
||||
} catch (err) {
|
||||
if (!err) err = {}
|
||||
const statusCode = err.statusCode || err.status || (err.response && err.response.status) || 500
|
||||
this.error({ statusCode, message: err.message })
|
||||
next(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Get matched components
|
||||
function resolveComponents(router) {
|
||||
const path = getLocation(router.options.base)
|
||||
|
||||
return flatMapComponents(router.match(path), (Component, _, match, key, index) => {
|
||||
// If component already resolved
|
||||
if (typeof Component !== 'function' || Component.options) {
|
||||
const _Component = sanitizeComponent(Component)
|
||||
match.components[key] = _Component
|
||||
return _Component
|
||||
}
|
||||
|
||||
// Resolve component
|
||||
return Component().then(Component => {
|
||||
const _Component = sanitizeComponent(Component)
|
||||
if (NUXT.serverRendered) {
|
||||
applyAsyncData(_Component, NUXT.data[index])
|
||||
if (NUXT.components) {
|
||||
Component.options.components = Object.assign(_Component.options.components, NUXT.components[index])
|
||||
}
|
||||
_Component._Ctor = _Component
|
||||
}
|
||||
match.components[key] = _Component
|
||||
return _Component
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function callMiddleware (Components, context, layout) {
|
||||
// if layout is undefined, only call global middleware
|
||||
let midd = <%= serialize(router.middleware, { isJSON: true }) %>
|
||||
let unknownMiddleware = false
|
||||
|
||||
// If layout is undefined, only call global middleware
|
||||
if (typeof layout !== 'undefined') {
|
||||
midd = [] // exclude global middleware if layout defined (already called before)
|
||||
midd = [] // Exclude global middleware if layout defined (already called before)
|
||||
if (layout.middleware) {
|
||||
midd = midd.concat(layout.middleware)
|
||||
}
|
||||
Components.forEach((Component) => {
|
||||
Components.forEach(Component => {
|
||||
if (Component.options.middleware) {
|
||||
midd = midd.concat(Component.options.middleware)
|
||||
}
|
||||
})
|
||||
}
|
||||
midd = midd.map((name) => {
|
||||
|
||||
midd = midd.map(name => {
|
||||
if (typeof middleware[name] !== 'function') {
|
||||
unknownMiddleware = true
|
||||
this.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
||||
}
|
||||
return middleware[name]
|
||||
})
|
||||
|
||||
if (unknownMiddleware) return
|
||||
return middlewareSeries(midd, context)
|
||||
}
|
||||
|
||||
async function render (to, from, next) {
|
||||
if (this._hashChanged) return next()
|
||||
let layout
|
||||
|
||||
// nextCalled is true when redirected
|
||||
let nextCalled = false
|
||||
const _next = function (path) {
|
||||
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
||||
const _next = path => {
|
||||
<% if(loading) { %>if(this.$loading.finish) this.$loading.finish()<% } %>
|
||||
if (nextCalled) return
|
||||
nextCalled = true
|
||||
next(path)
|
||||
}
|
||||
let context = getContext({ to<%= (store ? ', store' : '') %>, isClient: true, next: _next.bind(this), error: this.error.bind(this) }, app)
|
||||
let Components = getMatchedComponents(to)
|
||||
|
||||
// Update context
|
||||
const context = getContext({
|
||||
to,
|
||||
<% if (store) { %>store,<% } %>
|
||||
isClient: true,
|
||||
next: _next.bind(this),
|
||||
error: this.error.bind(this),
|
||||
app
|
||||
})
|
||||
this._context = context
|
||||
this._dateLastError = this.$options._nuxt.dateErr
|
||||
this._hadError = !!this.$options._nuxt.err
|
||||
|
||||
// Get route's matched components
|
||||
const Components = getMatchedComponents(to)
|
||||
|
||||
// If no Components matched, generate 404
|
||||
if (!Components.length) {
|
||||
// Default layout
|
||||
await callMiddleware.call(this, Components, context)
|
||||
if (context._redirected) return
|
||||
|
||||
// Load layout for error page
|
||||
layout = await this.loadLayout(typeof NuxtError.layout === 'function' ? NuxtError.layout(context) : NuxtError.layout)
|
||||
await callMiddleware.call(this, Components, context, layout)
|
||||
if (context._redirected) return
|
||||
|
||||
this.error({ statusCode: 404, message: 'This page could not be found.' })
|
||||
return next()
|
||||
}
|
||||
|
||||
// Update ._data and other properties if hot reloaded
|
||||
Components.forEach(function (Component) {
|
||||
Components.forEach(Component => {
|
||||
if (Component._Ctor && Component._Ctor.options) {
|
||||
Component.options.asyncData = Component._Ctor.options.asyncData
|
||||
Component.options.fetch = Component._Ctor.options.fetch
|
||||
}
|
||||
})
|
||||
|
||||
// Apply transitions
|
||||
this.setTransitions(mapTransitions(Components, to, from))
|
||||
|
||||
try {
|
||||
// Set layout
|
||||
// Call middleware
|
||||
await callMiddleware.call(this, Components, context)
|
||||
if (context._redirected) return
|
||||
layout = Components[0].options.layout
|
||||
|
||||
// Set layout
|
||||
let layout = Components[0].options.layout
|
||||
if (typeof layout === 'function') {
|
||||
layout = layout(context)
|
||||
}
|
||||
layout = await this.loadLayout(layout)
|
||||
|
||||
// Call middleware for layout
|
||||
await callMiddleware.call(this, Components, context, layout)
|
||||
if (context._redirected) return
|
||||
// Pass validation?
|
||||
|
||||
// Call .validate()
|
||||
let isValid = true
|
||||
Components.forEach((Component) => {
|
||||
Components.forEach(Component => {
|
||||
if (!isValid) return
|
||||
if (typeof Component.options.validate !== 'function') return
|
||||
isValid = Component.options.validate({
|
||||
params: to.params || {},
|
||||
query : to.query || {}<%= (store ? ', store: context.store' : '') %>
|
||||
query : to.query || {},
|
||||
<% if(store) { %>store: context.store <% } %>
|
||||
})
|
||||
})
|
||||
// ...If .validate() returned false
|
||||
if (!isValid) {
|
||||
this.error({ statusCode: 404, message: 'This page could not be found.' })
|
||||
return next()
|
||||
}
|
||||
|
||||
// Call asyncData & fetch hooks on components matched by the route.
|
||||
await Promise.all(Components.map((Component, i) => {
|
||||
// Check if only children route changed
|
||||
Component._path = compile(to.matched[i].path)(to.params)
|
||||
if (!this._hadError && Component._path === _lastPaths[i] && (i + 1) !== Components.length) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
let promises = []
|
||||
// asyncData method
|
||||
if (Component.options.asyncData && typeof Component.options.asyncData === 'function') {
|
||||
var promise = promisify(Component.options.asyncData, context)
|
||||
promise.then((asyncDataResult) => {
|
||||
|
||||
const hasAsyncData = Component.options.asyncData && typeof Component.options.asyncData === 'function'
|
||||
const hasFetch = !!Component.options.fetch
|
||||
<% if(loading) { %>const loadingIncrease = (hasAsyncData && hasFetch) ? 30 : 45<% } %>
|
||||
|
||||
// Call asyncData(context)
|
||||
if (hasAsyncData) {
|
||||
const promise = promisify(Component.options.asyncData, context)
|
||||
.then(asyncDataResult => {
|
||||
applyAsyncData(Component, asyncDataResult)
|
||||
<%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %>
|
||||
<% if(loading) { %>if(this.$loading.increase) this.$loading.increase(loadingIncrease)<% } %>
|
||||
})
|
||||
promises.push(promise)
|
||||
}
|
||||
if (Component.options.fetch) {
|
||||
var p = Component.options.fetch(context)
|
||||
if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) { p = Promise.resolve(p) }
|
||||
<%= (loading ? 'p.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %>
|
||||
|
||||
// Call fetch(context)
|
||||
if (hasFetch) {
|
||||
let p = Component.options.fetch(context)
|
||||
if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) {
|
||||
p = Promise.resolve(p)
|
||||
}
|
||||
p.then(fetchResult => {
|
||||
<% if(loading) { %>if(this.$loading.increase) this.$loading.increase(loadingIncrease)<% } %>
|
||||
})
|
||||
promises.push(p)
|
||||
}
|
||||
|
||||
return Promise.all(promises)
|
||||
}))
|
||||
|
||||
_lastPaths = Components.map((Component, i) => compile(to.matched[i].path)(to.params))
|
||||
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
||||
|
||||
<% if(loading) { %>if(this.$loading.finish) this.$loading.finish()<% } %>
|
||||
|
||||
// If not redirected
|
||||
if (!nextCalled) {
|
||||
next()
|
||||
}
|
||||
if (!nextCalled) next()
|
||||
|
||||
} catch (error) {
|
||||
if (!error) error = {}
|
||||
_lastPaths = []
|
||||
error.statusCode = error.statusCode || error.status || (error.response && error.response.status) || 500
|
||||
|
||||
// Load error layout
|
||||
let layout = NuxtError.layout
|
||||
if (typeof layout === 'function') {
|
||||
layout = layout(context)
|
||||
}
|
||||
this.loadLayout(layout)
|
||||
.then(() => {
|
||||
await this.loadLayout(layout)
|
||||
|
||||
this.error(error)
|
||||
next(false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,46 +327,74 @@ function normalizeComponents (to, ___) {
|
||||
}
|
||||
|
||||
// When navigating on a different route but the same component is used, Vue.js
|
||||
// will not update the instance data, so we have to update $data ourselves
|
||||
// Will not update the instance data, so we have to update $data ourselves
|
||||
function fixPrepatch (to, ___) {
|
||||
if (this._hashChanged) return
|
||||
|
||||
Vue.nextTick(() => {
|
||||
let instances = getMatchedComponentsInstances(to)
|
||||
const instances = getMatchedComponentsInstances(to)
|
||||
|
||||
_lastComponentsFiles = instances.map((instance, i) => {
|
||||
if (!instance) return '';
|
||||
|
||||
if (_lastPaths[i] === instance.constructor._path && typeof instance.constructor.options.data === 'function') {
|
||||
let newData = instance.constructor.options.data.call(instance)
|
||||
const newData = instance.constructor.options.data.call(instance)
|
||||
for (let key in newData) {
|
||||
Vue.set(instance.$data, key, newData[key])
|
||||
}
|
||||
}
|
||||
|
||||
return instance.constructor.options.__file
|
||||
})
|
||||
// hide error component if no error
|
||||
|
||||
// Hide error component if no error
|
||||
if (this._hadError && this._dateLastError === this.$options._nuxt.dateErr) {
|
||||
this.error()
|
||||
}
|
||||
|
||||
// Set layout
|
||||
let layout = this.$options._nuxt.err ? NuxtError.layout : to.matched[0].components.default.options.layout
|
||||
if (typeof layout === 'function') {
|
||||
layout = layout(this._context)
|
||||
}
|
||||
this.setLayout(layout)
|
||||
// hot reloading
|
||||
|
||||
<% if (isDev) { %>
|
||||
// Hot reloading
|
||||
setTimeout(() => hotReloadAPI(this), 100)
|
||||
<% } %>
|
||||
})
|
||||
}
|
||||
|
||||
function nuxtReady (app) {
|
||||
window._nuxtReadyCbs.forEach((cb) => {
|
||||
if (typeof cb === 'function') {
|
||||
cb(app)
|
||||
}
|
||||
})
|
||||
// Special JSDOM
|
||||
if (typeof window._onNuxtLoaded === 'function') {
|
||||
window._onNuxtLoaded(app)
|
||||
}
|
||||
// Add router hooks
|
||||
router.afterEach(function (to, from) {
|
||||
app.$nuxt.$emit('routeChanged', to, from)
|
||||
})
|
||||
}
|
||||
|
||||
<% if (isDev) { %>
|
||||
// Special hot reload with asyncData(context)
|
||||
function hotReloadAPI (_app) {
|
||||
if (!module.hot) return
|
||||
|
||||
let $components = []
|
||||
let $nuxt = _app.$nuxt
|
||||
|
||||
while ($nuxt && $nuxt.$children && $nuxt.$children.length) {
|
||||
$nuxt.$children.forEach(function (child, i) {
|
||||
$nuxt.$children.forEach((child, i) => {
|
||||
if (child.$vnode.data.nuxtChild) {
|
||||
let hasAlready = false
|
||||
$components.forEach(function (component) {
|
||||
$components.forEach(component => {
|
||||
if (component.$options.__file === child.$options.__file) {
|
||||
hasAlready = true
|
||||
}
|
||||
@ -248,13 +406,16 @@ function hotReloadAPI (_app) {
|
||||
$nuxt = child
|
||||
})
|
||||
}
|
||||
|
||||
$components.forEach(addHotReload.bind(_app))
|
||||
}
|
||||
|
||||
function addHotReload ($component, depth) {
|
||||
if ($component.$vnode.data._hasHotReload) return
|
||||
$component.$vnode.data._hasHotReload = true
|
||||
|
||||
var _forceUpdate = $component.$forceUpdate.bind($component.$parent)
|
||||
|
||||
$component.$vnode.context.$forceUpdate = () => {
|
||||
let Components = getMatchedComponents(router.currentRoute)
|
||||
let Component = Components[depth]
|
||||
@ -314,104 +475,80 @@ function addHotReload ($component, depth) {
|
||||
})
|
||||
}
|
||||
}
|
||||
<% } %>
|
||||
|
||||
// Load vue app
|
||||
const NUXT = window.__NUXT__ || {}
|
||||
if (!NUXT) {
|
||||
throw new Error('[nuxt.js] cannot find the global variable __NUXT__, make sure the server is working.')
|
||||
}
|
||||
// Get matched components
|
||||
const resolveComponents = function (router) {
|
||||
const path = getLocation(router.options.base)
|
||||
return flatMapComponents(router.match(path), (Component, _, match, key, index) => {
|
||||
if (typeof Component === 'function' && !Component.options) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const _resolve = (Component) => {
|
||||
Component = sanitizeComponent(Component)
|
||||
if (NUXT.serverRendered) {
|
||||
applyAsyncData(Component, NUXT.data[index])
|
||||
}
|
||||
match.components[key] = Component
|
||||
resolve(Component)
|
||||
}
|
||||
Component().then(_resolve).catch(reject)
|
||||
})
|
||||
}
|
||||
Component = sanitizeComponent(Component)
|
||||
match.components[key] = Component
|
||||
return Component
|
||||
})
|
||||
}
|
||||
|
||||
function nuxtReady (app) {
|
||||
window._nuxtReadyCbs.forEach((cb) => {
|
||||
if (typeof cb === 'function') {
|
||||
cb(app)
|
||||
}
|
||||
})
|
||||
// Special JSDOM
|
||||
if (typeof window._onNuxtLoaded === 'function') {
|
||||
window._onNuxtLoaded(app)
|
||||
}
|
||||
// Add router hooks
|
||||
router.afterEach(function (to, from) {
|
||||
app.$nuxt.$emit('routeChanged', to, from)
|
||||
})
|
||||
}
|
||||
|
||||
createApp()
|
||||
.then(async (__app) => {
|
||||
async function mountApp(__app) {
|
||||
// Set global variables
|
||||
app = __app.app
|
||||
router = __app.router
|
||||
<%= (store ? 'store = __app.store' : '') %>
|
||||
<% if (store) { %>store = __app.store <% } %>
|
||||
|
||||
// Resolve route components
|
||||
const Components = await Promise.all(resolveComponents(router))
|
||||
|
||||
// Create Vue instance
|
||||
const _app = new Vue(app)
|
||||
|
||||
// Load layout
|
||||
const layout = NUXT.layout || 'default'
|
||||
await _app.loadLayout(layout)
|
||||
_app.setLayout(layout)
|
||||
|
||||
// Mounts Vue app to DOM element
|
||||
const mountApp = () => {
|
||||
_app.$mount('#__nuxt')
|
||||
|
||||
// Listen for first Vue update
|
||||
Vue.nextTick(() => {
|
||||
// Hot reloading
|
||||
hotReloadAPI(_app)
|
||||
// Call window.onNuxtReady callbacks
|
||||
nuxtReady(_app)
|
||||
<% if (isDev) { %>
|
||||
// Enable hot reloading
|
||||
hotReloadAPI(_app)
|
||||
<% } %>
|
||||
})
|
||||
}
|
||||
|
||||
// Enable transitions
|
||||
_app.setTransitions = _app.$options._nuxt.setTransitions.bind(_app)
|
||||
if (Components.length) {
|
||||
_app.setTransitions(mapTransitions(Components, router.currentRoute))
|
||||
_lastPaths = router.currentRoute.matched.map((route) => compile(route.path)(router.currentRoute.params))
|
||||
_lastComponentsFiles = Components.map((Component) => Component.options.__file)
|
||||
_lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params))
|
||||
_lastComponentsFiles = Components.map(Component => Component.options.__file)
|
||||
}
|
||||
|
||||
// Initialize error handler
|
||||
_app.error = _app.$options._nuxt.error.bind(_app)
|
||||
_app.$loading = {} // to avoid error while _app.$nuxt does not exist
|
||||
_app.$loading = {} // To avoid error while _app.$nuxt does not exist
|
||||
if (NUXT.error) _app.error(NUXT.error)
|
||||
|
||||
// Add router hooks
|
||||
router.beforeEach(loadAsyncComponents.bind(_app))
|
||||
router.beforeEach(render.bind(_app))
|
||||
router.afterEach(normalizeComponents)
|
||||
router.afterEach(fixPrepatch.bind(_app))
|
||||
|
||||
// If page already is server rendered
|
||||
if (NUXT.serverRendered) {
|
||||
mountApp()
|
||||
return
|
||||
}
|
||||
render.call(_app, router.currentRoute, router.currentRoute, function (path) {
|
||||
if (path) {
|
||||
|
||||
render.call(_app, router.currentRoute, router.currentRoute, path => {
|
||||
if (!path) {
|
||||
normalizeComponents(router.currentRoute, router.currentRoute)
|
||||
fixPrepatch.call(_app, router.currentRoute, router.currentRoute)
|
||||
mountApp()
|
||||
return
|
||||
}
|
||||
|
||||
// Push the path and then mount app
|
||||
let mounted = false
|
||||
router.afterEach(function () {
|
||||
router.afterEach(() => {
|
||||
if (mounted) return
|
||||
mounted = true
|
||||
mountApp()
|
||||
})
|
||||
router.push(path)
|
||||
return
|
||||
})
|
||||
}
|
||||
normalizeComponents(router.currentRoute, router.currentRoute)
|
||||
fixPrepatch.call(_app, router.currentRoute, router.currentRoute)
|
||||
mountApp()
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('[nuxt.js] Cannot load components', err) // eslint-disable-line no-console
|
||||
})
|
||||
|
@ -30,7 +30,7 @@ export default {
|
||||
functional: true,
|
||||
render (h, { parent, data }) {
|
||||
data.nuxtChild = true
|
||||
|
||||
const _parent = parent
|
||||
const transitions = parent.$nuxt.nuxt.transitions
|
||||
const defaultTransition = parent.$nuxt.nuxt.defaultTransition
|
||||
let depth = 0
|
||||
@ -51,7 +51,7 @@ export default {
|
||||
let listeners = {}
|
||||
listenersKeys.forEach((key) => {
|
||||
if (typeof transition[key] === 'function') {
|
||||
listeners[key] = transition[key]
|
||||
listeners[key] = transition[key].bind(_parent)
|
||||
}
|
||||
})
|
||||
return h('transition', {
|
||||
|
@ -1,15 +1,16 @@
|
||||
<template>
|
||||
<nuxt-error v-if="nuxt.err" :error="nuxt.err"></nuxt-error>
|
||||
<nuxt-child v-else></nuxt-child>
|
||||
<nuxt-child :key="routerViewKey" v-else></nuxt-child>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import NuxtChild from './nuxt-child'
|
||||
import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./nuxt-error.vue" %>'
|
||||
import NuxtError from '<%= components.ErrorPage ? ((components.ErrorPage.includes('~') || components.ErrorPage.includes('@')) ? components.ErrorPage : "../" + components.ErrorPage) : "./nuxt-error.vue" %>'
|
||||
|
||||
export default {
|
||||
name: 'nuxt',
|
||||
props: ['nuxtChildKey'],
|
||||
beforeCreate () {
|
||||
Vue.util.defineReactive(this, 'nuxt', this.$root.$options._nuxt)
|
||||
},
|
||||
@ -45,6 +46,15 @@ export default {
|
||||
}
|
||||
},
|
||||
<% } %>
|
||||
computed: {
|
||||
routerViewKey () {
|
||||
// If nuxtChildKey prop is given or current route has children
|
||||
if (typeof this.nuxtChildKey !== 'undefined' || this.$route.matched.length > 1) {
|
||||
return this.nuxtChildKey || ''
|
||||
}
|
||||
return this.$route.fullPath.split('#')[0]
|
||||
}
|
||||
},
|
||||
components: {
|
||||
NuxtChild,
|
||||
NuxtError
|
||||
|
1
lib/app/empty.js
Normal file
1
lib/app/empty.js
Normal file
@ -0,0 +1 @@
|
||||
// This file is intentially left empty for noop aliases
|
@ -1,37 +1,24 @@
|
||||
'use strict'
|
||||
|
||||
import 'es6-promise/auto'
|
||||
import Vue from 'vue'
|
||||
import Meta from 'vue-meta'
|
||||
import { createRouter } from './router.js'
|
||||
<% if (store) { %>import { createStore } from './store.js'<% } %>
|
||||
import NuxtChild from './components/nuxt-child.js'
|
||||
import NuxtLink from './components/nuxt-link.js'
|
||||
import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./components/nuxt-error.vue" %>'
|
||||
import Nuxt from './components/nuxt.vue'
|
||||
import App from '<%= appPath %>'
|
||||
|
||||
import { getContext } from './utils'
|
||||
|
||||
if (process.browser) {
|
||||
// window.onNuxtReady(() => console.log('Ready')) hook
|
||||
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
|
||||
window._nuxtReadyCbs = []
|
||||
window.onNuxtReady = function (cb) {
|
||||
window._nuxtReadyCbs.push(cb)
|
||||
}
|
||||
}
|
||||
|
||||
// Import SSR plugins
|
||||
<% plugins.forEach(function (plugin) { if (plugin.ssr)
|
||||
{ %>let <%= plugin.name %> = require('<%= r(plugin.src) %>')
|
||||
<%= plugin.name %> = <%= plugin.name %>.default || <%= plugin.name %>
|
||||
<% }}) %>
|
||||
<% if (store) { %>import { createStore } from './store.js'<% } %>
|
||||
<% plugins.forEach(plugin => { %>import <%= plugin.name %> from '<%= plugin.name %>'
|
||||
<% }) %>
|
||||
|
||||
// Component: <nuxt-child>
|
||||
Vue.component(NuxtChild.name, NuxtChild)
|
||||
|
||||
// Component: <nuxt-link>
|
||||
Vue.component(NuxtLink.name, NuxtLink)
|
||||
// Component: <nuxt>
|
||||
|
||||
// Component: <nuxt>`
|
||||
Vue.component(Nuxt.name, Nuxt)
|
||||
|
||||
// vue-meta configuration
|
||||
@ -50,34 +37,33 @@ const defaultTransition = <%=
|
||||
%>
|
||||
|
||||
async function createApp (ssrContext) {
|
||||
<% if (store) { %>
|
||||
const store = createStore()
|
||||
<% } %>
|
||||
const router = createRouter()
|
||||
|
||||
<% if (store) { %>const store = createStore()<% } %>
|
||||
|
||||
if (process.server && ssrContext && ssrContext.url) {
|
||||
await new Promise((resolve, reject) => {
|
||||
router.push(ssrContext.url, resolve, reject)
|
||||
})
|
||||
}
|
||||
|
||||
if (process.browser) {
|
||||
<% if (store) { %>
|
||||
if (process.browser) {
|
||||
// Replace store state before calling plugins
|
||||
if (window.__NUXT__ && window.__NUXT__.state) {
|
||||
store.replaceState(window.__NUXT__.state)
|
||||
}
|
||||
<% } %>
|
||||
}
|
||||
<% } %>
|
||||
|
||||
// root instance
|
||||
// Create Root instance
|
||||
// here we inject the router and store to all child components,
|
||||
// making them available everywhere as `this.$router` and `this.$store`.
|
||||
let app = {
|
||||
const app = {
|
||||
router,
|
||||
<%= (store ? 'store,' : '') %>
|
||||
<% if(store) { %> store,<% } %>
|
||||
_nuxt: {
|
||||
defaultTransition: defaultTransition,
|
||||
defaultTransition,
|
||||
transitions: [ defaultTransition ],
|
||||
setTransitions (transitions) {
|
||||
if (!Array.isArray(transitions)) {
|
||||
@ -103,43 +89,44 @@ async function createApp (ssrContext) {
|
||||
if (typeof err === 'string') {
|
||||
err = { statusCode: 500, message: err }
|
||||
}
|
||||
this.$options._nuxt.dateErr = Date.now()
|
||||
this.$options._nuxt.err = err;
|
||||
const _nuxt = this._nuxt || this.$options._nuxt
|
||||
_nuxt.dateErr = Date.now()
|
||||
_nuxt.err = err
|
||||
if (process.env.NODE_ENV !== 'production' && typeof console !== 'undefined') {
|
||||
console.error(err)
|
||||
console.error('[nuxt] ' + err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
},
|
||||
...App
|
||||
}
|
||||
|
||||
const next = ssrContext ? ssrContext.next : location => app.router.push(location)
|
||||
|
||||
const ctx = getContext({
|
||||
isServer: !!ssrContext,
|
||||
isClient: !ssrContext,
|
||||
route: router.currentRoute,
|
||||
<%= (store ? 'store,' : '') %>
|
||||
next,
|
||||
error: app._nuxt.error.bind(app),
|
||||
<% if(store) { %>store,<% } %>
|
||||
req: ssrContext ? ssrContext.req : undefined,
|
||||
res: ssrContext ? ssrContext.res : undefined,
|
||||
}, app)
|
||||
delete ctx.redirect
|
||||
delete ctx.error
|
||||
|
||||
// Inject external plugins
|
||||
<% plugins.forEach(function (plugin) {
|
||||
if (plugin.ssr) { %>
|
||||
if (typeof <%= plugin.name %> === 'function') {
|
||||
await <%= plugin.name %>(ctx)
|
||||
}
|
||||
<% } else { %>
|
||||
if (process.browser) {
|
||||
let <%= plugin.name %> = require('<%= plugin.src %>')
|
||||
<%= plugin.name %> = <%= plugin.name %>.default || <%= plugin.name %>
|
||||
if (typeof <%= plugin.name %> === 'function') {
|
||||
await <%= plugin.name %>(ctx)
|
||||
}
|
||||
}
|
||||
<% }
|
||||
}) %>
|
||||
<% plugins.filter(p => p.ssr).forEach(plugin => { %>
|
||||
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(ctx)<% }) %>
|
||||
<% if (plugins.filter(p => !p.ssr).length) { %>
|
||||
if (process.browser) { <% plugins.filter(p => !p.ssr).forEach(plugin => { %>
|
||||
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(ctx)<% }) %>
|
||||
}<% } %>
|
||||
|
||||
return { app, router<%= (store ? ', store' : '') %> }
|
||||
return {
|
||||
app,
|
||||
router,
|
||||
<% if(store) { %> store <% } %>
|
||||
}
|
||||
}
|
||||
|
||||
export { createApp, NuxtError }
|
@ -1,5 +1,5 @@
|
||||
<% if (middleware) { %>
|
||||
let files = require.context('~/middleware', false, /^\.\/.*\.(js|ts)$/)
|
||||
let files = require.context('@/middleware', false, /^\.\/.*\.(js|ts)$/)
|
||||
let filenames = files.keys()
|
||||
|
||||
function getModule (filename) {
|
||||
|
@ -1,5 +1,3 @@
|
||||
'use strict'
|
||||
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
|
||||
@ -7,7 +5,7 @@ Vue.use(Router)
|
||||
|
||||
<%
|
||||
function recursiveRoutes(routes, tab, components) {
|
||||
var res = ''
|
||||
let res = ''
|
||||
routes.forEach((route, i) => {
|
||||
route._name = '_' + hash(route.component)
|
||||
components.push({ _name: route._name, component: route.component, name: route.name })
|
||||
@ -20,31 +18,30 @@ function recursiveRoutes(routes, tab, components) {
|
||||
})
|
||||
return res
|
||||
}
|
||||
var _components = []
|
||||
var _routes = recursiveRoutes(router.routes, '\t\t', _components)
|
||||
uniqBy(_components, '_name').forEach((route) => { %>
|
||||
const <%= route._name %> = () => import('<%= route.component %>' /* webpackChunkName: "pages/<%= route.name %>" */)
|
||||
const _components = []
|
||||
const _routes = recursiveRoutes(router.routes, '\t\t', _components)
|
||||
uniqBy(_components, '_name').forEach((route) => { %>const <%= route._name %> = () => import('<%= relativeToBuild(route.component) %>' /* webpackChunkName: "pages/<%= route.name %>" */).then(m => m.default || m)
|
||||
<% }) %>
|
||||
|
||||
<% if (router.scrollBehavior) { %>
|
||||
const scrollBehavior = <%= serialize(router.scrollBehavior).replace('scrollBehavior(', 'function(') %>
|
||||
<% } else { %>
|
||||
const scrollBehavior = (to, from, savedPosition) => {
|
||||
// savedPosition is only available for popstate navigations.
|
||||
// SavedPosition is only available for popstate navigations.
|
||||
if (savedPosition) {
|
||||
return savedPosition
|
||||
} else {
|
||||
let position = {}
|
||||
// if no children detected
|
||||
// If no children detected
|
||||
if (to.matched.length < 2) {
|
||||
// scroll to the top of the page
|
||||
// Scroll to the top of the page
|
||||
position = { x: 0, y: 0 }
|
||||
}
|
||||
else if (to.matched.some((r) => r.components.default.options.scrollToTop)) {
|
||||
// if one of the children has scrollToTop option set to true
|
||||
// If one of the children has scrollToTop option set to true
|
||||
position = { x: 0, y: 0 }
|
||||
}
|
||||
// if link has anchor, scroll to anchor by returning the selector
|
||||
// If link has anchor, scroll to anchor by returning the selector
|
||||
if (to.hash) {
|
||||
position = { selector: to.hash }
|
||||
}
|
||||
@ -62,6 +59,7 @@ export function createRouter () {
|
||||
scrollBehavior,
|
||||
routes: [
|
||||
<%= _routes %>
|
||||
]
|
||||
],
|
||||
fallback: <%= router.fallback %>
|
||||
})
|
||||
}
|
||||
|
@ -1,36 +1,21 @@
|
||||
'use strict'
|
||||
|
||||
import Vue from 'vue'
|
||||
import clone from 'clone'
|
||||
import { stringify } from 'querystring'
|
||||
import { omit } from 'lodash'
|
||||
import middleware from './middleware'
|
||||
import { createApp, NuxtError } from './index'
|
||||
import { applyAsyncData, sanitizeComponent, getMatchedComponents, getContext, middlewareSeries, promisify, urlJoin } from './utils'
|
||||
|
||||
const debug = require('debug')('nuxt:render')
|
||||
debug.color = 4 // force blue color
|
||||
|
||||
const isDev = <%= isDev %>
|
||||
|
||||
// This exported function will be called by `bundleRenderer`.
|
||||
// This is where we perform data-prefetching to determine the
|
||||
// state of our application before actually rendering it.
|
||||
// Since data fetching is async, this function is expected to
|
||||
// return a Promise that resolves to the app instance.
|
||||
export default async (context) => {
|
||||
const { app, router<%= (store ? ', store' : '') %> } = await createApp(context)
|
||||
const _app = new Vue(app)
|
||||
const _noopApp = new Vue({ render: (h) => h('div') })
|
||||
// Add store to the context
|
||||
<%= (store ? 'context.store = store' : '') %>
|
||||
// Add route to the context
|
||||
context.route = router.currentRoute
|
||||
// Nuxt object
|
||||
context.nuxt = { layout: 'default', data: [], error: null<%= (store ? ', state: null' : '') %>, serverRendered: true }
|
||||
// create context.next for simulate next() of beforeEach() when wanted to redirect
|
||||
context.redirected = false
|
||||
context.next = function (opts) {
|
||||
const noopApp = () => new Vue({ render: (h) => h('div') })
|
||||
|
||||
const createNext = context => opts => {
|
||||
context.redirected = opts
|
||||
// if nuxt generate
|
||||
// If nuxt generate
|
||||
if (!context.res) {
|
||||
context.nuxt.serverRendered = false
|
||||
return
|
||||
@ -40,40 +25,78 @@ export default async (context) => {
|
||||
if (opts.path.indexOf('http') !== 0 && ('<%= router.base %>' !== '/' && opts.path.indexOf('<%= router.base %>') !== 0)) {
|
||||
opts.path = urlJoin('<%= router.base %>', opts.path)
|
||||
}
|
||||
// Avoid loop redirect
|
||||
if (opts.path === context.url) {
|
||||
context.redirected = false
|
||||
return
|
||||
}
|
||||
context.res.writeHead(opts.status, {
|
||||
'Location': opts.path
|
||||
})
|
||||
context.res.end()
|
||||
}
|
||||
|
||||
// This exported function will be called by `bundleRenderer`.
|
||||
// This is where we perform data-prefetching to determine the
|
||||
// state of our application before actually rendering it.
|
||||
// Since data fetching is async, this function is expected to
|
||||
// return a Promise that resolves to the app instance.
|
||||
export default async context => {
|
||||
// Create context.next for simulate next() of beforeEach() when wanted to redirect
|
||||
context.redirected = false
|
||||
context.next = createNext(context)
|
||||
|
||||
const { app, router<%= (store ? ', store' : '') %> } = await createApp(context)
|
||||
const _app = new Vue(app)
|
||||
|
||||
<% if (store) { %>
|
||||
// Add store to the context
|
||||
context.store = store
|
||||
<% } %>
|
||||
|
||||
// Add route to the context
|
||||
context.route = router.currentRoute
|
||||
|
||||
// Nuxt object
|
||||
context.nuxt = { layout: 'default', data: [], error: null<%= (store ? ', state: null' : '') %>, serverRendered: true }
|
||||
|
||||
// Add meta infos
|
||||
context.meta = _app.$meta()
|
||||
|
||||
// Error function
|
||||
context.error = _app.$options._nuxt.error.bind(_app)
|
||||
|
||||
<%= (isDev ? 'const s = isDev && Date.now()' : '') %>
|
||||
let ctx = getContext(context, app)
|
||||
// Keep asyncData for each matched component in context
|
||||
context.asyncData = {}
|
||||
|
||||
// Create shared ctx
|
||||
const ctx = getContext(context, app)
|
||||
|
||||
<% if (isDev) { %>const s = isDev && Date.now()<% } %>
|
||||
|
||||
// Resolve components
|
||||
let Components = []
|
||||
let promises = getMatchedComponents(router.match(context.url)).map((Component) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (typeof Component !== 'function' || Component.super === Vue) return resolve(sanitizeComponent(Component))
|
||||
const _resolve = (Component) => resolve(sanitizeComponent(Component))
|
||||
Component().then(_resolve).catch(reject)
|
||||
})
|
||||
})
|
||||
try {
|
||||
Components = await Promise.all(promises)
|
||||
Components = await Promise.all(getMatchedComponents(router.match(context.url)).map(Component => {
|
||||
if (typeof Component !== 'function' || Component.super === Vue) {
|
||||
return sanitizeComponent(Component)
|
||||
}
|
||||
return Component().then(Component => sanitizeComponent(Component))
|
||||
}))
|
||||
} catch (err) {
|
||||
// Throw back error to renderRoute()
|
||||
throw err
|
||||
}
|
||||
// nuxtServerInit
|
||||
|
||||
<% if (store) { %>
|
||||
let promise = (store._actions && store._actions.nuxtServerInit ? store.dispatch('nuxtServerInit', omit(getContext(context, app), 'redirect', 'error')) : null)
|
||||
if (!promise || (!(promise instanceof Promise) && (typeof promise.then !== 'function'))) promise = Promise.resolve()
|
||||
<% } else { %>
|
||||
let promise = Promise.resolve()
|
||||
// Dispatch store nuxtServerInit
|
||||
if (store._actions && store._actions.nuxtServerInit) {
|
||||
await store.dispatch('nuxtServerInit', ctx)
|
||||
}
|
||||
// ...If there is a redirect
|
||||
if (context.redirected) return noopApp()
|
||||
<% } %>
|
||||
await promise
|
||||
|
||||
// Call global middleware (nuxt.config.js)
|
||||
let midd = <%= serialize(router.middleware, { isJSON: true }) %>
|
||||
midd = midd.map((name) => {
|
||||
@ -85,15 +108,19 @@ export default async (context) => {
|
||||
if (!context.nuxt.error) {
|
||||
await middlewareSeries(midd, ctx)
|
||||
}
|
||||
if (context.redirected) return _noopApp
|
||||
// ...If there is a redirect
|
||||
if (context.redirected) return noopApp()
|
||||
|
||||
// Set layout
|
||||
let layout = Components.length ? Components[0].options.layout : NuxtError.layout
|
||||
if (typeof layout === 'function') layout = layout(ctx)
|
||||
await _app.loadLayout(layout)
|
||||
layout = _app.setLayout(layout)
|
||||
// Set layout to __NUXT__
|
||||
// ...Set layout to __NUXT__
|
||||
context.nuxt.layout = _app.layoutName
|
||||
|
||||
// Call middleware (layout + pages)
|
||||
if (!context.nuxt.error) {
|
||||
midd = []
|
||||
if (layout.middleware) midd = midd.concat(layout.middleware)
|
||||
Components.forEach((Component) => {
|
||||
@ -107,10 +134,13 @@ export default async (context) => {
|
||||
}
|
||||
return middleware[name]
|
||||
})
|
||||
if (!context.nuxt.error) {
|
||||
|
||||
await middlewareSeries(midd, ctx)
|
||||
|
||||
// If there is a redirect
|
||||
if (context.redirected) return noopApp()
|
||||
}
|
||||
if (context.redirected) return _noopApp
|
||||
|
||||
// Call .validate()
|
||||
let isValid = true
|
||||
Components.forEach((Component) => {
|
||||
@ -118,10 +148,11 @@ export default async (context) => {
|
||||
if (typeof Component.options.validate !== 'function') return
|
||||
isValid = Component.options.validate({
|
||||
params: context.route.params || {},
|
||||
query: context.route.query || {}<%= (store ? ', store: ctx.store' : '') %>
|
||||
query: context.route.query || {},
|
||||
<%= (store ? 'store: ctx.store' : '') %>
|
||||
})
|
||||
})
|
||||
// If .validate() returned false
|
||||
// ...If .validate() returned false
|
||||
if (!isValid) {
|
||||
// Don't server-render the page in generate mode
|
||||
if (context._generate) {
|
||||
@ -130,52 +161,65 @@ export default async (context) => {
|
||||
// Call the 404 error by making the Components array empty
|
||||
Components = []
|
||||
}
|
||||
|
||||
// Call asyncData & fetch hooks on components matched by the route.
|
||||
let asyncDatas = await Promise.all(Components.map((Component) => {
|
||||
let asyncDatas = await Promise.all(Components.map(Component => {
|
||||
let promises = []
|
||||
|
||||
// Call asyncData(context)
|
||||
if (Component.options.asyncData && typeof Component.options.asyncData === 'function') {
|
||||
let promise = promisify(Component.options.asyncData, ctx)
|
||||
// Call asyncData(context)
|
||||
promise.then((asyncDataResult) => {
|
||||
applyAsyncData(Component, asyncDataResult)
|
||||
promise.then(asyncDataResult => {
|
||||
context.asyncData[Component.options.name] = asyncDataResult
|
||||
applyAsyncData(Component)
|
||||
return asyncDataResult
|
||||
})
|
||||
promises.push(promise)
|
||||
} else promises.push(null)
|
||||
// call fetch(context)
|
||||
if (Component.options.fetch) promises.push(Component.options.fetch(ctx))
|
||||
else promises.push(null)
|
||||
} else {
|
||||
promises.push(null)
|
||||
}
|
||||
|
||||
// Call fetch(context)
|
||||
if (Component.options.fetch) {
|
||||
promises.push(Component.options.fetch(ctx))
|
||||
}
|
||||
else {
|
||||
promises.push(null)
|
||||
}
|
||||
|
||||
return Promise.all(promises)
|
||||
}))
|
||||
|
||||
// If no Components found, returns 404
|
||||
if (!Components.length) {
|
||||
context.nuxt.error = context.error({ statusCode: 404, message: 'This page could not be found.' })
|
||||
}
|
||||
<% if (isDev) { %>
|
||||
if (asyncDatas.length) debug('Data fetching ' + context.url + ': ' + (Date.now() - s) + 'ms')
|
||||
<% } %>
|
||||
|
||||
<% if (isDev) { %>if (asyncDatas.length) debug('Data fetching ' + context.url + ': ' + (Date.now() - s) + 'ms')<% } %>
|
||||
|
||||
// datas are the first row of each
|
||||
context.nuxt.data = asyncDatas.map((r) => (r[0] || {}))
|
||||
context.nuxt.data = asyncDatas.map(r => r[0] || {})
|
||||
|
||||
// If an error occured in the execution
|
||||
if (_app.$options._nuxt.err) {
|
||||
context.nuxt.error = _app.$options._nuxt.err
|
||||
}
|
||||
<%= (store ? '// Add the state from the vuex store' : '') %>
|
||||
<%= (store ? 'context.nuxt.state = store.state' : '') %>
|
||||
|
||||
<% if (store) { %>
|
||||
// Add the state from the vuex store
|
||||
context.nuxt.state = store.state
|
||||
<% } %>
|
||||
|
||||
// If no error, return main app
|
||||
if (!context.nuxt.error) {
|
||||
return _app
|
||||
}
|
||||
|
||||
// Load layout for error page
|
||||
layout = (typeof NuxtError.layout === 'function' ? NuxtError.layout(ctx) : NuxtError.layout)
|
||||
context.nuxt.layout = layout || ''
|
||||
await _app.loadLayout(layout)
|
||||
_app.setLayout(layout)
|
||||
|
||||
return _app
|
||||
// if (typeof error === 'string') {
|
||||
// error = { statusCode: 500, message: error }
|
||||
// }
|
||||
// context.nuxt.error = context.error(error)
|
||||
// <%= (store ? 'context.nuxt.state = store.state' : '') %>
|
||||
// return _app
|
||||
}
|
||||
|
@ -3,15 +3,20 @@ import Vuex from 'vuex'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
// Recursive find files in ~/store
|
||||
const files = require.context('~/store', true, /^\.\/.*\.(js|ts)$/)
|
||||
// Recursive find files in {srcDir}/store
|
||||
const files = require.context('@/store', true, /^\.\/.*\.(js|ts)$/)
|
||||
const filenames = files.keys()
|
||||
|
||||
// Store
|
||||
let storeData = {}
|
||||
|
||||
// Check if store/index.js exists
|
||||
const indexFilename = filenames.find((filename) => filename.includes('./index.'))
|
||||
let indexFilename
|
||||
filenames.forEach((filename) => {
|
||||
if (filename.indexOf('./index.') !== -1) {
|
||||
indexFilename = filename
|
||||
}
|
||||
})
|
||||
if (indexFilename) {
|
||||
storeData = getModule(indexFilename)
|
||||
}
|
||||
|
@ -1,12 +1,28 @@
|
||||
'use strict'
|
||||
import Vue from 'vue'
|
||||
|
||||
const noopData = () => ({})
|
||||
|
||||
// window.onNuxtReady(() => console.log('Ready')) hook
|
||||
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
|
||||
if (process.browser) {
|
||||
window._nuxtReadyCbs = []
|
||||
window.onNuxtReady = function (cb) {
|
||||
window._nuxtReadyCbs.push(cb)
|
||||
}
|
||||
}
|
||||
|
||||
export function applyAsyncData (Component, asyncData = {}) {
|
||||
const ComponentData = Component.options.data || noopData
|
||||
// Prevent calling this method for each request on SSR context
|
||||
if(!asyncData && Component.options.hasAsyncData) {
|
||||
return
|
||||
}
|
||||
Component.options.hasAsyncData = true
|
||||
Component.options.data = function () {
|
||||
const data = ComponentData.call(this)
|
||||
if(this.$ssrContext) {
|
||||
asyncData = this.$ssrContext.asyncData[Component.options.name]
|
||||
}
|
||||
return { ...data, ...asyncData }
|
||||
}
|
||||
if (Component._Ctor && Component._Ctor.options) {
|
||||
|
565
lib/build.js
565
lib/build.js
@ -1,565 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
import _ from 'lodash'
|
||||
import chokidar from 'chokidar'
|
||||
import fs from 'fs-extra'
|
||||
import hash from 'hash-sum'
|
||||
import pify from 'pify'
|
||||
import webpack from 'webpack'
|
||||
import PostCompilePlugin from 'post-compile-webpack-plugin'
|
||||
import serialize from 'serialize-javascript'
|
||||
import { createBundleRenderer } from 'vue-server-renderer'
|
||||
import { join, resolve, basename, dirname } from 'path'
|
||||
import { isUrl, r, wp } from './utils'
|
||||
import clientWebpackConfig from './webpack/client.config.js'
|
||||
import serverWebpackConfig from './webpack/server.config.js'
|
||||
const debug = require('debug')('nuxt:build')
|
||||
const remove = pify(fs.remove)
|
||||
const readFile = pify(fs.readFile)
|
||||
const utimes = pify(fs.utimes)
|
||||
const writeFile = pify(fs.writeFile)
|
||||
const mkdirp = pify(fs.mkdirp)
|
||||
const glob = pify(require('glob'))
|
||||
|
||||
let webpackStats = 'none'
|
||||
debug.color = 2 // force green color
|
||||
|
||||
const defaults = {
|
||||
analyze: false,
|
||||
extractCSS: false,
|
||||
publicPath: '/_nuxt/',
|
||||
filenames: {
|
||||
css: 'common.[chunkhash].css',
|
||||
manifest: 'manifest.[hash].js',
|
||||
vendor: 'vendor.bundle.[chunkhash].js',
|
||||
app: 'nuxt.bundle.[chunkhash].js'
|
||||
},
|
||||
vendor: [],
|
||||
loaders: [],
|
||||
plugins: [],
|
||||
babel: {},
|
||||
postcss: [],
|
||||
templates: [],
|
||||
watch: []
|
||||
}
|
||||
const defaultsLoaders = [
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg)$/,
|
||||
loader: 'url-loader',
|
||||
query: {
|
||||
limit: 1000, // 1KO
|
||||
name: 'img/[name].[hash:7].[ext]'
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
query: {
|
||||
limit: 1000, // 1 KO
|
||||
name: 'fonts/[name].[hash:7].[ext]'
|
||||
}
|
||||
}
|
||||
]
|
||||
const defaultsPostcss = [
|
||||
require('autoprefixer')({
|
||||
browsers: ['last 3 versions']
|
||||
})
|
||||
]
|
||||
|
||||
export function options () {
|
||||
// Defaults build options
|
||||
let extraDefaults = {}
|
||||
if (this.options.build && !Array.isArray(this.options.build.loaders)) extraDefaults.loaders = defaultsLoaders
|
||||
if (this.options.build && !Array.isArray(this.options.build.postcss)) extraDefaults.postcss = defaultsPostcss
|
||||
this.options.build = _.defaultsDeep(this.options.build, defaults, extraDefaults)
|
||||
/* istanbul ignore if */
|
||||
if (this.dev && isUrl(this.options.build.publicPath)) {
|
||||
this.options.build.publicPath = defaults.publicPath
|
||||
}
|
||||
}
|
||||
|
||||
export function production () {
|
||||
// Production, create server-renderer
|
||||
webpackStats = {
|
||||
chunks: false,
|
||||
children: false,
|
||||
modules: false,
|
||||
colors: true
|
||||
}
|
||||
const serverConfig = getWebpackServerConfig.call(this)
|
||||
const bundlePath = join(serverConfig.output.path, 'server-bundle.json')
|
||||
const manifestPath = join(serverConfig.output.path, 'client-manifest.json')
|
||||
if (fs.existsSync(bundlePath) && fs.existsSync(manifestPath)) {
|
||||
const bundle = fs.readFileSync(bundlePath, 'utf8')
|
||||
const manifest = fs.readFileSync(manifestPath, 'utf8')
|
||||
createRenderer.call(this, JSON.parse(bundle), JSON.parse(manifest))
|
||||
addAppTemplate.call(this)
|
||||
}
|
||||
}
|
||||
|
||||
export async function build () {
|
||||
// Avoid calling this method multiple times
|
||||
if (this._buildDone) {
|
||||
return this
|
||||
}
|
||||
// If building
|
||||
if (this._building) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(this.build())
|
||||
}, 300)
|
||||
})
|
||||
}
|
||||
this._building = true
|
||||
// Wait for Nuxt.js to be ready
|
||||
await this.ready()
|
||||
// Check if pages dir exists and warn if not
|
||||
this._nuxtPages = typeof this.createRoutes !== 'function'
|
||||
if (this._nuxtPages) {
|
||||
if (!fs.existsSync(join(this.srcDir, 'pages'))) {
|
||||
if (fs.existsSync(join(this.srcDir, '..', 'pages'))) {
|
||||
console.error('> No `pages` directory found. Did you mean to run `nuxt` in the parent (`../`) directory?') // eslint-disable-line no-console
|
||||
} else {
|
||||
console.error('> Couldn\'t find a `pages` directory. Please create one under the project root') // eslint-disable-line no-console
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
debug(`App root: ${this.srcDir}`)
|
||||
debug(`Generating ${this.buildDir} files...`)
|
||||
// Create .nuxt/, .nuxt/components and .nuxt/dist folders
|
||||
await remove(r(this.buildDir))
|
||||
await mkdirp(r(this.buildDir, 'components'))
|
||||
if (!this.dev) {
|
||||
await mkdirp(r(this.buildDir, 'dist'))
|
||||
}
|
||||
// Generate routes and interpret the template files
|
||||
await generateRoutesAndFiles.call(this)
|
||||
// Generate .nuxt/dist/ files
|
||||
await buildFiles.call(this)
|
||||
// Flag to set that building is done
|
||||
this._buildDone = true
|
||||
return this
|
||||
}
|
||||
|
||||
async function buildFiles () {
|
||||
if (this.dev) {
|
||||
debug('Adding webpack middleware...')
|
||||
createWebpackMiddleware.call(this)
|
||||
webpackWatchAndUpdate.call(this)
|
||||
watchFiles.call(this)
|
||||
} else {
|
||||
debug('Building files...')
|
||||
await webpackRunClient.call(this)
|
||||
await webpackRunServer.call(this)
|
||||
addAppTemplate.call(this)
|
||||
}
|
||||
}
|
||||
|
||||
function addAppTemplate () {
|
||||
let templatePath = resolve(this.buildDir, 'dist', 'index.html')
|
||||
if (fs.existsSync(templatePath)) {
|
||||
this.appTemplate = _.template(fs.readFileSync(templatePath, 'utf8'), {
|
||||
interpolate: /{{([\s\S]+?)}}/g
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function generateRoutesAndFiles () {
|
||||
debug('Generating files...')
|
||||
// -- Templates --
|
||||
let templatesFiles = [
|
||||
'App.vue',
|
||||
'client.js',
|
||||
'index.js',
|
||||
'middleware.js',
|
||||
'router.js',
|
||||
'server.js',
|
||||
'utils.js',
|
||||
'components/nuxt-error.vue',
|
||||
'components/nuxt-loading.vue',
|
||||
'components/nuxt-child.js',
|
||||
'components/nuxt-link.js',
|
||||
'components/nuxt.vue'
|
||||
]
|
||||
const templateVars = {
|
||||
options: this.options,
|
||||
uniqBy: _.uniqBy,
|
||||
isDev: this.dev,
|
||||
router: {
|
||||
mode: this.options.router.mode,
|
||||
base: this.options.router.base,
|
||||
middleware: this.options.router.middleware,
|
||||
linkActiveClass: this.options.router.linkActiveClass,
|
||||
linkExactActiveClass: this.options.router.linkExactActiveClass,
|
||||
scrollBehavior: this.options.router.scrollBehavior
|
||||
},
|
||||
env: this.options.env,
|
||||
head: this.options.head,
|
||||
middleware: fs.existsSync(join(this.srcDir, 'middleware')),
|
||||
store: this.options.store || fs.existsSync(join(this.srcDir, 'store')),
|
||||
css: this.options.css,
|
||||
plugins: this.options.plugins.map((p, i) => {
|
||||
if (typeof p === 'string') p = { src: p }
|
||||
p.src = r(this.srcDir, p.src)
|
||||
return { src: p.src, ssr: (p.ssr !== false), name: `plugin${i}` }
|
||||
}),
|
||||
appPath: './App.vue',
|
||||
layouts: Object.assign({}, this.options.layouts),
|
||||
loading: (typeof this.options.loading === 'string' ? r(this.srcDir, this.options.loading) : this.options.loading),
|
||||
transition: this.options.transition,
|
||||
components: {
|
||||
ErrorPage: this.options.ErrorPage ? r(this.options.ErrorPage) : null
|
||||
}
|
||||
}
|
||||
|
||||
// -- Layouts --
|
||||
if (fs.existsSync(resolve(this.srcDir, 'layouts'))) {
|
||||
const layoutsFiles = await glob('layouts/*.vue', {cwd: this.srcDir})
|
||||
layoutsFiles.forEach((file) => {
|
||||
let name = file.split('/').slice(-1)[0].replace('.vue', '')
|
||||
if (name === 'error') return
|
||||
templateVars.layouts[name] = r(this.srcDir, file)
|
||||
})
|
||||
if (layoutsFiles.includes('layouts/error.vue')) {
|
||||
templateVars.components.ErrorPage = r(this.srcDir, 'layouts/error.vue')
|
||||
}
|
||||
}
|
||||
// If no default layout, create its folder and add the default folder
|
||||
if (!templateVars.layouts.default) {
|
||||
await mkdirp(r(this.buildDir, 'layouts'))
|
||||
templatesFiles.push('layouts/default.vue')
|
||||
templateVars.layouts.default = r(__dirname, 'app', 'layouts', 'default.vue')
|
||||
}
|
||||
|
||||
// -- Routes --
|
||||
debug('Generating routes...')
|
||||
// If user defined a custom method to create routes
|
||||
if (this._nuxtPages) {
|
||||
// Use nuxt.js createRoutes bases on pages/
|
||||
const files = await glob('pages/**/*.vue', {cwd: this.srcDir})
|
||||
templateVars.router.routes = createRoutes(files, this.srcDir)
|
||||
} else {
|
||||
templateVars.router.routes = this.createRoutes(this.srcDir)
|
||||
}
|
||||
// router.extendRoutes method
|
||||
if (typeof this.options.router.extendRoutes === 'function') {
|
||||
// let the user extend the routes
|
||||
this.options.router.extendRoutes.call(this, templateVars.router.routes || [], r)
|
||||
}
|
||||
// Routes for generate command
|
||||
this.routes = flatRoutes(templateVars.router.routes || [])
|
||||
|
||||
// -- Store --
|
||||
// Add store if needed
|
||||
if (this.options.store) {
|
||||
templatesFiles.push('store.js')
|
||||
}
|
||||
|
||||
// Resolve template files
|
||||
const customTemplateFiles = this.options.build.templates.map(t => t.dst || basename(t.src || t))
|
||||
templatesFiles = templatesFiles.map(file => {
|
||||
// Skip if custom file was already provided in build.templates[]
|
||||
if (customTemplateFiles.indexOf(file) !== -1) {
|
||||
return
|
||||
}
|
||||
// Allow override templates using a file with same name in ${srcDir}/app
|
||||
const customPath = r(this.srcDir, 'app', file)
|
||||
const customFileExists = fs.existsSync(customPath)
|
||||
return {
|
||||
src: customFileExists ? customPath : r(__dirname, 'app', file),
|
||||
dst: file,
|
||||
custom: customFileExists
|
||||
}
|
||||
}).filter(i => !!i)
|
||||
|
||||
// -- Custom templates --
|
||||
// Add custom template files
|
||||
templatesFiles = templatesFiles.concat(this.options.build.templates.map(t => {
|
||||
return Object.assign({
|
||||
src: r(this.dir, t.src || t),
|
||||
dst: t.dst || basename(t.src || t),
|
||||
custom: true
|
||||
}, t)
|
||||
}))
|
||||
|
||||
// Interpret and move template files to .nuxt/
|
||||
return Promise.all(templatesFiles.map(async ({ src, dst, options, custom }) => {
|
||||
// Add template to watchers
|
||||
this.options.build.watch.push(src)
|
||||
// Render template to dst
|
||||
const fileContent = await readFile(src, 'utf8')
|
||||
const template = _.template(fileContent, {
|
||||
imports: {
|
||||
serialize,
|
||||
hash,
|
||||
r,
|
||||
wp
|
||||
}
|
||||
})
|
||||
const content = template(Object.assign({}, templateVars, {
|
||||
options: options || {},
|
||||
custom,
|
||||
src,
|
||||
dst
|
||||
}))
|
||||
const path = r(this.buildDir, dst)
|
||||
// Ensure parent dir exits
|
||||
await mkdirp(dirname(path))
|
||||
// Write file
|
||||
await writeFile(path, content, 'utf8')
|
||||
// Fix webpack loop (https://github.com/webpack/watchpack/issues/25#issuecomment-287789288)
|
||||
const dateFS = Date.now() / 1000 - 30
|
||||
return utimes(path, dateFS, dateFS)
|
||||
}))
|
||||
}
|
||||
|
||||
function createRoutes (files, srcDir) {
|
||||
let routes = []
|
||||
files.forEach((file) => {
|
||||
let keys = file.replace(/^pages/, '').replace(/\.vue$/, '').replace(/\/{2,}/g, '/').split('/').slice(1)
|
||||
let route = { name: '', path: '', component: r(srcDir, file) }
|
||||
let parent = routes
|
||||
keys.forEach((key, i) => {
|
||||
route.name = route.name ? route.name + '-' + key.replace('_', '') : key.replace('_', '')
|
||||
route.name += (key === '_') ? 'all' : ''
|
||||
let child = _.find(parent, { name: route.name })
|
||||
if (child) {
|
||||
if (!child.children) {
|
||||
child.children = []
|
||||
}
|
||||
parent = child.children
|
||||
route.path = ''
|
||||
} else {
|
||||
if (key === 'index' && (i + 1) === keys.length) {
|
||||
route.path += (i > 0 ? '' : '/')
|
||||
} else {
|
||||
route.path += '/' + (key === '_' ? '*' : key.replace('_', ':'))
|
||||
if (key !== '_' && key.indexOf('_') !== -1) {
|
||||
route.path += '?'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// Order Routes path
|
||||
parent.push(route)
|
||||
parent.sort((a, b) => {
|
||||
if (!a.path.length || a.path === '/') { return -1 }
|
||||
if (!b.path.length || b.path === '/') { return 1 }
|
||||
var res = 0
|
||||
var _a = a.path.split('/')
|
||||
var _b = b.path.split('/')
|
||||
for (var i = 0; i < _a.length; i++) {
|
||||
if (res !== 0) { break }
|
||||
var y = (_a[i].indexOf('*') > -1) ? 2 : (_a[i].indexOf(':') > -1 ? 1 : 0)
|
||||
var z = (_b[i].indexOf('*') > -1) ? 2 : (_b[i].indexOf(':') > -1 ? 1 : 0)
|
||||
res = y - z
|
||||
if (i === _b.length - 1 && res === 0) {
|
||||
res = 1
|
||||
}
|
||||
}
|
||||
return res === 0 ? -1 : res
|
||||
})
|
||||
})
|
||||
return cleanChildrenRoutes(routes)
|
||||
}
|
||||
|
||||
function cleanChildrenRoutes (routes, isChild = false) {
|
||||
let start = -1
|
||||
let routesIndex = []
|
||||
routes.forEach((route) => {
|
||||
if (/-index$/.test(route.name) || route.name === 'index') {
|
||||
// Save indexOf 'index' key in name
|
||||
let res = route.name.split('-')
|
||||
let s = res.indexOf('index')
|
||||
start = (start === -1 || s < start) ? s : start
|
||||
routesIndex.push(res)
|
||||
}
|
||||
})
|
||||
routes.forEach((route) => {
|
||||
route.path = (isChild) ? route.path.replace('/', '') : route.path
|
||||
if (route.path.indexOf('?') > -1) {
|
||||
let names = route.name.split('-')
|
||||
let paths = route.path.split('/')
|
||||
if (!isChild) { paths.shift() } // clean first / for parents
|
||||
routesIndex.forEach((r) => {
|
||||
let i = r.indexOf('index') - start // children names
|
||||
if (i < paths.length) {
|
||||
for (var a = 0; a <= i; a++) {
|
||||
if (a === i) { paths[a] = paths[a].replace('?', '') }
|
||||
if (a < i && names[a] !== r[a]) { break }
|
||||
}
|
||||
}
|
||||
})
|
||||
route.path = (isChild ? '' : '/') + paths.join('/')
|
||||
}
|
||||
route.name = route.name.replace(/-index$/, '')
|
||||
if (route.children) {
|
||||
if (route.children.find((child) => child.path === '')) {
|
||||
delete route.name
|
||||
}
|
||||
route.children = cleanChildrenRoutes(route.children, true)
|
||||
}
|
||||
})
|
||||
return routes
|
||||
}
|
||||
|
||||
function flatRoutes (router, path = '', routes = []) {
|
||||
router.forEach((r) => {
|
||||
if (!r.path.includes(':') && !r.path.includes('*')) {
|
||||
if (r.children) {
|
||||
flatRoutes(r.children, path + r.path + '/', routes)
|
||||
} else {
|
||||
routes.push((r.path === '' && path[path.length - 1] === '/' ? path.slice(0, -1) : path) + r.path)
|
||||
}
|
||||
}
|
||||
})
|
||||
return routes
|
||||
}
|
||||
|
||||
function getWebpackClientConfig () {
|
||||
return clientWebpackConfig.call(this)
|
||||
}
|
||||
|
||||
function getWebpackServerConfig () {
|
||||
return serverWebpackConfig.call(this)
|
||||
}
|
||||
|
||||
function createWebpackMiddleware () {
|
||||
const clientConfig = getWebpackClientConfig.call(this)
|
||||
const host = process.env.HOST || process.env.npm_package_config_nuxt_host || '127.0.0.1'
|
||||
const port = process.env.PORT || process.env.npm_package_config_nuxt_port || '3000'
|
||||
// setup on the fly compilation + hot-reload
|
||||
clientConfig.entry.app = _.flatten(['webpack-hot-middleware/client?reload=true', clientConfig.entry.app])
|
||||
clientConfig.plugins.push(
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
new PostCompilePlugin(stats => {
|
||||
if (!stats.hasErrors() && !stats.hasWarnings()) {
|
||||
console.log(`> Open http://${host}:${port}\n`) // eslint-disable-line no-console
|
||||
}
|
||||
})
|
||||
)
|
||||
const clientCompiler = webpack(clientConfig)
|
||||
this.clientCompiler = clientCompiler
|
||||
// Add the middleware to the instance context
|
||||
this.webpackDevMiddleware = pify(require('webpack-dev-middleware')(clientCompiler, {
|
||||
publicPath: clientConfig.output.publicPath,
|
||||
stats: webpackStats,
|
||||
quiet: true,
|
||||
noInfo: true,
|
||||
watchOptions: this.options.watchers.webpack
|
||||
}))
|
||||
this.webpackHotMiddleware = pify(require('webpack-hot-middleware')(clientCompiler, {
|
||||
log: () => {}
|
||||
}))
|
||||
clientCompiler.plugin('done', () => {
|
||||
const fs = this.webpackDevMiddleware.fileSystem
|
||||
const filePath = join(clientConfig.output.path, 'index.html')
|
||||
if (fs.existsSync(filePath)) {
|
||||
const template = fs.readFileSync(filePath, 'utf-8')
|
||||
this.appTemplate = _.template(template, {
|
||||
interpolate: /{{([\s\S]+?)}}/g
|
||||
})
|
||||
}
|
||||
this.watchHandler()
|
||||
})
|
||||
}
|
||||
|
||||
function webpackWatchAndUpdate () {
|
||||
const MFS = require('memory-fs') // <- dependencies of webpack
|
||||
const serverFS = new MFS()
|
||||
const clientFS = this.clientCompiler.outputFileSystem
|
||||
const serverConfig = getWebpackServerConfig.call(this)
|
||||
const serverCompiler = webpack(serverConfig)
|
||||
const bundlePath = join(serverConfig.output.path, 'server-bundle.json')
|
||||
const manifestPath = join(serverConfig.output.path, 'client-manifest.json')
|
||||
serverCompiler.outputFileSystem = serverFS
|
||||
const watchHandler = (err) => {
|
||||
if (err) throw err
|
||||
const bundleExists = serverFS.existsSync(bundlePath)
|
||||
const manifestExists = clientFS.existsSync(manifestPath)
|
||||
if (bundleExists && manifestExists) {
|
||||
const bundle = serverFS.readFileSync(bundlePath, 'utf8')
|
||||
const manifest = clientFS.readFileSync(manifestPath, 'utf8')
|
||||
createRenderer.call(this, JSON.parse(bundle), JSON.parse(manifest))
|
||||
}
|
||||
}
|
||||
this.watchHandler = watchHandler
|
||||
this.webpackServerWatcher = serverCompiler.watch(this.options.watchers.webpack, watchHandler)
|
||||
}
|
||||
|
||||
function webpackRunClient () {
|
||||
return new Promise((resolve, reject) => {
|
||||
const clientConfig = getWebpackClientConfig.call(this)
|
||||
const clientCompiler = webpack(clientConfig)
|
||||
clientCompiler.run((err, stats) => {
|
||||
if (err) return reject(err)
|
||||
console.log('[nuxt:build:client]\n', stats.toString(webpackStats)) // eslint-disable-line no-console
|
||||
if (stats.hasErrors()) return reject(new Error('Webpack build exited with errors'))
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function webpackRunServer () {
|
||||
return new Promise((resolve, reject) => {
|
||||
const serverConfig = getWebpackServerConfig.call(this)
|
||||
const serverCompiler = webpack(serverConfig)
|
||||
serverCompiler.run((err, stats) => {
|
||||
if (err) return reject(err)
|
||||
console.log('[nuxt:build:server]\n', stats.toString(webpackStats)) // eslint-disable-line no-console
|
||||
if (stats.hasErrors()) return reject(new Error('Webpack build exited with errors'))
|
||||
const bundlePath = join(serverConfig.output.path, 'server-bundle.json')
|
||||
const manifestPath = join(serverConfig.output.path, 'client-manifest.json')
|
||||
readFile(bundlePath, 'utf8')
|
||||
.then(bundle => {
|
||||
readFile(manifestPath, 'utf8')
|
||||
.then(manifest => {
|
||||
createRenderer.call(this, JSON.parse(bundle), JSON.parse(manifest))
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function createRenderer (bundle, manifest) {
|
||||
// Create bundle renderer to give a fresh context for every request
|
||||
this.renderer = createBundleRenderer(bundle, Object.assign({
|
||||
clientManifest: manifest,
|
||||
runInNewContext: false,
|
||||
basedir: this.dir
|
||||
}, this.options.build.ssr))
|
||||
this.renderToString = pify(this.renderer.renderToString)
|
||||
this.renderToStream = this.renderer.renderToStream
|
||||
}
|
||||
|
||||
function watchFiles () {
|
||||
const patterns = [
|
||||
r(this.srcDir, 'layouts'),
|
||||
r(this.srcDir, 'store'),
|
||||
r(this.srcDir, 'middleware'),
|
||||
r(this.srcDir, 'layouts/*.vue'),
|
||||
r(this.srcDir, 'layouts/**/*.vue')
|
||||
]
|
||||
if (this._nuxtPages) {
|
||||
patterns.push(r(this.srcDir, 'pages'))
|
||||
patterns.push(r(this.srcDir, 'pages/*.vue'))
|
||||
patterns.push(r(this.srcDir, 'pages/**/*.vue'))
|
||||
}
|
||||
const options = Object.assign({}, this.options.watchers.chokidar, {
|
||||
ignoreInitial: true
|
||||
})
|
||||
/* istanbul ignore next */
|
||||
const refreshFiles = _.debounce(async () => {
|
||||
await generateRoutesAndFiles.call(this)
|
||||
}, 200)
|
||||
// Watch for internals
|
||||
this.filesWatcher = chokidar.watch(patterns, options)
|
||||
.on('add', refreshFiles)
|
||||
.on('unlink', refreshFiles)
|
||||
// Watch for custom provided files
|
||||
this.customFilesWatcher = chokidar.watch(_.uniq(this.options.build.watch), options)
|
||||
.on('change', refreshFiles)
|
||||
}
|
460
lib/builder/builder.js
Normal file
460
lib/builder/builder.js
Normal file
@ -0,0 +1,460 @@
|
||||
import _ from 'lodash'
|
||||
import chokidar from 'chokidar'
|
||||
import fs, { remove, readFile, writeFile, mkdirp, utimes } from 'fs-extra'
|
||||
import hash from 'hash-sum'
|
||||
import pify from 'pify'
|
||||
import webpack from 'webpack'
|
||||
import serialize from 'serialize-javascript'
|
||||
import { join, resolve, basename, dirname } from 'path'
|
||||
import Tapable from 'tappable'
|
||||
import MFS from 'memory-fs'
|
||||
import webpackDevMiddleware from 'webpack-dev-middleware'
|
||||
import webpackHotMiddleware from 'webpack-hot-middleware'
|
||||
import { r, wp, createRoutes, parallel, relativeTo } from 'utils'
|
||||
import Debug from 'debug'
|
||||
import Glob from 'glob'
|
||||
import clientWebpackConfig from './webpack/client.config.js'
|
||||
import serverWebpackConfig from './webpack/server.config.js'
|
||||
|
||||
const debug = Debug('nuxt:build')
|
||||
debug.color = 2 // Force green color
|
||||
|
||||
const glob = pify(Glob)
|
||||
|
||||
export default class Builder extends Tapable {
|
||||
constructor (nuxt) {
|
||||
super()
|
||||
this.nuxt = nuxt
|
||||
this.options = nuxt.options
|
||||
|
||||
// Fields that set on build
|
||||
this.compiler = null
|
||||
this.webpackDevMiddleware = null
|
||||
this.webpackHotMiddleware = null
|
||||
|
||||
// Mute stats on dev
|
||||
this.webpackStats = this.options.dev ? false : {
|
||||
chunks: false,
|
||||
children: false,
|
||||
modules: false,
|
||||
colors: true
|
||||
}
|
||||
|
||||
// Helper to resolve build paths
|
||||
this.relativeToBuild = (...args) => relativeTo(this.options.buildDir, ...args)
|
||||
|
||||
this._buildStatus = STATUS.INITIAL
|
||||
}
|
||||
|
||||
get plugins () {
|
||||
return this.options.plugins.map((p, i) => {
|
||||
if (typeof p === 'string') p = { src: p }
|
||||
p.src = r(this.options.srcDir, p.src)
|
||||
return { src: p.src, ssr: (p.ssr !== false), name: `plugin${i}` }
|
||||
})
|
||||
}
|
||||
|
||||
async build () {
|
||||
// Avoid calling build() method multiple times when dev:true
|
||||
/* istanbul ignore if */
|
||||
if (this._buildStatus === STATUS.BUILD_DONE && this.options.dev) {
|
||||
return this
|
||||
}
|
||||
// If building
|
||||
/* istanbul ignore if */
|
||||
if (this._buildStatus === STATUS.BUILDING) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(this.build())
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
this._buildStatus = STATUS.BUILDING
|
||||
|
||||
// Wait for nuxt ready
|
||||
await this.nuxt.ready()
|
||||
|
||||
await this.nuxt.applyPluginsAsync('build', this)
|
||||
|
||||
// Check if pages dir exists and warn if not
|
||||
this._nuxtPages = typeof this.options.build.createRoutes !== 'function'
|
||||
if (this._nuxtPages) {
|
||||
if (!fs.existsSync(join(this.options.srcDir, 'pages'))) {
|
||||
let dir = this.options.srcDir
|
||||
if (fs.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?`)
|
||||
} else {
|
||||
throw new Error(`Couldn't find a \`pages\` directory in ${dir}. Please create one under the project root`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug(`App root: ${this.options.srcDir}`)
|
||||
debug(`Generating ${this.options.buildDir} files...`)
|
||||
|
||||
// Create .nuxt/, .nuxt/components and .nuxt/dist folders
|
||||
await remove(r(this.options.buildDir))
|
||||
await mkdirp(r(this.options.buildDir, 'components'))
|
||||
if (!this.options.dev) {
|
||||
await mkdirp(r(this.options.buildDir, 'dist'))
|
||||
}
|
||||
|
||||
// Generate routes and interpret the template files
|
||||
await this.generateRoutesAndFiles()
|
||||
|
||||
// Start webpack build
|
||||
await this.webpackBuild()
|
||||
|
||||
await this.applyPluginsAsync('built', this)
|
||||
|
||||
// Flag to set that building is done
|
||||
this._buildStatus = STATUS.BUILD_DONE
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
async generateRoutesAndFiles () {
|
||||
debug('Generating files...')
|
||||
// -- Templates --
|
||||
let templatesFiles = [
|
||||
'App.vue',
|
||||
'client.js',
|
||||
'index.js',
|
||||
'middleware.js',
|
||||
'router.js',
|
||||
'server.js',
|
||||
'utils.js',
|
||||
'empty.js',
|
||||
'components/nuxt-error.vue',
|
||||
'components/nuxt-loading.vue',
|
||||
'components/nuxt-child.js',
|
||||
'components/nuxt-link.js',
|
||||
'components/nuxt.vue',
|
||||
'views/app.template.html',
|
||||
'views/error.html'
|
||||
]
|
||||
const templateVars = {
|
||||
options: this.options,
|
||||
uniqBy: _.uniqBy,
|
||||
isDev: this.options.dev,
|
||||
router: this.options.router,
|
||||
env: this.options.env,
|
||||
head: this.options.head,
|
||||
middleware: fs.existsSync(join(this.options.srcDir, 'middleware')),
|
||||
store: this.options.store,
|
||||
css: this.options.css,
|
||||
plugins: this.plugins,
|
||||
appPath: './App.vue',
|
||||
layouts: Object.assign({}, this.options.layouts),
|
||||
loading: typeof this.options.loading === 'string' ? this.relativeToBuild(this.options.srcDir, this.options.loading) : this.options.loading,
|
||||
transition: this.options.transition,
|
||||
components: {
|
||||
ErrorPage: this.options.ErrorPage ? this.relativeToBuild(this.options.ErrorPage) : null
|
||||
}
|
||||
}
|
||||
|
||||
// -- Layouts --
|
||||
if (fs.existsSync(resolve(this.options.srcDir, 'layouts'))) {
|
||||
const layoutsFiles = await glob('layouts/*.vue', { cwd: this.options.srcDir })
|
||||
layoutsFiles.forEach((file) => {
|
||||
let name = file.split('/').slice(-1)[0].replace('.vue', '')
|
||||
if (name === 'error') return
|
||||
templateVars.layouts[name] = this.relativeToBuild(this.options.srcDir, file)
|
||||
})
|
||||
if (layoutsFiles.includes('layouts/error.vue') && !templateVars.components.ErrorPage) {
|
||||
templateVars.components.ErrorPage = this.relativeToBuild(this.options.srcDir, 'layouts/error.vue')
|
||||
}
|
||||
}
|
||||
// If no default layout, create its folder and add the default folder
|
||||
if (!templateVars.layouts.default) {
|
||||
await mkdirp(r(this.options.buildDir, 'layouts'))
|
||||
templatesFiles.push('layouts/default.vue')
|
||||
templateVars.layouts.default = './layouts/default.vue'
|
||||
}
|
||||
|
||||
// -- Routes --
|
||||
debug('Generating routes...')
|
||||
// If user defined a custom method to create routes
|
||||
if (this._nuxtPages) {
|
||||
// Use nuxt.js createRoutes bases on pages/
|
||||
const files = await glob('pages/**/*.vue', { cwd: this.options.srcDir })
|
||||
templateVars.router.routes = createRoutes(files, this.options.srcDir)
|
||||
} else {
|
||||
templateVars.router.routes = this.options.build.createRoutes(this.options.srcDir)
|
||||
}
|
||||
|
||||
await this.applyPluginsAsync('extendRoutes', {routes: templateVars.router.routes, templateVars, r})
|
||||
|
||||
// router.extendRoutes method
|
||||
if (typeof this.options.router.extendRoutes === 'function') {
|
||||
// let the user extend the routes
|
||||
this.options.router.extendRoutes(templateVars.router.routes, r)
|
||||
}
|
||||
|
||||
// -- Store --
|
||||
// Add store if needed
|
||||
if (this.options.store) {
|
||||
templatesFiles.push('store.js')
|
||||
}
|
||||
|
||||
// Resolve template files
|
||||
const customTemplateFiles = this.options.build.templates.map(t => t.dst || basename(t.src || t))
|
||||
|
||||
templatesFiles = templatesFiles.map(file => {
|
||||
// Skip if custom file was already provided in build.templates[]
|
||||
if (customTemplateFiles.indexOf(file) !== -1) {
|
||||
return
|
||||
}
|
||||
// Allow override templates using a file with same name in ${srcDir}/app
|
||||
const customPath = r(this.options.srcDir, 'app', file)
|
||||
const customFileExists = fs.existsSync(customPath)
|
||||
|
||||
return {
|
||||
src: customFileExists
|
||||
? customPath
|
||||
: r(this.options.nuxtAppDir, file),
|
||||
dst: file,
|
||||
custom: customFileExists
|
||||
}
|
||||
}).filter(i => !!i)
|
||||
|
||||
// -- Custom templates --
|
||||
// Add custom template files
|
||||
templatesFiles = templatesFiles.concat(this.options.build.templates.map(t => {
|
||||
return Object.assign({
|
||||
src: r(this.options.srcDir, t.src || t),
|
||||
dst: t.dst || basename(t.src || t),
|
||||
custom: true
|
||||
}, t)
|
||||
}))
|
||||
|
||||
await this.applyPluginsAsync('generate', { builder: this, templatesFiles, templateVars })
|
||||
|
||||
// Interpret and move template files to .nuxt/
|
||||
await Promise.all(templatesFiles.map(async ({ src, dst, options, custom }) => {
|
||||
// Add template to watchers
|
||||
this.options.build.watch.push(src)
|
||||
// Render template to dst
|
||||
const fileContent = await readFile(src, 'utf8')
|
||||
const template = _.template(fileContent, {
|
||||
imports: {
|
||||
serialize,
|
||||
hash,
|
||||
r,
|
||||
wp,
|
||||
relativeToBuild: this.relativeToBuild
|
||||
}
|
||||
})
|
||||
const content = template(Object.assign({}, templateVars, {
|
||||
options: options || {},
|
||||
custom,
|
||||
src,
|
||||
dst
|
||||
}))
|
||||
const path = r(this.options.buildDir, dst)
|
||||
// Ensure parent dir exits
|
||||
await mkdirp(dirname(path))
|
||||
// Write file
|
||||
await writeFile(path, content, 'utf8')
|
||||
// Fix webpack loop (https://github.com/webpack/watchpack/issues/25#issuecomment-287789288)
|
||||
const dateFS = Date.now() / 1000 - 1000
|
||||
return utimes(path, dateFS, dateFS)
|
||||
}))
|
||||
|
||||
await this.applyPluginsAsync('generated', this)
|
||||
}
|
||||
|
||||
async webpackBuild () {
|
||||
debug('Building files...')
|
||||
const compilersOptions = []
|
||||
|
||||
// Client
|
||||
const clientConfig = clientWebpackConfig.call(this)
|
||||
compilersOptions.push(clientConfig)
|
||||
|
||||
// Server
|
||||
const serverConfig = serverWebpackConfig.call(this)
|
||||
if (this.options.build.ssr) {
|
||||
compilersOptions.push(serverConfig)
|
||||
}
|
||||
|
||||
// Alias plugins to their real path
|
||||
this.plugins.forEach(p => {
|
||||
const src = this.relativeToBuild(p.src)
|
||||
|
||||
// Client config
|
||||
if (!clientConfig.resolve.alias[p.name]) {
|
||||
clientConfig.resolve.alias[p.name] = src
|
||||
}
|
||||
|
||||
// Server config
|
||||
if (!serverConfig.resolve.alias[p.name]) {
|
||||
// Alias to noop for ssr:false plugins
|
||||
serverConfig.resolve.alias[p.name] = p.ssr ? src : './empty.js'
|
||||
}
|
||||
})
|
||||
|
||||
// Simulate webpack multi compiler interface
|
||||
// Separate compilers are simpler, safer and faster
|
||||
this.compiler = { compilers: [] }
|
||||
this.compiler.plugin = (...args) => {
|
||||
this.compiler.compilers.forEach(compiler => {
|
||||
compiler.plugin(...args)
|
||||
})
|
||||
}
|
||||
|
||||
// Initialize shared FS and Cache
|
||||
const sharedFS = this.options.dev && new MFS()
|
||||
const sharedCache = {}
|
||||
|
||||
// Initialize compilers
|
||||
compilersOptions.forEach(compilersOption => {
|
||||
const compiler = webpack(compilersOption)
|
||||
if (sharedFS) {
|
||||
compiler.outputFileSystem = sharedFS
|
||||
}
|
||||
compiler.cache = sharedCache
|
||||
this.compiler.compilers.push(compiler)
|
||||
})
|
||||
|
||||
// Access to compilers with name
|
||||
this.compiler.compilers.forEach(compiler => {
|
||||
if (compiler.name) {
|
||||
this.compiler[compiler.name] = compiler
|
||||
}
|
||||
})
|
||||
|
||||
// Run after each compile
|
||||
this.compiler.plugin('done', async stats => {
|
||||
// Don't reload failed builds
|
||||
/* istanbul ignore if */
|
||||
if (stats.hasErrors() || stats.hasWarnings()) {
|
||||
return
|
||||
}
|
||||
// Reload renderer if available
|
||||
if (this.nuxt.renderer) {
|
||||
this.nuxt.renderer.loadResources(sharedFS || fs)
|
||||
}
|
||||
|
||||
await this.applyPluginsAsync('done', { builder: this, stats })
|
||||
})
|
||||
|
||||
// Add dev Stuff
|
||||
if (this.options.dev) {
|
||||
this.webpackDev()
|
||||
}
|
||||
|
||||
await this.applyPluginsAsync('compile', { builder: this, compiler: this.compiler })
|
||||
|
||||
// Start Builds
|
||||
await parallel(this.compiler.compilers, compiler => new Promise((resolve, reject) => {
|
||||
if (this.options.dev) {
|
||||
// --- Dev Build ---
|
||||
if (compiler.options.name === 'client') {
|
||||
// Client watch is started by dev-middleware
|
||||
resolve()
|
||||
} else {
|
||||
// Build and watch for changes
|
||||
compiler.watch(this.options.watchers.webpack, (err) => {
|
||||
/* istanbul ignore if */
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// --- Production Build ---
|
||||
compiler.run((err, stats) => {
|
||||
/* istanbul ignore if */
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
if (err) return console.error(err) // eslint-disable-line no-console
|
||||
// Show build stats for production
|
||||
console.log(stats.toString(this.webpackStats)) // eslint-disable-line no-console
|
||||
/* istanbul ignore if */
|
||||
if (stats.hasErrors()) {
|
||||
return reject(new Error('Webpack build exited with errors'))
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
}))
|
||||
|
||||
await this.applyPluginsAsync('compiled', this)
|
||||
}
|
||||
|
||||
webpackDev () {
|
||||
debug('Adding webpack middleware...')
|
||||
|
||||
// Create webpack dev middleware
|
||||
this.webpackDevMiddleware = pify(webpackDevMiddleware(this.compiler.client, Object.assign({
|
||||
publicPath: this.options.build.publicPath,
|
||||
stats: this.webpackStats,
|
||||
noInfo: true,
|
||||
quiet: true,
|
||||
watchOptions: this.options.watchers.webpack
|
||||
}, this.options.build.devMiddleware)))
|
||||
|
||||
this.webpackHotMiddleware = pify(webpackHotMiddleware(this.compiler.client, Object.assign({
|
||||
log: false,
|
||||
heartbeat: 2500
|
||||
}, this.options.build.hotMiddleware)))
|
||||
|
||||
// Inject to renderer instance
|
||||
if (this.nuxt.renderer) {
|
||||
this.nuxt.renderer.webpackDevMiddleware = this.webpackDevMiddleware
|
||||
this.nuxt.renderer.webpackHotMiddleware = this.webpackHotMiddleware
|
||||
}
|
||||
|
||||
// Stop webpack middleware on nuxt.close()
|
||||
this.nuxt.plugin('close', () => new Promise(resolve => {
|
||||
this.webpackDevMiddleware.close(() => resolve())
|
||||
}))
|
||||
|
||||
// Start watching files
|
||||
this.watchFiles()
|
||||
}
|
||||
|
||||
watchFiles () {
|
||||
const patterns = [
|
||||
r(this.options.srcDir, 'layouts'),
|
||||
r(this.options.srcDir, 'store'),
|
||||
r(this.options.srcDir, 'middleware'),
|
||||
r(this.options.srcDir, 'layouts/*.vue'),
|
||||
r(this.options.srcDir, 'layouts/**/*.vue')
|
||||
]
|
||||
if (this._nuxtPages) {
|
||||
patterns.push(r(this.options.srcDir, 'pages'))
|
||||
patterns.push(r(this.options.srcDir, 'pages/*.vue'))
|
||||
patterns.push(r(this.options.srcDir, 'pages/**/*.vue'))
|
||||
}
|
||||
const options = Object.assign({}, this.options.watchers.chokidar, {
|
||||
ignoreInitial: true
|
||||
})
|
||||
/* istanbul ignore next */
|
||||
const refreshFiles = _.debounce(() => this.generateRoutesAndFiles(), 200)
|
||||
|
||||
// Watch for src Files
|
||||
let filesWatcher = chokidar.watch(patterns, options)
|
||||
.on('add', refreshFiles)
|
||||
.on('unlink', refreshFiles)
|
||||
|
||||
// Watch for custom provided files
|
||||
let customFilesWatcher = chokidar.watch(_.uniq(this.options.build.watch), options)
|
||||
.on('change', refreshFiles)
|
||||
|
||||
// Stop watching on nuxt.close()
|
||||
this.nuxt.plugin('close', () => {
|
||||
filesWatcher.close()
|
||||
customFilesWatcher.close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const STATUS = {
|
||||
INITIAL: 1,
|
||||
BUILD_DONE: 2,
|
||||
BUILDING: 3
|
||||
}
|
169
lib/builder/generator.js
Normal file
169
lib/builder/generator.js
Normal file
@ -0,0 +1,169 @@
|
||||
import fs from 'fs'
|
||||
import { copy, remove, writeFile, mkdirp } from 'fs-extra'
|
||||
import _ from 'lodash'
|
||||
import { resolve, join, dirname, sep } from 'path'
|
||||
import { minify } from 'html-minifier'
|
||||
import Tapable from 'tappable'
|
||||
import { isUrl, promisifyRoute, waitFor, flatRoutes } from 'utils'
|
||||
import Debug from 'debug'
|
||||
|
||||
const debug = Debug('nuxt:generate')
|
||||
|
||||
export default class Generator extends Tapable {
|
||||
constructor (nuxt, builder) {
|
||||
super()
|
||||
this.nuxt = nuxt
|
||||
this.options = nuxt.options
|
||||
this.builder = builder
|
||||
|
||||
// Set variables
|
||||
this.generateRoutes = resolve(this.options.srcDir, 'static')
|
||||
this.srcBuiltPath = resolve(this.options.buildDir, 'dist')
|
||||
this.distPath = resolve(this.options.rootDir, this.options.generate.dir)
|
||||
this.distNuxtPath = join(this.distPath, (isUrl(this.options.build.publicPath) ? '' : this.options.build.publicPath))
|
||||
}
|
||||
|
||||
async generate ({ build = true, init = true } = {}) {
|
||||
const s = Date.now()
|
||||
let errors = []
|
||||
|
||||
// Wait for nuxt be ready
|
||||
await this.nuxt.ready()
|
||||
|
||||
// Start build process
|
||||
if (this.builder && build) {
|
||||
await this.builder.build()
|
||||
}
|
||||
|
||||
await this.nuxt.applyPluginsAsync('generate', this)
|
||||
|
||||
// Initialize dist directory
|
||||
if (init) {
|
||||
await this.initDist()
|
||||
}
|
||||
|
||||
// Resolve config.generate.routes promises before generating the routes
|
||||
let generateRoutes = []
|
||||
if (this.options.router.mode !== 'hash') {
|
||||
try {
|
||||
console.log('Generating routes') // eslint-disable-line no-console
|
||||
generateRoutes = await promisifyRoute(this.options.generate.routes || [])
|
||||
await this.applyPluginsAsync('generateRoutes', {generator: this, generateRoutes})
|
||||
} catch (e) {
|
||||
console.error('Could not resolve routes') // eslint-disable-line no-console
|
||||
console.error(e) // eslint-disable-line no-console
|
||||
throw e // eslint-disable-line no-unreachable
|
||||
}
|
||||
}
|
||||
|
||||
// Generate only index.html for router.mode = 'hash'
|
||||
let routes = (this.options.router.mode === 'hash') ? ['/'] : flatRoutes(this.options.router.routes)
|
||||
routes = this.decorateWithPayloads(routes, generateRoutes)
|
||||
|
||||
await this.applyPluginsAsync('generate', {generator: this, routes})
|
||||
|
||||
// Start generate process
|
||||
while (routes.length) {
|
||||
let n = 0
|
||||
await Promise.all(routes.splice(0, this.options.generate.concurrency).map(async ({ route, payload }) => {
|
||||
await waitFor(n++ * this.options.generate.interval)
|
||||
await this.generateRoute({route, payload, errors})
|
||||
}))
|
||||
}
|
||||
|
||||
const duration = Math.round((Date.now() - s) / 100) / 10
|
||||
debug(`HTML Files generated in ${duration}s`)
|
||||
|
||||
if (errors.length) {
|
||||
const report = errors.map(({ type, route, error }) => {
|
||||
/* istanbul ignore if */
|
||||
if (type === 'unhandled') {
|
||||
return `Route: '${route}'\n${error.stack}`
|
||||
} else {
|
||||
return `Route: '${route}' thrown an error: \n` + JSON.stringify(error)
|
||||
}
|
||||
})
|
||||
console.error('==== Error report ==== \n' + report.join('\n\n')) // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
await this.applyPluginsAsync('generated', this)
|
||||
|
||||
return { duration, errors }
|
||||
}
|
||||
|
||||
async initDist () {
|
||||
// Clean destination folder
|
||||
await remove(this.distPath)
|
||||
debug('Destination folder cleaned')
|
||||
|
||||
// Copy static and built files
|
||||
/* istanbul ignore if */
|
||||
if (fs.existsSync(this.generateRoutes)) {
|
||||
await copy(this.generateRoutes, this.distPath)
|
||||
}
|
||||
await copy(this.srcBuiltPath, this.distNuxtPath)
|
||||
|
||||
// Add .nojekyll file to let Github Pages add the _nuxt/ folder
|
||||
// https://help.github.com/articles/files-that-start-with-an-underscore-are-missing/
|
||||
const nojekyllPath = resolve(this.distPath, '.nojekyll')
|
||||
writeFile(nojekyllPath, '')
|
||||
|
||||
debug('Static & build files copied')
|
||||
}
|
||||
|
||||
decorateWithPayloads (routes, generateRoutes) {
|
||||
let routeMap = {}
|
||||
// Fill routeMap for known routes
|
||||
routes.forEach((route) => {
|
||||
routeMap[route] = {
|
||||
route,
|
||||
payload: null
|
||||
}
|
||||
})
|
||||
// Fill routeMap with given generate.routes
|
||||
generateRoutes.forEach((route) => {
|
||||
// route is either a string or like {route : "/my_route/1"}
|
||||
const path = _.isString(route) ? route : route.route
|
||||
routeMap[path] = {
|
||||
route: path,
|
||||
payload: route.payload || null
|
||||
}
|
||||
})
|
||||
return _.values(routeMap)
|
||||
}
|
||||
|
||||
async generateRoute ({route, payload = {}, errors = []}) {
|
||||
let html
|
||||
|
||||
try {
|
||||
const res = await this.nuxt.renderer.renderRoute(route, { _generate: true, payload })
|
||||
html = res.html
|
||||
if (res.error) {
|
||||
errors.push({ type: 'handled', route, error: res.error })
|
||||
}
|
||||
} catch (err) {
|
||||
/* istanbul ignore next */
|
||||
return errors.push({ type: 'unhandled', route, error: err })
|
||||
}
|
||||
|
||||
if (this.options.generate.minify) {
|
||||
try {
|
||||
html = minify(html, this.options.generate.minify)
|
||||
} catch (err) /* istanbul ignore next */ {
|
||||
const minifyErr = new Error(`HTML minification failed. Make sure the route generates valid HTML. Failed HTML:\n ${html}`)
|
||||
errors.push({ type: 'unhandled', route, error: minifyErr })
|
||||
}
|
||||
}
|
||||
|
||||
let path = join(route, sep, 'index.html') // /about -> /about/index.html
|
||||
path = (path === '/404/index.html') ? '/404.html' : path // /404 -> /404.html
|
||||
debug('Generate file: ' + path)
|
||||
path = join(this.distPath, path)
|
||||
|
||||
// Make sure the sub folders are created
|
||||
await mkdirp(dirname(path))
|
||||
await writeFile(path, html, 'utf8')
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
7
lib/builder/index.js
Executable file
7
lib/builder/index.js
Executable file
@ -0,0 +1,7 @@
|
||||
import Builder from './builder'
|
||||
import Generator from './generator'
|
||||
|
||||
export default {
|
||||
Builder,
|
||||
Generator
|
||||
}
|
145
lib/builder/webpack/base.config.js
Normal file
145
lib/builder/webpack/base.config.js
Normal file
@ -0,0 +1,145 @@
|
||||
import ExtractTextPlugin from 'extract-text-webpack-plugin'
|
||||
import { defaults, cloneDeep } from 'lodash'
|
||||
import { join, resolve } from 'path'
|
||||
import webpack from 'webpack'
|
||||
import { isUrl, urlJoin } from 'utils'
|
||||
import autoprefixer from 'autoprefixer'
|
||||
import vueLoaderConfig from './vue-loader.config'
|
||||
import { styleLoader, extractStyles } from './helpers'
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Webpack Shared Config
|
||||
|
|
||||
| This is the config which is extended by the server and client
|
||||
| webpack config files
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
export default function webpackBaseConfig ({ isClient, isServer }) {
|
||||
const nodeModulesDir = join(__dirname, '..', 'node_modules')
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (!Array.isArray(this.options.build.postcss)) {
|
||||
this.options.build.postcss = [
|
||||
autoprefixer({
|
||||
browsers: ['last 3 versions']
|
||||
})
|
||||
]
|
||||
}
|
||||
|
||||
const config = {
|
||||
devtool: this.options.dev ? 'cheap-module-source-map' : 'nosources-source-map',
|
||||
entry: {
|
||||
vendor: ['vue', 'vue-router', 'vue-meta']
|
||||
},
|
||||
output: {
|
||||
path: resolve(this.options.buildDir, 'dist'),
|
||||
filename: this.options.build.filenames.app,
|
||||
publicPath: (isUrl(this.options.build.publicPath)
|
||||
? this.options.build.publicPath
|
||||
: urlJoin(this.options.router.base, this.options.build.publicPath))
|
||||
},
|
||||
performance: {
|
||||
maxEntrypointSize: 1000000,
|
||||
maxAssetSize: 300000,
|
||||
hints: this.options.dev ? false : 'warning'
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.json', '.vue', '.ts'],
|
||||
alias: {
|
||||
'~': join(this.options.srcDir),
|
||||
'~~': join(this.options.rootDir),
|
||||
'@': join(this.options.srcDir),
|
||||
'@@': join(this.options.rootDir),
|
||||
'static': join(this.options.srcDir, 'static'), // use in template with <img src="~static/nuxt.png" />
|
||||
'assets': join(this.options.srcDir, 'assets') // use in template with <img src="~assets/nuxt.png" />
|
||||
},
|
||||
modules: [
|
||||
join(this.options.rootDir, 'node_modules'),
|
||||
nodeModulesDir
|
||||
]
|
||||
},
|
||||
resolveLoader: {
|
||||
modules: [
|
||||
join(this.options.rootDir, 'node_modules'),
|
||||
nodeModulesDir
|
||||
]
|
||||
},
|
||||
module: {
|
||||
noParse: /es6-promise\.js$/, // avoid webpack shimming process
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
query: vueLoaderConfig.call(this, { isClient, isServer })
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/,
|
||||
query: defaults(this.options.build.babel, {
|
||||
presets: ['vue-app'],
|
||||
babelrc: false,
|
||||
cacheDirectory: !!this.options.dev
|
||||
})
|
||||
},
|
||||
{ test: /\.css$/, use: styleLoader.call(this, 'css') },
|
||||
{ test: /\.less$/, use: styleLoader.call(this, 'less', 'less-loader') },
|
||||
{ test: /\.sass$/, use: styleLoader.call(this, 'sass', 'sass-loader?indentedSyntax&sourceMap') },
|
||||
{ test: /\.scss$/, use: styleLoader.call(this, 'sass', 'sass-loader?sourceMap') },
|
||||
{ test: /\.styl(us)?$/, use: styleLoader.call(this, 'stylus', 'stylus-loader') },
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg)$/,
|
||||
loader: 'url-loader',
|
||||
query: {
|
||||
limit: 1000, // 1KO
|
||||
name: 'img/[name].[hash:7].[ext]'
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
query: {
|
||||
limit: 1000, // 1 KO
|
||||
name: 'fonts/[name].[hash:7].[ext]'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: this.options.build.plugins
|
||||
}
|
||||
|
||||
// CSS extraction
|
||||
if (extractStyles.call(this)) {
|
||||
config.plugins.push(
|
||||
new ExtractTextPlugin({ filename: this.options.build.filenames.css })
|
||||
)
|
||||
}
|
||||
|
||||
// --------------------------------------
|
||||
// Dev specific config
|
||||
// --------------------------------------
|
||||
if (this.options.dev) {
|
||||
//
|
||||
}
|
||||
|
||||
// --------------------------------------
|
||||
// Production specific config
|
||||
// --------------------------------------
|
||||
if (!this.options.dev) {
|
||||
// This is needed in webpack 2 for minify CSS
|
||||
config.plugins.push(
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: true
|
||||
})
|
||||
)
|
||||
|
||||
// Scope Hoisting
|
||||
// config.plugins.push(
|
||||
// new webpack.optimize.ModuleConcatenationPlugin()
|
||||
// )
|
||||
}
|
||||
|
||||
// Clone deep avoid leaking config between Client and Server
|
||||
return cloneDeep(config)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user