From b295c246504b2b19a594461a2082a7d567633f8a Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Mon, 13 Feb 2017 09:32:57 +0530 Subject: [PATCH 01/30] Add a plan for dynamic entry middleware. --- server/dynamic-entry-middleware.js | 37 ++++++++++++++++++++++++++++++ server/hot-reloader.js | 5 +++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 server/dynamic-entry-middleware.js diff --git a/server/dynamic-entry-middleware.js b/server/dynamic-entry-middleware.js new file mode 100644 index 0000000000000..027def786b2bd --- /dev/null +++ b/server/dynamic-entry-middleware.js @@ -0,0 +1,37 @@ +export default function dynamicEntryMiddleware (devMiddleware, compiler) { + // LRU cache + const entries = [] + let makeCallbacks = [] + let doneCallbacks = [] + + // Based on the http request to add an entry. Add it and invalidate. + setInterval(function () { + devMiddleware.invalidate() + }, 5000) + + compiler.plugin('make', function (compilation, done) { + entries.forEach(() => null) + console.log('Adding entries...') + // Add all entries and move makeCallbacks to doneCallbacks + doneCallbacks = [ + ...doneCallbacks, + ...makeCallbacks + ] + makeCallbacks = [] + done() + }) + + compiler.plugin('done', function (stats, done) { + // Call all the doneCallbacks + doneCallbacks.forEach((fn) => fn()) + done() + console.log('Done!') + }) + + // Add the entries to the map and invalidate + // Set the next to nextCallbacks + return function (req, res, next) { + console.log('req', req.url) + next() + } +} diff --git a/server/hot-reloader.js b/server/hot-reloader.js index 37eeecbd4cc06..027bf3dcbe207 100644 --- a/server/hot-reloader.js +++ b/server/hot-reloader.js @@ -1,6 +1,7 @@ import { join, relative, sep } from 'path' import webpackDevMiddleware from 'webpack-dev-middleware' import webpackHotMiddleware from 'webpack-hot-middleware' +import dynamicEntryMiddleware from './dynamic-entry-middleware' import isWindowsBash from 'is-windows-bash' import webpack from './build/webpack' import clean from './build/clean' @@ -145,10 +146,12 @@ export default class HotReloader { }) this.webpackHotMiddleware = webpackHotMiddleware(compiler, { log: false }) + this.dynamicEntryMiddleware = dynamicEntryMiddleware(this.webpackDevMiddleware, compiler) this.middlewares = [ this.webpackDevMiddleware, - this.webpackHotMiddleware + this.webpackHotMiddleware, + this.dynamicEntryMiddleware ] } From 3d975ef4143b73f54bd89aaa4ee8b9e15b1b1aa7 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Mon, 13 Feb 2017 16:31:01 +0530 Subject: [PATCH 02/30] Use dynamic pages middleware to load pages in dev. --- server/build/webpack.js | 12 +++++++----- server/dynamic-entry-middleware.js | 23 +++++++++++++++++++---- server/hot-reloader.js | 5 ++++- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/server/build/webpack.js b/server/build/webpack.js index 341968976e3ca..c7076eebe52b3 100644 --- a/server/build/webpack.js +++ b/server/build/webpack.js @@ -6,7 +6,6 @@ import WriteFilePlugin from 'write-file-webpack-plugin' import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin' import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin' import UnlinkFilePlugin from './plugins/unlink-file-plugin' -import WatchPagesPlugin from './plugins/watch-pages-plugin' import JsonPagesPlugin from './plugins/json-pages-plugin' import getConfig from '../config' import * as babelCore from 'babel-core' @@ -37,8 +36,12 @@ export default async function createCompiler (dir, { dev = false, quiet = false const entries = { 'main.js': mainJS } const pages = await glob('pages/**/*.js', { cwd: dir }) - for (const p of pages) { - entries[join('bundles', p)] = [...defaultEntries, `./${p}?entry`] + // In the dev environment, dynamic pages middleware will take care of + // managing pages. + if (!dev) { + for (const p of pages) { + entries[join('bundles', p)] = [...defaultEntries, `./${p}?entry`] + } } for (const p of defaultPages) { @@ -86,8 +89,7 @@ export default async function createCompiler (dir, { dev = false, quiet = false plugins.push( new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin(), - new UnlinkFilePlugin(), - new WatchPagesPlugin(dir) + new UnlinkFilePlugin() ) if (!quiet) { plugins.push(new FriendlyErrorsWebpackPlugin()) diff --git a/server/dynamic-entry-middleware.js b/server/dynamic-entry-middleware.js index 027def786b2bd..4daff9404db92 100644 --- a/server/dynamic-entry-middleware.js +++ b/server/dynamic-entry-middleware.js @@ -1,4 +1,7 @@ -export default function dynamicEntryMiddleware (devMiddleware, compiler) { +import { join } from 'path' +import DynamicEntryPlugin from 'webpack/lib/DynamicEntryPlugin' + +export default function dynamicEntryMiddleware (devMiddleware, compiler, { dir, dev }) { // LRU cache const entries = [] let makeCallbacks = [] @@ -9,22 +12,34 @@ export default function dynamicEntryMiddleware (devMiddleware, compiler) { devMiddleware.invalidate() }, 5000) + const defaultEntries = dev === 'development' + ? [join(__dirname, '..', 'client/webpack-hot-middleware-client.js')] : [] + compiler.plugin('make', function (compilation, done) { entries.forEach(() => null) + + const entry = [ + ...defaultEntries, + join(dir, 'pages', 'index.js?entry') + ] + + const name = 'bundles/pages/index.js' + + compilation.addEntry(this.context, DynamicEntryPlugin.createDependency(entry, name), name, done) console.log('Adding entries...') + compilation // Add all entries and move makeCallbacks to doneCallbacks doneCallbacks = [ ...doneCallbacks, ...makeCallbacks ] makeCallbacks = [] - done() + // done() }) - compiler.plugin('done', function (stats, done) { + compiler.plugin('done', function (stats) { // Call all the doneCallbacks doneCallbacks.forEach((fn) => fn()) - done() console.log('Done!') }) diff --git a/server/hot-reloader.js b/server/hot-reloader.js index 027bf3dcbe207..1b2a262122f31 100644 --- a/server/hot-reloader.js +++ b/server/hot-reloader.js @@ -146,7 +146,10 @@ export default class HotReloader { }) this.webpackHotMiddleware = webpackHotMiddleware(compiler, { log: false }) - this.dynamicEntryMiddleware = dynamicEntryMiddleware(this.webpackDevMiddleware, compiler) + this.dynamicEntryMiddleware = dynamicEntryMiddleware(this.webpackDevMiddleware, compiler, { + dir: this.dir, + dev: true + }) this.middlewares = [ this.webpackDevMiddleware, From 93b0967e8b49fe2eedc64a9ec8eb264f3b9f8959 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Tue, 14 Feb 2017 00:54:40 +0530 Subject: [PATCH 03/30] Add the first version of middleware but not tested. --- server/dynamic-entry-middleware.js | 107 +++++++++++++++++++---------- 1 file changed, 72 insertions(+), 35 deletions(-) diff --git a/server/dynamic-entry-middleware.js b/server/dynamic-entry-middleware.js index 4daff9404db92..74c32c07d077c 100644 --- a/server/dynamic-entry-middleware.js +++ b/server/dynamic-entry-middleware.js @@ -1,52 +1,89 @@ -import { join } from 'path' import DynamicEntryPlugin from 'webpack/lib/DynamicEntryPlugin' +import { EventEmitter } from 'events' export default function dynamicEntryMiddleware (devMiddleware, compiler, { dir, dev }) { - // LRU cache - const entries = [] - let makeCallbacks = [] - let doneCallbacks = [] + // TODO: Make this a LRU cache + const entries = {} + let doingEntries = {} + let completedEntries = {} - // Based on the http request to add an entry. Add it and invalidate. - setInterval(function () { - devMiddleware.invalidate() - }, 5000) - - const defaultEntries = dev === 'development' - ? [join(__dirname, '..', 'client/webpack-hot-middleware-client.js')] : [] + const doneCallbacks = new EventEmitter() compiler.plugin('make', function (compilation, done) { - entries.forEach(() => null) - - const entry = [ - ...defaultEntries, - join(dir, 'pages', 'index.js?entry') - ] - - const name = 'bundles/pages/index.js' - - compilation.addEntry(this.context, DynamicEntryPlugin.createDependency(entry, name), name, done) - console.log('Adding entries...') - compilation - // Add all entries and move makeCallbacks to doneCallbacks - doneCallbacks = [ - ...doneCallbacks, - ...makeCallbacks - ] - makeCallbacks = [] - // done() + const allEntries = Object.keys(entries).map((name) => { + doingEntries[name] = true + const entry = entries[name] + return addEntry(compilation, name, entry) + }) + + Promise.all(allEntries) + .then(() => done()) + .catch(done) + + console.log('MAKE') }) compiler.plugin('done', function (stats) { // Call all the doneCallbacks - doneCallbacks.forEach((fn) => fn()) - console.log('Done!') + Object.keys(doingEntries).forEach((name) => { + doneCallbacks.emit(name) + }) + + completedEntries = doingEntries + doingEntries = {} + + console.log('DONE') }) // Add the entries to the map and invalidate // Set the next to nextCallbacks return function (req, res, next) { - console.log('req', req.url) - next() + if (req.method !== 'POST') return next() + if (req.url !== '/webpack-config/add_entry') return next() + + reqToData(req, (err, data) => { + if (err) { + console.error(err.stack) + res.statusCode = 500 + res.end('Internal Error') + return + } + + if (completedEntries[data.name]) { + next() + return + } + + if (entries[data.name]) { + doneCallbacks.on(data.name, next) + return + } + + entries[data.name] = data.entry + doneCallbacks.on(data.name, next) + + devMiddleware.invalidate() + }) } } + +function addEntry (compilation, name, entry) { + return new Promise((resolve, reject) => { + const dep = DynamicEntryPlugin.createDependency(entry, name) + compilation.addEntry(this.context, dep, name, (err) => { + if (err) return reject(err) + resolve() + }) + }) +} + +function reqToData (req, cb) { + if (req.data) return cb(null, req.data) + + let jsonString = '' + req.on('data', (data) => { + jsonString += data.toString() + }) + req.on('end', () => cb(null, JSON.parse(jsonString))) + req.on('error', (err) => cb(err)) +} From e23758033e20f32d4dc6e4e65279a63c24b440e0 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Tue, 14 Feb 2017 01:38:43 +0530 Subject: [PATCH 04/30] Integrated. --- server/dynamic-entry-handler.js | 72 ++++++++++++++++++++++++ server/dynamic-entry-middleware.js | 89 ------------------------------ server/hot-reloader.js | 7 +-- server/index.js | 3 +- server/render.js | 24 +++++++- 5 files changed, 100 insertions(+), 95 deletions(-) create mode 100644 server/dynamic-entry-handler.js delete mode 100644 server/dynamic-entry-middleware.js diff --git a/server/dynamic-entry-handler.js b/server/dynamic-entry-handler.js new file mode 100644 index 0000000000000..51e61aa253a71 --- /dev/null +++ b/server/dynamic-entry-handler.js @@ -0,0 +1,72 @@ +import DynamicEntryPlugin from 'webpack/lib/DynamicEntryPlugin' +import { EventEmitter } from 'events' + +export default function dynamicEntryHandler (devMiddleware, compiler, { dir, dev }) { + // TODO: Make this a LRU cache + const entries = {} + let doingEntries = {} + let completedEntries = {} + + const doneCallbacks = new EventEmitter() + + compiler.plugin('make', function (compilation, done) { + const allEntries = Object.keys(entries).map((name) => { + doingEntries[name] = true + const entry = entries[name] + return addEntry(compilation, this.context, name, entry) + }) + + Promise.all(allEntries) + .then(() => done()) + .catch(done) + + console.log('MAKE') + }) + + compiler.plugin('done', function (stats) { + // Call all the doneCallbacks + Object.keys(doingEntries).forEach((name) => { + doneCallbacks.emit(name) + }) + + completedEntries = doingEntries + doingEntries = {} + + console.log('DONE') + }) + + return { + ensureEntry (name, entry) { + return new Promise((resolve, reject) => { + if (completedEntries[name]) { + return resolve() + } + + if (entries[name]) { + doneCallbacks.on(name, processCallback) + return + } + + entries[name] = entry + doneCallbacks.on(name, processCallback) + + devMiddleware.invalidate() + + function processCallback (err) { + if (err) return reject(err) + resolve() + } + }) + } + } +} + +function addEntry (compilation, context, name, entry) { + return new Promise((resolve, reject) => { + const dep = DynamicEntryPlugin.createDependency(entry, name) + compilation.addEntry(context, dep, name, (err) => { + if (err) return reject(err) + resolve() + }) + }) +} diff --git a/server/dynamic-entry-middleware.js b/server/dynamic-entry-middleware.js deleted file mode 100644 index 74c32c07d077c..0000000000000 --- a/server/dynamic-entry-middleware.js +++ /dev/null @@ -1,89 +0,0 @@ -import DynamicEntryPlugin from 'webpack/lib/DynamicEntryPlugin' -import { EventEmitter } from 'events' - -export default function dynamicEntryMiddleware (devMiddleware, compiler, { dir, dev }) { - // TODO: Make this a LRU cache - const entries = {} - let doingEntries = {} - let completedEntries = {} - - const doneCallbacks = new EventEmitter() - - compiler.plugin('make', function (compilation, done) { - const allEntries = Object.keys(entries).map((name) => { - doingEntries[name] = true - const entry = entries[name] - return addEntry(compilation, name, entry) - }) - - Promise.all(allEntries) - .then(() => done()) - .catch(done) - - console.log('MAKE') - }) - - compiler.plugin('done', function (stats) { - // Call all the doneCallbacks - Object.keys(doingEntries).forEach((name) => { - doneCallbacks.emit(name) - }) - - completedEntries = doingEntries - doingEntries = {} - - console.log('DONE') - }) - - // Add the entries to the map and invalidate - // Set the next to nextCallbacks - return function (req, res, next) { - if (req.method !== 'POST') return next() - if (req.url !== '/webpack-config/add_entry') return next() - - reqToData(req, (err, data) => { - if (err) { - console.error(err.stack) - res.statusCode = 500 - res.end('Internal Error') - return - } - - if (completedEntries[data.name]) { - next() - return - } - - if (entries[data.name]) { - doneCallbacks.on(data.name, next) - return - } - - entries[data.name] = data.entry - doneCallbacks.on(data.name, next) - - devMiddleware.invalidate() - }) - } -} - -function addEntry (compilation, name, entry) { - return new Promise((resolve, reject) => { - const dep = DynamicEntryPlugin.createDependency(entry, name) - compilation.addEntry(this.context, dep, name, (err) => { - if (err) return reject(err) - resolve() - }) - }) -} - -function reqToData (req, cb) { - if (req.data) return cb(null, req.data) - - let jsonString = '' - req.on('data', (data) => { - jsonString += data.toString() - }) - req.on('end', () => cb(null, JSON.parse(jsonString))) - req.on('error', (err) => cb(err)) -} diff --git a/server/hot-reloader.js b/server/hot-reloader.js index 1b2a262122f31..dca55db848cfa 100644 --- a/server/hot-reloader.js +++ b/server/hot-reloader.js @@ -1,7 +1,7 @@ import { join, relative, sep } from 'path' import webpackDevMiddleware from 'webpack-dev-middleware' import webpackHotMiddleware from 'webpack-hot-middleware' -import dynamicEntryMiddleware from './dynamic-entry-middleware' +import dynamicEntryHandler from './dynamic-entry-handler' import isWindowsBash from 'is-windows-bash' import webpack from './build/webpack' import clean from './build/clean' @@ -146,15 +146,14 @@ export default class HotReloader { }) this.webpackHotMiddleware = webpackHotMiddleware(compiler, { log: false }) - this.dynamicEntryMiddleware = dynamicEntryMiddleware(this.webpackDevMiddleware, compiler, { + this.dynamicEntries = dynamicEntryHandler(this.webpackDevMiddleware, compiler, { dir: this.dir, dev: true }) this.middlewares = [ this.webpackDevMiddleware, - this.webpackHotMiddleware, - this.dynamicEntryMiddleware + this.webpackHotMiddleware ] } diff --git a/server/index.js b/server/index.js index 9c2b34b8a00c0..d3476a7efb70c 100644 --- a/server/index.js +++ b/server/index.js @@ -23,9 +23,9 @@ export default class Server { this.dir = resolve(dir) this.dev = dev this.quiet = quiet - this.renderOpts = { dir: this.dir, dev, staticMarkup } this.router = new Router() this.hotReloader = dev ? new HotReloader(this.dir, { quiet }) : null + this.renderOpts = { dir: this.dir, dev, staticMarkup, hotReloader: this.hotReloader } this.http = null this.config = getConfig(this.dir) @@ -97,6 +97,7 @@ export default class Server { this.handleBuildId(params.buildId, res) const paths = params.path || ['index'] const pathname = `/${paths.join('/')}` + await this.renderJSON(req, res, pathname) }, diff --git a/server/render.js b/server/render.js index a9e5693defe21..c86b5e2a6f394 100644 --- a/server/render.js +++ b/server/render.js @@ -35,11 +35,15 @@ async function doRender (req, res, pathname, query, { err, page, buildId, + hotReloader, dir = process.cwd(), dev = false, staticMarkup = false } = {}) { page = page || pathname + + await ensurePage(page, { dir, hotReloader }) + let [Component, Document] = await Promise.all([ requireModule(join(dir, '.next', 'dist', 'pages', page)), requireModule(join(dir, '.next', 'dist', 'pages', '_document')) @@ -103,7 +107,8 @@ async function doRender (req, res, pathname, query, { return '' + renderToStaticMarkup(doc) } -export async function renderJSON (req, res, page, { dir = process.cwd() } = {}) { +export async function renderJSON (req, res, page, { dir = process.cwd(), hotReloader } = {}) { + await ensurePage(page, { dir, hotReloader }) const pagePath = await resolvePath(join(dir, '.next', 'bundles', 'pages', page)) return serveStaticWithGzip(req, res, pagePath) } @@ -193,3 +198,20 @@ export function serveStatic (req, res, path) { .on('finish', resolve) }) } + +async function ensurePage (page, { dir, hotReloader }) { + if (!hotReloader) return + if (page === '_error' || page === '_document') return + + const pagePath = join(dir, 'pages', page) + const pathname = await resolvePath(pagePath) + const name = join('bundles', pathname.substring(dir.length)) + + const entry = [ + join(__dirname, '..', 'client/webpack-hot-middleware-client'), + `${pathname}?entry` + ] + + await hotReloader.dynamicEntries.ensureEntry(name, entry) +} + From 04754a8c3a51ded848a572e5f2b16915a6d042b4 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Tue, 14 Feb 2017 02:05:20 +0530 Subject: [PATCH 05/30] Disable prefetching in development. Otherwise it'll discard the use of dynamic-entries. --- lib/prefetch.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/prefetch.js b/lib/prefetch.js index eef963354b303..4cc75e6cb9bea 100644 --- a/lib/prefetch.js +++ b/lib/prefetch.js @@ -116,6 +116,7 @@ function getPrefetchUrl (href) { } export async function prefetch (href) { + if (process.env.NODE_ENV !== 'production') return if (!hasServiceWorkerSupport()) return if (!isLocal(href)) return From b2383e15eae3b6e636f010471ba422f11939d27c Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Tue, 14 Feb 2017 02:15:50 +0530 Subject: [PATCH 06/30] Build custom document and error always. --- server/build/webpack.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server/build/webpack.js b/server/build/webpack.js index c7076eebe52b3..85760f38036ec 100644 --- a/server/build/webpack.js +++ b/server/build/webpack.js @@ -36,9 +36,15 @@ export default async function createCompiler (dir, { dev = false, quiet = false const entries = { 'main.js': mainJS } const pages = await glob('pages/**/*.js', { cwd: dir }) - // In the dev environment, dynamic pages middleware will take care of + const devPages = pages.filter((p) => p === 'pages/_document.js' || p === 'pages/_error.js') + + // In the dev environment, dynamic pages handler will take care of // managing pages. - if (!dev) { + if (dev) { + for (const p of devPages) { + entries[join('bundles', p)] = [...defaultEntries, `./${p}?entry`] + } + } else { for (const p of pages) { entries[join('bundles', p)] = [...defaultEntries, `./${p}?entry`] } From 93fc9d638e1116505e91766d635034910bfb33c4 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Tue, 14 Feb 2017 02:19:32 +0530 Subject: [PATCH 07/30] Refactor code base. --- server/hot-reloader.js | 4 ++++ server/render.js | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/server/hot-reloader.js b/server/hot-reloader.js index dca55db848cfa..cdfb4aebb593e 100644 --- a/server/hot-reloader.js +++ b/server/hot-reloader.js @@ -189,6 +189,10 @@ export default class HotReloader { send (action, ...args) { this.webpackHotMiddleware.publish({ action, data: args }) } + + ensureEntry (name, entry) { + return this.dynamicEntries.ensureEntry(name, entry) + } } function deleteCache (path) { diff --git a/server/render.js b/server/render.js index c86b5e2a6f394..8a823dbd39579 100644 --- a/server/render.js +++ b/server/render.js @@ -212,6 +212,5 @@ async function ensurePage (page, { dir, hotReloader }) { `${pathname}?entry` ] - await hotReloader.dynamicEntries.ensureEntry(name, entry) + await hotReloader.ensureEntry(name, entry) } - From dd5fb13fd253ff66bb73b4da2e270f1a38ef8016 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Tue, 14 Feb 2017 02:26:13 +0530 Subject: [PATCH 08/30] Change branding as on-demand entries. --- server/hot-reloader.js | 6 +++--- ...{dynamic-entry-handler.js => on-demand-entry-handler.js} | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename server/{dynamic-entry-handler.js => on-demand-entry-handler.js} (95%) diff --git a/server/hot-reloader.js b/server/hot-reloader.js index cdfb4aebb593e..d44d0f99bfd20 100644 --- a/server/hot-reloader.js +++ b/server/hot-reloader.js @@ -1,7 +1,7 @@ import { join, relative, sep } from 'path' import webpackDevMiddleware from 'webpack-dev-middleware' import webpackHotMiddleware from 'webpack-hot-middleware' -import dynamicEntryHandler from './dynamic-entry-handler' +import onDemandEntryHandler from './on-demand-entry-handler' import isWindowsBash from 'is-windows-bash' import webpack from './build/webpack' import clean from './build/clean' @@ -146,7 +146,7 @@ export default class HotReloader { }) this.webpackHotMiddleware = webpackHotMiddleware(compiler, { log: false }) - this.dynamicEntries = dynamicEntryHandler(this.webpackDevMiddleware, compiler, { + this.onDemandEntries = onDemandEntryHandler(this.webpackDevMiddleware, compiler, { dir: this.dir, dev: true }) @@ -191,7 +191,7 @@ export default class HotReloader { } ensureEntry (name, entry) { - return this.dynamicEntries.ensureEntry(name, entry) + return this.onDemandEntries.ensureEntry(name, entry) } } diff --git a/server/dynamic-entry-handler.js b/server/on-demand-entry-handler.js similarity index 95% rename from server/dynamic-entry-handler.js rename to server/on-demand-entry-handler.js index 51e61aa253a71..5d1b07349b7dd 100644 --- a/server/dynamic-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -1,7 +1,7 @@ import DynamicEntryPlugin from 'webpack/lib/DynamicEntryPlugin' import { EventEmitter } from 'events' -export default function dynamicEntryHandler (devMiddleware, compiler, { dir, dev }) { +export default function onDemandEntryHandler (devMiddleware, compiler, { dir, dev }) { // TODO: Make this a LRU cache const entries = {} let doingEntries = {} From 39219d0a6c67497f251a96eead7b5a22b6554068 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Tue, 14 Feb 2017 08:56:03 +0530 Subject: [PATCH 09/30] Fix tests. --- test/integration/basic/test/client-navigation.js | 14 ++++++++++++-- test/integration/basic/test/index.test.js | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/test/integration/basic/test/client-navigation.js b/test/integration/basic/test/client-navigation.js index c14bd34048e63..ae24ac406ccee 100644 --- a/test/integration/basic/test/client-navigation.js +++ b/test/integration/basic/test/client-navigation.js @@ -1,9 +1,19 @@ -/* global describe, it, expect */ +/* global describe, it, expect, beforeAll */ import webdriver from 'next-webdriver' -export default (context) => { +export default (context, render) => { describe('Client Navigation', () => { + beforeAll(async function () { + // pre-build pages before doing tests + await Promise.all([ + render('/empty-get-initial-props'), + render('/nav'), + render('/nav/about'), + render('/nav/querystring') + ]) + }) + describe('with ', () => { it('should navigate the page', async () => { const browser = await webdriver(context.appPort, '/nav') diff --git a/test/integration/basic/test/index.test.js b/test/integration/basic/test/index.test.js index 00a8782496981..2b1ec4e9aa35a 100644 --- a/test/integration/basic/test/index.test.js +++ b/test/integration/basic/test/index.test.js @@ -35,5 +35,5 @@ describe('Basic Features', () => { rendering(context, 'Rendering via HTTP', (p, q) => renderViaHTTP(context.appPort, p, q)) xPoweredBy(context) misc(context) - clientNavigation(context) + clientNavigation(context, (p, q) => renderViaHTTP(context.appPort, p, q)) }) From fc0a54fe24c6b27765c7a33c9b5925d6fba9ef44 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Tue, 14 Feb 2017 12:06:00 +0530 Subject: [PATCH 10/30] Add a client side pinger for on-demand-entries. --- client/on-demand-entries-client.js | 22 ++++ package.json | 1 + server/hot-reloader.js | 7 +- server/on-demand-entry-handler.js | 46 +++++-- server/render.js | 11 +- yarn.lock | 191 ++++++++++++++--------------- 6 files changed, 155 insertions(+), 123 deletions(-) create mode 100644 client/on-demand-entries-client.js diff --git a/client/on-demand-entries-client.js b/client/on-demand-entries-client.js new file mode 100644 index 0000000000000..17c437e1026b0 --- /dev/null +++ b/client/on-demand-entries-client.js @@ -0,0 +1,22 @@ +/* global fetch */ + +import Router from '../lib/router' +import 'whatwg-fetch' + +// Ping on every page change +const originalOnRouteChangeComplete = Router.onRouteChangeComplete +Router.onRouteChangeComplete = function (...args) { + ping() + originalOnRouteChangeComplete(...args) +} + +// Ping every 5 seconds +setInterval(ping, 5000) + +function ping () { + const url = `/on-demand-entries-ping?page=${Router.pathname}` + fetch(url) + .catch((err) => { + console.error(`Error with on-demand-entries-ping: ${err.message}`) + }) +} diff --git a/package.json b/package.json index 6ae0ca68ef870..9453a95f53403 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "webpack": "2.2.1", "webpack-dev-middleware": "1.10.0", "webpack-hot-middleware": "2.16.1", + "whatwg-fetch": "2.0.2", "write-file-webpack-plugin": "3.4.2" }, "devDependencies": { diff --git a/server/hot-reloader.js b/server/hot-reloader.js index d44d0f99bfd20..8e31419c3d66f 100644 --- a/server/hot-reloader.js +++ b/server/hot-reloader.js @@ -153,7 +153,8 @@ export default class HotReloader { this.middlewares = [ this.webpackDevMiddleware, - this.webpackHotMiddleware + this.webpackHotMiddleware, + this.onDemandEntries.middleware() ] } @@ -190,8 +191,8 @@ export default class HotReloader { this.webpackHotMiddleware.publish({ action, data: args }) } - ensureEntry (name, entry) { - return this.onDemandEntries.ensureEntry(name, entry) + ensurePage (page) { + return this.onDemandEntries.ensurePage(page) } } diff --git a/server/on-demand-entry-handler.js b/server/on-demand-entry-handler.js index 5d1b07349b7dd..0c5ee1817a036 100644 --- a/server/on-demand-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -1,8 +1,10 @@ import DynamicEntryPlugin from 'webpack/lib/DynamicEntryPlugin' import { EventEmitter } from 'events' +import { join } from 'path' +import { parse } from 'url' +import resolvePath from './resolve' export default function onDemandEntryHandler (devMiddleware, compiler, { dir, dev }) { - // TODO: Make this a LRU cache const entries = {} let doingEntries = {} let completedEntries = {} @@ -10,9 +12,10 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de const doneCallbacks = new EventEmitter() compiler.plugin('make', function (compilation, done) { - const allEntries = Object.keys(entries).map((name) => { - doingEntries[name] = true - const entry = entries[name] + const allEntries = Object.keys(entries).map((page) => { + console.log('XXXXXX', page) + const { name, entry } = entries[page] + doingEntries[page] = true return addEntry(compilation, this.context, name, entry) }) @@ -36,19 +39,29 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de }) return { - ensureEntry (name, entry) { - return new Promise((resolve, reject) => { - if (completedEntries[name]) { + async ensurePage (page) { + const pagePath = join(dir, 'pages', page) + const pathname = await resolvePath(pagePath) + const name = join('bundles', pathname.substring(dir.length)) + + const entry = [ + join(__dirname, '..', 'client/webpack-hot-middleware-client'), + join(__dirname, '..', 'client', 'on-demand-entries-client'), + `${pathname}?entry` + ] + + await new Promise((resolve, reject) => { + if (completedEntries[page]) { return resolve() } - if (entries[name]) { - doneCallbacks.on(name, processCallback) + if (entries[page]) { + doneCallbacks.on(page, processCallback) return } - entries[name] = entry - doneCallbacks.on(name, processCallback) + entries[page] = { name, entry } + doneCallbacks.on(page, processCallback) devMiddleware.invalidate() @@ -57,6 +70,17 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de resolve() } }) + }, + + middleware () { + return function (req, res, next) { + if (!/^\/on-demand-entries-ping/.test(req.url)) return next() + + const { query } = parse(req.url, true) + console.log(`Hit: ${query.page}`) + res.status = 200 + res.end('Success') + } } } } diff --git a/server/render.js b/server/render.js index 8a823dbd39579..454621d8b8581 100644 --- a/server/render.js +++ b/server/render.js @@ -203,14 +203,5 @@ async function ensurePage (page, { dir, hotReloader }) { if (!hotReloader) return if (page === '_error' || page === '_document') return - const pagePath = join(dir, 'pages', page) - const pathname = await resolvePath(pagePath) - const name = join('bundles', pathname.substring(dir.length)) - - const entry = [ - join(__dirname, '..', 'client/webpack-hot-middleware-client'), - `${pathname}?entry` - ] - - await hotReloader.ensureEntry(name, entry) + await hotReloader.ensurePage(page) } diff --git a/yarn.lock b/yarn.lock index a1e5a13a851ff..4c03b4405684d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,5 +1,7 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 + + abab@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d" @@ -33,6 +35,10 @@ acorn-jsx@^3.0.0: dependencies: acorn "^3.0.4" +acorn@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" + acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" @@ -41,10 +47,6 @@ acorn@^4.0.3, acorn@^4.0.4: version "4.0.11" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.11.tgz#edcda3bd937e7556410d42ed5860f67399c794c0" -acorn@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" - adm-zip@^0.4.7: version "0.4.7" resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.7.tgz#8606c2cbf1c426ce8c8ec00174447fd49b6eafc1" @@ -225,6 +227,12 @@ async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" +async@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/async/-/async-2.0.1.tgz#b709cc0280a9c36f09f4536be823c838a9049e25" + dependencies: + lodash "^4.8.0" + async@^1.4.0, async@^1.4.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -239,12 +247,6 @@ async@~0.2.6: version "0.2.10" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" -async@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.0.1.tgz#b709cc0280a9c36f09f4536be823c838a9049e25" - dependencies: - lodash "^4.8.0" - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -265,7 +267,7 @@ babel-code-frame@^6.16.0, babel-code-frame@^6.22.0: esutils "^2.0.2" js-tokens "^3.0.0" -babel-core@^6.0.0, babel-core@^6.22.0, babel-core@^6.3.0, babel-core@6.22.1: +babel-core@6.22.1, babel-core@^6.0.0, babel-core@^6.22.0, babel-core@^6.3.0: version "6.22.1" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.22.1.tgz#9c5fd658ba1772d28d721f6d25d968fc7ae21648" dependencies: @@ -299,7 +301,7 @@ babel-eslint@7.1.1: babylon "^6.13.0" lodash.pickby "^4.6.0" -babel-generator@^6.18.0, babel-generator@^6.22.0, babel-generator@6.22.0: +babel-generator@6.22.0, babel-generator@^6.18.0, babel-generator@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.22.0.tgz#d642bf4961911a8adc7c692b0c9297f325cda805" dependencies: @@ -604,7 +606,7 @@ babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015 babel-runtime "^6.22.0" babel-template "^6.22.0" -babel-plugin-transform-es2015-modules-commonjs@^6.22.0, babel-plugin-transform-es2015-modules-commonjs@^6.6.0, babel-plugin-transform-es2015-modules-commonjs@6.22.0: +babel-plugin-transform-es2015-modules-commonjs@6.22.0, babel-plugin-transform-es2015-modules-commonjs@^6.22.0, babel-plugin-transform-es2015-modules-commonjs@^6.6.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.22.0.tgz#6ca04e22b8e214fb50169730657e7a07dc941145" dependencies: @@ -884,7 +886,7 @@ babel-register@^6.22.0: mkdirp "^0.5.1" source-map-support "^0.4.2" -babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@6.22.0: +babel-runtime@6.22.0, babel-runtime@^6.18.0, babel-runtime@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.22.0.tgz#1cf8b4ac67c77a4ddb0db2ae1f74de52ac4ca611" dependencies: @@ -1365,10 +1367,6 @@ coveralls@2.11.16: minimist "1.2.0" request "2.79.0" -crc@^3.4.4: - version "3.4.4" - resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.4.tgz#9da1e980e3bd44fc5c93bf5ab3da3378d85e466b" - crc32-stream@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-1.0.1.tgz#ce2c5dc3bd8ffb3830f9cb47f540222c63c90fab" @@ -1376,6 +1374,10 @@ crc32-stream@^1.0.0: crc "^3.4.4" readable-stream "^2.0.0" +crc@^3.4.4: + version "3.4.4" + resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.4.tgz#9da1e980e3bd44fc5c93bf5ab3da3378d85e466b" + create-ecdh@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" @@ -1405,6 +1407,14 @@ cross-env@^3.1.4: dependencies: cross-spawn "^3.0.1" +cross-spawn@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.0.1.tgz#a3bbb302db2297cbea3c04edf36941f4613aa399" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + cross-spawn@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" @@ -1419,14 +1429,6 @@ cross-spawn@^4: lru-cache "^4.0.1" which "^1.2.9" -cross-spawn@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.0.1.tgz#a3bbb302db2297cbea3c04edf36941f4613aa399" - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -1461,7 +1463,7 @@ css-what@2.1: version "2.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" -"cssom@>= 0.3.2 < 0.4.0", cssom@0.3.x: +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": version "0.3.2" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b" @@ -1539,7 +1541,7 @@ deglob@^2.0.0: run-parallel "^1.1.2" uniq "^1.0.1" -del@^2.0.2, del@2.2.2: +del@2.2.2, del@^2.0.2: version "2.2.2" resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" dependencies: @@ -1599,7 +1601,7 @@ doctrine@^1.2.2: esutils "^2.0.2" isarray "^1.0.0" -dom-serializer@~0.1.0, dom-serializer@0: +dom-serializer@0, dom-serializer@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" dependencies: @@ -1614,7 +1616,7 @@ domain-browser@^1.1.1: version "1.1.7" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" -domelementtype@^1.3.0, domelementtype@1: +domelementtype@1, domelementtype@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" @@ -1628,7 +1630,7 @@ domhandler@^2.3.0: dependencies: domelementtype "1" -domutils@^1.5.1, domutils@1.5.1: +domutils@1.5.1, domutils@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" dependencies: @@ -1691,7 +1693,7 @@ entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" -errno@^0.1.3, "errno@>=0.1.1 <0.2.0-0": +"errno@>=0.1.1 <0.2.0-0", errno@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" dependencies: @@ -1762,7 +1764,7 @@ es6-set@~0.1.3: es6-symbol "3" event-emitter "~0.3.4" -es6-symbol@~3.1, es6-symbol@~3.1.0, es6-symbol@3: +es6-symbol@3, es6-symbol@~3.1, es6-symbol@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.0.tgz#94481c655e7a7cad82eba832d97d5433496d7ffa" dependencies: @@ -2423,14 +2425,14 @@ husky@0.13.1: is-ci "^1.0.9" normalize-path "^1.0.0" -iconv-lite@~0.4.13: - version "0.4.15" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" - iconv-lite@0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" +iconv-lite@~0.4.13: + version "0.4.15" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" + ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" @@ -2454,7 +2456,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@2, inherits@2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -2645,14 +2647,14 @@ is-windows-bash@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/is-windows-bash/-/is-windows-bash-1.0.3.tgz#00132a47dcdacb00a9d68f3408a4d01d76215e88" -isarray@^1.0.0, isarray@~1.0.0, isarray@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + isexe@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" @@ -2924,13 +2926,6 @@ js-tokens@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" -js-yaml@^3.5.1, js-yaml@^3.7.0: - version "3.8.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.1.tgz#782ba50200be7b9e5a8537001b7804db3ad02628" - dependencies: - argparse "^1.0.7" - esprima "^3.1.1" - js-yaml@3.6.1: version "3.6.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30" @@ -2938,6 +2933,13 @@ js-yaml@3.6.1: argparse "^1.0.7" esprima "^2.6.0" +js-yaml@^3.5.1, js-yaml@^3.7.0: + version "3.8.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.1.tgz#782ba50200be7b9e5a8537001b7804db3ad02628" + dependencies: + argparse "^1.0.7" + esprima "^3.1.1" + jsbn@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" @@ -2974,7 +2976,7 @@ jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" -json-loader@^0.5.4, json-loader@0.5.4: +json-loader@0.5.4, json-loader@^0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de" @@ -3069,7 +3071,7 @@ loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" -loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@0.2.16: +loader-utils@0.2.16, loader-utils@^0.2.11, loader-utils@^0.2.16: version "0.2.16" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d" dependencies: @@ -3199,14 +3201,14 @@ lodash.some@^4.4.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" -lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.3, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.6.1, lodash@^4.8.0: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" - lodash@4.16.2: version "4.16.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.2.tgz#3e626db827048a699281a8a125226326cfc0e652" +lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.3, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.6.1, lodash@^4.8.0: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + log-driver@1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.5.tgz#7ae4ec257302fd790d557cb10c97100d857b0056" @@ -3304,13 +3306,13 @@ mime-db@~1.26.0: version "1.26.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff" -mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.7, mime-types@2.1.14: +mime-types@2.1.14, mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.7: version "2.1.14" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.14.tgz#f7ef7d97583fcaf3b7d282b6f8b5679dab1e94ee" dependencies: mime-db "~1.26.0" -mime@^1.3.4, mime@1.3.4: +mime@1.3.4, mime@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" @@ -3330,18 +3332,14 @@ minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3: dependencies: brace-expansion "^1.0.0" -minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0, minimist@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - -minimist@0.0.8: +minimist@0.0.8, minimist@~0.0.1: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" +minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + mkdirp-then@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mkdirp-then/-/mkdirp-then-1.2.0.tgz#a492c879ca4d873f5ee45008f8f55fd0150de3c5" @@ -3349,7 +3347,7 @@ mkdirp-then@1.2.0: any-promise "^1.1.0" mkdirp "^0.5.0" -mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.0, mkdirp@~0.5.1: +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -3821,14 +3819,14 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" -punycode@^1.2.4, punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - -punycode@1.3.2: +punycode@1.3.2, punycode@^1.2.4: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + q@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" @@ -3841,7 +3839,7 @@ querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" -querystring@^0.2.0, querystring@0.2.0: +querystring@0.2.0, querystring@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" @@ -4036,7 +4034,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@^2.79.0, request@2.79.0: +request@2.79.0, request@^2.79.0: version "2.79.0" resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" dependencies: @@ -4088,14 +4086,14 @@ resolve-from@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" -resolve@^1.1.6, resolve@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" - resolve@1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" +resolve@^1.1.6, resolve@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" + restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" @@ -4109,7 +4107,7 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@^2.2.8, rimraf@^2.3.3, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@^2.5.4, rimraf@~2.5.1, rimraf@~2.5.4, rimraf@2: +rimraf@2, rimraf@^2.2.8, rimraf@^2.3.3, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@^2.5.4, rimraf@~2.5.1, rimraf@~2.5.4: version "2.5.4" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" dependencies: @@ -4148,7 +4146,7 @@ sax@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828" -semver@^5.1.0, semver@^5.3.0, semver@~5.3.0, "semver@2 || 3 || 4 || 5": +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -4244,7 +4242,7 @@ source-list-map@~0.1.7: version "0.1.8" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106" -source-map-support@^0.4.2, source-map-support@0.4.11: +source-map-support@0.4.11, source-map-support@^0.4.2: version "0.4.11" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.11.tgz#647f939978b38535909530885303daf23279f322" dependencies: @@ -4358,10 +4356,6 @@ stream-http@^2.3.1: to-arraybuffer "^1.0.0" xtend "^4.0.0" -string_decoder@^0.10.25, string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - string-hash@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.1.tgz#8e85bed291e0763b8f6809d9c3368fea048db3dc" @@ -4385,11 +4379,15 @@ string.prototype.codepointat@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.0.tgz#6b26e9bd3afcaa7be3b4269b526de1b82000ac78" +string_decoder@^0.10.25, string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" -strip-ansi@^3.0.0, strip-ansi@^3.0.1, strip-ansi@3.0.1: +strip-ansi@3.0.1, strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" dependencies: @@ -4600,7 +4598,7 @@ uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" -url@^0.11.0, url@0.11.0: +url@0.11.0, url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" dependencies: @@ -4617,13 +4615,13 @@ util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -util@^0.10.3, util@0.10.3: +util@0.10.3, util@^0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" dependencies: inherits "2.0.1" -uuid@^3.0.0, uuid@3.0.1: +uuid@3.0.1, uuid@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" @@ -4745,7 +4743,7 @@ whatwg-encoding@^1.0.1: dependencies: iconv-lite "0.4.13" -whatwg-fetch@>=0.10.0: +whatwg-fetch@>=0.10.0, whatwg-fetch@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.2.tgz#fe294d1d89e36c5be8b3195057f2e4bc74fc980e" @@ -4776,18 +4774,14 @@ window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" +wordwrap@0.0.2, wordwrap@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - worker-farm@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.3.1.tgz#4333112bb49b17aa050b87895ca6b2cacf40e5ff" @@ -4834,7 +4828,7 @@ xml-name-validator@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" -xtend@^4.0.0, xtend@^4.0.1, "xtend@>=4.0.0 <4.1.0-0": +"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -4887,4 +4881,3 @@ zip-stream@^1.1.0: compress-commons "^1.1.0" lodash "^4.8.0" readable-stream "^2.0.0" - From 617c33fe36bcaf012da5f9d3d89ecb063bb4c456 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Tue, 14 Feb 2017 18:44:11 +0530 Subject: [PATCH 11/30] Dispose inactive entries. --- server/hot-reloader.js | 1 + server/on-demand-entry-handler.js | 47 +++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/server/hot-reloader.js b/server/hot-reloader.js index 8e31419c3d66f..a9142f4ac4789 100644 --- a/server/hot-reloader.js +++ b/server/hot-reloader.js @@ -197,6 +197,7 @@ export default class HotReloader { } function deleteCache (path) { + console.log('DELETE', path) delete require.cache[path] delete readPage.cache[path] } diff --git a/server/on-demand-entry-handler.js b/server/on-demand-entry-handler.js index 0c5ee1817a036..72cb7be2162dc 100644 --- a/server/on-demand-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -13,7 +13,6 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de compiler.plugin('make', function (compilation, done) { const allEntries = Object.keys(entries).map((page) => { - console.log('XXXXXX', page) const { name, entry } = entries[page] doingEntries[page] = true return addEntry(compilation, this.context, name, entry) @@ -28,8 +27,9 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de compiler.plugin('done', function (stats) { // Call all the doneCallbacks - Object.keys(doingEntries).forEach((name) => { - doneCallbacks.emit(name) + Object.keys(doingEntries).forEach((page) => { + entries[page].lastActiveTime = Date.now() + doneCallbacks.emit(page) }) completedEntries = doingEntries @@ -38,6 +38,11 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de console.log('DONE') }) + setInterval(function () { + const maxInactiveAge = 1000 * 15 // 15 secs + disposeInactiveEntries(devMiddleware, entries, maxInactiveAge) + }, 5000) + return { async ensurePage (page) { const pagePath = join(dir, 'pages', page) @@ -77,9 +82,22 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de if (!/^\/on-demand-entries-ping/.test(req.url)) return next() const { query } = parse(req.url, true) - console.log(`Hit: ${query.page}`) - res.status = 200 - res.end('Success') + const entry = entries[query.page] + + // If there's an entry + if (entry) { + entry.lastActiveTime = Date.now() + res.status = 200 + res.end('Success') + return + } + + // If there's no entry. + // Then it seems like an weird issue. + const message = `Client pings but we have no entry for page: ${query.page}` + console.error(message) + res.status = 500 + res.end(message) } } } @@ -94,3 +112,20 @@ function addEntry (compilation, context, name, entry) { }) }) } + +function disposeInactiveEntries (devMiddleware, entries, maxAge) { + let disposedCount = 0 + + Object.keys(entries).forEach((page) => { + const { lastActiveTime } = entries[page] + if (Date.now() - lastActiveTime > maxAge) { + console.log('Disposing', page) + disposedCount++ + delete entries[page] + } + }) + + if (disposedCount > 0) { + devMiddleware.invalidate() + } +} From cf0da3f59457522a840a9ae61b31be653127c4c5 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Tue, 14 Feb 2017 20:00:57 +0530 Subject: [PATCH 12/30] Add proper logs. --- server/hot-reloader.js | 1 - server/on-demand-entry-handler.js | 29 ++++++++++++++++++----------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/server/hot-reloader.js b/server/hot-reloader.js index a9142f4ac4789..8e31419c3d66f 100644 --- a/server/hot-reloader.js +++ b/server/hot-reloader.js @@ -197,7 +197,6 @@ export default class HotReloader { } function deleteCache (path) { - console.log('DELETE', path) delete require.cache[path] delete readPage.cache[path] } diff --git a/server/on-demand-entry-handler.js b/server/on-demand-entry-handler.js index 72cb7be2162dc..2136706998e57 100644 --- a/server/on-demand-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -21,8 +21,6 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de Promise.all(allEntries) .then(() => done()) .catch(done) - - console.log('MAKE') }) compiler.plugin('done', function (stats) { @@ -34,8 +32,6 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de completedEntries = doingEntries doingEntries = {} - - console.log('DONE') }) setInterval(function () { @@ -45,6 +41,8 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de return { async ensurePage (page) { + page = normalizePage(page) + const pagePath = join(dir, 'pages', page) const pathname = await resolvePath(pagePath) const name = join('bundles', pathname.substring(dir.length)) @@ -65,6 +63,8 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de return } + console.log(`> Building page: ${page}`) + entries[page] = { name, entry } doneCallbacks.on(page, processCallback) @@ -82,7 +82,8 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de if (!/^\/on-demand-entries-ping/.test(req.url)) return next() const { query } = parse(req.url, true) - const entry = entries[query.page] + const page = normalizePage(query.page) + const entry = entries[page] // If there's an entry if (entry) { @@ -94,7 +95,7 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de // If there's no entry. // Then it seems like an weird issue. - const message = `Client pings but we have no entry for page: ${query.page}` + const message = `Client pings but we have no entry for page: ${page}` console.error(message) res.status = 500 res.end(message) @@ -114,18 +115,24 @@ function addEntry (compilation, context, name, entry) { } function disposeInactiveEntries (devMiddleware, entries, maxAge) { - let disposedCount = 0 + const disposingPages = [] Object.keys(entries).forEach((page) => { const { lastActiveTime } = entries[page] if (Date.now() - lastActiveTime > maxAge) { - console.log('Disposing', page) - disposedCount++ - delete entries[page] + disposingPages.push(page) } }) - if (disposedCount > 0) { + if (disposingPages.length > 0) { + disposingPages.forEach((page) => { + delete entries[page] + }) + console.log(`> Disposing inactive pages: ${disposingPages.join(', ')}`) devMiddleware.invalidate() } } + +function normalizePage (page) { + return page.replace(/\/index$/, '/') +} From f40d425685ff3151ff51dadf8348f67d0c33ee35 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Tue, 14 Feb 2017 22:20:00 +0530 Subject: [PATCH 13/30] Update grammer changes. --- server/build/webpack.js | 2 +- server/on-demand-entry-handler.js | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/server/build/webpack.js b/server/build/webpack.js index 85760f38036ec..bf2687d19ab5b 100644 --- a/server/build/webpack.js +++ b/server/build/webpack.js @@ -38,7 +38,7 @@ export default async function createCompiler (dir, { dev = false, quiet = false const pages = await glob('pages/**/*.js', { cwd: dir }) const devPages = pages.filter((p) => p === 'pages/_document.js' || p === 'pages/_error.js') - // In the dev environment, dynamic pages handler will take care of + // In the dev environment, on-demand-entry-handler will take care of // managing pages. if (dev) { for (const p of devPages) { diff --git a/server/on-demand-entry-handler.js b/server/on-demand-entry-handler.js index 2136706998e57..24f7293cf4f5e 100644 --- a/server/on-demand-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -95,7 +95,7 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de // If there's no entry. // Then it seems like an weird issue. - const message = `Client pings but we have no entry for page: ${page}` + const message = `Client pings, but there's no entry for page: ${page}` console.error(message) res.status = 500 res.end(message) @@ -114,12 +114,12 @@ function addEntry (compilation, context, name, entry) { }) } -function disposeInactiveEntries (devMiddleware, entries, maxAge) { +function disposeInactiveEntries (devMiddleware, entries, maxInactiveAge) { const disposingPages = [] Object.keys(entries).forEach((page) => { const { lastActiveTime } = entries[page] - if (Date.now() - lastActiveTime > maxAge) { + if (Date.now() - lastActiveTime > maxInactiveAge) { disposingPages.push(page) } }) @@ -128,11 +128,13 @@ function disposeInactiveEntries (devMiddleware, entries, maxAge) { disposingPages.forEach((page) => { delete entries[page] }) - console.log(`> Disposing inactive pages: ${disposingPages.join(', ')}`) + console.log(`> Disposing inactive page(s): ${disposingPages.join(', ')}`) devMiddleware.invalidate() } } +// /index and / is the same. So, we need to identify both pages as the same. +// This also applies to sub pages as well. function normalizePage (page) { return page.replace(/\/index$/, '/') } From 620e838d197a3202a2e48c520b0832f86adbdaf0 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Wed, 15 Feb 2017 10:18:03 +0530 Subject: [PATCH 14/30] Add integration tests for ondemand entries. --- server/hot-reloader.js | 6 ++- server/on-demand-entry-handler.js | 7 ++- test/integration/ondemand/next.config.js | 5 ++ test/integration/ondemand/pages/about.js | 5 ++ test/integration/ondemand/pages/index.js | 8 +++ test/integration/ondemand/test/index.test.js | 55 ++++++++++++++++++++ test/lib/next-test-utils.js | 4 ++ 7 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 test/integration/ondemand/next.config.js create mode 100644 test/integration/ondemand/pages/about.js create mode 100644 test/integration/ondemand/pages/index.js create mode 100644 test/integration/ondemand/test/index.test.js diff --git a/server/hot-reloader.js b/server/hot-reloader.js index 8e31419c3d66f..15998aab6ed43 100644 --- a/server/hot-reloader.js +++ b/server/hot-reloader.js @@ -6,6 +6,7 @@ import isWindowsBash from 'is-windows-bash' import webpack from './build/webpack' import clean from './build/clean' import readPage from './read-page' +import getConfig from './config' export default class HotReloader { constructor (dir, { quiet } = {}) { @@ -21,6 +22,8 @@ export default class HotReloader { this.prevChunkNames = null this.prevFailedChunkNames = null this.prevChunkHashes = null + + this.config = getConfig(dir) } async run (req, res) { @@ -148,7 +151,8 @@ export default class HotReloader { this.webpackHotMiddleware = webpackHotMiddleware(compiler, { log: false }) this.onDemandEntries = onDemandEntryHandler(this.webpackDevMiddleware, compiler, { dir: this.dir, - dev: true + dev: true, + ...this.config.onDemandEntries }) this.middlewares = [ diff --git a/server/on-demand-entry-handler.js b/server/on-demand-entry-handler.js index 24f7293cf4f5e..dab05a0c9a342 100644 --- a/server/on-demand-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -4,7 +4,11 @@ import { join } from 'path' import { parse } from 'url' import resolvePath from './resolve' -export default function onDemandEntryHandler (devMiddleware, compiler, { dir, dev }) { +export default function onDemandEntryHandler (devMiddleware, compiler, { + dir, + dev, + maxInactiveAge = 1000 * 25 +}) { const entries = {} let doingEntries = {} let completedEntries = {} @@ -35,7 +39,6 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { dir, de }) setInterval(function () { - const maxInactiveAge = 1000 * 15 // 15 secs disposeInactiveEntries(devMiddleware, entries, maxInactiveAge) }, 5000) diff --git a/test/integration/ondemand/next.config.js b/test/integration/ondemand/next.config.js new file mode 100644 index 0000000000000..bf398f938a3f6 --- /dev/null +++ b/test/integration/ondemand/next.config.js @@ -0,0 +1,5 @@ +module.exports = { + onDemandEntries: { + maxInactiveAge: 1000 * 5 + } +} diff --git a/test/integration/ondemand/pages/about.js b/test/integration/ondemand/pages/about.js new file mode 100644 index 0000000000000..6d08c2a434a46 --- /dev/null +++ b/test/integration/ondemand/pages/about.js @@ -0,0 +1,5 @@ +export default () => ( +
+

About Page

+
+) diff --git a/test/integration/ondemand/pages/index.js b/test/integration/ondemand/pages/index.js new file mode 100644 index 0000000000000..2ebb2236ff740 --- /dev/null +++ b/test/integration/ondemand/pages/index.js @@ -0,0 +1,8 @@ +import Link from 'next/link' + +export default () => ( +
+

Index Page

+ About Page +
+) diff --git a/test/integration/ondemand/test/index.test.js b/test/integration/ondemand/test/index.test.js new file mode 100644 index 0000000000000..6177995ed166e --- /dev/null +++ b/test/integration/ondemand/test/index.test.js @@ -0,0 +1,55 @@ +/* global jasmine, describe, beforeAll, afterAll, it, expect */ +import { join, resolve } from 'path' +import { existsSync } from 'fs' +import { + nextServer, + renderViaHTTP, + startApp, + stopApp, + waitFor +} from 'next-test-utils' +import webdriver from 'next-webdriver' + +const context = {} +context.app = nextServer({ + dir: join(__dirname, '../'), + dev: true, + quiet: true +}) + +jasmine.DEFAULT_TIMEOUT_INTERVAL = 40000 + +describe('On Demand Entries', () => { + beforeAll(async () => { + context.server = await startApp(context.app) + context.appPort = context.server.address().port + }) + afterAll(() => stopApp(context.server)) + + it('should compile pages for SSR', async () => { + const pageContent = await renderViaHTTP(context.appPort, '/') + expect(pageContent.includes('Index Page')).toBeTruthy() + }) + + it('should compile pages with client side ', async () => { + const browser = await webdriver(context.appPort, '/') + await browser.elementByCss('#about-link').click() + + // Wait for 3 secs until the pages gets compiled + await waitFor(1000 * 3) + const pageContent = await browser.elementByCss('p').text() + + expect(pageContent.includes('About Page')).toBeTruthy() + await browser.close() + }) + + it('should dispose inactive pages', async () => { + const aboutPagePath = resolve(__dirname, '../.next/bundles/pages/about.json') + expect(existsSync(aboutPagePath)).toBeTruthy() + + // Wait for page to get disposed + await waitFor(1000 * 15) + + expect(existsSync(aboutPagePath)).toBeFalsy() + }) +}) diff --git a/test/lib/next-test-utils.js b/test/lib/next-test-utils.js index 8930c581a29e9..f02ec061b4c88 100644 --- a/test/lib/next-test-utils.js +++ b/test/lib/next-test-utils.js @@ -47,3 +47,7 @@ function promiseCall (obj, method, ...args) { obj[method](...newArgs) }) } + +export function waitFor (millis) { + return new Promise((resolve) => setTimeout(resolve, millis)) +} From bea21d51588993d1a4074224a84e89a8c87b5f00 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Wed, 15 Feb 2017 10:44:42 +0530 Subject: [PATCH 15/30] Improve ondemand entry disposing logic. --- server/on-demand-entry-handler.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/server/on-demand-entry-handler.js b/server/on-demand-entry-handler.js index dab05a0c9a342..a48df9aa0751b 100644 --- a/server/on-demand-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -10,7 +10,7 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { maxInactiveAge = 1000 * 25 }) { const entries = {} - let doingEntries = {} + let buildingEntries = {} let completedEntries = {} const doneCallbacks = new EventEmitter() @@ -18,7 +18,7 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { compiler.plugin('make', function (compilation, done) { const allEntries = Object.keys(entries).map((page) => { const { name, entry } = entries[page] - doingEntries[page] = true + buildingEntries[page] = true return addEntry(compilation, this.context, name, entry) }) @@ -29,13 +29,13 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { compiler.plugin('done', function (stats) { // Call all the doneCallbacks - Object.keys(doingEntries).forEach((page) => { + Object.keys(buildingEntries).forEach((page) => { entries[page].lastActiveTime = Date.now() doneCallbacks.emit(page) }) - completedEntries = doingEntries - doingEntries = {} + completedEntries = buildingEntries + buildingEntries = {} }) setInterval(function () { @@ -88,6 +88,9 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { const page = normalizePage(query.page) const entry = entries[page] + // We don't need to maintain active state for currently building entries + if (buildingEntries[page]) return + // If there's an entry if (entry) { entry.lastActiveTime = Date.now() @@ -122,6 +125,11 @@ function disposeInactiveEntries (devMiddleware, entries, maxInactiveAge) { Object.keys(entries).forEach((page) => { const { lastActiveTime } = entries[page] + + // This means this entry is currently building + // We don't need to dispose those entries. + if (!lastActiveTime) return + if (Date.now() - lastActiveTime > maxInactiveAge) { disposingPages.push(page) } From d32c56ad35147daf517c89caa5c0d1ef86b73d5a Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Wed, 15 Feb 2017 11:54:08 +0530 Subject: [PATCH 16/30] Try to improve testing. --- client/on-demand-entries-client.js | 4 ++-- test/integration/ondemand/test/index.test.js | 23 ++++++++------------ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/client/on-demand-entries-client.js b/client/on-demand-entries-client.js index 17c437e1026b0..0ab9c0d3c1569 100644 --- a/client/on-demand-entries-client.js +++ b/client/on-demand-entries-client.js @@ -10,8 +10,8 @@ Router.onRouteChangeComplete = function (...args) { originalOnRouteChangeComplete(...args) } -// Ping every 5 seconds -setInterval(ping, 5000) +// Ping every 3 seconds +setInterval(ping, 3000) function ping () { const url = `/on-demand-entries-ping?page=${Router.pathname}` diff --git a/test/integration/ondemand/test/index.test.js b/test/integration/ondemand/test/index.test.js index 6177995ed166e..0726a8d446937 100644 --- a/test/integration/ondemand/test/index.test.js +++ b/test/integration/ondemand/test/index.test.js @@ -8,7 +8,6 @@ import { stopApp, waitFor } from 'next-test-utils' -import webdriver from 'next-webdriver' const context = {} context.app = nextServer({ @@ -31,25 +30,21 @@ describe('On Demand Entries', () => { expect(pageContent.includes('Index Page')).toBeTruthy() }) - it('should compile pages with client side ', async () => { - const browser = await webdriver(context.appPort, '/') - await browser.elementByCss('#about-link').click() - - // Wait for 3 secs until the pages gets compiled - await waitFor(1000 * 3) - const pageContent = await browser.elementByCss('p').text() - + it('should compile pages for JSON page requests', async () => { + const pageContent = await renderViaHTTP(context.appPort, '/_next/-/pages/about') expect(pageContent.includes('About Page')).toBeTruthy() - await browser.close() }) it('should dispose inactive pages', async () => { + await renderViaHTTP(context.appPort, '/_next/-/pages/about') const aboutPagePath = resolve(__dirname, '../.next/bundles/pages/about.json') expect(existsSync(aboutPagePath)).toBeTruthy() - // Wait for page to get disposed - await waitFor(1000 * 15) - - expect(existsSync(aboutPagePath)).toBeFalsy() + // Wait maximum of jasmine.DEFAULT_TIMEOUT_INTERVAL checking + // for disposing /about + while (true) { + await waitFor(1000 * 1) + if (!existsSync(aboutPagePath)) return + } }) }) From 8a495aa9fe95e78db29ff6b6ca0d80b5185d0730 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Wed, 15 Feb 2017 12:05:27 +0530 Subject: [PATCH 17/30] Make sure entries are not getting disposed in basic integration tests. --- test/integration/basic/next.config.js | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 test/integration/basic/next.config.js diff --git a/test/integration/basic/next.config.js b/test/integration/basic/next.config.js new file mode 100644 index 0000000000000..35dcf0f6b8744 --- /dev/null +++ b/test/integration/basic/next.config.js @@ -0,0 +1,6 @@ +module.exports = { + onDemandEntries: { + // Make sure entries are not getting disposed. + maxInactiveAge: 1000 * 60 * 60 + } +} From fbca518521ffb085bb2c2a48a9669e5bf3f390b4 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Thu, 16 Feb 2017 01:48:33 +0530 Subject: [PATCH 18/30] Resolve conflicts. --- lib/router/router.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/router/router.js b/lib/router/router.js index cbbca7241c2ae..9f8d2d4f47089 100644 --- a/lib/router/router.js +++ b/lib/router/router.js @@ -226,7 +226,7 @@ export default class Router extends EventEmitter { async prefetch (url) { // We don't add support for prefetch in the development mode. // If we do that, our on-demand-entries optimization won't performs better - if (process.env.NODE_ENV === 'development') return + if (process.env.NODE_ENV !== 'production') return const { pathname } = parse(url) const route = toRoute(pathname) From 44d65b28f46d302c2678a881303838e26e1e654b Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Thu, 16 Feb 2017 03:34:31 +0530 Subject: [PATCH 19/30] Fix tests. --- lib/router/router.js | 2 +- server/build/webpack.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/router/router.js b/lib/router/router.js index 9f8d2d4f47089..cbbca7241c2ae 100644 --- a/lib/router/router.js +++ b/lib/router/router.js @@ -226,7 +226,7 @@ export default class Router extends EventEmitter { async prefetch (url) { // We don't add support for prefetch in the development mode. // If we do that, our on-demand-entries optimization won't performs better - if (process.env.NODE_ENV !== 'production') return + if (process.env.NODE_ENV === 'development') return const { pathname } = parse(url) const route = toRoute(pathname) diff --git a/server/build/webpack.js b/server/build/webpack.js index f34d56290d7f6..761cf52503765 100644 --- a/server/build/webpack.js +++ b/server/build/webpack.js @@ -87,6 +87,9 @@ export default async function createCompiler (dir, { dev = false, quiet = false, return count >= minChunks } }), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify(dev ? 'development' : 'production') + }), new JsonPagesPlugin(), new CaseSensitivePathPlugin() ] @@ -102,9 +105,6 @@ export default async function createCompiler (dir, { dev = false, quiet = false, } } else { plugins.push( - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify('production') - }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false }, sourceMap: false From e5cf79b7a3497ec48b9e0b3f798932a1d1547eec Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Thu, 16 Feb 2017 04:56:22 +0530 Subject: [PATCH 20/30] Fix issue when running Router.onRouteChangeComplete --- client/on-demand-entries-client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/on-demand-entries-client.js b/client/on-demand-entries-client.js index 0ab9c0d3c1569..36d2f8c87429d 100644 --- a/client/on-demand-entries-client.js +++ b/client/on-demand-entries-client.js @@ -7,7 +7,7 @@ import 'whatwg-fetch' const originalOnRouteChangeComplete = Router.onRouteChangeComplete Router.onRouteChangeComplete = function (...args) { ping() - originalOnRouteChangeComplete(...args) + if (originalOnRouteChangeComplete) originalOnRouteChangeComplete(...args) } // Ping every 3 seconds From acaa301abd7e91f422de9c57ae214db475685e5d Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Fri, 17 Feb 2017 01:32:05 +0530 Subject: [PATCH 21/30] Simplify state management. --- server/on-demand-entry-handler.js | 75 +++++++++++++++++-------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/server/on-demand-entry-handler.js b/server/on-demand-entry-handler.js index a48df9aa0751b..e77cade4673fc 100644 --- a/server/on-demand-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -4,21 +4,22 @@ import { join } from 'path' import { parse } from 'url' import resolvePath from './resolve' +const ADDED = Symbol() +const BUILDING = Symbol() +const BUILT = Symbol() + export default function onDemandEntryHandler (devMiddleware, compiler, { dir, dev, maxInactiveAge = 1000 * 25 }) { const entries = {} - let buildingEntries = {} - let completedEntries = {} - const doneCallbacks = new EventEmitter() compiler.plugin('make', function (compilation, done) { const allEntries = Object.keys(entries).map((page) => { const { name, entry } = entries[page] - buildingEntries[page] = true + entries[page].status = BUILDING return addEntry(compilation, this.context, name, entry) }) @@ -29,13 +30,14 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { compiler.plugin('done', function (stats) { // Call all the doneCallbacks - Object.keys(buildingEntries).forEach((page) => { + Object.keys(entries).forEach((page) => { + const entryInfo = entries[page] + if (entryInfo.status !== BUILDING) return + + entryInfo.status = BUILT entries[page].lastActiveTime = Date.now() doneCallbacks.emit(page) }) - - completedEntries = buildingEntries - buildingEntries = {} }) setInterval(function () { @@ -57,18 +59,23 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { ] await new Promise((resolve, reject) => { - if (completedEntries[page]) { - return resolve() - } - - if (entries[page]) { - doneCallbacks.on(page, processCallback) - return + const entryInfo = entries[page] + + if (entryInfo) { + if (entryInfo.status === BUILT) { + resolve() + return + } + + if (entryInfo.status === BUILDING) { + doneCallbacks.on(page, processCallback) + return + } } console.log(`> Building page: ${page}`) - entries[page] = { name, entry } + entries[page] = { name, entry, status: ADDED } doneCallbacks.on(page, processCallback) devMiddleware.invalidate() @@ -86,25 +93,25 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { const { query } = parse(req.url, true) const page = normalizePage(query.page) - const entry = entries[page] + const entryInfo = entries[page] - // We don't need to maintain active state for currently building entries - if (buildingEntries[page]) return - - // If there's an entry - if (entry) { - entry.lastActiveTime = Date.now() - res.status = 200 - res.end('Success') + // If there's no entry. + // Then it seems like an weird issue. + if (!entryInfo) { + const message = `Client pings, but there's no entry for page: ${page}` + console.error(message) + res.status = 500 + res.end(message) return } - // If there's no entry. - // Then it seems like an weird issue. - const message = `Client pings, but there's no entry for page: ${page}` - console.error(message) - res.status = 500 - res.end(message) + // We don't need to maintain active state of anything other than BUILT entries + if (entryInfo.status !== BUILT) return + + // If there's an entryInfo + entryInfo.lastActiveTime = Date.now() + res.status = 200 + res.end('Success') } } } @@ -124,11 +131,11 @@ function disposeInactiveEntries (devMiddleware, entries, maxInactiveAge) { const disposingPages = [] Object.keys(entries).forEach((page) => { - const { lastActiveTime } = entries[page] + const { lastActiveTime, status } = entries[page] - // This means this entry is currently building + // This means this entry is currently building or just added // We don't need to dispose those entries. - if (!lastActiveTime) return + if (status !== BUILT) return if (Date.now() - lastActiveTime > maxInactiveAge) { disposingPages.push(page) From 0256e9d9f6f0c10ddf10972d64968a56c3ddb886 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Fri, 24 Feb 2017 05:47:43 +0530 Subject: [PATCH 22/30] Make sure we don't dispose the last active page. --- server/on-demand-entry-handler.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/on-demand-entry-handler.js b/server/on-demand-entry-handler.js index e77cade4673fc..83b4fabaa85b5 100644 --- a/server/on-demand-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -14,6 +14,7 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { maxInactiveAge = 1000 * 25 }) { const entries = {} + const lastAccessPages = [''] const doneCallbacks = new EventEmitter() compiler.plugin('make', function (compilation, done) { @@ -41,7 +42,7 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { }) setInterval(function () { - disposeInactiveEntries(devMiddleware, entries, maxInactiveAge) + disposeInactiveEntries(devMiddleware, entries, lastAccessPages, maxInactiveAge) }, 5000) return { @@ -109,6 +110,8 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { if (entryInfo.status !== BUILT) return // If there's an entryInfo + lastAccessPages.pop() + lastAccessPages.unshift(page) entryInfo.lastActiveTime = Date.now() res.status = 200 res.end('Success') @@ -127,7 +130,7 @@ function addEntry (compilation, context, name, entry) { }) } -function disposeInactiveEntries (devMiddleware, entries, maxInactiveAge) { +function disposeInactiveEntries (devMiddleware, entries, lastAccessPages, maxInactiveAge) { const disposingPages = [] Object.keys(entries).forEach((page) => { @@ -137,6 +140,11 @@ function disposeInactiveEntries (devMiddleware, entries, maxInactiveAge) { // We don't need to dispose those entries. if (status !== BUILT) return + // We should not build the last accessed page even we didn't get any pings + // Sometimes, it's possible our XHR ping to wait before completing other requests. + // In that case, we should not dispose the current viewing page + if (lastAccessPages[0] === page) return + if (Date.now() - lastActiveTime > maxInactiveAge) { disposingPages.push(page) } From 349579a69097a5cc40f166c057f4098bb99093f3 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Fri, 24 Feb 2017 06:09:02 +0530 Subject: [PATCH 23/30] Reload invalid pages detected with the client side ping. --- client/on-demand-entries-client.js | 10 +++++++++- server/on-demand-entry-handler.js | 13 +++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/client/on-demand-entries-client.js b/client/on-demand-entries-client.js index 36d2f8c87429d..f482a7364e717 100644 --- a/client/on-demand-entries-client.js +++ b/client/on-demand-entries-client.js @@ -1,4 +1,4 @@ -/* global fetch */ +/* global fetch, location */ import Router from '../lib/router' import 'whatwg-fetch' @@ -16,6 +16,14 @@ setInterval(ping, 3000) function ping () { const url = `/on-demand-entries-ping?page=${Router.pathname}` fetch(url) + .then((res) => { + return res.json() + }) + .then((payload) => { + if (payload.invalid) { + location.reload() + } + }) .catch((err) => { console.error(`Error with on-demand-entries-ping: ${err.message}`) }) diff --git a/server/on-demand-entry-handler.js b/server/on-demand-entry-handler.js index 83b4fabaa85b5..42916c6330fd1 100644 --- a/server/on-demand-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -101,8 +101,7 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { if (!entryInfo) { const message = `Client pings, but there's no entry for page: ${page}` console.error(message) - res.status = 500 - res.end(message) + sendJson(res, { invalid: true }) return } @@ -113,8 +112,8 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { lastAccessPages.pop() lastAccessPages.unshift(page) entryInfo.lastActiveTime = Date.now() - res.status = 200 - res.end('Success') + + sendJson(res, { success: true }) } } } @@ -164,3 +163,9 @@ function disposeInactiveEntries (devMiddleware, entries, lastAccessPages, maxIna function normalizePage (page) { return page.replace(/\/index$/, '/') } + +function sendJson (res, payload) { + res.setHeader('Content-Type', 'application/json') + res.status = 200 + res.end(JSON.stringify(payload)) +} From ddf6a0ccf1663b7bb0cb462f4b4b5a09a2792d34 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Sat, 25 Feb 2017 11:12:49 +0530 Subject: [PATCH 24/30] Improve the pinger code. --- client/on-demand-entries-client.js | 38 +++++++++++++++++------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/client/on-demand-entries-client.js b/client/on-demand-entries-client.js index f482a7364e717..1aede01304de9 100644 --- a/client/on-demand-entries-client.js +++ b/client/on-demand-entries-client.js @@ -10,21 +10,27 @@ Router.onRouteChangeComplete = function (...args) { if (originalOnRouteChangeComplete) originalOnRouteChangeComplete(...args) } -// Ping every 3 seconds -setInterval(ping, 3000) +async function ping () { + try { + const url = `/on-demand-entries-ping?page=${Router.pathname}` + const res = await fetch(url) + const payload = await res.json() + if (payload.invalid) { + location.reload() + } + } catch (err) { + console.error(`Error with on-demand-entries-ping: ${err.message}`) + } +} -function ping () { - const url = `/on-demand-entries-ping?page=${Router.pathname}` - fetch(url) - .then((res) => { - return res.json() - }) - .then((payload) => { - if (payload.invalid) { - location.reload() - } - }) - .catch((err) => { - console.error(`Error with on-demand-entries-ping: ${err.message}`) - }) +async function runPinger () { + while (true) { + await new Promise((resolve) => setTimeout(resolve, 5000)) + await ping() + } } + +runPinger() + .catch((err) => { + console.error(err) + }) From d11c5b3bb5f8f08f59b30a8b5882ac9ce5ffdd92 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Sat, 25 Feb 2017 12:08:18 +0530 Subject: [PATCH 25/30] Touch the first page to speed up the future rebuild times. --- package.json | 1 + server/on-demand-entry-handler.js | 15 ++++++++++++++- yarn.lock | 12 ++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 5c99c9f27fb9b..cc6af5e7e9d59 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "source-map-support": "0.4.11", "strip-ansi": "3.0.1", "styled-jsx": "0.5.7", + "touch": "^1.0.0", "url": "0.11.0", "uuid": "3.0.1", "webpack": "2.2.1", diff --git a/server/on-demand-entry-handler.js b/server/on-demand-entry-handler.js index 42916c6330fd1..565bbbbba23d9 100644 --- a/server/on-demand-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -3,6 +3,7 @@ import { EventEmitter } from 'events' import { join } from 'path' import { parse } from 'url' import resolvePath from './resolve' +import touch from 'touch' const ADDED = Symbol() const BUILDING = Symbol() @@ -16,6 +17,7 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { const entries = {} const lastAccessPages = [''] const doneCallbacks = new EventEmitter() + let touchedAPage = false compiler.plugin('make', function (compilation, done) { const allEntries = Object.keys(entries).map((page) => { @@ -35,6 +37,17 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { const entryInfo = entries[page] if (entryInfo.status !== BUILDING) return + // With this, we are triggering a filesystem based watch trigger + // It'll memorize some timestamp related info related to common files used + // in the page + // That'll reduce the page building time significantly. + if (!touchedAPage) { + setTimeout(() => { + touch.sync(entryInfo.pathname) + }, 0) + touchedAPage = true + } + entryInfo.status = BUILT entries[page].lastActiveTime = Date.now() doneCallbacks.emit(page) @@ -76,7 +89,7 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { console.log(`> Building page: ${page}`) - entries[page] = { name, entry, status: ADDED } + entries[page] = { name, entry, pathname, status: ADDED } doneCallbacks.on(page, processCallback) devMiddleware.invalidate() diff --git a/yarn.lock b/yarn.lock index 6ae6505459e10..aec05d0d24300 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3468,6 +3468,12 @@ node-pre-gyp@^0.6.29: tar "~2.2.1" tar-pack "~3.3.0" +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + dependencies: + abbrev "1" + nopt@~3.0.1, nopt@~3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -4596,6 +4602,12 @@ to-fast-properties@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" +touch@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-1.0.0.tgz#449cbe2dbae5a8c8038e30d71fa0ff464947c4de" + dependencies: + nopt "~1.0.10" + tough-cookie@^2.3.2, tough-cookie@~2.3.0: version "2.3.2" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" From f706a49a3d886d0231259b7a1fded750ced2e48f Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Sun, 26 Feb 2017 00:25:17 +0530 Subject: [PATCH 26/30] Add Websockets based pinger. --- client/on-demand-entries-client.js | 76 +++++++++++++------- package.json | 7 +- server/hot-reloader.js | 5 +- server/on-demand-entry-handler.js | 85 +++++++++++++++------- yarn.lock | 112 ++++++++++++++++++++++++++--- 5 files changed, 225 insertions(+), 60 deletions(-) diff --git a/client/on-demand-entries-client.js b/client/on-demand-entries-client.js index 1aede01304de9..22c0924c1c7e8 100644 --- a/client/on-demand-entries-client.js +++ b/client/on-demand-entries-client.js @@ -1,36 +1,64 @@ -/* global fetch, location */ +/* global fetch, location, WebSocket */ import Router from '../lib/router' import 'whatwg-fetch' -// Ping on every page change -const originalOnRouteChangeComplete = Router.onRouteChangeComplete -Router.onRouteChangeComplete = function (...args) { - ping() - if (originalOnRouteChangeComplete) originalOnRouteChangeComplete(...args) +initiatePinging() + .catch((err) => { + console.error(err.stack) + }) + +async function initiatePinging () { + const res = await fetch('/on-demand-entries-pinger-port') + const payload = await res.json() + startPinging(payload.port) } -async function ping () { - try { - const url = `/on-demand-entries-ping?page=${Router.pathname}` - const res = await fetch(url) - const payload = await res.json() - if (payload.invalid) { - location.reload() +async function retry () { + while (true) { + try { + await initiatePinging() + break + } catch (err) { + await new Promise((resolve) => setTimeout(resolve, 2000)) } - } catch (err) { - console.error(`Error with on-demand-entries-ping: ${err.message}`) } } -async function runPinger () { - while (true) { - await new Promise((resolve) => setTimeout(resolve, 5000)) - await ping() +function startPinging (port) { + const conn = new WebSocket(`ws://localhost:${port}/`, 'protocolOne') + let pingingHandler = null + conn.onopen = () => { + console.log('> Start pinging for the active page.') + ping() + pingingHandler = setInterval(ping, 1000 * 5) } -} -runPinger() - .catch((err) => { - console.error(err) - }) + conn.onmessage = (event) => { + const parsedMessage = JSON.parse(event.data) + if (parsedMessage.invalid) { + location.reload() + } + } + + conn.onclose = () => { + clearInterval(pingingHandler) + retry() + } + + conn.onerror = (error) => { + console.log(`Pinger error: ${error.message}`) + } + + // Ping on every page change + const originalOnRouteChangeComplete = Router.onRouteChangeComplete + Router.onRouteChangeComplete = function (...args) { + ping() + if (originalOnRouteChangeComplete) originalOnRouteChangeComplete(...args) + } + + function ping () { + const payload = { page: Router.pathname } + conn.send(JSON.stringify(payload)) + } +} diff --git a/package.json b/package.json index cc6af5e7e9d59..98a28020ecf07 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "babel-preset-latest": "6.22.0", "babel-preset-react": "6.23.0", "babel-runtime": "6.23.0", + "bufferutil": "2.0.1", "case-sensitive-paths-webpack-plugin": "1.1.4", "cross-spawn": "5.0.1", "del": "2.2.2", @@ -79,14 +80,16 @@ "source-map-support": "0.4.11", "strip-ansi": "3.0.1", "styled-jsx": "0.5.7", - "touch": "^1.0.0", + "touch": "1.0.0", "url": "0.11.0", + "utf-8-validate": "3.0.1", "uuid": "3.0.1", "webpack": "2.2.1", "webpack-dev-middleware": "1.10.1", "webpack-hot-middleware": "2.17.0", "whatwg-fetch": "2.0.2", - "write-file-webpack-plugin": "3.4.2" + "write-file-webpack-plugin": "3.4.2", + "ws": "2.1.0" }, "devDependencies": { "babel-eslint": "7.1.1", diff --git a/server/hot-reloader.js b/server/hot-reloader.js index 15998aab6ed43..836a88b0e00e9 100644 --- a/server/hot-reloader.js +++ b/server/hot-reloader.js @@ -49,12 +49,13 @@ export default class HotReloader { async stop () { if (this.webpackDevMiddleware) { - return new Promise((resolve, reject) => { + await new Promise((resolve, reject) => { this.webpackDevMiddleware.close((err) => { if (err) return reject(err) resolve() }) }) + await this.onDemandEntries.close() } } @@ -149,7 +150,7 @@ export default class HotReloader { }) this.webpackHotMiddleware = webpackHotMiddleware(compiler, { log: false }) - this.onDemandEntries = onDemandEntryHandler(this.webpackDevMiddleware, compiler, { + this.onDemandEntries = await onDemandEntryHandler(this.webpackDevMiddleware, compiler, { dir: this.dir, dev: true, ...this.config.onDemandEntries diff --git a/server/on-demand-entry-handler.js b/server/on-demand-entry-handler.js index 565bbbbba23d9..0af1ccc9f850a 100644 --- a/server/on-demand-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -1,19 +1,23 @@ import DynamicEntryPlugin from 'webpack/lib/DynamicEntryPlugin' import { EventEmitter } from 'events' +import { createServer } from 'http' import { join } from 'path' -import { parse } from 'url' import resolvePath from './resolve' import touch from 'touch' +import WebSocket from 'ws' const ADDED = Symbol() const BUILDING = Symbol() const BUILT = Symbol() -export default function onDemandEntryHandler (devMiddleware, compiler, { +export default async function onDemandEntryHandler (devMiddleware, compiler, { dir, dev, maxInactiveAge = 1000 * 25 }) { + const server = await getServer() + const wss = new WebSocket.Server({ server }) + const entries = {} const lastAccessPages = [''] const doneCallbacks = new EventEmitter() @@ -58,6 +62,31 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { disposeInactiveEntries(devMiddleware, entries, lastAccessPages, maxInactiveAge) }, 5000) + wss.on('connection', (conn) => { + conn.on('message', (message) => { + const parsedMessage = JSON.parse(message) + const page = normalizePage(parsedMessage.page) + const entryInfo = entries[page] + + // If there's no entry. + // Then it seems like an weird issue. + if (!entryInfo) { + const message = `Client pings, but there's no entry for page: ${page}` + console.error(message) + conn.send(JSON.stringify({ invalid: true })) + return + } + + // We don't need to maintain active state of anything other than BUILT entries + if (entryInfo.status !== BUILT) return + + // If there's an entryInfo + lastAccessPages.pop() + lastAccessPages.unshift(page) + entryInfo.lastActiveTime = Date.now() + }) + }) + return { async ensurePage (page) { page = normalizePage(page) @@ -103,31 +132,25 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { middleware () { return function (req, res, next) { - if (!/^\/on-demand-entries-ping/.test(req.url)) return next() - - const { query } = parse(req.url, true) - const page = normalizePage(query.page) - const entryInfo = entries[page] + if (!/^\/on-demand-entries-pinger-port/.test(req.url)) return next() - // If there's no entry. - // Then it seems like an weird issue. - if (!entryInfo) { - const message = `Client pings, but there's no entry for page: ${page}` - console.error(message) - sendJson(res, { invalid: true }) - return - } - - // We don't need to maintain active state of anything other than BUILT entries - if (entryInfo.status !== BUILT) return + sendJson(res, { + port: server.address().port + }) + } + }, - // If there's an entryInfo - lastAccessPages.pop() - lastAccessPages.unshift(page) - entryInfo.lastActiveTime = Date.now() + close () { + return new Promise((resolve, reject) => { + server.close((err) => { + if (err) { + reject(err) + return + } - sendJson(res, { success: true }) - } + resolve() + }) + }) } } } @@ -182,3 +205,17 @@ function sendJson (res, payload) { res.status = 200 res.end(JSON.stringify(payload)) } + +function getServer () { + return new Promise((resolve, reject) => { + const server = createServer() + server.listen((err) => { + if (err) { + reject(err) + return + } + + resolve(server) + }) + }) +} diff --git a/yarn.lock b/yarn.lock index aec05d0d24300..8ec3446bede9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -977,6 +977,10 @@ binary-extensions@^1.0.0: version "1.8.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" +bindings@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" + bl@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.0.tgz#1397e7ec42c5f5dc387470c500e34a9f6be9ea98" @@ -1115,6 +1119,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +bufferutil@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-2.0.1.tgz#8de37f5a300730c305fc3edd9f93348ee8a46288" + dependencies: + bindings "~1.2.1" + nan "~2.5.0" + prebuild-install "~2.1.0" + builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -1214,6 +1226,10 @@ chokidar@^1.4.3, chokidar@^1.6.1: optionalDependencies: fsevents "^1.0.0" +chownr@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" + chromedriver@2.26.1: version "2.26.1" resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-2.26.1.tgz#60036732224580d2699afd2626f5f26ea1f6c9fe" @@ -1684,7 +1700,7 @@ encoding@^0.1.11: dependencies: iconv-lite "~0.4.13" -end-of-stream@^1.0.0: +end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.1.0.tgz#e9353258baa9108965efc41cb0ef8ade2f3cfb07" dependencies: @@ -1967,6 +1983,10 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" +expand-template@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-1.0.3.tgz#6c303323177a62b1b22c070279f7861287b69b1a" + extend@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" @@ -2250,6 +2270,10 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + glamor@2.20.23: version "2.20.23" resolved "https://registry.yarnpkg.com/glamor/-/glamor-2.20.23.tgz#aa597b9b0e8835258d0655b19687dc4e772e6c49" @@ -3394,7 +3418,7 @@ mz@2.6.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.3.0: +nan@^2.3.0, nan@~2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2" @@ -3406,6 +3430,10 @@ ncp@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" +node-abi@^1.0.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-1.3.3.tgz#0f06f2815deba26107959d2213b36ce97437e6e2" + node-fetch@1.6.3, node-fetch@^1.0.1: version "1.6.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" @@ -3468,6 +3496,10 @@ node-pre-gyp@^0.6.29: tar "~2.2.1" tar-pack "~3.3.0" +noop-logger@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" + nopt@~1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" @@ -3600,7 +3632,7 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -once@^1.3.0, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -3797,6 +3829,24 @@ pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" +prebuild-install@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.1.0.tgz#526c7b3ed1e2707a247f7c040719173a321bc14f" + dependencies: + expand-template "^1.0.2" + github-from-package "0.0.0" + minimist "^1.2.0" + node-abi "^1.0.3" + noop-logger "^0.1.1" + npmlog "^4.0.1" + os-homedir "^1.0.1" + pump "^1.0.1" + rc "^1.1.6" + simple-get "^1.4.2" + tar-fs "^1.13.0" + tunnel-agent "^0.4.3" + xtend "4.0.1" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -3859,6 +3909,13 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" +pump@^1.0.0, pump@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.2.tgz#3b3ee6512f94f0e575538c17995f9f16990a5d51" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@1.3.2, punycode@^1.2.4: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -3898,7 +3955,7 @@ range-parser@^1.0.3, range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" -rc@~1.1.6: +rc@^1.1.6, rc@~1.1.6: version "1.1.7" resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.7.tgz#c5ea564bb07aff9fd3a5b32e906c1d3a65940fea" dependencies: @@ -4271,6 +4328,14 @@ signal-exit@^3.0.0, signal-exit@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" +simple-get@^1.4.2: + version "1.4.3" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-1.4.3.tgz#e9755eda407e96da40c5e5158c9ea37b33becbeb" + dependencies: + once "^1.3.1" + unzip-response "^1.0.0" + xtend "^4.0.0" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -4510,6 +4575,15 @@ tapable@^0.2.5, tapable@~0.2.5: version "0.2.6" resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d" +tar-fs@^1.13.0: + version "1.15.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.15.1.tgz#f4622f5d5e250742b3679a9a8463acfc12cdefd1" + dependencies: + chownr "^1.0.1" + mkdirp "^0.5.0" + pump "^1.0.0" + tar-stream "^1.1.2" + tar-pack@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.3.0.tgz#30931816418f55afc4d21775afdd6720cee45dae" @@ -4523,7 +4597,7 @@ tar-pack@~3.3.0: tar "~2.2.1" uid-number "~0.0.6" -tar-stream@^1.5.0: +tar-stream@^1.1.2, tar-stream@^1.5.0: version "1.5.2" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.2.tgz#fbc6c6e83c1a19d4cb48c7d96171fc248effc7bf" dependencies: @@ -4602,7 +4676,7 @@ to-fast-properties@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" -touch@^1.0.0: +touch@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/touch/-/touch-1.0.0.tgz#449cbe2dbae5a8c8038e30d71fa0ff464947c4de" dependencies: @@ -4630,7 +4704,7 @@ tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" -tunnel-agent@~0.4.1: +tunnel-agent@^0.4.3, tunnel-agent@~0.4.1: version "0.4.3" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" @@ -4673,6 +4747,10 @@ uid-number@~0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" +ultron@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.0.tgz#b07a2e6a541a815fc6a34ccd4533baec307ca864" + underscore.string@3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.4.tgz#2c2a3f9f83e64762fdc45e6ceac65142864213db" @@ -4684,6 +4762,10 @@ uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" +unzip-response@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe" + url@0.11.0, url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -4697,6 +4779,14 @@ user-home@^2.0.0: dependencies: os-homedir "^1.0.0" +utf-8-validate@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-3.0.1.tgz#5d2b8656b4ddcfded47217b647a98941b63cf213" + dependencies: + bindings "~1.2.1" + nan "~2.5.0" + prebuild-install "~2.1.0" + util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -4914,11 +5004,17 @@ write@^0.2.1: dependencies: mkdirp "^0.5.1" +ws@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-2.1.0.tgz#b24eaed9609f8632dd51e3f7698619a90fddcc92" + dependencies: + ultron "~1.1.0" + xml-name-validator@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" -"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1: +xtend@4.0.1, "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" From 3166858e32dbeece783c48de1bf4cf1951779440 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Sun, 26 Feb 2017 18:15:18 +0530 Subject: [PATCH 27/30] Revert "Add Websockets based pinger." This reverts commit f706a49a3d886d0231259b7a1fded750ced2e48f. --- client/on-demand-entries-client.js | 76 +++++++------------- package.json | 7 +- server/hot-reloader.js | 5 +- server/on-demand-entry-handler.js | 85 +++++++--------------- yarn.lock | 112 +++-------------------------- 5 files changed, 60 insertions(+), 225 deletions(-) diff --git a/client/on-demand-entries-client.js b/client/on-demand-entries-client.js index 22c0924c1c7e8..1aede01304de9 100644 --- a/client/on-demand-entries-client.js +++ b/client/on-demand-entries-client.js @@ -1,64 +1,36 @@ -/* global fetch, location, WebSocket */ +/* global fetch, location */ import Router from '../lib/router' import 'whatwg-fetch' -initiatePinging() - .catch((err) => { - console.error(err.stack) - }) - -async function initiatePinging () { - const res = await fetch('/on-demand-entries-pinger-port') - const payload = await res.json() - startPinging(payload.port) +// Ping on every page change +const originalOnRouteChangeComplete = Router.onRouteChangeComplete +Router.onRouteChangeComplete = function (...args) { + ping() + if (originalOnRouteChangeComplete) originalOnRouteChangeComplete(...args) } -async function retry () { - while (true) { - try { - await initiatePinging() - break - } catch (err) { - await new Promise((resolve) => setTimeout(resolve, 2000)) - } - } -} - -function startPinging (port) { - const conn = new WebSocket(`ws://localhost:${port}/`, 'protocolOne') - let pingingHandler = null - conn.onopen = () => { - console.log('> Start pinging for the active page.') - ping() - pingingHandler = setInterval(ping, 1000 * 5) - } - - conn.onmessage = (event) => { - const parsedMessage = JSON.parse(event.data) - if (parsedMessage.invalid) { +async function ping () { + try { + const url = `/on-demand-entries-ping?page=${Router.pathname}` + const res = await fetch(url) + const payload = await res.json() + if (payload.invalid) { location.reload() } + } catch (err) { + console.error(`Error with on-demand-entries-ping: ${err.message}`) } +} - conn.onclose = () => { - clearInterval(pingingHandler) - retry() - } - - conn.onerror = (error) => { - console.log(`Pinger error: ${error.message}`) - } - - // Ping on every page change - const originalOnRouteChangeComplete = Router.onRouteChangeComplete - Router.onRouteChangeComplete = function (...args) { - ping() - if (originalOnRouteChangeComplete) originalOnRouteChangeComplete(...args) - } - - function ping () { - const payload = { page: Router.pathname } - conn.send(JSON.stringify(payload)) +async function runPinger () { + while (true) { + await new Promise((resolve) => setTimeout(resolve, 5000)) + await ping() } } + +runPinger() + .catch((err) => { + console.error(err) + }) diff --git a/package.json b/package.json index e8e988b17b3d4..52bf2f65cb085 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ "babel-preset-latest": "6.22.0", "babel-preset-react": "6.23.0", "babel-runtime": "6.23.0", - "bufferutil": "2.0.1", "case-sensitive-paths-webpack-plugin": "1.1.4", "cross-spawn": "5.0.1", "del": "2.2.2", @@ -80,16 +79,14 @@ "source-map-support": "0.4.11", "strip-ansi": "3.0.1", "styled-jsx": "0.5.7", - "touch": "1.0.0", + "touch": "^1.0.0", "url": "0.11.0", - "utf-8-validate": "3.0.1", "uuid": "3.0.1", "webpack": "2.2.1", "webpack-dev-middleware": "1.10.1", "webpack-hot-middleware": "2.17.1", "whatwg-fetch": "2.0.2", - "write-file-webpack-plugin": "3.4.2", - "ws": "2.1.0" + "write-file-webpack-plugin": "3.4.2" }, "devDependencies": { "babel-eslint": "7.1.1", diff --git a/server/hot-reloader.js b/server/hot-reloader.js index 836a88b0e00e9..15998aab6ed43 100644 --- a/server/hot-reloader.js +++ b/server/hot-reloader.js @@ -49,13 +49,12 @@ export default class HotReloader { async stop () { if (this.webpackDevMiddleware) { - await new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { this.webpackDevMiddleware.close((err) => { if (err) return reject(err) resolve() }) }) - await this.onDemandEntries.close() } } @@ -150,7 +149,7 @@ export default class HotReloader { }) this.webpackHotMiddleware = webpackHotMiddleware(compiler, { log: false }) - this.onDemandEntries = await onDemandEntryHandler(this.webpackDevMiddleware, compiler, { + this.onDemandEntries = onDemandEntryHandler(this.webpackDevMiddleware, compiler, { dir: this.dir, dev: true, ...this.config.onDemandEntries diff --git a/server/on-demand-entry-handler.js b/server/on-demand-entry-handler.js index 0af1ccc9f850a..565bbbbba23d9 100644 --- a/server/on-demand-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -1,23 +1,19 @@ import DynamicEntryPlugin from 'webpack/lib/DynamicEntryPlugin' import { EventEmitter } from 'events' -import { createServer } from 'http' import { join } from 'path' +import { parse } from 'url' import resolvePath from './resolve' import touch from 'touch' -import WebSocket from 'ws' const ADDED = Symbol() const BUILDING = Symbol() const BUILT = Symbol() -export default async function onDemandEntryHandler (devMiddleware, compiler, { +export default function onDemandEntryHandler (devMiddleware, compiler, { dir, dev, maxInactiveAge = 1000 * 25 }) { - const server = await getServer() - const wss = new WebSocket.Server({ server }) - const entries = {} const lastAccessPages = [''] const doneCallbacks = new EventEmitter() @@ -62,31 +58,6 @@ export default async function onDemandEntryHandler (devMiddleware, compiler, { disposeInactiveEntries(devMiddleware, entries, lastAccessPages, maxInactiveAge) }, 5000) - wss.on('connection', (conn) => { - conn.on('message', (message) => { - const parsedMessage = JSON.parse(message) - const page = normalizePage(parsedMessage.page) - const entryInfo = entries[page] - - // If there's no entry. - // Then it seems like an weird issue. - if (!entryInfo) { - const message = `Client pings, but there's no entry for page: ${page}` - console.error(message) - conn.send(JSON.stringify({ invalid: true })) - return - } - - // We don't need to maintain active state of anything other than BUILT entries - if (entryInfo.status !== BUILT) return - - // If there's an entryInfo - lastAccessPages.pop() - lastAccessPages.unshift(page) - entryInfo.lastActiveTime = Date.now() - }) - }) - return { async ensurePage (page) { page = normalizePage(page) @@ -132,25 +103,31 @@ export default async function onDemandEntryHandler (devMiddleware, compiler, { middleware () { return function (req, res, next) { - if (!/^\/on-demand-entries-pinger-port/.test(req.url)) return next() + if (!/^\/on-demand-entries-ping/.test(req.url)) return next() - sendJson(res, { - port: server.address().port - }) - } - }, + const { query } = parse(req.url, true) + const page = normalizePage(query.page) + const entryInfo = entries[page] - close () { - return new Promise((resolve, reject) => { - server.close((err) => { - if (err) { - reject(err) - return - } + // If there's no entry. + // Then it seems like an weird issue. + if (!entryInfo) { + const message = `Client pings, but there's no entry for page: ${page}` + console.error(message) + sendJson(res, { invalid: true }) + return + } - resolve() - }) - }) + // We don't need to maintain active state of anything other than BUILT entries + if (entryInfo.status !== BUILT) return + + // If there's an entryInfo + lastAccessPages.pop() + lastAccessPages.unshift(page) + entryInfo.lastActiveTime = Date.now() + + sendJson(res, { success: true }) + } } } } @@ -205,17 +182,3 @@ function sendJson (res, payload) { res.status = 200 res.end(JSON.stringify(payload)) } - -function getServer () { - return new Promise((resolve, reject) => { - const server = createServer() - server.listen((err) => { - if (err) { - reject(err) - return - } - - resolve(server) - }) - }) -} diff --git a/yarn.lock b/yarn.lock index 789623a484ee0..917099225a831 100644 --- a/yarn.lock +++ b/yarn.lock @@ -973,10 +973,6 @@ binary-extensions@^1.0.0: version "1.8.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" -bindings@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" - bl@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.0.tgz#1397e7ec42c5f5dc387470c500e34a9f6be9ea98" @@ -1115,14 +1111,6 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -bufferutil@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-2.0.1.tgz#8de37f5a300730c305fc3edd9f93348ee8a46288" - dependencies: - bindings "~1.2.1" - nan "~2.5.0" - prebuild-install "~2.1.0" - builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -1222,10 +1210,6 @@ chokidar@^1.4.3, chokidar@^1.6.1: optionalDependencies: fsevents "^1.0.0" -chownr@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" - chromedriver@2.26.1: version "2.26.1" resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-2.26.1.tgz#60036732224580d2699afd2626f5f26ea1f6c9fe" @@ -1696,7 +1680,7 @@ encoding@^0.1.11: dependencies: iconv-lite "~0.4.13" -end-of-stream@^1.0.0, end-of-stream@^1.1.0: +end-of-stream@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.1.0.tgz#e9353258baa9108965efc41cb0ef8ade2f3cfb07" dependencies: @@ -1979,10 +1963,6 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -expand-template@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-1.0.3.tgz#6c303323177a62b1b22c070279f7861287b69b1a" - extend@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" @@ -2266,10 +2246,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -github-from-package@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" - glamor@2.20.23: version "2.20.23" resolved "https://registry.yarnpkg.com/glamor/-/glamor-2.20.23.tgz#aa597b9b0e8835258d0655b19687dc4e772e6c49" @@ -3414,7 +3390,7 @@ mz@2.6.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.3.0, nan@~2.5.0: +nan@^2.3.0: version "2.5.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2" @@ -3426,10 +3402,6 @@ ncp@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" -node-abi@^1.0.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-1.3.3.tgz#0f06f2815deba26107959d2213b36ce97437e6e2" - node-fetch@1.6.3, node-fetch@^1.0.1: version "1.6.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" @@ -3492,10 +3464,6 @@ node-pre-gyp@^0.6.29: tar "~2.2.1" tar-pack "~3.3.0" -noop-logger@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" - nopt@~1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" @@ -3628,7 +3596,7 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -once@^1.3.0, once@^1.3.1, once@^1.4.0: +once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -3825,24 +3793,6 @@ pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" -prebuild-install@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.1.0.tgz#526c7b3ed1e2707a247f7c040719173a321bc14f" - dependencies: - expand-template "^1.0.2" - github-from-package "0.0.0" - minimist "^1.2.0" - node-abi "^1.0.3" - noop-logger "^0.1.1" - npmlog "^4.0.1" - os-homedir "^1.0.1" - pump "^1.0.1" - rc "^1.1.6" - simple-get "^1.4.2" - tar-fs "^1.13.0" - tunnel-agent "^0.4.3" - xtend "4.0.1" - prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -3905,13 +3855,6 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" -pump@^1.0.0, pump@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.2.tgz#3b3ee6512f94f0e575538c17995f9f16990a5d51" - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - punycode@1.3.2, punycode@^1.2.4: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -3951,7 +3894,7 @@ range-parser@^1.0.3, range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" -rc@^1.1.6, rc@~1.1.6: +rc@~1.1.6: version "1.1.7" resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.7.tgz#c5ea564bb07aff9fd3a5b32e906c1d3a65940fea" dependencies: @@ -4326,14 +4269,6 @@ signal-exit@^3.0.0, signal-exit@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" -simple-get@^1.4.2: - version "1.4.3" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-1.4.3.tgz#e9755eda407e96da40c5e5158c9ea37b33becbeb" - dependencies: - once "^1.3.1" - unzip-response "^1.0.0" - xtend "^4.0.0" - slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -4573,15 +4508,6 @@ tapable@^0.2.5, tapable@~0.2.5: version "0.2.6" resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d" -tar-fs@^1.13.0: - version "1.15.1" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.15.1.tgz#f4622f5d5e250742b3679a9a8463acfc12cdefd1" - dependencies: - chownr "^1.0.1" - mkdirp "^0.5.0" - pump "^1.0.0" - tar-stream "^1.1.2" - tar-pack@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.3.0.tgz#30931816418f55afc4d21775afdd6720cee45dae" @@ -4595,7 +4521,7 @@ tar-pack@~3.3.0: tar "~2.2.1" uid-number "~0.0.6" -tar-stream@^1.1.2, tar-stream@^1.5.0: +tar-stream@^1.5.0: version "1.5.2" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.2.tgz#fbc6c6e83c1a19d4cb48c7d96171fc248effc7bf" dependencies: @@ -4674,7 +4600,7 @@ to-fast-properties@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" -touch@1.0.0: +touch@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/touch/-/touch-1.0.0.tgz#449cbe2dbae5a8c8038e30d71fa0ff464947c4de" dependencies: @@ -4702,7 +4628,7 @@ tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" -tunnel-agent@^0.4.3, tunnel-agent@~0.4.1: +tunnel-agent@~0.4.1: version "0.4.3" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" @@ -4745,10 +4671,6 @@ uid-number@~0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" -ultron@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.0.tgz#b07a2e6a541a815fc6a34ccd4533baec307ca864" - underscore.string@3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.4.tgz#2c2a3f9f83e64762fdc45e6ceac65142864213db" @@ -4760,10 +4682,6 @@ uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" -unzip-response@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe" - url@0.11.0, url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -4777,14 +4695,6 @@ user-home@^2.0.0: dependencies: os-homedir "^1.0.0" -utf-8-validate@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-3.0.1.tgz#5d2b8656b4ddcfded47217b647a98941b63cf213" - dependencies: - bindings "~1.2.1" - nan "~2.5.0" - prebuild-install "~2.1.0" - util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -5002,17 +4912,11 @@ write@^0.2.1: dependencies: mkdirp "^0.5.1" -ws@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-2.1.0.tgz#b24eaed9609f8632dd51e3f7698619a90fddcc92" - dependencies: - ultron "~1.1.0" - xml-name-validator@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" -xtend@4.0.1, "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1: +"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" From 07605a15ba5249853f064ad73c8f16288eac5062 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Sun, 26 Feb 2017 18:51:25 +0530 Subject: [PATCH 28/30] Do not send requests per every route change. --- client/on-demand-entries-client.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/client/on-demand-entries-client.js b/client/on-demand-entries-client.js index 582dd7528e026..ffa3805df7336 100644 --- a/client/on-demand-entries-client.js +++ b/client/on-demand-entries-client.js @@ -3,13 +3,6 @@ import Router from '../lib/router' import fetch from 'unfetch' -// Ping on every page change -const originalOnRouteChangeComplete = Router.onRouteChangeComplete -Router.onRouteChangeComplete = function (...args) { - ping() - if (originalOnRouteChangeComplete) originalOnRouteChangeComplete(...args) -} - async function ping () { try { const url = `/on-demand-entries-ping?page=${Router.pathname}` From 4278a18cedca5434a1e41b2331a26904d10792ef Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Sun, 26 Feb 2017 18:52:02 +0530 Subject: [PATCH 29/30] Make sure we are completing the middleware request always. --- server/on-demand-entry-handler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/on-demand-entry-handler.js b/server/on-demand-entry-handler.js index 565bbbbba23d9..f82f5ed31165a 100644 --- a/server/on-demand-entry-handler.js +++ b/server/on-demand-entry-handler.js @@ -118,6 +118,8 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { return } + sendJson(res, { success: true }) + // We don't need to maintain active state of anything other than BUILT entries if (entryInfo.status !== BUILT) return @@ -125,8 +127,6 @@ export default function onDemandEntryHandler (devMiddleware, compiler, { lastAccessPages.pop() lastAccessPages.unshift(page) entryInfo.lastActiveTime = Date.now() - - sendJson(res, { success: true }) } } } From f87f79638a403f663f8be326c7270ba2a94a0678 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Sun, 26 Feb 2017 20:09:12 +0530 Subject: [PATCH 30/30] Make sure test pages are prebuilt. --- .../basic/test/client-navigation.js | 12 +----------- test/integration/basic/test/index.test.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/test/integration/basic/test/client-navigation.js b/test/integration/basic/test/client-navigation.js index e2f095a2d5c29..eeea8078bd147 100644 --- a/test/integration/basic/test/client-navigation.js +++ b/test/integration/basic/test/client-navigation.js @@ -1,19 +1,9 @@ -/* global describe, it, expect, beforeAll */ +/* global describe, it, expect */ import webdriver from 'next-webdriver' export default (context, render) => { describe('Client Navigation', () => { - beforeAll(async function () { - // pre-build pages before doing tests - await Promise.all([ - render('/empty-get-initial-props'), - render('/nav'), - render('/nav/about'), - render('/nav/querystring') - ]) - }) - describe('with ', () => { it('should navigate the page', async () => { const browser = await webdriver(context.appPort, '/nav') diff --git a/test/integration/basic/test/index.test.js b/test/integration/basic/test/index.test.js index 7981e7ed60201..33de82ba1d4e6 100644 --- a/test/integration/basic/test/index.test.js +++ b/test/integration/basic/test/index.test.js @@ -28,6 +28,25 @@ describe('Basic Features', () => { beforeAll(async () => { context.server = await startApp(context.app) context.appPort = context.server.address().port + + // pre-build all pages at the start + await Promise.all([ + renderViaHTTP(context.appPort, '/async-props'), + renderViaHTTP(context.appPort, '/empty-get-initial-props'), + renderViaHTTP(context.appPort, '/error'), + renderViaHTTP(context.appPort, '/finish-response'), + renderViaHTTP(context.appPort, '/head'), + renderViaHTTP(context.appPort, '/json'), + renderViaHTTP(context.appPort, '/link'), + renderViaHTTP(context.appPort, '/stateful'), + renderViaHTTP(context.appPort, '/stateless'), + renderViaHTTP(context.appPort, '/styled-jsx'), + + renderViaHTTP(context.appPort, '/nav'), + renderViaHTTP(context.appPort, '/nav/about'), + renderViaHTTP(context.appPort, '/nav/querystring'), + renderViaHTTP(context.appPort, '/nav/self-reload') + ]) }) afterAll(() => stopApp(context.server))