Merge branch 'dynamic-import' into v3-beta

This commit is contained in:
Arunoda Susiripala 2017-05-15 10:15:13 +05:30
commit 737c283bb7
38 changed files with 888 additions and 55 deletions

View file

@ -24,6 +24,7 @@ const {
pathname, pathname,
query, query,
buildId, buildId,
chunks,
assetPrefix assetPrefix
}, },
location location
@ -37,7 +38,13 @@ window.__NEXT_LOADED_PAGES__.forEach(({ route, fn }) => {
}) })
delete window.__NEXT_LOADED_PAGES__ delete window.__NEXT_LOADED_PAGES__
window.__NEXT_LOADED_CHUNKS__.forEach(({ chunkName, fn }) => {
pageLoader.registerChunk(chunkName, fn)
})
delete window.__NEXT_LOADED_CHUNKS__
window.__NEXT_REGISTER_PAGE = pageLoader.registerPage.bind(pageLoader) window.__NEXT_REGISTER_PAGE = pageLoader.registerPage.bind(pageLoader)
window.__NEXT_REGISTER_CHUNK = pageLoader.registerChunk.bind(pageLoader)
const headManager = new HeadManager() const headManager = new HeadManager()
const appContainer = document.getElementById('__next') const appContainer = document.getElementById('__next')
@ -49,6 +56,11 @@ export let ErrorComponent
let Component let Component
export default async () => { export default async () => {
// Wait for all the dynamic chunks to get loaded
for (const chunkName of chunks) {
await pageLoader.waitForChunk(chunkName)
}
ErrorComponent = await pageLoader.loadPage('/_error') ErrorComponent = await pageLoader.loadPage('/_error')
try { try {

1
dynamic.js Normal file
View file

@ -0,0 +1 @@
module.exports = require('./dist/lib/dynamic')

View file

@ -0,0 +1,27 @@
# Example app with dynamic-imports
## How to use
Download the example [or clone the repo](https://github.com/zeit/next.js):
```bash
curl https://codeload.github.com/zeit/next.js/tar.gz/master | tar -xz --strip=2 next.js-master/examples/shared-modules
cd shared-modules
```
Install it and run:
```bash
npm install
npm run dev
```
Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download))
```bash
now
```
## The idea behind the example
This examples shows how to dynamically import modules via [`import()`](https://github.com/tc39/proposal-dynamic-import) API

View file

@ -0,0 +1,19 @@
import React from 'react'
let count = 0
export default class Counter extends React.Component {
add () {
count += 1
this.forceUpdate()
}
render () {
return (
<div>
<p>Count is: {count}</p>
<button onClick={() => this.add()}>Add</button>
</div>
)
}
}

View file

@ -0,0 +1,19 @@
import Link from 'next/link'
export default () => (
<div>
<Link href='/'>
<a style={styles.a} >Home</a>
</Link>
<Link href='/about'>
<a style={styles.a} >About</a>
</Link>
</div>
)
const styles = {
a: {
marginRight: 10
}
}

View file

@ -0,0 +1,3 @@
export default () => (
<p>Hello World 1 (imported dynamiclly) </p>
)

View file

@ -0,0 +1,3 @@
export default () => (
<p>Hello World 2 (imported dynamiclly) </p>
)

View file

@ -0,0 +1,3 @@
export default () => (
<p>Hello World 3 (imported dynamiclly) </p>
)

View file

@ -0,0 +1,3 @@
export default () => (
<p>Hello World 4 (imported dynamiclly) </p>
)

View file

@ -0,0 +1,3 @@
export default () => (
<p>Hello World 5 (imported dynamiclly) </p>
)

View file

@ -0,0 +1,19 @@
{
"name": "with-dynamic-import",
"version": "1.0.0",
"description": "This example features:",
"main": "index.js",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"async-reactor": "^1.1.1",
"next": "*",
"react": "^15.4.2",
"react-dom": "^15.4.2"
},
"author": "",
"license": "ISC"
}

View file

@ -0,0 +1,12 @@
import Header from '../components/Header'
import Counter from '../components/Counter'
const About = () => (
<div>
<Header />
<p>This is the about page.</p>
<Counter />
</div>
)
export default About

View file

@ -0,0 +1,43 @@
import React from 'react'
import Header from '../components/Header'
import Counter from '../components/Counter'
import dynamic from 'next/dynamic'
import { asyncReactor } from 'async-reactor'
const DynamicComponent = dynamic(import('../components/hello1'))
const DynamicComponentWithCustomLoading = dynamic(
import('../components/hello2'),
{
loading: () => (<p>...</p>)
}
)
const DynamicComponentWithNoSSR = dynamic(
import('../components/hello3'),
{ ssr: false }
)
const DynamicComponentWithAsyncReactor = asyncReactor(async () => {
const Hello4 = await import('../components/hello4')
return (<Hello4 />)
})
const DynamicComponent5 = dynamic(import('../components/hello5'))
export default () => (
<div>
<Header />
<DynamicComponent />
<DynamicComponentWithCustomLoading />
<DynamicComponentWithNoSSR />
<DynamicComponentWithAsyncReactor />
{
/*
Since DynamicComponent5 does not render in the client,
it won't get downloaded.
*/
}
{ React.noSuchField === true ? <DynamicComponent5 /> : null }
<p>HOME PAGE is here!</p>
<Counter />
</div>
)

154
lib/dynamic.js Normal file
View file

@ -0,0 +1,154 @@
import React from 'react'
let currentChunks = []
export default function dynamicComponent (promise, options = {}) {
return class DynamicComponent extends React.Component {
constructor (...args) {
super(...args)
this.LoadingComponent = options.loading ? options.loading : () => (<p>loading...</p>)
this.ssr = options.ssr === false ? options.ssr : true
this.state = { AsyncComponent: null }
this.isServer = typeof window === 'undefined'
if (this.ssr) {
this.loadComponent()
}
}
loadComponent () {
promise.then((AsyncComponent) => {
// Set a readable displayName for the wrapper component
const asyncCompName = AsyncComponent.displayName || AsyncComponent.name
if (asyncCompName) {
DynamicComponent.displayName = `DynamicComponent for ${asyncCompName}`
}
if (this.mounted) {
this.setState({ AsyncComponent })
} else {
if (this.isServer) {
currentChunks.push(AsyncComponent.__webpackChunkName)
}
this.state.AsyncComponent = AsyncComponent
}
})
}
componentDidMount () {
this.mounted = true
if (!this.ssr) {
this.loadComponent()
}
}
render () {
const { AsyncComponent } = this.state
const { LoadingComponent } = this
if (!AsyncComponent) return (<LoadingComponent {...this.props} />)
return <AsyncComponent {...this.props} />
}
}
}
export function flushChunks () {
const chunks = currentChunks
currentChunks = []
return chunks
}
export class SameLoopPromise {
constructor (cb) {
this.onResultCallbacks = []
this.onErrorCallbacks = []
this.cb = cb
}
setResult (result) {
this.gotResult = true
this.result = result
this.onResultCallbacks.forEach((cb) => cb(result))
this.onResultCallbacks = []
}
setError (error) {
this.gotError = true
this.error = error
this.onErrorCallbacks.forEach((cb) => cb(error))
this.onErrorCallbacks = []
}
then (onResult, onError) {
this.runIfNeeded()
const promise = new SameLoopPromise()
const handleError = () => {
if (onError) {
promise.setResult(onError(this.error))
} else {
promise.setError(this.error)
}
}
const handleResult = () => {
promise.setResult(onResult(this.result))
}
if (this.gotResult) {
handleResult()
return promise
}
if (this.gotError) {
handleError()
return promise
}
this.onResultCallbacks.push(handleResult)
this.onErrorCallbacks.push(handleError)
return promise
}
catch (onError) {
this.runIfNeeded()
const promise = new SameLoopPromise()
const handleError = () => {
promise.setResult(onError(this.error))
}
const handleResult = () => {
promise.setResult(this.result)
}
if (this.gotResult) {
handleResult()
return promise
}
if (this.gotError) {
handleError()
return promise
}
this.onErrorCallbacks.push(handleError)
this.onResultCallbacks.push(handleResult)
return promise
}
runIfNeeded () {
if (!this.cb) return
if (this.ran) return
this.ran = true
this.cb(
(result) => this.setResult(result),
(error) => this.setError(error)
)
}
}

View file

@ -10,8 +10,11 @@ export default class PageLoader {
this.pageCache = {} this.pageCache = {}
this.pageLoadedHandlers = {} this.pageLoadedHandlers = {}
this.registerEvents = mitt() this.pageRegisterEvents = mitt()
this.loadingRoutes = {} this.loadingRoutes = {}
this.chunkRegisterEvents = mitt()
this.loadedChunks = {}
} }
normalizeRoute (route) { normalizeRoute (route) {
@ -37,7 +40,7 @@ export default class PageLoader {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const fire = ({ error, page }) => { const fire = ({ error, page }) => {
this.registerEvents.off(route, fire) this.pageRegisterEvents.off(route, fire)
if (error) { if (error) {
reject(error) reject(error)
@ -46,7 +49,7 @@ export default class PageLoader {
} }
} }
this.registerEvents.on(route, fire) this.pageRegisterEvents.on(route, fire)
// If the page is loading via SSR, we need to wait for it // If the page is loading via SSR, we need to wait for it
// rather downloading it again. // rather downloading it again.
@ -75,7 +78,7 @@ export default class PageLoader {
script.type = 'text/javascript' script.type = 'text/javascript'
script.onerror = () => { script.onerror = () => {
const error = new Error(`Error when loading route: ${route}`) const error = new Error(`Error when loading route: ${route}`)
this.registerEvents.emit(route, { error }) this.pageRegisterEvents.emit(route, { error })
} }
document.body.appendChild(script) document.body.appendChild(script)
@ -86,7 +89,7 @@ export default class PageLoader {
const register = () => { const register = () => {
const { error, page } = regFn() const { error, page } = regFn()
this.pageCache[route] = { error, page } this.pageCache[route] = { error, page }
this.registerEvents.emit(route, { error, page }) this.pageRegisterEvents.emit(route, { error, page })
} }
// Wait for webpack to became idle if it's not. // Wait for webpack to became idle if it's not.
@ -106,6 +109,28 @@ export default class PageLoader {
} }
} }
registerChunk (chunkName, regFn) {
const chunk = regFn()
this.loadedChunks[chunkName] = true
this.chunkRegisterEvents.emit(chunkName, chunk)
}
waitForChunk (chunkName, regFn) {
const loadedChunk = this.loadedChunks[chunkName]
if (loadedChunk) {
return Promise.resolve(true)
}
return new Promise((resolve) => {
const register = (chunk) => {
this.chunkRegisterEvents.off(chunkName, register)
resolve(chunk)
}
this.chunkRegisterEvents.on(chunkName, register)
})
}
clearCache (route) { clearCache (route) {
route = this.normalizeRoute(route) route = this.normalizeRoute(route)
delete this.pageCache[route] delete this.pageCache[route]

View file

@ -49,6 +49,7 @@
"babel-loader": "7.0.0", "babel-loader": "7.0.0",
"babel-plugin-module-resolver": "2.6.2", "babel-plugin-module-resolver": "2.6.2",
"babel-plugin-react-require": "3.0.0", "babel-plugin-react-require": "3.0.0",
"babel-plugin-syntax-dynamic-import": "6.18.0",
"babel-plugin-transform-class-properties": "6.24.1", "babel-plugin-transform-class-properties": "6.24.1",
"babel-plugin-transform-es2015-modules-commonjs": "6.24.1", "babel-plugin-transform-es2015-modules-commonjs": "6.24.1",
"babel-plugin-transform-object-rest-spread": "6.22.0", "babel-plugin-transform-object-rest-spread": "6.22.0",
@ -58,6 +59,7 @@
"babel-preset-env": "1.3.3", "babel-preset-env": "1.3.3",
"babel-preset-react": "6.24.1", "babel-preset-react": "6.24.1",
"babel-runtime": "6.23.0", "babel-runtime": "6.23.0",
"babel-template": "6.24.1",
"case-sensitive-paths-webpack-plugin": "2.0.0", "case-sensitive-paths-webpack-plugin": "2.0.0",
"cross-spawn": "5.1.0", "cross-spawn": "5.1.0",
"del": "2.2.2", "del": "2.2.2",

View file

@ -32,6 +32,7 @@ Next.js is a minimalistic framework for server-rendered React applications.
- [With `<Link>`](#with-link-1) - [With `<Link>`](#with-link-1)
- [Imperatively](#imperatively-1) - [Imperatively](#imperatively-1)
- [Custom server and routing](#custom-server-and-routing) - [Custom server and routing](#custom-server-and-routing)
- [Dynamic Imports](#dynamic-imports)
- [Custom `<Document>`](#custom-document) - [Custom `<Document>`](#custom-document)
- [Custom error handling](#custom-error-handling) - [Custom error handling](#custom-error-handling)
- [Custom configuration](#custom-configuration) - [Custom configuration](#custom-configuration)
@ -579,6 +580,96 @@ Supported options:
Then, change your `start` script to `NODE_ENV=production node server.js`. Then, change your `start` script to `NODE_ENV=production node server.js`.
### Dynamic Import
<p><details>
<summary><b>Examples</b></summary>
<ul>
<li><a href="./examples/with-dynamic-import">With Dynamic Import</a></li>
</ul>
</details></p>
Next.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.
With that, you could import JavaScript modules (inc. React Components) dynamically and work with them.
You can think dynamic imports as another way to split your code into manageable chunks.
Since Next.js supports dynamic imports with SSR, you could do amazing things with it.
Here are a few ways to use dynamic imports.
#### 1. Basic Usage (Also does SSR)
```js
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(import('../components/hello'))
export default () => (
<div>
<Header />
<DynamicComponent />
<p>HOME PAGE is here!</p>
</div>
)
```
#### 2. With Custom Loading Component
```js
import dynamic from 'next/dynamic'
const DynamicComponentWithCustomLoading = dynamic(
import('../components/hello2'),
{
loading: () => (<p>...</p>)
}
)
export default () => (
<div>
<Header />
<DynamicComponentWithCustomLoading />
<p>HOME PAGE is here!</p>
</div>
)
```
#### 3. With No SSR
```js
import dynamic from 'next/dynamic'
const DynamicComponentWithNoSSR = dynamic(
import('../components/hello3'),
{ ssr: false }
)
export default () => (
<div>
<Header />
<DynamicComponentWithNoSSR />
<p>HOME PAGE is here!</p>
</div>
)
```
#### 4. With [async-reactor](https://github.com/xtuc/async-reactor)
> SSR support is not available here
```js
import { asyncReactor } from 'async-reactor'
const DynamicComponentWithAsyncReactor = asyncReactor(async () => {
const Hello4 = await import('../components/hello4')
return (<Hello4 />)
})
export default () => (
<div>
<Header />
<DynamicComponentWithAsyncReactor />
<p>HOME PAGE is here!</p>
</div>
)
```
### Custom `<Document>` ### Custom `<Document>`
<p><details> <p><details>

View file

@ -0,0 +1,57 @@
// Based on https://github.com/airbnb/babel-plugin-dynamic-import-webpack
// We've added support for SSR with this version
import template from 'babel-template'
import syntax from 'babel-plugin-syntax-dynamic-import'
import UUID from 'uuid'
const TYPE_IMPORT = 'Import'
const buildImport = (args) => (template(`
(
typeof window === 'undefined' ?
new (require('next/dynamic').SameLoopPromise)((resolve, reject) => {
eval('require.ensure = function (deps, callback) { callback(require) }')
require.ensure([], (require) => {
let m = require(SOURCE)
m = m.default || m
m.__webpackChunkName = '${args.name}.js'
resolve(m);
}, 'chunks/${args.name}.js');
})
:
new (require('next/dynamic').SameLoopPromise)((resolve, reject) => {
const weakId = require.resolveWeak(SOURCE)
try {
const weakModule = __webpack_require__(weakId)
return resolve(weakModule.default || weakModule)
} catch (err) {}
require.ensure([], (require) => {
try {
let m = require(SOURCE)
m = m.default || m
resolve(m)
} catch(error) {
reject(error)
}
}, 'chunks/${args.name}.js');
})
)
`))
export default () => ({
inherits: syntax,
visitor: {
CallExpression (path) {
if (path.node.callee.type === TYPE_IMPORT) {
const newImport = buildImport({
name: UUID.v4()
})({
SOURCE: path.node.arguments
})
path.replaceWith(newImport)
}
}
}
})

View file

@ -20,6 +20,7 @@ module.exports = {
], ],
plugins: [ plugins: [
require.resolve('babel-plugin-react-require'), require.resolve('babel-plugin-react-require'),
require.resolve('./plugins/handle-import'),
require.resolve('babel-plugin-transform-object-rest-spread'), require.resolve('babel-plugin-transform-object-rest-spread'),
require.resolve('babel-plugin-transform-class-properties'), require.resolve('babel-plugin-transform-class-properties'),
require.resolve('babel-plugin-transform-runtime'), require.resolve('babel-plugin-transform-runtime'),
@ -33,6 +34,7 @@ module.exports = {
'next/link': relativeResolve('../../../lib/link'), 'next/link': relativeResolve('../../../lib/link'),
'next/prefetch': relativeResolve('../../../lib/prefetch'), 'next/prefetch': relativeResolve('../../../lib/prefetch'),
'next/css': relativeResolve('../../../lib/css'), 'next/css': relativeResolve('../../../lib/css'),
'next/dynamic': relativeResolve('../../../lib/dynamic'),
'next/head': relativeResolve('../../../lib/head'), 'next/head': relativeResolve('../../../lib/head'),
'next/document': relativeResolve('../../../server/document'), 'next/document': relativeResolve('../../../server/document'),
'next/router': relativeResolve('../../../lib/router'), 'next/router': relativeResolve('../../../lib/router'),

View file

@ -0,0 +1,39 @@
export default class PagesPlugin {
apply (compiler) {
const isImportChunk = /^chunks[/\\].*\.js$/
const matchChunkName = /^chunks[/\\](.*)$/
compiler.plugin('after-compile', (compilation, callback) => {
const chunks = Object
.keys(compilation.namedChunks)
.map(key => compilation.namedChunks[key])
.filter(chunk => isImportChunk.test(chunk.name))
chunks.forEach((chunk) => {
const asset = compilation.assets[chunk.name]
if (!asset) return
const chunkName = matchChunkName.exec(chunk.name)[1]
const content = asset.source()
const newContent = `
window.__NEXT_REGISTER_CHUNK('${chunkName}', function() {
${content}
})
`
// Replace the exisiting chunk with the new content
compilation.assets[chunk.name] = {
source: () => newContent,
size: () => newContent.length
}
// This is to support, webpack dynamic import support with HMR
compilation.assets[`chunks/${chunk.id}`] = {
source: () => newContent,
size: () => newContent.length
}
})
callback()
})
}
}

View file

@ -7,6 +7,7 @@ import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin'
import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin' import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin'
import UnlinkFilePlugin from './plugins/unlink-file-plugin' import UnlinkFilePlugin from './plugins/unlink-file-plugin'
import PagesPlugin from './plugins/pages-plugin' import PagesPlugin from './plugins/pages-plugin'
import DynamicChunksPlugin from './plugins/dynamic-chunks-plugin'
import CombineAssetsPlugin from './plugins/combine-assets-plugin' import CombineAssetsPlugin from './plugins/combine-assets-plugin'
import getConfig from '../config' import getConfig from '../config'
import * as babelCore from 'babel-core' import * as babelCore from 'babel-core'
@ -116,6 +117,7 @@ export default async function createCompiler (dir, { dev = false, quiet = false,
'process.env.NODE_ENV': JSON.stringify(dev ? 'development' : 'production') 'process.env.NODE_ENV': JSON.stringify(dev ? 'development' : 'production')
}), }),
new PagesPlugin(), new PagesPlugin(),
new DynamicChunksPlugin(),
new CaseSensitivePathPlugin() new CaseSensitivePathPlugin()
] ]
@ -219,6 +221,7 @@ export default async function createCompiler (dir, { dev = false, quiet = false,
'next/link': relativeResolve('../../lib/link'), 'next/link': relativeResolve('../../lib/link'),
'next/prefetch': relativeResolve('../../lib/prefetch'), 'next/prefetch': relativeResolve('../../lib/prefetch'),
'next/css': relativeResolve('../../lib/css'), 'next/css': relativeResolve('../../lib/css'),
'next/dynamic': relativeResolve('../../lib/dynamic'),
'next/head': relativeResolve('../../lib/head'), 'next/head': relativeResolve('../../lib/head'),
'next/document': relativeResolve('../../server/document'), 'next/document': relativeResolve('../../server/document'),
'next/router': relativeResolve('../../lib/router'), 'next/router': relativeResolve('../../lib/router'),
@ -274,7 +277,9 @@ export default async function createCompiler (dir, { dev = false, quiet = false,
// append hash id for cache busting // append hash id for cache busting
return `webpack:///${resourcePath}?${id}` return `webpack:///${resourcePath}?${id}`
} },
// This saves chunks with the name given via require.ensure()
chunkFilename: '[name]'
}, },
resolve: { resolve: {
modules: [ modules: [

View file

@ -5,9 +5,9 @@ import flush from 'styled-jsx/server'
export default class Document extends Component { export default class Document extends Component {
static getInitialProps ({ renderPage }) { static getInitialProps ({ renderPage }) {
const { html, head, errorHtml } = renderPage() const { html, head, errorHtml, chunks } = renderPage()
const styles = flush() const styles = flush()
return { html, head, errorHtml, styles } return { html, head, errorHtml, chunks, styles }
} }
static childContextTypes = { static childContextTypes = {
@ -65,6 +65,19 @@ export class Head extends Component {
] ]
} }
getPreloadDynamicChunks () {
const { chunks, __NEXT_DATA__ } = this.context._documentProps
let { assetPrefix } = __NEXT_DATA__
return chunks.map((chunk) => (
<link
key={chunk}
rel='preload'
href={`${assetPrefix}/_next/webpack/chunks/${chunk}`}
as='script'
/>
))
}
render () { render () {
const { head, styles, __NEXT_DATA__ } = this.context._documentProps const { head, styles, __NEXT_DATA__ } = this.context._documentProps
const { pathname, buildId, assetPrefix, nextExport } = __NEXT_DATA__ const { pathname, buildId, assetPrefix, nextExport } = __NEXT_DATA__
@ -73,6 +86,7 @@ export class Head extends Component {
return <head> return <head>
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page${pagePathname}`} as='script' /> <link rel='preload' href={`${assetPrefix}/_next/${buildId}/page${pagePathname}`} as='script' />
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_error/index.js`} as='script' /> <link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_error/index.js`} as='script' />
{this.getPreloadDynamicChunks()}
{this.getPreloadMainLinks()} {this.getPreloadMainLinks()}
{(head || []).map((h, i) => React.cloneElement(h, { key: i }))} {(head || []).map((h, i) => React.cloneElement(h, { key: i }))}
{styles || null} {styles || null}
@ -132,26 +146,50 @@ export class NextScript extends Component {
return [this.getChunkScript('app.js', { async: true })] return [this.getChunkScript('app.js', { async: true })]
} }
getDynamicChunks () {
const { chunks, __NEXT_DATA__ } = this.context._documentProps
let { assetPrefix } = __NEXT_DATA__
return (
<div>
{chunks.map((chunk) => (
<script
async
key={chunk}
type='text/javascript'
src={`${assetPrefix}/_next/webpack/chunks/${chunk}`}
/>
))}
</div>
)
}
render () { render () {
const { staticMarkup, __NEXT_DATA__ } = this.context._documentProps const { staticMarkup, __NEXT_DATA__, chunks } = this.context._documentProps
const { pathname, nextExport, buildId, assetPrefix } = __NEXT_DATA__ const { pathname, nextExport, buildId, assetPrefix } = __NEXT_DATA__
const pagePathname = getPagePathname(pathname, nextExport) const pagePathname = getPagePathname(pathname, nextExport)
__NEXT_DATA__.chunks = chunks
return <div> return <div>
{staticMarkup ? null : <script dangerouslySetInnerHTML={{ {staticMarkup ? null : <script dangerouslySetInnerHTML={{
__html: ` __html: `
__NEXT_DATA__ = ${htmlescape(__NEXT_DATA__)} __NEXT_DATA__ = ${htmlescape(__NEXT_DATA__)}
module={} module={}
__NEXT_LOADED_PAGES__ = [] __NEXT_LOADED_PAGES__ = []
__NEXT_LOADED_CHUNKS__ = []
__NEXT_REGISTER_PAGE = function (route, fn) { __NEXT_REGISTER_PAGE = function (route, fn) {
__NEXT_LOADED_PAGES__.push({ route: route, fn: fn }) __NEXT_LOADED_PAGES__.push({ route: route, fn: fn })
} }
__NEXT_REGISTER_CHUNK = function (chunkName, fn) {
__NEXT_LOADED_CHUNKS__.push({ chunkName: chunkName, fn: fn })
}
` `
}} />} }} />}
<script async id={`__NEXT_PAGE__${pathname}`} type='text/javascript' src={`${assetPrefix}/_next/${buildId}/page${pagePathname}`} /> <script async id={`__NEXT_PAGE__${pathname}`} type='text/javascript' src={`${assetPrefix}/_next/${buildId}/page${pagePathname}`} />
<script async id={`__NEXT_PAGE__/_error`} type='text/javascript' src={`${assetPrefix}/_next/${buildId}/page/_error/index.js`} /> <script async id={`__NEXT_PAGE__/_error`} type='text/javascript' src={`${assetPrefix}/_next/${buildId}/page/_error/index.js`} />
{staticMarkup ? null : this.getDynamicChunks()}
{staticMarkup ? null : this.getScripts()} {staticMarkup ? null : this.getScripts()}
</div> </div>
} }

View file

@ -44,6 +44,17 @@ export default async function (dir, options) {
) )
} }
// Copy dynamic import chunks
if (existsSync(join(nextDir, 'chunks'))) {
log(' copying dynamic import chunks')
await mkdirp(join(outDir, '_next', 'webpack'))
await cp(
join(nextDir, 'chunks'),
join(outDir, '_next', 'webpack', 'chunks')
)
}
await copyPages(nextDir, outDir, buildId) await copyPages(nextDir, outDir, buildId)
// Get the exportPathMap from the `next.config.js` // Get the exportPathMap from the `next.config.js`

View file

@ -99,6 +99,19 @@ export default class Server {
await this.serveStatic(req, res, p) await this.serveStatic(req, res, p)
}, },
// This is to support, webpack dynamic imports in production.
'/_next/webpack/chunks/:name': async (req, res, params) => {
res.setHeader('Cache-Control', 'max-age=365000000, immutable')
const p = join(this.dir, '.next', 'chunks', params.name)
await this.serveStatic(req, res, p)
},
// This is to support, webpack dynamic import support with HMR
'/_next/webpack/:id': async (req, res, params) => {
const p = join(this.dir, '.next', 'chunks', params.id)
await this.serveStatic(req, res, p)
},
'/_next/:hash/manifest.js': async (req, res, params) => { '/_next/:hash/manifest.js': async (req, res, params) => {
this.handleBuildHash('manifest.js', params.hash, res) this.handleBuildHash('manifest.js', params.hash, res)
const p = join(this.dir, `${this.dist}/manifest.js`) const p = join(this.dir, `${this.dist}/manifest.js`)

View file

@ -12,6 +12,7 @@ import { loadGetInitialProps } from '../lib/utils'
import Head, { defaultHead } from '../lib/head' import Head, { defaultHead } from '../lib/head'
import App from '../lib/app' import App from '../lib/app'
import ErrorDebug from '../lib/error-debug' import ErrorDebug from '../lib/error-debug'
import { flushChunks } from '../lib/dynamic'
export async function render (req, res, pathname, query, opts) { export async function render (req, res, pathname, query, opts) {
const html = await renderToHTML(req, res, pathname, opts) const html = await renderToHTML(req, res, pathname, opts)
@ -79,12 +80,13 @@ async function doRender (req, res, pathname, query, {
} finally { } finally {
head = Head.rewind() || defaultHead() head = Head.rewind() || defaultHead()
} }
const chunks = flushChunks()
if (err && dev) { if (err && dev) {
errorHtml = render(createElement(ErrorDebug, { error: err })) errorHtml = render(createElement(ErrorDebug, { error: err }))
} }
return { html, head, errorHtml } return { html, head, errorHtml, chunks }
} }
const docProps = await loadGetInitialProps(Document, { ...ctx, renderPage }) const docProps = await loadGetInitialProps(Document, { ...ctx, renderPage })

View file

@ -0,0 +1,3 @@
export default () => (
<p>Hello World 1</p>
)

View file

@ -0,0 +1,8 @@
import dynamic from 'next/dynamic'
const Hello = dynamic(import('../../components/hello1'), {
ssr: false,
loading: () => (<p>LOADING</p>)
})
export default Hello

View file

@ -0,0 +1,5 @@
import dynamic from 'next/dynamic'
const Hello = dynamic(import('../../components/hello1'), { ssr: false })
export default Hello

View file

@ -0,0 +1,5 @@
import dynamic from 'next/dynamic'
const Hello = dynamic(import('../../components/hello1'))
export default Hello

View file

@ -79,5 +79,20 @@ export default function ({ app }, suiteName, render) {
expect($('h1').text()).toBe('404') expect($('h1').text()).toBe('404')
expect($('h2').text()).toBe('This page could not be found.') expect($('h2').text()).toBe('This page could not be found.')
}) })
test('render dynmaic import components via SSR', async () => {
const $ = await get$('/dynamic/ssr')
expect($('p').text()).toBe('Hello World 1')
})
test('stop render dynmaic import components in SSR', async () => {
const $ = await get$('/dynamic/no-ssr')
expect($('p').text()).toBe('loading...')
})
test('stop render dynmaic import components in SSR with custom loading', async () => {
const $ = await get$('/dynamic/no-ssr-custom-loading')
expect($('p').text()).toBe('LOADING')
})
}) })
} }

View file

@ -0,0 +1,5 @@
export default () => (
<p>
Welcome to dynamic imports.
</p>
)

View file

@ -4,6 +4,7 @@ module.exports = {
'/': { page: '/' }, '/': { page: '/' },
'/about': { page: '/about' }, '/about': { page: '/about' },
'/counter': { page: '/counter' }, '/counter': { page: '/counter' },
'/dynamic-imports': { page: '/dynamic-imports' },
'/dynamic': { page: '/dynamic', query: { text: 'cool dynamic text' } }, '/dynamic': { page: '/dynamic', query: { text: 'cool dynamic text' } },
'/dynamic/one': { page: '/dynamic', query: { text: 'next export is nice' } }, '/dynamic/one': { page: '/dynamic', query: { text: 'next export is nice' } },
'/dynamic/two': { page: '/dynamic', query: { text: 'zeit is awesome' } } '/dynamic/two': { page: '/dynamic', query: { text: 'zeit is awesome' } }

View file

@ -0,0 +1,15 @@
import Link from 'next/link'
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(import('../components/hello'))
export default () => (
<div id='dynamic-imports-page'>
<div>
<Link href='/'>
<a>Go Back</a>
</Link>
</div>
<DynamicComponent />
</div>
)

View file

@ -45,6 +45,9 @@ export default () => (
<Link href='/level1/about'> <Link href='/level1/about'>
<a id='level1-about-page'>Level1 about page</a> <a id='level1-about-page'>Level1 about page</a>
</Link> </Link>
<Link href='/dynamic-imports'>
<a id='dynamic-imports-page'>Dynamic imports page</a>
</Link>
</div> </div>
<p>This is the home page</p> <p>This is the home page</p>
<style jsx>{` <style jsx>{`

View file

@ -93,6 +93,17 @@ export default function (context) {
browser.close() browser.close()
}) })
it('should render dynamic import components in the client', async () => {
const browser = await webdriver(context.port, '/')
const text = await browser
.elementByCss('#dynamic-imports-page').click()
.waitForElementByCss('#dynamic-imports-page')
.elementByCss('#dynamic-imports-page p').text()
expect(text).toBe('Welcome to dynamic imports.')
browser.close()
})
describe('pages in the nested level: level1', () => { describe('pages in the nested level: level1', () => {
it('should render the home page', async () => { it('should render the home page', async () => {
const browser = await webdriver(context.port, '/') const browser = await webdriver(context.port, '/')

View file

@ -17,5 +17,10 @@ export default function (context) {
const html = await renderViaHTTP(context.port, '/dynamic/one') const html = await renderViaHTTP(context.port, '/dynamic/one')
expect(html).toMatch(/next export is nice/) expect(html).toMatch(/next export is nice/)
}) })
it('should render pages with dynamic imports', async() => {
const html = await renderViaHTTP(context.port, '/dynamic-imports')
expect(html).toMatch(/Welcome to dynamic imports./)
})
}) })
} }

View file

@ -0,0 +1,137 @@
/* global describe, it, expect */
import { SameLoopPromise } from '../../dist/lib/dynamic'
describe('SameLoopPromise', () => {
describe('basic api', () => {
it('should support basic promise resolving', (done) => {
const promise = new SameLoopPromise((resolve) => {
setTimeout(() => {
resolve(1000)
}, 100)
})
promise.then((value) => {
expect(value).toBe(1000)
done()
})
})
it('should support resolving in the same loop', () => {
let gotValue = null
const promise = new SameLoopPromise((resolve) => {
resolve(1000)
})
promise.then((value) => {
gotValue = value
})
expect(gotValue).toBe(1000)
})
it('should support basic promise rejecting', (done) => {
const error = new Error('Hello Error')
const promise = new SameLoopPromise((resolve, reject) => {
setTimeout(() => {
reject(error)
}, 100)
})
promise.catch((err) => {
expect(err).toBe(error)
done()
})
})
it('should support rejecting in the same loop', () => {
const error = new Error('Hello Error')
let gotError = null
const promise = new SameLoopPromise((resolve, reject) => {
reject(error)
})
promise.catch((err) => {
gotError = err
})
expect(gotError).toBe(error)
})
})
describe('complex usage', () => {
it('should support a chain of promises', (done) => {
const promise = new SameLoopPromise((resolve) => {
setTimeout(() => {
resolve(1000)
}, 100)
})
promise
.then((value) => value * 2)
.then((value) => value + 10)
.then((value) => {
expect(value).toBe(2010)
done()
})
})
it('should handle the error inside the then', (done) => {
const error = new Error('1000')
const promise = new SameLoopPromise((resolve, reject) => {
setTimeout(() => {
reject(error)
}, 100)
})
promise
.then(
() => 4000,
(err) => parseInt(err.message)
)
.then((value) => value + 10)
.then((value) => {
expect(value).toBe(1010)
done()
})
})
it('should catch the error at the end', (done) => {
const error = new Error('1000')
const promise = new SameLoopPromise((resolve, reject) => {
setTimeout(() => {
reject(error)
}, 100)
})
promise
.then((value) => value * 2)
.then((value) => value + 10)
.catch((err) => {
expect(err).toBe(error)
done()
})
})
it('should catch and proceed', (done) => {
const error = new Error('1000')
const promise = new SameLoopPromise((resolve, reject) => {
setTimeout(() => {
reject(error)
}, 100)
})
promise
.then((value) => value * 2)
.then((value) => value + 10)
.catch((err) => {
expect(err).toBe(error)
return 5000
})
.then((value) => {
expect(value).toBe(5000)
done()
})
})
})
})

102
yarn.lock
View file

@ -280,7 +280,7 @@ babel-code-frame@^6.16.0, babel-code-frame@^6.20.0, babel-code-frame@^6.22.0:
esutils "^2.0.2" esutils "^2.0.2"
js-tokens "^3.0.0" js-tokens "^3.0.0"
babel-core@6.24.0, babel-core@^6.3.0: babel-core@6.24.0, babel-core@^6.0.0, babel-core@^6.3.0:
version "6.24.0" version "6.24.0"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.0.tgz#8f36a0a77f5c155aed6f920b844d23ba56742a02" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.0.tgz#8f36a0a77f5c155aed6f920b844d23ba56742a02"
dependencies: dependencies:
@ -304,7 +304,7 @@ babel-core@6.24.0, babel-core@^6.3.0:
slash "^1.0.0" slash "^1.0.0"
source-map "^0.5.0" source-map "^0.5.0"
babel-core@^6.0.0, babel-core@^6.24.1: babel-core@^6.24.1:
version "6.24.1" version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83"
dependencies: dependencies:
@ -519,6 +519,10 @@ babel-plugin-syntax-class-properties@^6.8.0:
version "6.13.0" version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de"
babel-plugin-syntax-dynamic-import@6.18.0:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da"
babel-plugin-syntax-exponentiation-operator@^6.8.0: babel-plugin-syntax-exponentiation-operator@^6.8.0:
version "6.13.0" version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de"
@ -908,7 +912,7 @@ babel-runtime@6.23.0, babel-runtime@^6.18.0, babel-runtime@^6.20.0, babel-runtim
core-js "^2.4.0" core-js "^2.4.0"
regenerator-runtime "^0.10.0" regenerator-runtime "^0.10.0"
babel-template@^6.16.0, babel-template@^6.23.0, babel-template@^6.24.1, babel-template@^6.7.0: babel-template@6.24.1, babel-template@^6.16.0, babel-template@^6.23.0, babel-template@^6.24.1, babel-template@^6.7.0:
version "6.24.1" version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333"
dependencies: dependencies:
@ -1176,8 +1180,8 @@ camelcase@^3.0.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
caniuse-db@^1.0.30000639: caniuse-db@^1.0.30000639:
version "1.0.30000666" version "1.0.30000669"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000666.tgz#951ed9f3d3bfaa08a06dafbb5089ab07cce6ab90" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000669.tgz#dbe8f25700ecda631dfb05cb71027762bd4b03e5"
case-sensitive-paths-webpack-plugin@2.0.0: case-sensitive-paths-webpack-plugin@2.0.0:
version "2.0.0" version "2.0.0"
@ -1257,7 +1261,7 @@ ci-info@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.0.0.tgz#dc5285f2b4e251821683681c381c3388f46ec534" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.0.0.tgz#dc5285f2b4e251821683681c381c3388f46ec534"
cipher-base@^1.0.0, cipher-base@^1.0.1: cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07"
dependencies: dependencies:
@ -1458,21 +1462,25 @@ create-ecdh@^4.0.0:
bn.js "^4.1.0" bn.js "^4.1.0"
elliptic "^6.0.0" elliptic "^6.0.0"
create-hash@^1.1.0, create-hash@^1.1.1: create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2:
version "1.1.2" version "1.1.3"
resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.2.tgz#51210062d7bb7479f6c65bb41a92208b1d61abad" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd"
dependencies: dependencies:
cipher-base "^1.0.1" cipher-base "^1.0.1"
inherits "^2.0.1" inherits "^2.0.1"
ripemd160 "^1.0.0" ripemd160 "^2.0.0"
sha.js "^2.3.6" sha.js "^2.4.0"
create-hmac@^1.1.0, create-hmac@^1.1.2: create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
version "1.1.4" version "1.1.6"
resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.4.tgz#d3fb4ba253eb8b3f56e39ea2fbcb8af747bd3170" resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06"
dependencies: dependencies:
cipher-base "^1.0.3"
create-hash "^1.1.0" create-hash "^1.1.0"
inherits "^2.0.1" inherits "^2.0.1"
ripemd160 "^2.0.0"
safe-buffer "^5.0.1"
sha.js "^2.4.8"
cross-env@5.0.0: cross-env@5.0.0:
version "5.0.0" version "5.0.0"
@ -1587,8 +1595,8 @@ decamelize@^1.0.0, decamelize@^1.1.1:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
deep-extend@~0.4.0: deep-extend@~0.4.0:
version "0.4.1" version "0.4.2"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
deep-is@~0.1.3: deep-is@~0.1.3:
version "0.1.3" version "0.1.3"
@ -1732,8 +1740,8 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
electron-to-chromium@^1.2.7: electron-to-chromium@^1.2.7:
version "1.3.9" version "1.3.10"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.9.tgz#db1cba2a26aebcca2f7f5b8b034554468609157d" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.10.tgz#63d62b785471f0d8dda85199d64579de8a449f08"
elegant-spinner@^1.0.1: elegant-spinner@^1.0.1:
version "1.0.1" version "1.0.1"
@ -2534,6 +2542,12 @@ has@^1.0.1:
dependencies: dependencies:
function-bind "^1.0.2" function-bind "^1.0.2"
hash-base@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1"
dependencies:
inherits "^2.0.1"
hash.js@^1.0.0, hash.js@^1.0.3: hash.js@^1.0.0, hash.js@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573"
@ -3191,14 +3205,14 @@ js-tokens@^3.0.0:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
js-yaml@3.6.1, js-yaml@^3.4.3, js-yaml@^3.5.1: js-yaml@3.6.1, js-yaml@^3.4.3:
version "3.6.1" version "3.6.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30"
dependencies: dependencies:
argparse "^1.0.7" argparse "^1.0.7"
esprima "^2.6.0" esprima "^2.6.0"
js-yaml@^3.7.0: js-yaml@^3.5.1, js-yaml@^3.7.0:
version "3.8.4" version "3.8.4"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6"
dependencies: dependencies:
@ -4095,10 +4109,14 @@ path-type@^1.0.0:
pinkie-promise "^2.0.0" pinkie-promise "^2.0.0"
pbkdf2@^3.0.3: pbkdf2@^3.0.3:
version "3.0.9" version "3.0.12"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.9.tgz#f2c4b25a600058b3c3773c086c37dbbee1ffe693" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.12.tgz#be36785c5067ea48d806ff923288c5f750b6b8a2"
dependencies: dependencies:
create-hmac "^1.1.2" create-hash "^1.1.2"
create-hmac "^1.1.4"
ripemd160 "^2.0.1"
safe-buffer "^5.0.1"
sha.js "^2.4.8"
performance-now@^0.2.0: performance-now@^0.2.0:
version "0.2.0" version "0.2.0"
@ -4194,20 +4212,13 @@ promise@^7.0.1, promise@^7.1.1:
dependencies: dependencies:
asap "~2.0.3" asap "~2.0.3"
prop-types@15.5.10: prop-types@15.5.10, prop-types@^15.5.2, prop-types@^15.5.4, prop-types@~15.5.0:
version "15.5.10" version "15.5.10"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
dependencies: dependencies:
fbjs "^0.8.9" fbjs "^0.8.9"
loose-envify "^1.3.1" loose-envify "^1.3.1"
prop-types@^15.5.2, prop-types@^15.5.4, prop-types@~15.5.0:
version "15.5.9"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.9.tgz#d478eef0e761396942f70c78e772f76e8be747c9"
dependencies:
fbjs "^0.8.9"
loose-envify "^1.3.1"
proxy-addr@~1.1.3: proxy-addr@~1.1.3:
version "1.1.4" version "1.1.4"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.4.tgz#27e545f6960a44a627d9b44467e35c1b6b4ce2f3" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.4.tgz#27e545f6960a44a627d9b44467e35c1b6b4ce2f3"
@ -4456,7 +4467,7 @@ repeating@^2.0.0:
dependencies: dependencies:
is-finite "^1.0.0" is-finite "^1.0.0"
request@2.79.0: request@2.79.0, request@^2.79.0:
version "2.79.0" version "2.79.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
dependencies: dependencies:
@ -4481,7 +4492,7 @@ request@2.79.0:
tunnel-agent "~0.4.1" tunnel-agent "~0.4.1"
uuid "^3.0.0" uuid "^3.0.0"
request@^2.79.0, request@^2.81.0: request@^2.81.0:
version "2.81.0" version "2.81.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
dependencies: dependencies:
@ -4574,9 +4585,12 @@ rimraf@~2.4.0:
dependencies: dependencies:
glob "^6.0.1" glob "^6.0.1"
ripemd160@^1.0.0: ripemd160@^2.0.0, ripemd160@^2.0.1:
version "1.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7"
dependencies:
hash-base "^2.0.0"
inherits "^2.0.1"
run-async@^0.1.0: run-async@^0.1.0:
version "0.1.0" version "0.1.0"
@ -4683,7 +4697,7 @@ setprototypeof@1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
sha.js@^2.3.6: sha.js@^2.4.0, sha.js@^2.4.8:
version "2.4.8" version "2.4.8"
resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f"
dependencies: dependencies:
@ -4993,8 +5007,8 @@ tar-pack@^3.4.0:
uid-number "^0.0.6" uid-number "^0.0.6"
tar-stream@^1.5.0: tar-stream@^1.5.0:
version "1.5.2" version "1.5.4"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.2.tgz#fbc6c6e83c1a19d4cb48c7d96171fc248effc7bf" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.4.tgz#36549cf04ed1aee9b2a30c0143252238daf94016"
dependencies: dependencies:
bl "^1.0.0" bl "^1.0.0"
end-of-stream "^1.0.0" end-of-stream "^1.0.0"
@ -5125,8 +5139,8 @@ ua-parser-js@^0.7.9:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb"
uglify-js@^2.6, uglify-js@^2.8.5: uglify-js@^2.6, uglify-js@^2.8.5:
version "2.8.23" version "2.8.24"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.23.tgz#8230dd9783371232d62a7821e2cf9a817270a8a0" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.24.tgz#48eb5175cf32e22ec11a47e638d7c8b4e0faf2dd"
dependencies: dependencies:
source-map "~0.5.1" source-map "~0.5.1"
yargs "~3.10.0" yargs "~3.10.0"
@ -5346,10 +5360,10 @@ which@^1.2.10, which@^1.2.12, which@^1.2.4, which@^1.2.9:
isexe "^2.0.0" isexe "^2.0.0"
wide-align@^1.1.0: wide-align@^1.1.0:
version "1.1.1" version "1.1.2"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.1.tgz#d2ea8aa2db2e66467e8b60cc3e897de3bc4429e6" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"
dependencies: dependencies:
string-width "^2.0.0" string-width "^1.0.2"
window-size@0.1.0: window-size@0.1.0:
version "0.1.0" version "0.1.0"