'use strict';

import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _regeneratorRuntime from "@babel/runtime/regenerator";
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";

var fs = require('fs');

var path = require('path');

var mime = require('mime');

var unorm = require('unorm');

var _require = require('es6-promisify'),
    promisify = _require.promisify;

var Babel = require('@babel/core');

var readFile = promisify(fs.readFile);
var stat = promisify(fs.stat);

var toPOSIX = function toPOSIX(str) {
  return path.sep !== '/' ? str.split(path.sep).join('/') : str;
};

var types = require('./types.json');

var map = new Map();

var _arr = Object.keys(types);

for (var _i = 0; _i < _arr.length; _i++) {
  var key = _arr[_i];
  map.set(key, new RegExp(types[key], 'i'));
}

var validateType = function validateType(name, mimeType) {
  if (!map.has(name)) {
    throw new TypeError("".concat(name, " is not exist in validateType"));
  }

  return map.get(name).test(mimeType);
};

var babelConfig = {
  presets: [['@babel/preset-env', {
    targets: {
      ie: 11
    },
    useBuiltIns: false
  }]],
  plugins: [['@babel/plugin-transform-runtime', {
    corejs: false,
    helpers: true,
    regenerator: true,
    useESModules: false
  }]]
};

var MountFile =
/*#__PURE__*/
function () {
  function MountFile(mountName, mountDir, timestamp) {
    _classCallCheck(this, MountFile);

    // Copy props
    this.mountName = mountName;
    this.mountDir = mountDir;
    this.timestamp = timestamp;
  }

  _createClass(MountFile, [{
    key: "serialize",
    value: function () {
      var _serialize = _asyncToGenerator(
      /*#__PURE__*/
      _regeneratorRuntime.mark(function _callee() {
        var mountName, absolutePath, type, composed, text, _transpileCache;

        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                // Convert unicode
                mountName = unorm.nfc(this.mountName);
                absolutePath = path.resolve(this.mountDir, mountName);
                type = mime.getType(absolutePath) || 'text/plain';

                if (!validateType('text', type)) {
                  _context.next = 10;
                  break;
                }

                _context.next = 6;
                return readFile(absolutePath, 'utf8');

              case 6:
                text = _context.sent;
                composed = replaceEnvironmentVars(text); // 環境変数

                _context.next = 13;
                break;

              case 10:
                _context.next = 12;
                return readFile(absolutePath, 'base64');

              case 12:
                composed = _context.sent;

              case 13:
                if (validateType('javascript', type)) {
                  _transpileCache = Babel.transformSync(composed, babelConfig).code;
                }

                return _context.abrupt("return", {
                  name: toPOSIX(mountName),
                  type: type,
                  lastModified: Date.parse(this.timestamp),
                  composed: composed,
                  _transpileCache: _transpileCache,
                  options: {
                    isTrashed: false
                  }
                });

              case 15:
              case "end":
                return _context.stop();
            }
          }
        }, _callee, this);
      }));

      function serialize() {
        return _serialize.apply(this, arguments);
      }

      return serialize;
    }()
  }, {
    key: "serialized",
    get: function get() {
      if (!this._serialized) {
        // Serialize
        this._serialized = this.serialize();
      }

      return this._serialized;
    }
  }]);

  return MountFile;
}();

