mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Merge branch 'dynamic-import' into v3-beta
This commit is contained in:
commit
737c283bb7
|
@ -24,6 +24,7 @@ const {
|
|||
pathname,
|
||||
query,
|
||||
buildId,
|
||||
chunks,
|
||||
assetPrefix
|
||||
},
|
||||
location
|
||||
|
@ -37,7 +38,13 @@ window.__NEXT_LOADED_PAGES__.forEach(({ route, fn }) => {
|
|||
})
|
||||
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_CHUNK = pageLoader.registerChunk.bind(pageLoader)
|
||||
|
||||
const headManager = new HeadManager()
|
||||
const appContainer = document.getElementById('__next')
|
||||
|
@ -49,6 +56,11 @@ export let ErrorComponent
|
|||
let Component
|
||||
|
||||
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')
|
||||
|
||||
try {
|
||||
|
|
1
dynamic.js
Normal file
1
dynamic.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = require('./dist/lib/dynamic')
|
27
examples/with-dynamic-import/README.md
Normal file
27
examples/with-dynamic-import/README.md
Normal 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
|
19
examples/with-dynamic-import/components/Counter.js
Normal file
19
examples/with-dynamic-import/components/Counter.js
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
19
examples/with-dynamic-import/components/Header.js
Normal file
19
examples/with-dynamic-import/components/Header.js
Normal 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
|
||||
}
|
||||
}
|
3
examples/with-dynamic-import/components/hello1.js
Normal file
3
examples/with-dynamic-import/components/hello1.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default () => (
|
||||
<p>Hello World 1 (imported dynamiclly) </p>
|
||||
)
|
3
examples/with-dynamic-import/components/hello2.js
Normal file
3
examples/with-dynamic-import/components/hello2.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default () => (
|
||||
<p>Hello World 2 (imported dynamiclly) </p>
|
||||
)
|
3
examples/with-dynamic-import/components/hello3.js
Normal file
3
examples/with-dynamic-import/components/hello3.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default () => (
|
||||
<p>Hello World 3 (imported dynamiclly) </p>
|
||||
)
|
3
examples/with-dynamic-import/components/hello4.js
Normal file
3
examples/with-dynamic-import/components/hello4.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default () => (
|
||||
<p>Hello World 4 (imported dynamiclly) </p>
|
||||
)
|
3
examples/with-dynamic-import/components/hello5.js
Normal file
3
examples/with-dynamic-import/components/hello5.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default () => (
|
||||
<p>Hello World 5 (imported dynamiclly) </p>
|
||||
)
|
19
examples/with-dynamic-import/package.json
Normal file
19
examples/with-dynamic-import/package.json
Normal 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"
|
||||
}
|
12
examples/with-dynamic-import/pages/about.js
Normal file
12
examples/with-dynamic-import/pages/about.js
Normal 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
|
43
examples/with-dynamic-import/pages/index.js
Normal file
43
examples/with-dynamic-import/pages/index.js
Normal 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
154
lib/dynamic.js
Normal 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)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -10,8 +10,11 @@ export default class PageLoader {
|
|||
|
||||
this.pageCache = {}
|
||||
this.pageLoadedHandlers = {}
|
||||
this.registerEvents = mitt()
|
||||
this.pageRegisterEvents = mitt()
|
||||
this.loadingRoutes = {}
|
||||
|
||||
this.chunkRegisterEvents = mitt()
|
||||
this.loadedChunks = {}
|
||||
}
|
||||
|
||||
normalizeRoute (route) {
|
||||
|
@ -37,7 +40,7 @@ export default class PageLoader {
|
|||
|
||||
return new Promise((resolve, reject) => {
|
||||
const fire = ({ error, page }) => {
|
||||
this.registerEvents.off(route, fire)
|
||||
this.pageRegisterEvents.off(route, fire)
|
||||
|
||||
if (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
|
||||
// rather downloading it again.
|
||||
|
@ -75,7 +78,7 @@ export default class PageLoader {
|
|||
script.type = 'text/javascript'
|
||||
script.onerror = () => {
|
||||
const error = new Error(`Error when loading route: ${route}`)
|
||||
this.registerEvents.emit(route, { error })
|
||||
this.pageRegisterEvents.emit(route, { error })
|
||||
}
|
||||
|
||||
document.body.appendChild(script)
|
||||
|
@ -86,7 +89,7 @@ export default class PageLoader {
|
|||
const register = () => {
|
||||
const { error, page } = regFn()
|
||||
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.
|
||||
|
@ -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) {
|
||||
route = this.normalizeRoute(route)
|
||||
delete this.pageCache[route]
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
"babel-loader": "7.0.0",
|
||||
"babel-plugin-module-resolver": "2.6.2",
|
||||
"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-es2015-modules-commonjs": "6.24.1",
|
||||
"babel-plugin-transform-object-rest-spread": "6.22.0",
|
||||
|
@ -58,6 +59,7 @@
|
|||
"babel-preset-env": "1.3.3",
|
||||
"babel-preset-react": "6.24.1",
|
||||
"babel-runtime": "6.23.0",
|
||||
"babel-template": "6.24.1",
|
||||
"case-sensitive-paths-webpack-plugin": "2.0.0",
|
||||
"cross-spawn": "5.1.0",
|
||||
"del": "2.2.2",
|
||||
|
|
91
readme.md
91
readme.md
|
@ -32,6 +32,7 @@ Next.js is a minimalistic framework for server-rendered React applications.
|
|||
- [With `<Link>`](#with-link-1)
|
||||
- [Imperatively](#imperatively-1)
|
||||
- [Custom server and routing](#custom-server-and-routing)
|
||||
- [Dynamic Imports](#dynamic-imports)
|
||||
- [Custom `<Document>`](#custom-document)
|
||||
- [Custom error handling](#custom-error-handling)
|
||||
- [Custom configuration](#custom-configuration)
|
||||
|
@ -579,6 +580,96 @@ Supported options:
|
|||
|
||||
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>`
|
||||
|
||||
<p><details>
|
||||
|
|
57
server/build/babel/plugins/handle-import.js
Normal file
57
server/build/babel/plugins/handle-import.js
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
|
@ -20,6 +20,7 @@ module.exports = {
|
|||
],
|
||||
plugins: [
|
||||
require.resolve('babel-plugin-react-require'),
|
||||
require.resolve('./plugins/handle-import'),
|
||||
require.resolve('babel-plugin-transform-object-rest-spread'),
|
||||
require.resolve('babel-plugin-transform-class-properties'),
|
||||
require.resolve('babel-plugin-transform-runtime'),
|
||||
|
@ -33,6 +34,7 @@ module.exports = {
|
|||
'next/link': relativeResolve('../../../lib/link'),
|
||||
'next/prefetch': relativeResolve('../../../lib/prefetch'),
|
||||
'next/css': relativeResolve('../../../lib/css'),
|
||||
'next/dynamic': relativeResolve('../../../lib/dynamic'),
|
||||
'next/head': relativeResolve('../../../lib/head'),
|
||||
'next/document': relativeResolve('../../../server/document'),
|
||||
'next/router': relativeResolve('../../../lib/router'),
|
||||
|
|
39
server/build/plugins/dynamic-chunks-plugin.js
Normal file
39
server/build/plugins/dynamic-chunks-plugin.js
Normal 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()
|
||||
})
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin'
|
|||
import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin'
|
||||
import UnlinkFilePlugin from './plugins/unlink-file-plugin'
|
||||
import PagesPlugin from './plugins/pages-plugin'
|
||||
import DynamicChunksPlugin from './plugins/dynamic-chunks-plugin'
|
||||
import CombineAssetsPlugin from './plugins/combine-assets-plugin'
|
||||
import getConfig from '../config'
|
||||
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')
|
||||
}),
|
||||
new PagesPlugin(),
|
||||
new DynamicChunksPlugin(),
|
||||
new CaseSensitivePathPlugin()
|
||||
]
|
||||
|
||||
|
@ -219,6 +221,7 @@ export default async function createCompiler (dir, { dev = false, quiet = false,
|
|||
'next/link': relativeResolve('../../lib/link'),
|
||||
'next/prefetch': relativeResolve('../../lib/prefetch'),
|
||||
'next/css': relativeResolve('../../lib/css'),
|
||||
'next/dynamic': relativeResolve('../../lib/dynamic'),
|
||||
'next/head': relativeResolve('../../lib/head'),
|
||||
'next/document': relativeResolve('../../server/document'),
|
||||
'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
|
||||
return `webpack:///${resourcePath}?${id}`
|
||||
}
|
||||
},
|
||||
// This saves chunks with the name given via require.ensure()
|
||||
chunkFilename: '[name]'
|
||||
},
|
||||
resolve: {
|
||||
modules: [
|
||||
|
|
|
@ -5,9 +5,9 @@ import flush from 'styled-jsx/server'
|
|||
|
||||
export default class Document extends Component {
|
||||
static getInitialProps ({ renderPage }) {
|
||||
const { html, head, errorHtml } = renderPage()
|
||||
const { html, head, errorHtml, chunks } = renderPage()
|
||||
const styles = flush()
|
||||
return { html, head, errorHtml, styles }
|
||||
return { html, head, errorHtml, chunks, styles }
|
||||
}
|
||||
|
||||
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 () {
|
||||
const { head, styles, __NEXT_DATA__ } = this.context._documentProps
|
||||
const { pathname, buildId, assetPrefix, nextExport } = __NEXT_DATA__
|
||||
|
@ -73,6 +86,7 @@ export class Head extends Component {
|
|||
return <head>
|
||||
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page${pagePathname}`} as='script' />
|
||||
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_error/index.js`} as='script' />
|
||||
{this.getPreloadDynamicChunks()}
|
||||
{this.getPreloadMainLinks()}
|
||||
{(head || []).map((h, i) => React.cloneElement(h, { key: i }))}
|
||||
{styles || null}
|
||||
|
@ -132,26 +146,50 @@ export class NextScript extends Component {
|
|||
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 () {
|
||||
const { staticMarkup, __NEXT_DATA__ } = this.context._documentProps
|
||||
const { staticMarkup, __NEXT_DATA__, chunks } = this.context._documentProps
|
||||
const { pathname, nextExport, buildId, assetPrefix } = __NEXT_DATA__
|
||||
const pagePathname = getPagePathname(pathname, nextExport)
|
||||
|
||||
__NEXT_DATA__.chunks = chunks
|
||||
|
||||
return <div>
|
||||
{staticMarkup ? null : <script dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
__NEXT_DATA__ = ${htmlescape(__NEXT_DATA__)}
|
||||
module={}
|
||||
__NEXT_LOADED_PAGES__ = []
|
||||
__NEXT_LOADED_CHUNKS__ = []
|
||||
|
||||
__NEXT_REGISTER_PAGE = function (route, 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__/_error`} type='text/javascript' src={`${assetPrefix}/_next/${buildId}/page/_error/index.js`} />
|
||||
{staticMarkup ? null : this.getDynamicChunks()}
|
||||
{staticMarkup ? null : this.getScripts()}
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
// Get the exportPathMap from the `next.config.js`
|
||||
|
|
|
@ -99,6 +99,19 @@ export default class Server {
|
|||
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) => {
|
||||
this.handleBuildHash('manifest.js', params.hash, res)
|
||||
const p = join(this.dir, `${this.dist}/manifest.js`)
|
||||
|
|
|
@ -12,6 +12,7 @@ import { loadGetInitialProps } from '../lib/utils'
|
|||
import Head, { defaultHead } from '../lib/head'
|
||||
import App from '../lib/app'
|
||||
import ErrorDebug from '../lib/error-debug'
|
||||
import { flushChunks } from '../lib/dynamic'
|
||||
|
||||
export async function render (req, res, pathname, query, opts) {
|
||||
const html = await renderToHTML(req, res, pathname, opts)
|
||||
|
@ -79,12 +80,13 @@ async function doRender (req, res, pathname, query, {
|
|||
} finally {
|
||||
head = Head.rewind() || defaultHead()
|
||||
}
|
||||
const chunks = flushChunks()
|
||||
|
||||
if (err && dev) {
|
||||
errorHtml = render(createElement(ErrorDebug, { error: err }))
|
||||
}
|
||||
|
||||
return { html, head, errorHtml }
|
||||
return { html, head, errorHtml, chunks }
|
||||
}
|
||||
|
||||
const docProps = await loadGetInitialProps(Document, { ...ctx, renderPage })
|
||||
|
|
3
test/integration/basic/components/hello1.js
Normal file
3
test/integration/basic/components/hello1.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default () => (
|
||||
<p>Hello World 1</p>
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
import dynamic from 'next/dynamic'
|
||||
|
||||
const Hello = dynamic(import('../../components/hello1'), {
|
||||
ssr: false,
|
||||
loading: () => (<p>LOADING</p>)
|
||||
})
|
||||
|
||||
export default Hello
|
5
test/integration/basic/pages/dynamic/no-ssr.js
Normal file
5
test/integration/basic/pages/dynamic/no-ssr.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import dynamic from 'next/dynamic'
|
||||
|
||||
const Hello = dynamic(import('../../components/hello1'), { ssr: false })
|
||||
|
||||
export default Hello
|
5
test/integration/basic/pages/dynamic/ssr.js
Normal file
5
test/integration/basic/pages/dynamic/ssr.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import dynamic from 'next/dynamic'
|
||||
|
||||
const Hello = dynamic(import('../../components/hello1'))
|
||||
|
||||
export default Hello
|
|
@ -79,5 +79,20 @@ export default function ({ app }, suiteName, render) {
|
|||
expect($('h1').text()).toBe('404')
|
||||
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')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
5
test/integration/static/components/hello.js
Normal file
5
test/integration/static/components/hello.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default () => (
|
||||
<p>
|
||||
Welcome to dynamic imports.
|
||||
</p>
|
||||
)
|
|
@ -4,6 +4,7 @@ module.exports = {
|
|||
'/': { page: '/' },
|
||||
'/about': { page: '/about' },
|
||||
'/counter': { page: '/counter' },
|
||||
'/dynamic-imports': { page: '/dynamic-imports' },
|
||||
'/dynamic': { page: '/dynamic', query: { text: 'cool dynamic text' } },
|
||||
'/dynamic/one': { page: '/dynamic', query: { text: 'next export is nice' } },
|
||||
'/dynamic/two': { page: '/dynamic', query: { text: 'zeit is awesome' } }
|
||||
|
|
15
test/integration/static/pages/dynamic-imports.js
Normal file
15
test/integration/static/pages/dynamic-imports.js
Normal 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>
|
||||
)
|
|
@ -45,6 +45,9 @@ export default () => (
|
|||
<Link href='/level1/about'>
|
||||
<a id='level1-about-page'>Level1 about page</a>
|
||||
</Link>
|
||||
<Link href='/dynamic-imports'>
|
||||
<a id='dynamic-imports-page'>Dynamic imports page</a>
|
||||
</Link>
|
||||
</div>
|
||||
<p>This is the home page</p>
|
||||
<style jsx>{`
|
||||
|
|
|
@ -93,6 +93,17 @@ export default function (context) {
|
|||
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', () => {
|
||||
it('should render the home page', async () => {
|
||||
const browser = await webdriver(context.port, '/')
|
||||
|
|
|
@ -17,5 +17,10 @@ export default function (context) {
|
|||
const html = await renderViaHTTP(context.port, '/dynamic/one')
|
||||
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./)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
137
test/unit/same-loop-promise.test.js
Normal file
137
test/unit/same-loop-promise.test.js
Normal 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
102
yarn.lock
|
@ -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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.0.tgz#8f36a0a77f5c155aed6f920b844d23ba56742a02"
|
||||
dependencies:
|
||||
|
@ -304,7 +304,7 @@ babel-core@6.24.0, babel-core@^6.3.0:
|
|||
slash "^1.0.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"
|
||||
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83"
|
||||
dependencies:
|
||||
|
@ -519,6 +519,10 @@ babel-plugin-syntax-class-properties@^6.8.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"
|
||||
|
||||
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:
|
||||
version "6.13.0"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333"
|
||||
dependencies:
|
||||
|
@ -1176,8 +1180,8 @@ camelcase@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
|
||||
|
||||
caniuse-db@^1.0.30000639:
|
||||
version "1.0.30000666"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000666.tgz#951ed9f3d3bfaa08a06dafbb5089ab07cce6ab90"
|
||||
version "1.0.30000669"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000669.tgz#dbe8f25700ecda631dfb05cb71027762bd4b03e5"
|
||||
|
||||
case-sensitive-paths-webpack-plugin@2.0.0:
|
||||
version "2.0.0"
|
||||
|
@ -1257,7 +1261,7 @@ ci-info@^1.0.0:
|
|||
version "1.0.0"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07"
|
||||
dependencies:
|
||||
|
@ -1458,21 +1462,25 @@ create-ecdh@^4.0.0:
|
|||
bn.js "^4.1.0"
|
||||
elliptic "^6.0.0"
|
||||
|
||||
create-hash@^1.1.0, create-hash@^1.1.1:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.2.tgz#51210062d7bb7479f6c65bb41a92208b1d61abad"
|
||||
create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd"
|
||||
dependencies:
|
||||
cipher-base "^1.0.1"
|
||||
inherits "^2.0.1"
|
||||
ripemd160 "^1.0.0"
|
||||
sha.js "^2.3.6"
|
||||
ripemd160 "^2.0.0"
|
||||
sha.js "^2.4.0"
|
||||
|
||||
create-hmac@^1.1.0, create-hmac@^1.1.2:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.4.tgz#d3fb4ba253eb8b3f56e39ea2fbcb8af747bd3170"
|
||||
create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06"
|
||||
dependencies:
|
||||
cipher-base "^1.0.3"
|
||||
create-hash "^1.1.0"
|
||||
inherits "^2.0.1"
|
||||
ripemd160 "^2.0.0"
|
||||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
cross-env@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"
|
||||
|
||||
deep-extend@~0.4.0:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253"
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
|
||||
|
||||
deep-is@~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"
|
||||
|
||||
electron-to-chromium@^1.2.7:
|
||||
version "1.3.9"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.9.tgz#db1cba2a26aebcca2f7f5b8b034554468609157d"
|
||||
version "1.3.10"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.10.tgz#63d62b785471f0d8dda85199d64579de8a449f08"
|
||||
|
||||
elegant-spinner@^1.0.1:
|
||||
version "1.0.1"
|
||||
|
@ -2534,6 +2542,12 @@ has@^1.0.1:
|
|||
dependencies:
|
||||
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:
|
||||
version "1.0.3"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30"
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
esprima "^2.6.0"
|
||||
|
||||
js-yaml@^3.7.0:
|
||||
js-yaml@^3.5.1, js-yaml@^3.7.0:
|
||||
version "3.8.4"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6"
|
||||
dependencies:
|
||||
|
@ -4095,10 +4109,14 @@ path-type@^1.0.0:
|
|||
pinkie-promise "^2.0.0"
|
||||
|
||||
pbkdf2@^3.0.3:
|
||||
version "3.0.9"
|
||||
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.9.tgz#f2c4b25a600058b3c3773c086c37dbbee1ffe693"
|
||||
version "3.0.12"
|
||||
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.12.tgz#be36785c5067ea48d806ff923288c5f750b6b8a2"
|
||||
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:
|
||||
version "0.2.0"
|
||||
|
@ -4194,20 +4212,13 @@ promise@^7.0.1, promise@^7.1.1:
|
|||
dependencies:
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
|
||||
dependencies:
|
||||
fbjs "^0.8.9"
|
||||
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:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.4.tgz#27e545f6960a44a627d9b44467e35c1b6b4ce2f3"
|
||||
|
@ -4456,7 +4467,7 @@ repeating@^2.0.0:
|
|||
dependencies:
|
||||
is-finite "^1.0.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:
|
||||
|
@ -4481,7 +4492,7 @@ request@2.79.0:
|
|||
tunnel-agent "~0.4.1"
|
||||
uuid "^3.0.0"
|
||||
|
||||
request@^2.79.0, request@^2.81.0:
|
||||
request@^2.81.0:
|
||||
version "2.81.0"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
|
||||
dependencies:
|
||||
|
@ -4574,9 +4585,12 @@ rimraf@~2.4.0:
|
|||
dependencies:
|
||||
glob "^6.0.1"
|
||||
|
||||
ripemd160@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e"
|
||||
ripemd160@^2.0.0, ripemd160@^2.0.1:
|
||||
version "2.0.1"
|
||||
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:
|
||||
version "0.1.0"
|
||||
|
@ -4683,7 +4697,7 @@ setprototypeof@1.0.3:
|
|||
version "1.0.3"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f"
|
||||
dependencies:
|
||||
|
@ -4993,8 +5007,8 @@ tar-pack@^3.4.0:
|
|||
uid-number "^0.0.6"
|
||||
|
||||
tar-stream@^1.5.0:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.2.tgz#fbc6c6e83c1a19d4cb48c7d96171fc248effc7bf"
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.4.tgz#36549cf04ed1aee9b2a30c0143252238daf94016"
|
||||
dependencies:
|
||||
bl "^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"
|
||||
|
||||
uglify-js@^2.6, uglify-js@^2.8.5:
|
||||
version "2.8.23"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.23.tgz#8230dd9783371232d62a7821e2cf9a817270a8a0"
|
||||
version "2.8.24"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.24.tgz#48eb5175cf32e22ec11a47e638d7c8b4e0faf2dd"
|
||||
dependencies:
|
||||
source-map "~0.5.1"
|
||||
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"
|
||||
|
||||
wide-align@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.1.tgz#d2ea8aa2db2e66467e8b60cc3e897de3bc4429e6"
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"
|
||||
dependencies:
|
||||
string-width "^2.0.0"
|
||||
string-width "^1.0.2"
|
||||
|
||||
window-size@0.1.0:
|
||||
version "0.1.0"
|
||||
|
|
Loading…
Reference in a new issue