diff --git a/lib/prefetch.js b/lib/prefetch.js
index e2f6f93754618..eef963354b303 100644
--- a/lib/prefetch.js
+++ b/lib/prefetch.js
@@ -1,3 +1,5 @@
+/* global __NEXT_DATA__ */
+
import React from 'react'
import Link, { isLocal } from './link'
import { parse as urlParse } from 'url'
@@ -108,7 +110,7 @@ if (hasServiceWorkerSupport()) {
function getPrefetchUrl (href) {
let { pathname } = urlParse(href)
- const url = `/_next/pages${pathname}`
+ const url = `/_next/${__NEXT_DATA__.buildId}/pages${pathname}`
return url
}
diff --git a/lib/router/router.js b/lib/router/router.js
index 02ae798254cb9..f212ff43bcbe5 100644
--- a/lib/router/router.js
+++ b/lib/router/router.js
@@ -1,3 +1,5 @@
+/* global __NEXT_DATA__ */
+
import { parse, format } from 'url'
import evalScript from '../eval-script'
import shallowEquals from '../shallow-equals'
@@ -210,7 +212,7 @@ export default class Router extends EventEmitter {
}
}
- const url = `/_next/pages${route}`
+ const url = `/_next/${__NEXT_DATA__.buildId}/pages${route}`
const xhr = loadComponent(url, (err, data) => {
if (err) return reject(err)
resolve({
diff --git a/package.json b/package.json
index 7df095c8ad063..99bd6cb985d6a 100644
--- a/package.json
+++ b/package.json
@@ -76,6 +76,7 @@
"strip-ansi": "3.0.1",
"styled-jsx": "0.4.1",
"url": "0.11.0",
+ "uuid": "3.0.1",
"webpack": "2.2.0-rc.3",
"webpack-dev-middleware": "1.9.0",
"webpack-hot-middleware": "2.15.0",
diff --git a/server/build/index.js b/server/build/index.js
index e4fcb84574c05..482626deefef2 100644
--- a/server/build/index.js
+++ b/server/build/index.js
@@ -1,3 +1,6 @@
+import fs from 'mz/fs'
+import uuid from 'uuid'
+import path from 'path'
import webpack from './webpack'
import clean from './clean'
import gzipAssets from './gzip'
@@ -10,6 +13,7 @@ export default async function build (dir) {
await runCompiler(compiler)
await gzipAssets(dir)
+ await writeBuildId(dir)
}
function runCompiler (compiler) {
@@ -29,3 +33,9 @@ function runCompiler (compiler) {
})
})
}
+
+async function writeBuildId (dir) {
+ const buildIdPath = path.resolve(dir, '.next', 'BUILD_ID')
+ const buildId = uuid.v4()
+ await fs.writeFile(buildIdPath, buildId, 'utf8')
+}
diff --git a/server/document.js b/server/document.js
index 49f75272251d2..ed970b2e6cecb 100644
--- a/server/document.js
+++ b/server/document.js
@@ -85,11 +85,12 @@ export class NextScript extends Component {
}
render () {
- const { staticMarkup } = this.context._documentProps
+ const { staticMarkup, __NEXT_DATA__ } = this.context._documentProps
+ let { buildId } = __NEXT_DATA__
return
- { staticMarkup ? null : }
- { staticMarkup ? null : }
+ { staticMarkup ? null : }
+ { staticMarkup ? null : }
}
}
diff --git a/server/index.js b/server/index.js
index f50c6779c1a44..7bc89595778c2 100644
--- a/server/index.js
+++ b/server/index.js
@@ -1,5 +1,6 @@
import { resolve, join } from 'path'
import { parse } from 'url'
+import fs from 'mz/fs'
import http from 'http'
import {
renderToHTML,
@@ -46,6 +47,8 @@ export default class Server {
if (this.hotReloader) {
await this.hotReloader.start()
}
+
+ this.renderOpts.buildId = await this.readBuildId()
}
async close () {
@@ -60,17 +63,20 @@ export default class Server {
await serveStatic(req, res, p)
})
- this.router.get('/_next/main.js', async (req, res, params) => {
+ this.router.get('/_next/:buildId/main.js', async (req, res, params) => {
+ this.handleBuildId(params.buildId, res)
const p = join(this.dir, '.next/main.js')
await serveStaticWithGzip(req, res, p)
})
- this.router.get('/_next/commons.js', async (req, res, params) => {
+ this.router.get('/_next/:buildId/commons.js', async (req, res, params) => {
+ this.handleBuildId(params.buildId, res)
const p = join(this.dir, '.next/commons.js')
await serveStaticWithGzip(req, res, p)
})
- this.router.get('/_next/pages/:path*', async (req, res, params) => {
+ this.router.get('/_next/:buildId/pages/:path*', async (req, res, params) => {
+ this.handleBuildId(params.buildId, res)
const paths = params.path || ['index']
const pathname = `/${paths.join('/')}`
await this.renderJSON(req, res, pathname)
@@ -237,6 +243,31 @@ export default class Server {
}
}
+ async readBuildId () {
+ const buildIdPath = join(this.dir, '.next', 'BUILD_ID')
+ try {
+ const buildId = await fs.readFile(buildIdPath, 'utf8')
+ return buildId.trim()
+ } catch (err) {
+ if (err.code === 'ENOENT') {
+ return '-'
+ } else {
+ throw err
+ }
+ }
+ }
+
+ handleBuildId (buildId, res) {
+ if (this.dev) return
+ if (buildId !== this.renderOpts.buildId) {
+ const errorMessage = 'Build id mismatch!' +
+ 'Seems like the server and the client version of files are not the same.'
+ throw new Error(errorMessage)
+ }
+
+ res.setHeader('Cache-Control', 'max-age=365000000, immutable')
+ }
+
getCompilationError (page) {
if (!this.hotReloader) return
diff --git a/server/render.js b/server/render.js
index 6d26eaeec89f8..e1166758c01ae 100644
--- a/server/render.js
+++ b/server/render.js
@@ -34,6 +34,7 @@ export function renderErrorToHTML (err, req, res, pathname, query, opts = {}) {
async function doRender (req, res, pathname, query, {
err,
page,
+ buildId,
dir = process.cwd(),
dev = false,
staticMarkup = false
@@ -88,6 +89,7 @@ async function doRender (req, res, pathname, query, {
props,
pathname,
query,
+ buildId,
err: (err && dev) ? errorToJSON(err) : null
},
dev,
diff --git a/yarn.lock b/yarn.lock
index cb9f70c0e007b..932d774ee79a3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -733,9 +733,9 @@ babel-plugin-transform-strict-mode@^6.18.0:
babel-runtime "^6.0.0"
babel-types "^6.18.0"
-babel-preset-env@1.1.6:
- version "1.1.6"
- resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.1.6.tgz#83ce1402088e661cb5799e680d20c5a432b2873b"
+babel-preset-env@1.1.8:
+ version "1.1.8"
+ resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.1.8.tgz#c46734c6233c3f87d177513773db3cf3c1758aaa"
dependencies:
babel-plugin-check-es2015-constants "^6.3.13"
babel-plugin-syntax-trailing-function-commas "^6.13.0"
@@ -5028,7 +5028,7 @@ util@^0.10.3, util@0.10.3:
dependencies:
inherits "2.0.1"
-uuid@^3.0.0:
+uuid, uuid@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"