1
0
Fork 0
mirror of https://github.com/terribleplan/next.js.git synced 2024-01-19 02:48:18 +00:00
next.js/packages/next/build/webpack-config.js
Connor Davis 419bec0b9b Fix #5674 Append crossOrigin on the client side too, add config option for crossOrigin (#5873)
# Fixes https://github.com/zeit/next.js/issues/5674

This adds config option
```js
// next.config.js
module.exports = {
  crossOrigin: 'anonymous'
}
```
This config option is defined in the webpack Define Plugin at build.
`Head` and `NextScript` now use the config option, if it's not explicitly set on the element.
This value is now passed to Webpack so it can add it to scripts that it loads.
The value is now used in `PageLoader` (on the client) so it can add it to the scripts and links that it loads.
Using `<Head crossOrigin>` or `<NextScript crossOrigin>` is now deprecated.
2018-12-13 01:05:21 +01:00

371 lines
13 KiB
JavaScript

import path from 'path'
import webpack from 'webpack'
import resolve from 'resolve'
import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin'
import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin'
import WebpackBar from 'webpackbar'
import {getPages} from './webpack/utils'
import PagesPlugin from './webpack/plugins/pages-plugin'
import NextJsSsrImportPlugin from './webpack/plugins/nextjs-ssr-import'
import NextJsSSRModuleCachePlugin from './webpack/plugins/nextjs-ssr-module-cache'
import NextJsRequireCacheHotReloader from './webpack/plugins/nextjs-require-cache-hot-reloader'
import UnlinkFilePlugin from './webpack/plugins/unlink-file-plugin'
import PagesManifestPlugin from './webpack/plugins/pages-manifest-plugin'
import BuildManifestPlugin from './webpack/plugins/build-manifest-plugin'
import ChunkNamesPlugin from './webpack/plugins/chunk-names-plugin'
import { ReactLoadablePlugin } from './webpack/plugins/react-loadable-plugin'
import {SERVER_DIRECTORY, REACT_LOADABLE_MANIFEST, CLIENT_STATIC_FILES_RUNTIME_WEBPACK, CLIENT_STATIC_FILES_RUNTIME_MAIN} from 'next-server/constants'
import {NEXT_PROJECT_ROOT, NEXT_PROJECT_ROOT_NODE_MODULES, NEXT_PROJECT_ROOT_DIST_CLIENT, NEXT_PROJECT_ROOT_DIST_SERVER, DEFAULT_PAGES_DIR} from '../lib/constants'
import AutoDllPlugin from 'autodll-webpack-plugin'
import TerserPlugin from 'terser-webpack-plugin'
import AssetsSizePlugin from './webpack/plugins/assets-size-plugin'
// The externals config makes sure that
// on the server side when modules are
// in node_modules they don't get compiled by webpack
function externalsConfig (dir, isServer, lambdas) {
const externals = []
if (!isServer) {
return externals
}
// When lambdas mode is enabled all node_modules will be compiled into the server bundles
// So that all dependencies can be devDependencies and are not required to be installed
if (lambdas) {
return [
(context, request, callback) => {
// Make react/react-dom external until we bundle the server/renderer.
if (request === 'react' || request === 'react-dom') {
return callback(null, `commonjs ${request}`)
}
resolve(request, { basedir: context, preserveSymlinks: true }, (err, res) => {
if (err) {
return callback()
}
if (res.match(/next-server[/\\]dist[/\\]lib[/\\]head/)) {
return callback(null, `commonjs next-server/dist/lib/head.js`)
}
if (res.match(/next-server[/\\]dist[/\\]lib[/\\]asset/)) {
return callback(null, `commonjs next-server/dist/lib/asset.js`)
}
if (res.match(/next-server[/\\]dist[/\\]lib[/\\]runtime-config/)) {
return callback(null, `commonjs next-server/dist/lib/runtime-config.js`)
}
// Default pages have to be transpiled
if (res.match(/next-server[/\\]dist[/\\]lib[/\\]loadable/)) {
return callback(null, `commonjs next-server/dist/lib/loadable.js`)
}
callback()
})
}
]
}
const notExternalModules = ['next/app', 'next/document', 'next/link', 'next/router', 'next/error', 'http-status', 'string-hash', 'ansi-html', 'hoist-non-react-statics', 'htmlescape']
externals.push((context, request, callback) => {
if (notExternalModules.indexOf(request) !== -1) {
return callback()
}
resolve(request, { basedir: context, preserveSymlinks: true }, (err, res) => {
if (err) {
return callback()
}
// Default pages have to be transpiled
if (res.match(/next[/\\]dist[/\\]pages/) || res.match(/next[/\\]dist[/\\]client/) || res.match(/node_modules[/\\]@babel[/\\]runtime[/\\]/) || res.match(/node_modules[/\\]@babel[/\\]runtime-corejs2[/\\]/)) {
return callback()
}
// Webpack itself has to be compiled because it doesn't always use module relative paths
if (res.match(/node_modules[/\\]webpack/) || res.match(/node_modules[/\\]css-loader/)) {
return callback()
}
// styled-jsx has to be transpiled
if (res.match(/node_modules[/\\]styled-jsx/)) {
return callback()
}
if (res.match(/node_modules[/\\].*\.js$/)) {
return callback(null, `commonjs ${request}`)
}
callback()
})
})
return externals
}
function optimizationConfig ({ dir, dev, isServer, totalPages, lambdas }) {
const terserPluginConfig = {
parallel: true,
sourceMap: false,
cache: true,
cacheKeys: (keys) => {
// path changes per build because we add buildId
// because the input is already hashed the path is not needed
delete keys.path
return keys
}
}
if (isServer && lambdas) {
return {
splitChunks: false,
minimizer: [
new TerserPlugin(terserPluginConfig)
]
}
}
if (isServer) {
return {
splitChunks: false,
minimize: false
}
}
const config = {
runtimeChunk: {
name: CLIENT_STATIC_FILES_RUNTIME_WEBPACK
},
splitChunks: {
cacheGroups: {
default: false,
vendors: false
}
}
}
if (dev) {
return config
}
// Terser is a better uglifier
config.minimizer = [
new TerserPlugin(terserPluginConfig)
]
// Only enabled in production
// This logic will create a commons bundle
// with modules that are used in 50% of all pages
config.splitChunks.chunks = 'all'
config.splitChunks.cacheGroups.commons = {
name: 'commons',
chunks: 'all',
minChunks: totalPages > 2 ? totalPages * 0.5 : 2
}
config.splitChunks.cacheGroups.react = {
name: 'commons',
chunks: 'all',
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/
}
return config
}
export default async function getBaseWebpackConfig (dir, {dev = false, isServer = false, buildId, config, lambdas = false}) {
const defaultLoaders = {
babel: {
loader: 'next-babel-loader',
options: {dev, isServer, cwd: dir}
},
hotSelfAccept: {
loader: 'hot-self-accept-loader',
options: {
include: [
path.join(dir, 'pages')
],
// 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('|')})$`)
}
}
}
// Support for NODE_PATH
const nodePathList = (process.env.NODE_PATH || '')
.split(process.platform === 'win32' ? ';' : ':')
.filter((p) => !!p)
const distDir = path.join(dir, config.distDir)
const outputPath = path.join(distDir, isServer ? SERVER_DIRECTORY : '')
const pagesEntries = await getPages(dir, {nextPagesDir: DEFAULT_PAGES_DIR, dev, buildId, isServer, pageExtensions: config.pageExtensions.join('|')})
const totalPages = Object.keys(pagesEntries).length
const clientEntries = !isServer ? {
// Backwards compatibility
'main.js': [],
[CLIENT_STATIC_FILES_RUNTIME_MAIN]: [
path.join(NEXT_PROJECT_ROOT_DIST_CLIENT, (dev ? `next-dev` : 'next'))
].filter(Boolean)
} : {}
const devServerEntries = dev && isServer ? {
'error-debug.js': path.join(NEXT_PROJECT_ROOT_DIST_SERVER, 'error-debug.js')
} : {}
const resolveConfig = {
// Disable .mjs for node_modules bundling
extensions: ['.wasm', '.js', '.mjs', '.jsx', '.json'],
modules: [
NEXT_PROJECT_ROOT_NODE_MODULES,
'node_modules',
...nodePathList // Support for NODE_PATH environment variable
],
alias: {
next: NEXT_PROJECT_ROOT
},
mainFields: [!isServer && 'browser', !isServer && 'module', 'main'].filter(Boolean)
}
const webpackMode = dev ? 'development' : 'production'
let webpackConfig = {
mode: webpackMode,
devtool: dev ? 'cheap-module-source-map' : false,
name: isServer ? 'server' : 'client',
cache: true,
target: isServer ? 'node' : 'web',
externals: externalsConfig(dir, isServer, lambdas),
optimization: optimizationConfig({dir, dev, isServer, totalPages, lambdas}),
recordsPath: path.join(outputPath, 'records.json'),
context: dir,
// Kept as function to be backwards compatible
entry: async () => {
return {
...clientEntries,
...devServerEntries,
// Only _error and _document when in development. The rest is handled by on-demand-entries
...pagesEntries
}
},
output: {
path: outputPath,
filename: ({chunk}) => {
// Use `[name]-[contenthash].js` in production
if (!dev && (chunk.name === CLIENT_STATIC_FILES_RUNTIME_MAIN || chunk.name === CLIENT_STATIC_FILES_RUNTIME_WEBPACK)) {
return chunk.name.replace(/\.js$/, '-[contenthash].js')
}
return '[name]'
},
libraryTarget: isServer ? 'commonjs2' : 'jsonp',
hotUpdateChunkFilename: 'static/webpack/[id].[hash].hot-update.js',
hotUpdateMainFilename: 'static/webpack/[hash].hot-update.json',
// This saves chunks with the name given via `import()`
chunkFilename: isServer ? `${dev ? '[name]' : '[name].[contenthash]'}.js` : `static/chunks/${dev ? '[name]' : '[name].[contenthash]'}.js`,
strictModuleExceptionHandling: true,
crossOriginLoading: config.crossOrigin,
webassemblyModuleFilename: 'static/wasm/[modulehash].wasm'
},
performance: { hints: false },
resolve: resolveConfig,
resolveLoader: {
modules: [
NEXT_PROJECT_ROOT_NODE_MODULES,
'node_modules',
path.join(__dirname, 'webpack', 'loaders'), // The loaders Next.js provides
...nodePathList // Support for NODE_PATH environment variable
]
},
module: {
rules: [
dev && !isServer && {
test: defaultLoaders.hotSelfAccept.options.extensions,
include: defaultLoaders.hotSelfAccept.options.include,
use: defaultLoaders.hotSelfAccept
},
{
test: /\.(js|jsx)$/,
include: [dir, NEXT_PROJECT_ROOT_DIST_CLIENT, DEFAULT_PAGES_DIR, /next-server[\\/]dist[\\/]lib/],
exclude: (path) => {
if (path.indexOf(NEXT_PROJECT_ROOT_DIST_CLIENT) === 0 || path.indexOf(DEFAULT_PAGES_DIR) === 0 || /next-server[\\/]dist[\\/]lib/.exec(path)) {
return false
}
return /node_modules/.exec(path)
},
use: defaultLoaders.babel
}
].filter(Boolean)
},
plugins: [
// Precompile react / react-dom for development, speeding up webpack
dev && !isServer && new AutoDllPlugin({
filename: '[name]_[hash].js',
path: './static/development/dll',
context: dir,
entry: {
dll: [
'react',
'react-dom'
]
},
config: {
mode: webpackMode,
resolve: resolveConfig
}
}),
// This plugin makes sure `output.filename` is used for entry chunks
new ChunkNamesPlugin(),
!isServer && new ReactLoadablePlugin({
filename: REACT_LOADABLE_MANIFEST
}),
new WebpackBar({
name: isServer ? 'server' : 'client'
}),
dev && !isServer && new FriendlyErrorsWebpackPlugin(),
// Even though require.cache is server only we have to clear assets from both compilations
// This is because the client compilation generates the build manifest that's used on the server side
dev && new NextJsRequireCacheHotReloader(),
dev && !isServer && new webpack.HotModuleReplacementPlugin(),
dev && new webpack.NoEmitOnErrorsPlugin(),
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 webpack.HashedModuleIdsPlugin(),
// Removes server/client code by minifier
new webpack.DefinePlugin({
'process.crossOrigin': JSON.stringify(config.crossOrigin),
'process.browser': JSON.stringify(!isServer)
}),
// This is used in client/dev-error-overlay/hot-dev-client.js to replace the dist directory
!isServer && dev && new webpack.DefinePlugin({
'process.env.__NEXT_DIST_DIR': JSON.stringify(distDir)
}),
isServer && new PagesManifestPlugin(),
!isServer && new BuildManifestPlugin(),
!isServer && new PagesPlugin(),
isServer && new NextJsSsrImportPlugin(),
isServer && new NextJsSSRModuleCachePlugin({outputPath}),
!isServer && !dev && new AssetsSizePlugin({buildId, distDir})
].filter(Boolean)
}
if (typeof config.webpack === 'function') {
webpackConfig = config.webpack(webpackConfig, {dir, dev, isServer, buildId, config, defaultLoaders, totalPages})
}
// Backwards compat for `main.js` entry key
const originalEntry = webpackConfig.entry
webpackConfig.entry = async () => {
const entry = {...await originalEntry()}
// Server compilation doesn't have main.js
if (typeof entry['main.js'] !== 'undefined') {
entry[CLIENT_STATIC_FILES_RUNTIME_MAIN] = [
...entry['main.js'],
...entry[CLIENT_STATIC_FILES_RUNTIME_MAIN]
]
delete entry['main.js']
}
return entry
}
return webpackConfig
}