"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = loadPlugin;
function _path() {
  const data = _interopRequireDefault(require("path"));
  _path = function () {
    return data;
  };
  return data;
}
function _semver() {
  const data = _interopRequireDefault(require("semver"));
  _semver = function () {
    return data;
  };
  return data;
}
function _logger() {
  const data = _interopRequireDefault(require("@parcel/logger"));
  _logger = function () {
    return data;
  };
  return data;
}
function _nullthrows() {
  const data = _interopRequireDefault(require("nullthrows"));
  _nullthrows = function () {
    return data;
  };
  return data;
}
function _diagnostic() {
  const data = _interopRequireWildcard(require("@parcel/diagnostic"));
  _diagnostic = function () {
    return data;
  };
  return data;
}
function _utils() {
  const data = require("@parcel/utils");
  _utils = function () {
    return data;
  };
  return data;
}
var _projectPath = require("./projectPath");
var _package = require("../package.json");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const NODE_MODULES = `${_path().default.sep}node_modules${_path().default.sep}`;
const CONFIG = Symbol.for('parcel-plugin-config');
async function loadPlugin(pluginName, configPath, keyPath, options) {
  let resolveFrom = configPath;
  let range;
  let isOptional = false;
  if (resolveFrom.includes(NODE_MODULES) || "production" !== 'production' && /packages[/\\]configs/.test(resolveFrom)) {
    var _configPkg$config$dep;
    // Config packages can reference plugins, but cannot contain other plugins within them.
    // This forces every published plugin to be published separately so they can be mixed and matched if needed.
    if (pluginName.startsWith('.')) {
      let configContents = await options.inputFS.readFile(configPath, 'utf8');
      throw new (_diagnostic().default)({
        diagnostic: {
          message: (0, _diagnostic().md)`Local plugins are not supported in Parcel config packages. Please publish "${pluginName}" as a separate npm package.`,
          origin: '@parcel/core',
          codeFrames: keyPath ? [{
            filePath: configPath,
            language: 'json5',
            code: configContents,
            codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(configContents, [{
              key: keyPath,
              type: 'value'
            }])
          }] : undefined
        }
      });
    }
    let configPkg = await (0, _utils().loadConfig)(options.inputFS, resolveFrom, ['package.json'], options.projectRoot);
    if (configPkg != null && ((_configPkg$config$dep = configPkg.config.dependencies) === null || _configPkg$config$dep === void 0 ? void 0 : _configPkg$config$dep[pluginName]) == null) {
      var _configPkg$config$par;
      // If not in the config's dependencies, the plugin will be auto installed with
      // the version declared in "parcelDependencies".
      range = (_configPkg$config$par = configPkg.config.parcelDependencies) === null || _configPkg$config$par === void 0 ? void 0 : _configPkg$config$par[pluginName];
      isOptional = Array.isArray(configPkg.config.optionalParcelDependencies) && configPkg.config.optionalParcelDependencies.includes(pluginName);
      if (range == null && !isOptional) {
        let contents = await options.inputFS.readFile(configPkg.files[0].filePath, 'utf8');
        throw new (_diagnostic().default)({
          diagnostic: {
            message: (0, _diagnostic().md)`Could not determine version of ${pluginName} in ${_path().default.relative(process.cwd(), resolveFrom)}. Either include it in "dependencies" or "parcelDependencies".`,
            origin: '@parcel/core',
            codeFrames: configPkg.config.dependencies || configPkg.config.parcelDependencies ? [{
              filePath: configPkg.files[0].filePath,
              language: 'json5',
              code: contents,
              codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(contents, [{
                key: configPkg.config.parcelDependencies ? '/parcelDependencies' : '/dependencies',
                type: 'key'
              }])
            }] : undefined
          }
        });
      }

      // Resolve from project root if not in the config's dependencies.
      resolveFrom = _path().default.join(options.projectRoot, 'index');
    }
  }
  let resolved, pkg;
  try {
    ({
      resolved,
      pkg
    } = await options.packageManager.resolve(pluginName, resolveFrom, {
      shouldAutoInstall: options.shouldAutoInstall && !isOptional,
      range
    }));
  } catch (err) {
    if (err.code !== 'MODULE_NOT_FOUND') {
      throw err;
    }
    if (isOptional) {
      return null;
    }
    let configContents = await options.inputFS.readFile(configPath, 'utf8');
    let alternatives = await (0, _utils().findAlternativeNodeModules)(options.inputFS, pluginName, _path().default.dirname(resolveFrom));
    throw new (_diagnostic().default)({
      diagnostic: {
        message: (0, _diagnostic().md)`Cannot find Parcel plugin "${pluginName}"`,
        origin: '@parcel/core',
        codeFrames: keyPath ? [{
          filePath: configPath,
          language: 'json5',
          code: configContents,
          codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(configContents, [{
            key: keyPath,
            type: 'value',
            message: (0, _diagnostic().md)`Cannot find module "${pluginName}"${alternatives[0] ? `, did you mean "${alternatives[0]}"?` : ''}`
          }])
        }] : undefined
      }
    });
  }

  // Remove plugin version compatiblility validation in canary builds as they don't use semver

  if (!pluginName.startsWith('.')) {
    // Validate the engines.parcel field in the plugin's package.json
    let parcelVersionRange = pkg && pkg.engines && pkg.engines.parcel;
    if (!parcelVersionRange) {
      _logger().default.warn({
        origin: '@parcel/core',
        message: `The plugin "${pluginName}" needs to specify a \`package.json#engines.parcel\` field with the supported Parcel version range.`
      });
    }
    if (parcelVersionRange && !_semver().default.satisfies(_package.version, parcelVersionRange)) {
      let pkgFile = (0, _nullthrows().default)(await (0, _utils().resolveConfig)(options.inputFS, resolved, ['package.json'], options.projectRoot));
      let pkgContents = await options.inputFS.readFile(pkgFile, 'utf8');
      throw new (_diagnostic().default)({
        diagnostic: {
          message: (0, _diagnostic().md)`The plugin "${pluginName}" is not compatible with the current version of Parcel. Requires "${parcelVersionRange}" but the current version is "${_package.version}".`,
          origin: '@parcel/core',
          codeFrames: [{
            filePath: pkgFile,
            language: 'json5',
            code: pkgContents,
            codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(pkgContents, [{
              key: '/engines/parcel'
            }])
          }]
        }
      });
    }
  }
  let plugin = await options.packageManager.require(pluginName, resolveFrom, {
    shouldAutoInstall: options.shouldAutoInstall
  });
  plugin = plugin.default ? plugin.default : plugin;
  if (!plugin) {
    throw new Error(`Plugin ${pluginName} has no exports.`);
  }
  plugin = plugin[CONFIG];
  if (!plugin) {
    throw new Error(`Plugin ${pluginName} is not a valid Parcel plugin, should export an instance of a Parcel plugin ex. "export default new Reporter({ ... })".`);
  }
  return {
    plugin,
    version: (0, _nullthrows().default)(pkg).version,
    resolveFrom: (0, _projectPath.toProjectPath)(options.projectRoot, resolveFrom),
    range
  };
}