2016-10-15 16:17:27 +00:00
|
|
|
import { resolve, join } from 'path'
|
2016-12-02 01:43:38 +00:00
|
|
|
import { createHash } from 'crypto'
|
2016-10-14 15:05:08 +00:00
|
|
|
import webpack from 'webpack'
|
|
|
|
import glob from 'glob-promise'
|
2016-10-15 19:49:42 +00:00
|
|
|
import WriteFilePlugin from 'write-file-webpack-plugin'
|
2016-12-03 22:01:15 +00:00
|
|
|
import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin'
|
2017-01-09 02:12:29 +00:00
|
|
|
import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin'
|
2016-10-23 16:42:13 +00:00
|
|
|
import UnlinkFilePlugin from './plugins/unlink-file-plugin'
|
2017-04-18 04:18:43 +00:00
|
|
|
import PagesPlugin from './plugins/pages-plugin'
|
2017-03-24 07:51:34 +00:00
|
|
|
import CombineAssetsPlugin from './plugins/combine-assets-plugin'
|
2016-12-17 18:38:11 +00:00
|
|
|
import getConfig from '../config'
|
2017-01-31 06:31:27 +00:00
|
|
|
import * as babelCore from 'babel-core'
|
2017-02-12 21:18:22 +00:00
|
|
|
import findBabelConfig from './babel/find-config'
|
2017-02-16 02:07:29 +00:00
|
|
|
import rootModuleRelativePath from './root-module-relative-path'
|
2016-10-14 15:05:08 +00:00
|
|
|
|
2016-12-27 23:28:19 +00:00
|
|
|
const documentPage = join('pages', '_document.js')
|
|
|
|
const defaultPages = [
|
|
|
|
'_error.js',
|
|
|
|
'_document.js'
|
|
|
|
]
|
2017-01-12 15:39:04 +00:00
|
|
|
const nextPagesDir = join(__dirname, '..', '..', 'pages')
|
|
|
|
const nextNodeModulesDir = join(__dirname, '..', '..', '..', 'node_modules')
|
|
|
|
const interpolateNames = new Map(defaultPages.map((p) => {
|
|
|
|
return [join(nextPagesDir, p), `dist/pages/${p}`]
|
|
|
|
}))
|
2016-12-27 23:28:19 +00:00
|
|
|
|
2017-02-16 02:07:29 +00:00
|
|
|
const relativeResolve = rootModuleRelativePath(require)
|
|
|
|
|
2017-02-15 13:03:33 +00:00
|
|
|
export default async function createCompiler (dir, { dev = false, quiet = false, buildDir } = {}) {
|
2016-10-15 16:17:27 +00:00
|
|
|
dir = resolve(dir)
|
2016-12-22 01:36:00 +00:00
|
|
|
const config = getConfig(dir)
|
2017-03-05 01:29:35 +00:00
|
|
|
const defaultEntries = dev ? [
|
|
|
|
join(__dirname, '..', '..', 'client', 'webpack-hot-middleware-client'),
|
|
|
|
join(__dirname, '..', '..', 'client', 'on-demand-entries-client')
|
|
|
|
] : []
|
2017-01-12 15:39:04 +00:00
|
|
|
const mainJS = dev
|
|
|
|
? require.resolve('../../client/next-dev') : require.resolve('../../client/next')
|
|
|
|
|
2017-04-07 18:39:00 +00:00
|
|
|
let totalPages
|
2016-10-14 15:05:08 +00:00
|
|
|
|
2017-01-12 15:39:04 +00:00
|
|
|
const entry = async () => {
|
2017-03-05 01:29:35 +00:00
|
|
|
const entries = {
|
|
|
|
'main.js': [
|
|
|
|
...defaultEntries,
|
|
|
|
mainJS
|
|
|
|
]
|
|
|
|
}
|
2016-10-19 12:41:45 +00:00
|
|
|
|
2017-01-12 15:39:04 +00:00
|
|
|
const pages = await glob('pages/**/*.js', { cwd: dir })
|
2017-02-26 19:45:16 +00:00
|
|
|
const devPages = pages.filter((p) => p === 'pages/_document.js' || p === 'pages/_error.js')
|
|
|
|
|
|
|
|
// In the dev environment, on-demand-entry-handler will take care of
|
|
|
|
// managing pages.
|
|
|
|
if (dev) {
|
|
|
|
for (const p of devPages) {
|
2017-03-18 23:01:55 +00:00
|
|
|
entries[join('bundles', p)] = [`./${p}?entry`]
|
2017-02-26 19:45:16 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (const p of pages) {
|
2017-03-18 23:01:55 +00:00
|
|
|
entries[join('bundles', p)] = [`./${p}?entry`]
|
2017-02-26 19:45:16 +00:00
|
|
|
}
|
2016-12-27 23:28:19 +00:00
|
|
|
}
|
2016-10-14 15:05:08 +00:00
|
|
|
|
2017-01-12 15:39:04 +00:00
|
|
|
for (const p of defaultPages) {
|
|
|
|
const entryName = join('bundles', 'pages', p)
|
|
|
|
if (!entries[entryName]) {
|
2017-03-18 23:01:55 +00:00
|
|
|
entries[entryName] = [join(nextPagesDir, p) + '?entry']
|
2017-01-12 15:39:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-07 18:39:00 +00:00
|
|
|
totalPages = pages.filter((p) => p !== documentPage).length
|
2017-01-12 15:39:04 +00:00
|
|
|
|
|
|
|
return entries
|
|
|
|
}
|
2016-10-14 15:05:08 +00:00
|
|
|
|
2016-10-16 02:49:09 +00:00
|
|
|
const plugins = [
|
2016-12-28 18:16:52 +00:00
|
|
|
new webpack.LoaderOptionsPlugin({
|
|
|
|
options: {
|
|
|
|
context: dir,
|
|
|
|
customInterpolateName (url, name, opts) {
|
|
|
|
return interpolateNames.get(this.resourcePath) || url
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}),
|
2016-10-18 14:14:00 +00:00
|
|
|
new WriteFilePlugin({
|
|
|
|
exitOnErrors: false,
|
2016-10-23 16:42:13 +00:00
|
|
|
log: false,
|
|
|
|
// required not to cache removed files
|
|
|
|
useHashIndex: false
|
2016-11-28 00:15:56 +00:00
|
|
|
}),
|
|
|
|
new webpack.optimize.CommonsChunkPlugin({
|
|
|
|
name: 'commons',
|
2016-12-06 09:25:01 +00:00
|
|
|
filename: 'commons.js',
|
2017-01-12 15:39:04 +00:00
|
|
|
minChunks (module, count) {
|
2017-03-26 20:48:59 +00:00
|
|
|
// In the dev we use on-demand-entries.
|
|
|
|
// So, it makes no sense to use commonChunks based on the minChunks count.
|
|
|
|
// Instead, we move all the code in node_modules into this chunk.
|
|
|
|
// With that, we could gain better performance for page-rebuild process.
|
|
|
|
if (dev) {
|
|
|
|
return module.context && module.context.indexOf('node_modules') >= 0
|
|
|
|
}
|
2017-02-28 05:49:49 +00:00
|
|
|
|
2017-04-07 18:39:00 +00:00
|
|
|
// Move modules used in at-least 1/2 of the total pages into commons.
|
|
|
|
return count >= totalPages * 0.5
|
2017-01-12 15:39:04 +00:00
|
|
|
}
|
2016-12-31 12:46:23 +00:00
|
|
|
}),
|
2017-03-26 20:48:59 +00:00
|
|
|
// This chunk contains all the webpack related code. So, all the changes
|
|
|
|
// related to that happens to this chunk.
|
|
|
|
// It won't touch commons.js and that gives us much better re-build perf.
|
|
|
|
new webpack.optimize.CommonsChunkPlugin({
|
|
|
|
name: 'manifest',
|
|
|
|
filename: 'manifest.js'
|
|
|
|
}),
|
2017-02-26 19:45:16 +00:00
|
|
|
new webpack.DefinePlugin({
|
|
|
|
'process.env.NODE_ENV': JSON.stringify(dev ? 'development' : 'production')
|
|
|
|
}),
|
2017-04-18 04:18:43 +00:00
|
|
|
new PagesPlugin(),
|
2017-01-09 02:12:29 +00:00
|
|
|
new CaseSensitivePathPlugin()
|
2016-11-18 07:26:17 +00:00
|
|
|
]
|
|
|
|
|
2016-12-16 20:33:08 +00:00
|
|
|
if (dev) {
|
2016-11-18 07:26:17 +00:00
|
|
|
plugins.push(
|
|
|
|
new webpack.HotModuleReplacementPlugin(),
|
2017-01-12 15:39:04 +00:00
|
|
|
new webpack.NoEmitOnErrorsPlugin(),
|
2017-02-26 19:45:16 +00:00
|
|
|
new UnlinkFilePlugin()
|
2016-11-18 07:26:17 +00:00
|
|
|
)
|
2016-12-21 14:39:08 +00:00
|
|
|
if (!quiet) {
|
|
|
|
plugins.push(new FriendlyErrorsWebpackPlugin())
|
|
|
|
}
|
2016-12-16 20:33:08 +00:00
|
|
|
} else {
|
|
|
|
plugins.push(
|
2017-03-24 07:51:34 +00:00
|
|
|
new CombineAssetsPlugin({
|
2017-03-26 20:48:59 +00:00
|
|
|
input: ['manifest.js', 'commons.js', 'main.js'],
|
2017-03-24 07:51:34 +00:00
|
|
|
output: 'app.js'
|
|
|
|
}),
|
2016-12-16 20:33:08 +00:00
|
|
|
new webpack.optimize.UglifyJsPlugin({
|
|
|
|
compress: { warnings: false },
|
|
|
|
sourceMap: false
|
|
|
|
})
|
|
|
|
)
|
2016-11-18 07:26:17 +00:00
|
|
|
}
|
2016-10-15 19:49:42 +00:00
|
|
|
|
2017-01-16 05:40:14 +00:00
|
|
|
const nodePathList = (process.env.NODE_PATH || '')
|
|
|
|
.split(process.platform === 'win32' ? ';' : ':')
|
|
|
|
.filter((p) => !!p)
|
|
|
|
|
2016-12-26 18:13:45 +00:00
|
|
|
const mainBabelOptions = {
|
2016-12-23 00:49:29 +00:00
|
|
|
cacheDirectory: true,
|
2016-12-26 18:13:45 +00:00
|
|
|
presets: []
|
2016-12-22 01:36:00 +00:00
|
|
|
}
|
|
|
|
|
2017-02-12 21:18:22 +00:00
|
|
|
const externalBabelConfig = findBabelConfig(dir)
|
|
|
|
if (externalBabelConfig) {
|
2017-02-03 05:33:35 +00:00
|
|
|
console.log(`> Using external babel configuration`)
|
2017-02-12 21:18:22 +00:00
|
|
|
console.log(`> location: "${externalBabelConfig.loc}"`)
|
|
|
|
// It's possible to turn off babelrc support via babelrc itself.
|
|
|
|
// In that case, we should add our default preset.
|
|
|
|
// That's why we need to do this.
|
|
|
|
const { options } = externalBabelConfig
|
|
|
|
mainBabelOptions.babelrc = options.babelrc !== false
|
2016-12-26 18:13:45 +00:00
|
|
|
} else {
|
2017-02-03 20:18:44 +00:00
|
|
|
mainBabelOptions.babelrc = false
|
2016-12-22 01:36:00 +00:00
|
|
|
}
|
|
|
|
|
2017-02-12 21:18:22 +00:00
|
|
|
// Add our default preset if the no "babelrc" found.
|
|
|
|
if (!mainBabelOptions.babelrc) {
|
|
|
|
mainBabelOptions.presets.push(require.resolve('./babel/preset'))
|
|
|
|
}
|
|
|
|
|
2016-12-28 18:16:52 +00:00
|
|
|
const rules = (dev ? [{
|
2016-12-06 09:24:42 +00:00
|
|
|
test: /\.js(\?[^?]*)?$/,
|
2016-12-01 22:46:49 +00:00
|
|
|
loader: 'hot-self-accept-loader',
|
|
|
|
include: [
|
|
|
|
join(dir, 'pages'),
|
|
|
|
nextPagesDir
|
|
|
|
]
|
2016-12-05 17:09:38 +00:00
|
|
|
}, {
|
2016-12-06 09:24:42 +00:00
|
|
|
test: /\.js(\?[^?]*)?$/,
|
2016-12-05 17:09:38 +00:00
|
|
|
loader: 'react-hot-loader/webpack',
|
|
|
|
exclude: /node_modules/
|
2016-12-01 22:46:49 +00:00
|
|
|
}] : [])
|
|
|
|
.concat([{
|
|
|
|
test: /\.json$/,
|
|
|
|
loader: 'json-loader'
|
|
|
|
}, {
|
2016-12-06 09:24:42 +00:00
|
|
|
test: /\.(js|json)(\?[^?]*)?$/,
|
2016-10-15 16:17:27 +00:00
|
|
|
loader: 'emit-file-loader',
|
2016-10-19 12:41:45 +00:00
|
|
|
include: [dir, nextPagesDir],
|
2016-10-25 09:11:39 +00:00
|
|
|
exclude (str) {
|
|
|
|
return /node_modules/.test(str) && str.indexOf(nextPagesDir) !== 0
|
|
|
|
},
|
2016-12-28 18:16:52 +00:00
|
|
|
options: {
|
2017-01-31 06:31:27 +00:00
|
|
|
name: 'dist/[path][name].[ext]',
|
|
|
|
// By default, our babel config does not transpile ES2015 module syntax because
|
|
|
|
// webpack knows how to handle them. (That's how it can do tree-shaking)
|
|
|
|
// But Node.js doesn't know how to handle them. So, we have to transpile them here.
|
2017-02-01 06:17:15 +00:00
|
|
|
transform ({ content, sourceMap, interpolatedName }) {
|
|
|
|
// Only handle .js files
|
|
|
|
if (!(/\.js$/.test(interpolatedName))) {
|
|
|
|
return { content, sourceMap }
|
|
|
|
}
|
2017-02-16 02:07:29 +00:00
|
|
|
|
2017-01-31 06:31:27 +00:00
|
|
|
const transpiled = babelCore.transform(content, {
|
2017-02-03 20:18:44 +00:00
|
|
|
babelrc: false,
|
2017-01-31 06:31:27 +00:00
|
|
|
sourceMaps: dev ? 'both' : false,
|
2017-02-02 23:58:02 +00:00
|
|
|
// Here we need to resolve all modules to the absolute paths.
|
2017-02-02 05:56:21 +00:00
|
|
|
// Earlier we did it with the babel-preset.
|
|
|
|
// But since we don't transpile ES2015 in the preset this is not resolving.
|
|
|
|
// That's why we need to do it here.
|
|
|
|
// See more: https://github.com/zeit/next.js/issues/951
|
|
|
|
plugins: [
|
2017-02-03 20:18:44 +00:00
|
|
|
[require.resolve('babel-plugin-transform-es2015-modules-commonjs')],
|
2017-02-02 05:56:21 +00:00
|
|
|
[
|
|
|
|
require.resolve('babel-plugin-module-resolver'),
|
|
|
|
{
|
|
|
|
alias: {
|
2017-02-16 02:07:29 +00:00
|
|
|
'babel-runtime': relativeResolve('babel-runtime/package'),
|
|
|
|
'next/link': relativeResolve('../../lib/link'),
|
|
|
|
'next/prefetch': relativeResolve('../../lib/prefetch'),
|
|
|
|
'next/css': relativeResolve('../../lib/css'),
|
|
|
|
'next/head': relativeResolve('../../lib/head'),
|
|
|
|
'next/document': relativeResolve('../../server/document'),
|
|
|
|
'next/router': relativeResolve('../../lib/router'),
|
|
|
|
'next/error': relativeResolve('../../lib/error'),
|
|
|
|
'styled-jsx/style': relativeResolve('styled-jsx/style')
|
2017-02-02 05:56:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
],
|
2017-01-31 06:31:27 +00:00
|
|
|
inputSourceMap: sourceMap
|
|
|
|
})
|
|
|
|
|
|
|
|
return {
|
|
|
|
content: transpiled.code,
|
|
|
|
sourceMap: transpiled.map
|
|
|
|
}
|
|
|
|
}
|
2016-10-15 16:17:27 +00:00
|
|
|
}
|
2016-12-01 22:46:49 +00:00
|
|
|
}, {
|
2016-12-28 18:16:52 +00:00
|
|
|
loader: 'babel-loader',
|
2016-11-05 16:12:21 +00:00
|
|
|
include: nextPagesDir,
|
2017-01-12 01:57:33 +00:00
|
|
|
exclude (str) {
|
|
|
|
return /node_modules/.test(str) && str.indexOf(nextPagesDir) !== 0
|
|
|
|
},
|
2016-12-28 18:16:52 +00:00
|
|
|
options: {
|
2016-12-11 09:09:54 +00:00
|
|
|
babelrc: false,
|
2016-12-23 00:49:29 +00:00
|
|
|
cacheDirectory: true,
|
2017-01-12 01:57:33 +00:00
|
|
|
presets: [require.resolve('./babel/preset')]
|
2016-11-05 16:12:21 +00:00
|
|
|
}
|
|
|
|
}, {
|
2016-12-06 09:24:42 +00:00
|
|
|
test: /\.js(\?[^?]*)?$/,
|
2016-12-28 18:16:52 +00:00
|
|
|
loader: 'babel-loader',
|
2017-01-12 01:57:33 +00:00
|
|
|
include: [dir],
|
2016-10-25 09:11:39 +00:00
|
|
|
exclude (str) {
|
2017-01-12 01:57:33 +00:00
|
|
|
return /node_modules/.test(str)
|
2016-10-25 09:11:39 +00:00
|
|
|
},
|
2017-01-12 01:57:33 +00:00
|
|
|
options: mainBabelOptions
|
2016-10-19 12:41:45 +00:00
|
|
|
}])
|
|
|
|
|
2016-12-17 18:38:11 +00:00
|
|
|
let webpackConfig = {
|
2016-10-14 15:05:08 +00:00
|
|
|
context: dir,
|
|
|
|
entry,
|
|
|
|
output: {
|
2017-04-07 16:52:12 +00:00
|
|
|
path: buildDir ? join(buildDir, '.next') : join(dir, config.distDir),
|
2016-10-14 15:05:08 +00:00
|
|
|
filename: '[name]',
|
|
|
|
libraryTarget: 'commonjs2',
|
2017-04-07 17:58:35 +00:00
|
|
|
publicPath: '/_next/webpack/',
|
2017-01-05 17:28:54 +00:00
|
|
|
strictModuleExceptionHandling: true,
|
2016-12-02 01:43:38 +00:00
|
|
|
devtoolModuleFilenameTemplate ({ resourcePath }) {
|
|
|
|
const hash = createHash('sha1')
|
|
|
|
hash.update(Date.now() + '')
|
|
|
|
const id = hash.digest('hex').slice(0, 7)
|
|
|
|
|
|
|
|
// append hash id for cache busting
|
|
|
|
return `webpack:///${resourcePath}?${id}`
|
|
|
|
}
|
2016-10-14 15:05:08 +00:00
|
|
|
},
|
|
|
|
resolve: {
|
2016-12-28 18:16:52 +00:00
|
|
|
modules: [
|
2016-12-31 13:17:52 +00:00
|
|
|
nextNodeModulesDir,
|
2017-01-16 05:40:14 +00:00
|
|
|
'node_modules',
|
|
|
|
...nodePathList
|
|
|
|
]
|
2016-10-14 15:05:08 +00:00
|
|
|
},
|
|
|
|
resolveLoader: {
|
2016-12-28 18:16:52 +00:00
|
|
|
modules: [
|
2016-12-31 13:17:52 +00:00
|
|
|
nextNodeModulesDir,
|
2017-01-01 05:57:13 +00:00
|
|
|
'node_modules',
|
2017-01-16 05:40:14 +00:00
|
|
|
join(__dirname, 'loaders'),
|
|
|
|
...nodePathList
|
2016-10-14 15:05:08 +00:00
|
|
|
]
|
|
|
|
},
|
2016-10-15 19:49:42 +00:00
|
|
|
plugins,
|
2016-10-14 15:05:08 +00:00
|
|
|
module: {
|
2016-12-28 18:16:52 +00:00
|
|
|
rules
|
2016-10-16 04:01:17 +00:00
|
|
|
},
|
2017-05-06 06:54:47 +00:00
|
|
|
devtool: dev ? 'cheap-module-inline-source-map' : false,
|
2016-12-28 18:16:52 +00:00
|
|
|
performance: { hints: false }
|
2016-12-17 18:38:11 +00:00
|
|
|
}
|
2016-12-22 01:36:00 +00:00
|
|
|
|
2017-05-03 01:27:18 +00:00
|
|
|
if (!dev) {
|
2017-05-03 20:59:21 +00:00
|
|
|
// We do this to use the minified version of React in production.
|
|
|
|
// This will significant file size redution when turned off uglifyjs.
|
2017-05-03 01:27:18 +00:00
|
|
|
webpackConfig.resolve.alias = {
|
|
|
|
'react': require.resolve('react/dist/react.min.js'),
|
2017-05-03 20:59:21 +00:00
|
|
|
'react-dom': require.resolve('react-dom/dist/react-dom.min.js')
|
2017-05-03 01:27:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-17 18:38:11 +00:00
|
|
|
if (config.webpack) {
|
2016-12-22 01:36:00 +00:00
|
|
|
console.log('> Using "webpack" config function defined in next.config.js.')
|
2016-12-17 18:38:11 +00:00
|
|
|
webpackConfig = await config.webpack(webpackConfig, { dev })
|
|
|
|
}
|
|
|
|
return webpack(webpackConfig)
|
2016-10-14 15:05:08 +00:00
|
|
|
}
|