@savvy-web/cli 1.6.1 → 2.0.1
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/cli/index.js +62 -88
- package/commands/changeset/commands/check.js +2 -2
- package/commands/changeset/commands/config-validate.js +2 -2
- package/commands/changeset/commands/deps-detect.js +12 -18
- package/commands/changeset/commands/deps-regen.js +12 -18
- package/commands/changeset/commands/init.js +39 -40
- package/commands/changeset/commands/lint.js +3 -3
- package/commands/changeset/commands/transform.js +4 -4
- package/commands/changeset/commands/validate-file.js +3 -3
- package/commands/changeset/commands/version.js +2 -2
- package/commands/changeset/index.js +7 -4
- package/commands/check.js +12 -12
- package/commands/clean.js +9 -9
- package/commands/commit/check.js +5 -6
- package/commands/commit/hook.js +1 -1
- package/commands/commit/hooks/post-commit-verify.js +26 -46
- package/commands/commit/hooks/pre-commit-message.js +1 -1
- package/commands/commit/hooks/session-start.js +1 -1
- package/commands/commit/index.js +1 -1
- package/commands/commit/init.js +1 -2
- package/commands/init.js +6 -6
- package/commands/lint/check.js +15 -13
- package/commands/lint/fmt.js +12 -14
- package/commands/lint/index.js +1 -1
- package/commands/lint/init.js +7 -8
- package/commands/repos/commands/add.js +7 -7
- package/commands/repos/commands/note.js +19 -20
- package/commands/repos/commands/pin.js +4 -4
- package/commands/repos/commands/status.js +3 -3
- package/commands/repos/commands/sync.js +2 -2
- package/commands/repos/index.js +7 -4
- package/index.d.ts +62 -38
- package/package.json +10 -18
package/cli/index.js
CHANGED
|
@@ -5,15 +5,16 @@ import { commitCommand } from "../commands/commit/index.js";
|
|
|
5
5
|
import { initCommand } from "../commands/init.js";
|
|
6
6
|
import { lintCommand } from "../commands/lint/index.js";
|
|
7
7
|
import { reposCommand } from "../commands/repos/index.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
9
|
+
import { Git } from "@effected/git";
|
|
10
|
+
import { PackageManagerDetector, WorkspaceDiscovery, WorkspaceRoot } from "@effected/workspaces";
|
|
11
|
+
import { BiomeSchemaSyncLive, ChangesetConfigReaderLive, Changesets, ConfigDiscoveryLive, ManagedSectionLive, Repos, SilkPublishabilityDetectorLive, ToolDiscoveryLive, VersioningStrategyLive } from "@savvy-web/silk-effects";
|
|
11
12
|
import { Effect, Layer } from "effect";
|
|
12
|
-
import {
|
|
13
|
+
import { Command } from "effect/unstable/cli";
|
|
13
14
|
|
|
14
15
|
//#region src/cli/index.ts
|
|
15
16
|
/**
|
|
16
|
-
* Root `savvy` CLI entry point using
|
|
17
|
+
* Root `savvy` CLI entry point using `effect/unstable/cli`.
|
|
17
18
|
*
|
|
18
19
|
* @remarks
|
|
19
20
|
* Assembles the five Phase-B command pieces — the `init` and `check` top-level
|
|
@@ -21,23 +22,30 @@ import { PackageManagerDetectorLive, PointInTimeWorkspaceLive, PublishabilityDet
|
|
|
21
22
|
* single `savvy` root command, then provides the merged runtime Layer stack
|
|
22
23
|
* that satisfies every command's service requirements.
|
|
23
24
|
*
|
|
24
|
-
* The layer stack
|
|
25
|
-
*
|
|
26
|
-
* with each service's transitive dependencies wired:
|
|
25
|
+
* The layer stack wires the silk-effects Lives over their v4 requirement
|
|
26
|
+
* channels:
|
|
27
27
|
*
|
|
28
|
-
* - `
|
|
29
|
-
* by every config reader, workspace service,
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
28
|
+
* - `NodeServices.layer` — `FileSystem`, `Path`, `ChildProcessSpawner`, `Stdio`,
|
|
29
|
+
* and `Terminal`, consumed by every config reader, workspace service, git
|
|
30
|
+
* layer, and the CLI framework itself.
|
|
31
|
+
* - `Git.layer` (from `@effected/git`) — the typed git service backing
|
|
32
|
+
* `BranchAnalyzer`, `ReposManager`, the commit hooks
|
|
33
|
+
* (`readBranchInfo` / `readSigningDiagnostic`), and `detectGitHubRepo`.
|
|
34
|
+
* - Workspace services from `@effected/workspaces` — `WorkspaceRoot.layer`,
|
|
35
|
+
* `PackageManagerDetector.layer`, and a single root-bound
|
|
36
|
+
* `WorkspaceDiscovery.layer()` (the kit resolves the root through
|
|
37
|
+
* `WorkspaceRoot` from `process.cwd()` lazily on first use, so the CLI's
|
|
38
|
+
* startup cwd is the discovery root).
|
|
33
39
|
* - Flat silk-effects services — `ChangesetConfigReaderLive`,
|
|
34
40
|
* `SilkPublishabilityDetectorLive`, `ManagedSectionLive`, `BiomeSchemaSyncLive`,
|
|
35
|
-
* `ConfigDiscoveryLive`, `ToolDiscoveryLive`, and `VersioningStrategyLive
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* `
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
+
* `ConfigDiscoveryLive`, `ToolDiscoveryLive`, and `VersioningStrategyLive`.
|
|
42
|
+
* - Changesets-namespace services — `Changesets.ConfigInspectorLive`,
|
|
43
|
+
* `Changesets.ReleasePlannerLive`, and `Changesets.BranchAnalyzerLive`
|
|
44
|
+
* sharing a single `ConfigInspector` via `provideMerge`. `DepsRegen` is NOT
|
|
45
|
+
* part of AppLive: its graph is root-bound at layer build, so the deps
|
|
46
|
+
* commands compose `Changesets.makeDepsRegenDefault({ cwd })` per invocation
|
|
47
|
+
* against their parsed `--cwd` (platform services flow up to
|
|
48
|
+
* `NodeServices.layer` here).
|
|
41
49
|
*
|
|
42
50
|
* The CLI version is injected at build time via `process.env.__PACKAGE_VERSION__`.
|
|
43
51
|
*
|
|
@@ -56,99 +64,65 @@ const rootCommand = Command.make("savvy").pipe(Command.withSubcommands([
|
|
|
56
64
|
lintCommand,
|
|
57
65
|
reposCommand
|
|
58
66
|
]));
|
|
59
|
-
const cli = Command.run(rootCommand, {
|
|
60
|
-
name: "savvy",
|
|
61
|
-
version: "1.6.1"
|
|
62
|
-
});
|
|
63
67
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* `
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* handlers that yield those tags directly. This mirrors the three source CLIs'
|
|
75
|
-
* minimal workspace wiring rather than pulling in the heavier `WorkspacesLive`
|
|
76
|
-
* (which also forks `DependencyGraph` / `PublishabilityDetector` background work).
|
|
68
|
+
* CLI application: reads argv from the Stdio service provided by NodeServices.
|
|
69
|
+
* (v4's `Command.run` takes only `version` — the name comes from the root command.)
|
|
70
|
+
*/
|
|
71
|
+
const cli = Command.run(rootCommand, { version: "2.0.1" });
|
|
72
|
+
/**
|
|
73
|
+
* Shared workspace services from `@effected/workspaces`, wired as a
|
|
74
|
+
* self-contained unit and built ONCE (layers memoize by reference).
|
|
75
|
+
* `WorkspaceDiscovery.layer()` is root-bound at first use via `WorkspaceRoot`
|
|
76
|
+
* from the CLI's startup cwd — the single-root semantics every downstream
|
|
77
|
+
* consumer (analyzer, deps-regen, publishability) assumes.
|
|
77
78
|
*/
|
|
78
|
-
const
|
|
79
|
+
const WorkspaceRootLive = WorkspaceRoot.layer;
|
|
80
|
+
const WorkspaceDiscoveryLive = WorkspaceDiscovery.layer().pipe(Layer.provide(WorkspaceRootLive));
|
|
81
|
+
const WorkspaceLive = Layer.mergeAll(WorkspaceRootLive, PackageManagerDetector.layer, WorkspaceDiscoveryLive);
|
|
82
|
+
/**
|
|
83
|
+
* The typed git service, built once over the platform spawner and shared by
|
|
84
|
+
* every git-consuming layer AND exposed to handlers that yield `Git` directly
|
|
85
|
+
* (commit hooks, `detectGitHubRepo`).
|
|
86
|
+
*/
|
|
87
|
+
const GitLive = Git.layer;
|
|
79
88
|
/**
|
|
80
89
|
* Base layer membership: silk-effects leaf services (`ManagedSection`,
|
|
81
90
|
* `BiomeSchemaSync`, `ConfigDiscovery`, `SilkPublishabilityDetector`) that
|
|
82
91
|
* depend only on the platform, plus the changeset base layers
|
|
83
|
-
* (`WorkspaceLive`, `ChangesetConfigReader`) that `AppLive`'s
|
|
84
|
-
* build upon.
|
|
92
|
+
* (`WorkspaceLive`, `ChangesetConfigReader`, `GitLive`) that `AppLive`'s
|
|
93
|
+
* upper services build upon.
|
|
85
94
|
*/
|
|
86
|
-
const BaseLive = Layer.mergeAll(WorkspaceLive, ChangesetConfigReaderLive, ManagedSectionLive, BiomeSchemaSyncLive, ConfigDiscoveryLive, SilkPublishabilityDetectorLive);
|
|
95
|
+
const BaseLive = Layer.mergeAll(WorkspaceLive, GitLive, ChangesetConfigReaderLive, ManagedSectionLive, BiomeSchemaSyncLive, ConfigDiscoveryLive, SilkPublishabilityDetectorLive);
|
|
87
96
|
/**
|
|
88
|
-
* Merged runtime Layer stack — the union of the three source CLIs' stacks with
|
|
89
|
-
* every inter-layer dependency satisfied.
|
|
90
|
-
*
|
|
91
|
-
* @remarks
|
|
92
|
-
* The upper services depend on members of `BaseLive`:
|
|
93
|
-
* `ToolDiscoveryLive` needs `WorkspaceRoot`, `PackageManagerDetector`, and
|
|
94
|
-
* `CommandExecutor`; `VersioningStrategyLive` needs `ChangesetConfigReader`;
|
|
95
|
-
* `Changesets.ConfigInspectorLive` needs `ChangesetConfigReader`,
|
|
96
|
-
* `WorkspaceDiscovery`, and `FileSystem` (the last for its publishConfig-driven
|
|
97
|
-
* fallback when no explicit `packages` record is configured);
|
|
98
|
-
* `Changesets.BranchAnalyzerLive` needs `ConfigInspector`;
|
|
99
|
-
* `Changesets.ReleasePlannerLive` needs `ConfigInspector`.
|
|
100
|
-
*
|
|
101
97
|
* `ConfigInspectorLive` is built once via {@link Layer.provideMerge}: the merge
|
|
102
98
|
* feeds that single `ConfigInspector` instance into `ReleasePlannerLive` and
|
|
103
99
|
* `BranchAnalyzerLive` AND re-exposes it for the surviving `config validate`
|
|
104
100
|
* handler that yields it directly, so it is never constructed twice per run.
|
|
105
|
-
*
|
|
106
|
-
* `
|
|
107
|
-
* service) needs `ConfigInspector`, `WorkspaceDiscovery` (from
|
|
108
|
-
* `BaseLive`/`InspectorAndAnalyzerLive`), plus `PointInTimeWorkspace` and
|
|
109
|
-
* `PublishabilityDetector` from `workspaces-effect`, and `ChangesetConfig`
|
|
110
|
-
* (provided its own `ChangesetConfigReaderLive`, since `Layer.mergeAll` does
|
|
111
|
-
* not cross-feed sibling layers) — none of which the other commands need, so
|
|
112
|
-
* they're composed only for `DepsRegenGroupLive`. `PointInTimeWorkspaceLive`
|
|
113
|
-
* in turn needs `WorkspaceRoot`, `WorkspaceDiscovery`, `CommandExecutor`,
|
|
114
|
-
* `FileSystem`, `Path` — the workspace pair supplied by `WorkspaceLive`, the
|
|
115
|
-
* platform trio by `NodeContext.layer` below.
|
|
116
|
-
* `DepsRegenGroupLive` reuses the exact `InspectorAndAnalyzerLive` layer
|
|
117
|
-
* reference (not a fresh copy), so Effect's layer memoization builds that
|
|
118
|
-
* single `ConfigInspector` instance once and shares it here too.
|
|
119
|
-
*
|
|
120
|
-
* `provideMerge(BaseLive)` feeds the remaining deps and re-exposes the base
|
|
121
|
-
* services for handlers that yield them directly. `provideMerge(NodeContext.layer)`
|
|
122
|
-
* supplies `FileSystem`, `Path`, and `CommandExecutor` to everything underneath.
|
|
101
|
+
* `BranchAnalyzerLive`'s v4 requirements are `ConfigInspector |
|
|
102
|
+
* ChildProcessSpawner` (the spawner flows up to `NodeServices.layer`).
|
|
123
103
|
*/
|
|
124
104
|
const InspectorAndAnalyzerLive = Changesets.BranchAnalyzerLive.pipe(Layer.provideMerge(Changesets.ReleasePlannerLive), Layer.provideMerge(Changesets.ConfigInspectorLive));
|
|
125
105
|
/**
|
|
126
|
-
* `
|
|
127
|
-
*
|
|
128
|
-
* `
|
|
129
|
-
* so its `WorkspaceRoot`/`WorkspaceDiscovery` requirements resolve; the
|
|
130
|
-
* remaining `CommandExecutor`/`FileSystem`/`Path` flow up to `NodeContext.layer`.
|
|
131
|
-
*/
|
|
132
|
-
const DepsRegenGroupLive = Changesets.DepsRegenLive.pipe(Layer.provide(InspectorAndAnalyzerLive), Layer.provide(PointInTimeWorkspaceLive.pipe(Layer.provide(WorkspaceLive))), Layer.provide(PublishabilityDetectorLive), Layer.provide(ChangesetConfigLive.pipe(Layer.provide(ChangesetConfigReaderLive))));
|
|
133
|
-
/**
|
|
134
|
-
* `Repos.ReposManager`, provided its `Repos.ReposConfigStoreLive` dependency.
|
|
135
|
-
* Both draw their platform requirements (`FileSystem`, `Path`,
|
|
136
|
-
* `CommandExecutor`) from `NodeContext.layer` below.
|
|
106
|
+
* `Repos.ReposManager`, provided its `Repos.ReposConfigStoreLive` dependency
|
|
107
|
+
* and the shared `Git` service. Platform requirements (`FileSystem`, `Path`)
|
|
108
|
+
* flow up to `NodeServices.layer`.
|
|
137
109
|
*/
|
|
138
|
-
const ReposGroupLive = Repos.ReposManagerLive.pipe(Layer.provide(Repos.ReposConfigStoreLive));
|
|
139
|
-
const AppLive = Layer.mergeAll(ToolDiscoveryLive, VersioningStrategyLive, InspectorAndAnalyzerLive,
|
|
110
|
+
const ReposGroupLive = Repos.ReposManagerLive.pipe(Layer.provide(Repos.ReposConfigStoreLive), Layer.provide(GitLive));
|
|
111
|
+
const AppLive = Layer.mergeAll(ToolDiscoveryLive, VersioningStrategyLive, InspectorAndAnalyzerLive, ReposGroupLive).pipe(Layer.provideMerge(BaseLive), Layer.provideMerge(NodeServices.layer));
|
|
140
112
|
/**
|
|
141
113
|
* Bootstrap and run the `savvy` CLI application.
|
|
142
114
|
*
|
|
143
115
|
* @remarks
|
|
144
|
-
*
|
|
145
|
-
* stack
|
|
116
|
+
* `Command.run` returns an Effect reading `process.argv` from the Stdio
|
|
117
|
+
* service; the merged layer stack is provided and execution handed to
|
|
118
|
+
* `NodeRuntime.runMain`, whose default reporting covers defects (the v3
|
|
119
|
+
* `Cause.defects` wrapper is gone). No type casts: the layer graph is
|
|
120
|
+
* validated by the compiler.
|
|
146
121
|
*
|
|
147
122
|
* @internal
|
|
148
123
|
*/
|
|
149
124
|
function runCli() {
|
|
150
|
-
|
|
151
|
-
NodeRuntime.runMain(main);
|
|
125
|
+
NodeRuntime.runMain(cli.pipe(Effect.provide(AppLive)));
|
|
152
126
|
}
|
|
153
127
|
/* v8 ignore stop */
|
|
154
128
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Args, Command } from "@effect/cli";
|
|
2
1
|
import { Changesets } from "@savvy-web/silk-effects";
|
|
3
2
|
import { Effect } from "effect";
|
|
3
|
+
import { Argument, Command } from "effect/unstable/cli";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
|
|
6
6
|
//#region src/commands/changeset/commands/check.ts
|
|
@@ -27,7 +27,7 @@ import { resolve } from "node:path";
|
|
|
27
27
|
*/
|
|
28
28
|
const { ChangesetLinter } = Changesets;
|
|
29
29
|
/* v8 ignore next */
|
|
30
|
-
const dirArg =
|
|
30
|
+
const dirArg = Argument.directory("dir").pipe(Argument.withDefault(".changeset"));
|
|
31
31
|
/**
|
|
32
32
|
* Run the check validation pipeline on all changeset files in `dir`.
|
|
33
33
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Args, Command } from "@effect/cli";
|
|
2
1
|
import { Changesets } from "@savvy-web/silk-effects";
|
|
3
2
|
import { Effect } from "effect";
|
|
3
|
+
import { Argument, Command } from "effect/unstable/cli";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
|
|
6
6
|
//#region src/commands/changeset/commands/config-validate.ts
|
|
@@ -26,7 +26,7 @@ import { resolve } from "node:path";
|
|
|
26
26
|
*/
|
|
27
27
|
const { ConfigInspector } = Changesets;
|
|
28
28
|
/* v8 ignore next */
|
|
29
|
-
const dirArg =
|
|
29
|
+
const dirArg = Argument.directory("dir").pipe(Argument.withDefault("."));
|
|
30
30
|
/**
|
|
31
31
|
* Run validation. Logs a one-line OK on success; logs the error and sets
|
|
32
32
|
* `process.exitCode = 1` on failure.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Command, Options } from "@effect/cli";
|
|
2
1
|
import { Changesets } from "@savvy-web/silk-effects";
|
|
3
2
|
import { Console, Effect, Option } from "effect";
|
|
3
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
4
|
+
import { resolve } from "node:path";
|
|
4
5
|
|
|
5
6
|
//#region src/commands/changeset/commands/deps-detect.ts
|
|
6
7
|
/**
|
|
@@ -28,12 +29,12 @@ import { Console, Effect, Option } from "effect";
|
|
|
28
29
|
*/
|
|
29
30
|
const { DepsRegen, serializeDependencyTableToMarkdown } = Changesets;
|
|
30
31
|
/* v8 ignore start -- CLI option definitions */
|
|
31
|
-
const fromOption =
|
|
32
|
-
const toOption =
|
|
33
|
-
const cwdOption =
|
|
34
|
-
const packageOption =
|
|
35
|
-
const jsonOption =
|
|
36
|
-
const markdownOption =
|
|
32
|
+
const fromOption = Flag.string("from").pipe(Flag.withDescription("Older ref to diff from (defaults to merge-base with base branch)"), Flag.optional);
|
|
33
|
+
const toOption = Flag.string("to").pipe(Flag.withDescription("Newer ref to diff to (defaults to working tree)"), Flag.optional);
|
|
34
|
+
const cwdOption = Flag.directory("cwd").pipe(Flag.withDescription("Project root (defaults to the current working directory)"), Flag.withDefault("."));
|
|
35
|
+
const packageOption = Flag.string("package").pipe(Flag.withDescription("Restrict output to a single workspace package"), Flag.optional);
|
|
36
|
+
const jsonOption = Flag.boolean("json").pipe(Flag.withDescription("Emit JSON (default)"), Flag.withDefault(false));
|
|
37
|
+
const markdownOption = Flag.boolean("markdown").pipe(Flag.withDescription("Emit one CSH005 markdown block per workspace package"), Flag.withDefault(false));
|
|
37
38
|
/* v8 ignore stop */
|
|
38
39
|
/**
|
|
39
40
|
* Render a per-workspace diff as markdown — one frontmatter+section block
|
|
@@ -64,16 +65,9 @@ function runDepsDetect(cwd, from, to, pkg, json, markdown) {
|
|
|
64
65
|
...Option.isSome(pkg) ? { package: pkg.value } : {},
|
|
65
66
|
...Option.isSome(from) ? { from: from.value } : {},
|
|
66
67
|
...Option.isSome(to) ? { to: to.value } : {}
|
|
67
|
-
}).pipe(Effect.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return Effect.fail(err);
|
|
71
|
-
},
|
|
72
|
-
GitReadError: (err) => {
|
|
73
|
-
process.exitCode = 1;
|
|
74
|
-
return Effect.fail(err);
|
|
75
|
-
}
|
|
76
|
-
}))).toWrite.map((entry) => entry.diff);
|
|
68
|
+
}).pipe(Effect.tapError(() => Effect.sync(() => {
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
})))).toWrite.map((entry) => entry.diff);
|
|
77
71
|
if (markdown && !json) {
|
|
78
72
|
yield* Effect.log(renderMarkdownBlocks(diffs));
|
|
79
73
|
return;
|
|
@@ -89,7 +83,7 @@ const depsDetectCommand = Command.make("detect", {
|
|
|
89
83
|
package: packageOption,
|
|
90
84
|
json: jsonOption,
|
|
91
85
|
markdown: markdownOption
|
|
92
|
-
}, ({ from, to, cwd, package: pkg, json, markdown }) => runDepsDetect(cwd, from, to, pkg, json, markdown)).pipe(Command.withDescription("Compute the dependency diff between two refs"));
|
|
86
|
+
}, ({ from, to, cwd, package: pkg, json, markdown }) => runDepsDetect(cwd, from, to, pkg, json, markdown).pipe(Effect.provide(Changesets.makeDepsRegenDefault({ cwd: resolve(cwd) })))).pipe(Command.withDescription("Compute the dependency diff between two refs"));
|
|
93
87
|
|
|
94
88
|
//#endregion
|
|
95
89
|
export { depsDetectCommand, renderMarkdownBlocks, runDepsDetect };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Command, Options } from "@effect/cli";
|
|
2
1
|
import { Changesets } from "@savvy-web/silk-effects";
|
|
3
2
|
import { Console, Effect, Option } from "effect";
|
|
3
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
4
|
+
import { resolve } from "node:path";
|
|
4
5
|
|
|
5
6
|
//#region src/commands/changeset/commands/deps-regen.ts
|
|
6
7
|
/**
|
|
@@ -47,11 +48,11 @@ import { Console, Effect, Option } from "effect";
|
|
|
47
48
|
*/
|
|
48
49
|
const { DepsRegen } = Changesets;
|
|
49
50
|
/* v8 ignore start -- CLI option definitions */
|
|
50
|
-
const cwdOption =
|
|
51
|
-
const baseOption =
|
|
52
|
-
const packageOption =
|
|
53
|
-
const dryRunOption =
|
|
54
|
-
const jsonOption =
|
|
51
|
+
const cwdOption = Flag.directory("cwd").pipe(Flag.withDescription("Project root (defaults to the current working directory)"), Flag.withDefault("."));
|
|
52
|
+
const baseOption = Flag.string("base").pipe(Flag.withDescription("Override the base branch (defaults to config baseBranch)"), Flag.optional);
|
|
53
|
+
const packageOption = Flag.string("package").pipe(Flag.withDescription("Restrict regeneration to a single workspace package"), Flag.optional);
|
|
54
|
+
const dryRunOption = Flag.boolean("dry-run").pipe(Flag.withDescription("Print the plan without writing or deleting"), Flag.withDefault(false));
|
|
55
|
+
const jsonOption = Flag.boolean("json").pipe(Flag.withDescription("Emit a structured plan as JSON"), Flag.withDefault(false));
|
|
55
56
|
/* v8 ignore stop */
|
|
56
57
|
/**
|
|
57
58
|
* Handler exported for direct invocation in tests.
|
|
@@ -65,16 +66,9 @@ function runDepsRegen(cwd, base, pkg, dryRun, json) {
|
|
|
65
66
|
cwd,
|
|
66
67
|
...Option.isSome(base) ? { base: base.value } : {},
|
|
67
68
|
...Option.isSome(pkg) ? { package: pkg.value } : {}
|
|
68
|
-
}).pipe(Effect.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return Effect.fail(err);
|
|
72
|
-
},
|
|
73
|
-
GitReadError: (err) => {
|
|
74
|
-
process.exitCode = 1;
|
|
75
|
-
return Effect.fail(err);
|
|
76
|
-
}
|
|
77
|
-
}));
|
|
69
|
+
}).pipe(Effect.tapError(() => Effect.sync(() => {
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
})));
|
|
78
72
|
if (!dryRun) yield* service.execute(plan);
|
|
79
73
|
if (json) yield* Console.log(JSON.stringify(plan, null, 2));
|
|
80
74
|
else yield* renderHumanPlan(plan);
|
|
@@ -99,14 +93,14 @@ function renderHumanPlan(plan) {
|
|
|
99
93
|
}
|
|
100
94
|
});
|
|
101
95
|
}
|
|
102
|
-
/* v8 ignore next
|
|
96
|
+
/* v8 ignore next 12 */
|
|
103
97
|
const depsRegenCommand = Command.make("regen", {
|
|
104
98
|
cwd: cwdOption,
|
|
105
99
|
base: baseOption,
|
|
106
100
|
package: packageOption,
|
|
107
101
|
dryRun: dryRunOption,
|
|
108
102
|
json: jsonOption
|
|
109
|
-
}, ({ cwd, base, package: pkg, dryRun, json }) => runDepsRegen(cwd, base, pkg, dryRun, json)).pipe(Command.withDescription("Delete pure dependency changesets and regenerate them from the current diff"));
|
|
103
|
+
}, ({ cwd, base, package: pkg, dryRun, json }) => runDepsRegen(cwd, base, pkg, dryRun, json).pipe(Effect.provide(Changesets.makeDepsRegenDefault({ cwd: resolve(cwd) })))).pipe(Command.withDescription("Delete pure dependency changesets and regenerate them from the current diff"));
|
|
110
104
|
|
|
111
105
|
//#endregion
|
|
112
106
|
export { depsRegenCommand, runDepsRegen };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { Git } from "@effected/git";
|
|
2
|
+
import { WorkspaceRoot } from "@effected/workspaces";
|
|
1
3
|
import { Changesets } from "@savvy-web/silk-effects";
|
|
2
|
-
import { Data, Effect, Schema } from "effect";
|
|
3
|
-
import { WorkspaceRoot } from "workspaces-effect";
|
|
4
|
+
import { Data, Effect, Option, Result, Schema } from "effect";
|
|
4
5
|
import { join } from "node:path";
|
|
5
6
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
|
-
import {
|
|
7
|
-
import { applyEdits, modify, parse } from "jsonc-effect";
|
|
7
|
+
import { Jsonc, JsoncEdit, JsoncModifier } from "@effected/jsonc";
|
|
8
8
|
|
|
9
9
|
//#region src/commands/changeset/commands/init.ts
|
|
10
10
|
/**
|
|
@@ -117,17 +117,15 @@ var InitError = class extends InitErrorBase {
|
|
|
117
117
|
* @internal
|
|
118
118
|
*/
|
|
119
119
|
function detectGitHubRepo(cwd) {
|
|
120
|
-
|
|
121
|
-
const url =
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}).trim();
|
|
125
|
-
const https = url.match(/github\.com\/([^/]+)\/([^/.]+)/);
|
|
120
|
+
return Effect.gen(function* () {
|
|
121
|
+
const url = yield* (yield* Git).remoteUrl(cwd).pipe(Effect.catch(() => Effect.succeed(Option.none())));
|
|
122
|
+
if (Option.isNone(url)) return null;
|
|
123
|
+
const https = url.value.match(/github\.com\/([^/]+)\/([^/.]+)/);
|
|
126
124
|
if (https) return `${https[1]}/${https[2]}`;
|
|
127
|
-
const ssh = url.match(/github\.com:([^/]+)\/([^/.]+)/);
|
|
125
|
+
const ssh = url.value.match(/github\.com:([^/]+)\/([^/.]+)/);
|
|
128
126
|
if (ssh) return `${ssh[1]}/${ssh[2]}`;
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
return null;
|
|
128
|
+
});
|
|
131
129
|
}
|
|
132
130
|
/**
|
|
133
131
|
* Formatting options for `jsonc-effect` modify operations.
|
|
@@ -152,7 +150,7 @@ const JSONC_FORMAT = {
|
|
|
152
150
|
* @internal
|
|
153
151
|
*/
|
|
154
152
|
function resolveWorkspaceRoot(cwd) {
|
|
155
|
-
return WorkspaceRoot.pipe(Effect.flatMap((wr) => wr.find(cwd)), Effect.
|
|
153
|
+
return WorkspaceRoot.pipe(Effect.flatMap((wr) => wr.find(cwd)), Effect.catch(() => Effect.succeed(cwd)));
|
|
156
154
|
}
|
|
157
155
|
/**
|
|
158
156
|
* Find the first existing markdownlint config file from candidate paths.
|
|
@@ -331,30 +329,30 @@ function handleBaseMarkdownlint(root) {
|
|
|
331
329
|
reason: error instanceof Error ? error.message : String(error)
|
|
332
330
|
}));
|
|
333
331
|
}
|
|
334
|
-
let parsed = yield* parse(text);
|
|
332
|
+
let parsed = yield* Jsonc.parse(text);
|
|
335
333
|
const currentRules = Array.isArray(parsed.customRules) ? parsed.customRules : null;
|
|
336
334
|
if (currentRules === null) {
|
|
337
|
-
const edits = yield* modify(text, ["customRules"], [CUSTOM_RULES_ENTRY], { formattingOptions: JSONC_FORMAT });
|
|
338
|
-
text =
|
|
335
|
+
const edits = yield* JsoncModifier.modify(text, ["customRules"], [CUSTOM_RULES_ENTRY], { formattingOptions: JSONC_FORMAT });
|
|
336
|
+
text = JsoncEdit.applyAll(text, edits);
|
|
339
337
|
} else {
|
|
340
338
|
const desired = currentRules.filter((r) => r !== LEGACY_CUSTOM_RULES_ENTRY && r !== CUSTOM_RULES_ENTRY);
|
|
341
339
|
desired.push(CUSTOM_RULES_ENTRY);
|
|
342
340
|
if (desired.length !== currentRules.length || desired.some((r, i) => r !== currentRules[i])) {
|
|
343
|
-
const edits = yield* modify(text, ["customRules"], desired, { formattingOptions: JSONC_FORMAT });
|
|
344
|
-
text =
|
|
341
|
+
const edits = yield* JsoncModifier.modify(text, ["customRules"], desired, { formattingOptions: JSONC_FORMAT });
|
|
342
|
+
text = JsoncEdit.applyAll(text, edits);
|
|
345
343
|
}
|
|
346
344
|
}
|
|
347
|
-
parsed = yield* parse(text);
|
|
345
|
+
parsed = yield* Jsonc.parse(text);
|
|
348
346
|
const currentConfig = parsed.config;
|
|
349
347
|
if (typeof currentConfig !== "object" || currentConfig === null) {
|
|
350
|
-
const edits = yield* modify(text, ["config"], {}, { formattingOptions: JSONC_FORMAT });
|
|
351
|
-
text =
|
|
348
|
+
const edits = yield* JsoncModifier.modify(text, ["config"], {}, { formattingOptions: JSONC_FORMAT });
|
|
349
|
+
text = JsoncEdit.applyAll(text, edits);
|
|
352
350
|
}
|
|
353
|
-
parsed = yield* parse(text);
|
|
351
|
+
parsed = yield* Jsonc.parse(text);
|
|
354
352
|
const config = parsed.config;
|
|
355
353
|
for (const rule of RULE_NAMES) if (!(rule in config)) {
|
|
356
|
-
const edits = yield* modify(text, ["config", rule], false, { formattingOptions: JSONC_FORMAT });
|
|
357
|
-
text =
|
|
354
|
+
const edits = yield* JsoncModifier.modify(text, ["config", rule], false, { formattingOptions: JSONC_FORMAT });
|
|
355
|
+
text = JsoncEdit.applyAll(text, edits);
|
|
358
356
|
}
|
|
359
357
|
try {
|
|
360
358
|
writeFileSync(fullPath, text);
|
|
@@ -365,7 +363,7 @@ function handleBaseMarkdownlint(root) {
|
|
|
365
363
|
}));
|
|
366
364
|
}
|
|
367
365
|
return `Updated ${foundPath}`;
|
|
368
|
-
}).pipe(Effect.
|
|
366
|
+
}).pipe(Effect.catch((error) => {
|
|
369
367
|
if (error instanceof InitError) return Effect.fail(error);
|
|
370
368
|
return Effect.fail(new InitError({
|
|
371
369
|
step: "markdownlint config",
|
|
@@ -465,7 +463,8 @@ function checkConfig(changesetDir, repoSlug) {
|
|
|
465
463
|
});
|
|
466
464
|
const options = Array.isArray(changelog) ? changelog[1] : void 0;
|
|
467
465
|
if (options && typeof options === "object" && "versionFiles" in options) {
|
|
468
|
-
|
|
466
|
+
const result = Schema.decodeUnknownResult(LegacyVersionFilesSchema)(options.versionFiles);
|
|
467
|
+
if (Result.isFailure(result)) issues.push({
|
|
469
468
|
file: ".changeset/config.json",
|
|
470
469
|
message: "versionFiles config is invalid"
|
|
471
470
|
});
|
|
@@ -499,7 +498,7 @@ function checkBaseMarkdownlint(root) {
|
|
|
499
498
|
}];
|
|
500
499
|
try {
|
|
501
500
|
const raw = readFileSync(join(root, foundPath), "utf-8");
|
|
502
|
-
const parsed = Effect.runSync(parse(raw));
|
|
501
|
+
const parsed = Effect.runSync(Jsonc.parse(raw));
|
|
503
502
|
const issues = [];
|
|
504
503
|
if (!Array.isArray(parsed.customRules) || !parsed.customRules.some((r) => ACCEPTED_CUSTOM_RULES_ENTRIES.includes(r))) issues.push({
|
|
505
504
|
file: foundPath,
|
|
@@ -567,7 +566,7 @@ function runChangesetInit(opts) {
|
|
|
567
566
|
const { force, quiet, skipMarkdownlint, check } = opts;
|
|
568
567
|
return Effect.gen(function* () {
|
|
569
568
|
const root = yield* resolveWorkspaceRoot(process.cwd());
|
|
570
|
-
const repo = detectGitHubRepo(root);
|
|
569
|
+
const repo = yield* detectGitHubRepo(root);
|
|
571
570
|
if (!repo && !quiet) yield* Effect.log("Warning: could not detect GitHub repo from git remote, using placeholder");
|
|
572
571
|
const repoSlug = repo ?? "owner/repo";
|
|
573
572
|
if (check) {
|
|
@@ -589,26 +588,26 @@ function runChangesetInit(opts) {
|
|
|
589
588
|
const changesetDir = yield* ensureChangesetDir(root);
|
|
590
589
|
yield* Effect.log("Ensured .changeset/ directory");
|
|
591
590
|
const errors = [];
|
|
592
|
-
const configResult = yield* handleConfig(changesetDir, repoSlug, force).pipe(Effect.
|
|
593
|
-
if (configResult
|
|
594
|
-
yield* Effect.log(configResult.
|
|
591
|
+
const configResult = yield* handleConfig(changesetDir, repoSlug, force).pipe(Effect.result);
|
|
592
|
+
if (Result.isSuccess(configResult)) {
|
|
593
|
+
yield* Effect.log(configResult.success);
|
|
595
594
|
if (!quiet) yield* warnIfLegacyVersionFiles(changesetDir);
|
|
596
|
-
} else errors.push(configResult.
|
|
595
|
+
} else errors.push(configResult.failure);
|
|
597
596
|
if (!skipMarkdownlint) {
|
|
598
|
-
const baseResult = yield* handleBaseMarkdownlint(root).pipe(Effect.
|
|
599
|
-
if (baseResult
|
|
600
|
-
else errors.push(baseResult.
|
|
597
|
+
const baseResult = yield* handleBaseMarkdownlint(root).pipe(Effect.result);
|
|
598
|
+
if (Result.isSuccess(baseResult)) yield* Effect.log(baseResult.success);
|
|
599
|
+
else errors.push(baseResult.failure);
|
|
601
600
|
}
|
|
602
|
-
const mdlintResult = yield* handleChangesetMarkdownlint(changesetDir, root, force).pipe(Effect.
|
|
603
|
-
if (mdlintResult
|
|
604
|
-
else errors.push(mdlintResult.
|
|
601
|
+
const mdlintResult = yield* handleChangesetMarkdownlint(changesetDir, root, force).pipe(Effect.result);
|
|
602
|
+
if (Result.isSuccess(mdlintResult)) yield* Effect.log(mdlintResult.success);
|
|
603
|
+
else errors.push(mdlintResult.failure);
|
|
605
604
|
if (errors.length > 0) {
|
|
606
605
|
for (const err of errors) yield* Effect.logError(err.message);
|
|
607
606
|
if (!quiet) process.exitCode = 1;
|
|
608
607
|
return;
|
|
609
608
|
}
|
|
610
609
|
yield* Effect.log("Init complete.");
|
|
611
|
-
}).pipe(Effect.
|
|
610
|
+
}).pipe(Effect.catch((error) => Effect.gen(function* () {
|
|
612
611
|
if (!quiet) {
|
|
613
612
|
yield* Effect.logError(error instanceof InitError ? error.message : `Init failed: ${String(error)}`);
|
|
614
613
|
process.exitCode = 1;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Args, Command, Options } from "@effect/cli";
|
|
2
1
|
import { Changesets } from "@savvy-web/silk-effects";
|
|
3
2
|
import { Console, Effect } from "effect";
|
|
3
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
|
|
6
6
|
//#region src/commands/changeset/commands/lint.ts
|
|
@@ -28,8 +28,8 @@ import { resolve } from "node:path";
|
|
|
28
28
|
*/
|
|
29
29
|
const { ChangesetLinter } = Changesets;
|
|
30
30
|
/* v8 ignore start -- CLI option definitions; handler tested via runLint */
|
|
31
|
-
const dirArg =
|
|
32
|
-
const quietOption =
|
|
31
|
+
const dirArg = Argument.directory("dir").pipe(Argument.withDefault(".changeset"));
|
|
32
|
+
const quietOption = Flag.boolean("quiet").pipe(Flag.withAlias("q"), Flag.withDescription("Only output errors, no summary"), Flag.withDefault(false));
|
|
33
33
|
/* v8 ignore stop */
|
|
34
34
|
/**
|
|
35
35
|
* Run machine-readable lint validation on all changeset files in `dir`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { requireValidConfig } from "../utils/config-gate.js";
|
|
2
|
-
import { Args, Command, Options } from "@effect/cli";
|
|
3
2
|
import { Changesets } from "@savvy-web/silk-effects";
|
|
4
3
|
import { Effect } from "effect";
|
|
4
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
5
5
|
import { dirname, resolve } from "node:path";
|
|
6
6
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
7
7
|
|
|
@@ -38,9 +38,9 @@ import { readFileSync, writeFileSync } from "node:fs";
|
|
|
38
38
|
*/
|
|
39
39
|
const { ChangelogTransformer } = Changesets;
|
|
40
40
|
/* v8 ignore start -- CLI option definitions; handler tested via runTransform */
|
|
41
|
-
const fileArg =
|
|
42
|
-
const dryRunOption =
|
|
43
|
-
const checkOption =
|
|
41
|
+
const fileArg = Argument.file("file").pipe(Argument.withDefault("CHANGELOG.md"));
|
|
42
|
+
const dryRunOption = Flag.boolean("dry-run").pipe(Flag.withAlias("n"), Flag.withDescription("Print transformed output instead of writing"), Flag.withDefault(false));
|
|
43
|
+
const checkOption = Flag.boolean("check").pipe(Flag.withAlias("c"), Flag.withDescription("Exit 1 if file would change (for CI)"), Flag.withDefault(false));
|
|
44
44
|
/* v8 ignore stop */
|
|
45
45
|
/**
|
|
46
46
|
* Run the remark transform pipeline on a single changelog file.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Args, Command } from "@effect/cli";
|
|
2
1
|
import { Changesets } from "@savvy-web/silk-effects";
|
|
3
2
|
import { Effect } from "effect";
|
|
3
|
+
import { Argument, Command } from "effect/unstable/cli";
|
|
4
4
|
|
|
5
5
|
//#region src/commands/changeset/commands/validate-file.ts
|
|
6
6
|
/**
|
|
@@ -19,7 +19,7 @@ import { Effect } from "effect";
|
|
|
19
19
|
*/
|
|
20
20
|
const { ChangesetLinter } = Changesets;
|
|
21
21
|
/* v8 ignore next */
|
|
22
|
-
const fileArg =
|
|
22
|
+
const fileArg = Argument.file("file");
|
|
23
23
|
/**
|
|
24
24
|
* Run lint validation on a single changeset file.
|
|
25
25
|
*
|
|
@@ -34,7 +34,7 @@ const fileArg = Args.file({ name: "file" });
|
|
|
34
34
|
*/
|
|
35
35
|
function runValidateFile(filePath) {
|
|
36
36
|
return Effect.gen(function* () {
|
|
37
|
-
const result = yield* Effect.try(() => ChangesetLinter.validateFile(filePath)).pipe(Effect.
|
|
37
|
+
const result = yield* Effect.try(() => ChangesetLinter.validateFile(filePath)).pipe(Effect.catch((error) => Effect.gen(function* () {
|
|
38
38
|
yield* Effect.log(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
39
39
|
process.exitCode = 1;
|
|
40
40
|
return null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { requireValidConfig } from "../utils/config-gate.js";
|
|
2
|
-
import { Command, Options } from "@effect/cli";
|
|
3
2
|
import { Changesets } from "@savvy-web/silk-effects";
|
|
4
3
|
import { Effect } from "effect";
|
|
4
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
5
5
|
|
|
6
6
|
//#region src/commands/changeset/commands/version.ts
|
|
7
7
|
/**
|
|
@@ -15,7 +15,7 @@ import { Effect } from "effect";
|
|
|
15
15
|
* @internal
|
|
16
16
|
*/
|
|
17
17
|
/* v8 ignore start -- CLI option definitions; handler tested via runVersion */
|
|
18
|
-
const dryRunOption =
|
|
18
|
+
const dryRunOption = Flag.boolean("dry-run").pipe(Flag.withAlias("n"), Flag.withDescription("Compute and report the release without writing anything"), Flag.withDefault(false));
|
|
19
19
|
/* v8 ignore stop */
|
|
20
20
|
/**
|
|
21
21
|
* Validate config, then natively apply (or dry-run) the release via
|