diff --git a/.gitignore b/.gitignore index 5402c8e..6719e04 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ test/fixture/nextoutput/ test/fixture/next.config.js test/fixture2/.next/BUILD_ID test/fixture-next5/build/ +test/fixture-next6/.next/ diff --git a/README.md b/README.md index 98bd996..d4c00dd 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,29 @@ Simple CLI and module that lets you define your own build id when using Next.js. -This is necessary if you're running multiple instances of your Next.js app on different servers sitting behind a load balancer without session affinity. Otherwise, if your Next.js builds end up with different build ids, a client loading content from different servers can result in [this Next.js error](https://github.com/zeit/next.js/blob/52ccc14059673508803f96ef1c74eecdf27fe096/server/index.js#L444), which causes the app to blow up for that client. +## New in version 2! + +When using Next.js 6+ (which introduced the [generateBuildId](https://github.com/zeit/next.js#configuring-the-build-id) config prop), you can use `next-build-id` as a module within your next.config.js logic to set the BUILD_ID to the most recent git commit hash. This approach means you don't need to use the `next-build-id` CLI - just use `next build` as normal and you'll get the build id you want! + +```js +// next.config.js +const nextBuildId = require('next-build-id') +module.exports = { + generateBuildId: async () => { + const fromGit = await nextBuildId({ dir: __dirname }) + return fromGit.id + } +} +``` + +## Intro + +This tool is necessary if you're running multiple instances of your Next.js app on different servers sitting behind a load balancer without session affinity. Otherwise, if your Next.js builds end up with different build ids, a client loading content from different servers can result in [this Next.js error](https://github.com/zeit/next.js/blob/52ccc14059673508803f96ef1c74eecdf27fe096/server/index.js#L444), which causes the app to blow up for that client. This module updates/overrides the following: - uuid defined in `.next/BUILD_ID` -- hashes for all chunks defined in `.next/build-stats.json` +- hashes for all chunks defined in `.next/build-stats.json` (Next.js 4 or below) By default, this CLI/module will overwrite those values with the hash of the latest git commit (`git rev-parse HEAD`), but it will also allow you to define your own id. @@ -77,6 +94,7 @@ This module exports a single function that accepts an options object and returns The options supported are: - `dir` (string): the directory built by `next build` +- `write` (boolean): whether to overwrite the BUILD_ID in the dist dir (not needed when using `generateBuildId` in next.config.js) - `id` (string): define a custom id instead of deferring to `git rev-parse HEAD` The returned `Promise` resolves to a result object containing: @@ -93,6 +111,7 @@ const nextBuildId = require('next-build-id') const opts = {} // opts.dir = '/path/to/input/dir' +// opts.write = true // opts.id = 'my_custom_id' nextBuildId(opts).then(result => { @@ -108,6 +127,7 @@ nextBuildId(opts).then(result => { ## Reference +- [zeit/next.js#786](https://github.com/zeit/next.js/issues/786) - [zeit/next.js#2978 (comment)](https://github.com/zeit/next.js/issues/2978#issuecomment-334849384) - [zeit/next.js#3299 (comment)](https://github.com/zeit/next.js/issues/3299#issuecomment-344973091) - ["Handle BUILD_ID Mismatch Error" on Next.js wiki](https://github.com/zeit/next.js/wiki/Handle-BUILD_ID-Mismatch-Error) diff --git a/cli.js b/cli.js index 4e58da9..6db05f9 100755 --- a/cli.js +++ b/cli.js @@ -13,6 +13,7 @@ require('sywac') .outputSettings({ maxWidth: 60 }) .parseAndExit() .then(argv => { + argv.write = true argv.dir = path.resolve(process.cwd(), argv.dir || '.') const nextBuildId = require('./index') return nextBuildId(argv) diff --git a/index.js b/index.js index 784d4e3..ec51a29 100644 --- a/index.js +++ b/index.js @@ -14,7 +14,7 @@ const defaultConfig = { assetPrefix: '', configOrigin: 'default', useFileSystemPublicRoutes: true, - // generateBuildId: () => '9f2a37be-4545-445e-91bd-' + String(new Date().getTime()).slice(1, 13), + generateBuildId: () => '9f2a37be-4545-445e-91bd-' + String(new Date().getTime()).slice(1, 13), generateEtags: true, pageExtensions: ['jsx', 'js'] } @@ -26,19 +26,19 @@ module.exports = function nextBuildId (opts) { return resolveInputDir(opts.dir) .then(inputDir => { result.inputDir = inputDir - return resolveOutputDir(inputDir) + return opts.write && resolveOutputDir(inputDir) }) .then(outputDir => { - result.outputDir = outputDir - return getFiles(outputDir) + result.outputDir = outputDir || null + return opts.write && getFiles(outputDir) }) .then(files => { - result.files = files + result.files = files || null return determineBuildId(opts.id, result.inputDir) }) .then(id => { result.id = id - return updateFiles(id, result.files) + return opts.write && updateFiles(id, result.files) }) .then(() => result) } diff --git a/test/fixture-next6/next.config.js b/test/fixture-next6/next.config.js new file mode 100644 index 0000000..58100a8 --- /dev/null +++ b/test/fixture-next6/next.config.js @@ -0,0 +1,8 @@ +const nextBuildId = require('../../index') + +module.exports = (phase, nextConfig) => { + if (process.env.NBI_TEST_CALL_DEFAULT_GENERATEBUILDID) console.log(nextConfig.defaultConfig.generateBuildId()) + return { + generateBuildId: () => nextBuildId({ dir: __dirname }).then(result => result.id) + } +} diff --git a/test/fixture-next6/package.json b/test/fixture-next6/package.json new file mode 100644 index 0000000..069c286 --- /dev/null +++ b/test/fixture-next6/package.json @@ -0,0 +1,17 @@ +{ + "name": "fixture", + "version": "1.0.0", + "description": "Fixture to test against Next.js 6", + "private": true, + "main": "index.js", + "scripts": { + "build": "next build" + }, + "author": "", + "license": "ISC", + "dependencies": { + "next": "^6.0.3", + "react": "^16.4.0", + "react-dom": "^16.4.0" + } +} diff --git a/test/fixture-next6/pages/index.js b/test/fixture-next6/pages/index.js new file mode 100644 index 0000000..e346f85 --- /dev/null +++ b/test/fixture-next6/pages/index.js @@ -0,0 +1 @@ +export default () =>
Welcome to next.js!
diff --git a/test/test-next6.js b/test/test-next6.js new file mode 100644 index 0000000..95ae8af --- /dev/null +++ b/test/test-next6.js @@ -0,0 +1,58 @@ +const path = require('path') + +const tap = require('tap') +const rimraf = require('rimraf') + +const utils = require('./utils') + +const exec = utils.exec +const mockedGitEnv = utils.mockedGitEnv +const fixturePath = path.resolve(__dirname, 'fixture-next6') + +let npmi = false +tap.beforeEach(() => { + if (npmi) return Promise.resolve() + npmi = true + return exec('npm', 'i', fixturePath) +}) + +tap.afterEach(() => { + return Promise.all(['.next'].map(dir => { + return new Promise((resolve, reject) => { + rimraf(path.resolve(fixturePath, dir), err => { + if (err) return reject(err) + resolve() + }) + }) + })) +}) + +tap.test('with next.config.js that uses a generateBuildId function', t => { + return exec('npm', 'run build', fixturePath, mockedGitEnv()) + .then(io => { + t.notOk(io.err) + t.notOk(io.stderr) + return utils.readTextFile(path.resolve(fixturePath, '.next', 'BUILD_ID')) + }) + .then(buildId => { + t.equal(buildId, '0123456789abcdef0123456789abcdef01234567') + }) +}) + +tap.test('call stupid default generateBuildId for code coverage', t => { + return exec('npm', 'run build', fixturePath, mockedGitEnv()) + .then(() => { + return exec(path.resolve(__dirname, '..', 'cli.js'), '', fixturePath, Object.assign(mockedGitEnv(), { NBI_TEST_CALL_DEFAULT_GENERATEBUILDID: true })) + }) + .then(io => { + t.notOk(io.err) + t.notOk(io.stderr) + t.match(io.stdout, /9f2a37be-4545-445e-91bd/) + t.match(io.stdout, /Build ID: 0123456789abcdef0123456789abcdef01234567/) + t.match(io.stdout, /Updated:.*BUILD_ID/) + return utils.readTextFile(path.resolve(fixturePath, '.next', 'BUILD_ID')) + }) + .then(buildId => { + t.equal(buildId, '0123456789abcdef0123456789abcdef01234567') + }) +}) diff --git a/test/utils.js b/test/utils.js index 1652fe9..bb5453d 100644 --- a/test/utils.js +++ b/test/utils.js @@ -60,6 +60,7 @@ function writeTextFile (file, data) { module.exports = { exec, + mockedGitEnv, cli, readTextFile, readJsonFile,