"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

function _objectSpread2() {
  const data = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));

  _objectSpread2 = function () {
    return data;
  };

  return data;
}

function _bluebird() {
  const data = require("bluebird");

  _bluebird = function () {
    return data;
  };

  return data;
}

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

  _path = function () {
    return data;
  };

  return data;
}

function _ramda() {
  const data = _interopRequireDefault(require("ramda"));

  _ramda = function () {
    return data;
  };

  return data;
}

function _fsExtra() {
  const data = _interopRequireDefault(require("fs-extra"));

  _fsExtra = function () {
    return data;
  };

  return data;
}

function _vinyl() {
  const data = _interopRequireDefault(require("vinyl"));

  _vinyl = function () {
    return data;
  };

  return data;
}

function _dists() {
  const data = _interopRequireDefault(require("../component/sources/dists"));

  _dists = function () {
    return data;
  };

  return data;
}

function _invalidCompilerInterface() {
  const data = _interopRequireDefault(require("../component/exceptions/invalid-compiler-interface"));

  _invalidCompilerInterface = function () {
    return data;
  };

  return data;
}

function _environment() {
  const data = _interopRequireDefault(require("../../environment"));

  _environment = function () {
    return data;
  };

  return data;
}

function _componentMap() {
  const data = _interopRequireDefault(require("../bit-map/component-map"));

  _componentMap = function () {
    return data;
  };

  return data;
}

function _bitId() {
  const data = require("../../bit-id");

  _bitId = function () {
    return data;
  };

  return data;
}

function _logger() {
  const data = _interopRequireDefault(require("../../logger/logger"));

  _logger = function () {
    return data;
  };

  return data;
}

function _constants() {
  const data = require("../../constants");

  _constants = function () {
    return data;
  };

  return data;
}

function _externalBuildErrors() {
  const data = _interopRequireDefault(require("../component/exceptions/external-build-errors"));

  _externalBuildErrors = function () {
    return data;
  };

  return data;
}

function _utils() {
  const data = require("../../utils");

  _utils = function () {
    return data;
  };

  return data;
}

function _generalError() {
  const data = _interopRequireDefault(require("../../error/general-error"));

  _generalError = function () {
    return data;
  };

  return data;
}

function _sources() {
  const data = require("../component/sources");

  _sources = function () {
    return data;
  };

  return data;
}

function _ejectConf() {
  const data = require("./eject-conf");

  _ejectConf = function () {
    return data;
  };

  return data;
}

function _isolator() {
  const data = _interopRequireDefault(require("../../environment/isolator"));

  _isolator = function () {
    return data;
  };

  return data;
}

function _capsule() {
  const data = _interopRequireDefault(require("../../../components/core/capsule"));

  _capsule = function () {
    return data;
  };

  return data;
}

function _componentDependencies() {
  const data = _interopRequireDefault(require("../../scope/component-dependencies"));

  _componentDependencies = function () {
    return data;
  };

  return data;
}

function _packageJsonFile() {
  const data = _interopRequireDefault(require("../component/package-json-file"));

  _packageJsonFile = function () {
    return data;
  };

  return data;
}

function _dataToPersist() {
  const data = _interopRequireDefault(require("../component/sources/data-to-persist"));

  _dataToPersist = function () {
    return data;
  };

  return data;
}

function _linkGenerator() {
  const data = require("../../links/link-generator");

  _linkGenerator = function () {
    return data;
  };

  return data;
}

