makeArchiveImportHookMaker = (
  get,
  compartments,
  archiveLocation,
  parserForLanguage,
  computeSha512 = undefined,
  computeSourceLocation = undefined,
  exitModuleImportHook = undefined,
  computeSourceMapLocation = undefined,
) => {
  // per-assembly:
  /** @type {ImportHookMaker} */
  const makeImportHook = ({
    packageLocation,
    packageName,
    attenuators,
    // note `compartments` are not passed to makeImportHook because
    // the reference was passed to makeArchiveImportHookMaker.
  }) => {
    // per-compartment:
    const compartmentDescriptor = compartments[packageLocation];
    const { modules } = compartmentDescriptor;
    /** @type {ImportHook} */
    const importHook = async moduleSpecifier => {
      await null;
      // per-module:
      const module = modules[moduleSpecifier];
      if (module === undefined) {
        if (exitModuleImportHook) {
          // At this point in archive importing, if a module is not found and
          // exitModuleImportHook exists, the only possibility is that the
          // module is a "builtin" module and the policy needs to be enforced.
          enforceModulePolicy(moduleSpecifier, compartmentDescriptor, {
            exit: true,
            errorHint: `Blocked in loading. ${q(
              moduleSpecifier,
            )} was not in the archive and an attempt was made to load it as a builtin`,
          });
          const record = await exitModuleImportHook(
            moduleSpecifier,
            packageLocation,
          );
          if (record) {
            // note it's not being marked as exit in sources
            // it could get marked and the second pass, when the archive is being executed, would have the data
            // to enforce which exits can be dynamically imported
            return {
              record: await attenuateModuleHook(
                moduleSpecifier,
                record,
                compartmentDescriptor.policy,
                attenuators,
              ),
              specifier: moduleSpecifier,
            };
          } else {
            // if exitModuleImportHook is allowed, the mechanism to defer
            // errors in archive creation is never used. We don't want to
            // throw until the module execution is attempted. This is because
            // the cjs parser eagerly looks for require calls, and if it finds
            // one, it will try to import the module even if the require is
            // never reached.
            return postponeErrorToExecute(
              `Cannot find external module ${q(moduleSpecifier)} in package ${q(
                packageLocation,
              )} in archive ${q(archiveLocation)}`,
            );
          }
        }
        throw Error(
          `Cannot find module ${q(moduleSpecifier)} in package ${q(
            packageLocation,
          )} in archive ${q(archiveLocation)}`,
        );
      }
      if (module.deferredError !== undefined) {
        return postponeErrorToExecute(module.deferredError);
      }
      if (module.parser === undefined) {
        throw Error(
          `Cannot parse module ${q(moduleSpecifier)} in package ${q(
            packageLocation,
          )} in archive ${q(archiveLocation)}`,
        );
      }
      const parser = parserForLanguage[module.parser];
      if (parser === undefined) {
        throw Error(
          `Cannot parse ${q(module.parser)} module ${q(
            moduleSpecifier,
          )} in package ${q(packageLocation)} in archive ${q(archiveLocation)}`,
        );
      }
      const { parse } = parser;
      const moduleLocation = `${packageLocation}/${module.location}`;
      const moduleBytes = get(moduleLocation);

      if (computeSha512 !== undefined && module.sha512 !== undefined) {
        const sha512 = computeSha512(moduleBytes);
        if (sha512 !== module.sha512) {
          throw Error(
            `Module ${q(module.location)} of package ${q(
              packageLocation,
            )} in archive ${q(
              archiveLocation,
            )} failed a SHA-512 integrity check`,
          );
        }
      }

      let sourceLocation = `file:///${moduleLocation}`;
      if (packageName !== undefined) {
        const base = packageName.split('/').slice(-1).join('/');
        sourceLocation = `.../${join(base, moduleSpecifier)}`;
      }
      if (computeSourceLocation !== undefined) {
        sourceLocation =
          computeSourceLocation(packageLocation, moduleSpecifier) ||
          sourceLocation;
      }

      let sourceMapUrl;
      if (
        computeSourceMapLocation !== undefined &&
        module.sha512 !== undefined
      ) {
        sourceMapUrl = computeSourceMapLocation({
          compartment: packageLocation,
          module: moduleSpecifier,
          location: sourceLocation,
          sha512: module.sha512,
        });
      }

      // eslint-disable-next-line no-await-in-loop
      const { record } = await parse(
        moduleBytes,
        moduleSpecifier,
        sourceLocation,
        packageLocation,
        {
          sourceMapUrl,
          compartmentDescriptor,
        },
      );
      return { record, specifier: moduleSpecifier };
    };
    return importHook;
  };
  return makeImportHook;
}
@param {(path: string) => Uint8Array} get
@param {Record<string, CompartmentDescriptor>} compartments
@param {string} archiveLocation
@param {ParserForLanguage} parserForLanguage
@param {HashFn} [computeSha512]
@param {ComputeSourceLocationHook} [computeSourceLocation]
@param {ExitModuleImportHook} [exitModuleImportHook]
@param {ComputeSourceMapLocationHook} [computeSourceMapLocation]
@returns {ImportHookMaker}