From 36057fb8990fe1a2864a4b6f18eeb45b3479359b Mon Sep 17 00:00:00 2001 From: Clark Du Date: Sat, 3 Jun 2017 16:04:37 +0800 Subject: [PATCH 1/9] Use yarn instead of npm in building --- .travis.yml | 8 ++++---- appveyor.yml | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index c2a14e7f60..8f0c46e7fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/appveyor.yml b/appveyor.yml index 5cf1f0ebc9..2dfd73a345 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -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 From f958801fff7734c55ea277752752dc112dc55dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Sun, 4 Jun 2017 14:08:36 +0200 Subject: [PATCH 2/9] fix: modules called before renderer in production --- lib/build.js | 33 +++++++++++++++++---------------- lib/generate.js | 4 ++++ lib/module.js | 3 ++- lib/nuxt.js | 19 ++++++++++++------- lib/render.js | 5 +++-- 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/lib/build.js b/lib/build.js index a12807e06c..968feee155 100644 --- a/lib/build.js +++ b/lib/build.js @@ -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) } } diff --git a/lib/generate.js b/lib/generate.js index 65de915b46..7525cb843d 100644 --- a/lib/generate.js +++ b/lib/generate.js @@ -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) diff --git a/lib/module.js b/lib/module.js index f16669697a..83deb63533 100755 --- a/lib/module.js +++ b/lib/module.js @@ -19,10 +19,11 @@ class Module { 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) { diff --git a/lib/nuxt.js b/lib/nuxt.js index bce21038cc..ee28be97cf 100644 --- a/lib/nuxt.js +++ b/lib/nuxt.js @@ -124,20 +124,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) { diff --git a/lib/render.js b/lib/render.js index fcfb741613..0cc56b79df 100644 --- a/lib/render.js +++ b/lib/render.js @@ -11,6 +11,9 @@ 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 +26,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 From 2f32d03f83de48fc13718815e38197446d538b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Sun, 4 Jun 2017 14:11:18 +0200 Subject: [PATCH 3/9] Bump to alpha3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ec97f4e412..f78ff073d6 100644 --- a/package.json +++ b/package.json @@ -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": [ { From b26d3d6b6f2d0849cf81ae1cfe833794cf890973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Sun, 4 Jun 2017 14:28:39 +0200 Subject: [PATCH 4/9] Update i18n example --- examples/i18n/nuxt.config.js | 9 +++------ examples/i18n/package.json | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/i18n/nuxt.config.js b/examples/i18n/nuxt.config.js index 9cf19567b3..726c1e7e10 100644 --- a/examples/i18n/nuxt.config.js +++ b/examples/i18n/nuxt.config.js @@ -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' }, + } } diff --git a/examples/i18n/package.json b/examples/i18n/package.json index 5dd206d014..d4096ad025 100644 --- a/examples/i18n/package.json +++ b/examples/i18n/package.json @@ -1,7 +1,7 @@ { "name": "nuxt-i18n", "dependencies": { - "nuxt": "latest", + "nuxt": "1.0.0-alpha.3", "vue-i18n": "^7.0.0" }, "scripts": { From ce69c3be0f544afa9b36b23b9e106631c9c51f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Sun, 4 Jun 2017 19:59:36 +0200 Subject: [PATCH 5/9] Fix: error on template for layout when redirected --- lib/app/server.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/app/server.js b/lib/app/server.js index 0bba819264..5e0f1b5109 100644 --- a/lib/app/server.js +++ b/lib/app/server.js @@ -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 @@ -82,7 +83,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) @@ -107,7 +108,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) => { From 349f6e6219f709c7daaf1dec55ac3edd4b07b4ce Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Mon, 5 Jun 2017 13:19:27 +0430 Subject: [PATCH 6/9] feat(http2): add render.http2.push option This option disables http2 push headers by default as is currently inconsistent with different browser and webservers --- lib/nuxt.js | 7 +++++-- lib/render.js | 27 +++++++++++++++------------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/nuxt.js b/lib/nuxt.js index ee28be97cf..a7373a8cc6 100644 --- a/lib/nuxt.js +++ b/lib/nuxt.js @@ -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 diff --git a/lib/render.js b/lib/render.js index 0cc56b79df..aa3fa67689 100644 --- a/lib/render.js +++ b/lib/render.js @@ -74,20 +74,23 @@ export async function render (req, res) { } 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}`) + } + } + res.setHeader('Link', pushAssets) + } 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) { From 0468c7997e80280eba823ee634f384baf7937e5c Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Mon, 5 Jun 2017 13:22:02 +0430 Subject: [PATCH 7/9] feat(module): improve require --- lib/module.js | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/module.js b/lib/module.js index 83deb63533..c62b65e813 100755 --- a/lib/module.js +++ b/lib/module.js @@ -9,14 +9,14 @@ import {chainFn, sequence} from './utils' const debug = require('debug')('nuxt:module') class Module { - constructor (nuxt) { + constructor(nuxt) { this.nuxt = nuxt this.options = nuxt.options - this.modules = [] + this.requiredModules = [] this.initing = this.ready() } - async ready () { + async ready() { if (this.initing) { await this.initing return this @@ -26,7 +26,7 @@ class Module { return this } - addVendor (vendor) { + addVendor(vendor) { /* istanbul ignore if */ if (!vendor) { return @@ -34,7 +34,7 @@ class Module { this.options.build.vendor = uniq(this.options.build.vendor.concat(vendor)) } - addTemplate (template) { + addTemplate(template) { /* istanbul ignore if */ if (!template) { return @@ -60,7 +60,7 @@ class Module { return templateObj } - addPlugin (template) { + addPlugin(template) { const {dst} = this.addTemplate(template) // Add to nuxt plugins this.options.plugins.push({ @@ -69,27 +69,24 @@ class Module { }) } - addServerMiddleware (middleware) { + addServerMiddleware(middleware) { this.options.serverMiddleware.push(middleware) } - extendBuild (fn) { + extendBuild(fn) { this.options.build.extend = chainFn(this.options.build.extend, fn) } - extendRoutes (fn) { + extendRoutes(fn) { this.options.router.extendRoutes = chainFn(this.options.router.extendRoutes, fn) } - 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) + requireModule(moduleOpts) { + // Require once + return this.addModule(moduleOpts, true) } - addModule (moduleOpts) { + addModule(moduleOpts, requireOnce) { /* istanbul ignore if */ if (!moduleOpts) { return @@ -129,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 => { From bd54ddd2a74e5cb4ed28aac3b04b3c83526be44c Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Mon, 5 Jun 2017 13:26:07 +0430 Subject: [PATCH 8/9] feat(http2): push assets with single link header less payload! https://blog.cloudflare.com/http-2-server-push-with-multiple-assets-per-link-header --- lib/render.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/render.js b/lib/render.js index aa3fa67689..fda235b2c1 100644 --- a/lib/render.js +++ b/lib/render.js @@ -87,7 +87,9 @@ export async function render (req, res) { pushAssets.push(`<${href}>; rel=${rel}; as=${as}`) } } - res.setHeader('Link', pushAssets) + // 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)) From b37c7cbd8d4abd0873db53b40ab46b5af5e690e3 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Mon, 5 Jun 2017 13:27:41 +0430 Subject: [PATCH 9/9] chore: eslint --- lib/module.js | 24 ++++++++++++------------ lib/render.js | 16 +++++++++------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/module.js b/lib/module.js index c62b65e813..630db411e2 100755 --- a/lib/module.js +++ b/lib/module.js @@ -2,21 +2,21 @@ 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') class Module { - constructor(nuxt) { + constructor (nuxt) { this.nuxt = nuxt this.options = nuxt.options this.requiredModules = [] this.initing = this.ready() } - async ready() { + async ready () { if (this.initing) { await this.initing return this @@ -26,7 +26,7 @@ class Module { return this } - addVendor(vendor) { + addVendor (vendor) { /* istanbul ignore if */ if (!vendor) { return @@ -34,7 +34,7 @@ class Module { this.options.build.vendor = uniq(this.options.build.vendor.concat(vendor)) } - addTemplate(template) { + addTemplate (template) { /* istanbul ignore if */ if (!template) { return @@ -60,7 +60,7 @@ class Module { return templateObj } - addPlugin(template) { + addPlugin (template) { const {dst} = this.addTemplate(template) // Add to nuxt plugins this.options.plugins.push({ @@ -69,24 +69,24 @@ class Module { }) } - addServerMiddleware(middleware) { + addServerMiddleware (middleware) { this.options.serverMiddleware.push(middleware) } - extendBuild(fn) { + extendBuild (fn) { this.options.build.extend = chainFn(this.options.build.extend, fn) } - extendRoutes(fn) { + extendRoutes (fn) { this.options.router.extendRoutes = chainFn(this.options.router.extendRoutes, fn) } - requireModule(moduleOpts) { + requireModule (moduleOpts) { // Require once return this.addModule(moduleOpts, true) } - addModule(moduleOpts, requireOnce) { + addModule (moduleOpts, requireOnce) { /* istanbul ignore if */ if (!moduleOpts) { return diff --git a/lib/render.js b/lib/render.js index fda235b2c1..238c31a528 100644 --- a/lib/render.js +++ b/lib/render.js @@ -5,6 +5,7 @@ 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 @@ -57,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 } @@ -67,7 +68,7 @@ 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 @@ -75,7 +76,7 @@ export async function render (req, res) { res.setHeader('ETag', etag) } // HTTP2 push headers - if(!error && this.options.render.http2.push) { + 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 @@ -101,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)) }) @@ -133,7 +134,7 @@ export async function renderRoute (url, context = {}) { } const resourceHints = context.renderResourceHints() HEAD += resourceHints + context.renderStyles() - APP += `` + APP += `` APP += context.renderScripts() const html = this.appTemplate({ HTML_ATTRS: 'data-n-head-ssr ' + m.htmlAttrs.text(), @@ -168,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) {