src/services/configurations/baseConfiguration.js
const { provider } = require('jimple');
const ConfigurationFile = require('../../abstracts/configurationFile');
/**
* The base configuration is at the top of the Webpack configurations level and it includes the
* settings for `resolve` and `module`.
* @extends {ConfigurationFile}
*/
class WebpackBaseConfiguration extends ConfigurationFile {
/**
* Class constructor.
* @param {Events} events To reduce the configuration.
* @param {Object} packageInfo The contents of the
* `package.json`, to get the
* dependencies names and use them
* as externals.
* @param {PathUtils} pathUtils Required by `ConfigurationFile`
* in order to build the path to
* the overwrite file.
* @param {WebpackPluginInfo} webpackPluginInfo To get the paths that should be
* defined as external dependencies.
* @param {WebpackRulesConfiguration} webpackRulesConfiguration To get all the configuration rules
* for the type of files that will be
* bundled.
*/
constructor(
events,
packageInfo,
pathUtils,
webpackPluginInfo,
webpackRulesConfiguration
) {
super(pathUtils, 'webpack/base.config.js');
/**
* A local reference for the `events` service.
* @type {Events}
*/
this.events = events;
/**
* The information of the `package.json`.
* @type {Object}
*/
this.packageInfo = packageInfo;
/**
* A local reference for the plugin information.
* @type {WebpackPluginInfo}
*/
this.webpackPluginInfo = webpackPluginInfo;
/**
* A local reference for the `webpackRulesConfiguration` service.
* @type {WebpackRulesConfiguration}
*/
this.webpackRulesConfiguration = webpackRulesConfiguration;
}
/**
* Create the configuration with the `resolve` and the `module` `rules`.
* This method uses the reducer events `webpack-base-configuration-for-node` or
* `webpack-base-configuration-for-browser`, depending on the target type, and
* `webpack-base-configuration'`. The events recieve the configuration, the `params` and
* expects a configuration on return.
* @param {WebpackConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {object}
*/
createConfig(params) {
const { rules } = this.webpackRulesConfiguration.getConfig(params);
const config = {
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
modules: ['./', 'node_modules'],
},
module: {
rules,
},
externals: this._getExternalDependencies(params),
};
const eventName = params.target.is.node ?
'webpack-base-configuration-for-node' :
'webpack-base-configuration-for-browser';
return this.events.reduce(
[eventName, 'webpack-base-configuration'],
config,
params
);
}
/**
* Generates the dictionary with the external dependencies that won't be included on the bundle.
* This method uses the reducer events `webpack-external-configuration-for-node` or
* `webpack-external-configuration-for-browser`, depending on the target type, and
* `webpack-external-configuration'`. The events recieve the dictionary, the `params` and
* expects another dictionary on return.
* @param {WebpackConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getExternalDependencies(params) {
const { target, buildType } = params;
// First define list that will have the name of the external dependencies.
const list = [];
// If the target supports the `excludeModules` setting...
if (target.excludeModules) {
// ...push all the modules which names are not `RegExp`s.
list.push(...target.excludeModules.filter((name) => name.match(/^[\w\d-_/]+$/)));
}
// If the target type is Node...
if (target.is.node) {
// ...push all the production dependencies.
list.push(...Object.keys(this.packageInfo.dependencies));
// ...push the plugin subpaths
list.push(...this.webpackPluginInfo.external.map((subpath) => (
`${this.webpackPluginInfo.name}/${subpath}`
)));
// And if the bundle is for development, push the dev dependencies too.
if (buildType === 'development') {
list.push(...Object.keys(this.packageInfo.devDependencies));
}
}
// Build the dictionary with the format webpack requires.
const externals = {};
list.forEach((name) => {
externals[name] = `commonjs ${name}`;
});
const eventName = target.is.node ?
'webpack-externals-configuration-for-node' :
'webpack-externals-configuration-for-browser';
return this.events.reduce(
[eventName, 'webpack-externals-configuration'],
externals,
params
);
}
}
/**
* The service provider that once registered on the app container will set an instance of
* `WebpackBaseConfiguration` as the `webpackBaseConfiguration` service.
* @example
* // Register it on the container
* container.register(webpackBaseConfiguration);
* // Getting access to the service instance
* const webpackBaseConfiguration = container.get('webpackBaseConfiguration');
* @type {Provider}
*/
const webpackBaseConfiguration = provider((app) => {
app.set('webpackBaseConfiguration', () => new WebpackBaseConfiguration(
app.get('events'),
app.get('packageInfo'),
app.get('pathUtils'),
app.get('webpackPluginInfo'),
app.get('webpackRulesConfiguration')
));
});
module.exports = {
WebpackBaseConfiguration,
webpackBaseConfiguration,
};