Merge branch 'dev' of github.com:nuxt/nuxt.js into dev

This commit is contained in:
Sebastien Chopin 2017-06-05 11:24:09 +02:00
commit 3a2b732965
11 changed files with 102 additions and 71 deletions

View File

@ -6,9 +6,9 @@ node_js:
before_install:
- if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi
install:
- npm install
- npm run build
- yarn install
- yarn run build
script:
- npm test
- yarn run test
after_success:
- npm run coverage
- yarn run coverage

View File

@ -7,15 +7,16 @@ install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# install modules
- npm install
- yarn install
# Post-install test scripts.
test_script:
# Output useful info for debugging.
- node --version
- npm --version
- yarn --version
# run tests
- npm test
- yarn run test
# Don't actually build.
build: off

View File

@ -1,16 +1,13 @@
module.exports = {
loading: { color: 'cyan' },
build: {
vendor: ['vue-i18n']
},
router: {
middleware: 'i18n'
},
plugins: [
// Will inject the plugin in the $root app and also in the context as `i18n`
{ src: '~plugins/i18n.js', injectAs: 'i18n' }
],
plugins: ['~plugins/i18n.js',],
generate: {
routes: ['/', '/about', '/fr', '/fr/about']
},
loading: { color: 'cyan' },
}
}

View File

