@savvy-web/silk-effects 0.6.0 → 1.0.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 +48 -17
- package/_virtual/_rolldown/runtime.js +18 -0
- package/changesets/api/categories.js +247 -0
- package/changesets/api/changelog.js +134 -0
- package/changesets/api/dependency-table.js +163 -0
- package/changesets/api/linter.js +168 -0
- package/changesets/api/transformer.js +140 -0
- package/changesets/categories/index.js +299 -0
- package/changesets/categories/types.js +66 -0
- package/changesets/changelog/formatting.js +119 -0
- package/changesets/changelog/getDependencyReleaseLine.js +114 -0
- package/changesets/changelog/getReleaseLine.js +122 -0
- package/changesets/changelog/index.js +99 -0
- package/changesets/constants.js +43 -0
- package/changesets/errors.js +305 -0
- package/changesets/index.js +146 -0
- package/changesets/markdownlint/index.js +29 -0
- package/changesets/markdownlint/rules/content-structure.js +98 -0
- package/changesets/markdownlint/rules/dependency-table-format.js +170 -0
- package/changesets/markdownlint/rules/heading-hierarchy.js +61 -0
- package/changesets/markdownlint/rules/required-sections.js +54 -0
- package/changesets/markdownlint/rules/uncategorized-content.js +54 -0
- package/changesets/markdownlint/rules/utils.js +30 -0
- package/changesets/remark/plugins/aggregate-dependency-tables.js +47 -0
- package/changesets/remark/plugins/contributor-footnotes.js +123 -0
- package/changesets/remark/plugins/deduplicate-items.js +30 -0
- package/changesets/remark/plugins/issue-link-refs.js +58 -0
- package/changesets/remark/plugins/merge-sections.js +43 -0
- package/changesets/remark/plugins/normalize-format.js +47 -0
- package/changesets/remark/plugins/reorder-sections.js +34 -0
- package/changesets/remark/presets.js +119 -0
- package/changesets/remark/rules/content-structure.js +22 -0
- package/changesets/remark/rules/dependency-table-format.js +40 -0
- package/changesets/remark/rules/heading-hierarchy.js +19 -0
- package/changesets/remark/rules/required-sections.js +17 -0
- package/changesets/remark/rules/uncategorized-content.js +31 -0
- package/changesets/schemas/changeset.js +146 -0
- package/changesets/schemas/dependency-table.js +189 -0
- package/changesets/schemas/git.js +69 -0
- package/changesets/schemas/github.js +175 -0
- package/changesets/schemas/options.js +182 -0
- package/changesets/schemas/package-scope.js +128 -0
- package/changesets/schemas/primitives.js +72 -0
- package/changesets/schemas/version-files.js +151 -0
- package/changesets/services/branch-analyzer.js +278 -0
- package/changesets/services/changelog.js +50 -0
- package/changesets/services/config-inspector.js +390 -0
- package/changesets/services/github.js +178 -0
- package/changesets/services/markdown.js +106 -0
- package/changesets/services/workspace-snapshot.js +182 -0
- package/changesets/utils/commit-parser.js +80 -0
- package/changesets/utils/dep-diff.js +77 -0
- package/changesets/utils/dependency-table.js +347 -0
- package/changesets/utils/issue-refs.js +101 -0
- package/changesets/utils/jsonpath.js +175 -0
- package/changesets/utils/logger.js +50 -0
- package/changesets/utils/markdown-link.js +57 -0
- package/changesets/utils/publishability.js +39 -0
- package/changesets/utils/remark-pipeline.js +79 -0
- package/changesets/utils/section-parser.js +94 -0
- package/changesets/utils/strip-frontmatter.js +46 -0
- package/changesets/utils/version-blocks.js +108 -0
- package/changesets/utils/version-files.js +336 -0
- package/changesets/utils/worktree-snapshot.js +142 -0
- package/changesets/vendor/github-info.js +55 -0
- package/commitlint/config/factory.js +69 -0
- package/commitlint/config/plugins.js +227 -0
- package/commitlint/config/rules.js +155 -0
- package/commitlint/config/schema.js +46 -0
- package/commitlint/detection/dco.js +53 -0
- package/commitlint/detection/scopes.js +45 -0
- package/commitlint/formatter/format.js +85 -0
- package/commitlint/formatter/messages.js +79 -0
- package/commitlint/hook/diagnostics/branch.js +36 -0
- package/commitlint/hook/diagnostics/cache.js +37 -0
- package/commitlint/hook/diagnostics/commitlint-config.js +36 -0
- package/commitlint/hook/diagnostics/open-issues.js +56 -0
- package/commitlint/hook/diagnostics/package-manager.js +51 -0
- package/commitlint/hook/diagnostics/signing.js +107 -0
- package/commitlint/hook/envelope.js +46 -0
- package/commitlint/hook/output.js +45 -0
- package/commitlint/hook/parse-bash-command.js +105 -0
- package/commitlint/hook/rules/closes-trailer.js +31 -0
- package/commitlint/hook/rules/forbidden-content.js +32 -0
- package/commitlint/hook/rules/plan-leakage.js +36 -0
- package/commitlint/hook/rules/signing-flag-conflict.js +25 -0
- package/commitlint/hook/rules/soft-wrap.js +37 -0
- package/commitlint/hook/rules/types.js +14 -0
- package/commitlint/hook/rules/verbosity.js +31 -0
- package/commitlint/hook/silence-logger.js +39 -0
- package/commitlint/index.js +146 -0
- package/commitlint/prompt/config.js +91 -0
- package/commitlint/prompt/emojis.js +74 -0
- package/commitlint/prompt/prompter.js +135 -0
- package/commitlint/static.js +73 -0
- package/errors/BiomeSyncError.js +21 -0
- package/errors/ChangesetConfigError.js +20 -0
- package/errors/ConfigNotFoundError.js +21 -0
- package/errors/SectionParseError.js +16 -0
- package/errors/SectionValidationError.js +16 -0
- package/errors/SectionWriteError.js +16 -0
- package/errors/TagFormatError.js +20 -0
- package/errors/ToolNotFoundError.js +11 -0
- package/errors/ToolResolutionError.js +11 -0
- package/errors/ToolVersionMismatchError.js +11 -0
- package/errors/VersioningDetectionError.js +20 -0
- package/errors/WorkspaceAnalysisError.js +21 -0
- package/index.d.ts +9743 -8380
- package/index.js +36 -6657
- package/lint/Handler.js +39 -0
- package/lint/cli/sections.js +65 -0
- package/lint/cli/templates/markdownlint.gen.js +183 -0
- package/lint/config/Preset.js +152 -0
- package/lint/config/createConfig.js +89 -0
- package/lint/handlers/Biome.js +179 -0
- package/lint/handlers/Markdown.js +139 -0
- package/lint/handlers/PackageJson.js +130 -0
- package/lint/handlers/PnpmWorkspace.js +141 -0
- package/lint/handlers/ShellScripts.js +58 -0
- package/lint/handlers/TypeScript.js +134 -0
- package/lint/handlers/Yaml.js +167 -0
- package/lint/index.js +52 -0
- package/lint/utils/Command.js +285 -0
- package/lint/utils/Filter.js +100 -0
- package/lint/utils/Workspace.js +86 -0
- package/package.json +52 -63
- package/schemas/CommentStyle.js +16 -0
- package/schemas/ResolvedTool.js +63 -0
- package/schemas/SavvySections.js +113 -0
- package/schemas/SectionBlock.js +70 -0
- package/schemas/SectionDefinition.js +121 -0
- package/schemas/SectionResults.js +12 -0
- package/schemas/TagStrategySchemas.js +18 -0
- package/schemas/ToolDefinition.js +39 -0
- package/schemas/ToolResults.js +14 -0
- package/schemas/VersioningSchemas.js +95 -0
- package/schemas/WorkspaceAnalysisSchemas.js +190 -0
- package/services/BiomeSchemaSync.js +133 -0
- package/services/ChangesetConfig.js +78 -0
- package/services/ChangesetConfigReader.js +106 -0
- package/services/ConfigDiscovery.js +71 -0
- package/services/ManagedSection.js +288 -0
- package/services/SilkPublishability.js +193 -0
- package/services/SilkWorkspaceAnalyzer.js +213 -0
- package/services/TagStrategy.js +54 -0
- package/services/ToolDiscovery.js +229 -0
- package/services/VersioningStrategy.js +67 -0
- package/tsdoc-metadata.json +11 -11
- package/turbo/digest.js +127 -0
- package/turbo/errors.js +48 -0
- package/turbo/index.js +32 -0
- package/turbo/schemas/DryRun.js +57 -0
- package/turbo/schemas/results.js +61 -0
- package/turbo/services/TurboInspector.js +100 -0
- package/utils/ToolCommand.js +40 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/changesets/schemas/version-files.ts
|
|
4
|
+
/**
|
|
5
|
+
* Schemas for `versionFiles` configuration entries.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* `versionFiles` lets \@savvy-web/changesets update version fields in
|
|
9
|
+
* arbitrary JSON files (beyond `package.json`) during the
|
|
10
|
+
* `changeset version` command. Each entry specifies a glob pattern to
|
|
11
|
+
* match files and optional JSONPath expressions to locate version fields
|
|
12
|
+
* within those files.
|
|
13
|
+
*
|
|
14
|
+
* Two related schemas live here:
|
|
15
|
+
*
|
|
16
|
+
* - {@link VersionFileConfigSchema} — the **new** per-package entry shape
|
|
17
|
+
* used inside `packages[*].versionFiles`. It has no `package` field
|
|
18
|
+
* because the parent record key already names the owning package.
|
|
19
|
+
* - {@link LegacyVersionFileConfigSchema} — the **deprecated** entry shape
|
|
20
|
+
* used by the top-level `versionFiles[]` array on the changelog options.
|
|
21
|
+
* It carries an optional `package` field so the owner can be named
|
|
22
|
+
* inline. Accepted in 0.9.0 with a deprecation warning; removed in
|
|
23
|
+
* 1.0.0.
|
|
24
|
+
*
|
|
25
|
+
* @see {@link ChangesetOptionsSchema} for the consuming options shape
|
|
26
|
+
* @see {@link PackageScopeSchema} for the new per-package container
|
|
27
|
+
* @see {@link https://effect.website/docs/schema/introduction | Effect Schema documentation}
|
|
28
|
+
*
|
|
29
|
+
* @packageDocumentation
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Schema for a JSONPath expression starting with `$.`.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* Supports property access (`$.foo.bar`), array wildcard (`$.foo[*].bar`),
|
|
36
|
+
* and array index access (`$.foo[0].bar`). The expression must begin with
|
|
37
|
+
* `$.` followed by at least one property segment.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* import { Schema } from "effect";
|
|
42
|
+
* import { JsonPathSchema } from "@savvy-web/changesets";
|
|
43
|
+
*
|
|
44
|
+
* // Succeeds — simple property access
|
|
45
|
+
* Schema.decodeUnknownSync(JsonPathSchema)("$.version");
|
|
46
|
+
*
|
|
47
|
+
* // Succeeds — nested property access
|
|
48
|
+
* Schema.decodeUnknownSync(JsonPathSchema)("$.metadata.version");
|
|
49
|
+
*
|
|
50
|
+
* // Throws ParseError — missing "$." prefix
|
|
51
|
+
* Schema.decodeUnknownSync(JsonPathSchema)("version");
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @see {@link VersionFileConfigSchema} which uses this for the `paths` field
|
|
55
|
+
*
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
58
|
+
const JsonPathSchema = Schema.String.pipe(Schema.pattern(/^\$\.[^.]/, { message: () => "JSONPath must start with \"$.\" followed by a property (e.g., \"$.version\", \"$.metadata.version\")" }));
|
|
59
|
+
/**
|
|
60
|
+
* Schema for a single version file configuration entry — **new shape**.
|
|
61
|
+
*
|
|
62
|
+
* @remarks
|
|
63
|
+
* Used inside `packages[*].versionFiles` (i.e., underneath a parent
|
|
64
|
+
* record key that names the owning package). Has no `package` field
|
|
65
|
+
* because the parent key already supplies that information.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* import { Schema } from "effect";
|
|
70
|
+
* import { VersionFileConfigSchema } from "@savvy-web/changesets";
|
|
71
|
+
*
|
|
72
|
+
* const entry = Schema.decodeUnknownSync(VersionFileConfigSchema)({
|
|
73
|
+
* glob: "plugin/.claude-plugin/plugin.json",
|
|
74
|
+
* paths: ["$.version"],
|
|
75
|
+
* });
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @see {@link VersionFileConfig} for the inferred TypeScript type
|
|
79
|
+
* @see {@link LegacyVersionFileConfigSchema} for the deprecated shape
|
|
80
|
+
* used by the top-level `versionFiles[]` array
|
|
81
|
+
* @see {@link JsonPathSchema} for JSONPath validation rules
|
|
82
|
+
*
|
|
83
|
+
* @public
|
|
84
|
+
*/
|
|
85
|
+
const VersionFileConfigSchema = Schema.Struct({
|
|
86
|
+
/** Glob pattern to match JSON files. */
|
|
87
|
+
glob: Schema.String.pipe(Schema.minLength(1)),
|
|
88
|
+
/** JSONPath expressions to locate version fields. Defaults to `["$.version"]`. */
|
|
89
|
+
paths: Schema.optional(Schema.Array(JsonPathSchema))
|
|
90
|
+
});
|
|
91
|
+
/**
|
|
92
|
+
* Schema for an array of new-shape {@link VersionFileConfigSchema} entries.
|
|
93
|
+
*
|
|
94
|
+
* @public
|
|
95
|
+
*/
|
|
96
|
+
const VersionFilesSchema = Schema.Array(VersionFileConfigSchema);
|
|
97
|
+
/**
|
|
98
|
+
* Schema for a single version file configuration entry — **legacy shape**.
|
|
99
|
+
*
|
|
100
|
+
* @remarks
|
|
101
|
+
* DEPRECATED. Used only for parsing the top-level `versionFiles[]` array on
|
|
102
|
+
* the changelog options block. Carries an optional `package` field so the
|
|
103
|
+
* owning package can be named inline (alternatively, the runtime falls back
|
|
104
|
+
* to longest-prefix path matching against workspace package directories).
|
|
105
|
+
*
|
|
106
|
+
* Accepted in 0.9.0 with a deprecation warning emitted by `ConfigInspector`
|
|
107
|
+
* at config-load time. Removed in 1.0.0 — migrate to the new
|
|
108
|
+
* {@link VersionFileConfigSchema} under
|
|
109
|
+
* `changelog[1].packages[<name>].versionFiles`.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* import { Schema } from "effect";
|
|
114
|
+
* import { LegacyVersionFileConfigSchema } from "@savvy-web/changesets";
|
|
115
|
+
*
|
|
116
|
+
* // Owner named inline via the `package` field
|
|
117
|
+
* const entry = Schema.decodeUnknownSync(LegacyVersionFileConfigSchema)({
|
|
118
|
+
* glob: "plugin/.claude-plugin/plugin.json",
|
|
119
|
+
* paths: ["$.version"],
|
|
120
|
+
* package: "@savvy-web/changesets",
|
|
121
|
+
* });
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* @see {@link VersionFileConfigSchema} for the replacement (new) shape
|
|
125
|
+
* @see {@link LegacyVersionFileConfig} for the inferred TypeScript type
|
|
126
|
+
*
|
|
127
|
+
* @deprecated 0.9.0 — migrate to {@link VersionFileConfigSchema} inside
|
|
128
|
+
* `packages[*].versionFiles`. Removed in 1.0.0.
|
|
129
|
+
* @public
|
|
130
|
+
*/
|
|
131
|
+
const LegacyVersionFileConfigSchema = Schema.Struct({
|
|
132
|
+
/** Glob pattern to match JSON files. */
|
|
133
|
+
glob: Schema.String.pipe(Schema.minLength(1)),
|
|
134
|
+
/** JSONPath expressions to locate version fields. Defaults to `["$.version"]`. */
|
|
135
|
+
paths: Schema.optional(Schema.Array(JsonPathSchema)),
|
|
136
|
+
/** Workspace package name to source the version from, bypassing path-based resolution. */
|
|
137
|
+
package: Schema.optional(Schema.String.pipe(Schema.minLength(1)))
|
|
138
|
+
});
|
|
139
|
+
/**
|
|
140
|
+
* Schema for an array of legacy {@link LegacyVersionFileConfigSchema}
|
|
141
|
+
* entries — used by the deprecated top-level `versionFiles[]` array on
|
|
142
|
+
* `ChangesetOptionsSchema`.
|
|
143
|
+
*
|
|
144
|
+
* @deprecated 0.9.0 — migrate to {@link VersionFilesSchema} inside
|
|
145
|
+
* `packages[*].versionFiles`. Removed in 1.0.0.
|
|
146
|
+
* @public
|
|
147
|
+
*/
|
|
148
|
+
const LegacyVersionFilesSchema = Schema.Array(LegacyVersionFileConfigSchema);
|
|
149
|
+
|
|
150
|
+
//#endregion
|
|
151
|
+
export { JsonPathSchema, LegacyVersionFileConfigSchema, LegacyVersionFilesSchema, VersionFileConfigSchema, VersionFilesSchema };
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { GitError } from "../errors.js";
|
|
2
|
+
import { ConfigInspector } from "./config-inspector.js";
|
|
3
|
+
import { Context, Effect, Layer } from "effect";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
//#region src/changesets/services/branch-analyzer.ts
|
|
7
|
+
/**
|
|
8
|
+
* `BranchAnalyzer` service — combine a git diff against the base branch with
|
|
9
|
+
* the per-file classification produced by {@link ConfigInspector}.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* This is the single call that the changeset-manager agent uses during its
|
|
13
|
+
* inventory step: one invocation returns the diff, the per-file package
|
|
14
|
+
* attribution, the set of packages affected by the branch, and the list of
|
|
15
|
+
* paths that did not map to any known release surface (so the agent can ask
|
|
16
|
+
* the user about them rather than silently excluding).
|
|
17
|
+
*
|
|
18
|
+
* Base-branch resolution order (highest priority first):
|
|
19
|
+
*
|
|
20
|
+
* 1. Explicit `opts.baseBranch` passed to {@link BranchAnalyzerShape.analyzeBranch}.
|
|
21
|
+
* 2. `baseBranch` from `.changeset/config.json` (surfaced via the inspector).
|
|
22
|
+
* 3. The branch `origin/HEAD` points to (`git symbolic-ref refs/remotes/origin/HEAD`).
|
|
23
|
+
* 4. `"main"` as a final fallback.
|
|
24
|
+
*
|
|
25
|
+
* Diff resolution covers everything the user might commit before merging:
|
|
26
|
+
*
|
|
27
|
+
* 1. `git merge-base <base> HEAD` finds the common ancestor.
|
|
28
|
+
* 2. `git diff --name-status <merge-base>` (working tree vs merge-base)
|
|
29
|
+
* returns every committed, staged, AND unstaged change since the
|
|
30
|
+
* branch diverged. The two-arg `<merge-base>...HEAD` form would miss
|
|
31
|
+
* work-in-progress — what the user has open in their editor right
|
|
32
|
+
* now is exactly the state the agent needs to document.
|
|
33
|
+
* 3. `git ls-files --others --exclude-standard` adds untracked files
|
|
34
|
+
* (entirely new files not yet `git add`-ed). Each is reported with
|
|
35
|
+
* status `"added"`.
|
|
36
|
+
*
|
|
37
|
+
* The two results are deduped by path before classification. Renames
|
|
38
|
+
* are reported as `"renamed"` with the new path; the old path is
|
|
39
|
+
* discarded since the classifier needs a single canonical path per
|
|
40
|
+
* file.
|
|
41
|
+
*
|
|
42
|
+
* @see {@link BranchAnalyzer} for the service tag
|
|
43
|
+
* @see {@link BranchAnalyzerLive} for the production layer
|
|
44
|
+
* @see {@link ConfigInspector} for the underlying classification service
|
|
45
|
+
*
|
|
46
|
+
* @packageDocumentation
|
|
47
|
+
*/
|
|
48
|
+
const _tag = Context.Tag("BranchAnalyzer");
|
|
49
|
+
/**
|
|
50
|
+
* Base class for {@link BranchAnalyzer}.
|
|
51
|
+
*
|
|
52
|
+
* @privateRemarks
|
|
53
|
+
* Effect's `Context.Tag` creates an anonymous base class that api-extractor
|
|
54
|
+
* cannot follow without an explicit export. Do not delete.
|
|
55
|
+
*
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
const BranchAnalyzerBase = _tag();
|
|
59
|
+
/**
|
|
60
|
+
* Effect service tag for {@link BranchAnalyzerShape}.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* import { Effect } from "effect";
|
|
65
|
+
* import { BranchAnalyzer, BranchAnalyzerLive, ConfigInspectorLive } from "@savvy-web/changesets";
|
|
66
|
+
*
|
|
67
|
+
* const program = Effect.gen(function* () {
|
|
68
|
+
* const analyzer = yield* BranchAnalyzer;
|
|
69
|
+
* const analysis = yield* analyzer.analyzeBranch(process.cwd());
|
|
70
|
+
* return analysis.packagesAffected;
|
|
71
|
+
* });
|
|
72
|
+
*
|
|
73
|
+
* Effect.runPromise(
|
|
74
|
+
* program.pipe(
|
|
75
|
+
* Effect.provide(BranchAnalyzerLive),
|
|
76
|
+
* Effect.provide(ConfigInspectorLive),
|
|
77
|
+
* // ... + ChangesetConfigReaderLive + WorkspacesLive + NodeContext.layer
|
|
78
|
+
* ),
|
|
79
|
+
* );
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* @public
|
|
83
|
+
*/
|
|
84
|
+
var BranchAnalyzer = class extends BranchAnalyzerBase {};
|
|
85
|
+
/**
|
|
86
|
+
* Invoke `git` with the given args under `cwd` and return stdout. On
|
|
87
|
+
* non-zero exit (or any throw), maps to a {@link GitError} carrying the
|
|
88
|
+
* command, cwd, and captured stderr.
|
|
89
|
+
*/
|
|
90
|
+
function runGit(cwd, args) {
|
|
91
|
+
return Effect.try({
|
|
92
|
+
try: () => execFileSync("git", args, {
|
|
93
|
+
cwd,
|
|
94
|
+
encoding: "utf8",
|
|
95
|
+
stdio: [
|
|
96
|
+
"ignore",
|
|
97
|
+
"pipe",
|
|
98
|
+
"pipe"
|
|
99
|
+
]
|
|
100
|
+
}),
|
|
101
|
+
catch: (error) => {
|
|
102
|
+
const e = error;
|
|
103
|
+
const stderr = typeof e.stderr === "string" ? e.stderr : e.stderr?.toString() ?? "";
|
|
104
|
+
return new GitError({
|
|
105
|
+
command: `git ${args.join(" ")}`,
|
|
106
|
+
cwd,
|
|
107
|
+
reason: stderr.trim() || e.message || String(error)
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const STATUS_MAP = {
|
|
113
|
+
A: "added",
|
|
114
|
+
M: "modified",
|
|
115
|
+
D: "deleted",
|
|
116
|
+
R: "renamed",
|
|
117
|
+
C: "copied",
|
|
118
|
+
T: "typechange",
|
|
119
|
+
U: "unmerged"
|
|
120
|
+
};
|
|
121
|
+
function statusFromCode(code) {
|
|
122
|
+
return STATUS_MAP[code.charAt(0)] ?? "unknown";
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Parse `git diff --name-status -z` output into one entry per changed file.
|
|
126
|
+
*
|
|
127
|
+
* `-z` separates fields with NUL bytes and avoids the per-record `\n`
|
|
128
|
+
* delimiter, so paths containing spaces or special characters round-trip
|
|
129
|
+
* cleanly. Rename and copy entries occupy three NUL-separated tokens
|
|
130
|
+
* (status, old path, new path) instead of two; everything else is two.
|
|
131
|
+
*/
|
|
132
|
+
function parseNameStatus(output) {
|
|
133
|
+
if (output.length === 0) return [];
|
|
134
|
+
const tokens = output.split("\0");
|
|
135
|
+
if (tokens[tokens.length - 1] === "") tokens.pop();
|
|
136
|
+
const entries = [];
|
|
137
|
+
for (let i = 0; i < tokens.length;) {
|
|
138
|
+
const code = tokens[i] ?? "";
|
|
139
|
+
if (code.length === 0) {
|
|
140
|
+
/* v8 ignore next 2 -- defensive guard; trailing empties stripped at parse time */
|
|
141
|
+
i += 1;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const status = statusFromCode(code);
|
|
145
|
+
if (status === "renamed" || status === "copied") {
|
|
146
|
+
const newPath = tokens[i + 2] ?? "";
|
|
147
|
+
if (newPath.length > 0) entries.push({
|
|
148
|
+
path: newPath,
|
|
149
|
+
status
|
|
150
|
+
});
|
|
151
|
+
i += 3;
|
|
152
|
+
} else {
|
|
153
|
+
const path = tokens[i + 1] ?? "";
|
|
154
|
+
if (path.length > 0) entries.push({
|
|
155
|
+
path,
|
|
156
|
+
status
|
|
157
|
+
});
|
|
158
|
+
i += 2;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return entries;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Parse a NUL-separated list of paths (e.g., `git ls-files -z` output) into
|
|
165
|
+
* an array of strings, dropping any trailing empty entry left by the final
|
|
166
|
+
* NUL byte.
|
|
167
|
+
*/
|
|
168
|
+
function parseNulSeparatedPaths(output) {
|
|
169
|
+
if (output.length === 0) return [];
|
|
170
|
+
const tokens = output.split("\0");
|
|
171
|
+
if (tokens[tokens.length - 1] === "") tokens.pop();
|
|
172
|
+
return tokens.filter((t) => t.length > 0);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Resolve the base branch using the documented priority order.
|
|
176
|
+
*/
|
|
177
|
+
function resolveBaseBranch(opts) {
|
|
178
|
+
if (opts.explicit && opts.explicit.length > 0) return Effect.succeed(opts.explicit);
|
|
179
|
+
if (opts.configBaseBranch && opts.configBaseBranch !== "main") return Effect.succeed(opts.configBaseBranch);
|
|
180
|
+
return runGit(opts.cwd, [
|
|
181
|
+
"symbolic-ref",
|
|
182
|
+
"--quiet",
|
|
183
|
+
"--short",
|
|
184
|
+
"refs/remotes/origin/HEAD"
|
|
185
|
+
]).pipe(
|
|
186
|
+
/* v8 ignore start -- callback only reachable with a remote that exposes origin/HEAD */
|
|
187
|
+
Effect.map((stdout) => {
|
|
188
|
+
const trimmed = stdout.trim();
|
|
189
|
+
return trimmed.length > 0 ? trimmed.replace(/^origin\//, "") : opts.configBaseBranch;
|
|
190
|
+
}),
|
|
191
|
+
/* v8 ignore stop */
|
|
192
|
+
Effect.catchAll(() => Effect.succeed(opts.configBaseBranch))
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
function makeShape(inspector) {
|
|
196
|
+
const analyzeBranch = (cwd, opts) => Effect.gen(function* () {
|
|
197
|
+
const inspected = yield* inspector.inspect(cwd);
|
|
198
|
+
const baseBranch = yield* resolveBaseBranch({
|
|
199
|
+
explicit: opts?.baseBranch,
|
|
200
|
+
configBaseBranch: inspected.baseBranch,
|
|
201
|
+
cwd
|
|
202
|
+
});
|
|
203
|
+
const mergeBaseSha = (yield* runGit(cwd, [
|
|
204
|
+
"merge-base",
|
|
205
|
+
baseBranch,
|
|
206
|
+
"HEAD"
|
|
207
|
+
])).trim();
|
|
208
|
+
const diffEntries = parseNameStatus(yield* runGit(cwd, [
|
|
209
|
+
"diff",
|
|
210
|
+
"--name-status",
|
|
211
|
+
"-z",
|
|
212
|
+
mergeBaseSha
|
|
213
|
+
]));
|
|
214
|
+
const untrackedEntries = parseNulSeparatedPaths(yield* runGit(cwd, [
|
|
215
|
+
"ls-files",
|
|
216
|
+
"-z",
|
|
217
|
+
"--others",
|
|
218
|
+
"--exclude-standard"
|
|
219
|
+
])).map((path) => ({
|
|
220
|
+
path,
|
|
221
|
+
status: "added"
|
|
222
|
+
}));
|
|
223
|
+
const seen = /* @__PURE__ */ new Set();
|
|
224
|
+
const rawEntries = [];
|
|
225
|
+
for (const e of diffEntries) {
|
|
226
|
+
if (seen.has(e.path)) continue;
|
|
227
|
+
seen.add(e.path);
|
|
228
|
+
rawEntries.push(e);
|
|
229
|
+
}
|
|
230
|
+
for (const e of untrackedEntries) {
|
|
231
|
+
if (seen.has(e.path)) continue;
|
|
232
|
+
seen.add(e.path);
|
|
233
|
+
rawEntries.push(e);
|
|
234
|
+
}
|
|
235
|
+
const paths = rawEntries.map((e) => e.path);
|
|
236
|
+
const classifications = yield* inspector.classify(cwd, paths);
|
|
237
|
+
const files = rawEntries.map((entry, idx) => {
|
|
238
|
+
const c = classifications[idx];
|
|
239
|
+
return {
|
|
240
|
+
path: entry.path,
|
|
241
|
+
status: entry.status,
|
|
242
|
+
package: c?.package ?? null,
|
|
243
|
+
reason: c?.reason ?? null
|
|
244
|
+
};
|
|
245
|
+
});
|
|
246
|
+
return {
|
|
247
|
+
baseBranch,
|
|
248
|
+
mergeBaseSha,
|
|
249
|
+
files,
|
|
250
|
+
packagesAffected: Array.from(new Set(files.map((f) => f.package).filter((p) => p !== null))).sort(),
|
|
251
|
+
unmappedFiles: files.filter((f) => f.package === null).map((f) => f.path)
|
|
252
|
+
};
|
|
253
|
+
});
|
|
254
|
+
return { analyzeBranch };
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Live layer for {@link BranchAnalyzer}.
|
|
258
|
+
*
|
|
259
|
+
* Requires {@link ConfigInspector} (which in turn requires
|
|
260
|
+
* `ChangesetConfigReader` and `WorkspaceDiscovery`).
|
|
261
|
+
*
|
|
262
|
+
* @public
|
|
263
|
+
*/
|
|
264
|
+
const BranchAnalyzerLive = Layer.effect(BranchAnalyzer, Effect.gen(function* () {
|
|
265
|
+
return makeShape(yield* ConfigInspector);
|
|
266
|
+
}));
|
|
267
|
+
/**
|
|
268
|
+
* Test factory — build a {@link BranchAnalyzer} that returns a fixed
|
|
269
|
+
* {@link BranchAnalysis} for any input.
|
|
270
|
+
*
|
|
271
|
+
* @public
|
|
272
|
+
*/
|
|
273
|
+
function makeBranchAnalyzerTest(fixed) {
|
|
274
|
+
return Layer.succeed(BranchAnalyzer, { analyzeBranch: () => Effect.succeed(fixed) });
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
//#endregion
|
|
278
|
+
export { BranchAnalyzer, BranchAnalyzerBase, BranchAnalyzerLive, makeBranchAnalyzerTest };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/changesets/services/changelog.ts
|
|
4
|
+
const _tag = Context.Tag("ChangelogService");
|
|
5
|
+
/**
|
|
6
|
+
* Base class for ChangelogService.
|
|
7
|
+
*
|
|
8
|
+
* @privateRemarks
|
|
9
|
+
* This export is required for api-extractor documentation generation.
|
|
10
|
+
* Effect's Context.Tag creates an anonymous base class that must be
|
|
11
|
+
* explicitly exported to avoid "forgotten export" warnings. Do not delete.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
const ChangelogServiceBase = _tag();
|
|
16
|
+
/**
|
|
17
|
+
* Effect service tag for changelog formatting.
|
|
18
|
+
*
|
|
19
|
+
* Provides dependency-injected access to the two Changesets API formatter
|
|
20
|
+
* functions: `formatReleaseLine` and `formatDependencyReleaseLine`.
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* This is an abstract service tag — it has no default `Layer`. The concrete
|
|
24
|
+
* implementation is the `\@savvy-web/changesets/changelog` subpath export,
|
|
25
|
+
* which wires the formatting logic through `Effect.runPromise` for the
|
|
26
|
+
* Changesets CLI. For direct Effect usage, build your own layer or use
|
|
27
|
+
* the class-based `Changelog` wrapper.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* import { Effect } from "effect";
|
|
32
|
+
* import type { ChangesetOptions } from "\@savvy-web/changesets";
|
|
33
|
+
* import { ChangelogService, GitHubLive, MarkdownLive } from "\@savvy-web/changesets";
|
|
34
|
+
*
|
|
35
|
+
* const program = Effect.gen(function* () {
|
|
36
|
+
* const changelog = yield* ChangelogService;
|
|
37
|
+
* const line = yield* changelog.formatReleaseLine(changeset, "minor", options);
|
|
38
|
+
* return line;
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @see {@link ChangelogServiceShape} for the service interface
|
|
43
|
+
* @see {@link ChangelogServiceBase} for the api-extractor base class
|
|
44
|
+
*
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
var ChangelogService = class extends ChangelogServiceBase {};
|
|
48
|
+
|
|
49
|
+
//#endregion
|
|
50
|
+
export { ChangelogService, ChangelogServiceBase };
|