var _default =
/*#__PURE__*/
function () {
  var _buildComponent = (0, _bluebird().coroutine)(function* ({
    component,
    scope,
    save,
    // this is true only when originated from `runAndUpdateCI()`
    consumer,
    noCache,
    directory,
    verbose,
    dontPrintEnvMsg,
    keep
  }) {
    _logger().default.debug(`consumer-component.build ${component.id.toString()}`); // @TODO - write SourceMap Type


    if (!component.compiler) {
      if (!consumer || consumer.shouldDistsBeInsideTheComponent()) {
        _logger().default.debug('compiler was not found, nothing to build');

        return null;
      }

      _logger().default.debugAndAddBreadCrumb('build-component.buildComponent', 'compiler was not found, however, because the dists are set to be outside the components directory, save the source file as dists');

      component.copyFilesIntoDists();
      return component.dists;
    }

    const bitMap = consumer ? consumer.bitMap : undefined;
    const consumerPath = consumer ? consumer.getPath() : '';
    const componentMap = bitMap && bitMap.getComponentIfExist(component.id);
    let componentDir = consumerPath;

    if (componentMap) {
      componentDir = consumerPath && componentMap.rootDir ? _path().default.join(consumerPath, componentMap.rootDir) : undefined;
    }

    const needToRebuild = yield _isNeededToReBuild(consumer, component.id, noCache);

    if (!needToRebuild && !component.dists.isEmpty()) {
      _logger().default.debugAndAddBreadCrumb('build-component.buildComponent', 'skip the build process as the component was not modified, use the dists saved in the model');

      return component.dists;
    }

    _logger().default.debug('compiler found, start building');

    if (component.compiler && !component.compiler.loaded) {
      yield component.compiler.install(scope, {
        verbose: !!verbose,
        dontPrintEnvMsg
      }, {
        workspaceDir: consumerPath,
        componentDir,
        dependentId: component.id
      });
    }

    const compilerResults = (yield _buildIfNeeded({
      component,
      consumer,
      componentMap,
      scope,
      keep,
      directory,
      verbose: !!verbose
    })) || [];

    const {
      builtFiles,
      mainDist,
      packageJson
    } = _extractAndVerifyCompilerResults(compilerResults);

    builtFiles.forEach(file => {
      if (file && (!file.contents || !(0, _utils().isString)(file.contents.toString()))) {
        throw new (_generalError().default)('builder interface has to return object with a code attribute that contains string');
      }
    });
    component.setDists(builtFiles.map(file => new (_sources().Dist)(file)), mainDist);

    if (save) {
      yield scope.sources.updateDist({
        source: component
      });
    }

    if (packageJson && !_ramda().default.isEmpty(packageJson)) {
      yield _updateComponentPackageJson(component, packageJson);
      component.packageJsonChangedProps = Object.assign(component.packageJsonChangedProps || {}, packageJson);
    }

    return component.dists;
  });

  function buildComponent(_x) {
    return _buildComponent.apply(this, arguments);
  }

  return buildComponent;
}();

exports.default = _default;

function _updateComponentPackageJson(_x2, _x3) {
  return _updateComponentPackageJson2.apply(this, arguments);
}

function _updateComponentPackageJson2() {
  _updateComponentPackageJson2 = (0, _bluebird().coroutine)(function* (component, packageJsonPropsToAdd) {
    const componentPackageJsonFile = component.packageJsonFile;

    if (!componentPackageJsonFile) {
      _logger().default.debug(`ignore compiler packageJson result as the component ${component.id.toString()} does not have a package.json file`);

      return;
    }

    componentPackageJsonFile.mergePackageJsonObject(packageJsonPropsToAdd); // When running in capsule there is no workspace dir, so no need to update the package.json

    if (componentPackageJsonFile.workspaceDir) {
      yield componentPackageJsonFile.write();
    }
  });
  return _updateComponentPackageJson2.apply(this, arguments);
}

function _extractAndVerifyCompilerResults(compilerResults) {
  if (Array.isArray(compilerResults)) {
    return {
      builtFiles: compilerResults,
      mainDist: null,
      packageJson: null
    };
  }

  if (typeof compilerResults === 'object') {
    // $FlowFixMe
    if (compilerResults.files && !compilerResults.dists) {
      // previously, the new compiler "action" method expected to get "files", suggest to replace with 'dists'.
      throw new (_generalError().default)('fatal: compiler returned "files" instead of "dists", please change it to "dists"');
    }

    if (!compilerResults.dists) {
      throw new (_generalError().default)('fatal: compiler that returns an object, must include "dists" property');
    }

    if (compilerResults.packageJson) {
      _verifyPackageJsonReturnedByCompiler(compilerResults.packageJson);
    }

    return {
      builtFiles: compilerResults.dists,
      mainDist: compilerResults.mainFile,
      packageJson: compilerResults.packageJson
    };
  }

  throw new (_generalError().default)(`fatal: compiler must return an array or object, instead, got ${typeof compilerResults}`);
}

function _verifyPackageJsonReturnedByCompiler(packageJson) {
  if (typeof packageJson !== 'object') {
    throw new (_generalError().default)(`fatal: compiler must return packageJson as an object, got ${typeof packageJson}`);
  }

  _packageJsonFile().default.propsNonUserChangeable().forEach(prop => {
    if (packageJson[prop]) {
      throw new (_generalError().default)(`fatal: compiler must not return packageJson with "${prop}" property`);
    }
  });
}

