Add nuxt-build and nuxt-start, build:false and dev option

This commit is contained in:
Sébastien Chopin 2016-11-09 23:59:41 +01:00
parent 78d81228f4
commit 18a2b57655
20 changed files with 343 additions and 227 deletions

View File

@ -56,7 +56,7 @@ So far, we get:
## Using nuxt.js programmatically ## Using nuxt.js programmatically
Nuxt is built on the top of ES2015, which makes the code more enjoyable and cleaner to read. It doesn't make use of any transpilers and depends upon Core V8 implemented features. Nuxt is built on the top of ES2015, which makes the code more enjoyable and cleaner to read. It doesn't make use of any transpilers and depends upon Core V8 implemented features.
For these reasons, Nuxt.js targets Node.js `4.0` or higher (you might want to launch node with the `--harmony-proxies` flag if you running `node <= 6.5.0` ) For these reasons, nuxt.js targets Node.js `4.0` or higher (you might want to launch node with the `--harmony-proxies` flag if you running `node <= 6.5.0` )
```js ```js
const Nuxt = require('nuxt') const Nuxt = require('nuxt')
@ -115,3 +115,30 @@ cd node_modules/nuxt/
bin/nuxt examples/hello-world bin/nuxt examples/hello-world
# Go to http://localhost:3000 # Go to http://localhost:3000
``` ```
## Production deployment
To deploy, instead of running next, you probably want to build ahead of time. Therefore, building and starting are separate commands:
```bash
nuxt build
nuxt start
```
For example, to deploy with [`now`](https://zeit.co/now) a `package.json` like follows is recommended:
```json
{
"name": "my-app",
"dependencies": {
"next": "latest"
},
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start"
}
}
```
Then run `now` and enjoy!
Note: we recommend putting `.nuxt` in `.npmignore` or `.gitignore`.

View File

@ -3,10 +3,12 @@
const { join } = require('path') const { join } = require('path')
const { spawn } = require('cross-spawn') const { spawn } = require('cross-spawn')
const defaultCommand = 'start' const defaultCommand = 'dev'
const commands = new Set([ const commands = new Set([
defaultCommand, defaultCommand,
'init' 'init',
'build',
'start'
]) ])
let cmd = process.argv[2] let cmd = process.argv[2]

27
bin/nuxt-build Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env node
const fs = require('fs')
const Nuxt = require('../')
const { resolve } = require('path')
const rootDir = resolve(process.argv.slice(2)[0] || '.')
const nuxtConfigFile = resolve(rootDir, 'nuxt.config.js')
let options = {}
if (fs.existsSync(nuxtConfigFile)) {
options = require(nuxtConfigFile)
}
if (typeof options.rootDir !== 'string') {
options.rootDir = rootDir
}
options.dev = false // Create production build when calling `nuxt build`
console.log('[nuxt] Building...')
new Nuxt(options)
.then((nuxt) => {
console.log('[nuxt] Building done')
})
.catch((err) => {
console.error(err)
process.exit()
})

26
bin/nuxt-dev Executable file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env node
const fs = require('fs')
const Nuxt = require('../')
const Server = require('../lib/server')
const { resolve } = require('path')
const rootDir = resolve(process.argv.slice(2)[0] || '.')
const nuxtConfigFile = resolve(rootDir, 'nuxt.config.js')
let options = {}
if (fs.existsSync(nuxtConfigFile)) {
options = require(nuxtConfigFile)
}
if (typeof options.rootDir !== 'string') {
options.rootDir = rootDir
}
new Nuxt(options)
.then((nuxt) => {
new Server(nuxt)
.listen(process.env.PORT, process.env.HOST)
})
.catch((err) => {
console.error(err)
process.exit()
})

View File

@ -1,11 +1,8 @@
#!/usr/bin/env node #!/usr/bin/env node
const http = require('http')
const co = require('co')
const fs = require('fs') const fs = require('fs')
const pify = require('pify')
const serveStatic = require('serve-static')
const Nuxt = require('../') const Nuxt = require('../')
const Server = require('../lib/server')
const { resolve } = require('path') const { resolve } = require('path')
const rootDir = resolve(process.argv.slice(2)[0] || '.') const rootDir = resolve(process.argv.slice(2)[0] || '.')
@ -18,6 +15,9 @@ if (typeof options.rootDir !== 'string') {
options.rootDir = rootDir options.rootDir = rootDir
} }
options.build = false // Disable building
options.dev = false // Force production mode (no webpack middlewares called)
new Nuxt(options) new Nuxt(options)
.then((nuxt) => { .then((nuxt) => {
new Server(nuxt) new Server(nuxt)
@ -27,43 +27,3 @@ new Nuxt(options)
console.error(err) console.error(err)
process.exit() process.exit()
}) })
class Server {
constructor (nuxt) {
this.server = http.createServer(this.handle.bind(this))
this.serveStatic = pify(serveStatic(resolve(rootDir, 'static')))
this.nuxt = nuxt
return this
}
handle (req, res) {
const method = req.method.toUpperCase()
const self = this
if (method !== 'GET' && method !== 'HEAD') {
return this.nuxt.render(req, res)
}
co(function * () {
if (req.url.includes('/static/')) {
const url = req.url
req.url = req.url.replace('/static/', '/')
yield self.serveStatic(req, res)
req.url = url
}
})
.then(() => {
// File not found
this.nuxt.render(req, res)
})
}
listen (port, host) {
host = host || 'localhost'
port = port || 3000
this.server.listen(port, host, () => {
console.log('Ready on http://%s:%s', host, port)
})
}
}

