"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getNameFromPkg = getNameFromPkg;
exports.filterDependenciesRepeat = filterDependenciesRepeat;
exports.getAllBlockDependencies = getAllBlockDependencies;
exports.dependenciesConflictCheck = dependenciesConflictCheck;
exports.getMockDependencies = getMockDependencies;
exports.parseContentToSingular = parseContentToSingular;
exports.getSingularName = getSingularName;
exports.default = exports.isEmptyFolder = void 0;

function _react() {
  const data = _interopRequireDefault(require("react"));

  _react = function _react() {
    return data;
  };

  return data;
}

function _fs() {
  const data = require("fs");

  _fs = function _fs() {
    return data;
  };

  return data;
}

function _path() {
  const data = require("path");

  _path = function _path() {
    return data;
  };

  return data;
}

function _mkdirp() {
  const data = _interopRequireDefault(require("mkdirp"));

  _mkdirp = function _mkdirp() {
    return data;
  };

  return data;
}

function _semver() {
  const data = _interopRequireDefault(require("semver"));

  _semver = function _semver() {
    return data;
  };

  return data;
}

function _crequire() {
  const data = _interopRequireDefault(require("crequire"));

  _crequire = function _crequire() {
    return data;
  };

  return data;
}

function _mustache() {
  const data = _interopRequireDefault(require("mustache"));

  _mustache = function _mustache() {
    return data;
  };

  return data;
}

function _uppercamelcase() {
  const data = _interopRequireDefault(require("uppercamelcase"));

  _uppercamelcase = function _uppercamelcase() {
    return data;
  };

  return data;
}

function _rimraf() {
  const data = _interopRequireDefault(require("rimraf"));

  _rimraf = function _rimraf() {
    return data;
  };

  return data;
}

function _umiUtils() {
  const data = require("umi-utils");

  _umiUtils = function _umiUtils() {
    return data;
  };

  return data;
}

var _replaceContent = _interopRequireDefault(require("./replaceContent"));

var _constants = require("../../../constants");

var _util = require("./util");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }

function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }

function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

const debug = require('debug')('umi-build-dev:getBlockGenerator');
/**
 * 判断一个路径是否为空
 * 只要有一个文件就算非空
 * @param {*} path
 */


const isEmptyFolder = path => {
  let isEmpty = true;

  if (!(0, _fs().existsSync)(path)) {
    return true;
  }

  if ((0, _fs().lstatSync)(path).isFile()) {
    return false;
  }

  const files = (0, _fs().readdirSync)(path);
  files.forEach(file => {
    if (!isEmpty) {
      return;
    }

    const stat = (0, _fs().lstatSync)((0, _path().join)(path, file));

    if (stat.isFile()) {
      isEmpty = false;
      return;
    }

    if (stat.isDirectory()) {
      isEmpty = isEmptyFolder((0, _path().join)(path, file));
    }
  });
  return isEmpty;
};

exports.isEmptyFolder = isEmptyFolder;

function getNameFromPkg(pkg) {
  if (!pkg.name) {
    return null;
  }

  return pkg.name.split('/').pop();
}
/**
 * 检查两个依赖之间的冲突
 * @param {*} blockDeps
 * @param {*} projectDeps
 */


function checkConflict(blockDeps, projectDeps) {
  const lacks = [];
  const conflicts = [];
  Object.keys(blockDeps).forEach(dep => {
    if (!projectDeps[dep]) {
      lacks.push([dep, blockDeps[dep]]);
    } else if (!_semver().default.intersects(projectDeps[dep], blockDeps[dep])) {
      conflicts.push([dep, blockDeps[dep], projectDeps[dep]]);
    }
  });
  return [lacks, conflicts];
}
/**
 * 删除重复依赖，projectDeps 中的依赖从 blockDeps 中删除
 * @param {*} blockDeps
 * @param {*} projectDeps
 */


function filterDependenciesRepeat(blockDeps, projectDeps) {
  const filterDependencies = {};
  Object.keys(blockDeps).forEach(key => {
    if (!projectDeps[key]) {
      filterDependencies[key] = blockDeps[key];
    }
  });
  return filterDependencies;
}