module.exports =
/*#__PURE__*/
function () {
  function FeelesWebpackPlugin(params) {
    _classCallCheck(this, FeelesWebpackPlugin);

    params = Object.assign({
      paths: ['mount'],
      output: 'index.json',
      ignore: /[]/,
      // eslint-disable-line
      debug: false
    }, params);
    this.output = params.output;
    this.ignore = params.ignore;
    this.debug = params.debug;
    this.cache = new Map(); // { [mountName]: [<MountFile>] }

    this.mountDirs = params.paths.map(function (s) {
      return path.resolve(s);
    });
    this.priorityOrders = new Map(); // { [mountDir]: [order] }

    var _iteratorNormalCompletion = true;
    var _didIteratorError = false;
    var _iteratorError = undefined;

    try {
      for (var _iterator = this.mountDirs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
        var mountDir = _step.value;
        this.priorityOrders.set(mountDir, this.priorityOrders.size);
      }
    } catch (err) {
      _didIteratorError = true;
      _iteratorError = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion && _iterator.return != null) {
          _iterator.return();
        }
      } finally {
        if (_didIteratorError) {
          throw _iteratorError;
        }
      }
    }
  }

  _createClass(FeelesWebpackPlugin, [{
    key: "apply",
    value: function apply(compiler) {
      var _this = this;

      // コンパイル開始
      compiler.plugin('compilation', function (compilation, params) {
        var pushDirFiles = function pushDirFiles(dirPath) {
          var _iteratorNormalCompletion2 = true;
          var _didIteratorError2 = false;
          var _iteratorError2 = undefined;

          try {
            for (var _iterator2 = fs.readdirSync(dirPath)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
              var name = _step2.value;
              var targetPath = path.resolve(dirPath, name);
              var stats = fs.statSync(targetPath);

              if (stats.isFile() && !_this.ignore.test(targetPath)) {
                // 次の emit の compilation.fileDependencies に含める
                params.compilationDependencies.add(targetPath);
              }

              if (stats.isDirectory()) {
                pushDirFiles(targetPath);
              }
            }
          } catch (err) {
            _didIteratorError2 = true;
            _iteratorError2 = err;
          } finally {
            try {
              if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
                _iterator2.return();
              }
            } finally {
              if (_didIteratorError2) {
                throw _iteratorError2;
              }
            }
          }
        }; // すべての mountDir を再帰的に探索する


        var _iteratorNormalCompletion3 = true;
        var _didIteratorError3 = false;
        var _iteratorError3 = undefined;

        try {
          for (var _iterator3 = _this.mountDirs[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
            var mountDir = _step3.value;
            pushDirFiles(mountDir);
          }
        } catch (err) {
          _didIteratorError3 = true;
          _iteratorError3 = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
              _iterator3.return();
            }
          } finally {
            if (_didIteratorError3) {
              throw _iteratorError3;
            }
          }
        }
      });
      compiler.plugin('emit',
      /*#__PURE__*/
      function () {
        var _ref = _asyncToGenerator(
        /*#__PURE__*/
        _regeneratorRuntime.mark(function _callee2(compilation, callback) {
          var mountNameDir, _iteratorNormalCompletion4, _didIteratorError4, _iteratorError4, _iterator4, _step4, absolutePath, _iteratorNormalCompletion6, _didIteratorError6, _iteratorError6, _iterator6, _step6, mountDir, mountName, nextMountDir, entry, changed, _iteratorNormalCompletion5, _didIteratorError5, _iteratorError5, _iterator5, _step5, _step5$value, _mountName, _mountDir, _absolutePath, timestamp, p, add, files, json;

          return _regeneratorRuntime.wrap(function _callee2$(_context2) {
            while (1) {
              switch (_context2.prev = _context2.next) {
                case 0:
                  // compilation.fileDependencies をもとにプロジェクト全体をシリアライズ
                  // { [mountName]: [mountDir] }
                  mountNameDir = new Map();
                  _iteratorNormalCompletion4 = true;
                  _didIteratorError4 = false;
                  _iteratorError4 = undefined;
                  _context2.prev = 4;
                  _iterator4 = compilation.fileDependencies[Symbol.iterator]();

                case 6:
                  if (_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done) {
                    _context2.next = 32;
                    break;
                  }

                  absolutePath = _step4.value;

                  if (!_this.ignore.test(absolutePath)) {
                    _context2.next = 10;
                    break;
                  }

                  return _context2.abrupt("continue", 29);

                case 10:
                  // ignore
                  // mountName を切り出す
                  _iteratorNormalCompletion6 = true;
                  _didIteratorError6 = false;
                  _iteratorError6 = undefined;
                  _context2.prev = 13;

                  for (_iterator6 = _this.mountDirs[Symbol.iterator](); !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
                    mountDir = _step6.value;

                    if (absolutePath.startsWith(mountDir + path.sep)) {
                      mountName = path.relative(mountDir, absolutePath); // すでにある候補もふくめて最も優先順位の高いパスを mountNameDir に set

                      nextMountDir = _this.maxPriority(mountDir, mountNameDir.get(mountName));
                      mountNameDir.set(mountName, nextMountDir);
                    }
                  }

                  _context2.next = 21;
                  break;

                case 17:
                  _context2.prev = 17;
                  _context2.t0 = _context2["catch"](13);
                  _didIteratorError6 = true;
                  _iteratorError6 = _context2.t0;

                case 21:
                  _context2.prev = 21;
                  _context2.prev = 22;

                  if (!_iteratorNormalCompletion6 && _iterator6.return != null) {
                    _iterator6.return();
                  }

                case 24:
                  _context2.prev = 24;

                  if (!_didIteratorError6) {
                    _context2.next = 27;
                    break;
                  }

                  throw _iteratorError6;

                case 27:
                  return _context2.finish(24);

                case 28:
                  return _context2.finish(21);

                case 29:
                  _iteratorNormalCompletion4 = true;
                  _context2.next = 6;
                  break;

                case 32:
                  _context2.next = 38;
                  break;

                case 34:
                  _context2.prev = 34;
                  _context2.t1 = _context2["catch"](4);
                  _didIteratorError4 = true;
                  _iteratorError4 = _context2.t1;

                case 38:
                  _context2.prev = 38;
                  _context2.prev = 39;

                  if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
                    _iterator4.return();
                  }

                case 41:
                  _context2.prev = 41;

                  if (!_didIteratorError4) {
                    _context2.next = 44;
                    break;
                  }

                  throw _iteratorError4;

                case 44:
                  return _context2.finish(41);

                case 45:
                  return _context2.finish(38);

                case 46:
                  entry = []; // プロジェクトのファイル全体

                  changed = false; // 変更があったかどうか

                  _iteratorNormalCompletion5 = true;
                  _didIteratorError5 = false;
                  _iteratorError5 = undefined;
                  _context2.prev = 51;
                  _iterator5 = mountNameDir.entries()[Symbol.iterator]();

                case 53:
                  if (_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done) {
                    _context2.next = 81;
                    break;
                  }

                  _step5$value = _slicedToArray(_step5.value, 2), _mountName = _step5$value[0], _mountDir = _step5$value[1];
                  // webpack が提供する timestamp を取得
                  _absolutePath = path.resolve(_mountDir, _mountName);
                  _context2.t2 = compilation.fileTimestamps[_absolutePath];

                  if (_context2.t2) {
                    _context2.next = 63;
                    break;
                  }

                  _context2.t3 = Date;
                  _context2.next = 61;
                  return stat(_absolutePath);

                case 61:
                  _context2.t4 = _context2.sent.mtime;
                  _context2.t2 = _context2.t3.parse.call(_context2.t3, _context2.t4);

                case 63:
                  timestamp = _context2.t2;

                  if (!_this.cache.has(_mountName)) {
                    _context2.next = 73;
                    break;
                  }

                  p = _this.cache.get(_mountName);

                  if (!(_mountDir === p.mountDir && timestamp <= p.timestamp)) {
                    _context2.next = 71;
                    break;
                  }

                  // 前回のビルドと同じ. そのまま
                  entry.push(p.serialized);
                  return _context2.abrupt("continue", 78);

                case 71:
                  // 場所かタイムスタンプが異なるのでキャッシュを削除
                  _this.cache.delete(_mountName);

                  if (_this.debug) {
                    console.log('📦 Feeles/mod:', _mountName, _mountDir);
                  }

                case 73:
                  // キャッシュとは異なるので新しく作成
                  add = new MountFile(_mountName, _mountDir, timestamp);
                  entry.push(add.serialized);

                  _this.cache.set(_mountName, add); // フラグを立てる


                  changed = true;

                  if (_this.debug) {
                    console.log('📦 Feeles/add:', _mountName, _mountDir);
                  }

                case 78:
                  _iteratorNormalCompletion5 = true;
                  _context2.next = 53;
                  break;

                case 81:
                  _context2.next = 87;
                  break;

                case 83:
                  _context2.prev = 83;
                  _context2.t5 = _context2["catch"](51);
                  _didIteratorError5 = true;
                  _iteratorError5 = _context2.t5;

                case 87:
                  _context2.prev = 87;
                  _context2.prev = 88;

                  if (!_iteratorNormalCompletion5 && _iterator5.return != null) {
                    _iterator5.return();
                  }

                case 90:
                  _context2.prev = 90;

                  if (!_didIteratorError5) {
                    _context2.next = 93;
                    break;
                  }

                  throw _iteratorError5;

                case 93:
                  return _context2.finish(90);

                case 94:
                  return _context2.finish(87);

                case 95:
                  if (!changed) {
                    _context2.next = 102;
                    break;
                  }

                  _context2.next = 98;
                  return Promise.all(entry);

                case 98:
                  files = _context2.sent;
                  console.log("\uD83D\uDCE6 Feeles:".concat(entry.length, " files mounted\tin ").concat(_this.mountDirs.join()));
                  json = JSON.stringify(files);
                  compilation.assets[_this.output] = {
                    source: function source() {
                      return json;
                    },
                    size: function size() {
                      return json.length;
                    }
                  };

                case 102:
                  callback();

                case 103:
                case "end":
                  return _context2.stop();
              }
            }
          }, _callee2, this, [[4, 34, 38, 46], [13, 17, 21, 29], [22,, 24, 28], [39,, 41, 45], [51, 83, 87, 95], [88,, 90, 94]]);
        }));

        return function (_x, _x2) {
          return _ref.apply(this, arguments);
        };
      }());
      compiler.plugin('after-emit', function (compilation, callback) {
        // 次の emit の compilation.fileDependencies に含める
        var _iteratorNormalCompletion7 = true;
        var _didIteratorError7 = false;
        var _iteratorError7 = undefined;

        try {
          for (var _iterator7 = _this.mountDirs[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
            var mountDir = _step7.value;
            compilation.contextDependencies.add(mountDir);
          }
        } catch (err) {
          _didIteratorError7 = true;
          _iteratorError7 = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion7 && _iterator7.return != null) {
              _iterator7.return();
            }
          } finally {
            if (_didIteratorError7) {
              throw _iteratorError7;
            }
          }
        }

        callback();
      });
    }
  }, {
    key: "maxPriority",
    value: function maxPriority(a, b) {
      if (!this.priorityOrders.has(a)) return b;
      if (!this.priorityOrders.has(b)) return a;
      return this.priorityOrders.get(a) < this.priorityOrders.get(b) ? a : b;
    }
  }]);

  return FeelesWebpackPlugin;
}();
/**
 * Feeles で使える環境変数. これらの文字を "文字列としてリプレイス" する (import のロード先にも使える)
 * process.env.__FEELES_COMMON_REGISTER__: @hackforplay/common の register.js
 * process.env.__FEELES_COMMON_INDEX__: @hackforplay/common の index.js
 * process.env.__FEELES_NODE_ENV__: process.env.NODE_ENV
 */


var environmentVars = function (vars) {
  return function () {
    return vars = vars || Object.keys(process.env).filter(function (s) {
      return s.startsWith('__FEELES_');
    });
  };
}();

function replaceEnvironmentVars() {
  var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
  var _iteratorNormalCompletion8 = true;
  var _didIteratorError8 = false;
  var _iteratorError8 = undefined;

  try {
    for (var _iterator8 = environmentVars()[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
      var key = _step8.value;
      text = text.split(key).join(process.env[key]);
    }
  } catch (err) {
    _didIteratorError8 = true;
    _iteratorError8 = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion8 && _iterator8.return != null) {
        _iterator8.return();
      }
    } finally {
      if (_didIteratorError8) {
        throw _iteratorError8;
      }
    }
  }

  return text;
}