text: "use strict";
  
  var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
  
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  
  exports.__esModule = true;
  exports.handleBadExports = handleBadExports;
  exports.validateConfigPluginsOptions = validateConfigPluginsOptions;
  exports.collatePluginAPIs = collatePluginAPIs;
  exports.warnOnIncompatiblePeerDependency = warnOnIncompatiblePeerDependency;
  exports.handleMultipleReplaceRenderers = void 0;
  
  var _lodash = _interopRequireDefault(require("lodash"));
  
  var _path = _interopRequireDefault(require("path"));
  
  var semver = _interopRequireWildcard(require("semver"));
  
  var stringSimilarity = _interopRequireWildcard(require("string-similarity"));
  
  var _package = require("gatsby/package.json");
  
  var _reporter = _interopRequireDefault(require("gatsby-cli/lib/reporter"));
  
  var _gatsbyPluginUtils = require("gatsby-plugin-utils");
  
  var _resolveModuleExports = require("../resolve-module-exports");
  
  var _getLatestApis = require("../../utils/get-latest-apis");
  
  var _commonTags = require("common-tags");
  
  var _gatsbyTelemetry = require("gatsby-telemetry");
  
  const getGatsbyUpgradeVersion = entries => entries.reduce((version, entry) => {
    if (entry.api && entry.api.version) {
      return semver.gt(entry.api.version, version || `0.0.0`) ? entry.api.version : version;
    }
  
    return version;
  }, ``); // Given a plugin object, an array of the API names it exports and an
  // array of valid API names, return an array of invalid API exports.
  
  
  function getBadExports(plugin, pluginAPIKeys, apis) {
    let badExports = []; // Discover any exports from plugins which are not "known"
  
    badExports = badExports.concat(_lodash.default.difference(pluginAPIKeys, apis).map(e => {
      return {
        exportName: e,
        pluginName: plugin.name,
        pluginVersion: plugin.version
      };
    }));
    return badExports;
  }
  
  function getErrorContext(badExports, exportType, currentAPIs, latestAPIs) {
    const entries = badExports.map(ex => {
      return { ...ex,
        api: latestAPIs[exportType][ex.exportName]
      };
    });
    const gatsbyUpgradeVersion = getGatsbyUpgradeVersion(entries);
    const errors = [];
    const fixes = gatsbyUpgradeVersion ? [`npm install gatsby@^${gatsbyUpgradeVersion}`] : [];
    entries.forEach(entry => {
      const similarities = stringSimilarity.findBestMatch(entry.exportName, currentAPIs[exportType]);
      const isDefaultPlugin = entry.pluginName == `default-site-plugin`;
      const message = entry.api ? entry.api.version ? `was introduced in gatsby@${entry.api.version}` : `is not available in your version of Gatsby` : `is not a known API`;
  
      if (isDefaultPlugin) {
        errors.push(`- Your local gatsby-${exportType}.js is using the API "${entry.exportName}" which ${message}.`);
      } else {
        errors.push(`- The plugin ${entry.pluginName}@${entry.pluginVersion} is using the API "${entry.exportName}" which ${message}.`);
      }
  
      if (similarities.bestMatch.rating > 0.5) {
        fixes.push(`Rename "${entry.exportName}" -> "${similarities.bestMatch.target}"`);
      }
    });
    return {
      errors,
      entries,
      exportType,
      fixes,
      // note: this is a fallback if gatsby-cli is not updated with structured error
      sourceMessage: [`Your plugins must export known APIs from their gatsby-node.js.`].concat(errors).concat(fixes.length > 0 ? [`\n`, `Some of the following may help fix the error(s):`, ...fixes] : []).filter(Boolean).join(`\n`)
    };
  }
  
  async function handleBadExports({
    currentAPIs,
    badExports
  }) {
    const hasBadExports = Object.keys(badExports).find(api => badExports[api].length > 0);
  
    if (hasBadExports) {
      const latestAPIs = await (0, _getLatestApis.getLatestAPIs)(); // Output error messages for all bad exports
  
      _lodash.default.toPairs(badExports).forEach(badItem => {
        const [exportType, entries] = badItem;
  
        if (entries.length > 0) {
          const context = getErrorContext(entries, exportType, currentAPIs, latestAPIs);
  
          _reporter.default.error({
            id: `11329`,
            context
          });
        }
      });
    }
  }
  
  async function validatePluginsOptions(plugins, rootDir) {
    let errors = 0;
    const newPlugins = await Promise.all(plugins.map(async plugin => {
      let gatsbyNode;
  
      try {
        gatsbyNode = require(`${plugin.resolve}/gatsby-node`);
      } catch (err) {
        gatsbyNode = {};
      }
  
      if (!gatsbyNode.pluginOptionsSchema) return plugin;
      let optionsSchema = gatsbyNode.pluginOptionsSchema({
        Joi: _gatsbyPluginUtils.Joi
      }); // Validate correct usage of pluginOptionsSchema
  
      if (!_gatsbyPluginUtils.Joi.isSchema(optionsSchema) || optionsSchema.type !== `object`) {
        _reporter.default.warn(`Plugin "${plugin.resolve}" has an invalid options schema so we cannot verify your configuration for it.`);
  
        return plugin;
      }
  
      try {
        var _plugin$options;
  
        if (!optionsSchema.describe().keys.plugins) {
          // All plugins have "plugins: []"" added to their options in load.ts, even if they
          // do not have subplugins. We add plugins to the schema if it does not exist already
          // to make sure they pass validation.
          optionsSchema = optionsSchema.append({
            plugins: _gatsbyPluginUtils.Joi.array().length(0)
          });
        }
  
        plugin.options = await (0, _gatsbyPluginUtils.validateOptionsSchema)(optionsSchema, plugin.options || {});
  
        if ((_plugin$options = plugin.options) !== null && _plugin$options !== void 0 && _plugin$options.plugins) {
          const {
            errors: subErrors,
            plugins: subPlugins
          } = await validatePluginsOptions(plugin.options.plugins, rootDir);
          plugin.options.plugins = subPlugins;
          errors += subErrors;
        }
      } catch (error) {
        if (error instanceof _gatsbyPluginUtils.Joi.ValidationError) {
          // Show a small warning on unknown options rather than erroring
          const validationWarnings = error.details.filter(err => err.type === `object.unknown`);
          const validationErrors = error.details.filter(err => err.type !== `object.unknown`); // If rootDir and plugin.parentDir are the same, i.e. if this is a plugin a user configured in their gatsby-config.js (and not a sub-theme that added it), this will be ""
          // Otherwise, this will contain (and show) the relative path
  
          const configDir = plugin.parentDir && rootDir && _path.default.relative(rootDir, plugin.parentDir) || null;
  
          if (validationErrors.length > 0) {
            _reporter.default.error({
              id: `11331`,
              context: {
                configDir,
                validationErrors,
                pluginName: plugin.resolve
              }
            });
  
            errors++;
          }
  
          if (validationWarnings.length > 0) {
            _reporter.default.warn((0, _commonTags.stripIndent)(`
                  Warning: there are unknown plugin options for "${plugin.resolve}"${configDir ? `, configured by ${configDir}` : ``}: ${validationWarnings.map(error => error.path.join(`.`)).join(`, `)}
                  Please open an issue at ghub.io/${plugin.resolve} if you believe this option is valid.
                `));
  
            (0, _gatsbyTelemetry.trackCli)(`UNKNOWN_PLUGIN_OPTION`, {
              name: plugin.resolve,
              valueString: validationWarnings.map(error => error.path.join(`.`)).join(`, `)
            }); // We do not increment errors++ here as we do not want to process.exit if there are only warnings
          }
  
          return plugin;
        }
  
        throw error;
      }
  
      return plugin;
    }));
    return {
      errors,
      plugins: newPlugins
    };
  }
  
  async function validateConfigPluginsOptions(config = {}, rootDir) {
    if (!config.plugins) return;
    const {
      errors,
      plugins
    } = await validatePluginsOptions(config.plugins, rootDir);
    config.plugins = plugins;
  
    if (errors > 0) {
      process.exit(1);
    }
  }
  /**
   * Identify which APIs each plugin exports
   */
  
  
  function collatePluginAPIs({
    currentAPIs,
    flattenedPlugins
  }) {
    // Get a list of bad exports
    const badExports = {
      node: [],
      browser: [],
      ssr: []
    };
    flattenedPlugins.forEach(plugin => {
      plugin.nodeAPIs = [];
      plugin.browserAPIs = [];
      plugin.ssrAPIs = []; // Discover which APIs this plugin implements and store an array against
      // the plugin node itself *and* in an API to plugins map for faster lookups
      // later.
  
      const pluginNodeExports = (0, _resolveModuleExports.resolveModuleExports)(`${plugin.resolve}/gatsby-node`, {
        mode: `require`
      });
      const pluginBrowserExports = (0, _resolveModuleExports.resolveModuleExports)(`${plugin.resolve}/gatsby-browser`);
      const pluginSSRExports = (0, _resolveModuleExports.resolveModuleExports)(`${plugin.resolve}/gatsby-ssr`);
  
      if (pluginNodeExports.length > 0) {
        plugin.nodeAPIs = _lodash.default.intersection(pluginNodeExports, currentAPIs.node);
        badExports.node = badExports.node.concat(getBadExports(plugin, pluginNodeExports, currentAPIs.node)); // Collate any bad exports
      }
  
      if (pluginBrowserExports.length > 0) {
        plugin.browserAPIs = _lodash.default.intersection(pluginBrowserExports, currentAPIs.browser);
        badExports.browser = badExports.browser.concat(getBadExports(plugin, pluginBrowserExports, currentAPIs.browser)); // Collate any bad exports
      }
  
      if (pluginSSRExports.length > 0) {
        plugin.ssrAPIs = _lodash.default.intersection(pluginSSRExports, currentAPIs.ssr);
        badExports.ssr = badExports.ssr.concat(getBadExports(plugin, pluginSSRExports, currentAPIs.ssr)); // Collate any bad exports
      }
    });
    return {
      flattenedPlugins: flattenedPlugins,
      badExports
    };
  }
  
  const handleMultipleReplaceRenderers = ({
    flattenedPlugins
  }) => {
    // multiple replaceRenderers may cause problems at build time
    const rendererPlugins = flattenedPlugins.filter(plugin => plugin.ssrAPIs.includes(`replaceRenderer`)).map(plugin => plugin.name);
  
    if (rendererPlugins.length > 1) {
      if (rendererPlugins.includes(`default-site-plugin`)) {
        _reporter.default.warn(`replaceRenderer API found in these plugins:`);
  
        _reporter.default.warn(rendererPlugins.join(`, `));
  
        _reporter.default.warn(`This might be an error, see: https://www.gatsbyjs.org/docs/debugging-replace-renderer-api/`);
      } else {
        console.log(``);
  
        _reporter.default.error(`Gatsby's replaceRenderer API is implemented by multiple plugins:`);
  
        _reporter.default.error(rendererPlugins.join(`, `));
  
        _reporter.default.error(`This will break your build`);
  
        _reporter.default.error(`See: https://www.gatsbyjs.org/docs/debugging-replace-renderer-api/`);
  
        if (process.env.NODE_ENV === `production`) process.exit(1);
      } // Now update plugin list so only final replaceRenderer will run
  
  
      const ignorable = rendererPlugins.slice(0, -1); // For each plugin in ignorable, set a skipSSR flag to true
      // This prevents apiRunnerSSR() from attempting to run it later
  
      const messages = [];
      flattenedPlugins.forEach((fp, i) => {
        if (ignorable.includes(fp.name)) {
          messages.push(`Duplicate replaceRenderer found, skipping gatsby-ssr.js for plugin: ${fp.name}`);
          flattenedPlugins[i].skipSSR = true;
        }
      });
  
      if (messages.length > 0) {
        console.log(``);
        messages.forEach(m => _reporter.default.warn(m));
        console.log(``);
      }
    }
  
    return flattenedPlugins;
  };
  
  exports.handleMultipleReplaceRenderers = handleMultipleReplaceRenderers;
  
  function warnOnIncompatiblePeerDependency(name, packageJSON) {
    // Note: In the future the peer dependency should be enforced for all plugins.
    const gatsbyPeerDependency = _lodash.default.get(packageJSON, `peerDependencies.gatsby`);
  
    if (gatsbyPeerDependency && !semver.satisfies(_package.version, gatsbyPeerDependency, {
      includePrerelease: true
    })) {
      _reporter.default.warn(`Plugin ${name} is not compatible with your gatsby version ${_package.version} - It requires gatsby@${gatsbyPeerDependency}`);
    }
  }
  //# sourceMappingURL=validate.js.map