function getAllBlockDependencies(rootDir, pkg) {
  const _pkg$blockConfig = pkg.blockConfig,
        blockConfig = _pkg$blockConfig === void 0 ? {} : _pkg$blockConfig,
        _pkg$dependencies = pkg.dependencies,
        dependencies = _pkg$dependencies === void 0 ? {} : _pkg$dependencies;
  const _blockConfig$dependen = blockConfig.dependencies,
        depBlocks = _blockConfig$dependen === void 0 ? [] : _blockConfig$dependen;
  const allDependencies = {};
  /**
   * 合并重复依赖
   * @param {*} blockDeps
   * @param {*} projectDeps
   */

  function mergeDependencies(parent, sub) {
    const _checkConflict = checkConflict(sub, parent),
          _checkConflict2 = _slicedToArray(_checkConflict, 2),
          lacks = _checkConflict2[0],
          conflicts = _checkConflict2[1];

    if (conflicts.length) {
      throw new Error(`
      find dependencies conflict between blocks:
      ${conflicts.map(info => {
        return `* ${info[0]}: ${info[2]} not compatible with ${info[1]}`;
      }).join('\n')}`);
    }

    lacks.forEach(lack => {
      const _lack = _slicedToArray(lack, 2),
            name = _lack[0],
            version = _lack[1];

      parent[name] = version;
    });
    return parent;
  }

  depBlocks.forEach(block => {
    const rubBlockDeps = getAllBlockDependencies(rootDir, // eslint-disable-next-line
    require((0, _path().join)(rootDir, block, 'package.json')));
    mergeDependencies(allDependencies, rubBlockDeps);
  });
  mergeDependencies(allDependencies, dependencies);
  return allDependencies;
}
/**
 * 检查依赖项之间的冲突
 * @param {*}} blockPkgDeps
 * @param {*} projectPkgDeps
 * @param {*} blockPkgDevDeps
 * @param {*} projectPkgAllDeps
 */


function dependenciesConflictCheck(blockPkgDeps = {}, projectPkgDeps = {}, blockPkgDevDeps = {}, projectPkgAllDeps = {}) {
  const _checkConflict3 = checkConflict(blockPkgDeps, projectPkgDeps),
        _checkConflict4 = _slicedToArray(_checkConflict3, 2),
        lacks = _checkConflict4[0],
        conflicts = _checkConflict4[1];

  const _checkConflict5 = checkConflict(blockPkgDevDeps, projectPkgAllDeps),
        _checkConflict6 = _slicedToArray(_checkConflict5, 2),
        devLacks = _checkConflict6[0],
        devConflicts = _checkConflict6[1];

  return {
    conflicts,
    lacks,
    devConflicts,
    devLacks
  };
}
/**
 * 获取 mock 的依赖
 * @param {*} mockContent
 * @param {*} blockPkg
 */


function getMockDependencies(mockContent, blockPkg) {
  const allDependencies = _objectSpread({}, blockPkg.devDependencies, {}, blockPkg.dependencies);

  const deps = {};

  try {
    (0, _crequire().default)(mockContent).forEach(item => {
      if (allDependencies[item.path]) {
        deps[item.path] = allDependencies[item.path];
      }
    });
  } catch (e) {
    debug('parse mock content failed');
    debug(e);
  }

  return deps;
}

const singularReg = new RegExp(`['"](@/|[\\./]+)(${_constants.SINGULAR_SENSLTIVE.join('|')})/`, 'g');

function parseContentToSingular(content) {
  return content.replace(singularReg, (all, prefix, match) => {
    return all.replace(match, match.replace(/s$/, ''));
  });
}

function getSingularName(name) {
  if (_constants.SINGULAR_SENSLTIVE.includes(name)) {
    name = name.replace(/s$/, '');
  }

  return name;
}

