mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
feat: rewrite webpack config (#30)
This commit is contained in:
parent
dab1a831a6
commit
d6ed1dfc2c
@ -289,7 +289,7 @@ export default () => ({
|
||||
runtimeChunk: 'single',
|
||||
minimize: undefined as boolean | undefined,
|
||||
minimizer: undefined,
|
||||
cssMinimizer: undefined,
|
||||
// cssMinimizer: undefined,
|
||||
splitChunks: {
|
||||
chunks: 'all',
|
||||
name: undefined,
|
||||
@ -299,7 +299,9 @@ export default () => ({
|
||||
}
|
||||
}
|
||||
}
|
||||
} as WebpackConfiguration['optimization'] & { cssMinimizer: undefined | boolean | Record<string, any> },
|
||||
} as WebpackConfiguration['optimization'] & {
|
||||
// cssMinimizer: undefined | boolean | Record<string, any>
|
||||
},
|
||||
/**
|
||||
* Enable [thread-loader](https://github.com/webpack-contrib/thread-loader#thread-loader) in webpack building
|
||||
*
|
||||
|
@ -346,9 +346,9 @@ function normalizeConfig (_options: CliConfiguration) {
|
||||
}
|
||||
|
||||
// Enable cssMinimizer only when extractCSS is enabled
|
||||
if (options.build.optimization.cssMinimizer === undefined) {
|
||||
options.build.optimization.cssMinimizer = options.build.extractCSS ? {} : false
|
||||
}
|
||||
// if (options.build.optimization.cssMinimizer === undefined) {
|
||||
// options.build.optimization.cssMinimizer = options.build.extractCSS ? {} : false
|
||||
// }
|
||||
|
||||
const { loaders } = options.build
|
||||
// const vueLoader = loaders.vue
|
||||
|
@ -5,13 +5,11 @@ import Glob from 'glob'
|
||||
import webpackDevMiddleware from 'webpack-dev-middleware'
|
||||
import webpackHotMiddleware from 'webpack-hot-middleware'
|
||||
import consola from 'consola'
|
||||
|
||||
import { Nuxt } from 'src/core'
|
||||
import { TARGETS, parallel, sequence, wrapArray, isModernRequest } from 'src/utils'
|
||||
import { createMFS } from './utils/mfs'
|
||||
|
||||
import * as WebpackConfigs from './config'
|
||||
import PerfLoader from './utils/perf-loader'
|
||||
import { client, server } from './configs'
|
||||
import { createWebpackConfigContext, applyPresets, getWebpackConfig } from './utils/config'
|
||||
|
||||
const glob = pify(Glob)
|
||||
|
||||
@ -40,27 +38,32 @@ export class WebpackBundler {
|
||||
}
|
||||
|
||||
getWebpackConfig (name) {
|
||||
const Config = WebpackConfigs[name.toLowerCase()] // eslint-disable-line import/namespace
|
||||
if (!Config) {
|
||||
const ctx = createWebpackConfigContext({ nuxt: this.nuxt })
|
||||
|
||||
if (name === 'client') {
|
||||
applyPresets(ctx, client)
|
||||
} else if (name === 'server') {
|
||||
applyPresets(ctx, server)
|
||||
} else {
|
||||
throw new Error(`Unsupported webpack config ${name}`)
|
||||
}
|
||||
const config = new Config(this)
|
||||
return config.config()
|
||||
|
||||
return getWebpackConfig(ctx)
|
||||
}
|
||||
|
||||
async build () {
|
||||
const { options } = this.nuxt
|
||||
|
||||
const webpackConfigs = [
|
||||
this.getWebpackConfig('Client')
|
||||
this.getWebpackConfig('client')
|
||||
]
|
||||
|
||||
if (options.modern) {
|
||||
webpackConfigs.push(this.getWebpackConfig('Modern'))
|
||||
webpackConfigs.push(this.getWebpackConfig('modern'))
|
||||
}
|
||||
|
||||
if (options.build.ssr) {
|
||||
webpackConfigs.push(this.getWebpackConfig('Server'))
|
||||
webpackConfigs.push(this.getWebpackConfig('server'))
|
||||
}
|
||||
|
||||
await this.nuxt.callHook('webpack:config', webpackConfigs)
|
||||
@ -95,13 +98,6 @@ export class WebpackBundler {
|
||||
return compiler
|
||||
})
|
||||
|
||||
// Warm up perfLoader before build
|
||||
if (options.build.parallel) {
|
||||
consola.info('Warming up worker pools')
|
||||
PerfLoader.warmupAll({ dev: options.dev })
|
||||
consola.success('Worker pools ready')
|
||||
}
|
||||
|
||||
// Start Builds
|
||||
const runner = options.dev ? parallel : sequence
|
||||
|
||||
@ -128,7 +124,7 @@ export class WebpackBundler {
|
||||
|
||||
// --- Dev Build ---
|
||||
if (options.dev) {
|
||||
// Client buiild
|
||||
// Client build
|
||||
if (['client', 'modern'].includes(name)) {
|
||||
return new Promise((resolve, reject) => {
|
||||
compiler.hooks.done.tap('nuxt-dev', () => { resolve() })
|
||||
|
@ -1,521 +0,0 @@
|
||||
import path from 'path'
|
||||
import consola from 'consola'
|
||||
import TimeFixPlugin from 'time-fix-plugin'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
import escapeRegExp from 'lodash/escapeRegExp'
|
||||
import VueLoaderPlugin from 'vue-loader/dist/pluginWebpack5'
|
||||
// import ExtractCssChunksPlugin from 'extract-css-chunks-webpack-plugin'
|
||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
|
||||
import TerserWebpackPlugin from 'terser-webpack-plugin'
|
||||
import WebpackBar from 'webpackbar'
|
||||
import env from 'std-env'
|
||||
import semver from 'semver'
|
||||
import type { NormalizedConfiguration } from 'src/config'
|
||||
import { TARGETS, isUrl, urlJoin, getPKG } from 'src/utils'
|
||||
import PerfLoader from '../utils/perf-loader'
|
||||
import StyleLoader from '../utils/style-loader'
|
||||
import WarningIgnorePlugin from '../plugins/warning-ignore'
|
||||
import { reservedVueTags } from '../utils/reserved-tags'
|
||||
|
||||
export default class WebpackBaseConfig {
|
||||
options: NormalizedConfiguration
|
||||
|
||||
constructor (builder) {
|
||||
this.builder = builder
|
||||
this.options = builder.nuxt.options
|
||||
}
|
||||
|
||||
get colors () {
|
||||
return {
|
||||
client: 'green',
|
||||
server: 'orange',
|
||||
modern: 'blue'
|
||||
}
|
||||
}
|
||||
|
||||
get devtool () {
|
||||
return false
|
||||
}
|
||||
|
||||
get nuxtEnv () {
|
||||
return {
|
||||
isDev: this.dev,
|
||||
isServer: this.isServer,
|
||||
isClient: !this.isServer,
|
||||
isModern: Boolean(this.isModern),
|
||||
isLegacy: Boolean(!this.isModern)
|
||||
}
|
||||
}
|
||||
|
||||
get mode () {
|
||||
return this.dev ? 'development' : 'production'
|
||||
}
|
||||
|
||||
get target () {
|
||||
return this.options.target
|
||||
}
|
||||
|
||||
get dev () {
|
||||
return this.options.dev
|
||||
}
|
||||
|
||||
get loaders () {
|
||||
if (!this._loaders) {
|
||||
this._loaders = cloneDeep(this.options.build.loaders)
|
||||
// sass-loader<8 support (#6460)
|
||||
const sassLoaderPKG = getPKG('sass-loader')
|
||||
if (sassLoaderPKG && semver.lt(sassLoaderPKG.version, '8.0.0')) {
|
||||
const { sass } = this._loaders
|
||||
sass.indentedSyntax = sass.sassOptions.indentedSyntax
|
||||
delete sass.sassOptions.indentedSyntax
|
||||
}
|
||||
}
|
||||
return this._loaders
|
||||
}
|
||||
|
||||
get modulesToTranspile () {
|
||||
return [
|
||||
/\.vue\.js/i, // include SFCs in node_modules
|
||||
/consola\/src/,
|
||||
...this.normalizeTranspile({ pathNormalize: true })
|
||||
]
|
||||
}
|
||||
|
||||
normalizeTranspile ({ pathNormalize = false } = {}) {
|
||||
const transpile = []
|
||||
for (let pattern of this.options.build.transpile) {
|
||||
if (typeof pattern === 'function') {
|
||||
pattern = pattern(this.nuxtEnv)
|
||||
}
|
||||
if (pattern instanceof RegExp) {
|
||||
transpile.push(pattern)
|
||||
} else if (typeof pattern === 'string') {
|
||||
const posixModule = pattern.replace(/\\/g, '/')
|
||||
transpile.push(new RegExp(escapeRegExp(
|
||||
pathNormalize ? path.normalize(posixModule) : posixModule
|
||||
)))
|
||||
}
|
||||
}
|
||||
return transpile
|
||||
}
|
||||
|
||||
getBabelOptions () {
|
||||
const envName = this.name
|
||||
const options = {
|
||||
...this.options.build.babel,
|
||||
envName
|
||||
}
|
||||
|
||||
if (options.configFile || options.babelrc) {
|
||||
return options
|
||||
}
|
||||
|
||||
if (typeof options.plugins === 'function') {
|
||||
options.plugins = options.plugins(
|
||||
{
|
||||
envName,
|
||||
...this.nuxtEnv
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const defaultPreset = [require.resolve('../../babel-preset-app'), {}]
|
||||
|
||||
if (typeof options.presets === 'function') {
|
||||
options.presets = options.presets(
|
||||
{
|
||||
envName,
|
||||
...this.nuxtEnv
|
||||
},
|
||||
defaultPreset
|
||||
)
|
||||
}
|
||||
|
||||
if (!options.presets) {
|
||||
options.presets = [defaultPreset]
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
getFileName (key) {
|
||||
let fileName = this.options.build.filenames[key]
|
||||
if (typeof fileName === 'function') {
|
||||
fileName = fileName(this.nuxtEnv)
|
||||
}
|
||||
|
||||
if (typeof fileName === 'string' && this.dev) {
|
||||
const hash = /\[(chunkhash|contenthash|hash)(?::(\d+))?]/.exec(fileName)
|
||||
if (hash) {
|
||||
consola.warn(`Notice: Please do not use ${hash[1]} in dev mode to prevent memory leak`)
|
||||
}
|
||||
}
|
||||
return fileName
|
||||
}
|
||||
|
||||
env () {
|
||||
const env = {
|
||||
'process.env.NODE_ENV': JSON.stringify(this.mode),
|
||||
'process.mode': JSON.stringify(this.mode),
|
||||
'process.dev': this.dev,
|
||||
'process.static': this.target === TARGETS.static,
|
||||
'process.target': JSON.stringify(this.target)
|
||||
}
|
||||
if (this.options.build.aggressiveCodeRemoval) {
|
||||
env['typeof process'] = JSON.stringify(this.isServer ? 'object' : 'undefined')
|
||||
env['typeof window'] = JSON.stringify(!this.isServer ? 'object' : 'undefined')
|
||||
env['typeof document'] = JSON.stringify(!this.isServer ? 'object' : 'undefined')
|
||||
}
|
||||
|
||||
Object.entries(this.options.env).forEach(([key, value]) => {
|
||||
env['process.env.' + key] =
|
||||
['boolean', 'number'].includes(typeof value)
|
||||
? value
|
||||
: JSON.stringify(value)
|
||||
})
|
||||
return env
|
||||
}
|
||||
|
||||
output () {
|
||||
const {
|
||||
build: { publicPath },
|
||||
buildDir,
|
||||
router
|
||||
} = this.options
|
||||
return {
|
||||
path: path.resolve(buildDir, 'dist', this.isServer ? 'server' : 'client'),
|
||||
filename: this.getFileName('app'),
|
||||
chunkFilename: this.getFileName('chunk'),
|
||||
publicPath: isUrl(publicPath) ? publicPath : urlJoin(router.base, publicPath)
|
||||
}
|
||||
}
|
||||
|
||||
cache () {
|
||||
if (!this.options.build.cache) {
|
||||
return false
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'filesystem',
|
||||
cacheDirectory: path.resolve('node_modules/.cache/@nuxt/webpack/'),
|
||||
buildDependencies: {
|
||||
config: [...this.options._nuxtConfigFiles]
|
||||
},
|
||||
...this.options.build.cache,
|
||||
name: this.name
|
||||
}
|
||||
}
|
||||
|
||||
optimization () {
|
||||
const optimization = cloneDeep(this.options.build.optimization)
|
||||
|
||||
if (optimization.minimize && optimization.minimizer === undefined) {
|
||||
optimization.minimizer = this.minimizer()
|
||||
}
|
||||
|
||||
return optimization
|
||||
}
|
||||
|
||||
resolve () {
|
||||
// Prioritize nested node_modules in webpack search path (#2558)
|
||||
const webpackModulesDir = ['node_modules'].concat(this.options.modulesDir)
|
||||
|
||||
return {
|
||||
resolve: {
|
||||
extensions: ['.wasm', '.mjs', '.js', '.ts', '.json', '.vue', '.jsx', '.tsx'],
|
||||
alias: this.alias(),
|
||||
modules: webpackModulesDir
|
||||
},
|
||||
resolveLoader: {
|
||||
modules: webpackModulesDir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
minimizer () {
|
||||
const minimizer = []
|
||||
const { terser, cache } = this.options.build
|
||||
|
||||
// https://github.com/webpack-contrib/terser-webpack-plugin
|
||||
if (terser) {
|
||||
minimizer.push(
|
||||
new TerserWebpackPlugin(Object.assign({
|
||||
cache,
|
||||
extractComments: {
|
||||
condition: 'some',
|
||||
filename: 'LICENSES'
|
||||
},
|
||||
terserOptions: {
|
||||
compress: {
|
||||
ecma: this.isModern ? 6 : undefined
|
||||
},
|
||||
mangle: {
|
||||
reserved: reservedVueTags
|
||||
}
|
||||
}
|
||||
}, terser))
|
||||
)
|
||||
}
|
||||
|
||||
return minimizer
|
||||
}
|
||||
|
||||
alias () {
|
||||
return {
|
||||
...this.options.alias,
|
||||
app: this.options.appDir,
|
||||
'nuxt-build': this.options.buildDir,
|
||||
'vue-meta': require.resolve(`vue-meta${this.isServer ? '' : '/dist/vue-meta.esm.browser.js'}`)
|
||||
}
|
||||
}
|
||||
|
||||
rules () {
|
||||
const perfLoader = new PerfLoader(this.name, this.options)
|
||||
const styleLoader = new StyleLoader(
|
||||
this.builder.nuxt,
|
||||
{ isServer: this.isServer, perfLoader }
|
||||
)
|
||||
|
||||
const babelLoader = {
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: this.getBabelOptions()
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
test: /\.vue$/i,
|
||||
loader: 'vue-loader',
|
||||
options: this.loaders.vue
|
||||
},
|
||||
{
|
||||
test: /\.pug$/i,
|
||||
oneOf: [
|
||||
{
|
||||
resourceQuery: /^\?vue/i,
|
||||
use: [{
|
||||
loader: 'pug-plain-loader',
|
||||
options: this.loaders.pugPlain
|
||||
}]
|
||||
},
|
||||
{
|
||||
use: [
|
||||
'raw-loader',
|
||||
{
|
||||
loader: 'pug-plain-loader',
|
||||
options: this.loaders.pugPlain
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.m?[jt]sx?$/i,
|
||||
exclude: (file) => {
|
||||
file = file.split('node_modules', 2)[1]
|
||||
|
||||
// not exclude files outside node_modules
|
||||
if (!file) {
|
||||
return false
|
||||
}
|
||||
|
||||
// item in transpile can be string or regex object
|
||||
return !this.modulesToTranspile.some(module => module.test(file))
|
||||
},
|
||||
use: perfLoader.js().concat(babelLoader)
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
oneOf: styleLoader.apply('css')
|
||||
},
|
||||
{
|
||||
test: /\.p(ost)?css$/i,
|
||||
oneOf: styleLoader.apply('postcss')
|
||||
},
|
||||
{
|
||||
test: /\.less$/i,
|
||||
oneOf: styleLoader.apply('less', {
|
||||
loader: 'less-loader',
|
||||
options: this.loaders.less
|
||||
})
|
||||
},
|
||||
{
|
||||
test: /\.sass$/i,
|
||||
oneOf: styleLoader.apply('sass', {
|
||||
loader: 'sass-loader',
|
||||
options: this.loaders.sass
|
||||
})
|
||||
},
|
||||
{
|
||||
test: /\.scss$/i,
|
||||
oneOf: styleLoader.apply('scss', {
|
||||
loader: 'sass-loader',
|
||||
options: this.loaders.scss
|
||||
})
|
||||
},
|
||||
{
|
||||
test: /\.styl(us)?$/i,
|
||||
oneOf: styleLoader.apply('stylus', {
|
||||
loader: 'stylus-loader',
|
||||
options: this.loaders.stylus
|
||||
})
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg|webp)$/i,
|
||||
use: [{
|
||||
loader: 'url-loader',
|
||||
options: Object.assign(
|
||||
this.loaders.imgUrl,
|
||||
{ name: this.getFileName('img') }
|
||||
)
|
||||
}]
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
|
||||
use: [{
|
||||
loader: 'url-loader',
|
||||
options: Object.assign(
|
||||
this.loaders.fontUrl,
|
||||
{ name: this.getFileName('font') }
|
||||
)
|
||||
}]
|
||||
},
|
||||
{
|
||||
test: /\.(webm|mp4|ogv)$/i,
|
||||
use: [{
|
||||
loader: 'file-loader',
|
||||
options: Object.assign(
|
||||
this.loaders.file,
|
||||
{ name: this.getFileName('video') }
|
||||
)
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
plugins () {
|
||||
const plugins = []
|
||||
const { nuxt } = this.builder
|
||||
const { build: buildOptions } = this.options
|
||||
|
||||
// Add timefix-plugin before others plugins
|
||||
if (this.dev) {
|
||||
plugins.push(new TimeFixPlugin())
|
||||
}
|
||||
|
||||
// CSS extraction)
|
||||
if (buildOptions.extractCSS) {
|
||||
plugins.push(new MiniCssExtractPlugin(Object.assign({
|
||||
filename: this.getFileName('css'),
|
||||
chunkFilename: this.getFileName('css')
|
||||
}, buildOptions.extractCSS)))
|
||||
}
|
||||
|
||||
plugins.push(new VueLoaderPlugin())
|
||||
|
||||
plugins.push(...(buildOptions.plugins || []))
|
||||
|
||||
plugins.push(new WarningIgnorePlugin(this.warningIgnoreFilter()))
|
||||
|
||||
// Build progress indicator
|
||||
plugins.push(new WebpackBar({
|
||||
name: this.name,
|
||||
color: this.colors[this.name],
|
||||
reporters: [
|
||||
'basic',
|
||||
'fancy',
|
||||
'profile',
|
||||
'stats'
|
||||
],
|
||||
basic: !buildOptions.quiet && env.minimalCLI,
|
||||
fancy: !buildOptions.quiet && !env.minimalCLI,
|
||||
profile: !buildOptions.quiet && buildOptions.profile,
|
||||
stats: !buildOptions.quiet && !this.dev && buildOptions.stats,
|
||||
reporter: {
|
||||
change: (_, { shortPath }) => {
|
||||
if (!this.isServer) {
|
||||
nuxt.callHook('bundler:change', shortPath)
|
||||
}
|
||||
},
|
||||
done: (stats) => {
|
||||
if (stats.hasErrors) {
|
||||
nuxt.callHook('bundler:error')
|
||||
}
|
||||
},
|
||||
allDone: () => {
|
||||
nuxt.callHook('bundler:done')
|
||||
},
|
||||
progress ({ statesArray }) {
|
||||
nuxt.callHook('bundler:progress', statesArray)
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
// CSS extraction
|
||||
if (this.options.build.extractCSS) {
|
||||
plugins.push(new MiniCssExtractPlugin(Object.assign({
|
||||
filename: this.getFileName('css'),
|
||||
chunkFilename: this.getFileName('css'),
|
||||
// TODO: https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/132
|
||||
reloadAll: true
|
||||
}, this.options.build.extractCSS)))
|
||||
}
|
||||
|
||||
return plugins
|
||||
}
|
||||
|
||||
warningIgnoreFilter () {
|
||||
const filters = [
|
||||
// Hide warnings about plugins without a default export (#1179)
|
||||
warn => warn.name === 'ModuleDependencyWarning' &&
|
||||
warn.message.includes('export \'default\'') &&
|
||||
warn.message.includes('nuxt_plugin_'),
|
||||
...(this.options.build.warningIgnoreFilters || [])
|
||||
]
|
||||
|
||||
return warn => !filters.some(ignoreFilter => ignoreFilter(warn))
|
||||
}
|
||||
|
||||
extendConfig (config) {
|
||||
const { extend } = this.options.build
|
||||
if (typeof extend === 'function') {
|
||||
const extendedConfig = extend.call(
|
||||
this.builder, config, { loaders: this.loaders, ...this.nuxtEnv }
|
||||
) || config
|
||||
|
||||
const pragma = /@|#/
|
||||
const { devtool } = extendedConfig
|
||||
if (typeof devtool === 'string' && pragma.test(devtool)) {
|
||||
extendedConfig.devtool = devtool.replace(pragma, '')
|
||||
consola.warn(`devtool has been normalized to ${extendedConfig.devtool} as webpack documented value`)
|
||||
}
|
||||
|
||||
return extendedConfig
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
config () {
|
||||
const config = {
|
||||
name: this.name,
|
||||
mode: this.mode,
|
||||
devtool: this.devtool,
|
||||
cache: this.cache(),
|
||||
optimization: this.optimization(),
|
||||
output: this.output(),
|
||||
performance: {
|
||||
maxEntrypointSize: 1000 * 1024,
|
||||
hints: this.dev ? false : 'warning'
|
||||
},
|
||||
module: {
|
||||
rules: this.rules()
|
||||
},
|
||||
plugins: this.plugins(),
|
||||
...this.resolve()
|
||||
}
|
||||
|
||||
// Clone deep avoid leaking config between Client and Server
|
||||
const extendedConfig = cloneDeep(this.extendConfig(config))
|
||||
|
||||
return extendedConfig
|
||||
}
|
||||
}
|
@ -1,233 +0,0 @@
|
||||
import path from 'path'
|
||||
import querystring from 'querystring'
|
||||
import webpack from 'webpack'
|
||||
import HTMLPlugin from 'html-webpack-plugin'
|
||||
import BundleAnalyzer from 'webpack-bundle-analyzer'
|
||||
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'
|
||||
import FriendlyErrorsWebpackPlugin from '@nuxt/friendly-errors-webpack-plugin'
|
||||
|
||||
import CorsPlugin from '../plugins/vue/cors'
|
||||
import ModernModePlugin from '../plugins/vue/modern'
|
||||
import VueSSRClientPlugin from '../plugins/vue/client'
|
||||
import WebpackBaseConfig from './base'
|
||||
|
||||
export default class WebpackClientConfig extends WebpackBaseConfig {
|
||||
constructor (builder) {
|
||||
super(builder)
|
||||
this.name = 'client'
|
||||
this.isServer = false
|
||||
this.isModern = false
|
||||
}
|
||||
|
||||
get devtool () {
|
||||
if (!this.dev) {
|
||||
return false
|
||||
}
|
||||
const scriptPolicy = this.getCspScriptPolicy()
|
||||
const noUnsafeEval = scriptPolicy && !scriptPolicy.includes('\'unsafe-eval\'')
|
||||
return noUnsafeEval
|
||||
? 'cheap-module-source-map'
|
||||
: 'eval-cheap-module-source-map'
|
||||
}
|
||||
|
||||
getCspScriptPolicy () {
|
||||
const { csp } = this.options.render
|
||||
if (typeof csp === 'object') {
|
||||
const { policies = {} } = csp
|
||||
return policies['script-src'] || policies['default-src'] || []
|
||||
}
|
||||
}
|
||||
|
||||
env () {
|
||||
return Object.assign(
|
||||
super.env(),
|
||||
{
|
||||
'process.env.VUE_ENV': JSON.stringify('client'),
|
||||
'process.browser': true,
|
||||
'process.client': true,
|
||||
'process.server': false,
|
||||
'process.modern': false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
optimization () {
|
||||
const optimization = super.optimization()
|
||||
const { splitChunks } = optimization
|
||||
const { cacheGroups } = splitChunks
|
||||
|
||||
// Small, known and common modules which are usually used project-wise
|
||||
// Sum of them may not be more than 244 KiB
|
||||
if (
|
||||
this.options.build.splitChunks.commons === true &&
|
||||
cacheGroups.commons === undefined
|
||||
) {
|
||||
cacheGroups.commons = {
|
||||
test: /node_modules[\\/](vue|vue-loader|vue-router|vuex|vue-meta|core-js|@babel\/runtime|axios|webpack|setimmediate|timers-browserify|process|regenerator-runtime|cookie|js-cookie|is-buffer|dotprop|nuxt\.js)[\\/]/,
|
||||
chunks: 'all',
|
||||
priority: 10,
|
||||
name: 'commons',
|
||||
automaticNameDelimiter: '/'
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.dev && cacheGroups.default && cacheGroups.default.name === undefined) {
|
||||
cacheGroups.default.name = (_module, chunks) => {
|
||||
// Use default name for single chunks
|
||||
if (chunks.length === 1) {
|
||||
return chunks[0].name || ''
|
||||
}
|
||||
// Use compact name for concatinated modules
|
||||
return 'commons/' + chunks.filter(c => c.name).map(c =>
|
||||
c.name.replace(/\//g, '.').replace(/_/g, '').replace('pages.', '')
|
||||
).join('~')
|
||||
}
|
||||
}
|
||||
|
||||
return optimization
|
||||
}
|
||||
|
||||
minimizer () {
|
||||
const minimizer = super.minimizer()
|
||||
const { cssMinimizer } = this.options.build.optimization
|
||||
|
||||
if (cssMinimizer) {
|
||||
minimizer.push(new CssMinimizerPlugin(Object.assign({}, cssMinimizer)))
|
||||
}
|
||||
|
||||
return minimizer
|
||||
}
|
||||
|
||||
alias () {
|
||||
const aliases = super.alias()
|
||||
|
||||
for (const p of this.builder.plugins) {
|
||||
if (!aliases[p.name]) {
|
||||
// Do not load server-side plugins on client-side
|
||||
aliases[p.name] = p.mode === 'server' ? './empty.js' : p.src
|
||||
}
|
||||
}
|
||||
|
||||
return aliases
|
||||
}
|
||||
|
||||
plugins () {
|
||||
const plugins = super.plugins()
|
||||
const { build: buildOptions, appTemplatePath, buildDir, modern, render } = this.options
|
||||
|
||||
// Generate output HTML for SSR
|
||||
if (buildOptions.ssr) {
|
||||
plugins.push(
|
||||
new HTMLPlugin({
|
||||
filename: '../server/index.ssr.html',
|
||||
template: appTemplatePath,
|
||||
minify: buildOptions.html.minify,
|
||||
inject: false // Resources will be injected using bundleRenderer
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
plugins.push(
|
||||
new HTMLPlugin({
|
||||
filename: '../server/index.spa.html',
|
||||
template: appTemplatePath,
|
||||
minify: buildOptions.html.minify,
|
||||
inject: true
|
||||
}),
|
||||
new VueSSRClientPlugin({
|
||||
filename: `../server/${this.name}.manifest.json`
|
||||
}),
|
||||
new webpack.DefinePlugin(this.env())
|
||||
)
|
||||
|
||||
if (this.dev) {
|
||||
// TODO: webpackHotUpdate is not defined: https://github.com/webpack/webpack/issues/6693
|
||||
plugins.push(new webpack.HotModuleReplacementPlugin())
|
||||
}
|
||||
|
||||
// Webpack Bundle Analyzer
|
||||
// https://github.com/webpack-contrib/webpack-bundle-analyzer
|
||||
if (!this.dev && buildOptions.analyze) {
|
||||
const statsDir = path.resolve(buildDir, 'stats')
|
||||
|
||||
plugins.push(new BundleAnalyzer.BundleAnalyzerPlugin(Object.assign({
|
||||
analyzerMode: 'static',
|
||||
defaultSizes: 'gzip',
|
||||
generateStatsFile: true,
|
||||
openAnalyzer: !buildOptions.quiet,
|
||||
reportFilename: path.resolve(statsDir, `${this.name}.html`),
|
||||
statsFilename: path.resolve(statsDir, `${this.name}.json`)
|
||||
}, buildOptions.analyze)))
|
||||
}
|
||||
|
||||
if (modern) {
|
||||
const scriptPolicy = this.getCspScriptPolicy()
|
||||
const noUnsafeInline = scriptPolicy && !scriptPolicy.includes('\'unsafe-inline\'')
|
||||
plugins.push(new ModernModePlugin({
|
||||
targetDir: path.resolve(buildDir, 'dist', 'client'),
|
||||
isModernBuild: this.isModern,
|
||||
noUnsafeInline
|
||||
}))
|
||||
}
|
||||
|
||||
if (render.crossorigin) {
|
||||
plugins.push(new CorsPlugin({
|
||||
crossorigin: render.crossorigin
|
||||
}))
|
||||
}
|
||||
|
||||
return plugins
|
||||
}
|
||||
|
||||
config () {
|
||||
const config = super.config()
|
||||
const {
|
||||
router,
|
||||
buildDir,
|
||||
build: { hotMiddleware, quiet, friendlyErrors }
|
||||
} = this.options
|
||||
|
||||
const { client = {} } = hotMiddleware || {}
|
||||
const { ansiColors, overlayStyles, ...options } = client
|
||||
|
||||
const hotMiddlewareClientOptions = {
|
||||
reload: true,
|
||||
timeout: 30000,
|
||||
ansiColors: JSON.stringify(ansiColors),
|
||||
overlayStyles: JSON.stringify(overlayStyles),
|
||||
path: `${router.base}/__webpack_hmr/${this.name}`.replace(/\/\//g, '/'),
|
||||
...options,
|
||||
name: this.name
|
||||
}
|
||||
|
||||
const hotMiddlewareClientOptionsStr = querystring.stringify(hotMiddlewareClientOptions)
|
||||
|
||||
// Entry points
|
||||
config.entry = Object.assign({}, config.entry, {
|
||||
app: [path.resolve(buildDir, 'entry.client.ts')]
|
||||
})
|
||||
|
||||
// Add HMR support
|
||||
if (this.dev) {
|
||||
config.entry.app.unshift(
|
||||
// https://github.com/webpack-contrib/webpack-hot-middleware/issues/53#issuecomment-162823945
|
||||
'eventsource-polyfill',
|
||||
// https://github.com/glenjamin/webpack-hot-middleware#config
|
||||
`webpack-hot-middleware/client?${hotMiddlewareClientOptionsStr}`
|
||||
)
|
||||
}
|
||||
|
||||
// Add friendly error plugin
|
||||
if (this.dev && !quiet && friendlyErrors) {
|
||||
config.plugins.push(
|
||||
new FriendlyErrorsWebpackPlugin({
|
||||
clearConsole: false,
|
||||
reporter: 'consola',
|
||||
logLevel: 'WARNING'
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export { default as client } from './client'
|
||||
export { default as modern } from './modern'
|
||||
export { default as server } from './server'
|
@ -1,15 +0,0 @@
|
||||
import WebpackClientConfig from './client'
|
||||
|
||||
export default class WebpackModernConfig extends WebpackClientConfig {
|
||||
constructor (...args) {
|
||||
super(...args)
|
||||
this.name = 'modern'
|
||||
this.isModern = true
|
||||
}
|
||||
|
||||
env () {
|
||||
return Object.assign(super.env(), {
|
||||
'process.modern': true
|
||||
})
|
||||
}
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import { DefinePlugin, ProvidePlugin } from 'webpack'
|
||||
import FriendlyErrorsWebpackPlugin from '@nuxt/friendly-errors-webpack-plugin'
|
||||
|
||||
// TODO: remove when webpack-node-externals support webpack5
|
||||
// import nodeExternals from 'webpack-node-externals'
|
||||
import nodeExternals from '../plugins/externals'
|
||||
import VueSSRServerPlugin from '../plugins/vue/server'
|
||||
|
||||
import WebpackBaseConfig from './base'
|
||||
|
||||
const nativeFileExtensions = [
|
||||
'.json',
|
||||
'.js'
|
||||
]
|
||||
|
||||
export default class WebpackServerConfig extends WebpackBaseConfig {
|
||||
constructor (...args) {
|
||||
super(...args)
|
||||
this.name = 'server'
|
||||
this.isServer = true
|
||||
}
|
||||
|
||||
get devtool () {
|
||||
return 'cheap-module-source-map'
|
||||
}
|
||||
|
||||
get externalsWhitelist () {
|
||||
return [
|
||||
this.isNonNativeImport.bind(this),
|
||||
...this.normalizeTranspile()
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* files *not* ending on js|json should be processed by webpack
|
||||
*
|
||||
* this might generate false-positives for imports like
|
||||
* - "someFile.umd" (actually requiring someFile.umd.js)
|
||||
* - "some.folder" (some.folder being a directory containing a package.json)
|
||||
*/
|
||||
isNonNativeImport (modulePath) {
|
||||
const extname = path.extname(modulePath)
|
||||
return extname !== '' && !nativeFileExtensions.includes(extname)
|
||||
}
|
||||
|
||||
env () {
|
||||
return Object.assign(
|
||||
super.env(),
|
||||
{
|
||||
'process.env.VUE_ENV': JSON.stringify('server'),
|
||||
'process.browser': false,
|
||||
'process.client': false,
|
||||
'process.server': true,
|
||||
'process.modern': false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
optimization () {
|
||||
const { _minifyServer } = this.options.build
|
||||
|
||||
return {
|
||||
splitChunks: false,
|
||||
minimizer: _minifyServer ? this.minimizer() : []
|
||||
}
|
||||
}
|
||||
|
||||
resolve () {
|
||||
const resolveConfig = super.resolve()
|
||||
|
||||
resolveConfig.resolve.mainFields = ['main', 'module']
|
||||
|
||||
return resolveConfig
|
||||
}
|
||||
|
||||
alias () {
|
||||
const aliases = super.alias()
|
||||
|
||||
for (const p of this.builder.plugins) {
|
||||
if (!aliases[p.name]) {
|
||||
// Do not load client-side plugins on server-side
|
||||
aliases[p.name] = p.mode === 'client' ? './empty.js' : p.src
|
||||
}
|
||||
}
|
||||
|
||||
return aliases
|
||||
}
|
||||
|
||||
plugins () {
|
||||
const plugins = super.plugins()
|
||||
plugins.push(
|
||||
new VueSSRServerPlugin({ filename: `${this.name}.manifest.json` }),
|
||||
new DefinePlugin(this.env())
|
||||
)
|
||||
|
||||
const { serverURLPolyfill } = this.options.build
|
||||
|
||||
if (serverURLPolyfill) {
|
||||
plugins.push(new ProvidePlugin({
|
||||
URL: [serverURLPolyfill, 'URL'],
|
||||
URLSearchParams: [serverURLPolyfill, 'URLSearchParams']
|
||||
}))
|
||||
}
|
||||
|
||||
return plugins
|
||||
}
|
||||
|
||||
config () {
|
||||
const config = super.config()
|
||||
|
||||
Object.assign(config, {
|
||||
target: 'node',
|
||||
node: false,
|
||||
entry: Object.assign({}, config.entry, {
|
||||
app: [path.resolve(this.options.buildDir, 'entry.server.ts')]
|
||||
}),
|
||||
output: Object.assign({}, config.output, {
|
||||
filename: 'server.js',
|
||||
chunkFilename: '[name].js',
|
||||
libraryTarget: 'commonjs2'
|
||||
}),
|
||||
performance: {
|
||||
hints: false,
|
||||
maxEntrypointSize: Infinity,
|
||||
maxAssetSize: Infinity
|
||||
},
|
||||
externals: [].concat(config.externals || [])
|
||||
})
|
||||
|
||||
// https://webpack.js.org/configuration/externals/#externals
|
||||
// https://github.com/liady/webpack-node-externals
|
||||
// https://vue-loader.vuejs.org/migrating.html#ssr-externals
|
||||
if (!this.options.build.standalone) {
|
||||
this.options.modulesDir.forEach((dir) => {
|
||||
if (fs.existsSync(dir)) {
|
||||
config.externals.push(
|
||||
nodeExternals({
|
||||
whitelist: this.externalsWhitelist,
|
||||
modulesDir: dir
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Add friendly error plugin
|
||||
if (this.dev) {
|
||||
config.plugins.push(
|
||||
new FriendlyErrorsWebpackPlugin({
|
||||
clearConsole: false,
|
||||
reporter: 'consola',
|
||||
logLevel: 'WARNING'
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
}
|
147
packages/nuxt3/src/webpack/configs/client.ts
Normal file
147
packages/nuxt3/src/webpack/configs/client.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import path from 'path'
|
||||
import querystring from 'querystring'
|
||||
import webpack from 'webpack'
|
||||
import HTMLPlugin from 'html-webpack-plugin'
|
||||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
|
||||
import CorsPlugin from '../plugins/vue/cors'
|
||||
import { applyPresets, WebpackConfigContext } from '../utils/config'
|
||||
import { nuxt } from '../presets/nuxt'
|
||||
|
||||
export function client (ctx: WebpackConfigContext) {
|
||||
ctx.name = 'client'
|
||||
ctx.isClient = true
|
||||
|
||||
applyPresets(ctx, [
|
||||
nuxt,
|
||||
clientPlugins,
|
||||
clientOptimization,
|
||||
clientDevtool,
|
||||
clientPerformance,
|
||||
clientHMR,
|
||||
clientHTML
|
||||
])
|
||||
}
|
||||
|
||||
function clientDevtool (ctx: WebpackConfigContext) {
|
||||
if (!ctx.isDev) {
|
||||
ctx.config.devtool = false
|
||||
return
|
||||
}
|
||||
|
||||
const scriptPolicy = getCspScriptPolicy(ctx)
|
||||
const noUnsafeEval = scriptPolicy && !scriptPolicy.includes('\'unsafe-eval\'')
|
||||
ctx.config.devtool = noUnsafeEval
|
||||
? 'cheap-module-source-map'
|
||||
: 'eval-cheap-module-source-map'
|
||||
}
|
||||
|
||||
function clientPerformance (ctx: WebpackConfigContext) {
|
||||
ctx.config.performance = {
|
||||
maxEntrypointSize: 1000 * 1024,
|
||||
hints: ctx.isDev ? false : 'warning',
|
||||
...ctx.config.performance
|
||||
}
|
||||
}
|
||||
|
||||
function clientHMR (ctx: WebpackConfigContext) {
|
||||
const { options, config } = ctx
|
||||
|
||||
if (!ctx.isDev) {
|
||||
return
|
||||
}
|
||||
|
||||
const clientOptions = options.build.hotMiddleware?.client || {}
|
||||
const hotMiddlewareClientOptions = {
|
||||
reload: true,
|
||||
timeout: 30000,
|
||||
ansiColors: JSON.stringify(clientOptions.ansiColors || {}),
|
||||
overlayStyles: JSON.stringify(clientOptions.overlayStyles || {}),
|
||||
path: `${options.router.base}/__webpack_hmr/${ctx.name}`.replace(/\/\//g, '/'),
|
||||
...clientOptions,
|
||||
name: ctx.name
|
||||
}
|
||||
const hotMiddlewareClientOptionsStr = querystring.stringify(hotMiddlewareClientOptions)
|
||||
|
||||
// Add HMR support
|
||||
const app = (config.entry as any).app as any
|
||||
app.unshift(
|
||||
// https://github.com/webpack-contrib/webpack-hot-middleware/issues/53#issuecomment-162823945
|
||||
'eventsource-polyfill',
|
||||
// https://github.com/glenjamin/webpack-hot-middleware#config
|
||||
`webpack-hot-middleware/client?${hotMiddlewareClientOptionsStr}`
|
||||
)
|
||||
|
||||
// TODO: webpackHotUpdate is not defined: https://github.com/webpack/webpack/issues/6693
|
||||
config.plugins.push(new webpack.HotModuleReplacementPlugin())
|
||||
}
|
||||
|
||||
function clientOptimization (ctx: WebpackConfigContext) {
|
||||
const { options, config } = ctx
|
||||
|
||||
config.optimization = {
|
||||
...config.optimization,
|
||||
...options.build.optimization as any
|
||||
}
|
||||
|
||||
// TODO: Improve optimization.splitChunks.cacheGroups
|
||||
}
|
||||
|
||||
function clientHTML (ctx: WebpackConfigContext) {
|
||||
const { options, config } = ctx
|
||||
|
||||
// Generate output HTML for SSR
|
||||
if (options.build.ssr) {
|
||||
config.plugins.push(
|
||||
new HTMLPlugin({
|
||||
filename: '../server/index.ssr.html',
|
||||
template: options.appTemplatePath,
|
||||
minify: options.build.html.minify as any,
|
||||
inject: false // Resources will be injected using bundleRenderer
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
config.plugins.push(
|
||||
new HTMLPlugin({
|
||||
filename: '../server/index.spa.html',
|
||||
template: options.appTemplatePath,
|
||||
minify: options.build.html.minify as any,
|
||||
inject: true
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function clientPlugins (ctx: WebpackConfigContext) {
|
||||
const { options, config } = ctx
|
||||
|
||||
// Webpack Bundle Analyzer
|
||||
// https://github.com/webpack-contrib/webpack-bundle-analyzer
|
||||
if (!ctx.isDev && options.build.analyze) {
|
||||
const statsDir = path.resolve(options.buildDir, 'stats')
|
||||
|
||||
config.plugins.push(new BundleAnalyzerPlugin({
|
||||
analyzerMode: 'static',
|
||||
defaultSizes: 'gzip',
|
||||
generateStatsFile: true,
|
||||
openAnalyzer: !options.build.quiet,
|
||||
reportFilename: path.resolve(statsDir, `${ctx.name}.html`),
|
||||
statsFilename: path.resolve(statsDir, `${ctx.name}.json`),
|
||||
...options.build.analyze as any
|
||||
}))
|
||||
}
|
||||
|
||||
// CORS
|
||||
if (ctx.options.render.crossorigin) {
|
||||
ctx.config.plugins.push(new CorsPlugin({
|
||||
crossorigin: ctx.options.render.crossorigin
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
function getCspScriptPolicy (ctx: WebpackConfigContext) {
|
||||
const { csp } = ctx.options.render
|
||||
if (typeof csp === 'object') {
|
||||
const { policies = {} } = csp
|
||||
return policies['script-src'] || policies['default-src'] || []
|
||||
}
|
||||
}
|
2
packages/nuxt3/src/webpack/configs/index.ts
Normal file
2
packages/nuxt3/src/webpack/configs/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { client } from './client'
|
||||
export { server } from './server'
|
69
packages/nuxt3/src/webpack/configs/server.ts
Normal file
69
packages/nuxt3/src/webpack/configs/server.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import { ProvidePlugin } from 'webpack'
|
||||
import nodeExternals from '../plugins/externals'
|
||||
import { WebpackConfigContext, applyPresets, getWebpackConfig } from '../utils/config'
|
||||
import { nuxt } from '../presets/nuxt'
|
||||
import { node } from '../presets/node'
|
||||
|
||||
export function server (ctx: WebpackConfigContext) {
|
||||
ctx.name = 'server'
|
||||
ctx.isServer = true
|
||||
|
||||
applyPresets(ctx, [
|
||||
nuxt,
|
||||
node,
|
||||
serverStandalone,
|
||||
serverPreset,
|
||||
serverPlugins
|
||||
])
|
||||
|
||||
return getWebpackConfig(ctx)
|
||||
}
|
||||
|
||||
function serverPreset (ctx: WebpackConfigContext) {
|
||||
const { config } = ctx
|
||||
|
||||
config.output.filename = 'server.js'
|
||||
config.devtool = 'cheap-module-source-map'
|
||||
|
||||
config.optimization = {
|
||||
splitChunks: false,
|
||||
minimize: false
|
||||
}
|
||||
}
|
||||
|
||||
function serverStandalone (ctx: WebpackConfigContext) {
|
||||
const { options, config } = ctx
|
||||
|
||||
// https://webpack.js.org/configuration/externals/#externals
|
||||
// https://github.com/liady/webpack-node-externals
|
||||
// https://vue-loader.vuejs.org/migrating.html#ssr-externals
|
||||
if (!options.build.standalone) {
|
||||
options.modulesDir.forEach((dir) => {
|
||||
if (fs.existsSync(dir)) {
|
||||
(config.externals as any[]).push(
|
||||
nodeExternals({
|
||||
whitelist: [
|
||||
modulePath => !['.js', '.json', ''].includes(path.extname(modulePath)),
|
||||
ctx.transpile
|
||||
],
|
||||
modulesDir: dir
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function serverPlugins (ctx: WebpackConfigContext) {
|
||||
const { config, options } = ctx
|
||||
|
||||
// Server polyfills
|
||||
if (options.build.serverURLPolyfill) {
|
||||
config.plugins.push(new ProvidePlugin({
|
||||
URL: [options.build.serverURLPolyfill, 'URL'],
|
||||
URLSearchParams: [options.build.serverURLPolyfill, 'URLSearchParams']
|
||||
}))
|
||||
}
|
||||
}
|
38
packages/nuxt3/src/webpack/presets/assets.ts
Normal file
38
packages/nuxt3/src/webpack/presets/assets.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { fileName, WebpackConfigContext } from '../utils/config'
|
||||
|
||||
export function assets (ctx: WebpackConfigContext) {
|
||||
const { options } = ctx
|
||||
|
||||
ctx.config.module.rules.push(
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg|webp)$/i,
|
||||
use: [{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
...options.build.loaders.imgUrl,
|
||||
name: fileName(ctx, 'img')
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
|
||||
use: [{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
...options.build.loaders.fontUrl,
|
||||
name: fileName(ctx, 'font')
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
test: /\.(webm|mp4|ogv)$/i,
|
||||
use: [{
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
...options.build.loaders.file,
|
||||
name: fileName(ctx, 'video')
|
||||
}
|
||||
}]
|
||||
}
|
||||
)
|
||||
}
|
78
packages/nuxt3/src/webpack/presets/babel.ts
Normal file
78
packages/nuxt3/src/webpack/presets/babel.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import TerserWebpackPlugin from 'terser-webpack-plugin'
|
||||
import { WebpackPluginInstance } from 'webpack'
|
||||
import { reservedVueTags } from '../utils/reserved-tags'
|
||||
import { WebpackConfigContext } from '../utils/config'
|
||||
|
||||
export function babel (ctx: WebpackConfigContext) {
|
||||
const { config, options } = ctx
|
||||
|
||||
const babelLoader = {
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: getBabelOptions(ctx)
|
||||
}
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.m?[jt]sx?$/i,
|
||||
exclude: (file) => {
|
||||
file = file.split('node_modules', 2)[1]
|
||||
// not exclude files outside node_modules
|
||||
if (!file) {
|
||||
return false
|
||||
}
|
||||
// item in transpile can be string or regex object
|
||||
return !ctx.transpile.some(module => module.test(file))
|
||||
},
|
||||
use: babelLoader
|
||||
})
|
||||
|
||||
// https://github.com/webpack-contrib/terser-webpack-plugin
|
||||
if (options.build.terser) {
|
||||
const terser = new TerserWebpackPlugin({
|
||||
// cache, TODO
|
||||
extractComments: {
|
||||
condition: 'some',
|
||||
filename: 'LICENSES'
|
||||
},
|
||||
terserOptions: {
|
||||
compress: {
|
||||
ecma: ctx.isModern ? 6 : undefined
|
||||
},
|
||||
mangle: {
|
||||
reserved: reservedVueTags
|
||||
}
|
||||
},
|
||||
...options.build.terser as any
|
||||
})
|
||||
|
||||
config.plugins.push(terser as WebpackPluginInstance)
|
||||
}
|
||||
}
|
||||
|
||||
function getBabelOptions (ctx: WebpackConfigContext) {
|
||||
const { options } = ctx
|
||||
|
||||
const babelOptions: any = {
|
||||
...options.build.babel,
|
||||
envName: ctx.name
|
||||
}
|
||||
|
||||
if (babelOptions.configFile || babelOptions.babelrc) {
|
||||
return babelOptions
|
||||
}
|
||||
|
||||
if (typeof babelOptions.plugins === 'function') {
|
||||
babelOptions.plugins = babelOptions.plugins(ctx)
|
||||
}
|
||||
|
||||
const defaultPreset = [require.resolve('../../babel-preset-app'), {}]
|
||||
|
||||
if (typeof babelOptions.presets === 'function') {
|
||||
babelOptions.presets = babelOptions.presets(ctx, defaultPreset)
|
||||
}
|
||||
|
||||
if (!babelOptions.presets) {
|
||||
babelOptions.presets = [defaultPreset]
|
||||
}
|
||||
|
||||
return babelOptions
|
||||
}
|
240
packages/nuxt3/src/webpack/presets/base.ts
Normal file
240
packages/nuxt3/src/webpack/presets/base.ts
Normal file
@ -0,0 +1,240 @@
|
||||
import { resolve, normalize } from 'path'
|
||||
import TimeFixPlugin from 'time-fix-plugin'
|
||||
import WebpackBar from 'webpackbar'
|
||||
import stdEnv from 'std-env'
|
||||
import { DefinePlugin, Configuration } from 'webpack'
|
||||
import FriendlyErrorsWebpackPlugin from '@nuxt/friendly-errors-webpack-plugin'
|
||||
import { isUrl, urlJoin, TARGETS } from 'src/utils'
|
||||
import escapeRegExp from 'lodash/escapeRegExp'
|
||||
import WarningIgnorePlugin from '../plugins/warning-ignore'
|
||||
import { WebpackConfigContext, applyPresets, fileName } from '../utils/config'
|
||||
|
||||
export function base (ctx: WebpackConfigContext) {
|
||||
applyPresets(ctx, [
|
||||
baseAlias,
|
||||
baseConfig,
|
||||
basePlugins,
|
||||
baseResolve
|
||||
])
|
||||
}
|
||||
|
||||
function baseConfig (ctx: WebpackConfigContext) {
|
||||
const { options } = ctx
|
||||
|
||||
ctx.config = {
|
||||
name: ctx.name,
|
||||
entry: { app: [resolve(options.buildDir, `entry.${ctx.name}.ts`)] },
|
||||
module: { rules: [] },
|
||||
plugins: [],
|
||||
externals: [],
|
||||
optimization: {
|
||||
...options.build.optimization as any,
|
||||
minimizer: []
|
||||
},
|
||||
mode: ctx.isDev ? 'development' : 'production',
|
||||
cache: getCache(ctx),
|
||||
output: getOutput(ctx),
|
||||
...ctx.config
|
||||
}
|
||||
}
|
||||
|
||||
function basePlugins (ctx: WebpackConfigContext) {
|
||||
const { config, options, nuxt } = ctx
|
||||
|
||||
// Add timefix-plugin before others plugins
|
||||
if (options.dev) {
|
||||
config.plugins.push(new TimeFixPlugin())
|
||||
}
|
||||
|
||||
// User plugins
|
||||
config.plugins.push(...(options.build.plugins || []))
|
||||
|
||||
// Ignore empty warnings
|
||||
config.plugins.push(new WarningIgnorePlugin(getWarningIgnoreFilter(ctx)))
|
||||
|
||||
// Provide env via DefinePlugin
|
||||
config.plugins.push(new DefinePlugin(getEnv(ctx)))
|
||||
|
||||
// Friendly errors
|
||||
if (
|
||||
ctx.isServer ||
|
||||
(ctx.isDev && !options.build.quiet && options.build.friendlyErrors)
|
||||
) {
|
||||
ctx.config.plugins.push(
|
||||
new FriendlyErrorsWebpackPlugin({
|
||||
clearConsole: false,
|
||||
reporter: 'consola',
|
||||
logLevel: 'WARNING'
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// Webpackbar
|
||||
const colors = {
|
||||
client: 'green',
|
||||
server: 'orange',
|
||||
modern: 'blue'
|
||||
}
|
||||
config.plugins.push(new WebpackBar({
|
||||
name: ctx.name,
|
||||
color: colors[ctx.name],
|
||||
reporters: [
|
||||
'basic',
|
||||
'fancy',
|
||||
'profile',
|
||||
'stats'
|
||||
],
|
||||
basic: !options.build.quiet && stdEnv.minimalCLI,
|
||||
fancy: !options.build.quiet && !stdEnv.minimalCLI,
|
||||
profile: !options.build.quiet && options.build.profile,
|
||||
stats: !options.build.quiet && !options.dev && options.build.stats,
|
||||
reporter: {
|
||||
change: (_, { shortPath }) => {
|
||||
if (!ctx.isServer) {
|
||||
nuxt.callHook('bundler:change', shortPath)
|
||||
}
|
||||
},
|
||||
done: (stats) => {
|
||||
if (stats.hasErrors) {
|
||||
nuxt.callHook('bundler:error')
|
||||
}
|
||||
},
|
||||
allDone: () => {
|
||||
nuxt.callHook('bundler:done')
|
||||
},
|
||||
progress ({ statesArray }) {
|
||||
nuxt.callHook('bundler:progress', statesArray)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
function baseAlias (ctx: WebpackConfigContext) {
|
||||
const { options, isServer } = ctx
|
||||
|
||||
ctx.alias = {
|
||||
app: options.appDir,
|
||||
'nuxt-build': options.buildDir,
|
||||
'vue-meta': require.resolve(`vue-meta${isServer ? '' : '/dist/vue-meta.esm.browser.js'}`),
|
||||
...options.alias,
|
||||
...ctx.alias
|
||||
}
|
||||
}
|
||||
|
||||
function baseResolve (ctx: WebpackConfigContext) {
|
||||
const { options, config } = ctx
|
||||
|
||||
// Prioritize nested node_modules in webpack search path (#2558)
|
||||
// TODO: this might be refactored as default modulesDir?
|
||||
const webpackModulesDir = ['node_modules'].concat(options.modulesDir)
|
||||
|
||||
config.resolve = {
|
||||
extensions: ['.wasm', '.mjs', '.js', '.ts', '.json', '.vue', '.jsx', '.tsx'],
|
||||
alias: ctx.alias,
|
||||
modules: webpackModulesDir,
|
||||
...config.resolve
|
||||
}
|
||||
|
||||
config.resolveLoader = {
|
||||
modules: webpackModulesDir,
|
||||
...config.resolveLoader
|
||||
}
|
||||
}
|
||||
|
||||
export function baseTranspile (ctx: WebpackConfigContext) {
|
||||
const { options } = ctx
|
||||
|
||||
const transpile = [
|
||||
/\.vue\.js/i, // include SFCs in node_modules
|
||||
/consola\/src/
|
||||
]
|
||||
|
||||
for (let pattern of options.build.transpile) {
|
||||
if (typeof pattern === 'function') {
|
||||
pattern = pattern(ctx)
|
||||
}
|
||||
if (typeof pattern === 'string') {
|
||||
const posixModule = pattern.replace(/\\/g, '/')
|
||||
// TODO: should only do for clientside? (hint: pathNormalize)
|
||||
transpile.push(new RegExp(escapeRegExp(normalize(posixModule))))
|
||||
} else if (pattern instanceof RegExp) {
|
||||
transpile.push(pattern)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: unique
|
||||
ctx.transpile = [...transpile, ...ctx.transpile]
|
||||
}
|
||||
|
||||
function getCache (ctx: WebpackConfigContext): Configuration['cache'] {
|
||||
const { options } = ctx
|
||||
|
||||
if (!options.build.cache) {
|
||||
return false
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'filesystem',
|
||||
cacheDirectory: resolve('node_modules/.cache/@nuxt/webpack/'),
|
||||
buildDependencies: {
|
||||
config: [...options._nuxtConfigFiles]
|
||||
},
|
||||
...(options.build.cache as any),
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
function getOutput (ctx: WebpackConfigContext): Configuration['output'] {
|
||||
const { options } = ctx
|
||||
|
||||
return {
|
||||
path: resolve(options.buildDir, 'dist', ctx.isServer ? 'server' : 'client'),
|
||||
filename: fileName(ctx, 'app'),
|
||||
chunkFilename: fileName(ctx, 'chunk'),
|
||||
publicPath: isUrl(options.build.publicPath) ? options.build.publicPath
|
||||
: urlJoin(options.router.base, options.build.publicPath)
|
||||
}
|
||||
}
|
||||
|
||||
function getWarningIgnoreFilter (ctx: WebpackConfigContext) {
|
||||
const { options } = ctx
|
||||
|
||||
const filters = [
|
||||
// Hide warnings about plugins without a default export (#1179)
|
||||
warn => warn.name === 'ModuleDependencyWarning' &&
|
||||
warn.message.includes('export \'default\'') &&
|
||||
warn.message.includes('nuxt_plugin_'),
|
||||
...(options.build.warningIgnoreFilters || [])
|
||||
]
|
||||
|
||||
return warn => !filters.some(ignoreFilter => ignoreFilter(warn))
|
||||
}
|
||||
|
||||
function getEnv (ctx: WebpackConfigContext) {
|
||||
const { options } = ctx
|
||||
|
||||
const _env = {
|
||||
'process.env.NODE_ENV': JSON.stringify(ctx.config.mode),
|
||||
'process.mode': JSON.stringify(ctx.config.mode),
|
||||
'process.dev': options.dev,
|
||||
'process.static': options.target === TARGETS.static,
|
||||
'process.target': JSON.stringify(options.target),
|
||||
'process.env.VUE_ENV': JSON.stringify(ctx.name),
|
||||
'process.browser': ctx.isClient,
|
||||
'process.client': ctx.isClient,
|
||||
'process.server': ctx.isServer,
|
||||
'process.modern': ctx.isModern
|
||||
}
|
||||
|
||||
if (options.build.aggressiveCodeRemoval) {
|
||||
_env['typeof process'] = JSON.stringify(ctx.isServer ? 'object' : 'undefined')
|
||||
_env['typeof window'] = _env['typeof document'] = JSON.stringify(!ctx.isServer ? 'object' : 'undefined')
|
||||
}
|
||||
|
||||
Object.entries(options.env).forEach(([key, value]) => {
|
||||
const isNative = ['boolean', 'number'].includes(typeof value)
|
||||
_env['process.env.' + key] = isNative ? value : JSON.stringify(value)
|
||||
})
|
||||
|
||||
return _env
|
||||
}
|
40
packages/nuxt3/src/webpack/presets/esbuild.ts
Normal file
40
packages/nuxt3/src/webpack/presets/esbuild.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { ESBuildPlugin, ESBuildMinifyPlugin } from 'esbuild-loader'
|
||||
import { WebpackConfigContext } from '../utils/config'
|
||||
|
||||
export function esbuild (ctx: WebpackConfigContext) {
|
||||
const { config } = ctx
|
||||
|
||||
config.optimization.minimizer.push(new ESBuildMinifyPlugin())
|
||||
|
||||
config.plugins.push(new ESBuildPlugin())
|
||||
|
||||
config.module.rules.push(
|
||||
{
|
||||
test: /\.[jt]sx?$/,
|
||||
loader: 'esbuild-loader',
|
||||
exclude: (file) => {
|
||||
file = file.split('node_modules', 2)[1]
|
||||
|
||||
// Not exclude files outside node_modules
|
||||
if (!file) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Item in transpile can be string or regex object
|
||||
return !ctx.transpile.some(module => module.test(file))
|
||||
},
|
||||
options: {
|
||||
loader: 'ts',
|
||||
target: 'es2015'
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.tsx$/,
|
||||
loader: 'esbuild-loader',
|
||||
options: {
|
||||
loader: 'tsx',
|
||||
target: 'es2015'
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
23
packages/nuxt3/src/webpack/presets/node.ts
Normal file
23
packages/nuxt3/src/webpack/presets/node.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { WebpackConfigContext } from '../utils/config'
|
||||
|
||||
export function node (ctx: WebpackConfigContext) {
|
||||
const { config } = ctx
|
||||
|
||||
config.target = 'node'
|
||||
config.node = false
|
||||
|
||||
config.resolve.mainFields = ['main', 'module']
|
||||
|
||||
config.output = {
|
||||
...config.output,
|
||||
chunkFilename: '[name].js',
|
||||
libraryTarget: 'commonjs2'
|
||||
}
|
||||
|
||||
config.performance = {
|
||||
...config.performance,
|
||||
hints: false,
|
||||
maxEntrypointSize: Infinity,
|
||||
maxAssetSize: Infinity
|
||||
}
|
||||
}
|
20
packages/nuxt3/src/webpack/presets/nuxt.ts
Normal file
20
packages/nuxt3/src/webpack/presets/nuxt.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { WebpackConfigContext, applyPresets } from '../utils/config'
|
||||
|
||||
import { assets } from './assets'
|
||||
import { base } from './base'
|
||||
import { esbuild } from './esbuild'
|
||||
import { pug } from './pug'
|
||||
import { style } from './style'
|
||||
import { vue } from './vue'
|
||||
|
||||
export function nuxt (ctx: WebpackConfigContext) {
|
||||
applyPresets(ctx, [
|
||||
base,
|
||||
assets,
|
||||
// babel,
|
||||
esbuild,
|
||||
pug,
|
||||
style,
|
||||
vue
|
||||
])
|
||||
}
|
25
packages/nuxt3/src/webpack/presets/pug.ts
Normal file
25
packages/nuxt3/src/webpack/presets/pug.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { WebpackConfigContext } from '../utils/config'
|
||||
|
||||
export function pug (ctx: WebpackConfigContext) {
|
||||
ctx.config.module.rules.push({
|
||||
test: /\.pug$/i,
|
||||
oneOf: [
|
||||
{
|
||||
resourceQuery: /^\?vue/i,
|
||||
use: [{
|
||||
loader: 'pug-plain-loader',
|
||||
options: ctx.options.build.loaders.pugPlain
|
||||
}]
|
||||
},
|
||||
{
|
||||
use: [
|
||||
'raw-loader',
|
||||
{
|
||||
loader: 'pug-plain-loader',
|
||||
options: ctx.options.build.loaders.pugPlain
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
62
packages/nuxt3/src/webpack/presets/style.ts
Normal file
62
packages/nuxt3/src/webpack/presets/style.ts
Normal file
@ -0,0 +1,62 @@
|
||||
// import MiniCssExtractPlugin from 'mini-css-extract-plugin'
|
||||
// import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin'
|
||||
// import StyleLoader from '../utils/style-loader'
|
||||
|
||||
import { WebpackConfigContext } from '../utils/config'
|
||||
|
||||
export function style (_ctx: WebpackConfigContext) {
|
||||
// // CSS extraction)
|
||||
// if (options.build.extractCSS) {
|
||||
// plugins.push(new MiniCssExtractPlugin(Object.assign({
|
||||
// filename: fileName(ctx, 'css'),
|
||||
// chunkFilename: fileName(ctx, 'css')
|
||||
// }, options.build.extractCSS)))
|
||||
// }
|
||||
// CSS extraction
|
||||
// if (options.build.extractCSS) {
|
||||
// plugins.push(new MiniCssExtractPlugin(Object.assign({
|
||||
// filename: fileName(ctx, 'css'),
|
||||
// chunkFilename: fileName(ctx, 'css'),
|
||||
// // TODO: https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/132
|
||||
// reloadAll: true
|
||||
// }, options.build.extractCSS)))
|
||||
// }
|
||||
return [
|
||||
// {
|
||||
// test: /\.css$/i,
|
||||
// oneOf: styleLoader.apply('css')
|
||||
// },
|
||||
// {
|
||||
// test: /\.p(ost)?css$/i,
|
||||
// oneOf: styleLoader.apply('postcss')
|
||||
// },
|
||||
// {
|
||||
// test: /\.less$/i,
|
||||
// oneOf: styleLoader.apply('less', {
|
||||
// loader: 'less-loader',
|
||||
// options: loaders.less
|
||||
// })
|
||||
// },
|
||||
// {
|
||||
// test: /\.sass$/i,
|
||||
// oneOf: styleLoader.apply('sass', {
|
||||
// loader: 'sass-loader',
|
||||
// options: loaders.sass
|
||||
// })
|
||||
// },
|
||||
// {
|
||||
// test: /\.scss$/i,
|
||||
// oneOf: styleLoader.apply('scss', {
|
||||
// loader: 'sass-loader',
|
||||
// options: loaders.scss
|
||||
// })
|
||||
// },
|
||||
// {
|
||||
// test: /\.styl(us)?$/i,
|
||||
// oneOf: styleLoader.apply('stylus', {
|
||||
// loader: 'stylus-loader',
|
||||
// options: loaders.stylus
|
||||
// })
|
||||
// }
|
||||
]
|
||||
}
|
26
packages/nuxt3/src/webpack/presets/vue.ts
Normal file
26
packages/nuxt3/src/webpack/presets/vue.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import VueLoaderPlugin from 'vue-loader/dist/pluginWebpack5'
|
||||
import VueSSRClientPlugin from '../plugins/vue/client'
|
||||
import VueSSRServerPlugin from '../plugins/vue/server'
|
||||
import { WebpackConfigContext } from '../utils/config'
|
||||
|
||||
export function vue (ctx: WebpackConfigContext) {
|
||||
const { options, config } = ctx
|
||||
|
||||
config.plugins.push(new VueLoaderPlugin())
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.vue$/i,
|
||||
loader: 'vue-loader',
|
||||
options: options.build.loaders.vue
|
||||
})
|
||||
|
||||
if (ctx.isClient) {
|
||||
config.plugins.push(new VueSSRClientPlugin({
|
||||
filename: `../server/${ctx.name}.manifest.json`
|
||||
}))
|
||||
} else {
|
||||
config.plugins.push(new VueSSRServerPlugin({
|
||||
filename: `${ctx.name}.manifest.json`
|
||||
}))
|
||||
}
|
||||
}
|
88
packages/nuxt3/src/webpack/utils/config.ts
Normal file
88
packages/nuxt3/src/webpack/utils/config.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import consola from 'consola'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
import { Configuration } from 'webpack'
|
||||
import { Nuxt } from 'src/core'
|
||||
|
||||
export interface WebpackConfigContext extends ReturnType<typeof createWebpackConfigContext>{ }
|
||||
|
||||
type WebpackConfigPreset = (ctx: WebpackConfigContext, options?: object) => void
|
||||
type WebpackConfigPresetItem = WebpackConfigPreset | [WebpackConfigPreset, any]
|
||||
|
||||
export function createWebpackConfigContext ({ nuxt }) {
|
||||
return {
|
||||
nuxt: nuxt as Nuxt,
|
||||
options: nuxt.options as Nuxt['options'],
|
||||
config: {} as Configuration,
|
||||
|
||||
name: 'base',
|
||||
isDev: nuxt.options.dev,
|
||||
isServer: false,
|
||||
isClient: false,
|
||||
isModern: undefined, // TODO
|
||||
isLegacy: false,
|
||||
|
||||
alias: {} as Configuration['resolve']['alias'],
|
||||
transpile: [] as any[]
|
||||
}
|
||||
}
|
||||
|
||||
export function applyPresets (ctx: WebpackConfigContext, presets: WebpackConfigPresetItem | WebpackConfigPresetItem[]) {
|
||||
if (!Array.isArray(presets)) {
|
||||
presets = [presets]
|
||||
}
|
||||
for (const preset of presets) {
|
||||
if (Array.isArray(preset)) {
|
||||
preset[0](ctx, preset[1])
|
||||
} else {
|
||||
preset(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function fileName (ctx: WebpackConfigContext, key: string) {
|
||||
const { options } = ctx
|
||||
|
||||
let fileName = options.build.filenames[key]
|
||||
|
||||
if (typeof fileName === 'function') {
|
||||
fileName = fileName(ctx)
|
||||
}
|
||||
|
||||
if (typeof fileName === 'string' && options.dev) {
|
||||
const hash = /\[(chunkhash|contenthash|hash)(?::(\d+))?]/.exec(fileName)
|
||||
if (hash) {
|
||||
consola.warn(`Notice: Please do not use ${hash[1]} in dev mode to prevent memory leak`)
|
||||
}
|
||||
}
|
||||
|
||||
return fileName
|
||||
}
|
||||
|
||||
export function getWebpackConfig (ctx: WebpackConfigContext): Configuration {
|
||||
const { options, config } = ctx
|
||||
|
||||
// TODO
|
||||
const builder = {}
|
||||
const loaders = []
|
||||
|
||||
const { extend } = options.build
|
||||
if (typeof extend === 'function') {
|
||||
const extendedConfig = extend.call(
|
||||
builder,
|
||||
config,
|
||||
{ loaders, ...ctx }
|
||||
) || config
|
||||
|
||||
const pragma = /@|#/
|
||||
const { devtool } = extendedConfig
|
||||
if (typeof devtool === 'string' && pragma.test(devtool)) {
|
||||
extendedConfig.devtool = devtool.replace(pragma, '')
|
||||
consola.warn(`devtool has been normalized to ${extendedConfig.devtool} as webpack documented value`)
|
||||
}
|
||||
|
||||
return extendedConfig
|
||||
}
|
||||
|
||||
// Clone deep avoid leaking config between Client and Server
|
||||
return cloneDeep(config)
|
||||
}
|
@ -1,3 +1,2 @@
|
||||
export { default as PerfLoader } from './perf-loader'
|
||||
export { default as StyleLoader } from './style-loader'
|
||||
export { reservedVueTags } from './reserved-tags'
|
||||
|
@ -1,53 +0,0 @@
|
||||
import { warmup } from 'thread-loader'
|
||||
|
||||
// https://github.com/webpack-contrib/thread-loader
|
||||
|
||||
export default class PerfLoader {
|
||||
constructor (name, options) {
|
||||
this.name = name
|
||||
this.options = options
|
||||
this.workerPools = PerfLoader.defaultPools({ dev: options.dev })
|
||||
return new Proxy(this, {
|
||||
get (target, name) {
|
||||
return target[name] ? target[name] : target.use.bind(target, name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static defaultPools ({ dev }) {
|
||||
const poolTimeout = dev ? Infinity : 2000
|
||||
return {
|
||||
js: { name: 'js', poolTimeout },
|
||||
css: { name: 'css', poolTimeout }
|
||||
}
|
||||
}
|
||||
|
||||
static warmupAll ({ dev }) {
|
||||
const pools = PerfLoader.defaultPools({ dev })
|
||||
PerfLoader.warmup(pools.js, [
|
||||
require.resolve('babel-loader'),
|
||||
require.resolve('@babel/preset-env')
|
||||
])
|
||||
PerfLoader.warmup(pools.css, ['css-loader'])
|
||||
}
|
||||
|
||||
static warmup (...args) {
|
||||
warmup(...args)
|
||||
}
|
||||
|
||||
use (poolName) {
|
||||
const loaders = []
|
||||
|
||||
if (this.options.build.buildOptions) {
|
||||
const pool = this.workerPools[poolName]
|
||||
if (pool) {
|
||||
loaders.push({
|
||||
loader: 'thread-loader',
|
||||
options: pool
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return loaders
|
||||
}
|
||||
}
|
@ -11,10 +11,9 @@ import PostcssConfig from './postcss'
|
||||
export default class StyleLoader {
|
||||
options: NormalizedConfiguration
|
||||
|
||||
constructor (nuxt: Nuxt, { isServer, perfLoader }) {
|
||||
constructor (nuxt: Nuxt, { isServer }) {
|
||||
this.options = nuxt.options
|
||||
this.isServer = isServer
|
||||
this.perfLoader = perfLoader
|
||||
|
||||
if (this.options.build.postcss) {
|
||||
this.postcssConfig = new PostcssConfig(nuxt)
|
||||
@ -121,17 +120,17 @@ export default class StyleLoader {
|
||||
// This matches <style module>
|
||||
{
|
||||
resourceQuery: /module/,
|
||||
use: this.perfLoader.css().concat(
|
||||
use: [
|
||||
this.cssModules(cssModules),
|
||||
customLoaders
|
||||
)
|
||||
]
|
||||
},
|
||||
// This matches plain <style> or <style scoped>
|
||||
{
|
||||
use: this.perfLoader.css().concat(
|
||||
use: [
|
||||
this.css(css),
|
||||
customLoaders
|
||||
)
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user