1
0
Fork 0
mirror of https://github.com/terribleplan/next.js.git synced 2024-01-19 02:48:18 +00:00
next.js/server/build/webpack.js

266 lines
8.1 KiB
JavaScript
Raw Normal View History

2016-10-15 16:17:27 +00:00
import { resolve, join } from 'path'
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'
import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin'
import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin'
2016-10-23 16:42:13 +00:00
import UnlinkFilePlugin from './plugins/unlink-file-plugin'
import WatchPagesPlugin from './plugins/watch-pages-plugin'
import JsonPagesPlugin from './plugins/json-pages-plugin'
import getConfig from '../config'
import * as babelCore from 'babel-core'
import findBabelConfigLocation from './babel/find-config-location'
2016-10-14 15:05:08 +00:00
const documentPage = join('pages', '_document.js')
const defaultPages = [
'_error.js',
'_document.js'
]
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-21 14:39:08 +00:00
export default async function createCompiler (dir, { dev = false, quiet = false } = {}) {
2016-10-15 16:17:27 +00:00
dir = resolve(dir)
const config = getConfig(dir)
const defaultEntries = dev
? [join(__dirname, '..', '..', 'client/webpack-hot-middleware-client')] : []
const mainJS = dev
? require.resolve('../../client/next-dev') : require.resolve('../../client/next')
let minChunks
2016-10-14 15:05:08 +00:00
const entry = async () => {
const entries = { 'main.js': mainJS }
2016-10-19 12:41:45 +00:00
const pages = await glob('pages/**/*.js', { cwd: dir })
for (const p of pages) {
entries[join('bundles', p)] = [...defaultEntries, `./${p}?entry`]
}
2016-10-14 15:05:08 +00:00
for (const p of defaultPages) {
const entryName = join('bundles', 'pages', p)
if (!entries[entryName]) {
entries[entryName] = [...defaultEntries, join(nextPagesDir, p) + '?entry']
}
}
// calculate minChunks of CommonsChunkPlugin for later use
minChunks = Math.max(2, pages.filter((p) => p !== documentPage).length)
return entries
}
2016-10-14 15:05:08 +00:00
2016-10-16 02:49:09 +00:00
const plugins = [
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
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'commons',
filename: 'commons.js',
minChunks (module, count) {
// NOTE: it depends on the fact that the entry funtion is always called
// before applying CommonsChunkPlugin
return count >= minChunks
}
}),
new JsonPagesPlugin(),
new CaseSensitivePathPlugin()
]
if (dev) {
plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new UnlinkFilePlugin(),
2016-12-21 14:39:08 +00:00
new WatchPagesPlugin(dir)
)
2016-12-21 14:39:08 +00:00
if (!quiet) {
plugins.push(new FriendlyErrorsWebpackPlugin())
}
} else {
plugins.push(
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new webpack.optimize.UglifyJsPlugin({
compress: { warnings: false },
sourceMap: false
})
)
}
2016-10-15 19:49:42 +00:00
const nodePathList = (process.env.NODE_PATH || '')
.split(process.platform === 'win32' ? ';' : ':')
.filter((p) => !!p)
const mainBabelOptions = {
babelrc: true,
cacheDirectory: true,
sourceMaps: dev ? 'both' : false,
presets: []
}
const configLocation = findBabelConfigLocation(dir)
if (configLocation) {
console.log(`> Using external babel configuration`)
console.log(`> location: "${configLocation}"`)
} else {
mainBabelOptions.presets.push(require.resolve('./babel/preset'))
}
const rules = (dev ? [{
test: /\.js(\?[^?]*)?$/,
loader: 'hot-self-accept-loader',
include: [
join(dir, 'pages'),
nextPagesDir
]
2016-12-05 17:09:38 +00:00
}, {
test: /\.js(\?[^?]*)?$/,
2016-12-05 17:09:38 +00:00
loader: 'react-hot-loader/webpack',
exclude: /node_modules/
}] : [])
.concat([{
test: /\.json$/,
loader: 'json-loader'
}, {
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
},
options: {
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.
transform ({ content, sourceMap, interpolatedName }) {
// Only handle .js files
if (!(/\.js$/.test(interpolatedName))) {
return { content, sourceMap }
}
const babelRuntimePath = require.resolve('babel-runtime/package')
.replace(/[\\/]package\.json$/, '')
const transpiled = babelCore.transform(content, {
presets: [require.resolve('babel-preset-es2015')],
sourceMaps: dev ? 'both' : false,
// Here we need to resolve all modules to the absolute paths.
// 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: [
[
require.resolve('babel-plugin-module-resolver'),
{
alias: {
'babel-runtime': babelRuntimePath,
react: require.resolve('react'),
'react-dom': require.resolve('react-dom'),
'react-dom/server': require.resolve('react-dom/server'),
'next/link': require.resolve('../../lib/link'),
'next/prefetch': require.resolve('../../lib/prefetch'),
'next/css': require.resolve('../../lib/css'),
'next/head': require.resolve('../../lib/head'),
'next/document': require.resolve('../../server/document'),
'next/router': require.resolve('../../lib/router'),
'styled-jsx/style': require.resolve('styled-jsx/style')
}
}
]
],
inputSourceMap: sourceMap
})
return {
content: transpiled.code,
sourceMap: transpiled.map
}
}
2016-10-15 16:17:27 +00:00
}
}, {
loader: 'babel-loader',
include: nextPagesDir,
exclude (str) {
return /node_modules/.test(str) && str.indexOf(nextPagesDir) !== 0
},
options: {
2016-12-11 09:09:54 +00:00
babelrc: false,
cacheDirectory: true,
sourceMaps: dev ? 'both' : false,
presets: [require.resolve('./babel/preset')]
}
}, {
test: /\.js(\?[^?]*)?$/,
loader: 'babel-loader',
include: [dir],
2016-10-25 09:11:39 +00:00
exclude (str) {
return /node_modules/.test(str)
2016-10-25 09:11:39 +00:00
},
options: mainBabelOptions
2016-10-19 12:41:45 +00:00
}])
let webpackConfig = {
2016-10-14 15:05:08 +00:00
context: dir,
entry,
output: {
2016-10-19 12:58:08 +00:00
path: join(dir, '.next'),
2016-10-14 15:05:08 +00:00
filename: '[name]',
libraryTarget: 'commonjs2',
publicPath: '/_webpack/',
strictModuleExceptionHandling: true,
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: {
modules: [
nextNodeModulesDir,
'node_modules',
...nodePathList
]
2016-10-14 15:05:08 +00:00
},
resolveLoader: {
modules: [
nextNodeModulesDir,
2017-01-01 05:57:13 +00:00
'node_modules',
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: {
rules
2016-10-16 04:01:17 +00:00
},
devtool: dev ? 'inline-source-map' : false,
performance: { hints: false }
}
if (config.webpack) {
console.log('> Using "webpack" config function defined in next.config.js.')
webpackConfig = await config.webpack(webpackConfig, { dev })
}
return webpack(webpackConfig)
2016-10-14 15:05:08 +00:00
}