Home Reference Source

src/services/runner/targets.js

const fs = require('fs-extra');
const { provider } = require('jimple');
/**
 * This service is in charge of providing and validating the targets stored on the runner file.
 */
class Targets {
  /**
   * Class constructor.
   * @param {Object}        packageInfo   The project's `package.json`, necessary to get the
   *                                      project's name and use it as the name of the default
   *                                      target.
   * @param {PathUtils}     pathUtils     To create the targets exeuction paths.
   * @param {ProjextPlugin} projextPlugin To check if projext is present or not.
   * @param {RunnerFile}    runnerFile    To get the targets information.
   */
  constructor(packageInfo, pathUtils, projextPlugin, runnerFile) {
    /**
     * The information of the project's `package.json`.
     * @type {Object}
     */
    this.packageInfo = packageInfo;
    /**
     * A local reference for the `pathUtils` service.
     * @type {PathUtils}
     */
    this.pathUtils = pathUtils;
    /**
     * A local reference for the `projextPlugin` service.
     * @type {ProjextPlugin}
     */
    this.projextPlugin = projextPlugin;
    /**
     * A local reference for the `runnerFile` service.
     * @type {RunnerFile}
     */
    this.runnerFile = runnerFile;
  }
  /**
   * Get a target information by its name.
   * @param {string} name The target name.
   * @return {Target}
   * @throws {Error} If the target information is not on the runner file.
   */
  getTarget(name) {
    const target = this.runnerFile.read().targets[name];
    if (!target) {
      throw new Error(
        'The target information is not on the runner file, you first ned to build it'
      );
    }

    return this._normalizeTarget(target);
  }
  /**
   * Returns the target with the name of project (specified on the `package.json`) and if there's
   * no target with that name, then the first one, using a list of the targets name on alphabetical
   * order.
   * @return {Target}
   * @throws {Error} If the project has no targets
   */
  getDefaultTarget() {
    const { targets } = this.runnerFile.read();
    const names = Object.keys(targets).sort();
    let target;
    if (names.length) {
      const { name: projectName } = this.packageInfo;
      target = targets[projectName] || targets[names[0]];
    } else {
      throw new Error('The project doesn\'t have any targets or none has been built yet');
    }

    return this._normalizeTarget(target);
  }
  /**
   * Validate a target information.
   * @param  {?string} name The target name.
   * @return {boolean}
   * @throws {Error} If the runner file doesn't exist.
   * @throws {Error} If the target executable doesn't exist.
   */
  validate(name) {
    // Only validate the target if the plugin is on a production environment.
    if (!this.projextPlugin.isInstalled()) {
      // Check if the runner file exists.
      if (!this.runnerFile.exists()) {
        throw new Error('The runner file doesn\'t exist, you first need to build a target');
      }

      const target = name ? this.getTarget(name) : this.getDefaultTarget();
      // Check if the target executable exists.
      if (!fs.pathExistsSync(target.exec)) {
        throw new Error(`The target executable doesn't exist: ${target.exec}`);
      }
    }

    return true;
  }
  /**
   * Add the execution path (`exec`) to a {@link Target}.
   * @param {Target} target The target for which the execution path will be generated for.
   * @return {Target}
   */
  _normalizeTarget(target) {
    return Object.assign({
      exec: this.pathUtils.join(target.path),
    }, target);
  }
}
/**
 * The service provider that once registered on the app container will set an instance of
 * `Targets` as the `targets` service.
 * @example
 * // Register it on the container
 * container.register(targets);
 * // Getting access to the service instance
 * const targets = container.get('targets');
 * @type {Provider}
 */
const targets = provider((app) => {
  app.set('targets', () => new Targets(
    app.get('packageInfo'),
    app.get('pathUtils'),
    app.get('projextPlugin'),
    app.get('runnerFile')
  ));
});

module.exports = {
  Targets,
  targets,
};