diff --git a/examples/with-dynamic-import/README.md b/examples/with-dynamic-import/README.md
new file mode 100644
index 00000000..79f242e4
--- /dev/null
+++ b/examples/with-dynamic-import/README.md
@@ -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
diff --git a/examples/with-dynamic-import/components/Counter.js b/examples/with-dynamic-import/components/Counter.js
new file mode 100644
index 00000000..959d8ae9
--- /dev/null
+++ b/examples/with-dynamic-import/components/Counter.js
@@ -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 (
+
+
Count is: {count}
+
this.add()}>Add
+
+ )
+ }
+}
diff --git a/examples/with-dynamic-import/components/Header.js b/examples/with-dynamic-import/components/Header.js
new file mode 100644
index 00000000..4409ca63
--- /dev/null
+++ b/examples/with-dynamic-import/components/Header.js
@@ -0,0 +1,19 @@
+import Link from 'next/link'
+
+export default () => (
+
+)
+
+const styles = {
+ a: {
+ marginRight: 10
+ }
+}
diff --git a/examples/with-dynamic-import/components/hello.js b/examples/with-dynamic-import/components/hello.js
new file mode 100644
index 00000000..b2441590
--- /dev/null
+++ b/examples/with-dynamic-import/components/hello.js
@@ -0,0 +1,3 @@
+export default () => (
+ Hello World (imported dynamiclly)
+)
diff --git a/examples/with-dynamic-import/lib/with-import.js b/examples/with-dynamic-import/lib/with-import.js
new file mode 100644
index 00000000..ea537f48
--- /dev/null
+++ b/examples/with-dynamic-import/lib/with-import.js
@@ -0,0 +1,23 @@
+import React from 'react'
+
+export default function withImport (promise, Loading = () => (Loading...
)) {
+ return class Comp extends React.Component {
+ constructor (...args) {
+ super(...args)
+ this.state = { AsyncComponent: null }
+ }
+
+ componentDidMount () {
+ promise.then((AsyncComponent) => {
+ this.setState({ AsyncComponent })
+ })
+ }
+
+ render () {
+ const { AsyncComponent } = this.state
+ if (!AsyncComponent) return ( )
+
+ return
+ }
+ }
+}
diff --git a/examples/with-dynamic-import/package.json b/examples/with-dynamic-import/package.json
new file mode 100644
index 00000000..21309efd
--- /dev/null
+++ b/examples/with-dynamic-import/package.json
@@ -0,0 +1,18 @@
+{
+ "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": {
+ "next": "*",
+ "react": "^15.4.2",
+ "react-dom": "^15.4.2"
+ },
+ "author": "",
+ "license": "ISC"
+}
diff --git a/examples/with-dynamic-import/pages/about.js b/examples/with-dynamic-import/pages/about.js
new file mode 100644
index 00000000..6468d744
--- /dev/null
+++ b/examples/with-dynamic-import/pages/about.js
@@ -0,0 +1,12 @@
+import Header from '../components/Header'
+import Counter from '../components/Counter'
+
+const About = () => (
+
+
+
This is the about page.
+
+
+)
+
+export default About
diff --git a/examples/with-dynamic-import/pages/index.js b/examples/with-dynamic-import/pages/index.js
new file mode 100644
index 00000000..ac9508ae
--- /dev/null
+++ b/examples/with-dynamic-import/pages/index.js
@@ -0,0 +1,15 @@
+import React from 'react'
+import Header from '../components/Header'
+import Counter from '../components/Counter'
+import withImport from '../lib/with-import'
+
+const DynamicComponent = withImport(import('../components/hello'))
+
+export default () => (
+
+
+
+
HOME PAGE is here!
+
+
+)
diff --git a/package.json b/package.json
index 309f9557..013a3918 100644
--- a/package.json
+++ b/package.json
@@ -52,6 +52,7 @@
"babel-loader": "6.4.1",
"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.22.0",
"babel-plugin-transform-es2015-modules-commonjs": "6.24.0",
"babel-plugin-transform-object-rest-spread": "6.22.0",
@@ -61,6 +62,7 @@
"babel-preset-latest": "6.24.0",
"babel-preset-react": "6.23.0",
"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",
diff --git a/server/build/babel/plugins/handle-import.js b/server/build/babel/plugins/handle-import.js
new file mode 100644
index 00000000..5d1a5242
--- /dev/null
+++ b/server/build/babel/plugins/handle-import.js
@@ -0,0 +1,41 @@
+// 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(`
+ (
+ new Promise((resolve) => {
+ if (process.pid) {
+ eval('require.ensure = (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');
+ })
+ )
+`))
+
+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)
+ }
+ }
+ }
+})
diff --git a/server/build/babel/preset.js b/server/build/babel/preset.js
index 8a61e22b..5a1cf53b 100644
--- a/server/build/babel/preset.js
+++ b/server/build/babel/preset.js
@@ -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'),
diff --git a/server/build/webpack.js b/server/build/webpack.js
index d32360c8..462c592a 100644
--- a/server/build/webpack.js
+++ b/server/build/webpack.js
@@ -277,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: [
diff --git a/server/index.js b/server/index.js
index d6af168e..6078d962 100644
--- a/server/index.js
+++ b/server/index.js
@@ -91,6 +91,13 @@ export default class Server {
await this.serveStatic(req, res, p)
},
+ // This is to support, webpack dynamic imports in production.
+ '/_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)
+ },
+
'/_next/:hash/manifest.js': async (req, res, params) => {
this.handleBuildHash('manifest.js', params.hash, res)
const p = join(this.dir, `${this.dist}/manifest.js`)
diff --git a/yarn.lock b/yarn.lock
index 7b397e2c..de75ba66 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -504,6 +504,10 @@ babel-plugin-syntax-class-properties@^6.8.0:
version "6.13.0"
resolved "https://registry.npmjs.org/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.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de"
@@ -894,6 +898,16 @@ babel-template@^6.16.0, babel-template@^6.22.0, babel-template@^6.23.0, babel-te
babylon "^6.11.0"
lodash "^4.2.0"
+babel-template@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+ babylon "^6.11.0"
+ lodash "^4.2.0"
+
babel-traverse@6.21.0:
version "6.21.0"
resolved "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.21.0.tgz#69c6365804f1a4f69eb1213f85b00a818b8c21ad"
@@ -922,6 +936,20 @@ babel-traverse@^6.18.0, babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-tr
invariant "^2.2.0"
lodash "^4.2.0"
+babel-traverse@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695"
+ dependencies:
+ babel-code-frame "^6.22.0"
+ babel-messages "^6.23.0"
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+ babylon "^6.15.0"
+ debug "^2.2.0"
+ globals "^9.0.0"
+ invariant "^2.2.0"
+ lodash "^4.2.0"
+
babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.21.0, babel-types@^6.22.0, babel-types@^6.23.0:
version "6.23.0"
resolved "https://registry.npmjs.org/babel-types/-/babel-types-6.23.0.tgz#bb17179d7538bad38cd0c9e115d340f77e7e9acf"
@@ -931,6 +959,15 @@ babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.21.0, babel-types@^6.22
lodash "^4.2.0"
to-fast-properties "^1.0.1"
+babel-types@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975"
+ dependencies:
+ babel-runtime "^6.22.0"
+ esutils "^2.0.2"
+ lodash "^4.2.0"
+ to-fast-properties "^1.0.1"
+
babylon@6.14.1, babylon@^6.11.0:
version "6.14.1"
resolved "https://registry.npmjs.org/babylon/-/babylon-6.14.1.tgz#956275fab72753ad9b3435d7afe58f8bf0a29815"
@@ -4023,11 +4060,11 @@ public-encrypt@^4.0.0:
parse-asn1 "^5.0.0"
randombytes "^2.0.1"
-punycode@1.3.2:
+punycode@1.3.2, punycode@^1.2.4:
version "1.3.2"
resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
-punycode@^1.2.4, punycode@^1.4.1:
+punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"