/*!
|
* @nuxt/webpack v2.15.8 (c) 2016-2021
|
* Released under the MIT License
|
* Repository: https://github.com/nuxt/nuxt.js
|
* Website: https://nuxtjs.org
|
*/
|
'use strict';
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
const path = require('path');
|
const pify = require('pify');
|
const webpack = require('webpack');
|
const Glob = require('glob');
|
const webpackDevMiddleware = require('webpack-dev-middleware');
|
const webpackHotMiddleware = require('webpack-hot-middleware');
|
const consola = require('consola');
|
const utils = require('@nuxt/utils');
|
const MFS = require('memory-fs');
|
const querystring = require('querystring');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const BundleAnalyzer = require('webpack-bundle-analyzer');
|
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
const FriendlyErrorsWebpackPlugin = require('@nuxt/friendly-errors-webpack-plugin');
|
const EventEmitter = require('events');
|
const hash = require('hash-sum');
|
const lodash = require('lodash');
|
const TimeFixPlugin = require('time-fix-plugin');
|
const VueLoader = require('vue-loader');
|
const ExtractCssChunksPlugin = require('extract-css-chunks-webpack-plugin');
|
const PnpWebpackPlugin = require('pnp-webpack-plugin');
|
const HardSourcePlugin = require('hard-source-webpack-plugin');
|
const TerserWebpackPlugin = require('terser-webpack-plugin');
|
const WebpackBar = require('webpackbar');
|
const env = require('std-env');
|
const semver = require('semver');
|
const ufo = require('ufo');
|
const threadLoader = require('thread-loader');
|
const fs = require('fs');
|
const createResolver = require('postcss-import-resolver');
|
const nodeExternals = require('webpack-node-externals');
|
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
function _interopNamespace(e) {
|
if (e && e.__esModule) return e;
|
var n = Object.create(null);
|
if (e) {
|
Object.keys(e).forEach(function (k) {
|
if (k !== 'default') {
|
var d = Object.getOwnPropertyDescriptor(e, k);
|
Object.defineProperty(n, k, d.get ? d : {
|
enumerable: true,
|
get: function () {
|
return e[k];
|
}
|
});
|
}
|
});
|
}
|
n['default'] = e;
|
return Object.freeze(n);
|
}
|
|
const path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
const pify__default = /*#__PURE__*/_interopDefaultLegacy(pify);
|
const webpack__default = /*#__PURE__*/_interopDefaultLegacy(webpack);
|
const Glob__default = /*#__PURE__*/_interopDefaultLegacy(Glob);
|
const webpackDevMiddleware__default = /*#__PURE__*/_interopDefaultLegacy(webpackDevMiddleware);
|
const webpackHotMiddleware__default = /*#__PURE__*/_interopDefaultLegacy(webpackHotMiddleware);
|
const consola__default = /*#__PURE__*/_interopDefaultLegacy(consola);
|
const MFS__default = /*#__PURE__*/_interopDefaultLegacy(MFS);
|
const querystring__default = /*#__PURE__*/_interopDefaultLegacy(querystring);
|
const HtmlWebpackPlugin__default = /*#__PURE__*/_interopDefaultLegacy(HtmlWebpackPlugin);
|
const BundleAnalyzer__default = /*#__PURE__*/_interopDefaultLegacy(BundleAnalyzer);
|
const OptimizeCSSAssetsPlugin__default = /*#__PURE__*/_interopDefaultLegacy(OptimizeCSSAssetsPlugin);
|
const FriendlyErrorsWebpackPlugin__default = /*#__PURE__*/_interopDefaultLegacy(FriendlyErrorsWebpackPlugin);
|
const EventEmitter__default = /*#__PURE__*/_interopDefaultLegacy(EventEmitter);
|
const hash__default = /*#__PURE__*/_interopDefaultLegacy(hash);
|
const TimeFixPlugin__default = /*#__PURE__*/_interopDefaultLegacy(TimeFixPlugin);
|
const VueLoader__default = /*#__PURE__*/_interopDefaultLegacy(VueLoader);
|
const ExtractCssChunksPlugin__default = /*#__PURE__*/_interopDefaultLegacy(ExtractCssChunksPlugin);
|
const PnpWebpackPlugin__namespace = /*#__PURE__*/_interopNamespace(PnpWebpackPlugin);
|
const HardSourcePlugin__default = /*#__PURE__*/_interopDefaultLegacy(HardSourcePlugin);
|
const TerserWebpackPlugin__default = /*#__PURE__*/_interopDefaultLegacy(TerserWebpackPlugin);
|
const WebpackBar__default = /*#__PURE__*/_interopDefaultLegacy(WebpackBar);
|
const env__default = /*#__PURE__*/_interopDefaultLegacy(env);
|
const semver__default = /*#__PURE__*/_interopDefaultLegacy(semver);
|
const fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
const createResolver__default = /*#__PURE__*/_interopDefaultLegacy(createResolver);
|
const nodeExternals__default = /*#__PURE__*/_interopDefaultLegacy(nodeExternals);
|
|
class AsyncMFS extends MFS__default['default'] {}
|
|
const syncRegex = /Sync$/;
|
|
const propsToPromisify = Object.getOwnPropertyNames(MFS__default['default'].prototype).filter(n => syncRegex.test(n));
|
|
for (const prop of propsToPromisify) {
|
const asyncProp = prop.replace(syncRegex, '');
|
const origAsync = AsyncMFS.prototype[asyncProp];
|
|
AsyncMFS.prototype[asyncProp] = function (...args) {
|
// Callback support for webpack
|
if (origAsync && args.length && typeof args[args.length - 1] === 'function') {
|
return origAsync.call(this, ...args)
|
}
|
|
try {
|
return Promise.resolve(MFS__default['default'].prototype[prop].call(this, ...args))
|
} catch (error) {
|
return Promise.reject(error)
|
}
|
};
|
}
|
|
class CorsPlugin {
|
constructor ({ crossorigin }) {
|
this.crossorigin = crossorigin;
|
}
|
|
apply (compiler) {
|
const ID = 'vue-cors-plugin';
|
compiler.hooks.compilation.tap(ID, (compilation) => {
|
HtmlWebpackPlugin__default['default'].getHooks(compilation).alterAssetTagGroups.tap(ID, (data) => {
|
if (!this.crossorigin) {
|
return
|
}
|
[...data.headTags, ...data.bodyTags].forEach((tag) => {
|
if (['script', 'link'].includes(tag.tagName)) {
|
if (tag.attributes) {
|
tag.attributes.crossorigin = this.crossorigin;
|
}
|
}
|
});
|
});
|
});
|
}
|
}
|
|
/*
|
* This file is based on @vue/cli-service (MIT) ModernModePlugin
|
* https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/webpack/ModernModePlugin.js
|
*/
|
|
const legacyTemplateTags = {};
|
const legacyTemplateWatcher = new EventEmitter__default['default']();
|
|
class ModernModePlugin {
|
constructor ({ targetDir, isModernBuild, noUnsafeInline }) {
|
this.targetDir = targetDir;
|
this.isModernBuild = isModernBuild;
|
this.noUnsafeInline = noUnsafeInline;
|
}
|
|
apply (compiler) {
|
if (!this.isModernBuild) {
|
this.applyLegacy(compiler);
|
} else {
|
this.applyModern(compiler);
|
}
|
}
|
|
getLegacyTemplateTags (name) {
|
return new Promise((resolve) => {
|
const tags = legacyTemplateTags[name];
|
if (tags) {
|
return resolve(tags)
|
}
|
return legacyTemplateWatcher.once(name, () => {
|
const tags = legacyTemplateTags[name];
|
return tags && resolve(tags)
|
})
|
})
|
}
|
|
applyLegacy (compiler) {
|
const ID = 'nuxt-legacy-bundle';
|
compiler.hooks.compilation.tap(ID, (compilation) => {
|
HtmlWebpackPlugin__default['default'].getHooks(compilation).alterAssetTagGroups.tap(ID, (data) => {
|
HtmlWebpackPlugin__default['default'].getHooks(compilation).afterEmit.tap(ID, ({ outputName }) => {
|
// get stats, write to disk
|
legacyTemplateTags[data.plugin.options.filename] = data.bodyTags;
|
legacyTemplateWatcher.emit(outputName);
|
});
|
return data
|
});
|
});
|
}
|
|
applyModern (compiler) {
|
const ID = 'nuxt-modern-bundle';
|
compiler.hooks.compilation.tap(ID, (compilation) => {
|
HtmlWebpackPlugin__default['default'].getHooks(compilation).alterAssetTagGroups.tapPromise(ID, async (data) => {
|
// use <script type="module"> for modern assets
|
data.bodyTags.forEach((tag) => {
|
if (tag.tagName === 'script' && tag.attributes) {
|
tag.attributes.type = 'module';
|
}
|
});
|
|
// use <link rel="modulepreload"> instead of <link rel="preload">
|
// for modern assets
|
data.headTags.forEach((tag) => {
|
if (tag.tagName === 'link' &&
|
tag.attributes.rel === 'preload' &&
|
tag.attributes.as === 'script') {
|
tag.attributes.rel = 'modulepreload';
|
}
|
});
|
|
// inject links for legacy assets as <script nomodule>
|
const fileName = data.plugin.options.filename;
|
const legacyScriptTags = (await this.getLegacyTemplateTags(fileName))
|
.filter(a => a.tagName === 'script' && a.attributes);
|
|
for (const a of legacyScriptTags) {
|
a.attributes.nomodule = true;
|
data.bodyTags.push(a);
|
}
|
|
if (this.noUnsafeInline) {
|
// inject the fix as an external script
|
const safariFixFilename = 'safari-nomodule-fix.js';
|
const safariFixPath = legacyScriptTags[0].attributes.src
|
.split('/')
|
.slice(0, -1)
|
.concat([safariFixFilename])
|
.join('/');
|
|
compilation.assets[safariFixFilename] = {
|
source: () => Buffer.from(utils.safariNoModuleFix),
|
size: () => Buffer.byteLength(utils.safariNoModuleFix)
|
};
|
data.bodyTags.push({
|
tagName: 'script',
|
closeTag: true,
|
attributes: {
|
src: safariFixPath
|
}
|
});
|
} else {
|
// inject Safari 10 nomodule fix
|
data.bodyTags.push({
|
tagName: 'script',
|
closeTag: true,
|
innerHTML: utils.safariNoModuleFix
|
});
|
}
|
|
delete legacyTemplateTags[fileName];
|
|
return data
|
});
|
});
|
}
|
}
|
|
/**
|
* This file is based on Vue.js (MIT) webpack plugins
|
* https://github.com/vuejs/vue/blob/dev/src/server/webpack-plugin/util.js
|
*/
|
|
const validate = (compiler) => {
|
if (compiler.options.target !== 'node') {
|
consola__default['default'].warn('webpack config `target` should be "node".');
|
}
|
|
if (compiler.options.output && compiler.options.output.libraryTarget !== 'commonjs2') {
|
consola__default['default'].warn('webpack config `output.libraryTarget` should be "commonjs2".');
|
}
|
|
if (!compiler.options.externals) {
|
consola__default['default'].info(
|
'It is recommended to externalize dependencies in the server build for ' +
|
'better build performance.'
|
);
|
}
|
};
|
|
const isJSRegExp = /\.js(\?[^.]+)?$/;
|
|
const isJS = file => isJSRegExp.test(file);
|
|
const extractQueryPartJS = file => isJSRegExp.exec(file)[1];
|
|
const isCSS = file => /\.css(\?[^.]+)?$/.test(file);
|
|
/**
|
* This file is based on Vue.js (MIT) webpack plugins
|
* https://github.com/vuejs/vue/blob/dev/src/server/webpack-plugin/client.js
|
*/
|
|
class VueSSRClientPlugin {
|
constructor (options = {}) {
|
this.options = Object.assign({
|
filename: null
|
}, options);
|
}
|
|
apply (compiler) {
|
compiler.hooks.emit.tapAsync('vue-client-plugin', (compilation, cb) => {
|
const stats = compilation.getStats().toJson();
|
|
const allFiles = lodash.uniq(stats.assets
|
.map(a => a.name));
|
|
const initialFiles = lodash.uniq(Object.keys(stats.entrypoints)
|
.map(name => stats.entrypoints[name].assets)
|
.reduce((assets, all) => all.concat(assets), [])
|
.filter(file => isJS(file) || isCSS(file)));
|
|
const asyncFiles = allFiles
|
.filter(file => isJS(file) || isCSS(file))
|
.filter(file => !initialFiles.includes(file));
|
|
const assetsMapping = {};
|
stats.assets
|
.filter(({ name }) => isJS(name))
|
.forEach(({ name, chunkNames }) => {
|
const componentHash = hash__default['default'](chunkNames.join('|'));
|
if (!assetsMapping[componentHash]) {
|
assetsMapping[componentHash] = [];
|
}
|
assetsMapping[componentHash].push(name);
|
});
|
|
const manifest = {
|
publicPath: stats.publicPath,
|
all: allFiles,
|
initial: initialFiles,
|
async: asyncFiles,
|
modules: { /* [identifier: string]: Array<index: number> */ },
|
assetsMapping
|
};
|
|
const { entrypoints, namedChunkGroups } = stats;
|
const assetModules = stats.modules.filter(m => m.assets.length);
|
const fileToIndex = file => manifest.all.indexOf(file);
|
stats.modules.forEach((m) => {
|
// Ignore modules duplicated in multiple chunks
|
if (m.chunks.length === 1) {
|
const [cid] = m.chunks;
|
const chunk = stats.chunks.find(c => c.id === cid);
|
if (!chunk || !chunk.files) {
|
return
|
}
|
const id = m.identifier.replace(/\s\w+$/, ''); // remove appended hash
|
const filesSet = new Set(chunk.files.map(fileToIndex));
|
|
for (const chunkName of chunk.names) {
|
if (!entrypoints[chunkName]) {
|
const chunkGroup = namedChunkGroups[chunkName];
|
if (chunkGroup) {
|
for (const asset of chunkGroup.assets) {
|
filesSet.add(fileToIndex(asset));
|
}
|
}
|
}
|
}
|
|
const files = Array.from(filesSet);
|
manifest.modules[hash__default['default'](id)] = files;
|
|
// In production mode, modules may be concatenated by scope hoisting
|
// Include ConcatenatedModule for not losing module-component mapping
|
if (Array.isArray(m.modules)) {
|
for (const concatenatedModule of m.modules) {
|
const id = hash__default['default'](concatenatedModule.identifier.replace(/\s\w+$/, ''));
|
if (!manifest.modules[id]) {
|
manifest.modules[id] = files;
|
}
|
}
|
}
|
|
// Find all asset modules associated with the same chunk
|
assetModules.forEach((m) => {
|
if (m.chunks.includes(cid)) {
|
files.push.apply(files, m.assets.map(fileToIndex));
|
}
|
});
|
}
|
});
|
|
const src = JSON.stringify(manifest, null, 2);
|
|
compilation.assets[this.options.filename] = {
|
source: () => src,
|
size: () => src.length
|
};
|
cb();
|
});
|
}
|
}
|
|
// https://github.com/webpack-contrib/thread-loader
|
// https://github.com/webpack-contrib/cache-loader
|
|
class PerfLoader {
|
constructor (name, buildContext, { resolveModule }) {
|
this.name = name;
|
this.buildContext = buildContext;
|
this.workerPools = PerfLoader.defaultPools({ dev: buildContext.options.dev });
|
this.resolveModule = resolveModule;
|
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, resolveModule }) {
|
const pools = PerfLoader.defaultPools({ dev });
|
PerfLoader.warmup(pools.js, [
|
resolveModule('babel-loader'),
|
resolveModule('@babel/preset-env')
|
]);
|
PerfLoader.warmup(pools.css, [resolveModule('css-loader')]);
|
}
|
|
static warmup (...args) {
|
threadLoader.warmup(...args);
|
}
|
|
use (poolName) {
|
const loaders = [];
|
|
if (this.buildContext.buildOptions.cache) {
|
loaders.push({
|
loader: this.resolveModule('cache-loader'),
|
options: {
|
cacheDirectory: path__default['default'].resolve(`node_modules/.cache/cache-loader/${this.name}`)
|
}
|
});
|
}
|
|
if (this.buildContext.buildOptions.parallel) {
|
const pool = this.workerPools[poolName];
|
if (pool) {
|
loaders.push({
|
loader: this.resolveModule('thread-loader'),
|
options: pool
|
});
|
}
|
}
|
|
return loaders
|
}
|
}
|
|
const orderPresets$1 = {
|
cssnanoLast (names) {
|
const nanoIndex = names.indexOf('cssnano');
|
if (nanoIndex !== names.length - 1) {
|
names.push(names.splice(nanoIndex, 1)[0]);
|
}
|
return names
|
},
|
presetEnvLast (names) {
|
const nanoIndex = names.indexOf('postcss-preset-env');
|
if (nanoIndex !== names.length - 1) {
|
names.push(names.splice(nanoIndex, 1)[0]);
|
}
|
return names
|
},
|
presetEnvAndCssnanoLast (names) {
|
return orderPresets$1.cssnanoLast(orderPresets$1.presetEnvLast(names))
|
}
|
};
|
|
function postcssConfigFileWarning$1 () {
|
if (postcssConfigFileWarning$1.executed) {
|
return
|
}
|
consola__default['default'].warn('Please use `build.postcss` in your nuxt.config.js instead of an external config file. Support for such files will be removed in Nuxt 3 as they remove all defaults set by Nuxt and can cause severe problems with features like alias resolving inside your CSS.');
|
postcssConfigFileWarning$1.executed = true;
|
}
|
|
class PostcssConfig$1 {
|
constructor (buildContext) {
|
this.buildContext = buildContext;
|
}
|
|
get postcssOptions () {
|
return this.buildContext.buildOptions.postcss
|
}
|
|
get postcssImportAlias () {
|
const alias = { ...this.buildContext.options.alias };
|
|
for (const key in alias) {
|
if (key.startsWith('~')) {
|
continue
|
}
|
const newKey = '~' + key;
|
if (!alias[newKey]) {
|
alias[newKey] = alias[key];
|
}
|
}
|
|
return alias
|
}
|
|
get defaultConfig () {
|
const { dev, srcDir, rootDir, modulesDir } = this.buildContext.options;
|
return {
|
sourceMap: this.buildContext.buildOptions.cssSourceMap,
|
plugins: {
|
// https://github.com/postcss/postcss-import
|
'postcss-import': {
|
resolve: createResolver__default['default']({
|
alias: this.postcssImportAlias,
|
modules: [srcDir, rootDir, ...modulesDir]
|
})
|
},
|
|
// https://github.com/postcss/postcss-url
|
'postcss-url': {},
|
|
// https://github.com/csstools/postcss-preset-env
|
'postcss-preset-env': this.preset || {},
|
cssnano: dev
|
? false
|
: {
|
preset: ['default', {
|
// Keep quotes in font values to prevent from HEX conversion
|
// https://github.com/nuxt/nuxt.js/issues/6306
|
minifyFontValues: { removeQuotes: false }
|
}]
|
}
|
},
|
// Array, String or Function
|
order: 'presetEnvAndCssnanoLast'
|
}
|
}
|
|
searchConfigFile () {
|
// Search for postCSS config file and use it if exists
|
// https://github.com/michael-ciniawsky/postcss-load-config
|
// TODO: Remove in Nuxt 3
|
const { srcDir, rootDir } = this.buildContext.options;
|
for (const dir of [srcDir, rootDir]) {
|
for (const file of [
|
'postcss.config.js',
|
'.postcssrc.js',
|
'.postcssrc',
|
'.postcssrc.json',
|
'.postcssrc.yaml'
|
]) {
|
const configFile = path__default['default'].resolve(dir, file);
|
if (fs__default['default'].existsSync(configFile)) {
|
postcssConfigFileWarning$1();
|
return configFile
|
}
|
}
|
}
|
}
|
|
configFromFile () {
|
const loaderConfig = (this.postcssOptions && this.postcssOptions.config) || {};
|
loaderConfig.path = loaderConfig.path || this.searchConfigFile();
|
|
if (loaderConfig.path) {
|
return {
|
sourceMap: this.buildContext.buildOptions.cssSourceMap,
|
config: loaderConfig
|
}
|
}
|
}
|
|
normalize (config) {
|
// TODO: Remove in Nuxt 3
|
if (Array.isArray(config)) {
|
consola__default['default'].warn('Using an Array as `build.postcss` will be deprecated in Nuxt 3. Please switch to the object' +
|
' declaration');
|
config = { plugins: config };
|
}
|
return config
|
}
|
|
sortPlugins ({ plugins, order }) {
|
const names = Object.keys(plugins);
|
if (typeof order === 'string') {
|
order = orderPresets$1[order];
|
}
|
return typeof order === 'function' ? order(names, orderPresets$1) : (order || names)
|
}
|
|
loadPlugins (config) {
|
const { plugins } = config;
|
if (utils.isPureObject(plugins)) {
|
// Map postcss plugins into instances on object mode once
|
config.plugins = this.sortPlugins(config)
|
.map((p) => {
|
const plugin = this.buildContext.nuxt.resolver.requireModule(p, { paths: [__dirname] });
|
const opts = plugins[p];
|
if (opts === false) {
|
return false // Disabled
|
}
|
return plugin(opts)
|
})
|
.filter(Boolean);
|
}
|
}
|
|
config () {
|
/* istanbul ignore if */
|
if (!this.postcssOptions) {
|
return false
|
}
|
|
let config = this.configFromFile();
|
if (config) {
|
return config
|
}
|
|
config = this.normalize(lodash.cloneDeep(this.postcssOptions));
|
|
// Apply default plugins
|
if (utils.isPureObject(config)) {
|
if (config.preset) {
|
this.preset = config.preset;
|
delete config.preset;
|
}
|
if (Array.isArray(config.plugins)) {
|
lodash.defaults(config, this.defaultConfig);
|
} else {
|
// Keep the order of default plugins
|
config = lodash.merge({}, this.defaultConfig, config);
|
this.loadPlugins(config);
|
}
|
return config
|
}
|
}
|
}
|
|
const orderPresets = {
|
cssnanoLast (names) {
|
const nanoIndex = names.indexOf('cssnano');
|
if (nanoIndex !== names.length - 1) {
|
names.push(names.splice(nanoIndex, 1)[0]);
|
}
|
return names
|
},
|
presetEnvLast (names) {
|
const nanoIndex = names.indexOf('postcss-preset-env');
|
if (nanoIndex !== names.length - 1) {
|
names.push(names.splice(nanoIndex, 1)[0]);
|
}
|
return names
|
},
|
presetEnvAndCssnanoLast (names) {
|
return orderPresets.cssnanoLast(orderPresets.presetEnvLast(names))
|
}
|
};
|
|
function postcssConfigFileWarning () {
|
if (postcssConfigFileWarning.executed) {
|
return
|
}
|
consola__default['default'].warn('Please use `build.postcss` in your nuxt.config.js instead of an external config file. Support for such files will be removed in Nuxt 3 as they remove all defaults set by Nuxt and can cause severe problems with features like alias resolving inside your CSS.');
|
postcssConfigFileWarning.executed = true;
|
}
|
|
class PostcssConfig {
|
constructor (buildContext) {
|
this.buildContext = buildContext;
|
}
|
|
get cssSourceMap () {
|
return this.buildContext.buildOptions.cssSourceMap
|
}
|
|
get postcssOptions () {
|
return this.buildContext.buildOptions.postcss
|
}
|
|
get postcssImportAlias () {
|
const alias = { ...this.buildContext.options.alias };
|
|
for (const key in alias) {
|
if (key.startsWith('~')) {
|
continue
|
}
|
const newKey = '~' + key;
|
if (!alias[newKey]) {
|
alias[newKey] = alias[key];
|
}
|
}
|
|
return alias
|
}
|
|
get defaultPostcssOptions () {
|
const { dev, srcDir, rootDir, modulesDir } = this.buildContext.options;
|
return {
|
plugins: {
|
// https://github.com/postcss/postcss-import
|
'postcss-import': {
|
resolve: createResolver__default['default']({
|
alias: this.postcssImportAlias,
|
modules: [srcDir, rootDir, ...modulesDir]
|
})
|
},
|
|
// https://github.com/postcss/postcss-url
|
'postcss-url': {},
|
|
// https://github.com/csstools/postcss-preset-env
|
// TODO: enable when https://github.com/csstools/postcss-preset-env/issues/191 gets closed
|
// 'postcss-preset-env': this.preset || {},
|
cssnano: dev
|
? false
|
: {
|
preset: ['default', {
|
// Keep quotes in font values to prevent from HEX conversion
|
// https://github.com/nuxt/nuxt.js/issues/6306
|
minifyFontValues: { removeQuotes: false }
|
}]
|
}
|
},
|
// Array, String or Function
|
order: 'presetEnvAndCssnanoLast'
|
}
|
}
|
|
searchConfigFile () {
|
// Search for postCSS config file and use it if exists
|
// https://github.com/michael-ciniawsky/postcss-load-config
|
// TODO: Remove in Nuxt 3
|
const { srcDir, rootDir } = this.buildContext.options;
|
for (const dir of [srcDir, rootDir]) {
|
for (const file of [
|
'postcss.config.js',
|
'.postcssrc.js',
|
'.postcssrc',
|
'.postcssrc.json',
|
'.postcssrc.yaml'
|
]) {
|
const configFile = path__default['default'].resolve(dir, file);
|
if (fs__default['default'].existsSync(configFile)) {
|
postcssConfigFileWarning();
|
return configFile
|
}
|
}
|
}
|
}
|
|
configFromFile () {
|
const loaderConfig = (this.postcssOptions && this.postcssOptions.config) || {};
|
loaderConfig.path = loaderConfig.path || this.searchConfigFile();
|
|
if (loaderConfig.path) {
|
return {
|
config: loaderConfig
|
}
|
}
|
}
|
|
normalize (postcssOptions) {
|
// TODO: Remove in Nuxt 3
|
if (Array.isArray(postcssOptions)) {
|
consola__default['default'].warn('Using an Array as `build.postcss` will be deprecated in Nuxt 3. Please switch to the object' +
|
' declaration');
|
postcssOptions = { plugins: postcssOptions };
|
}
|
return postcssOptions
|
}
|
|
sortPlugins ({ plugins, order }) {
|
const names = Object.keys(plugins);
|
if (typeof order === 'string') {
|
order = orderPresets[order];
|
}
|
return typeof order === 'function' ? order(names, orderPresets) : (order || names)
|
}
|
|
loadPlugins (postcssOptions) {
|
const { plugins } = postcssOptions;
|
if (utils.isPureObject(plugins)) {
|
// Map postcss plugins into instances on object mode once
|
postcssOptions.plugins = this.sortPlugins(postcssOptions)
|
.map((p) => {
|
const plugin = this.buildContext.nuxt.resolver.requireModule(p, { paths: [__dirname] });
|
const opts = plugins[p];
|
if (opts === false) {
|
return false // Disabled
|
}
|
return plugin(opts)
|
})
|
.filter(Boolean);
|
}
|
}
|
|
config () {
|
/* istanbul ignore if */
|
if (!this.postcssOptions) {
|
return false
|
}
|
|
let postcssOptions = this.configFromFile();
|
if (postcssOptions) {
|
return {
|
postcssOptions,
|
sourceMap: this.cssSourceMap
|
}
|
}
|
|
postcssOptions = this.normalize(lodash.cloneDeep(this.postcssOptions));
|
|
// Apply default plugins
|
if (utils.isPureObject(postcssOptions)) {
|
if (postcssOptions.preset) {
|
this.preset = postcssOptions.preset;
|
delete postcssOptions.preset;
|
}
|
if (Array.isArray(postcssOptions.plugins)) {
|
lodash.defaults(postcssOptions, this.defaultPostcssOptions);
|
} else {
|
// Keep the order of default plugins
|
postcssOptions = lodash.merge({}, this.defaultPostcssOptions, postcssOptions);
|
this.loadPlugins(postcssOptions);
|
}
|
|
const { execute } = postcssOptions;
|
delete postcssOptions.execute;
|
delete postcssOptions.order;
|
|
return {
|
execute,
|
postcssOptions,
|
sourceMap: this.cssSourceMap
|
}
|
}
|
}
|
}
|
|
class StyleLoader {
|
constructor (buildContext, { isServer, perfLoader, resolveModule }) {
|
this.buildContext = buildContext;
|
this.isServer = isServer;
|
this.perfLoader = perfLoader;
|
this.resolveModule = resolveModule;
|
|
const { postcss: postcssOptions } = buildContext.options.build;
|
if (postcssOptions) {
|
const postcss = require(resolveModule('postcss'));
|
// postcss >= v8
|
if (!postcss.vendor) {
|
this.postcssConfig = new PostcssConfig(buildContext);
|
} else {
|
this.postcssConfig = new PostcssConfig$1(buildContext);
|
}
|
}
|
}
|
|
get extractCSS () {
|
return this.buildContext.buildOptions.extractCSS
|
}
|
|
get exportOnlyLocals () {
|
return Boolean(this.isServer && this.extractCSS)
|
}
|
|
isUrlResolvingEnabled (url, resourcePath) {
|
// Ignore absolute URLs, it will be handled by serve-static.
|
return !url.startsWith('/')
|
}
|
|
normalize (loaders) {
|
loaders = utils.wrapArray(loaders);
|
return loaders.map(loader => (typeof loader === 'string' ? { loader } : loader))
|
}
|
|
styleResource (ext) {
|
const { buildOptions: { styleResources }, options: { rootDir } } = this.buildContext;
|
const extResource = styleResources[ext];
|
// style-resources-loader
|
// https://github.com/yenshih/style-resources-loader
|
if (!extResource) {
|
return
|
}
|
const patterns = utils.wrapArray(extResource).map(p => path__default['default'].resolve(rootDir, p));
|
|
return {
|
loader: this.resolveModule('style-resources-loader'),
|
options: Object.assign(
|
{ patterns },
|
styleResources.options || {}
|
)
|
}
|
}
|
|
postcss () {
|
// postcss-loader
|
// https://github.com/postcss/postcss-loader
|
if (!this.postcssConfig) {
|
return
|
}
|
|
const config = this.postcssConfig.config();
|
|
if (!config) {
|
return
|
}
|
|
return {
|
loader: this.resolveModule('postcss-loader'),
|
options: Object.assign({ sourceMap: this.buildContext.buildOptions.cssSourceMap }, config)
|
}
|
}
|
|
css (options) {
|
const cssLoader = { loader: this.resolveModule('css-loader'), options };
|
|
if (!options.url) {
|
options.url = this.isUrlResolvingEnabled;
|
}
|
|
if (this.exportOnlyLocals) {
|
options.modules = {
|
...options.modules,
|
exportOnlyLocals: true
|
};
|
return [cssLoader]
|
}
|
|
return [this.styleLoader(), cssLoader]
|
}
|
|
cssModules (options) {
|
return this.css(options)
|
}
|
|
extract () {
|
if (this.extractCSS) {
|
const isDev = this.buildContext.options.dev;
|
return {
|
loader: ExtractCssChunksPlugin__default['default'].loader,
|
options: {
|
// TODO: https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/132
|
// https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/161#issuecomment-500162574
|
reloadAll: isDev,
|
hmr: isDev
|
}
|
}
|
}
|
}
|
|
styleLoader () {
|
return this.extract() || {
|
loader: this.resolveModule('vue-style-loader'),
|
options: this.buildContext.buildOptions.loaders.vueStyle
|
}
|
}
|
|
apply (ext, loaders = []) {
|
const { css, cssModules } = this.buildContext.buildOptions.loaders;
|
|
const customLoaders = [].concat(
|
this.postcss(),
|
this.normalize(loaders),
|
this.styleResource(ext)
|
).filter(Boolean);
|
|
css.importLoaders = cssModules.importLoaders = customLoaders.length;
|
|
return [
|
// This matches <style module>
|
{
|
resourceQuery: /module/,
|
use: this.perfLoader.css().concat(
|
this.cssModules(cssModules),
|
customLoaders
|
)
|
},
|
// This matches plain <style> or <style scoped>
|
{
|
use: this.perfLoader.css().concat(
|
this.css(css),
|
customLoaders
|
)
|
}
|
]
|
}
|
}
|
|
class WarningIgnorePlugin {
|
constructor (filter) {
|
this.filter = filter;
|
}
|
|
apply (compiler) /* istanbul ignore next */ {
|
compiler.hooks.done.tap('warnfix-plugin', (stats) => {
|
stats.compilation.warnings = stats.compilation.warnings.filter(this.filter);
|
});
|
}
|
}
|
|
const reservedVueTags = [
|
// HTML tags
|
'html', 'body', 'base', 'head', 'link', 'meta', 'style', 'title', 'address',
|
'article', 'aside', 'footer', 'header', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
'hgroup', 'nav', 'section', 'div', 'dd', 'dl', 'dt', 'figcaption', 'figure',
|
'picture', 'hr', 'img', 'li', 'main', 'ol', 'p', 'pre', 'ul', 'a', 'b', 'abbr',
|
'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'dfn', 'em', 'i', 'kbd', 'mark',
|
'q', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'small', 'span', 'strong', 'sub',
|
'sup', 'time', 'u', 'var', 'wbr', 'area', 'audio', 'map', 'track', 'video',
|
'embed', 'object', 'param', 'source', 'canvas', 'script', 'noscript', 'del',
|
'ins', 'caption', 'col', 'colgroup', 'table', 'thead', 'tbody', 'td', 'th', 'tr',
|
'button', 'datalist', 'fieldset', 'form', 'input', 'label', 'legend', 'meter',
|
'optgroup', 'option', 'output', 'progress', 'select', 'textarea', 'details',
|
'dialog', 'menu', 'menuitem', 'summary', 'content', 'element', 'shadow', 'template',
|
'blockquote', 'iframe', 'tfoot',
|
// SVG tags
|
'svg', 'animate', 'circle', 'clippath', 'cursor', 'defs', 'desc', 'ellipse', 'filter',
|
'font-face', 'foreignObject', 'g', 'glyph', 'image', 'line', 'marker', 'mask',
|
'missing-glyph', 'path', 'pattern', 'polygon', 'polyline', 'rect', 'switch', 'symbol',
|
'text', 'textpath', 'tspan', 'use', 'view',
|
// Vue built-in tags
|
'slot', 'component'
|
];
|
|
class WebpackBaseConfig {
|
constructor (builder) {
|
this.builder = builder;
|
this.buildContext = builder.buildContext;
|
this.resolveModule = id => utils.tryResolve(id, [this.buildContext.options.rootDir, __dirname]) || id;
|
}
|
|
get colors () {
|
return {
|
client: 'green',
|
server: 'orange',
|
modern: 'blue'
|
}
|
}
|
|
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.buildContext.target
|
}
|
|
get dev () {
|
return this.buildContext.options.dev
|
}
|
|
get loaders () {
|
if (!this._loaders) {
|
this._loaders = lodash.cloneDeep(this.buildContext.buildOptions.loaders);
|
// sass-loader<8 support (#6460)
|
const sassLoaderPKG = utils.getPKG('sass-loader');
|
if (sassLoaderPKG && semver__default['default'].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/,
|
/ufo/, // exports modern syntax for browser field
|
...this.normalizeTranspile({ pathNormalize: true })
|
]
|
}
|
|
normalizeTranspile ({ pathNormalize = false } = {}) {
|
const transpile = [];
|
for (let pattern of this.buildContext.buildOptions.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(lodash.escapeRegExp(
|
pathNormalize ? path__default['default'].normalize(posixModule) : posixModule
|
)));
|
}
|
}
|
return transpile
|
}
|
|
getBabelOptions () {
|
const envName = this.name;
|
const { buildOptions: { corejs }, options: { rootDir } } = this.buildContext;
|
const options = {
|
...this.buildContext.buildOptions.babel,
|
envName
|
};
|
|
if (options.configFile || options.babelrc) {
|
return options
|
}
|
|
if (typeof options.plugins === 'function') {
|
options.plugins = options.plugins(
|
{
|
envName,
|
...this.nuxtEnv
|
}
|
);
|
}
|
|
// Auto detect corejs version
|
let corejsVersion = corejs;
|
if (corejsVersion === 'auto') {
|
try {
|
corejsVersion = Number.parseInt(utils.requireModule('core-js/package.json', rootDir).version.split('.')[0]);
|
} catch (_err) {
|
corejsVersion = 2;
|
}
|
} else {
|
corejsVersion = Number.parseInt(corejsVersion);
|
}
|
|
if (![2, 3].includes(corejsVersion)) {
|
consola__default['default'].warn(`Invalid corejs version ${corejsVersion}! Please set "build.corejs" to either "auto", 2 or 3.`);
|
corejsVersion = 2;
|
}
|
|
const defaultPreset = [this.resolveModule('@nuxt/babel-preset-app'), {
|
corejs: {
|
version: corejsVersion
|
}
|
}];
|
|
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.buildContext.buildOptions.filenames[key];
|
if (typeof fileName === 'function') {
|
fileName = fileName(this.nuxtEnv);
|
}
|
if (this.dev) {
|
const hash = /\[(chunkhash|contenthash|hash)(?::(\d+))?]/.exec(fileName);
|
if (hash) {
|
consola__default['default'].warn(`Notice: Please do not use ${hash[1]} in dev mode to prevent memory leak`);
|
}
|
}
|
if (this.buildContext.buildOptions.analyze && !fileName.includes('[name]')) {
|
fileName = '[name].' + fileName;
|
}
|
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 === utils.TARGETS.static,
|
'process.target': JSON.stringify(this.target)
|
};
|
if (this.buildContext.buildOptions.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.buildContext.options.env).forEach(([key, value]) => {
|
env['process.env.' + key] =
|
['boolean', 'number'].includes(typeof value)
|
? value
|
: JSON.stringify(value);
|
});
|
return env
|
}
|
|
output () {
|
const {
|
options: { buildDir, router },
|
buildOptions: { publicPath }
|
} = this.buildContext;
|
return {
|
path: path__default['default'].resolve(buildDir, 'dist', this.isServer ? 'server' : 'client'),
|
filename: this.getFileName('app'),
|
futureEmitAssets: true, // TODO: Remove when using webpack 5
|
chunkFilename: this.getFileName('chunk'),
|
publicPath: utils.isUrl(publicPath) ? publicPath : ufo.isRelative(publicPath) ? publicPath.replace(/^\.+\//, '/') : utils.urlJoin(router.base, publicPath)
|
}
|
}
|
|
optimization () {
|
const optimization = lodash.cloneDeep(this.buildContext.buildOptions.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.buildContext.options.modulesDir);
|
|
const resolvePath = [
|
...(global.__NUXT_PREPATHS__ || []),
|
this.buildContext.options.rootDir,
|
__dirname,
|
...(global.__NUXT_PATHS__ || []),
|
utils.resolveModule('@nuxt/vue-app'),
|
utils.resolveModule('@nuxt/babel-preset-app')
|
];
|
const resolvePlugins = [PnpWebpackPlugin__namespace].concat(resolvePath.map(p => PnpWebpackPlugin__namespace.moduleLoader(p)));
|
|
return {
|
resolve: {
|
extensions: ['.wasm', '.mjs', '.js', '.json', '.vue', '.jsx'],
|
alias: this.alias(),
|
modules: webpackModulesDir,
|
plugins: resolvePlugins
|
},
|
resolveLoader: {
|
modules: [
|
path__default['default'].resolve(__dirname, '../node_modules'),
|
...webpackModulesDir
|
],
|
plugins: resolvePlugins
|
}
|
}
|
}
|
|
minimizer () {
|
const minimizer = [];
|
const { terser, cache } = this.buildContext.buildOptions;
|
|
// https://github.com/webpack-contrib/terser-webpack-plugin
|
if (terser) {
|
minimizer.push(
|
new TerserWebpackPlugin__default['default'](Object.assign({
|
cache,
|
extractComments: {
|
condition: 'some',
|
filename: 'LICENSES'
|
},
|
terserOptions: {
|
compress: {
|
ecma: this.isModern ? 2015 : undefined
|
},
|
mangle: {
|
reserved: reservedVueTags
|
}
|
}
|
}, terser))
|
);
|
}
|
|
return minimizer
|
}
|
|
alias () {
|
return {
|
...this.buildContext.options.alias,
|
'vue-meta': this.resolveModule(`vue-meta${this.isServer ? '' : '/dist/vue-meta.esm.browser.js'}`)
|
}
|
}
|
|
rules () {
|
const perfLoader = new PerfLoader(this.name, this.buildContext, { resolveModule: this.resolveModule });
|
const styleLoader = new StyleLoader(
|
this.buildContext,
|
{ isServer: this.isServer, perfLoader, resolveModule: this.resolveModule }
|
);
|
|
const babelLoader = {
|
loader: this.resolveModule('babel-loader'),
|
options: this.getBabelOptions()
|
};
|
|
return [
|
{
|
test: /\.vue$/i,
|
loader: this.resolveModule('vue-loader'),
|
options: this.loaders.vue
|
},
|
{
|
test: /\.pug$/i,
|
oneOf: [
|
{
|
resourceQuery: /^\?vue/i,
|
use: [{
|
loader: this.resolveModule('pug-plain-loader'),
|
options: this.loaders.pugPlain
|
}]
|
},
|
{
|
use: [
|
this.resolveModule('raw-loader'),
|
{
|
loader: this.resolveModule('pug-plain-loader'),
|
options: this.loaders.pugPlain
|
}
|
]
|
}
|
]
|
},
|
{
|
test: /\.m?jsx?$/i,
|
exclude: (file) => {
|
file = file.split(/node_modules(.*)/)[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: this.resolveModule('less-loader'),
|
options: this.loaders.less
|
})
|
},
|
{
|
test: /\.sass$/i,
|
oneOf: styleLoader.apply('sass', {
|
loader: this.resolveModule('sass-loader'),
|
options: this.loaders.sass
|
})
|
},
|
{
|
test: /\.scss$/i,
|
oneOf: styleLoader.apply('scss', {
|
loader: this.resolveModule('sass-loader'),
|
options: this.loaders.scss
|
})
|
},
|
{
|
test: /\.styl(us)?$/i,
|
oneOf: styleLoader.apply('stylus', {
|
loader: this.resolveModule('stylus-loader'),
|
options: this.loaders.stylus
|
})
|
},
|
{
|
test: /\.(png|jpe?g|gif|svg|webp|avif)$/i,
|
use: [{
|
loader: this.resolveModule('url-loader'),
|
options: Object.assign(
|
this.loaders.imgUrl,
|
{ name: this.getFileName('img') }
|
)
|
}]
|
},
|
{
|
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
|
use: [{
|
loader: this.resolveModule('url-loader'),
|
options: Object.assign(
|
this.loaders.fontUrl,
|
{ name: this.getFileName('font') }
|
)
|
}]
|
},
|
{
|
test: /\.(webm|mp4|ogv)$/i,
|
use: [{
|
loader: this.resolveModule('file-loader'),
|
options: Object.assign(
|
this.loaders.file,
|
{ name: this.getFileName('video') }
|
)
|
}]
|
}
|
]
|
}
|
|
plugins () {
|
const plugins = [];
|
const { nuxt, buildOptions } = this.buildContext;
|
|
// Add timefix-plugin before others plugins
|
if (this.dev) {
|
plugins.push(new TimeFixPlugin__default['default']());
|
}
|
|
// CSS extraction)
|
if (buildOptions.extractCSS) {
|
plugins.push(new ExtractCssChunksPlugin__default['default'](Object.assign({
|
filename: this.getFileName('css'),
|
chunkFilename: this.getFileName('css')
|
}, buildOptions.extractCSS)));
|
}
|
|
plugins.push(new VueLoader__default['default'].VueLoaderPlugin());
|
|
plugins.push(...(buildOptions.plugins || []));
|
|
plugins.push(new WarningIgnorePlugin(this.warningIgnoreFilter()));
|
|
// Build progress indicator
|
plugins.push(new WebpackBar__default['default']({
|
name: this.name,
|
color: this.colors[this.name],
|
reporters: [
|
'basic',
|
'fancy',
|
'profile',
|
'stats'
|
],
|
basic: !buildOptions.quiet && env__default['default'].minimalCLI,
|
fancy: !buildOptions.quiet && !env__default['default'].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: (buildContext) => {
|
if (buildContext.hasErrors) {
|
nuxt.callHook('bundler:error');
|
}
|
},
|
allDone: () => {
|
nuxt.callHook('bundler:done');
|
},
|
progress ({ statesArray }) {
|
nuxt.callHook('bundler:progress', statesArray);
|
}
|
}
|
}));
|
|
if (buildOptions.hardSource) {
|
// https://github.com/mzgoddard/hard-source-webpack-plugin
|
plugins.push(new HardSourcePlugin__default['default']({
|
info: {
|
level: 'warn'
|
},
|
...buildOptions.hardSource
|
}));
|
}
|
|
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.buildContext.buildOptions.warningIgnoreFilters || [])
|
];
|
|
return warn => !filters.some(ignoreFilter => ignoreFilter(warn))
|
}
|
|
extendConfig (config) {
|
const { extend } = this.buildContext.buildOptions;
|
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__default['default'].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,
|
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 = lodash.cloneDeep(this.extendConfig(config));
|
|
return extendedConfig
|
}
|
}
|
|
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'
|
: 'cheap-module-eval-source-map'
|
}
|
|
getCspScriptPolicy () {
|
const { csp } = this.buildContext.options.render;
|
if (csp) {
|
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.buildContext.buildOptions.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|url-polyfill|@nuxt[\\/]ufo|ufo|nuxt\.js)[\\/]/,
|
chunks: 'all',
|
name: true,
|
priority: 10
|
};
|
}
|
|
return optimization
|
}
|
|
minimizer () {
|
const minimizer = super.minimizer();
|
const { optimizeCSS } = this.buildContext.buildOptions;
|
|
// https://github.com/NMFR/optimize-css-assets-webpack-plugin
|
// https://github.com/webpack-contrib/mini-css-extract-plugin#minimizing-for-production
|
// TODO: Remove OptimizeCSSAssetsPlugin when upgrading to webpack 5
|
if (optimizeCSS) {
|
minimizer.push(new OptimizeCSSAssetsPlugin__default['default'](Object.assign({}, optimizeCSS)));
|
}
|
|
return minimizer
|
}
|
|
alias () {
|
const aliases = super.alias();
|
|
for (const p of this.buildContext.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 { buildOptions, options: { appTemplatePath, buildDir, modern, render } } = this.buildContext;
|
|
// Generate output HTML for SSR
|
if (buildOptions.ssr) {
|
plugins.push(
|
new HtmlWebpackPlugin__default['default']({
|
filename: '../server/index.ssr.html',
|
template: appTemplatePath,
|
minify: buildOptions.html.minify,
|
inject: false // Resources will be injected using bundleRenderer
|
})
|
);
|
}
|
|
plugins.push(
|
new HtmlWebpackPlugin__default['default']({
|
filename: '../server/index.spa.html',
|
template: appTemplatePath,
|
minify: buildOptions.html.minify,
|
inject: true
|
}),
|
new VueSSRClientPlugin({
|
filename: `../server/${this.name}.manifest.json`
|
}),
|
new webpack__default['default'].DefinePlugin(this.env())
|
);
|
|
if (this.dev) {
|
// TODO: webpackHotUpdate is not defined: https://github.com/webpack/webpack/issues/6693
|
plugins.push(new webpack__default['default'].HotModuleReplacementPlugin());
|
}
|
|
// Webpack Bundle Analyzer
|
// https://github.com/webpack-contrib/webpack-bundle-analyzer
|
if (!this.dev && buildOptions.analyze) {
|
const statsDir = path__default['default'].resolve(buildDir, 'stats');
|
|
plugins.push(new BundleAnalyzer__default['default'].BundleAnalyzerPlugin(Object.assign({
|
analyzerMode: 'static',
|
defaultSizes: 'gzip',
|
generateStatsFile: true,
|
openAnalyzer: !buildOptions.quiet,
|
reportFilename: path__default['default'].resolve(statsDir, `${this.name}.html`),
|
statsFilename: path__default['default'].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__default['default'].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 {
|
options: { router, buildDir },
|
buildOptions: { hotMiddleware, quiet, friendlyErrors }
|
} = this.buildContext;
|
|
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__default['default'].stringify(hotMiddlewareClientOptions);
|
|
// Entry points
|
config.entry = Object.assign({}, config.entry, {
|
app: [path__default['default'].resolve(buildDir, 'client.js')]
|
});
|
|
// Add HMR support
|
if (this.dev) {
|
config.entry.app.unshift(
|
// https://github.com/webpack-contrib/webpack-hot-middleware/issues/53#issuecomment-162823945
|
this.resolveModule('eventsource-polyfill'),
|
// https://github.com/glenjamin/webpack-hot-middleware#config
|
`${this.resolveModule('webpack-hot-middleware/client')}?${hotMiddlewareClientOptionsStr}`
|
);
|
}
|
|
// Add friendly error plugin
|
if (this.dev && !quiet && friendlyErrors) {
|
config.plugins.push(
|
new FriendlyErrorsWebpackPlugin__default['default']({
|
clearConsole: false,
|
reporter: 'consola',
|
logLevel: 'WARNING'
|
})
|
);
|
}
|
|
return config
|
}
|
}
|
|
class WebpackModernConfig extends WebpackClientConfig {
|
constructor (...args) {
|
super(...args);
|
this.name = 'modern';
|
this.isModern = true;
|
}
|
|
env () {
|
return Object.assign(super.env(), {
|
'process.modern': true
|
})
|
}
|
}
|
|
class VueSSRServerPlugin {
|
constructor (options = {}) {
|
this.options = Object.assign({
|
filename: null
|
}, options);
|
}
|
|
apply (compiler) {
|
validate(compiler);
|
|
compiler.hooks.emit.tapAsync('vue-server-plugin', (compilation, cb) => {
|
const stats = compilation.getStats().toJson();
|
const [entryName] = Object.keys(stats.entrypoints);
|
const entryInfo = stats.entrypoints[entryName];
|
|
if (!entryInfo) {
|
// #5553
|
return cb()
|
}
|
|
const entryAssets = entryInfo.assets.filter(isJS);
|
|
if (entryAssets.length > 1) {
|
throw new Error(
|
'Server-side bundle should have one single entry file. ' +
|
'Avoid using CommonsChunkPlugin in the server config.'
|
)
|
}
|
|
const [entry] = entryAssets;
|
if (!entry || typeof entry !== 'string') {
|
throw new Error(
|
`Entry "${entryName}" not found. Did you specify the correct entry option?`
|
)
|
}
|
|
const bundle = {
|
entry,
|
files: {},
|
maps: {}
|
};
|
|
stats.assets.forEach((asset) => {
|
if (isJS(asset.name)) {
|
const queryPart = extractQueryPartJS(asset.name);
|
if (queryPart !== undefined) {
|
bundle.files[asset.name] = asset.name.replace(queryPart, '');
|
} else {
|
bundle.files[asset.name] = asset.name;
|
}
|
} else if (asset.name.match(/\.js\.map$/)) {
|
bundle.maps[asset.name.replace(/\.map$/, '')] = asset.name;
|
} else {
|
// Do not emit non-js assets for server
|
delete compilation.assets[asset.name];
|
}
|
});
|
|
const src = JSON.stringify(bundle, null, 2);
|
|
compilation.assets[this.options.filename] = {
|
source: () => src,
|
size: () => src.length
|
};
|
|
cb();
|
});
|
}
|
}
|
|
const nativeFileExtensions = [
|
'.json',
|
'.js'
|
];
|
|
class WebpackServerConfig extends WebpackBaseConfig {
|
constructor (...args) {
|
super(...args);
|
this.name = 'server';
|
this.isServer = true;
|
}
|
|
get devtool () {
|
return 'cheap-module-source-map'
|
}
|
|
get externalsAllowlist () {
|
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__default['default'].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.buildContext.buildOptions;
|
|
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.buildContext.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 webpack.DefinePlugin(this.env())
|
);
|
|
const { serverURLPolyfill } = this.buildContext.options.build;
|
|
if (serverURLPolyfill) {
|
plugins.push(new webpack.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__default['default'].resolve(this.buildContext.options.buildDir, 'server.js')]
|
}),
|
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.buildContext.buildOptions.standalone) {
|
this.buildContext.options.modulesDir.forEach((dir) => {
|
if (fs__default['default'].existsSync(dir)) {
|
config.externals.push(
|
nodeExternals__default['default']({
|
allowlist: this.externalsAllowlist,
|
modulesDir: dir
|
})
|
);
|
}
|
});
|
}
|
|
return config
|
}
|
}
|
|
const WebpackConfigs = /*#__PURE__*/Object.freeze({
|
__proto__: null,
|
client: WebpackClientConfig,
|
modern: WebpackModernConfig,
|
server: WebpackServerConfig
|
});
|
|
const glob = pify__default['default'](Glob__default['default']);
|
|
class WebpackBundler {
|
constructor (buildContext) {
|
this.buildContext = buildContext;
|
|
// Class fields
|
this.compilers = [];
|
this.compilersWatching = [];
|
this.devMiddleware = {};
|
this.hotMiddleware = {};
|
|
// Bind middleware to self
|
this.middleware = this.middleware.bind(this);
|
|
// Initialize shared MFS for dev
|
if (this.buildContext.options.dev) {
|
this.mfs = new AsyncMFS();
|
}
|
}
|
|
getWebpackConfig (name) {
|
const Config = WebpackConfigs[name.toLowerCase()]; // eslint-disable-line import/namespace
|
if (!Config) {
|
throw new Error(`Unsupported webpack config ${name}`)
|
}
|
const config = new Config(this);
|
return config.config()
|
}
|
|
async build () {
|
const { options } = this.buildContext;
|
|
const webpackConfigs = [
|
this.getWebpackConfig('Client')
|
];
|
|
if (options.modern) {
|
webpackConfigs.push(this.getWebpackConfig('Modern'));
|
}
|
|
if (options.build.ssr) {
|
webpackConfigs.push(this.getWebpackConfig('Server'));
|
}
|
|
await this.buildContext.nuxt.callHook('webpack:config', webpackConfigs);
|
|
// Check styleResource existence
|
const { styleResources } = this.buildContext.options.build;
|
if (styleResources && Object.keys(styleResources).length) {
|
consola__default['default'].warn(
|
'Using styleResources without the @nuxtjs/style-resources is not suggested and can lead to severe performance issues.',
|
'Please use https://github.com/nuxt-community/style-resources-module'
|
);
|
for (const ext of Object.keys(styleResources)) {
|
await Promise.all(utils.wrapArray(styleResources[ext]).map(async (p) => {
|
const styleResourceFiles = await glob(path__default['default'].resolve(this.buildContext.options.rootDir, p));
|
|
if (!styleResourceFiles || styleResourceFiles.length === 0) {
|
throw new Error(`Style Resource not found: ${p}`)
|
}
|
}));
|
}
|
}
|
|
// Configure compilers
|
this.compilers = webpackConfigs.map((config) => {
|
const compiler = webpack__default['default'](config);
|
|
// In dev, write files in memory FS
|
if (options.dev) {
|
compiler.outputFileSystem = this.mfs;
|
}
|
|
return compiler
|
});
|
|
// Warm up perfLoader before build
|
if (options.build.parallel) {
|
consola__default['default'].info('Warming up worker pools');
|
PerfLoader.warmupAll({ dev: options.dev, resolveModule: id => utils.tryResolve(id, __filename) || id });
|
consola__default['default'].success('Worker pools ready');
|
}
|
|
// Start Builds
|
const runner = options.dev ? utils.parallel : utils.sequence;
|
|
await runner(this.compilers, compiler => this.webpackCompile(compiler));
|
}
|
|
async webpackCompile (compiler) {
|
const { name } = compiler.options;
|
const { nuxt, options } = this.buildContext;
|
|
await nuxt.callHook('build:compile', { name, compiler });
|
|
// Load renderer resources after build
|
compiler.hooks.done.tap('load-resources', async (stats) => {
|
await nuxt.callHook('build:compiled', {
|
name,
|
compiler,
|
stats
|
});
|
|
// Reload renderer
|
await nuxt.callHook('build:resources', this.mfs);
|
});
|
|
// --- Dev Build ---
|
if (options.dev) {
|
// Client Build, watch is started by dev-middleware
|
if (['client', 'modern'].includes(name)) {
|
return new Promise((resolve, reject) => {
|
compiler.hooks.done.tap('nuxt-dev', () => resolve());
|
return this.webpackDev(compiler)
|
})
|
}
|
|
// Server, build and watch for changes
|
return new Promise((resolve, reject) => {
|
const watching = compiler.watch(options.watchers.webpack, (err) => {
|
if (err) {
|
return reject(err)
|
}
|
resolve();
|
});
|
|
watching.close = pify__default['default'](watching.close);
|
this.compilersWatching.push(watching);
|
})
|
}
|
|
// --- Production Build ---
|
compiler.run = pify__default['default'](compiler.run);
|
const stats = await compiler.run();
|
|
if (stats.hasErrors()) {
|
// non-quiet mode: errors will be printed by webpack itself
|
const error = new Error('Nuxt build error');
|
if (options.build.quiet === true) {
|
error.stack = stats.toString('errors-only');
|
}
|
throw error
|
}
|
|
// Await for renderer to load resources (programmatic, tests and generate)
|
await nuxt.callHook('build:resources');
|
}
|
|
async webpackDev (compiler) {
|
consola__default['default'].debug('Creating webpack middleware...');
|
|
const { name } = compiler.options;
|
const buildOptions = this.buildContext.options.build;
|
const { client, ...hotMiddlewareOptions } = buildOptions.hotMiddleware || {};
|
|
compiler.options.watchOptions = this.buildContext.options.watchers.webpack;
|
compiler.hooks.infrastructureLog.tap('webpack-dev-middleware-log', (name) => {
|
if (name === 'webpack-dev-middleware') {
|
return false
|
}
|
return undefined
|
});
|
|
// Create webpack dev middleware
|
this.devMiddleware[name] = pify__default['default'](
|
webpackDevMiddleware__default['default'](
|
compiler, {
|
outputFileSystem: compiler.outputFileSystem,
|
...buildOptions.devMiddleware
|
})
|
);
|
|
this.devMiddleware[name].close = pify__default['default'](this.devMiddleware[name].close);
|
|
this.compilersWatching.push(this.devMiddleware[name].context.watching);
|
|
this.hotMiddleware[name] = pify__default['default'](
|
webpackHotMiddleware__default['default'](
|
compiler, {
|
log: false,
|
heartbeat: 10000,
|
path: `/__webpack_hmr/${name}`,
|
...hotMiddlewareOptions
|
})
|
);
|
|
// Register devMiddleware on server
|
await this.buildContext.nuxt.callHook('server:devMiddleware', this.middleware);
|
}
|
|
async middleware (req, res, next) {
|
const name = utils.isModernRequest(req, this.buildContext.options.modern) ? 'modern' : 'client';
|
|
if (this.devMiddleware && this.devMiddleware[name]) {
|
const { url } = req;
|
req.url = req.originalUrl || req.url;
|
await this.devMiddleware[name](req, res);
|
req.url = url;
|
}
|
|
if (this.hotMiddleware && this.hotMiddleware[name]) {
|
await this.hotMiddleware[name](req, res);
|
}
|
|
next();
|
}
|
|
async unwatch () {
|
await Promise.all(this.compilersWatching.map(watching => watching.close()));
|
}
|
|
async close () {
|
if (this.__closed) {
|
return
|
}
|
this.__closed = true;
|
|
// Unwatch
|
await this.unwatch();
|
|
// Stop webpack middleware
|
for (const devMiddleware of Object.values(this.devMiddleware)) {
|
await devMiddleware.close();
|
}
|
|
// Cleanup MFS
|
if (this.mfs) {
|
delete this.mfs.data;
|
delete this.mfs;
|
}
|
|
// Cleanup more resources
|
delete this.compilers;
|
delete this.compilersWatching;
|
delete this.devMiddleware;
|
delete this.hotMiddleware;
|
}
|
|
forGenerate () {
|
this.buildContext.target = utils.TARGETS.static;
|
}
|
}
|
|
exports.BundleBuilder = WebpackBundler;
|