function _buildIfNeeded(_x4) {
  return _buildIfNeeded2.apply(this, arguments);
} // Ideally it's better to use the dists from the model.
// If there is no consumer, it comes from the scope or isolated environment, which the dists are already saved.
// If there is consumer, check whether the component was modified. If it wasn't, no need to re-build.


function _buildIfNeeded2() {
  _buildIfNeeded2 = (0, _bluebird().coroutine)(function* ({
    component,
    consumer,
    componentMap,
    scope,
    verbose,
    directory,
    keep
  }) {
    const compiler = component.compiler;

    if (!compiler) {
      throw new (_generalError().default)('compiler was not found, nothing to build');
    }

    if (!compiler.action && !compiler.oldAction) {
      throw new (_invalidCompilerInterface().default)(compiler.name);
    }

    const runBuildParams = {
      component,
      consumer,
      scope,
      componentMap,
      verbose
    };

    if (consumer) {
      return _runBuild((0, _objectSpread2().default)({}, runBuildParams, {
        componentRoot: consumer.getPath()
      }));
    }

    if (component.isolatedEnvironment) {
      // $FlowFixMe
      return _runBuild((0, _objectSpread2().default)({}, runBuildParams, {
        componentRoot: component.writtenPath
      }));
    }

    const isolatedEnvironment = new (_environment().default)(scope, directory);

    try {
      yield isolatedEnvironment.create();
      const isolateOpts = {
        verbose,
        installPackages: true,
        noPackageJson: false
      };
      const componentWithDependencies = yield isolatedEnvironment.isolateComponent(component.id, isolateOpts);
      const isolatedComponent = componentWithDependencies.component;
      const result = yield _runBuild((0, _objectSpread2().default)({}, runBuildParams, {
        componentRoot: isolatedComponent.writtenPath
      }));
      if (!keep) yield isolatedEnvironment.destroy();
      return result;
    } catch (err) {
      yield isolatedEnvironment.destroy();
      throw err;
    }
  });
  return _buildIfNeeded2.apply(this, arguments);
}

const _isNeededToReBuild =
/*#__PURE__*/
function () {
  var _ref = (0, _bluebird().coroutine)(function* (consumer, componentId, noCache) {
    // Forcly rebuild
    if (noCache) return true;
    if (!consumer) return false;
    const componentStatus = yield consumer.getComponentStatusById(componentId);
    return componentStatus.modified;
  });

  return function _isNeededToReBuild(_x5, _x6, _x7) {
    return _ref.apply(this, arguments);
  };
}();

