text: // @flow strict-local
  
  import type {ContentKey} from '@parcel/graph';
  import type {Async} from '@parcel/types';
  import type {SharedReference} from '@parcel/workers';
  import type {StaticRunOpts} from '../RequestTracker';
  import type {PackagedBundleInfo} from '../types';
  import type BundleGraph from '../BundleGraph';
  import type {BundleInfo} from '../PackagerRunner';
  
  import {HASH_REF_PREFIX} from '../constants';
  import {serialize} from '../serializer';
  import {joinProjectPath} from '../projectPath';
  import nullthrows from 'nullthrows';
  import {hashString} from '@parcel/hash';
  import {createPackageRequest} from './PackageRequest';
  import createWriteBundleRequest from './WriteBundleRequest';
  
  type WriteBundlesRequestInput = {|
    bundleGraph: BundleGraph,
    optionsRef: SharedReference,
  |};
  
  type RunInput = {|
    input: WriteBundlesRequestInput,
    ...StaticRunOpts,
  |};
  
  export type WriteBundlesRequest = {|
    id: ContentKey,
    +type: 'write_bundles_request',
    run: RunInput => Async<Map<string, PackagedBundleInfo>>,
    input: WriteBundlesRequestInput,
  |};
  
  /**
   * Packages, optimizes, and writes all bundles to the dist directory.
   */
  export default function createWriteBundlesRequest(
    input: WriteBundlesRequestInput,
  ): WriteBundlesRequest {
    return {
      type: 'write_bundles_request',
      id: 'write_bundles:' + input.bundleGraph.getBundleGraphHash(),
      run,
      input,
    };
  }
  
  async function run({input, api, farm, options}: RunInput) {
    let {bundleGraph, optionsRef} = input;
    let {ref, dispose} = await farm.createSharedReference(
      bundleGraph,
      serialize(bundleGraph),
    );
  
    api.invalidateOnOptionChange('shouldContentHash');
  
    let res = new Map();
    let bundleInfoMap: {|
      [string]: BundleInfo,
    |} = {};
    let writeEarlyPromises = {};
    let hashRefToNameHash = new Map();
    let bundles = bundleGraph.getBundles().filter(bundle => {
      // Do not package and write placeholder bundles to disk. We just
      // need to update the name so other bundles can reference it.
      if (bundle.isPlaceholder) {
        let hash = bundle.id.slice(-8);
        hashRefToNameHash.set(bundle.hashReference, hash);
        let name = nullthrows(bundle.name).replace(bundle.hashReference, hash);
        res.set(bundle.id, {
          filePath: joinProjectPath(bundle.target.distDir, name),
          stats: {
            time: 0,
            size: 0,
          },
        });
        return false;
      }
  
      return true;
    });
  
    try {
      await Promise.all(
        bundles.map(async bundle => {
          let request = createPackageRequest({
            bundle,
            bundleGraph,
            bundleGraphReference: ref,
            optionsRef,
          });
          let info = await api.runRequest(request);
  
          bundleInfoMap[bundle.id] = info;
          if (!info.hashReferences.length) {
            hashRefToNameHash.set(
              bundle.hashReference,
              options.shouldContentHash
                ? info.hash.slice(-8)
                : bundle.id.slice(-8),
            );
            let writeBundleRequest = createWriteBundleRequest({
              bundle,
              info,
              hashRefToNameHash,
              bundleGraph,
            });
            writeEarlyPromises[bundle.id] = api.runRequest(writeBundleRequest);
          }
        }),
      );
      assignComplexNameHashes(hashRefToNameHash, bundles, bundleInfoMap, options);
      await Promise.all(
        bundles.map(bundle => {
          let promise =
            writeEarlyPromises[bundle.id] ??
            api.runRequest(
              createWriteBundleRequest({
                bundle,
                info: bundleInfoMap[bundle.id],
                hashRefToNameHash,
                bundleGraph,
              }),
            );
  
          return promise.then(r => res.set(bundle.id, r));
        }),
      );
  
      api.storeResult(res);
      return res;
    } finally {
      await dispose();
    }
  }
  
  function assignComplexNameHashes(
    hashRefToNameHash,
    bundles,
    bundleInfoMap,
    options,
  ) {
    for (let bundle of bundles) {
      if (hashRefToNameHash.get(bundle.hashReference) != null) {
        continue;
      }
  
      hashRefToNameHash.set(
        bundle.hashReference,
        options.shouldContentHash
          ? hashString(
              [...getBundlesIncludedInHash(bundle.id, bundleInfoMap)]
                .map(bundleId => bundleInfoMap[bundleId].hash)
                .join(':'),
            ).slice(-8)
          : bundle.id.slice(-8),
      );
    }
  }
  
  function getBundlesIncludedInHash(
    bundleId,
    bundleInfoMap,
    included = new Set(),
  ) {
    included.add(bundleId);
    for (let hashRef of bundleInfoMap[bundleId].hashReferences) {
      let referencedId = getIdFromHashRef(hashRef);
      if (!included.has(referencedId)) {
        getBundlesIncludedInHash(referencedId, bundleInfoMap, included);
      }
    }
  
    return included;
  }
  
  function getIdFromHashRef(hashRef: string) {
    return hashRef.slice(HASH_REF_PREFIX.length);
  }
  