Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specify a different build directory for #1513 #1599

Merged
merged 7 commits into from Apr 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -11,3 +11,5 @@ npm-debug.log
# coverage
.nyc_output
coverage

.DS_Store
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we removed the .DS_Store a million times in PRs 馃槄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should just add .DS_Store like it's done in this PR 馃槃

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, yeh I have one setup just not on my personal laptop (don't use it much 馃憖 ) just thought it might be useful as I'm probably not the only one. Can remove if you want?

5 changes: 3 additions & 2 deletions bin/next-build
Expand Up @@ -22,8 +22,9 @@ if (argv.help) {
Usage
$ next build <dir>

<dir> represents where the compiled .next folder should go.
If no directory is provided, .next will be created in the current directory
<dir> 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)
}
Expand Down
5 changes: 3 additions & 2 deletions bin/next-dev
Expand Up @@ -29,8 +29,9 @@ if (argv.help) {
Usage
$ next dev <dir> -p <port number>

<dir> represents where the compiled .next folder should go.
If no directory is provided, .next will be created in the current directory
<dir> 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
Expand Down
9 changes: 6 additions & 3 deletions bin/next-start
Expand Up @@ -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'

Expand Down Expand Up @@ -32,9 +33,10 @@ if (argv.help) {
Usage
$ next start <dir> -p <port>

<dir> is the directory that contains the compiled .next folder
<dir> 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
Expand All @@ -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)
}

Expand Down
13 changes: 12 additions & 1 deletion readme.md
Expand Up @@ -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`.
Expand Down Expand Up @@ -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

Expand Down
4 changes: 3 additions & 1 deletion 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))
}
19 changes: 12 additions & 7 deletions 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'
Expand All @@ -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
Expand Down Expand Up @@ -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')
}
9 changes: 6 additions & 3 deletions 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)
Expand Down
2 changes: 1 addition & 1 deletion server/build/webpack.js
Expand Up @@ -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/',
Expand Down
3 changes: 2 additions & 1 deletion server/config.js
Expand Up @@ -5,7 +5,8 @@ const cache = new Map()

const defaultConfig = {
webpack: null,
poweredByHeader: true
poweredByHeader: true,
distDir: '.next'
}

export default function getConfig (dir) {
Expand Down
15 changes: 8 additions & 7 deletions server/index.js
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
},

Expand Down Expand Up @@ -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()
}
Expand All @@ -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]
}
Expand Down
17 changes: 11 additions & 6 deletions server/render.js
Expand Up @@ -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'
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions 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'
}
3 changes: 3 additions & 0 deletions test/integration/dist-dir/pages/index.js
@@ -0,0 +1,3 @@
export default () => (
<div>Hello World</div>
)
49 changes: 49 additions & 0 deletions 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()
})
})
})