spaceout/site/frontend/node_modules/webpack-cli/lib/groups/resolveConfig.js

252 lines
7.4 KiB
JavaScript

const { existsSync } = require('fs');
const { resolve, extname } = require('path');
const webpackMerge = require('webpack-merge');
const { extensions, jsVariants } = require('interpret');
const rechoir = require('rechoir');
const logger = require('../utils/logger');
// Order defines the priority, in increasing order
// example - config file lookup will be in order of .webpack/webpack.config.development.js -> webpack.config.development.js -> webpack.config.js
const DEFAULT_CONFIG_LOC = [
'webpack.config',
'webpack.config.dev',
'webpack.config.development',
'webpack.config.prod',
'webpack.config.production',
'.webpack/webpack.config',
'.webpack/webpack.config.none',
'.webpack/webpack.config.dev',
'.webpack/webpack.config.development',
'.webpack/webpack.config.prod',
'.webpack/webpack.config.production',
'.webpack/webpackfile',
];
const modeAlias = {
production: 'prod',
development: 'dev',
};
let opts = {
outputOptions: {},
options: {},
};
// Return a list of default configs in various formats
const getDefaultConfigFiles = () => {
return DEFAULT_CONFIG_LOC.map((filename) => {
// Since .cjs is not available on interpret side add it manually to default config extension list
return [...Object.keys(extensions), '.cjs'].map((ext) => {
return {
path: resolve(filename + ext),
ext: ext,
module: extensions[ext],
};
});
}).reduce((a, i) => a.concat(i), []);
};
const getConfigInfoFromFileName = (filename) => {
const ext = extname(filename);
// since we support only one config for now
const allFiles = [filename];
// return all the file metadata
return allFiles
.map((file) => {
return {
path: resolve(file),
ext: ext,
module: extensions[ext] || null,
};
})
.filter((e) => existsSync(e.path));
};
// Reads a config file given the config metadata
const requireConfig = (configModule) => {
const extension = Object.keys(jsVariants).find((t) => configModule.ext.endsWith(t));
if (extension) {
rechoir.prepare(extensions, configModule.path, process.cwd());
}
let config = require(configModule.path);
if (config.default) {
config = config.default;
}
return { config, path: configModule.path };
};
// Responsible for reading user configuration files
// else does a default config lookup and resolves it.
const resolveConfigFiles = async (args) => {
const { config, mode } = args;
if (config && config.length > 0) {
const resolvedOptions = [];
const finalizedConfigs = config.map(async (webpackConfig) => {
const configPath = resolve(webpackConfig);
const configFiles = getConfigInfoFromFileName(configPath);
if (!configFiles.length) {
logger.error(`The specified config file doesn't exist in ${configPath}`);
process.exit(2);
}
const foundConfig = configFiles[0];
const resolvedConfig = requireConfig(foundConfig);
return finalize(resolvedConfig, args);
});
// resolve all the configs
for await (const resolvedOption of finalizedConfigs) {
if (Array.isArray(resolvedOption.options)) {
resolvedOptions.push(...resolvedOption.options);
} else {
resolvedOptions.push(resolvedOption.options);
}
}
opts['options'] = resolvedOptions.length > 1 ? resolvedOptions : resolvedOptions[0] || {};
return;
}
// When no config is supplied, lookup for default configs
const defaultConfigFiles = getDefaultConfigFiles();
const tmpConfigFiles = defaultConfigFiles.filter((file) => {
return existsSync(file.path);
});
const configFiles = tmpConfigFiles.map(requireConfig);
if (configFiles.length) {
const defaultConfig = configFiles.find((p) => p.path.includes(mode) || p.path.includes(modeAlias[mode]));
if (defaultConfig) {
opts = await finalize(defaultConfig, args, true);
return;
}
const foundConfig = configFiles.pop();
opts = await finalize(foundConfig, args, true);
return;
}
};
// Given config data, determines the type of config and
// returns final config
const finalize = async (moduleObj, args, isDefaultConfig = false) => {
const { env, configName } = args;
const newOptionsObject = {
outputOptions: {},
options: {},
};
if (!moduleObj) {
return newOptionsObject;
}
if (isDefaultConfig) {
newOptionsObject.outputOptions.defaultConfig = moduleObj.path;
}
const config = moduleObj.config;
const isMultiCompilerMode = Array.isArray(config);
const rawConfigs = isMultiCompilerMode ? config : [config];
let configs = [];
const allConfigs = await Promise.all(
rawConfigs.map(async (rawConfig) => {
const isPromise = typeof rawConfig.then === 'function';
if (isPromise) {
rawConfig = await rawConfig;
}
// `Promise` may return `Function`
if (typeof rawConfig === 'function') {
// when config is a function, pass the env from args to the config function
rawConfig = await rawConfig(env, args);
}
return rawConfig;
}),
);
for (const singleConfig of allConfigs) {
if (Array.isArray(singleConfig)) {
configs.push(...singleConfig);
} else {
configs.push(singleConfig);
}
}
if (configName) {
const foundConfigNames = [];
configs = configs.filter((options) => {
const found = configName.includes(options.name);
if (found) {
foundConfigNames.push(options.name);
}
return found;
});
if (foundConfigNames.length !== configName.length) {
// Configuration with name "test" was not found.
logger.error(
configName
.filter((name) => !foundConfigNames.includes(name))
.map((configName) => `Configuration with name "${configName}" was not found.`)
.join('\n'),
);
process.exit(2);
}
}
if (configs.length === 0) {
logger.error('No configurations found');
process.exit(2);
}
newOptionsObject['options'] = configs.length > 1 ? configs : configs[0];
return newOptionsObject;
};
const resolveConfigMerging = async (args) => {
const { merge } = args;
if (merge) {
// Get the current configuration options
const { options: configOptions } = opts;
// we can only merge when there are multiple configurations
// either by passing multiple configs by flags or passing a
// single config exporting an array
if (!Array.isArray(configOptions)) {
logger.error('At least two configurations are required for merge.');
process.exit(2);
}
// We return a single config object which is passed to the compiler
opts['options'] = configOptions.reduce((currentConfig, mergedConfig) => webpackMerge(currentConfig, mergedConfig), {});
}
};
module.exports = async (args) => {
await resolveConfigFiles(args);
await resolveConfigMerging(args);
return opts;
};