You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

229 lines
8.5 KiB

const webpackMerge = require('webpack-merge');
const GroupHelper = require('./utils/GroupHelper');
const { Compiler } = require('./utils/Compiler');
const { groups, core } = require('./utils/cli-flags');
const argParser = require('./utils/arg-parser');
const { outputStrategy } = require('./utils/merge-strategies');
const { toKebabCase } = require('./utils/helpers');
const assignFlagDefaults = require('./utils/flag-defaults');
// CLI arg resolvers
const handleConfigResolution = require('./groups/ConfigGroup');
const resolveMode = require('./groups/resolveMode');
const resolveStats = require('./groups/resolveStats');
const resolveOutput = require('./groups/resolveOutput');
const basicResolver = require('./groups/basicResolver');
const resolveAdvanced = require('./groups/resolveAdvanced');
class WebpackCLI extends GroupHelper {
constructor() {
super();
this.groupMap = new Map();
this.compilation = new Compiler();
this.compilerConfiguration = {};
this.outputConfiguration = {};
}
setMappedGroups(args, inlineOptions) {
Object.keys(args).forEach((key) => {
this.setGroupMap(toKebabCase(key), args[key], inlineOptions);
});
}
setGroupMap(key, val, inlineOptions) {
if (val === undefined) return;
const opt = inlineOptions.find((opt) => opt.name === key);
const groupName = opt.group;
if (this.groupMap.has(groupName)) {
const pushToMap = this.groupMap.get(groupName);
pushToMap.push({ [opt.name]: val });
} else {
this.groupMap.set(groupName, [{ [opt.name]: val }]);
}
}
/**
* Responsible for handling flags coming from webpack/webpack
* @private\
* @returns {void}
*/
_handleCoreFlags(parsedArgs) {
if (this.groupMap.has('core')) {
const coreFlags = this.groupMap.get('core');
// convert all the flags from map to single object
const coreConfig = coreFlags.reduce((allFlag, curFlag) => ({ ...allFlag, ...curFlag }), {});
const coreCliHelper = require('webpack').cli;
const coreCliArgs = coreCliHelper.getArguments();
// Merge the core flag config with the compilerConfiguration
coreCliHelper.processArguments(coreCliArgs, this.compilerConfiguration, coreConfig);
// Assign some defaults to core flags
}
const configWithDefaults = assignFlagDefaults(this.compilerConfiguration, parsedArgs);
this._mergeOptionsToConfiguration(configWithDefaults);
}
async _baseResolver(cb, parsedArgs, strategy) {
const resolvedConfig = await cb(parsedArgs, this.compilerConfiguration);
this._mergeOptionsToConfiguration(resolvedConfig.options, strategy);
this._mergeOptionsToOutputConfiguration(resolvedConfig.outputOptions);
}
/**
* Expose commander argParser
* @param {...any} args args for argParser
*/
argParser(...args) {
return argParser(...args);
}
getCoreFlags() {
return core;
}
/**
* Based on the parsed keys, the function will import and create
* a group that handles respective values
*
* @returns {void}
*/
resolveGroups() {
for (const [key] of this.groupMap.entries()) {
switch (key) {
case groups.HELP_GROUP: {
const HelpGroup = require('./groups/runHelp');
this.helpGroup = new HelpGroup();
break;
}
}
}
}
/**
* Responsible to override webpack options.
* @param {Object} options The options returned by a group helper
* @param {Object} strategy The strategy to pass to webpack-merge. The strategy
* is implemented inside the group helper
* @private
* @returns {void}
*/
_mergeOptionsToConfiguration(options, strategy) {
/**
* options where they differ per config use this method to apply relevant option to relevant config
* eg mode flag applies per config
*/
if (Array.isArray(options) && Array.isArray(this.compilerConfiguration)) {
this.compilerConfiguration = options.map((option, index) => {
const compilerConfig = this.compilerConfiguration[index];
if (strategy) {
return webpackMerge.strategy(strategy)(compilerConfig, option);
}
return webpackMerge(compilerConfig, option);
});
return;
}
/**
* options is an array (multiple configuration) so we create a new
* configuration where each element is individually merged
*/
if (Array.isArray(options)) {
this.compilerConfiguration = options.map((configuration) => {
if (strategy) {
return webpackMerge.strategy(strategy)(this.compilerConfiguration, configuration);
}
return webpackMerge(this.compilerConfiguration, configuration);
});
} else {
/**
* The compiler configuration is already an array, so for each element
* we merge the options
*/
if (Array.isArray(this.compilerConfiguration)) {
this.compilerConfiguration = this.compilerConfiguration.map((thisConfiguration) => {
if (strategy) {
return webpackMerge.strategy(strategy)(thisConfiguration, options);
}
return webpackMerge(thisConfiguration, options);
});
} else {
if (strategy) {
this.compilerConfiguration = webpackMerge.strategy(strategy)(this.compilerConfiguration, options);
} else {
this.compilerConfiguration = webpackMerge(this.compilerConfiguration, options);
}
}
}
}
/**
* Responsible for creating and updating the new output configuration
*
* @param {Object} options Output options emitted by the group helper
* @private
* @returns {void}
*/
_mergeOptionsToOutputConfiguration(options) {
if (options) {
this.outputConfiguration = Object.assign(this.outputConfiguration, options);
}
}
/**
* It receives a group helper, it runs and it merges its result inside
* the file result that will be passed to the compiler
*
* @param {GroupHelper?} groupHelper A group helper
* @private
* @returns {void}
*/
async _handleGroupHelper(groupHelper) {
if (groupHelper) {
const result = await groupHelper.run();
this._mergeOptionsToConfiguration(result.options, groupHelper.strategy);
this._mergeOptionsToOutputConfiguration(result.outputOptions);
}
}
/**
* It runs in a fancy order all the expected groups.
* Zero config and configuration goes first.
*
* The next groups will override existing parameters
* @returns {Promise<void>} A Promise
*/
async runOptionGroups(parsedArgs) {
await Promise.resolve()
.then(() => this._baseResolver(handleConfigResolution, parsedArgs))
.then(() => this._baseResolver(resolveMode, parsedArgs))
.then(() => this._baseResolver(resolveOutput, parsedArgs, outputStrategy))
.then(() => this._handleCoreFlags(parsedArgs))
.then(() => this._baseResolver(basicResolver, parsedArgs))
.then(() => this._baseResolver(resolveAdvanced, parsedArgs))
.then(() => this._baseResolver(resolveStats, parsedArgs))
.then(() => this._handleGroupHelper(this.helpGroup));
}
async processArgs(args, cliOptions) {
this.setMappedGroups(args, cliOptions);
this.resolveGroups(args);
const groupResult = await this.runOptionGroups(args);
return groupResult;
}
async getCompiler(args, cliOptions) {
await this.processArgs(args, cliOptions);
await this.compilation.createCompiler(this.compilerConfiguration);
return this.compilation.compiler;
}
async run(args, cliOptions) {
await this.processArgs(args, cliOptions);
await this.compilation.createCompiler(this.compilerConfiguration);
const webpack = await this.compilation.webpackInstance({
options: this.compilerConfiguration,
outputOptions: this.outputConfiguration,
});
return webpack;
}
}
module.exports = WebpackCLI;