kubosho/kotori

View on GitHub
src/build.js

Summary

Maintainability
A
2 hrs
Test Coverage
import fs from "fs";
import through from "through2";
import autoprefixer from "autoprefixer";
import CleanCSS from "clean-css";
import { cssfmt } from "cssfmt";
import postcss from "postcss";
import reporter from "postcss-reporter";
import stylelint from "stylelint";
import Config from "./config";
import Stats from "./stats";

/**
 * Kotori build based on config
 * @param {Object} config - Kotori config (object or JSON)
 */
export default class Build {
  constructor(conf) {
    const config = new Config();

    this.config = config.parse(conf) || config.load();

    if (this.config.env !== void 0) {
      this.config.environment = this.config.env;
    }
  }

  /**
   * through2.obj() wrapper
   * @returns {Stream} Transform stream
   */
  transform() {
    return through.obj((file, encode, callback) => {
      this.transformCore(this.config, file, callback)
    });
  }

  /**
   * Operate on written data, then read the result of each file
   * @param {Object} config - Kotori config (object or JSON)
   * @param {File} file - chunk
   * @param {Function} callback - callback function
   * @private
   */
  transformCore(config, file, callback) {
    if (file.isNull()) {
      callback(null, file);
      return;
    }

    if (file.isStream()) {
      callback(Error("Stream is not supported"));
      return;
    }

    const postcssPlugins = activatePostCSSPlugins(config);
    const processor = postcss(postcssPlugins)
      .process(file.contents.toString(), {
        map : file.sourceMap ? {annotation: false} : false,
        from: file.path,
        to  : file.path
      });

    processor
      .then((res) => {
        let contents = res.css;

        if (/product(?:ion)?/.test(config.environment)) {
          const minified = new CleanCSS().minify(res.css);
          contents = minified.styles;
        }

        file.contents = new Buffer(contents);

        if (config.stats) {
          const stats = new Stats(file.path, config.stats);

          stats.parseCSS()
            .then((result) => {
              return stats.writeStats(result);
            })
            .then(() => {
              callback(null, file);
            })
            .catch((err) => {
              callback(err);
            });
        } else {
          callback(null, file);
        }
      })
      .catch((err) => {
        callback(err);
      });
  }
}

/**
 * Activating PostCSS plugins
 * @param {Object} config - Kotori config object
 * @returns {Function[]} PostCSS plugins list
 * @private
 */
function activatePostCSSPlugins(config) {
  const plugins = [];

  if (config.lintRules || config.lintRules !== "") {
    // TODO: Throw easy-to-understand error message
    // incorrect path? (ex. "lintRules": "aaa")
    // typo? (ex. "lintRules": "stylelint-config-suitcs")
    const lintRules = require(config.lintRules);

    if (lintRules.rules) {
      plugins.push(stylelint(lintRules));
    } else {
      throw new Error("Illegal lint rule: \"rules\" property is not found.");
    }
  }

  if (config.browsers && config.browsers !== "") {
    plugins.push(autoprefixer(config.browsers));
  }

  plugins.push(cssfmt);
  plugins.push(reporter);

  return plugins;
}