diff --git a/.gitignore b/.gitignore
index 1a60afccbeee..1f02e940c27a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,5 @@ npm-debug.log
# coverage
.nyc_output
coverage
+
+.DS_Store
diff --git a/bin/next-build b/bin/next-build
index 6a964540ec12..fcd1347aa9ac 100755
--- a/bin/next-build
+++ b/bin/next-build
@@ -22,8 +22,9 @@ if (argv.help) {
Usage
$ next build
- represents where the compiled .next folder should go.
- If no directory is provided, .next will be created in the current directory
+ represents where the compiled dist folder should go.
+ If no directory is provided, the dist folder will be created in the current directory.
+ You can set a custom folder in config https://github.com/zeit/next.js#custom-configuration, otherwise it will be created inside '.next'
`)
process.exit(0)
}
diff --git a/bin/next-dev b/bin/next-dev
index d5055a3cf200..02dda15dec7f 100755
--- a/bin/next-dev
+++ b/bin/next-dev
@@ -29,8 +29,9 @@ if (argv.help) {
Usage
$ next dev -p
- represents where the compiled .next folder should go.
- If no directory is provided, .next will be created in the current directory
+ represents where the compiled folder should go.
+ If no directory is provided, the folder will be created in the current directory.
+ You can set a custom folder in config https://github.com/zeit/next.js#custom-configuration.
Options
--port, -p A port number on which to start the application
diff --git a/bin/next-start b/bin/next-start
index 854a11e9b267..09d222315436 100755
--- a/bin/next-start
+++ b/bin/next-start
@@ -4,6 +4,7 @@ import { resolve } from 'path'
import parseArgs from 'minimist'
import Server from '../server'
import { existsSync } from 'fs'
+import getConfig from '../server/config'
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
@@ -32,9 +33,10 @@ if (argv.help) {
Usage
$ next start -p
- is the directory that contains the compiled .next folder
+ is the directory that contains the compiled dist folder
created by running \`next build\`.
If no directory is provided, the current directory will be assumed.
+ You can set a custom dist folder in config https://github.com/zeit/next.js#custom-configuration
Options
--port, -p A port number on which to start the application
@@ -45,11 +47,12 @@ if (argv.help) {
}
const dir = resolve(argv._[0] || '.')
+const dist = getConfig(dir).distDir
const srv = new Server({ dir })
-if (!existsSync(resolve(dir, '.next', 'BUILD_ID'))) {
- console.error(`> Could not find a valid build in the '.next' directory! Try building your app with 'next build' before starting the server.`)
+if (!existsSync(resolve(dir, dist, 'BUILD_ID'))) {
+ console.error(`> Could not find a valid build in the '${dist}' directory! Try building your app with 'next build' before starting the server.`)
process.exit(1)
}
diff --git a/readme.md b/readme.md
index d5bb023b6fe5..3f5c607b1b4c 100644
--- a/readme.md
+++ b/readme.md
@@ -630,6 +630,17 @@ module.exports = {
}
```
+#### Setting a custom build directory
+
+You can specify a name to use for a custom build directory. For example, the following config will create a `build` folder instead of a `.next` folder. If no configuration is specified then next will create a `.next` folder.
+
+```javascript
+// next.config.js
+module.exports = {
+ distDir: 'build'
+}
+```
+
### Customizing webpack config
In order to extend our usage of `webpack`, you can define a function that extends its config via `next.config.js`.
@@ -704,7 +715,7 @@ Then run `now` and enjoy!
Next.js can be deployed to other hosting solutions too. Please have a look at the ['Deployment'](https://github.com/zeit/next.js/wiki/Deployment) section of the wiki.
-Note: we recommend putting `.next` in `.npmignore` or `.gitignore`. Otherwise, use `files` or `now.files` to opt-into a whitelist of files you want to deploy (and obviously exclude `.next`)
+Note: we recommend putting `.next`, or your custom dist folder (Please have a look at ['Custom Config'](You can set a custom folder in config https://github.com/zeit/next.js#custom-configuration.)), in `.npmignore` or `.gitignore`. Otherwise, use `files` or `now.files` to opt-into a whitelist of files you want to deploy (and obviously exclude `.next` or your custom dist folder)
## FAQ
diff --git a/server/build/clean.js b/server/build/clean.js
index f0d0876a47db..f108bd2568e3 100644
--- a/server/build/clean.js
+++ b/server/build/clean.js
@@ -1,6 +1,8 @@
import { resolve } from 'path'
import del from 'del'
+import getConfig from '../config'
export default function clean (dir) {
- return del(resolve(dir, '.next'))
+ const dist = getConfig(dir).distDir
+ return del(resolve(dir, dist))
}
diff --git a/server/build/index.js b/server/build/index.js
index e13d36ff66f9..95195460e749 100644
--- a/server/build/index.js
+++ b/server/build/index.js
@@ -1,5 +1,6 @@
import { tmpdir } from 'os'
import { join } from 'path'
+import getConfig from '../config'
import fs from 'mz/fs'
import uuid from 'uuid'
import del from 'del'
@@ -13,8 +14,10 @@ export default async function build (dir) {
try {
await runCompiler(compiler)
- await writeBuildStats(buildDir)
- await writeBuildId(buildDir)
+
+ // Pass in both the buildDir and the dir to retrieve config
+ await writeBuildStats(buildDir, dir)
+ await writeBuildId(buildDir, dir)
} catch (err) {
console.error(`> Failed to build on ${buildDir}`)
throw err
@@ -45,22 +48,24 @@ function runCompiler (compiler) {
})
}
-async function writeBuildStats (dir) {
+async function writeBuildStats (buildDir, dir) {
+ const dist = getConfig(dir).distDir
// Here we can't use hashes in webpack chunks.
// That's because the "app.js" is not tied to a chunk.
// It's created by merging a few assets. (commons.js and main.js)
// So, we need to generate the hash ourself.
const assetHashMap = {
'app.js': {
- hash: await md5File(join(dir, '.next', 'app.js'))
+ hash: await md5File(join(buildDir, dist, 'app.js'))
}
}
- const buildStatsPath = join(dir, '.next', 'build-stats.json')
+ const buildStatsPath = join(buildDir, dist, 'build-stats.json')
await fs.writeFile(buildStatsPath, JSON.stringify(assetHashMap), 'utf8')
}
-async function writeBuildId (dir) {
- const buildIdPath = join(dir, '.next', 'BUILD_ID')
+async function writeBuildId (buildDir, dir) {
+ const dist = getConfig(dir).distDir
+ const buildIdPath = join(buildDir, dist, 'BUILD_ID')
const buildId = uuid.v4()
await fs.writeFile(buildIdPath, buildId, 'utf8')
}
diff --git a/server/build/replace.js b/server/build/replace.js
index daf071647b65..1679ba25ad97 100644
--- a/server/build/replace.js
+++ b/server/build/replace.js
@@ -1,10 +1,13 @@
import mv from 'mv'
import { join } from 'path'
+import getConfig from '../config'
export default async function replaceCurrentBuild (dir, buildDir) {
- const _dir = join(dir, '.next')
- const _buildDir = join(buildDir, '.next')
- const oldDir = join(buildDir, '.next.old')
+ const dist = getConfig(dir).distDir
+ const buildDist = getConfig(buildDir).distDir
+ const _dir = join(dir, dist)
+ const _buildDir = join(buildDir, dist)
+ const oldDir = join(buildDir, `${buildDist}.old`)
try {
await move(_dir, oldDir)
diff --git a/server/build/webpack.js b/server/build/webpack.js
index 804434a32d9f..5e9785edcd6a 100644
--- a/server/build/webpack.js
+++ b/server/build/webpack.js
@@ -265,7 +265,7 @@ export default async function createCompiler (dir, { dev = false, quiet = false,
context: dir,
entry,
output: {
- path: join(buildDir || dir, '.next'),
+ path: join(buildDir || dir, config.distDir),
filename: '[name]',
libraryTarget: 'commonjs2',
publicPath: '/_webpack/',
diff --git a/server/config.js b/server/config.js
index 0c14009e40a1..5a70252cb941 100644
--- a/server/config.js
+++ b/server/config.js
@@ -5,7 +5,8 @@ const cache = new Map()
const defaultConfig = {
webpack: null,
- poweredByHeader: true
+ poweredByHeader: true,
+ distDir: '.next'
}
export default function getConfig (dir) {
diff --git a/server/index.js b/server/index.js
index 17a45090cd73..75e4e7bf3d0d 100644
--- a/server/index.js
+++ b/server/index.js
@@ -27,7 +27,8 @@ export default class Server {
this.hotReloader = dev ? new HotReloader(this.dir, { quiet }) : null
this.http = null
this.config = getConfig(this.dir)
- this.buildStats = !dev ? require(join(this.dir, '.next', 'build-stats.json')) : null
+ this.dist = this.config.distDir
+ this.buildStats = !dev ? require(join(this.dir, this.dist, 'build-stats.json')) : null
this.buildId = !dev ? this.readBuildId() : '-'
this.renderOpts = {
dev,
@@ -92,25 +93,25 @@ export default class Server {
'/_next/:hash/manifest.js': async (req, res, params) => {
this.handleBuildHash('manifest.js', params.hash, res)
- const p = join(this.dir, '.next/manifest.js')
+ const p = join(this.dir, `${this.dist}/manifest.js`)
await this.serveStatic(req, res, p)
},
'/_next/:hash/main.js': async (req, res, params) => {
this.handleBuildHash('main.js', params.hash, res)
- const p = join(this.dir, '.next/main.js')
+ const p = join(this.dir, `${this.dist}/main.js`)
await this.serveStatic(req, res, p)
},
'/_next/:hash/commons.js': async (req, res, params) => {
this.handleBuildHash('commons.js', params.hash, res)
- const p = join(this.dir, '.next/commons.js')
+ const p = join(this.dir, `${this.dist}/commons.js`)
await this.serveStatic(req, res, p)
},
'/_next/:hash/app.js': async (req, res, params) => {
this.handleBuildHash('app.js', params.hash, res)
- const p = join(this.dir, '.next/app.js')
+ const p = join(this.dir, `${this.dist}/app.js`)
await this.serveStatic(req, res, p)
},
@@ -291,7 +292,7 @@ export default class Server {
}
readBuildId () {
- const buildIdPath = join(this.dir, '.next', 'BUILD_ID')
+ const buildIdPath = join(this.dir, this.dist, 'BUILD_ID')
const buildId = fs.readFileSync(buildIdPath, 'utf8')
return buildId.trim()
}
@@ -312,7 +313,7 @@ export default class Server {
const errors = this.hotReloader.getCompilationErrors()
if (!errors.size) return
- const id = join(this.dir, '.next', 'bundles', 'pages', page)
+ const id = join(this.dir, this.dist, 'bundles', 'pages', page)
const p = resolveFromList(id, errors.keys())
if (p) return errors.get(p)[0]
}
diff --git a/server/render.js b/server/render.js
index 36c18994e369..c0447bdf96e9 100644
--- a/server/render.js
+++ b/server/render.js
@@ -3,6 +3,7 @@ import { createElement } from 'react'
import { renderToString, renderToStaticMarkup } from 'react-dom/server'
import send from 'send'
import requireModule from './require'
+import getConfig from './config'
import resolvePath from './resolve'
import readPage from './read-page'
import { Router } from '../lib/router'
@@ -42,9 +43,11 @@ async function doRender (req, res, pathname, query, {
await ensurePage(page, { dir, hotReloader })
+ const dist = getConfig(dir).distDir
+
let [Component, Document] = await Promise.all([
- requireModule(join(dir, '.next', 'dist', 'pages', page)),
- requireModule(join(dir, '.next', 'dist', 'pages', '_document'))
+ requireModule(join(dir, dist, 'dist', 'pages', page)),
+ requireModule(join(dir, dist, 'dist', 'pages', '_document'))
])
Component = Component.default || Component
Document = Document.default || Document
@@ -56,8 +59,8 @@ async function doRender (req, res, pathname, query, {
errorComponent
] = await Promise.all([
loadGetInitialProps(Component, ctx),
- readPage(join(dir, '.next', 'bundles', 'pages', page)),
- readPage(join(dir, '.next', 'bundles', 'pages', '_error'))
+ readPage(join(dir, dist, 'bundles', 'pages', page)),
+ readPage(join(dir, dist, 'bundles', 'pages', '_error'))
])
// the response might be finshed on the getinitialprops call
@@ -107,13 +110,15 @@ async function doRender (req, res, pathname, query, {
}
export async function renderJSON (req, res, page, { dir = process.cwd(), hotReloader } = {}) {
+ const dist = getConfig(dir).distDir
await ensurePage(page, { dir, hotReloader })
- const pagePath = await resolvePath(join(dir, '.next', 'bundles', 'pages', page))
+ const pagePath = await resolvePath(join(dir, dist, 'bundles', 'pages', page))
return serveStatic(req, res, pagePath)
}
export async function renderErrorJSON (err, req, res, { dir = process.cwd(), dev = false } = {}) {
- const component = await readPage(join(dir, '.next', 'bundles', 'pages', '_error'))
+ const dist = getConfig(dir).distDir
+ const component = await readPage(join(dir, dist, 'bundles', 'pages', '_error'))
sendJSON(res, {
component,
diff --git a/test/integration/dist-dir/next.config.js b/test/integration/dist-dir/next.config.js
new file mode 100644
index 000000000000..db58ee17b506
--- /dev/null
+++ b/test/integration/dist-dir/next.config.js
@@ -0,0 +1,7 @@
+module.exports = {
+ onDemandEntries: {
+ // Make sure entries are not getting disposed.
+ maxInactiveAge: 1000 * 60 * 60
+ },
+ distDir: 'dist'
+}
diff --git a/test/integration/dist-dir/pages/index.js b/test/integration/dist-dir/pages/index.js
new file mode 100644
index 000000000000..3d446a4e89b8
--- /dev/null
+++ b/test/integration/dist-dir/pages/index.js
@@ -0,0 +1,3 @@
+export default () => (
+ Hello World
+)
diff --git a/test/integration/dist-dir/test/index.test.js b/test/integration/dist-dir/test/index.test.js
new file mode 100644
index 000000000000..913f223f230f
--- /dev/null
+++ b/test/integration/dist-dir/test/index.test.js
@@ -0,0 +1,49 @@
+/* global jasmine, describe, it, expect, beforeAll, afterAll */
+
+import { join } from 'path'
+import { existsSync } from 'fs'
+import {
+ nextServer,
+ nextBuild,
+ startApp,
+ stopApp,
+ renderViaHTTP
+} from 'next-test-utils'
+
+const appDir = join(__dirname, '../')
+let appPort
+let server
+let app
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 40000
+
+describe('Production Usage', () => {
+ beforeAll(async () => {
+ await nextBuild(appDir)
+ app = nextServer({
+ dir: join(__dirname, '../'),
+ dev: false,
+ quiet: true
+ })
+
+ server = await startApp(app)
+ appPort = server.address().port
+ })
+ afterAll(() => stopApp(server))
+
+ describe('With basic usage', () => {
+ it('should render the page', async () => {
+ const html = await renderViaHTTP(appPort, '/')
+ expect(html).toMatch(/Hello World/)
+ })
+ })
+
+ describe('File locations', () => {
+ it('should build the app within the given `dist` directory', () => {
+ expect(existsSync(join(__dirname, '/../dist/app.js'))).toBeTruthy()
+ })
+
+ it('should not build the app within the default `.next` directory', () => {
+ expect(existsSync(join(__dirname, '/../.next/app.js'))).toBeFalsy()
+ })
+ })
+})