View File

@ -27,19 +27,20 @@ module.exports = {
To see the demo working: To see the demo working:
```bash ```bash
npm install npm install
npm start npm run dev
``` ```
Go to [http://localhost:8080](http://localhost:8080) and navigate inside the app. Go to [http://localhost:3000](http://localhost:3000) and navigate inside the app.
## Production ## Production
In production, they will be minified and extracted in a file named `styles.css` and added in the `<head>` of the page. In production, they will be minified and extracted in a file named `styles.css` and added in the `<head>` of the page.
To launch the demo in production mode so you can see the ``<head>` populated with the `<link>` tag: To launch the demo in production mode so you can see the `<head>` populated with the `<link>` tag:
```bash ```bash
NODE_ENV=production npm start npm run build
npm start
``` ```
Go to [http://localhost:8080](http://localhost:8080) and check the source code. Go to [http://localhost:3000](http://localhost:3000) and check the source code.

View File

@ -9,6 +9,8 @@
"sass-loader": "^4.0.2" "sass-loader": "^4.0.2"
}, },
"scripts": { "scripts": {
"start": "nuxt" "dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start"
} }
} }

View File

@ -1,4 +1,4 @@
# Using external modules and plugings with Nuxt.js # Using external modules and plugings with nuxt.js
## Configuration: `build.vendor` ## Configuration: `build.vendor`

View File

@ -11,10 +11,10 @@ let server = null
// Init nuxt.js and create server listening on localhost:4000 // Init nuxt.js and create server listening on localhost:4000
test.before('Init nuxt.js', (t) => { test.before('Init nuxt.js', (t) => {
process.env.NODE_ENV = 'test'
const Nuxt = require('../../../') const Nuxt = require('../../../')
const options = { const options = {
rootDir: resolve(__dirname, '..') rootDir: resolve(__dirname, '..'),
dev: false
} }
return new Nuxt(options) return new Nuxt(options)
.then(function (_nuxt) { .then(function (_nuxt) {
@ -65,9 +65,11 @@ test('Route / exits and render HTML', async t => {
*/ */
test('Route / exits and render HTML', async t => { test('Route / exits and render HTML', async t => {
const window = await renderAndGetWindow('/') const window = await renderAndGetWindow('/')
t.is(window.document.querySelector('p').textContent, 'Hello world!') const element = window.document.querySelector('.red-color')
t.is(window.document.querySelector('p').className, 'red-color') t.not(element, null)
t.true(window.document.querySelectorAll('style')[2].textContent.includes('.red-color {\n color: red;\n}')) t.is(element.textContent, 'Hello world!')
t.is(element.className, 'red-color')
t.is(window.getComputedStyle(element).color, 'red')
}) })
// Close server and ask nuxt to stop listening to file changes // Close server and ask nuxt to stop listening to file changes

View File

@ -176,5 +176,5 @@ Promise.all(resolveComponents)
} }
}) })
.catch((err) => { .catch((err) => {
console.error('[Nuxt.js] Cannot load components', err) console.error('[nuxt.js] Cannot load components', err)
}) })

View File

@ -4,7 +4,7 @@ import { pick } from 'lodash'
import { app, router<%= (store ? ', store' : '') %> } from './index' import { app, router<%= (store ? ', store' : '') %> } from './index'
import { getMatchedComponents, getContext } from './utils' import { getMatchedComponents, getContext } from './utils'
const isDev = process.env.NODE_ENV !== 'production' const isDev = <%= isDev %>
const _app = new Vue(app) const _app = new Vue(app)
// This exported function will be called by `bundleRenderer`. // This exported function will be called by `bundleRenderer`.
@ -54,7 +54,7 @@ export default context => {
})) }))
.then((res) => { .then((res) => {
<% if (isDev) { %> <% if (isDev) { %>
debug('Data fetch ' + context.req.url + ': ' + (Date.now() - s) + 'ms') debug('Data fetching ' + context.req.url + ': ' + (Date.now() - s) + 'ms')
<% } %> <% } %>
// datas are the first row of each // datas are the first row of each
context.nuxt.data = res.map((tab) => tab[0]) context.nuxt.data = res.map((tab) => tab[0])

View File

@ -42,21 +42,25 @@ const defaultsLoaders = [
] ]
module.exports = function * () { module.exports = function * () {
if (this.options.build === false) { const noBuild = this.options.build === false
return Promise.resolve()
}
// Defaults build options // Defaults build options
if (this.options.build && Array.isArray(this.options.build.loaders)) { if (this.options.build && Array.isArray(this.options.build.loaders)) {
this.options.build = _.defaultsDeep(this.options.build, defaults) this.options.build = _.defaultsDeep(this.options.build, defaults)
} else { } else {
this.options.build = _.defaultsDeep(this.options.build, defaults, { loaders: defaultsLoaders }) this.options.build = _.defaultsDeep(this.options.build, defaults, { loaders: defaultsLoaders })
} }
if (noBuild) {
const serverConfig = getWebpackServerConfig.call(this)
const bundlePath = join(serverConfig.output.path, serverConfig.output.filename)
createRenderer.call(this, fs.readFileSync(bundlePath, 'utf8'))
return Promise.resolve()
}
/* /*
** Check if pages dir exists and warn if not ** Check if pages dir exists and warn if not
*/ */
if (!fs.existsSync(join(this.dir, 'pages'))) { if (!fs.existsSync(join(this.dir, 'pages'))) {
if (fs.existsSync(join(this.dir, '..', 'pages'))) { if (fs.existsSync(join(this.dir, '..', 'pages'))) {
console.error('> No `pages` directory found. Did you mean to run `next` in the parent (`../`) directory?') console.error('> No `pages` directory found. Did you mean to run `nuxt` in the parent (`../`) directory?')
} else { } else {
console.error('> Couldn\'t find a `pages` directory. Please create one under the project root') console.error('> Couldn\'t find a `pages` directory. Please create one under the project root')
} }
@ -75,9 +79,11 @@ module.exports = function * () {
/* /*
** Create .nuxt/, .nuxt/components and .nuxt/dist folders ** Create .nuxt/, .nuxt/components and .nuxt/dist folders
*/ */
yield del(r(this.dir, '.nuxt'), { force: process.env.NODE_ENV === 'test' }) try {
yield del(r(this.dir, '.nuxt'))
} catch (e) {}
yield mkdirp(r(this.dir, '.nuxt/components')) yield mkdirp(r(this.dir, '.nuxt/components'))
if (this.isProd) { if (!this.dev) {
yield mkdirp(r(this.dir, '.nuxt/dist')) yield mkdirp(r(this.dir, '.nuxt/dist'))
} }
/* /*
@ -116,24 +122,24 @@ module.exports = function * () {
'components/Loading.vue' 'components/Loading.vue'
] ]
let templateVars = { let templateVars = {
isDev: this.isDev, isDev: this.dev,
store: this.options.store, store: this.options.store,
css: this.options.css, css: this.options.css,
plugins: this.options.plugins.map((p) => r(this.dir, p)), plugins: this.options.plugins.map((p) => r(this.dir, p)),
loading: (this.options.loading === 'string' ? r(this.dir, this.options.loading) : this.options.loading), loading: (this.options.loading === 'string' ? r(this.dir, this.options.loading) : this.options.loading),
components: { components: {
Loading: r(__dirname, '..', 'app', 'components', 'Loading.vue'), Loading: r(__dirname, '..', 'app', 'components', 'Loading.vue'),
ErrorPage: r(__dirname, '..', '..', 'pages', (this.isDev ? '_error-debug.vue' : '_error.vue')) ErrorPage: r(__dirname, '..', '..', 'pages', (this.dev ? '_error-debug.vue' : '_error.vue'))
}, },
routes: this.options.routes routes: this.options.routes
} }
if (this.options.store) { if (this.options.store) {
templateVars.storePath = r(this.dir, 'store') templateVars.storePath = r(this.dir, 'store')
} }
if (this.isDev && files.includes('pages/_error-debug.vue')) { if (this.dev && files.includes('pages/_error-debug.vue')) {
templateVars.components.ErrorPage = r(this.dir, 'pages/_error-debug.vue') templateVars.components.ErrorPage = r(this.dir, 'pages/_error-debug.vue')
} }
if (!this.isDev && files.includes('pages/_error.vue')) { if (!this.dev && files.includes('pages/_error.vue')) {
templateVars.components.ErrorPage = r(this.dir, 'pages/_error.vue') templateVars.components.ErrorPage = r(this.dir, 'pages/_error.vue')
} }
const readFile = pify(fs.readFile) const readFile = pify(fs.readFile)
@ -151,7 +157,7 @@ module.exports = function * () {
/* /*
** Generate .nuxt/dist/ files ** Generate .nuxt/dist/ files
*/ */
if (this.isDev) { if (this.dev) {
debug('Adding webpack middlewares...') debug('Adding webpack middlewares...')
createWebpackMiddlewares.call(this) createWebpackMiddlewares.call(this)
webpackWatchAndUpdate.call(this) webpackWatchAndUpdate.call(this)
@ -164,66 +170,14 @@ module.exports = function * () {
} }
} }
function addGlobalWebpackConfig (config) {
const nodeModulesDir = join(__dirname, '..', '..', 'node_modules')
config.resolve = {
modules: [
nodeModulesDir,
join(this.dir, 'node_modules')
]
}
config.resolveLoader = {
modules: [
nodeModulesDir,
join(this.dir, 'node_modules')
]
}
config.module.rules = config.module.rules.concat(this.options.build.loaders)
return config
}
function getWebpackClientConfig () { function getWebpackClientConfig () {
var config = require(r(__dirname, 'webpack', 'client.config.js')) const clientConfigPath = r(__dirname, 'webpack', 'client.config.js')
config = _.cloneDeep(config) return require(clientConfigPath).call(this)
// Entry
config.entry.app = r(this.dir, '.nuxt', 'client.js')
// Add vendors
if (this.options.store) config.entry.vendor.push('vuex')
config.entry.vendor = config.entry.vendor.concat(this.options.build.vendor)
// extract vendor chunks for better caching
config.plugins.push(
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: this.options.build.filenames.vendor
})
)
// Output
config.output.path = r(this.dir, '.nuxt', 'dist')
config.output.filename = this.options.build.filenames.app
// Extract text plugin
if (this.isProd) {
const ExtractTextPlugin = require('extract-text-webpack-plugin')
let plugin = config.plugins.find((plugin) => plugin instanceof ExtractTextPlugin)
if (plugin) plugin.filename = this.options.build.filenames.css
}
return addGlobalWebpackConfig.call(this, config)
} }
function getWebpackServerConfig () { function getWebpackServerConfig () {
var config = require(r(__dirname, 'webpack', 'server.config.js')) const configServerPath = r(__dirname, 'webpack', 'server.config.js')
config = _.cloneDeep(config) return require(configServerPath).call(this)
// Entry
config.entry = r(this.dir, '.nuxt', 'server.js')
// Output
config.output.path = r(this.dir, '.nuxt', 'dist')
// Externals
config.externals = Object.keys(require(r(__dirname, '..', '..', 'package.json')).dependencies || {})
const projectPackageJson = r(this.dir, 'package.json')
if (fs.existsSync(projectPackageJson)) {
config.externals = config.externals.concat(Object.keys(require(r(this.dir, 'package.json')).dependencies || {}))
}
config.externals = _.uniq(config.externals)
return addGlobalWebpackConfig.call(this, config)
} }
function createWebpackMiddlewares () { function createWebpackMiddlewares () {
@ -270,7 +224,7 @@ function webpackRunClient () {
const serverCompiler = webpack(clientConfig) const serverCompiler = webpack(clientConfig)
serverCompiler.run((err, stats) => { serverCompiler.run((err, stats) => {
if (err) return reject(err) if (err) return reject(err)
console.log('[webpack:build:client]\n', stats.toString({ chunks: false, colors: true })) console.log('[nuxt:build:client]\n', stats.toString({ chunks: false, colors: true }))
resolve() resolve()
}) })
}) })
@ -282,7 +236,7 @@ function webpackRunServer () {
const serverCompiler = webpack(serverConfig) const serverCompiler = webpack(serverConfig)
serverCompiler.run((err, stats) => { serverCompiler.run((err, stats) => {
if (err) return reject(err) if (err) return reject(err)
console.log('[webpack:build:server]\n', stats.toString({ chunks: false, colors: true })) console.log('[nuxt:build:server]\n', stats.toString({ chunks: false, colors: true }))
const bundlePath = join(serverConfig.output.path, serverConfig.output.filename) const bundlePath = join(serverConfig.output.path, serverConfig.output.filename)
createRenderer.call(this, fs.readFileSync(bundlePath, 'utf8')) createRenderer.call(this, fs.readFileSync(bundlePath, 'utf8'))
resolve() resolve()

View File

@ -1,4 +1,5 @@
const vueLoaderConfig = require('./vue-loader.config') const vueLoaderConfig = require('./vue-loader.config')
const { join } = require('path')
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -8,29 +9,49 @@ const vueLoaderConfig = require('./vue-loader.config')
| webpack config files | webpack config files
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
*/ */
module.exports = { module.exports = function () {
devtool: 'source-map', const nodeModulesDir = join(__dirname, '..', '..', '..', 'node_modules')
entry: { let config = {
vendor: ['vue', 'vue-router', 'vue-meta', 'es6-promise', 'es6-object-assign'] devtool: 'source-map',
}, entry: {
output: { vendor: ['vue', 'vue-router', 'vue-meta', 'es6-promise', 'es6-object-assign']
publicPath: '/_nuxt/' },
}, output: {
module: { publicPath: '/_nuxt/'
rules: [ },
{ resolve: {
test: /\.vue$/, modules: [
loader: 'vue', nodeModulesDir,
options: vueLoaderConfig join(this.dir, 'node_modules')
}, ]
{ },
test: /\.js$/, resolveLoader: {
loader: 'babel', modules: [
exclude: /node_modules/, nodeModulesDir,
options: { join(this.dir, 'node_modules')
presets: ['es2015', 'stage-2'] ]
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue',
options: vueLoaderConfig.call(this)
},
{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/,
options: {
presets: ['es2015', 'stage-2']
}
} }
} ]
] }
} }
// Add nuxt build loaders (can be configured in nuxt.config.js)
config.module.rules = config.module.rules.concat(this.options.build.loaders)
// Return config
return config
} }

View File

@ -1,6 +1,7 @@
const webpack = require('webpack') const webpack = require('webpack')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const base = require('./base.config') const base = require('./base.config')
const vueConfig = require('./vue-loader.config') const { resolve } = require('path')
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -12,47 +13,55 @@ const vueConfig = require('./vue-loader.config')
| In production, will generate public/dist/style.css | In production, will generate public/dist/style.css
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
*/ */
module.exports = function () {
let config = base.call(this)
const config = Object.assign({}, base, { // Entry
plugins: (base.plugins || []).concat([ config.entry.app = resolve(this.dir, '.nuxt', 'client.js')
// Add vendors
if (this.options.store) {
config.entry.vendor.push('vuex')
}
config.entry.vendor = config.entry.vendor.concat(this.options.build.vendor)
// Output
config.output.path = resolve(this.dir, '.nuxt', 'dist')
config.output.filename = this.options.build.filenames.app
// Webpack plugins
config.plugins = (config.plugins || []).concat([
// strip comments in Vue code // strip comments in Vue code
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), 'process.env.NODE_ENV': JSON.stringify(this.dev ? 'development' : 'production'),
'process.BROWSER': true 'process.BROWSER': true
}),
// Extract vendor chunks for better caching
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: this.options.build.filenames.vendor
}) })
]) ])
})
if (process.env.NODE_ENV === 'production') { // Production client build
// Use ExtractTextPlugin to extract CSS into a single file if (!this.dev) {
// so it's applied on initial render config.plugins.push(
const ExtractTextPlugin = require('extract-text-webpack-plugin') // Use ExtractTextPlugin to extract CSS into a single file
new ExtractTextPlugin({
// vueConfig is already included in the config via LoaderOptionsPlugin filename: this.options.build.filenames.css,
// here we overwrite the loader config for <style lang='stylus'> allChunks: true
// so they are extracted. }),
vueConfig.loaders.css = ExtractTextPlugin.extract({ loader: 'css-loader' }) // This is needed in webpack 2 for minifying CSS
vueConfig.loaders.scss = ExtractTextPlugin.extract({ loader: 'css-loader!sass-loader', fallbackLoader: 'vue-style-loader' }) new webpack.LoaderOptionsPlugin({
vueConfig.loaders.sass = ExtractTextPlugin.extract({ loader: 'css-loader!sass-loader?indentedSyntax', fallbackLoader: 'vue-style-loader' }) minimize: true
vueConfig.loaders.stylus = ExtractTextPlugin.extract({ loader: 'css-loader!stylus-loader', fallbackLoader: 'vue-style-loader' }) }),
vueConfig.loaders.less = ExtractTextPlugin.extract({ loader: 'css-loader!less-loader', fallbackLoader: 'vue-style-loader' }) // Minify JS
new webpack.optimize.UglifyJsPlugin({
config.plugins.push( compress: {
new ExtractTextPlugin({ warnings: false
filename: 'style.css', }
allChunks: true })
}), )
// this is needed in webpack 2 for minifying CSS }
new webpack.LoaderOptionsPlugin({ return config
minimize: true
}),
// minify JS
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
)
} }
module.exports = config

View File

@ -1,23 +1,44 @@
const webpack = require('webpack') const webpack = require('webpack')
const base = require('./base.config') const base = require('./base.config')
const { uniq } = require('lodash')
const { existsSync } = require('fs')
const { resolve } = require('path')
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Webpack Server Config | Webpack Server Config
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
*/ */
module.exports = Object.assign({}, base, { module.exports = function () {
target: 'node', let config = base.call(this)
devtool: false,
output: Object.assign({}, base.output, { config = Object.assign(config, {
filename: 'server-bundle.js', target: 'node',
libraryTarget: 'commonjs2' devtool: false,
}), entry: resolve(this.dir, '.nuxt', 'server.js'),
plugins: [ output: Object.assign({}, base.output, {
new webpack.DefinePlugin({ path: resolve(this.dir, '.nuxt', 'dist'),
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), filename: 'server-bundle.js',
'process.env.VUE_ENV': '"server"', libraryTarget: 'commonjs2'
'process.BROWSER': false }),
}) plugins: [
] new webpack.DefinePlugin({
}) 'process.env.NODE_ENV': JSON.stringify(this.dev ? 'development' : 'production'),
'process.env.VUE_ENV': '"server"',
'process.BROWSER': false
})
]
})
// Externals
const nuxtPackageJson = require(resolve(__dirname, '..', '..', '..', 'package.json'))
const projectPackageJson = resolve(this.dir, 'package.json')
config.externals = Object.keys(nuxtPackageJson.dependencies || {})
if (existsSync(projectPackageJson)) {
config.externals = config.externals.concat(Object.keys(require(projectPackageJson).dependencies || {}))
}
config.externals = uniq(config.externals)
// Return config
return config
}

View File

@ -1,16 +1,31 @@
module.exports = { module.exports = function () {
postcss: [ let config = {
require('autoprefixer')({ postcss: [
browsers: ['last 3 versions'] require('autoprefixer')({
}) browsers: ['last 3 versions']
], })
loaders: { ],
'js': 'babel-loader?presets[]=es2015&presets[]=stage-2', loaders: {
'postcss': 'vue-style-loader!css-loader', 'js': 'babel-loader?presets[]=es2015&presets[]=stage-2',
'less': 'vue-style-loader!css-loader!less-loader', 'postcss': 'vue-style-loader!css-loader',
'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax', 'less': 'vue-style-loader!css-loader!less-loader',
'scss': 'vue-style-loader!css-loader!sass-loader', 'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax',
'stylus': 'vue-style-loader!css-loader!stylus-loader', 'scss': 'vue-style-loader!css-loader!sass-loader',
'styl': 'vue-style-loader!css-loader!stylus-loader' 'stylus': 'vue-style-loader!css-loader!stylus-loader',
'styl': 'vue-style-loader!css-loader!stylus-loader'
}
} }
if (!this.dev) {
// Use ExtractTextPlugin to extract CSS into a single file
const ExtractTextPlugin = require('extract-text-webpack-plugin')
config.loaders.css = ExtractTextPlugin.extract({ loader: 'css-loader' })
config.loaders.scss = ExtractTextPlugin.extract({ loader: 'css-loader!sass-loader', fallbackLoader: 'vue-style-loader' })
config.loaders.sass = ExtractTextPlugin.extract({ loader: 'css-loader!sass-loader?indentedSyntax', fallbackLoader: 'vue-style-loader' })
config.loaders.stylus = ExtractTextPlugin.extract({ loader: 'css-loader!stylus-loader', fallbackLoader: 'vue-style-loader' })
config.loaders.less = ExtractTextPlugin.extract({ loader: 'css-loader!less-loader', fallbackLoader: 'vue-style-loader' })
}
// Return the config
return config
} }

View File

@ -17,6 +17,7 @@ class Nuxt {
constructor (options = {}, cb) { constructor (options = {}, cb) {
var defaults = { var defaults = {
dev: true,
routes: [], routes: [],
plugins: [], plugins: [],
css: [], css: [],
@ -32,8 +33,7 @@ class Nuxt {
if (options.loading === true) delete options.loading if (options.loading === true) delete options.loading
this.options = _.defaultsDeep(options, defaults) this.options = _.defaultsDeep(options, defaults)
// Env variables // Env variables
this.isProd = process.env.NODE_ENV === 'production' this.dev = this.options.dev
this.isDev = !process.env.NODE_ENV || process.env.NODE_ENV === 'development'
this.dir = (typeof options.rootDir === 'string' && options.rootDir ? options.rootDir : process.cwd()) this.dir = (typeof options.rootDir === 'string' && options.rootDir ? options.rootDir : process.cwd())
// Template // Template
this.appTemplate = _.template(fs.readFileSync(resolve(__dirname, 'views', 'app.html'), 'utf8'), { this.appTemplate = _.template(fs.readFileSync(resolve(__dirname, 'views', 'app.html'), 'utf8'), {
@ -70,7 +70,7 @@ class Nuxt {
const self = this const self = this
const context = getContext(req, res) const context = getContext(req, res)
co(function * () { co(function * () {
if (self.isDev) { if (self.dev) {
// Call webpack middlewares only in development // Call webpack middlewares only in development
yield self.webpackDevMiddleware(req, res) yield self.webpackDevMiddleware(req, res)
yield self.webpackHotMiddleware(req, res) yield self.webpackHotMiddleware(req, res)
@ -84,7 +84,7 @@ class Nuxt {
} }
}) })
.then(() => { .then(() => {
if (this.isDev && req.url.includes('/_nuxt/') && req.url.includes('.hot-update.json')) { if (this.dev && req.url.includes('/_nuxt/') && req.url.includes('.hot-update.json')) {
res.statusCode = 404 res.statusCode = 404
return res.end() return res.end()
} }
@ -115,7 +115,7 @@ class Nuxt {
context.nuxt.error = { statusCode: 500, message: context.nuxt.error.message } context.nuxt.error = { statusCode: 500, message: context.nuxt.error.message }
} }
const app = self.appTemplate({ const app = self.appTemplate({
isProd: self.isProd, // Use to add the extracted CSS <link> in production dev: self.dev, // Use to add the extracted CSS <link> in production
APP: html, APP: html,
context: context, context: context,
files: { files: {

49
lib/server.js Normal file
View File

@ -0,0 +1,49 @@
'use strict'
const http = require('http')
const co = require('co')
const pify = require('pify')
const serveStatic = require('serve-static')
const { resolve } = require('path')
class Server {
constructor (nuxt) {
this.server = http.createServer(this.handle.bind(this))
this.serveStatic = pify(serveStatic(resolve(nuxt.dir, 'static')))
this.nuxt = nuxt
return this
}
handle (req, res) {
const method = req.method.toUpperCase()
const self = this
if (method !== 'GET' && method !== 'HEAD') {
return this.nuxt.render(req, res)
}
co(function * () {
if (req.url.includes('/static/')) {
const url = req.url
req.url = req.url.replace('/static/', '/')
yield self.serveStatic(req, res)
req.url = url
}
})
.then(() => {
// File not found
this.nuxt.render(req, res)
})
}
listen (port, host) {
host = host || 'localhost'
port = port || 3000
this.server.listen(port, host, () => {
console.log('Ready on http://%s:%s', host, port)
})
}
}
module.exports = Server

View File

@ -8,7 +8,7 @@
<html> <html>
<head> <head>
${title.toString()} ${title.toString()}
<% if (isProd) { %><link rel="stylesheet" href="<%= files.css %>"><% } %> <% if (!dev) { %><link rel="stylesheet" href="<%= files.css %>"><% } %>
</head> </head>
<body> <body>
<%= APP %> <%= APP %>

View File

@ -25,7 +25,7 @@ test(async t => {
test(async t => { test(async t => {
const html = await (render('/head')) const html = await (render('/head'))
t.true(html.includes('<meta content="my meta" class="next-head"/>')) t.true(html.includes('<meta content="my meta" class="nuxt-head"/>'))
t.true(html.includes('<div><h1>I can haz meta tags</h1></div>')) t.true(html.includes('<div><h1>I can haz meta tags</h1></div>'))
}) })