@ -1,7 +1,7 @@
{
"name": "nuxt-i18n",
"dependencies": {
"nuxt": "latest",
"nuxt": "1.0.0-alpha.3",
"vue-i18n": "^7.0.0"
},
"scripts": {

View File

@ -19,6 +19,7 @@ const isDev = <%= isDev %>
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
@ -84,7 +85,7 @@ export default async (context) => {
if (!context.nuxt.error) {
await middlewareSeries(midd, ctx)
}
if (context.redirected) return _app
if (context.redirected) return _noopApp
// Set layout
let layout = Components.length ? Components[0].options.layout : NuxtError.layout
if (typeof layout === 'function') layout = layout(ctx)
@ -109,7 +110,7 @@ export default async (context) => {
if (!context.nuxt.error) {
await middlewareSeries(midd, ctx)
}
if (context.redirected) return _app
if (context.redirected) return _noopApp
// Call .validate()
let isValid = true
Components.forEach((Component) => {

View File

@ -76,23 +76,24 @@ export function options () {
if (this.dev && isUrl(this.options.build.publicPath)) {
this.options.build.publicPath = defaults.publicPath
}
}
export function production () {
// Production, create server-renderer
if (!this.dev) {
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)
}
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)
}
}

View File

@ -42,6 +42,10 @@ export default async function () {
const s = Date.now()
let errors = []
/*
** Wait for modules to be initialized
*/
await this.ready()
/*
** Set variables
*/
this.options.generate = _.defaultsDeep(this.options.generate, defaults)

View File

@ -2,9 +2,9 @@
import path from 'path'
import fs from 'fs'
import {uniq} from 'lodash'
import { uniq } from 'lodash'
import hash from 'hash-sum'
import {chainFn, sequence} from './utils'
import { chainFn, sequence } from './utils'
const debug = require('debug')('nuxt:module')
@ -12,17 +12,18 @@ class Module {
constructor (nuxt) {
this.nuxt = nuxt
this.options = nuxt.options
this.modules = []
this.requiredModules = []
this.initing = this.ready()
}
async ready () {
if (this.initing) {
await this.initing
return
return this
}
// Install all modules in sequence
await sequence(this.options.modules, this.addModule.bind(this))
return this
}
addVendor (vendor) {
@ -81,14 +82,11 @@ class Module {
}
requireModule (moduleOpts) {
if (this.modules.indexOf(moduleOpts) !== -1 || this.modules.indexOf(moduleOpts.src) !== -1) {
return false
}
this.modules.push(moduleOpts.src || moduleOpts)
return this.addModule(moduleOpts)
// Require once
return this.addModule(moduleOpts, true)
}
addModule (moduleOpts) {
addModule (moduleOpts, requireOnce) {
/* istanbul ignore if */
if (!moduleOpts) {
return
@ -128,6 +126,19 @@ class Module {
console.error(`[nuxt] Module [${originalSrc}] should export a function`)
process.exit(1)
}
// Module meta
if (!module.meta) {
module.meta = {}
}
if (module.meta.name) {
const alreadyRequired = this.requiredModules.indexOf(module.meta.name) !== -1
if (requireOnce && alreadyRequired) {
return
}
if (!alreadyRequired) {
this.requiredModules.push(module.meta.name)
}
}
// Call module with `this` context and pass options
return new Promise((resolve, reject) => {
const result = module.call(this, options, err => {

View File

@ -51,6 +51,9 @@ class Nuxt {
scrollBehavior: null
},
render: {
http2: {
push: false
},
static: {},
gzip: {
threshold: 0
@ -66,11 +69,11 @@ class Nuxt {
}
// Sanitization
if (options.loading === true) delete options.loading
if (options.router && typeof options.router.middleware === 'string') options.router.middleware = [ options.router.middleware ]
if (options.router && typeof options.router.middleware === 'string') options.router.middleware = [options.router.middleware]
if (options.router && typeof options.router.base === 'string') {
this._routerBaseSpecified = true
}
if (typeof options.transition === 'string') options.transition = { name: options.transition }
if (typeof options.transition === 'string') options.transition = {name: options.transition}
this.options = _.defaultsDeep(options, defaults)
// Ready variable
this._ready = false
@ -124,20 +127,25 @@ class Nuxt {
// Add module integration
this.module = new Module(this)
// Init nuxt.js
this.ready()
// Launch build in development but don't wait for him to be finished
if (this.dev) {
this.build()
}
this._ready = this.ready()
// Return nuxt.js instance
return this
}
async ready () {
if (this._ready) return this
if (this._ready) {
await this._ready
return this
}
// Init modules
await this.module.ready()
this._ready = true
// Launch build in development but don't wait for it to be finished
if (this.dev) {
this.build()
} else {
build.production.call(this)
}
return this
}
close (callback) {

View File

@ -5,12 +5,16 @@ import serialize from 'serialize-javascript'
import generateETag from 'etag'
import fresh from 'fresh'
import { getContext, setAnsiColors, encodeHtml } from './utils'
const debug = require('debug')('nuxt:render')
// force blue color
debug.color = 4
setAnsiColors(ansiHTML)
export async function render (req, res) {
// Wait for nuxt.js to be ready
await this.ready()
// Check if project is built for production
if (!this.renderer && !this.dev) {
console.error('> No build files found, please run `nuxt build` before launching `nuxt start`') // eslint-disable-line no-console
process.exit(1)
@ -23,8 +27,6 @@ export async function render (req, res) {
}, 1000)
})
}
// Wait for nuxt.js to be ready
await this.ready()
// Get context
const context = getContext(req, res)
res.statusCode = 200
@ -56,7 +58,7 @@ export async function render (req, res) {
res.statusCode = 404
return res.end()
}
const { html, error, redirected, resourceHints } = await this.renderRoute(req.url, context)
const {html, error, redirected, resourceHints} = await this.renderRoute(req.url, context)
if (redirected) {
return html
}
@ -66,27 +68,32 @@ export async function render (req, res) {
// ETag header
if (!error && this.options.render.etag) {
const etag = generateETag(html, this.options.render.etag)
if (fresh(req.headers, { etag })) {
if (fresh(req.headers, {etag})) {
res.statusCode = 304
res.end()
return
}
res.setHeader('ETag', etag)
}
// HTTP2 push headers
if (!error && this.options.render.http2.push) {
// Parse resourceHints to extract HTTP.2 prefetch/push headers
// https://w3c.github.io/preload/#server-push-http-2
const regex = /link rel="([^"]*)" href="([^"]*)" as="([^"]*)"/g
const pushAssets = []
let m
while (m = regex.exec(resourceHints)) { // eslint-disable-line no-cond-assign
const [_, rel, href, as] = m // eslint-disable-line no-unused-vars
if (rel === 'preload') {
pushAssets.push(`<${href}>; rel=${rel}; as=${as}`)
}
}
// Pass with single Link header
// https://blog.cloudflare.com/http-2-server-push-with-multiple-assets-per-link-header
res.setHeader('Link', pushAssets.join(','))
}
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.setHeader('Content-Length', Buffer.byteLength(html))
// Parse resourceHints to extract HTTP.2 prefetch/push headers
// https://w3c.github.io/preload/#server-push-http-2
const regex = /link rel="([^"]*)" href="([^"]*)" as="([^"]*)"/g
const pushAssets = []
let m
while (m = regex.exec(resourceHints)) { // eslint-disable-line no-cond-assign
const [_, rel, href, as] = m // eslint-disable-line no-unused-vars
if (rel === 'preload') {
pushAssets.push(`<${href}>; rel=${rel}; as=${as}`)
}
}
res.setHeader('Link', pushAssets)
res.end(html, 'utf8')
return html
} catch (err) {
@ -95,7 +102,7 @@ export async function render (req, res) {
return err
}
const html = this.errorTemplate({
/* istanbul ignore if */
/* istanbul ignore if */
error: err,
stack: ansiHTML(encodeHtml(err.stack))
})
@ -127,7 +134,7 @@ export async function renderRoute (url, context = {}) {
}
const resourceHints = context.renderResourceHints()
HEAD += resourceHints + context.renderStyles()
APP += `<script type="text/javascript">window.__NUXT__=${serialize(context.nuxt, { isJSON: true })}</script>`
APP += `<script type="text/javascript">window.__NUXT__=${serialize(context.nuxt, {isJSON: true})}</script>`
APP += context.renderScripts()
const html = this.appTemplate({
HTML_ATTRS: 'data-n-head-ssr ' + m.htmlAttrs.text(),
@ -162,14 +169,15 @@ export async function renderAndGetWindow (url, opts = {}) {
runScripts: 'dangerously',
beforeParse (window) {
// Mock window.scrollTo
window.scrollTo = () => {}
window.scrollTo = () => {
}
}
}
if (opts.virtualConsole !== false) {
options.virtualConsole = new jsdom.VirtualConsole().sendTo(console)
}
url = url || 'http://localhost:3000'
const { window } = await jsdom.JSDOM.fromURL(url, options)
const {window} = await jsdom.JSDOM.fromURL(url, options)
// If Nuxt could not be loaded (error from the server-side)
const nuxtExists = window.document.body.innerHTML.includes('window.__NUXT__')
if (!nuxtExists) {

View File

@ -1,6 +1,6 @@
{
"name": "nuxt",
"version": "1.0.0-alpha2",
"version": "1.0.0-alpha.3",
"description": "A minimalistic framework for server-rendered Vue.js applications (inspired by Next.js)",
"contributors": [
{