packages/mastery/build/components/BuildCommand.js

import {
  Spinner,
} from 'cli-spinner';
import _ from 'lodash';
import del from 'del';
import fs from 'fs';
import gulp from 'gulp';
import gulpBabel from 'gulp-babel';
import gulpCopy from 'gulp-copy';
import gulpUglify from 'gulp-uglify';
import path from 'path';

const ColorizeText = requireF('services/ColorizeText');
const {
  getServerName,
  getTargetBabelRecipe,
} = requireF('services/CommonServices');
const {
  validateRootDir,
} = requireF('services/CommonValidations');

/**
 * The main class that handles the 'build' command execution.
 *
 * @export
 * @class BuildCommand
 * @property {string} BUILD_DIR Build directory relative path
 * @property {string} JS_GLOB A glob pattern where javascript files are
 * @property {Object} BABEL_RECIPES Babel configuration for transforming es6 MasteryJS source codes
 * @property {string[]} COPY_GLOB Glob patterns where non javascript files are
 * @property {string} MK_INTRO The translation key of 'initializing' phase message
 * @property {string} MK_BUILD_SUCCESS The translation key of 'build success' phase message
 * @property {string} MK_CLEANING The translation key of 'cleaning' phase message
 * @property {string} MK_COPYING_NON_JS The translation key of 'copying non javascript files' phase message
 * @property {string} MK_COPYING_JS The translation key of 'copying javascript files' phase message
 * @property {string} MK_WRITING_RUN_CONF The translation key of 'writing mastery.run.js file' phase message
 */
export default class BuildCommand {
  BUILD_DIR = 'build';
  JS_GLOB = 'src/**/*.js';
  COPY_GLOB = [
    'src/config/**/*.json',
    'src/core/config/**/*.json',
    'src/core/components/**/*.config.json',
    'src/main/components/**/*.config.json',
    'src/core/locales/**/*.json',
    'src/main/locales/**/*.json',
  ]
  BABEL_RECIPES = {
    presets: [
      'es2015',
      'stage-3',
    ],
    plugins: [
      'add-module-exports',
      'transform-runtime',
      'transform-decorators-legacy',
      'transform-class-properties',
      'transform-es2015-classes',
      'transform-flow-strip-types',
    ],
  }
  MK_INTRO = 'mastery.build.intro';
  MK_BUILD_SUCCESS = 'mastery.build.success';
  MK_CLEANING = 'mastery.build.cleaning';
  MK_COPYING_NON_JS = 'mastery.build.copying.nonJS';
  MK_COPYING_JS = 'mastery.build.copying.JS';
  MK_WRITING_RUN_CONF = 'mastery.build.writing.conf';

  /**
   * Copy none javascript files such as json configuration files, etc.
   *
   * @returns {Promise}
   */
  async copyNonJS() {
    const copyGlobs = _.map(
      _.castArray(this.COPY_GLOB), val => path.join(this.basePath, val),
    );
    await new Promise((resolve) => {
      gulp
        .src(copyGlobs)
        .pipe(gulpCopy(this.targetDir, {
          prefix: 1,
        }))
        .pipe(gulp.dest(this.targetDir))
        .on('end', resolve);
    });
  }

  /**
   * Copy javascript files -> transform using babel -> babili.
   *
   * @returns {Promise}
   */
  async copyJS() {
    const self = this;
    const jsGlobs = _.map(_.castArray(self.JS_GLOB), val => path.join(self.basePath, val));
    await new Promise((resolve) => {
      gulp
        .src(jsGlobs)
        .pipe(gulpBabel(getTargetBabelRecipe()))
        .pipe(gulpUglify())
        .pipe(gulp.dest(self.targetDir))
        .on('end', resolve);
    });
  }

  /**
   * Write mastery.run.json file, append 'name' key value from project name question answered by user before.
   */
  writeRunConf() {
    process.chdir(this.BUILD_DIR);
    const runConf = _.merge(constants.DEFAULT_RUN_CONF, {
      name: this.serverName,
    });
    fs.writeFileSync(constants.RUN_FILE, JSON.stringify({
      apps: runConf,
    }, null, 2));
  }

  /**
   * The main method to call another methods sequentially, including decorations output.
   *
   * @returns {Promise}
   */
  async execute() {
    validateRootDir();

    this.basePath = path.resolve('.');
    this.targetDir = path.join(this.basePath, this.BUILD_DIR);
    this.serverName = getServerName(this.basePath);

    console.log(ColorizeText.info(i18n.t(this.MK_INTRO, {
      serverName: this.serverName,
    })));
    console.log('');

    const loading = new Spinner(ColorizeText.info(i18n.t(this.MK_CLEANING)));
    loading.setSpinnerString(constants.DEFAULT_SPINNER);
    loading.start();

    del.sync(this.targetDir);

    loading.setSpinnerTitle(ColorizeText.info(i18n.t(this.MK_COPYING_NON_JS)));
    await this.copyNonJS();

    loading.setSpinnerTitle(ColorizeText.info(i18n.t(this.MK_COPYING_JS)));
    await this.copyJS();

    loading.setSpinnerTitle(ColorizeText.info(i18n.t(this.MK_WRITING_RUN_CONF)));
    this.writeRunConf();

    loading.stop(true);
    console.log(ColorizeText.info(i18n.t(this.MK_BUILD_SUCCESS, {
      serverName: this.serverName,
    })));
    console.log('');
  }
}