Skip to content

Commit 5ab7463

Browse files
CompuIvesrauchg
authored andcommitted
Exploration of different config + expose webpack config (vercel#222)
* Use next.config.js instead of package.json * Remove irrelevant comment * Integrate with custom webpack config * Include hotReload option * Remove async/await for getConfig * Read package.json, show warning when webpack in config is defined * Prepend warning message with WARNING * Update log statements * Documentation * Restart server on change of config * Fix process handling and cases where there is no config * Also restart server when config file gets deleted * Changed second parameter of webpack to config * Support for returning Promise * Update documentation, fix bug with webpack config * Remove package.json, cdn and hotReload from config
1 parent b62a0e8 commit 5ab7463

File tree

4 files changed

+107
-26
lines changed

4 files changed

+107
-26
lines changed

README.md

+61-1
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,66 @@ Then run `now` and enjoy!
285285

286286
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`)
287287

288+
## Configuration
289+
290+
While Next.js aims to work without any configuration, sometimes there is a need to add custom behaviour.
291+
You can define custom configuration in a file called `next.config.js` in the project root directory.
292+
An example of a configuration looks like this:
293+
294+
```javascript
295+
// next.config.js
296+
module.exports = {
297+
cdn: true
298+
}
299+
```
300+
301+
### Customizing webpack config
302+
303+
Sometimes the user needs to have custom configuration for webpack to add a specific behaviour in the build process.
304+
An example of this is using `eslint-loader` to lint the files before compiling. This can be done by defining
305+
`webpack` in the config.
306+
307+
```javascript
308+
module.exports = {
309+
webpack: (webpackConfig, { dev }) => {
310+
webpackConfig.module.preLoaders.push({ test: /\.js$/, loader: 'eslint-loader' })
311+
return webpackConfig
312+
}
313+
}
314+
```
315+
316+
As you can see you need to provide a function which has two parameters `webpackConfig`, which is the config used by Next.js, and `options`, which contains
317+
`dev` (`true` if dev environment). The config you return is the config used by Next.js.
318+
You can also return a `Promise` which will be resolved first.
319+
320+
_NOTE: Use this option with care, because you can potentially break the existing webpack build configuration by using this option._
321+
322+
These are some more examples:
323+
324+
```javascript
325+
const I18nPlugin = require('i18n-webpack-plugin');
326+
327+
module.exports = {
328+
webpack: (webpackConfig, { dev }) => {
329+
// Read image files:
330+
webpackConfig.module.loaders.push({
331+
test: /\.png$/,
332+
loader: 'file'
333+
})
334+
335+
// Adding a plugin
336+
webpackConfig.plugins.push(new I18nPlugin())
337+
338+
// Or adding an alias
339+
// Create webpackConfig.resolve.alias if it doesn't exist yet:
340+
webpackConfig.resolve.alias = webpackConfig.resolve.alias || {}
341+
webpackConfig.resolve.alias.src = './src'
342+
343+
return webpackConfig
344+
}
345+
}
346+
```
347+
288348
## FAQ
289349

290350
<details>
@@ -423,7 +483,7 @@ For this reason we want to promote a situation where users can share the cache f
423483

424484
We are committed to providing a great uptime and levels of security for our CDN. Even so, we also **automatically fall back** if the CDN script fails to load [with a simple trick](http://www.hanselman.com/blog/CDNsFailButYourScriptsDontHaveToFallbackFromCDNToLocalJQuery.aspx).
425485

426-
To turn the CDN off, just set `{ “next”: { cdn: false } }` in `package.json`.
486+
To turn the CDN off, just set `module.exports = { cdn: false }` in `next.config.js`.
427487
</details>
428488

429489
<details>

bin/next

+24-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { join } from 'path'
44
import { spawn } from 'cross-spawn'
5+
import { watchFile } from 'fs'
56

67
const defaultCommand = 'dev'
78
const commands = new Set([
@@ -23,9 +24,26 @@ if (commands.has(cmd)) {
2324

2425
const bin = join(__dirname, 'next-' + cmd)
2526

26-
const proc = spawn(bin, args, { stdio: 'inherit', customFds: [0, 1, 2] })
27-
proc.on('close', (code) => process.exit(code))
28-
proc.on('error', (err) => {
29-
console.error(err)
30-
process.exit(1)
31-
})
27+
const startProcess = () => {
28+
const proc = spawn(bin, args, { stdio: 'inherit', customFds: [0, 1, 2] })
29+
proc.on('close', (code) => process.exit(code))
30+
proc.on('error', (err) => {
31+
console.error(err)
32+
process.exit(1)
33+
})
34+
return proc
35+
}
36+
37+
let proc = startProcess()
38+
39+
if (cmd === 'dev') {
40+
watchFile(join(process.cwd(), 'next.config.js'), (cur, prev) => {
41+
if (cur.size > 0 || prev.size > 0) {
42+
console.log('\n> Found a change in next.config.js, restarting the server...')
43+
// Don't listen to 'close' now since otherwise parent gets killed by listener
44+
proc.removeAllListeners('close')
45+
proc.kill()
46+
proc = startProcess()
47+
}
48+
})
49+
}

server/build/webpack.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import WatchPagesPlugin from './plugins/watch-pages-plugin'
99
import WatchRemoveEventPlugin from './plugins/watch-remove-event-plugin'
1010
import DynamicEntryPlugin from './plugins/dynamic-entry-plugin'
1111
import DetachPlugin from './plugins/detach-plugin'
12+
import getConfig from '../config'
1213

1314
export default async function createCompiler (dir, { dev = false } = {}) {
1415
dir = resolve(dir)
@@ -166,7 +167,7 @@ export default async function createCompiler (dir, { dev = false } = {}) {
166167
[errorDebugPath, 'dist/pages/_error-debug.js']
167168
])
168169

169-
return webpack({
170+
let webpackConfig = {
170171
context: dir,
171172
entry,
172173
output: {
@@ -206,5 +207,11 @@ export default async function createCompiler (dir, { dev = false } = {}) {
206207
customInterpolateName: function (url, name, opts) {
207208
return interpolateNames.get(this.resourcePath) || url
208209
}
209-
})
210+
}
211+
const config = getConfig(dir)
212+
if (config.webpack) {
213+
console.log('> Using Webpack config function defined in next.config.js.')
214+
webpackConfig = await config.webpack(webpackConfig, { dev })
215+
}
216+
return webpack(webpackConfig)
210217
}

server/config.js

+13-17
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { join } from 'path'
2-
import { readFile } from 'mz/fs'
2+
import { existsSync } from 'fs'
33

44
const cache = new Map()
55

6-
const defaultConfig = {}
6+
const defaultConfig = {
7+
webpack: null
8+
}
79

810
export default function getConfig (dir) {
911
if (!cache.has(dir)) {
@@ -12,22 +14,16 @@ export default function getConfig (dir) {
1214
return cache.get(dir)
1315
}
1416

15-
async function loadConfig (dir) {
16-
const path = join(dir, 'package.json')
17+
function loadConfig (dir) {
18+
const path = join(dir, 'next.config.js')
1719

18-
let data
19-
try {
20-
data = await readFile(path, 'utf8')
21-
} catch (err) {
22-
if (err.code === 'ENOENT') {
23-
data = '{}'
24-
} else {
25-
throw err
26-
}
27-
}
20+
let userConfig = {}
2821

29-
// no try-cache, it must be a valid json
30-
const config = JSON.parse(data).next || {}
22+
const userHasConfig = existsSync(path)
23+
if (userHasConfig) {
24+
const userConfigModule = require(path)
25+
userConfig = userConfigModule.default || userConfigModule
26+
}
3127

32-
return Object.assign({}, defaultConfig, config)
28+
return Object.assign({}, defaultConfig, userConfig)
3329
}

0 commit comments

Comments
 (0)