var _default = api => {
  const paths = api.paths,
        Generator = api.Generator,
        config = api.config,
        applyPlugins = api.applyPlugins,
        findJS = api.findJS;
  const blockConfig = config.block || {};
  return class BlockGenerator extends Generator {
    constructor(args, opts) {
      super(args, opts);
      this.sourcePath = opts.sourcePath;
      this.dryRun = opts.dryRun;
      this.path = opts.path;
      this.routePath = opts.routePath || opts.path;
      this.blockName = opts.blockName;
      this.isPageBlock = opts.isPageBlock;
      this.execution = opts.execution;
      this.needCreateNewRoute = this.isPageBlock;
      this.blockFolderName = (0, _uppercamelcase().default)(this.blockName); // 这个参数是区块的 index.tsx | js

      this.entryPath = null; // 这个参数是当前区块的目录

      this.blockFolderPath = (0, _path().join)(paths.absPagesPath, this.path);
      this.routes = opts.routes || [];
      this.on('error', e => {
        debug(e); // handle the error for aviod throw generator default error stack
      });
    }

    writing() {
      var _this = this;

      return _asyncToGenerator(function* () {
        let targetPath = (0, _umiUtils().winPath)((0, _path().join)(paths.absPagesPath, _this.path));
        debug(`get targetPath ${targetPath}`); // for old page block check for duplicate path
        // if there is, prompt for input a new path

        if (isEmptyFolder(targetPath)) {
          _rimraf().default.sync(targetPath);
        }

        while (_this.isPageBlock && (0, _fs().existsSync)(targetPath)) {
          if (_this.execution === 'auto') {
            throw new Error(`path ${_this.path} already exist, press input a new path for it`);
          } // eslint-disable-next-line no-await-in-loop


          _this.path = (yield _this.prompt({
            type: 'input',
            name: 'path',
            message: `path ${_this.path} already exist, press input a new path for it`,
            required: true,
            default: _this.path
          })).path; // fix demo => /demo

          if (!/^\//.test(_this.path)) {
            _this.path = `/${_this.path}`;
          }

          targetPath = (0, _path().join)(paths.absPagesPath, _this.path);
          debug(`targetPath exist get new targetPath ${targetPath}`);
        } // 如果路由重复，重新输入


        while (_this.isPageBlock && (0, _util.routeExists)(_this.routePath, _this.routes)) {
          if (_this.execution === 'auto') {
            throw new Error(`router path ${_this.routePath} already exist, press input a new path for it`);
          } // eslint-disable-next-line no-await-in-loop


          _this.routePath = (yield _this.prompt({
            type: 'input',
            name: 'routePath',
            message: `router path ${_this.routePath} already exist, press input a new path for it`,
            required: true,
            default: _this.routePath
          })).routePath;
          debug(`router path exist get new targetPath ${_this.routePath}`);
        }

        _this.blockFolderPath = targetPath;
        const blockPath = _this.path;
        debug(`blockPath is ${blockPath}`);
        applyPlugins('beforeBlockWriting', {
          args: {
            sourcePath: _this.sourcePath,
            blockPath
          }
        });

        if (_this.dryRun) {
          debug('dryRun is true, skip copy files');
          return;
        } // check for duplicate block name under the path
        // if there is, prompt for a new block name


        while (!_this.isPageBlock && (0, _fs().existsSync)((0, _path().join)(targetPath, _this.blockFolderName))) {
          // eslint-disable-next-line no-await-in-loop
          _this.blockFolderName = (yield _this.prompt({
            type: 'input',
            name: 'path',
            message: `block with name ${_this.blockFolderName} already exist, please input a new name for it`,
            required: true,
            default: _this.blockFolderName
          })).path; // if (!/^\//.test(blockFolderName)) {
          //   blockFolderName = `/${blockFolderName}`;
          // }

          debug(`blockFolderName exist get new blockFolderName ${_this.blockFolderName}`);
        } // create container


        _this.entryPath = findJS(targetPath, 'index') || findJS(targetPath);

        if (!_this.entryPath) {
          _this.entryPath = (0, _path().join)(targetPath, `index.${_this.isTypeScript ? 'tsx' : 'js'}`);
        }

        if (!_this.isPageBlock && !(0, _fs().existsSync)(_this.entryPath)) {
          const confirmResult = (yield _this.prompt({
            type: 'confirm',
            name: 'needCreate',
            message: `Not find a exist page file at ${_this.path}. Do you want to create it and import this block.`
          })).needCreate;

          if (!confirmResult) {
            throw new Error('You stop it!');
          }

          debug('start to generate the entry file for block(s) under the path...');
          _this.needCreateNewRoute = true;
          const blockEntryTpl = (0, _fs().readFileSync)(blockConfig.entryTemplatePath || paths.defaultBlockEntryTplPath, 'utf-8');
          const tplContent = {
            blockEntryName: `${_this.path.slice(1)}Container`
          };

          const entry = _mustache().default.render(blockEntryTpl, tplContent);

          _mkdirp().default.sync(targetPath);

          (0, _fs().writeFileSync)(_this.entryPath, entry);
        } // copy block to target
        // you can find the copy api detail in https://github.com/SBoudrias/mem-fs-editor/blob/master/lib/actions/copy.js


        debug('start copy block file to your project...'); // 替换 相对路径

        ['src', '@'].forEach(folder => {
          if (!_this.isPageBlock && folder === '@') {
            // @ folder not support anymore in new specVersion
            return;
          }

          const folderPath = (0, _path().join)(_this.sourcePath, folder);
          let targetFolder;

          if (_this.isPageBlock) {
            targetFolder = folder === 'src' ? targetPath : paths.absSrcPath;
          } else {
            targetFolder = (0, _path().join)((0, _path().dirname)(_this.entryPath), _this.blockFolderName);
          }

          const options = {
            process(content, itemTargetPath) {
              content = String(content);

              if (config.singular) {
                content = parseContentToSingular(content);
              }

              content = (0, _replaceContent.default)(content, {
                path: blockPath
              });
              return applyPlugins('_modifyBlockFile', {
                initialValue: content,
                args: {
                  blockPath,
                  targetPath: itemTargetPath
                }
              });
            }

          };

          if ((0, _fs().existsSync)(folderPath)) {
            (0, _fs().readdirSync)(folderPath).forEach(name => {
              // ignore the dot files
              if (name.charAt(0) === '.') {
                return;
              }

              const thePath = (0, _path().join)(folderPath, name);

              if ((0, _fs().statSync)(thePath).isDirectory() && config.singular) {
                // @/components/ => @/src/component/ and ./components/ => ./component etc.
                name = getSingularName(name);
              }

              const realTarget = applyPlugins('_modifyBlockTarget', {
                initialValue: (0, _path().join)(targetFolder, name),
                args: {
                  source: thePath,
                  blockPath,
                  sourceName: name
                }
              });
              debug(`copy ${thePath} to ${realTarget}`);

              _this.fs.copy(thePath, realTarget, options);
            });
          }
        });
      })();
    }

  };
};

exports.default = _default;