"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = createBundleGraphRequest;
function _assert() {
  const data = _interopRequireDefault(require("assert"));
  _assert = function () {
    return data;
  };
  return data;
}
function _nullthrows() {
  const data = _interopRequireDefault(require("nullthrows"));
  _nullthrows = function () {
    return data;
  };
  return data;
}
function _logger() {
  const data = require("@parcel/logger");
  _logger = function () {
    return data;
  };
  return data;
}
function _diagnostic() {
  const data = _interopRequireWildcard(require("@parcel/diagnostic"));
  _diagnostic = function () {
    return data;
  };
  return data;
}
var _AssetGraph = _interopRequireDefault(require("../AssetGraph"));
var _BundleGraph = _interopRequireDefault(require("../public/BundleGraph"));
var _BundleGraph2 = _interopRequireWildcard(require("../BundleGraph"));
var _MutableBundleGraph = _interopRequireDefault(require("../public/MutableBundleGraph"));
var _Bundle = require("../public/Bundle");
var _ReporterRunner = require("../ReporterRunner");
var _dumpGraphToGraphViz = _interopRequireDefault(require("../dumpGraphToGraphViz"));
function _rust() {
  const data = require("@parcel/rust");
  _rust = function () {
    return data;
  };
  return data;
}
var _PluginOptions = _interopRequireDefault(require("../public/PluginOptions"));
var _applyRuntimes = _interopRequireDefault(require("../applyRuntimes"));
var _constants = require("../constants");
var _utils = require("../utils");
var _ParcelConfigRequest = _interopRequireWildcard(require("./ParcelConfigRequest"));
var _DevDepRequest = require("./DevDepRequest");
var _InternalConfig = require("../InternalConfig");
var _ConfigRequest = require("./ConfigRequest");
var _projectPath = require("../projectPath");
var _AssetGraphRequest = _interopRequireDefault(require("./AssetGraphRequest"));
function _profiler() {
  const data = require("@parcel/profiler");
  _profiler = function () {
    return data;
  };
  return data;
}
var _RequestTracker = require("../RequestTracker");
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 }; }
// TODO: Rename to BundleGraphRequestResult
function createBundleGraphRequest(input) {
  return {
    type: _RequestTracker.requestTypes.bundle_graph_request,
    id: 'BundleGraph',
    run: async input => {
      let {
        options,
        api,
        invalidateReason
      } = input;
      let {
        optionsRef,
        requestedAssetIds,
        signal
      } = input.input;
      let measurement = _profiler().tracer.createMeasurement('building');
      let request = (0, _AssetGraphRequest.default)({
        name: 'Main',
        entries: options.entries,
        optionsRef,
        shouldBuildLazily: options.shouldBuildLazily,
        lazyIncludes: options.lazyIncludes,
        lazyExcludes: options.lazyExcludes,
        requestedAssetIds
      });
      let {
        assetGraph,
        changedAssets,
        assetRequests
      } = await api.runRequest(request, {
        force: options.shouldBuildLazily && requestedAssetIds.size > 0
      });
      measurement && measurement.end();
      (0, _utils.assertSignalNotAborted)(signal);

      // If any subrequests are invalid (e.g. dev dep requests or config requests),
      // bail on incremental bundling. We also need to invalidate for option changes,
      // which are hoisted to direct invalidations on the bundle graph request.
      let subRequestsInvalid = Boolean(invalidateReason & _constants.OPTION_CHANGE) || input.api.getSubRequests().some(req => !input.api.canSkipSubrequest(req.id));
      if (subRequestsInvalid) {
        assetGraph.safeToIncrementallyBundle = false;
      }
      let configResult = (0, _nullthrows().default)(await input.api.runRequest((0, _ParcelConfigRequest.default)()));
      (0, _utils.assertSignalNotAborted)(signal);
      let parcelConfig = (0, _ParcelConfigRequest.getCachedParcelConfig)(configResult, input.options);
      let {
        devDeps,
        invalidDevDeps
      } = await (0, _DevDepRequest.getDevDepRequests)(input.api);
      (0, _DevDepRequest.invalidateDevDeps)(invalidDevDeps, input.options, parcelConfig);
      let bundlingMeasurement = _profiler().tracer.createMeasurement('bundling');
      let builder = new BundlerRunner(input, parcelConfig, devDeps);
      let res = await builder.bundle({
        graph: assetGraph,
        changedAssets: changedAssets,
        assetRequests
      });
      bundlingMeasurement && bundlingMeasurement.end();
      for (let [id, asset] of changedAssets) {
        res.changedAssets.set(id, asset);
      }
      await (0, _dumpGraphToGraphViz.default)(
      // $FlowFixMe Added in Flow 0.121.0 upgrade in #4381 (Windows only)
      res.bundleGraph._graph, 'BundleGraph', _BundleGraph2.bundleGraphEdgeTypes);
      return res;
    },
    input
  };
}
class BundlerRunner {
  constructor({
    input,
    api,
    options
  }, config, previousDevDeps) {
    this.options = options;
    this.api = api;
    this.optionsRef = input.optionsRef;
    this.config = config;
    this.previousDevDeps = previousDevDeps;
    this.devDepRequests = new Map();
    this.configs = new Map();
    this.pluginOptions = new _PluginOptions.default((0, _utils.optionsProxy)(this.options, api.invalidateOnOptionChange));
    this.cacheKey = (0, _rust().hashString)(`${_constants.PARCEL_VERSION}:BundleGraph:${JSON.stringify(options.entries) ?? ''}${options.mode}${options.shouldBuildLazily ? 'lazy' : 'eager'}`) + '-BundleGraph';
  }
  async loadConfigs() {
    // Load all configs up front so we can use them in the cache key
    let bundler = await this.config.getBundler();
    await this.loadConfig(bundler);
    let namers = await this.config.getNamers();
    for (let namer of namers) {
      await this.loadConfig(namer);
    }
    let runtimes = await this.config.getRuntimes();
    for (let runtime of runtimes) {
      await this.loadConfig(runtime);
    }
  }
  async loadConfig(plugin) {
    let config = (0, _InternalConfig.createConfig)({
      plugin: plugin.name,
      searchPath: (0, _projectPath.toProjectPathUnsafe)('index')
    });
    await (0, _ConfigRequest.loadPluginConfig)(plugin, config, this.options);
    await (0, _ConfigRequest.runConfigRequest)(this.api, config);
    for (let devDep of config.devDeps) {
      let devDepRequest = await (0, _DevDepRequest.createDevDependency)(devDep, this.previousDevDeps, this.options);
      await this.runDevDepRequest(devDepRequest);
    }
    this.configs.set(plugin.name, config);
  }
  async runDevDepRequest(devDepRequest) {
    let {
      specifier,
      resolveFrom
    } = devDepRequest;
    let key = `${specifier}:${(0, _projectPath.fromProjectPathRelative)(resolveFrom)}`;
    this.devDepRequests.set(key, devDepRequest);
    await (0, _DevDepRequest.runDevDepRequest)(this.api, devDepRequest);
  }
  async bundle({
    graph,
    changedAssets,
    assetRequests
  }) {
    (0, _ReporterRunner.report)({
      type: 'buildProgress',
      phase: 'bundling'
    });
    await this.loadConfigs();
    let plugin = await this.config.getBundler();
    let {
      plugin: bundler,
      name,
      resolveFrom
    } = plugin;

    // if a previous asset graph hash is passed in, check if the bundle graph is also available
    let previousBundleGraphResult;
    if (graph.safeToIncrementallyBundle) {
      try {
        previousBundleGraphResult = await this.api.getPreviousResult();
      } catch {
        // if the bundle graph had an error or was removed, don't fail the build
      }
    }
    if (previousBundleGraphResult == null) {
      graph.safeToIncrementallyBundle = false;
    }
    let internalBundleGraph;
    let logger = new (_logger().PluginLogger)({
      origin: name
    });
    let tracer = new (_profiler().PluginTracer)({
      origin: name,
      category: 'bundle'
    });
    try {
      if (previousBundleGraphResult) {
        internalBundleGraph = previousBundleGraphResult.bundleGraph;
        for (let changedAssetId of changedAssets.keys()) {
          // Copy over the whole node to also have correct symbol data
          let changedAssetNode = (0, _nullthrows().default)(graph.getNodeByContentKey(changedAssetId));
          (0, _assert().default)(changedAssetNode.type === 'asset');
          internalBundleGraph.updateAsset(changedAssetNode);
        }
      } else {
        var _this$configs$get;
        internalBundleGraph = _BundleGraph2.default.fromAssetGraph(graph, this.options.mode === 'production');
        (0, _assert().default)(internalBundleGraph != null); // ensures the graph was created

        await (0, _dumpGraphToGraphViz.default)(
        // $FlowFixMe
        internalBundleGraph._graph, 'before_bundle', _BundleGraph2.bundleGraphEdgeTypes);
        let mutableBundleGraph = new _MutableBundleGraph.default(internalBundleGraph, this.options);
        let measurement;
        let measurementFilename;
        if (tracer.enabled) {
          measurementFilename = graph.getEntryAssets().map(asset => (0, _projectPath.fromProjectPathRelative)(asset.filePath)).join(', ');
          measurement = tracer.createMeasurement(plugin.name, 'bundling:bundle', measurementFilename);
        }

        // this the normal bundle workflow (bundle, optimizing, run-times, naming)
        await bundler.bundle({
          bundleGraph: mutableBundleGraph,
          config: (_this$configs$get = this.configs.get(plugin.name)) === null || _this$configs$get === void 0 ? void 0 : _this$configs$get.result,
          options: this.pluginOptions,
          logger,
          tracer
        });
        measurement && measurement.end();
        if (this.pluginOptions.mode === 'production') {
          let optimizeMeasurement;
          try {
            var _this$configs$get2;
            if (tracer.enabled) {
              optimizeMeasurement = tracer.createMeasurement(plugin.name, 'bundling:optimize', (0, _nullthrows().default)(measurementFilename));
            }
            await bundler.optimize({
              bundleGraph: mutableBundleGraph,
              config: (_this$configs$get2 = this.configs.get(plugin.name)) === null || _this$configs$get2 === void 0 ? void 0 : _this$configs$get2.result,
              options: this.pluginOptions,
              logger
            });
          } catch (e) {
            throw new (_diagnostic().default)({
              diagnostic: (0, _diagnostic().errorToDiagnostic)(e, {
                origin: plugin.name
              })
            });
          } finally {
            optimizeMeasurement && optimizeMeasurement.end();
            await (0, _dumpGraphToGraphViz.default)(
            // $FlowFixMe[incompatible-call]
            internalBundleGraph._graph, 'after_optimize');
          }
        }

        // Add dev dependency for the bundler. This must be done AFTER running it due to
        // the potential for lazy require() that aren't executed until the request runs.
        let devDepRequest = await (0, _DevDepRequest.createDevDependency)({
          specifier: name,
          resolveFrom
        }, this.previousDevDeps, this.options);
        await this.runDevDepRequest(devDepRequest);
      }
    } catch (e) {
      if (internalBundleGraph != null) {
        this.api.storeResult({
          bundleGraph: internalBundleGraph,
          changedAssets: new Map(),
          assetRequests: []
        }, this.cacheKey);
      }
      throw new (_diagnostic().default)({
        diagnostic: (0, _diagnostic().errorToDiagnostic)(e, {
          origin: name
        })
      });
    } finally {
      (0, _assert().default)(internalBundleGraph != null); // ensures the graph was created
      await (0, _dumpGraphToGraphViz.default)(
      // $FlowFixMe[incompatible-call]
      internalBundleGraph._graph, 'after_bundle', _BundleGraph2.bundleGraphEdgeTypes);
    }
    let changedRuntimes = new Map();
    if (!previousBundleGraphResult) {
      let namers = await this.config.getNamers();
      // inline bundles must still be named so the PackagerRunner
      // can match them to the correct packager/optimizer plugins.
      let bundles = internalBundleGraph.getBundles({
        includeInline: true
      });
      await Promise.all(bundles.map(bundle => this.nameBundle(namers, bundle, internalBundleGraph)));
      changedRuntimes = await (0, _applyRuntimes.default)({
        bundleGraph: internalBundleGraph,
        api: this.api,
        config: this.config,
        options: this.options,
        optionsRef: this.optionsRef,
        pluginOptions: this.pluginOptions,
        previousDevDeps: this.previousDevDeps,
        devDepRequests: this.devDepRequests,
        configs: this.configs
      });

      // Add dev deps for namers, AFTER running them to account for lazy require().
      for (let namer of namers) {
        let devDepRequest = await (0, _DevDepRequest.createDevDependency)({
          specifier: namer.name,
          resolveFrom: namer.resolveFrom
        }, this.previousDevDeps, this.options);
        await this.runDevDepRequest(devDepRequest);
      }
      this.validateBundles(internalBundleGraph);

      // Pre-compute the hashes for each bundle so they are only computed once and shared between workers.
      internalBundleGraph.getBundleGraphHash();
    }
    await (0, _dumpGraphToGraphViz.default)(
    // $FlowFixMe
    internalBundleGraph._graph, 'after_runtimes', _BundleGraph2.bundleGraphEdgeTypes);
    this.api.storeResult({
      bundleGraph: internalBundleGraph,
      changedAssets: new Map(),
      assetRequests: []
    }, this.cacheKey);
    return {
      bundleGraph: internalBundleGraph,
      changedAssets: changedRuntimes,
      assetRequests
    };
  }
  validateBundles(bundleGraph) {
    let bundles = bundleGraph.getBundles();
    let bundleNames = bundles.map(b => (0, _projectPath.joinProjectPath)(b.target.distDir, (0, _nullthrows().default)(b.name)));
    let seenNames = new Set();
    let duplicateNames = bundleNames.filter(name => {
      let isDuplicate = seenNames.has(name);
      seenNames.add(name);
      return isDuplicate;
    });
    (0, _assert().default)(duplicateNames.length === 0, 'Bundles must have unique names. Conflicting names: ' + duplicateNames.join(', '));
  }
  async nameBundle(namers, internalBundle, internalBundleGraph) {
    let bundle = _Bundle.Bundle.get(internalBundle, internalBundleGraph, this.options);
    let bundleGraph = new _BundleGraph.default(internalBundleGraph, _Bundle.NamedBundle.get.bind(_Bundle.NamedBundle), this.options);
    for (let namer of namers) {
      let measurement;
      try {
        var _this$configs$get3;
        measurement = _profiler().tracer.createMeasurement(namer.name, 'namer', bundle.id);
        let name = await namer.plugin.name({
          bundle,
          bundleGraph,
          config: (_this$configs$get3 = this.configs.get(namer.name)) === null || _this$configs$get3 === void 0 ? void 0 : _this$configs$get3.result,
          options: this.pluginOptions,
          logger: new (_logger().PluginLogger)({
            origin: namer.name
          }),
          tracer: new (_profiler().PluginTracer)({
            origin: namer.name,
            category: 'namer'
          })
        });
        if (name != null) {
          internalBundle.name = name;
          let {
            hashReference
          } = internalBundle;
          internalBundle.displayName = name.includes(hashReference) ? name.replace(hashReference, '[hash]') : name;
          return;
        }
      } catch (e) {
        throw new (_diagnostic().default)({
          diagnostic: (0, _diagnostic().errorToDiagnostic)(e, {
            origin: namer.name
          })
        });
      } finally {
        measurement && measurement.end();
      }
    }
    throw new Error('Unable to name bundle');
  }
}