import * as Effect from "effect/Effect"; import type { Command } from "effect/unstable/cli"; import { Prompt } from "effect/unstable/cli"; import type { PlatformFilter, PlatformTarget, Resolution } from "@packall/core"; import { parsePlatformTarget, platformTargets } from "@packall/core"; import type { Config } from "../config.js"; /** * The `os-arch` pairs the resolved packages declare. * * Negations and wildcards are skipped — a package saying "not win32" tells you * nothing about which platforms exist to choose between. */ export const availablePlatforms = (resolution: Resolution): ReadonlyArray => { const found = new Set(); for (const pkg of resolution.packages) { const oses = usableValues(pkg.manifest.os); if (oses.length === 0) continue; const cpus = usableValues(pkg.manifest.cpu); for (const os of oses) { if (cpus.length === 0) found.add(os); else for (const cpu of cpus) found.add(`${os}-${cpu}`); } } return [...found].toSorted(); }; const usableValues = (values: ReadonlyArray | undefined): ReadonlyArray => (values ?? []).flatMap((value) => { const normalized = value.trim().toLowerCase(); const usable = !normalized.startsWith("!") && normalized !== "any" && normalized !== "*"; return usable ? normalized : []; }); /** * Offers the platforms this resolution contains, and narrows to the chosen set. * * Only when `--interactive` is on, `--platform` was not already given, and * optional dependencies are being followed — there is nothing to ask about * otherwise. Answering with everything selected is the same as not narrowing. */ export const choosePlatforms = ( resolution: Resolution, config: Config, current: PlatformFilter, interactive: boolean, ): Effect.Effect => Effect.gen(function* () { const ask = config.interactive && interactive && !config.yes && !config.json && config.platform.length === 0 && config.optional; if (!ask) return current; const available = availablePlatforms(resolution); if (available.length < 2) return current; const chosen = yield* Prompt.run( Prompt.multiSelect({ message: `${available.length} platforms are present in this closure. Bundle which?`, choices: available.map((platform) => ({ title: platform, value: platform })), }), ).pipe(Effect.orElseSucceed((): Array => [])); // Nothing picked, or everything picked: both mean "do not narrow". if (chosen.length === 0 || chosen.length === available.length) return current; const targets: ReadonlyArray = chosen.flatMap( (platform) => parsePlatformTarget(platform) ?? [], ); return targets.length === 0 ? current : platformTargets(targets); });