rolldown-pnpm-config 2.1.7 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/cli/commands/export.js +1 -1
- package/cli/commands/upgrade.js +61 -57
- package/cli/exit-codes.js +16 -0
- package/cli/load-config.js +2 -1
- package/cli/main.js +10 -6
- package/cli/resolve.js +2 -1
- package/cli/select-file.js +16 -10
- package/cli/summary.js +2 -2
- package/cli/ui/walk-screen.js +5 -5
- package/cli/version.js +1 -1
- package/cli/walk-reducer.js +9 -17
- package/cli/workspace-file.js +1 -1
- package/cli/workspace-resolve.js +11 -9
- package/package.json +8 -8
- package/patches/discover.js +18 -3
- package/plugin/freeze.js +4 -3
- package/runtime/ctx.js +1 -1
package/README.md
CHANGED
|
@@ -78,7 +78,7 @@ The bundled `rolldown-pnpm-config upgrade` command rewrites the version ranges i
|
|
|
78
78
|
```bash
|
|
79
79
|
npx rolldown-pnpm-config upgrade
|
|
80
80
|
# opens a radio-group table, one row per package, then on <Enter>:
|
|
81
|
-
# Applied <n>
|
|
81
|
+
# Applied <n> changes.
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
The command shows an interactive table by default — every catalog package at once, one row per package, `●`/`○` bubbles, modeled on `pnpm up -i`. `--yes` takes the latest in-range version for every package without prompting and fails hard on any warning or unresolvable package name; `--dry-run` runs the identical table and skips only the write; `--catalog <name>` restricts the table to one catalog. Pass `--preview` for a non-interactive projection of what an upgrade would do, with `--full` to show up-to-date entries too. The output is colorized in a supporting terminal. For packages that declare a `strategy`, the command also resyncs their materialized peer range, preserving any prerelease identifier rather than rebuilding it from parsed version parts. See [upgrading catalogs](https://github.com/spencerbeggs/rolldown-pnpm-config/blob/main/docs/05-upgrading-catalogs.md) for the full surface.
|
package/cli/commands/export.js
CHANGED
|
@@ -15,9 +15,9 @@ import { failureDoc } from "../render/report.js";
|
|
|
15
15
|
import { findConfigFiles, pickConfigCandidate } from "../select-file.js";
|
|
16
16
|
import { overlayWorkspace } from "../workspace-overlay.js";
|
|
17
17
|
import { Data, Effect, Option, Predicate } from "effect";
|
|
18
|
-
import { existsSync, writeFileSync } from "node:fs";
|
|
19
18
|
import { dirname, relative } from "node:path";
|
|
20
19
|
import { CliDoc, CliMessage } from "@effected/cli";
|
|
20
|
+
import { existsSync, writeFileSync } from "node:fs";
|
|
21
21
|
import { Argument, Command, Flag } from "effect/cli";
|
|
22
22
|
|
|
23
23
|
// src/cli/commands/export.ts
|
package/cli/commands/upgrade.js
CHANGED
|
@@ -18,10 +18,10 @@ import { summaryDoc } from "../summary.js";
|
|
|
18
18
|
import { validateEdits } from "../validate.js";
|
|
19
19
|
import { buildWalkItems } from "../walk-plan.js";
|
|
20
20
|
import { findWorkspaceRoot, makeWorkspaceResolver } from "../workspace-resolve.js";
|
|
21
|
-
import { Clock,
|
|
22
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { Clock, Data, Effect, Option, Result } from "effect";
|
|
23
22
|
import { dirname } from "node:path";
|
|
24
|
-
import { CliDoc, CliExit, CliInteractive, CliMessage, Doc } from "@effected/cli";
|
|
23
|
+
import { CliDoc, CliExit, CliFormat, CliInteractive, CliMessage, Doc, Fmt } from "@effected/cli";
|
|
24
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
25
25
|
import { Argument, Command, Flag } from "effect/cli";
|
|
26
26
|
import { CliUi } from "@effected/cli/ui";
|
|
27
27
|
import { ReleaseAgeGate } from "@effected/npm";
|
|
@@ -40,12 +40,12 @@ var UpgradeError = class extends Data.TaggedError("UpgradeError") {
|
|
|
40
40
|
};
|
|
41
41
|
/**
|
|
42
42
|
* An invalid flag combination: a usage error, so it exits 64 (BSD
|
|
43
|
-
* `EX_USAGE`) rather than the 1 a resolution failure exits with.
|
|
43
|
+
* `EX_USAGE`) rather than the 1 a resolution failure exits with. The code is
|
|
44
|
+
* mapped by tag in `EXIT_CODES`, which `main` hands to `CliRuntime.main`.
|
|
44
45
|
*
|
|
45
46
|
* @internal
|
|
46
47
|
*/
|
|
47
48
|
var UpgradeUsageError = class extends Data.TaggedError("UpgradeUsageError") {
|
|
48
|
-
[Runtime.errorExitCode] = 64;
|
|
49
49
|
/** The failure report `CliRuntime.main` prints: the message, without the class name. */
|
|
50
50
|
[CliDoc]() {
|
|
51
51
|
return failureDoc(this.message);
|
|
@@ -80,7 +80,7 @@ function readCatalogSource(file) {
|
|
|
80
80
|
});
|
|
81
81
|
const { entries, skipped } = yield* Effect.try({
|
|
82
82
|
try: () => discoverCatalogEntries(source, file),
|
|
83
|
-
catch: (e) => new UpgradeError({ message:
|
|
83
|
+
catch: (e) => new UpgradeError({ message: Fmt.describe(e) })
|
|
84
84
|
});
|
|
85
85
|
return {
|
|
86
86
|
source,
|
|
@@ -279,7 +279,7 @@ function runUpgrade(opts) {
|
|
|
279
279
|
kind: "peer-strategy"
|
|
280
280
|
}));
|
|
281
281
|
const { accepted, rejected } = yield* validateEdits(edits, versionsByPkg.raw);
|
|
282
|
-
if (rejected.length > 0) return yield* Effect.fail(new UpgradeError({ message: `Refusing to write unsatisfiable range
|
|
282
|
+
if (rejected.length > 0) return yield* Effect.fail(new UpgradeError({ message: `Refusing to write ${Fmt.plural(rejected.length, "unsatisfiable range")}:\n${rejected.map((r) => ` ${r.reason}`).join("\n")}` }));
|
|
283
283
|
const allEdits = [...accepted, ...interopEdits];
|
|
284
284
|
if (allEdits.length > 0 && !opts.dryRun) {
|
|
285
285
|
const next = applyEdits(source, allEdits);
|
|
@@ -331,7 +331,7 @@ function applyInteropAndDecisions(file, source, nonInteropEdits, interopEdits) {
|
|
|
331
331
|
* @internal
|
|
332
332
|
*/
|
|
333
333
|
function nothingToUpgradeMessage(totalItems) {
|
|
334
|
-
return totalItems === 0 ? "Nothing to upgrade — no catalog packages found." : `Nothing to upgrade — ${totalItems
|
|
334
|
+
return totalItems === 0 ? "Nothing to upgrade — no catalog packages found." : `Nothing to upgrade — ${Fmt.plural(totalItems, "package")} already up to date.`;
|
|
335
335
|
}
|
|
336
336
|
/**
|
|
337
337
|
* The message for packages the registry could not resolve. Almost always a
|
|
@@ -342,7 +342,7 @@ function nothingToUpgradeMessage(totalItems) {
|
|
|
342
342
|
*/
|
|
343
343
|
function unresolvedMessage(unresolved) {
|
|
344
344
|
const list = unresolved.map((p) => ` ${p}`).join("\n");
|
|
345
|
-
return `Could not resolve ${unresolved.length
|
|
345
|
+
return `Could not resolve ${Fmt.plural(unresolved.length, "package")} from the registry — check each name for a typo, or your registry auth:\n${list}`;
|
|
346
346
|
}
|
|
347
347
|
/** Project walk items to the non-interactive default decisions (latest-in-range, plus peer-only keeps). @internal */
|
|
348
348
|
function projectDecisions(items, full) {
|
|
@@ -413,7 +413,7 @@ function checkOutcome(changed) {
|
|
|
413
413
|
return {
|
|
414
414
|
exitCode: 1,
|
|
415
415
|
doc: [
|
|
416
|
-
Doc.line(`Catalog drift detected in ${changed.length
|
|
416
|
+
Doc.line(`Catalog drift detected in ${Fmt.plural(changed.length, "package")}:`),
|
|
417
417
|
Doc.lines(changed.map((c) => ` ${c.name} (${c.source})`), { wrap: false }),
|
|
418
418
|
Doc.line([
|
|
419
419
|
"Run ",
|
|
@@ -434,12 +434,12 @@ function driftRowJson(row) {
|
|
|
434
434
|
};
|
|
435
435
|
}
|
|
436
436
|
/**
|
|
437
|
-
* The `--check --json` outcome. stdout carries exactly one
|
|
438
|
-
* document (`
|
|
439
|
-
* resolution-failure family, which keeps its non-zero exit but
|
|
440
|
-
* leave a bash gate with exit 1 and an empty stdout. A resolution
|
|
441
|
-
* also returned as `failure`, which the command fails with so the
|
|
442
|
-
* human report lands on stderr.
|
|
437
|
+
* The `--check --format json` outcome. stdout carries exactly one JSON
|
|
438
|
+
* document (`data`, printed by `CliFormat.print` on one line) in EVERY case —
|
|
439
|
+
* including the resolution-failure family, which keeps its non-zero exit but
|
|
440
|
+
* must never leave a bash gate with exit 1 and an empty stdout. A resolution
|
|
441
|
+
* failure is also returned as `failure`, which the command fails with so the
|
|
442
|
+
* labelled human report lands on stderr.
|
|
443
443
|
*
|
|
444
444
|
* @internal
|
|
445
445
|
*/
|
|
@@ -448,14 +448,14 @@ function checkJsonOutcome(result) {
|
|
|
448
448
|
const { message, kind } = result.failure;
|
|
449
449
|
return {
|
|
450
450
|
exitCode: 1,
|
|
451
|
-
|
|
451
|
+
data: {
|
|
452
452
|
command: "check",
|
|
453
453
|
inSync: false,
|
|
454
454
|
error: {
|
|
455
455
|
kind: kind ?? "resolution",
|
|
456
456
|
message
|
|
457
457
|
}
|
|
458
|
-
}
|
|
458
|
+
},
|
|
459
459
|
failure: new CheckFailedError({
|
|
460
460
|
message,
|
|
461
461
|
...kind !== void 0 ? { kind } : {}
|
|
@@ -465,15 +465,15 @@ function checkJsonOutcome(result) {
|
|
|
465
465
|
const changed = result.success.changed;
|
|
466
466
|
return {
|
|
467
467
|
exitCode: changed.length === 0 ? 0 : 1,
|
|
468
|
-
|
|
468
|
+
data: {
|
|
469
469
|
command: "check",
|
|
470
470
|
inSync: changed.length === 0,
|
|
471
471
|
drift: changed.map(driftRowJson)
|
|
472
|
-
}
|
|
472
|
+
}
|
|
473
473
|
};
|
|
474
474
|
}
|
|
475
475
|
/**
|
|
476
|
-
* The `--yes --json` / `--dry-run --json` outcome. `applied` reports whether
|
|
476
|
+
* The `--yes --format json` / `--dry-run --format json` outcome. `applied` reports whether
|
|
477
477
|
* the run wrote at least one change — always false under dry-run AND on an
|
|
478
478
|
* already-in-sync run, so a consumer keying on it never reads a no-op as a
|
|
479
479
|
* real apply; `changed` uses the same row object as check's `drift`. A failure
|
|
@@ -487,19 +487,19 @@ function upgradeJsonOutcome(result, dryRun) {
|
|
|
487
487
|
if (Result.isFailure(result)) {
|
|
488
488
|
const { message, kind } = result.failure;
|
|
489
489
|
return {
|
|
490
|
-
|
|
490
|
+
data: {
|
|
491
491
|
command: "upgrade",
|
|
492
492
|
applied: false,
|
|
493
493
|
error: {
|
|
494
494
|
kind: kind ?? "resolution",
|
|
495
495
|
message
|
|
496
496
|
}
|
|
497
|
-
}
|
|
497
|
+
},
|
|
498
498
|
failure: result.failure
|
|
499
499
|
};
|
|
500
500
|
}
|
|
501
501
|
const r = result.success;
|
|
502
|
-
return {
|
|
502
|
+
return { data: {
|
|
503
503
|
command: "upgrade",
|
|
504
504
|
applied: !dryRun && r.updated > 0,
|
|
505
505
|
updated: r.updated,
|
|
@@ -510,21 +510,21 @@ function upgradeJsonOutcome(result, dryRun) {
|
|
|
510
510
|
ceiling: c.ceiling,
|
|
511
511
|
blockedBy: c.blockedBy
|
|
512
512
|
}))
|
|
513
|
-
}
|
|
513
|
+
} };
|
|
514
514
|
}
|
|
515
515
|
/**
|
|
516
|
-
* Reject `--json` outside the non-interactive modes it exists for. JSON
|
|
517
|
-
* is a machine contract (a GitHub Action parsing stdout from bash); the
|
|
516
|
+
* Reject `--format json` outside the non-interactive modes it exists for. JSON
|
|
517
|
+
* mode is a machine contract (a GitHub Action parsing stdout from bash); the
|
|
518
518
|
* interactive table and the preview views have no meaningful document to emit.
|
|
519
|
-
* Returns the usage error to fail with (exit 64), or null
|
|
520
|
-
* is valid.
|
|
519
|
+
* Returns the usage error to fail with (exit 64, see `EXIT_CODES`), or null
|
|
520
|
+
* when the combination is valid.
|
|
521
521
|
*
|
|
522
522
|
* @internal
|
|
523
523
|
*/
|
|
524
|
-
function
|
|
525
|
-
if (
|
|
526
|
-
if (flags.preview) return new UpgradeUsageError({ message: "--json cannot be combined with --preview; use --check, --yes, or --dry-run" });
|
|
527
|
-
if (!flags.check && !flags.yes && !flags.dryRun) return new UpgradeUsageError({ message: "--json requires a non-interactive mode: combine it with --check, --yes, or --dry-run" });
|
|
524
|
+
function validateFormatMode(flags) {
|
|
525
|
+
if (flags.format !== "json") return null;
|
|
526
|
+
if (flags.preview) return new UpgradeUsageError({ message: "--format json cannot be combined with --preview; use --check, --yes, or --dry-run" });
|
|
527
|
+
if (!flags.check && !flags.yes && !flags.dryRun) return new UpgradeUsageError({ message: "--format json requires a non-interactive mode: combine it with --check, --yes, or --dry-run" });
|
|
528
528
|
return null;
|
|
529
529
|
}
|
|
530
530
|
/**
|
|
@@ -607,7 +607,9 @@ const catalogOption = Flag.String("catalog").pipe(Flag.withDescription("Only upg
|
|
|
607
607
|
const previewFlag = Flag.Boolean("preview").pipe(Flag.withDescription("Print the default picks as a summary and exit"), Flag.withDefault(false));
|
|
608
608
|
const fullFlag = Flag.Boolean("full").pipe(Flag.withDescription("Include up-to-date packages in a printed summary"), Flag.withDefault(false));
|
|
609
609
|
const checkFlag = Flag.Boolean("check").pipe(Flag.withDescription("Exit 1 when --yes would change anything (a drift gate; never writes)"), Flag.withDefault(false));
|
|
610
|
-
const
|
|
610
|
+
const formatFlag = CliFormat.flag(["auto", "json"], { description: "Output format: auto (for whoever is reading) or json (one JSON document on stdout, with --check, --yes or --dry-run)" });
|
|
611
|
+
/** JSON mode writes one single-line document, so a bash gate can pipe it straight into jq. */
|
|
612
|
+
const ONE_LINE = { indent: 0 };
|
|
611
613
|
/**
|
|
612
614
|
* The "upgrade" command. The default path runs the interactive table;
|
|
613
615
|
* --yes applies latest-in-range non-interactively; --dry-run runs the identical
|
|
@@ -624,10 +626,10 @@ const upgradeCommand = Command.make("upgrade", {
|
|
|
624
626
|
preview: previewFlag,
|
|
625
627
|
full: fullFlag,
|
|
626
628
|
check: checkFlag,
|
|
627
|
-
|
|
628
|
-
}, ({ file: fileOpt, yes, dryRun, catalog, preview, full, check,
|
|
629
|
-
const jsonRejection =
|
|
630
|
-
|
|
629
|
+
format: formatFlag
|
|
630
|
+
}, ({ file: fileOpt, yes, dryRun, catalog, preview, full, check, format }) => Effect.gen(function* () {
|
|
631
|
+
const jsonRejection = validateFormatMode({
|
|
632
|
+
format,
|
|
631
633
|
check,
|
|
632
634
|
yes,
|
|
633
635
|
dryRun,
|
|
@@ -644,26 +646,28 @@ const upgradeCommand = Command.make("upgrade", {
|
|
|
644
646
|
})), Effect.result);
|
|
645
647
|
if (check) {
|
|
646
648
|
const result = yield* nonInteractive(true);
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
if (
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
return yield* Effect.fail(new CheckFailedError({
|
|
656
|
-
message,
|
|
657
|
-
...kind !== void 0 ? { kind } : {}
|
|
658
|
-
}));
|
|
649
|
+
const json = checkJsonOutcome(result);
|
|
650
|
+
const failure = json.failure;
|
|
651
|
+
if (failure !== void 0) {
|
|
652
|
+
if (format === "json") yield* CliFormat.print("json", {
|
|
653
|
+
doc: () => failure[CliDoc](),
|
|
654
|
+
json: () => json.data
|
|
655
|
+
}, ONE_LINE);
|
|
656
|
+
return yield* Effect.fail(failure);
|
|
659
657
|
}
|
|
660
|
-
const outcome = checkOutcome(result.success.changed);
|
|
661
|
-
yield*
|
|
658
|
+
const outcome = checkOutcome(Result.isSuccess(result) ? result.success.changed : []);
|
|
659
|
+
yield* CliFormat.print(format, {
|
|
660
|
+
doc: () => outcome.doc,
|
|
661
|
+
json: () => json.data
|
|
662
|
+
}, ONE_LINE);
|
|
662
663
|
return yield* CliExit.set(outcome.exitCode);
|
|
663
664
|
}
|
|
664
|
-
if (json) {
|
|
665
|
+
if (format === "json") {
|
|
665
666
|
const o = upgradeJsonOutcome(yield* nonInteractive(false), dryRun);
|
|
666
|
-
yield*
|
|
667
|
+
yield* CliFormat.print("json", {
|
|
668
|
+
doc: () => [],
|
|
669
|
+
json: () => o.data
|
|
670
|
+
}, ONE_LINE);
|
|
667
671
|
if (o.failure !== void 0) return yield* Effect.fail(o.failure);
|
|
668
672
|
return;
|
|
669
673
|
}
|
|
@@ -687,7 +691,7 @@ const upgradeCommand = Command.make("upgrade", {
|
|
|
687
691
|
workspaceResolver,
|
|
688
692
|
onProgress: resolveProgress(report)
|
|
689
693
|
}));
|
|
690
|
-
yield* dryRun ? CliMessage.info(`Dry run — no changes written. ${result.updated
|
|
694
|
+
yield* dryRun ? CliMessage.info(`Dry run — no changes written. ${Fmt.plural(result.updated, "package")} would be updated; skipped ${result.skipped.length}.`) : CliMessage.success(`Updated ${Fmt.plural(result.updated, "package")}; skipped ${result.skipped.length}.`);
|
|
691
695
|
if (result.conflicts.length > 0) {
|
|
692
696
|
const lines = result.conflicts.map((c) => ` ${c.pkg} (kept ${c.ceiling}) blocked by ${c.blockedBy}`);
|
|
693
697
|
yield* Doc.print(warningDoc(["Interop conflicts (left at your pick):", ...lines].join("\n")), { stream: "stderr" });
|
|
@@ -759,8 +763,8 @@ const upgradeCommand = Command.make("upgrade", {
|
|
|
759
763
|
yield* Doc.print(summaryDoc(decisions, { conflicts: allConflicts }, rejected));
|
|
760
764
|
if (!dryRun) yield* applyInteropAndDecisions(file, source, accepted, interopEdits);
|
|
761
765
|
const changed = countChangedDecisions(nonInteropDecisions.filter((d) => acceptedPkgs.has(d.item.entry.pkg))) + interopChanged;
|
|
762
|
-
yield* dryRun ? CliMessage.info(`Dry run — no changes written. ${changed
|
|
766
|
+
yield* dryRun ? CliMessage.info(`Dry run — no changes written. ${Fmt.plural(changed, "change")} would be applied.`) : CliMessage.success(`Applied ${Fmt.plural(changed, "change")}.`);
|
|
763
767
|
yield* warnUnresolved(versions.unresolved);
|
|
764
768
|
})).pipe(Command.withDescription("Upgrade catalog versions in a config file"));
|
|
765
769
|
|
|
766
|
-
export { CHECK_FAILED_LABEL, CheckFailedError, UpgradeError, UpgradeUsageError, applyInteropAndDecisions, checkJsonOutcome, checkOutcome, computeGate, countChangedDecisions, nothingToUpgradeMessage, projectDecisions, readCatalogSource, resolveGatedVersions, resolveProgress, resolveTargetFile, runUpgrade, runUpgradePreview, unresolvedMessage, upgradeCommand, upgradeJsonOutcome,
|
|
770
|
+
export { CHECK_FAILED_LABEL, CheckFailedError, UpgradeError, UpgradeUsageError, applyInteropAndDecisions, checkJsonOutcome, checkOutcome, computeGate, countChangedDecisions, nothingToUpgradeMessage, projectDecisions, readCatalogSource, resolveGatedVersions, resolveProgress, resolveTargetFile, runUpgrade, runUpgradePreview, unresolvedMessage, upgradeCommand, upgradeJsonOutcome, validateFormatMode };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/cli/exit-codes.ts
|
|
2
|
+
/**
|
|
3
|
+
* Exit codes by failure tag, handed to `CliRuntime.main`'s `exitCodes`. A
|
|
4
|
+
* typed failure whose `_tag` is a key here exits with that code; every other
|
|
5
|
+
* failure falls back to 1. The kit owns the remaining codes: a parse-time
|
|
6
|
+
* usage error is 64 (`usageExitCode`) and a cancelled screen is 130.
|
|
7
|
+
*
|
|
8
|
+
* `UpgradeUsageError` is the command's own invalid flag combination (`--format
|
|
9
|
+
* json` outside `--check`/`--yes`/`--dry-run`), so it shares the parser's 64
|
|
10
|
+
* (BSD `EX_USAGE`) rather than a resolution failure's 1.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
const EXIT_CODES = { UpgradeUsageError: 64 };
|
|
15
|
+
|
|
16
|
+
export { EXIT_CODES };
|
package/cli/load-config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parseWorkspace } from "./workspace-file.js";
|
|
2
2
|
import { evaluatePluginConfig } from "./evaluate.js";
|
|
3
3
|
import { Effect, Predicate } from "effect";
|
|
4
|
+
import { Fmt } from "@effected/cli";
|
|
4
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
6
|
|
|
6
7
|
// src/cli/load-config.ts
|
|
@@ -24,7 +25,7 @@ function loadConfigAndWorkspace(opts, mkError) {
|
|
|
24
25
|
const path = opts.workspacePath;
|
|
25
26
|
const parsed = existsSync(path) ? yield* Effect.try({
|
|
26
27
|
try: () => parseWorkspace(readFileSync(path, "utf8")),
|
|
27
|
-
catch: (e) => mkError(`Cannot read or parse ${path}: ${
|
|
28
|
+
catch: (e) => mkError(`Cannot read or parse ${path}: ${Fmt.causeMessage(e) ?? Fmt.describe(e)}`)
|
|
28
29
|
}) : {};
|
|
29
30
|
return {
|
|
30
31
|
config,
|
package/cli/main.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { WorkingDirectory } from "./cwd.js";
|
|
2
|
+
import { EXIT_CODES } from "./exit-codes.js";
|
|
2
3
|
import { RegistryResolverLive } from "./resolve.js";
|
|
3
4
|
import { rootCommand } from "./root.js";
|
|
4
5
|
import { CLI_VERSION } from "./version.js";
|
|
5
6
|
import { Effect } from "effect";
|
|
6
|
-
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
7
7
|
import { CliAudience, CliRuntime } from "@effected/cli";
|
|
8
|
+
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
8
9
|
|
|
9
10
|
// src/cli/main.ts
|
|
10
11
|
/**
|
|
11
12
|
* Assemble and run the CLI. One of the three files allowed to read `process`:
|
|
12
|
-
* the working directory
|
|
13
|
-
*
|
|
13
|
+
* the working directory and argv for the build-time log format are read here
|
|
14
|
+
* and passed down as plain values. Stderr's terminal state is not: the
|
|
15
|
+
* platform's `Stdio` (in `NodeServices.layer`) answers it, which is where the
|
|
16
|
+
* kit reads it from when `env.stderrIsTerminal` is omitted.
|
|
14
17
|
*
|
|
15
18
|
* The registry resolver (`pnpm view`, spawned through the platform) is
|
|
16
19
|
* provided here, at the edge, so a test drives the same command tree against
|
|
@@ -18,7 +21,8 @@ import { CliAudience, CliRuntime } from "@effected/cli";
|
|
|
18
21
|
*
|
|
19
22
|
* `CliRuntime.main` builds the audience, terminal and theme from `env`,
|
|
20
23
|
* decides whether the run may prompt, reports every failure on stderr for the
|
|
21
|
-
* run's audience, and maps the exit code (usage 64, cancel 130,
|
|
24
|
+
* run's audience, and maps the exit code (usage 64, cancel 130, `EXIT_CODES`
|
|
25
|
+
* by tag, otherwise 1).
|
|
22
26
|
*
|
|
23
27
|
* @internal
|
|
24
28
|
*/
|
|
@@ -27,13 +31,13 @@ function main() {
|
|
|
27
31
|
NodeRuntime.runMain(CliRuntime.main(program, {
|
|
28
32
|
platform: NodeServices.layer,
|
|
29
33
|
helpOnUsageError: "stderr",
|
|
34
|
+
exitCodes: EXIT_CODES,
|
|
30
35
|
env: {
|
|
31
36
|
audienceEnvVar: "ROLLDOWN_PNPM_CONFIG_AUDIENCE",
|
|
32
37
|
log: {
|
|
33
38
|
envVar: "ROLLDOWN_PNPM_CONFIG_LOG_LEVEL",
|
|
34
39
|
argv: process.argv.slice(2)
|
|
35
|
-
}
|
|
36
|
-
stderrIsTerminal: Effect.sync(() => process.stderr.isTTY === true)
|
|
40
|
+
}
|
|
37
41
|
}
|
|
38
42
|
}));
|
|
39
43
|
}
|
package/cli/resolve.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Context, Data, Effect, Layer, Predicate } from "effect";
|
|
2
|
+
import { Fmt } from "@effected/cli";
|
|
2
3
|
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
3
4
|
|
|
4
5
|
// src/cli/resolve.ts
|
|
@@ -85,7 +86,7 @@ const RegistryResolverLive = Layer.effect(RegistryResolver, Effect.gen(function*
|
|
|
85
86
|
"--json"
|
|
86
87
|
])).pipe(Effect.map(stripPnpmNotices), Effect.mapError((e) => new ResolveError({
|
|
87
88
|
pkg,
|
|
88
|
-
message:
|
|
89
|
+
message: Fmt.causeMessage(e) ?? Fmt.describe(e)
|
|
89
90
|
})));
|
|
90
91
|
return {
|
|
91
92
|
versions: (pkg) => pnpmView(pkg, pkg, "versions").pipe(Effect.flatMap((out) => parseVersions(pkg, out))),
|
package/cli/select-file.js
CHANGED
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
import { discoverCatalogEntries } from "./discover.js";
|
|
2
|
-
import { Effect } from "effect";
|
|
3
|
-
import { readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { Effect, Result } from "effect";
|
|
4
3
|
import { join } from "node:path";
|
|
4
|
+
import { GlobPattern, GlobPatternOptions } from "@effected/glob";
|
|
5
|
+
import { descendSync } from "@effected/walker";
|
|
6
|
+
import { nodeWalkerSyncOps } from "@effected/walker/node-sync";
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
5
8
|
|
|
6
9
|
// src/cli/select-file.ts
|
|
10
|
+
/** Top-level `.ts` FILES of a directory, no dotfiles, sorted. */
|
|
11
|
+
const TS_FILES = Result.getOrThrow(GlobPattern.compileResult("*.ts", GlobPatternOptions.make({ dot: false })));
|
|
7
12
|
/**
|
|
8
13
|
* Scan a directory for top-level `.ts` files whose source contains a usable
|
|
9
14
|
* `PnpmConfigPlugin(...)` catalog (discovery yields at least one entry). Read
|
|
10
|
-
* or parse failures are skipped silently
|
|
15
|
+
* or parse failures are skipped silently, and so is an unreadable directory.
|
|
16
|
+
* Returns joined paths in sorted order (`@effected/walker`'s `descendSync`),
|
|
17
|
+
* so a "multiple config files" report lists them the same way on every
|
|
18
|
+
* filesystem.
|
|
11
19
|
*
|
|
12
20
|
* @internal
|
|
13
21
|
*/
|
|
14
22
|
function findConfigFiles(dir) {
|
|
15
23
|
return Effect.sync(() => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
return [];
|
|
21
|
-
}
|
|
24
|
+
const names = Result.getOrElse(descendSync(TS_FILES, {
|
|
25
|
+
cwd: dir,
|
|
26
|
+
...nodeWalkerSyncOps
|
|
27
|
+
}), () => []);
|
|
22
28
|
const matches = [];
|
|
23
29
|
for (const name of names) {
|
|
24
|
-
if (
|
|
30
|
+
if (name.endsWith(".d.ts")) continue;
|
|
25
31
|
const path = join(dir, name);
|
|
26
32
|
try {
|
|
27
33
|
const source = readFileSync(path, "utf8");
|
package/cli/summary.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { row, tone } from "./render/tone.js";
|
|
2
2
|
import { cellTone, displayCandidates, peerFor, tableLayout } from "./walk-reducer.js";
|
|
3
|
-
import { Doc } from "@effected/cli";
|
|
3
|
+
import { Doc, Fmt } from "@effected/cli";
|
|
4
4
|
|
|
5
5
|
// src/cli/summary.ts
|
|
6
6
|
/**
|
|
@@ -27,7 +27,7 @@ function summaryDoc(decisions, interop, rejected) {
|
|
|
27
27
|
lines.push(row(" ", 0, [tone(`── catalog: ${entry.catalog} ──`, "unchanged")]));
|
|
28
28
|
}
|
|
29
29
|
const cells = displayCandidates(item);
|
|
30
|
-
const content = [tone(entry.pkg
|
|
30
|
+
const content = [tone(Fmt.pad(entry.pkg, pkgWidth + 2), "plain")];
|
|
31
31
|
for (const c of cells) {
|
|
32
32
|
const selected = c.kind === chosen.kind;
|
|
33
33
|
content.push(tone(cellText(c, selected), cellTone(c, selected) ?? "unchanged"));
|
package/cli/ui/walk-screen.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TONES } from "../render/tone.js";
|
|
2
2
|
import { computeGroupPeers } from "../interop-live.js";
|
|
3
|
-
import { cellTone, displayCandidates, initTable, peerFor, tableDecisions, tableLayout, tableStep
|
|
3
|
+
import { cellTone, displayCandidates, initTable, peerFor, tableDecisions, tableLayout, tableStep } from "../walk-reducer.js";
|
|
4
4
|
import { Fmt } from "@effected/cli";
|
|
5
5
|
import { KeyHelp, KeyTable, Styled, useKeys, useTerminalSize } from "@effected/cli/ui";
|
|
6
6
|
import { Box, Text } from "ink";
|
|
@@ -104,7 +104,7 @@ function makeWalkScreen(props) {
|
|
|
104
104
|
const gp = interopPeers.get(item.entry.catalog);
|
|
105
105
|
const peerText = gp?.peer.get(item.entry.pkg) ?? (chosen === void 0 ? "—" : peerFor(item, chosen));
|
|
106
106
|
const conflict = gp?.conflict.get(item.entry.pkg);
|
|
107
|
-
const room = Math.max(8, columns - fixedPrefix - peerText
|
|
107
|
+
const room = Math.max(8, columns - fixedPrefix - Fmt.width(peerText) - 3);
|
|
108
108
|
rows.push(/* @__PURE__ */ jsxs(Text, {
|
|
109
109
|
wrap: "truncate-end",
|
|
110
110
|
children: [
|
|
@@ -114,7 +114,7 @@ function makeWalkScreen(props) {
|
|
|
114
114
|
}) : " ",
|
|
115
115
|
/* @__PURE__ */ jsx(Text, {
|
|
116
116
|
bold: onCursor,
|
|
117
|
-
children: Fmt.sanitize(item.entry.pkg)
|
|
117
|
+
children: Fmt.pad(Fmt.sanitize(item.entry.pkg), pkgWidth + 2)
|
|
118
118
|
}),
|
|
119
119
|
candidates.map((c, ci) => {
|
|
120
120
|
const t = cellTone(c, ci === pick);
|
|
@@ -131,11 +131,11 @@ function makeWalkScreen(props) {
|
|
|
131
131
|
}),
|
|
132
132
|
conflict ? /* @__PURE__ */ jsx(Styled, {
|
|
133
133
|
token: "failure",
|
|
134
|
-
children:
|
|
134
|
+
children: Fmt.truncate(` ⚠ needs ${Fmt.sanitize(conflict)}`, room)
|
|
135
135
|
}) : null,
|
|
136
136
|
item.peerWarning ? /* @__PURE__ */ jsx(Styled, {
|
|
137
137
|
token: "failure",
|
|
138
|
-
children:
|
|
138
|
+
children: Fmt.truncate(` ⚠ ${Fmt.sanitize(item.peerWarning.message)}`, room)
|
|
139
139
|
}) : null
|
|
140
140
|
]
|
|
141
141
|
}, `${item.entry.catalog}/${item.entry.pkg}`));
|
package/cli/version.js
CHANGED
package/cli/walk-reducer.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Fmt } from "@effected/cli";
|
|
2
|
+
|
|
1
3
|
// src/cli/walk-reducer.ts
|
|
2
4
|
const ORDER = {
|
|
3
5
|
keep: 0,
|
|
@@ -17,35 +19,25 @@ const BUBBLE_WIDTH = 2;
|
|
|
17
19
|
* Compute the table geometry once for a set of rows. The range and the major
|
|
18
20
|
* suffix are padded TOGETHER so a major annotation never shifts a later
|
|
19
21
|
* column, and every row emits `maxCells` cells so the peer separator lands
|
|
20
|
-
* in the same column regardless of how many candidates a row has.
|
|
22
|
+
* in the same column regardless of how many candidates a row has. Widths are
|
|
23
|
+
* terminal columns (`Fmt.width`) and padding is `Fmt.pad`, so a wide character
|
|
24
|
+
* in a name or range keeps every later column aligned.
|
|
21
25
|
*
|
|
22
26
|
* @internal
|
|
23
27
|
*/
|
|
24
28
|
function tableLayout(items) {
|
|
25
29
|
const cells = items.map(displayCandidates);
|
|
26
|
-
const pkgWidth = items.length ? Math.max(...items.map((i) => i.entry.pkg
|
|
27
|
-
const cellWidth = items.length ? Math.max(...cells.flatMap((cs) => cs.map((c) => c.range
|
|
30
|
+
const pkgWidth = items.length ? Math.max(...items.map((i) => Fmt.width(i.entry.pkg))) : 0;
|
|
31
|
+
const cellWidth = items.length ? Math.max(...cells.flatMap((cs) => cs.map((c) => Fmt.width(`${c.range}${c.isMajor ? MAJOR_SUFFIX : ""}`)))) : 0;
|
|
28
32
|
return {
|
|
29
33
|
pkgWidth,
|
|
30
34
|
cellWidth,
|
|
31
35
|
maxCells: items.length ? Math.max(...cells.map((cs) => cs.length)) : 0,
|
|
32
36
|
blankCell: `${" ".repeat(BUBBLE_WIDTH + cellWidth)} `,
|
|
33
|
-
cellText: (c, selected) => `${selected ? "●" : "○"} ${`${c.range}${c.isMajor ? MAJOR_SUFFIX : ""}
|
|
37
|
+
cellText: (c, selected) => `${selected ? "●" : "○"} ${Fmt.pad(`${c.range}${c.isMajor ? MAJOR_SUFFIX : ""}`, cellWidth)} `
|
|
34
38
|
};
|
|
35
39
|
}
|
|
36
40
|
/**
|
|
37
|
-
* Truncate `s` to at most `max` display columns, appending `…` when clipped.
|
|
38
|
-
* Keeps a long peer-conflict annotation from wrapping and breaking the table's
|
|
39
|
-
* column alignment. A `max` of 1 or less yields just the ellipsis.
|
|
40
|
-
*
|
|
41
|
-
* @internal
|
|
42
|
-
*/
|
|
43
|
-
function truncateEnd(s, max) {
|
|
44
|
-
if (s.length <= max) return s;
|
|
45
|
-
if (max <= 1) return "…";
|
|
46
|
-
return `${s.slice(0, max - 1)}…`;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
41
|
* The row's options in display order: keep first (always index 0, always the
|
|
50
42
|
* default), then the in-range bump, then the latest-overall bump. `planEntry`
|
|
51
43
|
* emits them in the opposite order, with keep last.
|
|
@@ -146,4 +138,4 @@ function cellTone(candidate, selected) {
|
|
|
146
138
|
return candidate.isMajor ? "changed" : "added";
|
|
147
139
|
}
|
|
148
140
|
|
|
149
|
-
export { MAJOR_SUFFIX, cellTone, displayCandidates, initTable, peerFor, tableDecisions, tableLayout, tableStep
|
|
141
|
+
export { MAJOR_SUFFIX, cellTone, displayCandidates, initTable, peerFor, tableDecisions, tableLayout, tableStep };
|
package/cli/workspace-file.js
CHANGED
package/cli/workspace-resolve.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { parseVersion } from "../semver-util.js";
|
|
2
2
|
import { findWorkspaceFile } from "./workspace-file.js";
|
|
3
3
|
import { ResolveError } from "./resolve.js";
|
|
4
|
-
import { Effect, Layer } from "effect";
|
|
5
|
-
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
4
|
+
import { Effect, Layer, Result } from "effect";
|
|
6
5
|
import { dirname, join, resolve } from "node:path";
|
|
6
|
+
import { GlobPattern, GlobPatternOptions } from "@effected/glob";
|
|
7
|
+
import { descendSync } from "@effected/walker";
|
|
8
|
+
import { nodeWalkerSyncOps } from "@effected/walker/node-sync";
|
|
9
|
+
import { readFileSync } from "node:fs";
|
|
7
10
|
import { getWorkspacePackagesSync } from "@effected/workspaces";
|
|
8
11
|
import { nodeSyncOps } from "@effected/workspaces/node-sync";
|
|
9
12
|
|
|
@@ -13,6 +16,8 @@ const BUMP_ORDER = {
|
|
|
13
16
|
minor: 1,
|
|
14
17
|
major: 2
|
|
15
18
|
};
|
|
19
|
+
/** The changeset files of `.changeset/`: top-level `.md` FILES, no dotfiles, sorted. */
|
|
20
|
+
const CHANGESET_FILES = Result.getOrThrow(GlobPattern.compileResult("*.md", GlobPatternOptions.make({ dot: false })));
|
|
16
21
|
/** Matches one changeset frontmatter line: `"@scope/name": minor` (quotes optional). */
|
|
17
22
|
const FRONTMATTER_LINE_RE = /^\s*["']?([^"':\s]+)["']?\s*:\s*(major|minor|patch)\s*$/;
|
|
18
23
|
/**
|
|
@@ -62,13 +67,10 @@ function readManifests(rootDir) {
|
|
|
62
67
|
function readPendingBumps(rootDir) {
|
|
63
68
|
const out = /* @__PURE__ */ new Map();
|
|
64
69
|
const dir = join(rootDir, ".changeset");
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
} catch {
|
|
70
|
-
return out;
|
|
71
|
-
}
|
|
70
|
+
const names = Result.getOrElse(descendSync(CHANGESET_FILES, {
|
|
71
|
+
cwd: dir,
|
|
72
|
+
...nodeWalkerSyncOps
|
|
73
|
+
}), () => []).filter((n) => n !== "README.md");
|
|
72
74
|
for (const name of names) {
|
|
73
75
|
let text;
|
|
74
76
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rolldown-pnpm-config",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A dogfooding example of our plugin",
|
|
6
6
|
"repository": {
|
|
@@ -36,17 +36,17 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@effect/platform-node": "^4.0.6",
|
|
39
|
-
"@effected/cli": "^0.
|
|
40
|
-
"@effected/env": "^0.
|
|
41
|
-
"@effected/glob": "^0.
|
|
42
|
-
"@effected/npm": "^0.27.
|
|
39
|
+
"@effected/cli": "^0.23.0",
|
|
40
|
+
"@effected/env": "^0.6.0",
|
|
41
|
+
"@effected/glob": "^0.15.0",
|
|
42
|
+
"@effected/npm": "^0.27.1",
|
|
43
43
|
"@effected/semver": "^0.18.0",
|
|
44
|
-
"@effected/walker": "^0.
|
|
45
|
-
"@effected/workspaces": "^0.39.
|
|
44
|
+
"@effected/walker": "^0.21.0",
|
|
45
|
+
"@effected/workspaces": "^0.39.1",
|
|
46
46
|
"@effected/yaml": "^0.25.1",
|
|
47
47
|
"effect": "^4.0.6",
|
|
48
48
|
"ink": "^8.0.0",
|
|
49
|
-
"oxc-parser": "^0.
|
|
49
|
+
"oxc-parser": "^0.156.0",
|
|
50
50
|
"react": "^19.3.1"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
package/patches/discover.js
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import { patchKeyFromFileName } from "./keys.js";
|
|
2
2
|
import { distributedPatchPath, distributedRel } from "./paths.js";
|
|
3
|
-
import {
|
|
3
|
+
import { Result } from "effect";
|
|
4
4
|
import { isAbsolute, join } from "node:path";
|
|
5
|
+
import { GlobPattern, GlobPatternOptions } from "@effected/glob";
|
|
6
|
+
import { descendSync } from "@effected/walker";
|
|
7
|
+
import { nodeWalkerSyncOps } from "@effected/walker/node-sync";
|
|
5
8
|
|
|
6
9
|
// src/patches/discover.ts
|
|
7
10
|
/**
|
|
11
|
+
* The patch files of one convention folder: top-level `.patch` FILES only
|
|
12
|
+
* (a directory named `x.patch` is not a patch), no dotfiles, sorted.
|
|
13
|
+
*/
|
|
14
|
+
const PATCH_FILES = Result.getOrThrow(GlobPattern.compileResult("*.patch", GlobPatternOptions.make({ dot: false })));
|
|
15
|
+
/**
|
|
8
16
|
* Discover owned patches in the two convention folders adjacent to the build
|
|
9
17
|
* file: `public/patches/` (distributed, rewritten) and `patches/` (local-only).
|
|
10
18
|
* `localPatchesDir` overrides the distributed source root only. Read-only.
|
|
11
19
|
*
|
|
20
|
+
* Each folder is listed with `@effected/walker`'s `descendSync`: an absent
|
|
21
|
+
* folder yields nothing, and an unreadable one throws its `DescendError`, as
|
|
22
|
+
* the bare `readdirSync` this replaced threw its `EACCES`.
|
|
23
|
+
*
|
|
12
24
|
* @internal
|
|
13
25
|
*/
|
|
14
26
|
function discoverPatches(opts) {
|
|
@@ -19,8 +31,11 @@ function discoverPatches(opts) {
|
|
|
19
31
|
collect(localOnlyRoot, false);
|
|
20
32
|
return out;
|
|
21
33
|
function collect(dir, distributed) {
|
|
22
|
-
|
|
23
|
-
|
|
34
|
+
const files = Result.getOrThrow(descendSync(PATCH_FILES, {
|
|
35
|
+
cwd: dir,
|
|
36
|
+
...nodeWalkerSyncOps
|
|
37
|
+
}));
|
|
38
|
+
for (const fileName of files) {
|
|
24
39
|
const key = patchKeyFromFileName(fileName);
|
|
25
40
|
if (key === null) continue;
|
|
26
41
|
const absPath = join(dir, fileName);
|
package/plugin/freeze.js
CHANGED
|
@@ -4,6 +4,7 @@ import { FIELD_REGISTRY } from "../registry.js";
|
|
|
4
4
|
import { resolvePeerDependencyRules } from "./allowed-versions.js";
|
|
5
5
|
import { resolveOverrides } from "./overrides-from-catalogs.js";
|
|
6
6
|
import { Data, Effect, Schema } from "effect";
|
|
7
|
+
import { Fmt } from "@effected/cli";
|
|
7
8
|
|
|
8
9
|
// src/plugin/freeze.ts
|
|
9
10
|
/**
|
|
@@ -57,13 +58,13 @@ function freeze(config) {
|
|
|
57
58
|
return Effect.gen(function* () {
|
|
58
59
|
const base = {};
|
|
59
60
|
const manifest = {};
|
|
60
|
-
base.catalogs = yield* Schema.decodeUnknownEffect(CatalogsSchema)(normalizeCatalogs(config.catalogs)).pipe(Effect.mapError((error) => new ConfigError({ message: `Invalid catalogs: ${
|
|
61
|
+
base.catalogs = yield* Schema.decodeUnknownEffect(CatalogsSchema)(normalizeCatalogs(config.catalogs)).pipe(Effect.mapError((error) => new ConfigError({ message: `Invalid catalogs: ${Fmt.describe(error)}` })));
|
|
61
62
|
manifest.catalogs = {
|
|
62
63
|
strategy: "catalogs",
|
|
63
64
|
enforcement: "warn"
|
|
64
65
|
};
|
|
65
66
|
if (typeof config.name !== "string" || config.name.trim() === "") return yield* Effect.fail(new ConfigError({ message: "Config `name` is required and must be a non-empty string" }));
|
|
66
|
-
const directiveError = (error) => new ConfigError({ message:
|
|
67
|
+
const directiveError = (error) => new ConfigError({ message: Fmt.describe(error) });
|
|
67
68
|
for (const [field, reg] of Object.entries(FIELD_REGISTRY)) {
|
|
68
69
|
if (field === "catalogs") continue;
|
|
69
70
|
let raw = config[field];
|
|
@@ -78,7 +79,7 @@ function freeze(config) {
|
|
|
78
79
|
try: () => resolveOverrides(decl.value, config.overridesFromCatalogs, config.catalogs),
|
|
79
80
|
catch: directiveError
|
|
80
81
|
}) : decl.value;
|
|
81
|
-
base[field] = schema ? yield* Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError((error) => new ConfigError({ message: `Invalid ${field}: ${
|
|
82
|
+
base[field] = schema ? yield* Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError((error) => new ConfigError({ message: `Invalid ${field}: ${Fmt.describe(error)}` }))) : value;
|
|
82
83
|
manifest[field] = {
|
|
83
84
|
strategy: reg.strategy,
|
|
84
85
|
enforcement: decl.enforcement ?? reg.enforcement,
|