2018-06-16 17:23:02 +00:00
|
|
|
// @flow
|
|
|
|
import type {NextConfig} from '../server/config'
|
2018-01-30 15:40:52 +00:00
|
|
|
import path, {sep} from 'path'
|
2016-10-14 15:05:08 +00:00
|
|
|
import webpack from 'webpack'
|
2018-01-30 15:40:52 +00:00
|
|
|
import resolve from 'resolve'
|
|
|
|
import UglifyJSPlugin from 'uglifyjs-webpack-plugin'
|
|
|
|
import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin'
|
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'
|
2018-01-30 15:40:52 +00:00
|
|
|
import {getPages} from './webpack/utils'
|
2018-06-16 17:23:02 +00:00
|
|
|
import PagesPlugin from './webpack/plugins/pages-plugin'
|
|
|
|
import NextJsSsrImportPlugin from './webpack/plugins/nextjs-ssr-import'
|
|
|
|
import DynamicChunksPlugin from './webpack/plugins/dynamic-chunks-plugin'
|
|
|
|
import UnlinkFilePlugin from './webpack/plugins/unlink-file-plugin'
|
|
|
|
import PagesManifestPlugin from './webpack/plugins/pages-manifest-plugin'
|
|
|
|
import BuildManifestPlugin from './webpack/plugins/build-manifest-plugin'
|
2018-06-14 17:30:14 +00:00
|
|
|
import {SERVER_DIRECTORY, NEXT_PROJECT_ROOT, NEXT_PROJECT_ROOT_NODE_MODULES, NEXT_PROJECT_ROOT_DIST, DEFAULT_PAGES_DIR} from '../lib/constants'
|
2016-12-27 23:28:19 +00:00
|
|
|
|
2018-01-30 15:40:52 +00:00
|
|
|
function externalsConfig (dir, isServer) {
|
|
|
|
const externals = []
|
2017-12-05 23:46:06 +00:00
|
|
|
|
2018-01-30 15:40:52 +00:00
|
|
|
if (!isServer) {
|
|
|
|
return externals
|
|
|
|
}
|
2017-12-05 23:46:06 +00:00
|
|
|
|
2018-01-30 15:40:52 +00:00
|
|
|
externals.push((context, request, callback) => {
|
|
|
|
resolve(request, { basedir: dir, preserveSymlinks: true }, (err, res) => {
|
|
|
|
if (err) {
|
|
|
|
return callback()
|
|
|
|
}
|
2017-02-16 02:07:29 +00:00
|
|
|
|
2018-04-12 08:33:22 +00:00
|
|
|
// Default pages have to be transpiled
|
2018-02-06 12:09:41 +00:00
|
|
|
if (res.match(/node_modules[/\\]next[/\\]dist[/\\]pages/)) {
|
|
|
|
return callback()
|
|
|
|
}
|
|
|
|
|
2018-04-12 08:33:22 +00:00
|
|
|
// Webpack itself has to be compiled because it doesn't always use module relative paths
|
2018-02-06 12:09:41 +00:00
|
|
|
if (res.match(/node_modules[/\\]webpack/)) {
|
|
|
|
return callback()
|
|
|
|
}
|
|
|
|
|
2018-03-31 14:08:16 +00:00
|
|
|
if (res.match(/node_modules[/\\].*\.js$/)) {
|
2018-01-30 15:40:52 +00:00
|
|
|
return callback(null, `commonjs ${request}`)
|
|
|
|
}
|
2017-01-31 06:31:27 +00:00
|
|
|
|
2018-01-30 15:40:52 +00:00
|
|
|
callback()
|
|
|
|
})
|
|
|
|
})
|
2017-08-30 10:49:40 +00:00
|
|
|
|
2018-01-30 15:40:52 +00:00
|
|
|
return externals
|
|
|
|
}
|
2017-08-30 10:49:40 +00:00
|
|
|
|
2018-06-16 17:23:02 +00:00
|
|
|
type BaseConfigContext = {|
|
|
|
|
dev: boolean,
|
|
|
|
isServer: boolean,
|
|
|
|
buildId: string,
|
|
|
|
config: NextConfig
|
|
|
|
|}
|
|
|
|
|
|
|
|
export default async function getBaseWebpackConfig (dir: string, {dev = false, isServer = false, buildId, config}: BaseConfigContext) {
|
2018-01-30 15:40:52 +00:00
|
|
|
const defaultLoaders = {
|
|
|
|
babel: {
|
2018-05-23 18:26:57 +00:00
|
|
|
loader: 'next-babel-loader',
|
|
|
|
options: {dev, isServer}
|
2018-06-14 17:30:14 +00:00
|
|
|
},
|
|
|
|
hotSelfAccept: {
|
|
|
|
loader: 'hot-self-accept-loader',
|
|
|
|
options: {
|
|
|
|
include: [
|
|
|
|
path.join(dir, 'pages')
|
|
|
|
],
|
2018-06-16 13:51:00 +00:00
|
|
|
// All pages are javascript files. So we apply hot-self-accept-loader here to facilitate hot reloading of pages.
|
|
|
|
// This makes sure plugins just have to implement `pageExtensions` instead of also implementing the loader
|
|
|
|
extensions: new RegExp(`\\.+(${config.pageExtensions.join('|')})$`)
|
2018-06-14 17:30:14 +00:00
|
|
|
}
|
2016-10-15 16:17:27 +00:00
|
|
|
}
|
2018-01-30 15:40:52 +00:00
|
|
|
}
|
|
|
|
|
2018-02-01 15:21:18 +00:00
|
|
|
// Support for NODE_PATH
|
|
|
|
const nodePathList = (process.env.NODE_PATH || '')
|
|
|
|
.split(process.platform === 'win32' ? ';' : ':')
|
|
|
|
.filter((p) => !!p)
|
|
|
|
|
2018-06-14 17:30:14 +00:00
|
|
|
const pagesEntries = await getPages(dir, {nextPagesDir: DEFAULT_PAGES_DIR, dev, isServer, pageExtensions: config.pageExtensions.join('|')})
|
2018-03-31 12:00:56 +00:00
|
|
|
const totalPages = Object.keys(pagesEntries).length
|
|
|
|
const clientEntries = !isServer ? {
|
|
|
|
'main.js': [
|
2018-06-14 17:30:14 +00:00
|
|
|
dev && !isServer && path.join(NEXT_PROJECT_ROOT_DIST, 'client', 'webpack-hot-middleware-client'),
|
|
|
|
dev && !isServer && path.join(NEXT_PROJECT_ROOT_DIST, 'client', 'on-demand-entries-client'),
|
|
|
|
path.join(NEXT_PROJECT_ROOT_DIST, 'client', (dev ? `next-dev` : 'next'))
|
2018-03-31 12:00:56 +00:00
|
|
|
].filter(Boolean)
|
|
|
|
} : {}
|
2016-10-19 12:41:45 +00:00
|
|
|
|
2016-12-17 18:38:11 +00:00
|
|
|
let webpackConfig = {
|
2018-04-18 16:18:06 +00:00
|
|
|
devtool: dev ? 'cheap-module-source-map' : false,
|
2018-01-30 15:40:52 +00:00
|
|
|
name: isServer ? 'server' : 'client',
|
|
|
|
cache: true,
|
|
|
|
target: isServer ? 'node' : 'web',
|
|
|
|
externals: externalsConfig(dir, isServer),
|
2016-10-14 15:05:08 +00:00
|
|
|
context: dir,
|
2018-03-31 12:00:56 +00:00
|
|
|
// Kept as function to be backwards compatible
|
2018-01-30 15:40:52 +00:00
|
|
|
entry: async () => {
|
|
|
|
return {
|
2018-03-31 12:00:56 +00:00
|
|
|
...clientEntries,
|
|
|
|
// Only _error and _document when in development. The rest is handled by on-demand-entries
|
|
|
|
...pagesEntries
|
2018-01-30 15:40:52 +00:00
|
|
|
}
|
|
|
|
},
|
2016-10-14 15:05:08 +00:00
|
|
|
output: {
|
2018-05-31 18:56:04 +00:00
|
|
|
path: path.join(dir, config.distDir, isServer ? SERVER_DIRECTORY : ''),
|
2016-10-14 15:05:08 +00:00
|
|
|
filename: '[name]',
|
|
|
|
libraryTarget: 'commonjs2',
|
2017-04-17 15:33:40 +00:00
|
|
|
// This saves chunks with the name given via require.ensure()
|
2018-05-23 12:37:02 +00:00
|
|
|
chunkFilename: dev ? '[name].js' : '[name]-[chunkhash].js',
|
2018-04-18 16:18:06 +00:00
|
|
|
strictModuleExceptionHandling: true
|
2016-10-14 15:05:08 +00:00
|
|
|
},
|
2018-01-30 15:40:52 +00:00
|
|
|
performance: { hints: false },
|
2016-10-14 15:05:08 +00:00
|
|
|
resolve: {
|
2018-01-30 15:40:52 +00:00
|
|
|
extensions: ['.js', '.jsx', '.json'],
|
|
|
|
modules: [
|
2018-06-14 17:30:14 +00:00
|
|
|
NEXT_PROJECT_ROOT_NODE_MODULES,
|
2018-02-01 15:21:18 +00:00
|
|
|
'node_modules',
|
|
|
|
...nodePathList // Support for NODE_PATH environment variable
|
2018-01-30 15:40:52 +00:00
|
|
|
],
|
2018-01-03 12:43:48 +00:00
|
|
|
alias: {
|
2018-06-14 17:30:14 +00:00
|
|
|
next: NEXT_PROJECT_ROOT
|
2018-01-30 15:40:52 +00:00
|
|
|
}
|
2016-10-14 15:05:08 +00:00
|
|
|
},
|
|
|
|
resolveLoader: {
|
2016-12-28 18:16:52 +00:00
|
|
|
modules: [
|
2018-06-14 17:30:14 +00:00
|
|
|
NEXT_PROJECT_ROOT_NODE_MODULES,
|
2017-01-01 05:57:13 +00:00
|
|
|
'node_modules',
|
2018-06-16 17:23:02 +00:00
|
|
|
path.join(__dirname, 'webpack', 'loaders'), // The loaders Next.js provides
|
2018-02-01 15:21:18 +00:00
|
|
|
...nodePathList // Support for NODE_PATH environment variable
|
2016-10-14 15:05:08 +00:00
|
|
|
]
|
|
|
|
},
|
|
|
|
module: {
|
2018-01-03 12:43:48 +00:00
|
|
|
rules: [
|
2018-01-30 15:40:52 +00:00
|
|
|
dev && !isServer && {
|
2018-06-28 18:07:41 +00:00
|
|
|
test: defaultLoaders.hotSelfAccept.options.extensions,
|
2018-06-14 17:30:14 +00:00
|
|
|
include: defaultLoaders.hotSelfAccept.options.include,
|
|
|
|
use: defaultLoaders.hotSelfAccept
|
2018-01-30 15:40:52 +00:00
|
|
|
},
|
|
|
|
{
|
2018-03-31 21:19:06 +00:00
|
|
|
test: /\.(js|jsx)$/,
|
2018-01-30 15:40:52 +00:00
|
|
|
include: [dir],
|
|
|
|
exclude: /node_modules/,
|
|
|
|
use: defaultLoaders.babel
|
|
|
|
}
|
|
|
|
].filter(Boolean)
|
2016-10-16 04:01:17 +00:00
|
|
|
},
|
2018-01-30 15:40:52 +00:00
|
|
|
plugins: [
|
|
|
|
new webpack.IgnorePlugin(/(precomputed)/, /node_modules.+(elliptic)/),
|
|
|
|
dev && new webpack.NoEmitOnErrorsPlugin(),
|
|
|
|
dev && !isServer && new FriendlyErrorsWebpackPlugin(),
|
|
|
|
dev && new webpack.NamedModulesPlugin(),
|
|
|
|
dev && !isServer && new webpack.HotModuleReplacementPlugin(), // Hot module replacement
|
|
|
|
dev && new UnlinkFilePlugin(),
|
|
|
|
dev && new CaseSensitivePathPlugin(), // Since on macOS the filesystem is case-insensitive this will make sure your path are case-sensitive
|
|
|
|
dev && new WriteFilePlugin({
|
|
|
|
exitOnErrors: false,
|
|
|
|
log: false,
|
|
|
|
// required not to cache removed files
|
|
|
|
useHashIndex: false
|
|
|
|
}),
|
|
|
|
!isServer && !dev && new UglifyJSPlugin({
|
|
|
|
parallel: true,
|
|
|
|
sourceMap: false,
|
|
|
|
uglifyOptions: {
|
2018-05-16 12:07:10 +00:00
|
|
|
mangle: {
|
|
|
|
safari10: true
|
2018-01-30 15:40:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
new webpack.DefinePlugin({
|
|
|
|
'process.env.NODE_ENV': JSON.stringify(dev ? 'development' : 'production')
|
|
|
|
}),
|
|
|
|
!dev && new webpack.optimize.ModuleConcatenationPlugin(),
|
2018-03-30 13:08:09 +00:00
|
|
|
isServer && new PagesManifestPlugin(),
|
2018-04-12 07:47:42 +00:00
|
|
|
!isServer && new BuildManifestPlugin(),
|
2018-01-30 15:40:52 +00:00
|
|
|
!isServer && new PagesPlugin(),
|
|
|
|
!isServer && new DynamicChunksPlugin(),
|
2018-02-19 10:49:41 +00:00
|
|
|
isServer && new NextJsSsrImportPlugin(),
|
2018-03-06 09:45:29 +00:00
|
|
|
// In dev mode, we don't move anything to the commons bundle.
|
|
|
|
// In production we move common modules into the existing main.js bundle
|
2018-03-21 11:16:44 +00:00
|
|
|
!isServer && new webpack.optimize.CommonsChunkPlugin({
|
2018-03-06 09:45:29 +00:00
|
|
|
name: 'main.js',
|
2018-04-12 07:47:42 +00:00
|
|
|
filename: dev ? 'static/commons/main.js' : 'static/commons/main-[chunkhash].js',
|
2018-01-30 15:40:52 +00:00
|
|
|
minChunks (module, count) {
|
2018-03-21 11:16:44 +00:00
|
|
|
// React and React DOM are used everywhere in Next.js. So they should always be common. Even in development mode, to speed up compilation.
|
2018-03-06 09:45:29 +00:00
|
|
|
if (module.resource && module.resource.includes(`${sep}react-dom${sep}`) && count >= 0) {
|
2018-01-30 15:40:52 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2018-03-06 09:45:29 +00:00
|
|
|
if (module.resource && module.resource.includes(`${sep}react${sep}`) && count >= 0) {
|
2018-01-30 15:40:52 +00:00
|
|
|
return true
|
|
|
|
}
|
2018-03-21 11:16:44 +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 each of the pages.
|
|
|
|
if (dev) {
|
|
|
|
return false
|
|
|
|
}
|
2018-01-30 15:40:52 +00:00
|
|
|
|
2018-06-07 11:12:48 +00:00
|
|
|
// Check if the module is used in the _app.js bundle
|
|
|
|
// Because _app.js is used on every page we don't want to
|
|
|
|
// duplicate them in other bundles.
|
|
|
|
const chunks = module.getChunks()
|
2018-06-28 13:19:39 +00:00
|
|
|
const appBundlePath = path.normalize('bundles/pages/_app.js')
|
2018-06-07 11:12:48 +00:00
|
|
|
const inAppBundle = chunks.some(chunk => chunk.entryModule
|
2018-06-28 13:19:39 +00:00
|
|
|
? chunk.entryModule.name === appBundlePath
|
2018-06-07 11:12:48 +00:00
|
|
|
: null
|
|
|
|
)
|
|
|
|
|
|
|
|
if (inAppBundle && chunks.length > 1) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2018-01-30 15:40:52 +00:00
|
|
|
// If there are one or two pages, only move modules to common if they are
|
|
|
|
// used in all of the pages. Otherwise, move modules used in at-least
|
|
|
|
// 1/2 of the total pages into commons.
|
|
|
|
if (totalPages <= 2) {
|
|
|
|
return count >= totalPages
|
|
|
|
}
|
|
|
|
return count >= totalPages * 0.5
|
|
|
|
}
|
|
|
|
}),
|
2018-03-06 09:45:29 +00:00
|
|
|
// We use a manifest file in development to speed up HMR
|
|
|
|
dev && !isServer && new webpack.optimize.CommonsChunkPlugin({
|
2018-04-12 07:47:42 +00:00
|
|
|
name: 'manifest.js',
|
|
|
|
filename: dev ? 'static/commons/manifest.js' : 'static/commons/manifest-[chunkhash].js'
|
2018-01-30 15:40:52 +00:00
|
|
|
})
|
|
|
|
].filter(Boolean)
|
2016-12-17 18:38:11 +00:00
|
|
|
}
|
2016-12-22 01:36:00 +00:00
|
|
|
|
2018-01-30 15:40:52 +00:00
|
|
|
if (typeof config.webpack === 'function') {
|
2018-02-17 11:37:33 +00:00
|
|
|
webpackConfig = config.webpack(webpackConfig, {dir, dev, isServer, buildId, config, defaultLoaders, totalPages})
|
2016-12-17 18:38:11 +00:00
|
|
|
}
|
2018-01-30 15:40:52 +00:00
|
|
|
|
|
|
|
return webpackConfig
|
2016-10-14 15:05:08 +00:00
|
|
|
}
|