sortedModules = (
  compartmentDescriptors,
  compartmentSources,
  entryCompartmentName,
  entryModuleSpecifier,
  exitModuleSpecifiers,
) => {
  /** @type {BundleModule<unknown>[]} */
  const modules = [];
  /** @type {Map<string, string>} aliaes */
  const aliases = new Map();
  /** @type {Set<string>} seen */
  const seen = new Set();

  for (const exit of exitModuleSpecifiers) {
    modules.push({
      key: exit,
      exit,
      // @ts-expect-error
      index: undefined,
      // @ts-expect-error
      bundlerKit: null,
    });
  }

  /**
   * @param {string} compartmentName
   * @param {string} moduleSpecifier
   */
  const recur = (compartmentName, moduleSpecifier) => {
    const key = `${compartmentName}#${moduleSpecifier}`;
    if (seen.has(key)) {
      return key;
    }
    seen.add(key);

    const source = compartmentSources[compartmentName][moduleSpecifier];
    if (source !== undefined) {
      const { record, parser, deferredError, bytes, sourceDirname, exit } =
        source;
      if (exit !== undefined) {
        return exit;
      }
      assert(
        bytes !== undefined,
        `No bytes for ${moduleSpecifier} in ${compartmentName}`,
      );
      assert(
        parser !== undefined,
        `No parser for ${moduleSpecifier} in ${compartmentName}`,
      );
      assert(
        sourceDirname !== undefined,
        `No sourceDirname for ${moduleSpecifier} in ${compartmentName}`,
      );
      if (deferredError) {
        throw Error(
          `Cannot bundle: encountered deferredError ${deferredError}`,
        );
      }
      if (record) {
        const { imports = [], reexports = [] } =
          /** @type {PrecompiledStaticModuleInterface} */ (record);
        const resolvedImports = Object.create(null);
        for (const importSpecifier of [...imports, ...reexports]) {
          // If we ever support another module resolution algorithm, that
          // should be indicated in the compartment descriptor by name and the
          // corresponding behavior selected here.
          const resolvedSpecifier = resolve(importSpecifier, moduleSpecifier);
          resolvedImports[importSpecifier] = recur(
            compartmentName,
            resolvedSpecifier,
          );
        }

        modules.push({
          key,
          compartmentName,
          moduleSpecifier,
          sourceDirname,
          parser,
          record,
          resolvedImports,
          bytes,
          // @ts-expect-error
          index: undefined,
          // @ts-expect-error
          bundlerKit: null,
        });

        return key;
      }
    } else {
      const descriptor =
        compartmentDescriptors[compartmentName].modules[moduleSpecifier];
      if (descriptor) {
        const {
          compartment: aliasCompartmentName,
          module: aliasModuleSpecifier,
        } = descriptor;
        if (
          aliasCompartmentName !== undefined &&
          aliasModuleSpecifier !== undefined
        ) {
          const aliasKey = recur(aliasCompartmentName, aliasModuleSpecifier);
          aliases.set(key, aliasKey);
          return aliasKey;
        }
      }
    }

    throw Error(
      `Cannot bundle: cannot follow module import ${moduleSpecifier} in compartment ${compartmentName}`,
    );
  };

  recur(entryCompartmentName, entryModuleSpecifier);

  return { modules, aliases };
}
Produces a list of modules in the order they should be evaluated, and
a side-table for following aliases.
The modules are produce in topological postorder, such that the entry
module appears last.
The post-order traversal does not revisit modules that appear in cycles.

Synthesizes a unique key for each module and translates
each module's imports to their corresponding keys.
Some import keys are aliased to other keys, such that walking from
key to value in the aliases side table will eventually arrive at
the key of a module that is present in the modules list.

The first modules are place-holders for the modules that exit
the compartment map to the host's module system.

@param {Record<string, CompartmentDescriptor>} compartmentDescriptors
@param {Record<string, CompartmentSources>} compartmentSources
@param {string} entryCompartmentName
@param {string} entryModuleSpecifier
@param {Array<string>} exitModuleSpecifiers