Home Reference Source

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,
};