Repository: electron-userland/electron-builder
File: packages/app-builder-lib/src/macPackager.ts (TypeScript)

Code before:
    const processTargetPack = async (target: Target) => {
      const targetName = target.name
      if (!(targetName === "mas" || targetName === "mas-dev")) {
        return;
      }

      const masBuildOptions = deepAssign({}, this.platformSpecificBuildOptions, this.config.mas)
      if (targetName === "mas-dev") {
        deepAssign(masBuildOptions, this.config.masDev, {
          type: "development",
        })
      }

      const targetOutDir = path.join(outDir, `${targetName}${getArchSuffix(arch, this.platformSpecificBuildOptions.defaultArch)}`)
      if (prepackaged == null) {
        await this.doPack(outDir, targetOutDir, "mas", arch, masBuildOptions, [target])
        await this.sign(path.join(targetOutDir, `${this.appInfo.productFilename}.app`), targetOutDir, masBuildOptions, arch)
      } else {
        await this.sign(prepackaged, targetOutDir, masBuildOptions, arch)
      }
    }

    async function processTargetsPack(targets: Array<Target>, index: number) {
      if (index < targets.length) {
        await processTargetPack(targets[index]);
        await processTargetsPack(targets, index + 1);
      }
    }

    await processTargetsPack(targets, 0);

    if (nonMasPromise != null) {
      await nonMasPromise
    }
  }

  private async sign(appPath: string, outDir: string | null, masOptions: MasConfiguration | null, arch: Arch | null): Promise<boolean> {
    if (!isSignAllowed()) {
      return false
    }

    const isMas = masOptions != null
    const options = masOptions == null ? this.platformSpecificBuildOptions : masOptions
    const qualifier = options.identity

    if (qualifier === null) {
      if (this.forceCodeSigning) {
        throw new InvalidConfigurationError("identity explicitly is set to null, but forceCodeSigning is set to true")
      }
      log.info({ reason: "identity explicitly is set to null" }, "skipped macOS code signing")
      return false

Review comment (bug):
I love that you extracted the logic to a single `processTargetPack`

This logic should fail if there are more than two targets specified though, such as `"target":  ["mas", "dmg", "zip"],`, right?

How is this recursion different than the current loop of `for (const target of targets)`?

I don't see anything different here from this code
```
for (const target of targets) {
      await processTargetPack(target)
}
```


Code after:
      }

      const targetOutDir = path.join(outDir, `${targetName}${getArchSuffix(arch, this.platformSpecificBuildOptions.defaultArch)}`)
      if (prepackaged == null) {
        await this.doPack(outDir, targetOutDir, "mas", arch, masBuildOptions, [target])
        await this.sign(path.join(targetOutDir, `${this.appInfo.productFilename}.app`), targetOutDir, masBuildOptions, arch)
      } else {
        await this.sign(prepackaged, targetOutDir, masBuildOptions, arch)
      }
    }

    if (!hasMas || targets.length > 1) {
      const appPath = prepackaged == null ? path.join(this.computeAppOutDir(outDir, arch), `${this.appInfo.productFilename}.app`) : prepackaged
      if (prepackaged == null) {
        await this.doPack(outDir, path.dirname(appPath), this.platform.nodeName as ElectronPlatformName, arch, this.platformSpecificBuildOptions, targets)
      }
      this.packageInDistributableFormat(appPath, arch, targets, taskManager)
    }
  }

  private async sign(appPath: string, outDir: string | null, masOptions: MasConfiguration | null, arch: Arch | null): Promise<boolean> {
    if (!isSignAllowed()) {
      return false
    }

    const isMas = masOptions != null
    const options = masOptions == null ? this.platformSpecificBuildOptions : masOptions
    const qualifier = options.identity

    if (qualifier === null) {
      if (this.forceCodeSigning) {
        throw new InvalidConfigurationError("identity explicitly is set to null, but forceCodeSigning is set to true")
      }
      log.info({ reason: "identity explicitly is set to null" }, "skipped macOS code signing")
      return false
    }

    const keychainFile = (await this.codeSigningInfo.value).keychainFile
    const explicitType = options.type
    const type = explicitType || "distribution"
    const isDevelopment = type === "development"
    const certificateTypes = getCertificateTypes(isMas, isDevelopment)

    let identity = null
    for (const certificateType of certificateTypes) {
      identity = await findIdentity(certificateType, qualifier, keychainFile)
      if (identity != null) {
        break
      }
    }