Home Reference Source

src/plugin.js

/**
 * It updates targets Babel configuration in order to add support for JSX.
 */
class ProjextReactPlugin {
  /**
   * Class constructor.
   */
  constructor() {
    /**
     * The name of the reducer event the service will listen for in order to exclude React packages
     * from the bundle when the target is Node or when is a library for the browser.
     * @type {string}
     * @access protected
     * @ignore
     */
    this._externalSettingsEventName = 'rollup-external-plugin-settings-configuration';
    /**
     * The list of React packages that should never end up on the bundle. For browser targets,
     * this is only if the target is also a library.
     * @type {Array}
     * @access protected
     * @ignore
     */
    this._externalModules = [
      'react',
      'react-dom',
      'react-dom/server',
    ];
    /**
     * The name of the reducer event the service will listen for in order to add support for JSX
     * when a target is bundled.
     * @type {string}
     * @access protected
     * @ignore
     */
    this._babelConfigurationEvent = 'babel-configuration';
    /**
     * The name of the Babel preset required to add support for React's JSX.
     * @type {string}
     * @access protected
     * @ignore
     */
    this._babelPreset = '@babel/preset-react';
    /**
     * The required value a target `framework` setting needs to have in order for the service to
     * take action.
     * @type {string}
     * @access protected
     * @ignore
     */
    this._frameworkProperty = 'react';
  }
  /**
   * This is the method called when the plugin is loaded by projext. It setups all the listeners
   * for the events the plugin needs to intercept in order to add support for JSX.
   * It also listens for the event that defines the external dependencies, because if the
   * target type is Node or is a library, it should include the React packages as externals.
   * @param {Projext} app The projext main container.
   */
  register(app) {
    // Get the `events` service to listen for the events.
    const events = app.get('events');
    // Get the `babelHelper` to send to the method that adds support for JSX.
    const babelHelper = app.get('babelHelper');
    // Add the listener for the target Babel configuration.
    events.on(this._babelConfigurationEvent, (configuration, target) => (
      this._updateBabelConfiguration(configuration, target, babelHelper)
    ));
    // Add the listener for the external plugin settings event.
    events.on(this._externalSettingsEventName, (settings, params) => (
      this._updateExternals(settings, params.target)
    ));
  }
  /**
   * This method gets called when projext reduces a target Babel configuration. The method will
   * validate the target settings and add the Babel preset needed for JSX.
   * @param {Object}      currentConfiguration The current Babel configuration for the target.
   * @param {Target}      target               The target information.
   * @param {BabelHelper} babelHelper          To update the target configuration and add the
   *                                           required preset and plugin.
   * @return {Object} The updated configuration.
   * @access protected
   * @ignore
   */
  _updateBabelConfiguration(currentConfiguration, target, babelHelper) {
    let updatedConfiguration;
    if (target.framework === this._frameworkProperty) {
      updatedConfiguration = babelHelper.addPreset(currentConfiguration, this._babelPreset);
    } else {
      updatedConfiguration = currentConfiguration;
    }

    return updatedConfiguration;
  }
  /**
   * This method gets called when the Rollup plugin reduces the list of modules that should be
   * handled as external dependencies. The method validates the target settings and if it's a
   * Node target or a browser library, it pushes the React packages to the list.
   * @param {Object} currentSettings          The settings for external dependencies.
   * @param {Array}  currentSettings.external The list of dependencies that should be handled as
   *                                          external.
   * @param {Target} target                   The target information.
   * @return {Object} The updated settings.
   * @access protected
   * @ignore
   */
  _updateExternals(currentSettings, target) {
    let updatedSettings;
    if (
      target.framework === this._frameworkProperty &&
      (target.is.node || target.library)
    ) {
      updatedSettings = {
        external: currentSettings.external.slice(),
      };
      updatedSettings.external.push(...this._externalModules);
    } else {
      updatedSettings = currentSettings;
    }

    return updatedSettings;
  }
}

module.exports = ProjextReactPlugin;