const ChainedMap = require('./ChainedMap'); const ChainedSet = require('./ChainedSet'); const Resolve = require('./Resolve'); const ResolveLoader = require('./ResolveLoader'); const Output = require('./Output'); const DevServer = require('./DevServer'); const Plugin = require('./Plugin'); const Module = require('./Module'); const Optimization = require('./Optimization'); const Performance = require('./Performance'); module.exports = class extends ChainedMap { constructor() { super(); this.devServer = new DevServer(this); this.entryPoints = new ChainedMap(this); this.module = new Module(this); this.node = new ChainedMap(this); this.optimization = new Optimization(this); this.output = new Output(this); this.performance = new Performance(this); this.plugins = new ChainedMap(this); this.resolve = new Resolve(this); this.resolveLoader = new ResolveLoader(this); this.extend([ 'amd', 'bail', 'cache', 'context', 'devtool', 'externals', 'loader', 'mode', 'parallelism', 'profile', 'recordsInputPath', 'recordsPath', 'recordsOutputPath', 'stats', 'target', 'watch', 'watchOptions', ]); } static toString(config, { verbose = false, configPrefix = 'config' } = {}) { // eslint-disable-next-line global-require const stringify = require('javascript-stringify'); return stringify( config, (value, indent, stringify) => { // improve plugin output if (value && value.__pluginName) { const prefix = `/* ${configPrefix}.plugin('${ value.__pluginName }') */\n`; const constructorExpression = value.__pluginPath ? // The path is stringified to ensure special characters are escaped // (such as the backslashes in Windows-style paths). `(require(${stringify(value.__pluginPath)}))` : value.__pluginConstructorName; if (constructorExpression) { // get correct indentation for args by stringifying the args array and // discarding the square brackets. const args = stringify(value.__pluginArgs).slice(1, -1); return `${prefix}new ${constructorExpression}(${args})`; } return ( prefix + stringify( value.__pluginArgs && value.__pluginArgs.length ? { args: value.__pluginArgs } : {} ) ); } // improve rule/use output if (value && value.__ruleNames) { const prefix = `/* ${configPrefix}.module.rule('${ value.__ruleNames[0] }')${value.__ruleNames .slice(1) .map(r => `.oneOf('${r}')`) .join('')}${ value.__useName ? `.use('${value.__useName}')` : `` } */\n`; return prefix + stringify(value); } // shorten long functions if (typeof value === 'function') { if (value.__expression) { return value.__expression; } if (!verbose && value.toString().length > 100) { return `function () { /* omitted long function */ }`; } } return stringify(value); }, 2 ); } entry(name) { return this.entryPoints.getOrCompute(name, () => new ChainedSet(this)); } plugin(name) { return this.plugins.getOrCompute(name, () => new Plugin(this, name)); } toConfig() { const entryPoints = this.entryPoints.entries() || {}; return this.clean( Object.assign(this.entries() || {}, { node: this.node.entries(), output: this.output.entries(), resolve: this.resolve.toConfig(), resolveLoader: this.resolveLoader.toConfig(), devServer: this.devServer.toConfig(), module: this.module.toConfig(), optimization: this.optimization.entries(), plugins: this.plugins.values().map(plugin => plugin.toConfig()), performance: this.performance.entries(), entry: Object.keys(entryPoints).reduce( (acc, key) => Object.assign(acc, { [key]: entryPoints[key].values() }), {} ), }) ); } toString(options) { return module.exports.toString(this.toConfig(), options); } merge(obj = {}, omit = []) { const omissions = [ 'node', 'output', 'resolve', 'resolveLoader', 'devServer', 'optimization', 'performance', 'module', ]; if (!omit.includes('entry') && 'entry' in obj) { Object.keys(obj.entry).forEach(name => this.entry(name).merge([].concat(obj.entry[name])) ); } if (!omit.includes('plugin') && 'plugin' in obj) { Object.keys(obj.plugin).forEach(name => this.plugin(name).merge(obj.plugin[name]) ); } omissions.forEach(key => { if (!omit.includes(key) && key in obj) { this[key].merge(obj[key]); } }); return super.merge(obj, [...omit, ...omissions, 'entry', 'plugin']); } };