const _runBuild =
/*#__PURE__*/
function () {
  var _ref2 = (0, _bluebird().coroutine)(function* ({
    component,
    componentRoot,
    consumer,
    scope,
    componentMap,
    verbose
  }) {
    const compiler = component.compiler;

    if (!compiler) {
      throw new (_generalError().default)('compiler was not found, nothing to build');
    }

    let rootDistDir = _path().default.join(componentRoot, _constants().DEFAULT_DIST_DIRNAME);

    const consumerPath = consumer ? consumer.getPath() : '';
    const files = component.files.map(file => file.clone());
    let tmpFolderFullPath;
    let componentDir = '';

    if (componentMap) {
      const rootDistDirRelative = component.dists.getDistDir(consumer, componentMap.getRootDir());
      if (consumer) rootDistDir = consumer.toAbsolutePath(rootDistDirRelative);

      if (consumerPath && componentMap.getComponentDir()) {
        componentDir = componentMap.getComponentDir() || '';
      }
    }

    return Promise.resolve().then(
    /*#__PURE__*/
    (0, _bluebird().coroutine)(function* () {
      if (!compiler.action && !compiler.oldAction) {
        throw new (_invalidCompilerInterface().default)(compiler.name);
      }

      const isolateFunc =
      /*#__PURE__*/
      function () {
        var _ref4 = (0, _bluebird().coroutine)(function* ({
          targetDir,
          shouldBuildDependencies
        }) {
          const isolator = yield _isolator().default.getInstance('fs', scope, consumer, targetDir);
          const componentWithDependencies = yield isolator.isolate(component.id, {
            shouldBuildDependencies
          });

          const writeDists =
          /*#__PURE__*/
          function () {
            var _ref5 = (0, _bluebird().coroutine)(function* (builtFiles, mainDist) {
              const capsuleComponent = componentWithDependencies.component;
              capsuleComponent.setDists(builtFiles.map(file => new (_sources().Dist)(file)), mainDist); // $FlowFixMe result is not null here because the dists exist

              const distsToWrite = yield capsuleComponent.dists.getDistsToWrite(capsuleComponent, isolator.capsuleBitMap, null, true, componentWithDependencies);
              distsToWrite.persistAllToCapsule(isolator.capsule);
            });

            return function writeDists(_x10, _x11) {
              return _ref5.apply(this, arguments);
            };
          }();

          const getDependenciesLinks = () => {
            const links = (0, _linkGenerator().getComponentsDependenciesLinks)([componentWithDependencies], null, false, isolator.capsuleBitMap);
            return links.files;
          };

          const addSharedDir = filesToAdd => {
            const sharedDir = componentWithDependencies.component.originallySharedDir;
            let updatedFiles = filesToAdd;

            if (sharedDir) {
              updatedFiles = filesToAdd.map(file => {
                const fileAsAbstractVinyl = _sources().AbstractVinyl.fromVinyl(file);

                fileAsAbstractVinyl.updatePaths({
                  newRelative: _path().default.join(sharedDir, file.relative)
                });
                return fileAsAbstractVinyl;
              });
            }

            return updatedFiles;
          };

          const installPackages =
          /*#__PURE__*/
          function () {
            var _ref6 = (0, _bluebird().coroutine)(function* (packages = []) {
              yield isolator.installPackagesOnRoot(packages); // after installing packages on capsule root, some links/symlinks from node_modules might
              // be deleted. rewrite the links to recreate them.

              yield isolator.writeLinksOnNodeModules();
            });

            return function installPackages() {
              return _ref6.apply(this, arguments);
            };
          }();

          const capsuleFiles = componentWithDependencies.component.files;
          return {
            capsule: isolator.capsule,
            capsuleFiles,
            componentWithDependencies,
            writeDists,
            getDependenciesLinks,
            writeLinks: () => isolator.writeLinks(),
            capsuleExec: (cmd, options) => isolator.capsuleExec(cmd, options),
            installPackages,
            addSharedDir
          };
        });

        return function isolateFunc(_x9) {
          return _ref4.apply(this, arguments);
        };
      }();

      const context = {
        componentObject: component.toObject(),
        rootDistDir,
        componentDir,
        isolate: isolateFunc
      }; // Change the cwd to make sure we found the needed files

      process.chdir(componentRoot);

      if (compiler.action) {
        const isCompilerDetached = yield component.getDetachedCompiler(consumer);
        const shouldWriteConfig = compiler.writeConfigFilesOnAction && isCompilerDetached; // Write config files to tmp folder

        if (shouldWriteConfig) {
          tmpFolderFullPath = component.getTmpFolder(consumerPath);

          if (verbose) {
            console.log(`\nwriting config files to ${tmpFolderFullPath}`); // eslint-disable-line no-console
          }

          yield (0, _ejectConf().writeEnvFiles)({
            configDir: component.getTmpFolder(),
            env: compiler,
            consumer,
            component,
            deleteOldFiles: false,
            verbose
          });
        }

        const actionParams = {
          files,
          rawConfig: compiler.rawConfig,
          dynamicConfig: compiler.dynamicConfig,
          configFiles: compiler.files,
          api: compiler.api,
          context
        }; // $FlowFixMe we verified above that action is set

        const result = yield Promise.resolve(compiler.action(actionParams));

        if (tmpFolderFullPath) {
          if (verbose) {
            console.log(`\ndeleting tmp directory ${tmpFolderFullPath}`); // eslint-disable-line no-console
          }

          _logger().default.info(`build-components, deleting ${tmpFolderFullPath}`);

          yield _fsExtra().default.remove(tmpFolderFullPath);
        }

        return result;
      }

      if (!compiler.oldAction) {
        throw new (_invalidCompilerInterface().default)(compiler.name);
      }

      return Promise.resolve(compiler.oldAction(files, rootDistDir, context));
    })).catch(e => {
      if (tmpFolderFullPath) {
        _logger().default.info(`build-components, deleting ${tmpFolderFullPath}`);

        _fsExtra().default.removeSync(tmpFolderFullPath);
      }

      const errors = e.errors || [e];
      const err = new (_externalBuildErrors().default)(component.id.toString(), errors);
      throw err;
    });
  });

  return function _runBuild(_x8) {
    return _ref2.apply(this, arguments);
  };
}();