Home Reference Source

src/services/building/engine.js

const path = require('path');
const { provider } = require('jimple');
/**
 * This build engine is in charge of generating the CLI commands and the configuration to bundle
 * a target using Rollup.
 */
class RollupBuildEngine {
  /**
   * @param {EnvironmentUtils}    environmentUtils    To load environment variables sent by the
   *                                                  CLI command to the configuration builder
   *                                                  method.
   * @param {Targets}             targets             To get a target information.
   * @param {RollupConfiguration} rollupConfiguration To generate a configuration for a target.
   * @param {RollupPluginInfo}    rollupPluginInfo    To get the path to the configuration file.
   */
  constructor(
    environmentUtils,
    targets,
    rollupConfiguration,
    rollupPluginInfo
  ) {
    /**
     * A local reference for the `environmentUtils` service.
     * @type {EnvironmentUtils}
     */
    this.environmentUtils = environmentUtils;
    /**
     * A local reference for the `targets` service.
     * @type {Targets}
     */
    this.targets = targets;
    /**
     * A local reference for the `rollupConfiguration` service.
     * @type {RollupConfiguration}
     */
    this.rollupConfiguration = rollupConfiguration;
    /**
     * A local reference for the plugin information.
     * @type {RollupPluginInfo}
     */
    this.rollupPluginInfo = rollupPluginInfo;
    /**
     * A dictionary of environment variables the service will include on the CLI command and
     * that will be retrieved when generating the configuration.
     * The keys are the purpose and the values the actual names of the variables.
     * @type {Object}
     * @property {string} target  The name of the target being builded.
     * @property {string} type    The intended build type: `development` or `production`.
     * @property {string} run     Whether or not to execute the target. This will be like a fake
     *                            boolean as the CLI doesn't support boolean variables, so its
     *                            value will be either `'true'` or `'false'`.
     * @property {string} watch   Whether or not to watch the target files. This will be like a
     *                            fake boolean as the CLI doesn't support boolean variables, so
     *                            its value will be either `'true'` or `'false'`.
     * @property {string} inspect Whether or not to enable the Node inspector. This will be like a
     *                            fake boolean as the CLI doesn't support boolean variables, so its
     *                            value will be either `'true'` or `'false'`.
     * @property {string} analyze Whether or not to enable the bundle analyzer. This will be like a
     *                            fake boolean as the CLI doesn't support boolean variables, so its
     *                            value will be either `'true'` or `'false'`.
     * @access protected
     * @ignore
     */
    this._envVars = {
      target: 'PXTRP_TARGET',
      type: 'PXTRP_TYPE',
      run: 'PXTRP_RUN',
      watch: 'PXTRP_WATCH',
      inspect: 'PXTRP_INSPECT',
      analyze: 'PXTRP_ANALYZE',
    };
  }
  /**
   * Get the CLI build command to bundle a target.
   * @param {Target}  target               The target information.
   * @param {string}  buildType            The intended build type: `development` or `production`.
   * @param {boolean} [forceRun=false]     Force the target to run even if the `runOnDevelopment`
   *                                       setting is `false`.
   * @param {boolean} [forceWatch=false]   Force Rollup to use the watch mode even if the `watch`
   *                                       setting for the required build type is set to `false`.
   * @param {boolean} [forceInspect=false] Enables the Node inspector even if the target setting
   *                                       is set to `false`.
   * @param {boolean} [forceAnalyze=false] Enables the bundle analyzer.
   * @return {string}
   */
  getBuildCommand(
    target,
    buildType,
    forceRun = false,
    forceWatch = false,
    forceInspect = false,
    forceAnalyze = false
  ) {
    const vars = this._getEnvVarsAsString({
      target: target.name,
      type: buildType,
      run: forceRun,
      watch: forceWatch,
      inspect: forceInspect,
      analyze: forceAnalyze,
    });

    const config = path.join(
      'node_modules',
      this.rollupPluginInfo.name,
      this.rollupPluginInfo.configuration
    );

    const optionsList = [];

    if ((buildType === 'development' && target.runOnDevelopment) || forceRun || forceWatch) {
      optionsList.push('--watch');
    }

    const options = optionsList.join(' ');

    return `${vars} rollup --config ${config} ${options}`;
  }
  /**
   * Get a Rollup configuration for a target.
   * @param {Target} target    The target configuration.
   * @param {string} buildType The intended build type: `development` or `production`.
   * @return {object}
   */
  getConfiguration(target, buildType) {
    return this.rollupConfiguration.getConfig(target, buildType);
  }
  /**
   * Get a Rollup configuration by reading the environment variables sent by the CLI command
   * `getBuildCommand` generates.
   * @return {object}
   * @throws {Error} If the environment variables are not present.
   */
  getRollupConfig() {
    const vars = this._getEnvVarsValues();
    if (!vars.target || !vars.type) {
      throw new Error('This file can only be run by using the `build` command');
    }

    const {
      type,
      run,
      inspect,
      analyze,
    } = vars;
    const target = Object.assign({}, this.targets.getTarget(vars.target));
    if (analyze === 'true') {
      target.analyze = true;
    } else {
      if (run === 'true') {
        target.runOnDevelopment = true;
        if (inspect === 'true') {
          target.inspect.enabled = true;
        }
      }

      if (vars.watch === 'true') {
        target.watch[type] = true;
      }
    }

    return this.getConfiguration(target, type);
  }
  /**
   * Given a dictionary with the environment variables purpose and values, this method generates
   * a string with the variables real names and values.
   * @example
   * console.log(_getEnvVarsAsString{
   *   target: 'my-target',
   *   type: 'development',
   * });
   * // will output `PXTRP_TARGET=my-target PXTRP_TYPE=development`
   * @param {object} values A dictionary with the purpose(alias) of the variables as keys.
   * @return {string}
   * @access protected
   * @ignore
   */
  _getEnvVarsAsString(values) {
    return Object.keys(values)
    .map((name) => `${this._envVars[name]}=${values[name]}`)
    .join(' ');
  }
  /**
   * Load the environment variables and returns them on a dictionary.
   * @return {object} The dictionary will have the purpose(alias) of the variables as keys.
   * @access protected
   * @ignore
   */
  _getEnvVarsValues() {
    const vars = {};
    Object.keys(this._envVars).forEach((name) => {
      vars[name] = this.environmentUtils.get(this._envVars[name]);
    });

    return vars;
  }
}
/**
 * The service provider that once registered on the app container will set an instance of
 * `RollupBuildEngine` as the `rollupBuildEngine` service.
 * @example
 * // Register it on the container
 * container.register(rollupBuildEngine);
 * // Getting access to the service instance
 * const rollupBuildEngine = container.get('rollupBuildEngine');
 * @type {Provider}
 */
const rollupBuildEngine = provider((app) => {
  app.set('rollupBuildEngine', () => new RollupBuildEngine(
    app.get('environmentUtils'),
    app.get('targets'),
    app.get('rollupConfiguration'),
    app.get('rollupPluginInfo')
  ));
});

module.exports = {
  RollupBuildEngine,
  rollupBuildEngine,
};