@savvy-web/changesets 0.3.0 → 0.4.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 +3 -1
- package/cjs/changelog.cjs +2 -2
- package/cjs/index.cjs +19 -12
- package/cjs/markdownlint.cjs +14 -7
- package/cjs/remark.cjs +14 -7
- package/esm/160.js +2 -2
- package/esm/260.js +8 -0
- package/esm/622.js +8 -7
- package/esm/bin/savvy-changesets.js +39 -13
- package/esm/index.js +3 -3
- package/esm/markdownlint.js +8 -7
- package/package.json +97 -97
package/README.md
CHANGED
|
@@ -14,11 +14,12 @@ Custom changelog formatter and markdown processing pipeline for the Silk Suite.
|
|
|
14
14
|
- **GitHub integration** -- Automatic PR links, commit references, and contributor attribution
|
|
15
15
|
- **Version file syncing** -- Bump version fields in additional JSON files using glob patterns and JSONPath expressions
|
|
16
16
|
- **Editor support** -- markdownlint rules for real-time validation in VS Code and CI
|
|
17
|
+
- **AI-agent-friendly errors** -- All lint and validation errors include inline fix instructions and documentation links, so AI agents can resolve issues without examining source code
|
|
17
18
|
|
|
18
19
|
## Installation
|
|
19
20
|
|
|
20
21
|
```bash
|
|
21
|
-
|
|
22
|
+
npm install @savvy-web/changesets -D
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
## Quick Start
|
|
@@ -64,6 +65,7 @@ Added a new authentication system with OAuth2 support.
|
|
|
64
65
|
- [CLI Reference](./docs/cli.md) -- All commands and options
|
|
65
66
|
- [API Reference](./docs/api.md) -- Classes, types, Effect services, remark plugins
|
|
66
67
|
- [Architecture](./docs/architecture.md) -- Three-layer pipeline design and export map
|
|
68
|
+
- [Markdownlint Rule Docs](./docs/rules/) -- Per-rule documentation with examples, fix instructions, and rationale
|
|
67
69
|
|
|
68
70
|
## License
|
|
69
71
|
|
package/cjs/changelog.cjs
CHANGED
|
@@ -181,7 +181,7 @@ var __webpack_modules__ = {
|
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
const UsernameSchema = effect__rspack_import_0.Schema.String.pipe(effect__rspack_import_0.Schema.pattern(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/, {
|
|
184
|
-
message: ()=>
|
|
184
|
+
message: ()=>'Invalid GitHub username format. Usernames must contain only alphanumeric characters and hyphens, and cannot start or end with a hyphen. Example: "octocat" or "my-user-123"'
|
|
185
185
|
}));
|
|
186
186
|
const IssueNumberSchema = _primitives_js__rspack_import_1.e.annotations({
|
|
187
187
|
title: "IssueNumber",
|
|
@@ -192,7 +192,7 @@ var __webpack_modules__ = {
|
|
|
192
192
|
const match = _utils_markdown_link_js__rspack_import_2.o.exec(value);
|
|
193
193
|
return match?.[2] ? isValidUrl(match[2]) : false;
|
|
194
194
|
}, {
|
|
195
|
-
message: ()=>
|
|
195
|
+
message: ()=>'Value must be a valid URL or a markdown link. Expected a plain URL (e.g., "https://github.com/owner/repo/pull/42") or a markdown link (e.g., "[#42](https://github.com/owner/repo/pull/42)")'
|
|
196
196
|
}));
|
|
197
197
|
const GitHubInfoSchema = effect__rspack_import_0.Schema.Struct({
|
|
198
198
|
user: effect__rspack_import_0.Schema.optional(UsernameSchema),
|
package/cjs/index.cjs
CHANGED
|
@@ -490,7 +490,7 @@ var __webpack_modules__ = {
|
|
|
490
490
|
}
|
|
491
491
|
}
|
|
492
492
|
const UsernameSchema = effect__rspack_import_0.Schema.String.pipe(effect__rspack_import_0.Schema.pattern(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/, {
|
|
493
|
-
message: ()=>
|
|
493
|
+
message: ()=>'Invalid GitHub username format. Usernames must contain only alphanumeric characters and hyphens, and cannot start or end with a hyphen. Example: "octocat" or "my-user-123"'
|
|
494
494
|
}));
|
|
495
495
|
const IssueNumberSchema = _primitives_js__rspack_import_1.e.annotations({
|
|
496
496
|
title: "IssueNumber",
|
|
@@ -501,7 +501,7 @@ var __webpack_modules__ = {
|
|
|
501
501
|
const match = _utils_markdown_link_js__rspack_import_2.o.exec(value);
|
|
502
502
|
return match?.[2] ? isValidUrl(match[2]) : false;
|
|
503
503
|
}, {
|
|
504
|
-
message: ()=>
|
|
504
|
+
message: ()=>'Value must be a valid URL or a markdown link. Expected a plain URL (e.g., "https://github.com/owner/repo/pull/42") or a markdown link (e.g., "[#42](https://github.com/owner/repo/pull/42)")'
|
|
505
505
|
}));
|
|
506
506
|
const GitHubInfoSchema = effect__rspack_import_0.Schema.Struct({
|
|
507
507
|
user: effect__rspack_import_0.Schema.optional(UsernameSchema),
|
|
@@ -830,25 +830,32 @@ var __webpack_exports__ = {};
|
|
|
830
830
|
var external_mdast_util_to_string_ = __webpack_require__("mdast-util-to-string");
|
|
831
831
|
const external_unified_lint_rule_namespaceObject = require("unified-lint-rule");
|
|
832
832
|
const external_unist_util_visit_namespaceObject = require("unist-util-visit");
|
|
833
|
+
const DOCS_BASE = "https://github.com/savvy-web/changesets/blob/main/docs/rules";
|
|
834
|
+
const RULE_DOCS = {
|
|
835
|
+
CSH001: `${DOCS_BASE}/CSH001.md`,
|
|
836
|
+
CSH002: `${DOCS_BASE}/CSH002.md`,
|
|
837
|
+
CSH003: `${DOCS_BASE}/CSH003.md`,
|
|
838
|
+
CSH004: `${DOCS_BASE}/CSH004.md`
|
|
839
|
+
};
|
|
833
840
|
const ContentStructureRule = (0, external_unified_lint_rule_namespaceObject.lintRule)("remark-lint:changeset-content-structure", (tree, file)=>{
|
|
834
841
|
(0, external_unist_util_visit_namespaceObject.visit)(tree, "heading", (node, index, parent)=>{
|
|
835
842
|
if (2 !== node.depth || null == parent || null == index) return;
|
|
836
843
|
const next = parent.children[index + 1];
|
|
837
|
-
if (!next || "heading" === next.type && 2 === next.depth) file.message(
|
|
844
|
+
if (!next || "heading" === next.type && 2 === next.depth) file.message(`Empty section: heading has no content before the next section or end of file. Add a list of changes (e.g., "- Added feature X") under this heading, or remove the empty heading. See: ${RULE_DOCS.CSH003}`, node);
|
|
838
845
|
});
|
|
839
846
|
(0, external_unist_util_visit_namespaceObject.visit)(tree, "code", (node)=>{
|
|
840
|
-
if (!node.lang) file.message(
|
|
847
|
+
if (!node.lang) file.message(`Code block is missing a language identifier. Add a language after the opening fence (e.g., \`\`\`ts, \`\`\`json, \`\`\`bash). See: ${RULE_DOCS.CSH003}`, node);
|
|
841
848
|
});
|
|
842
849
|
(0, external_unist_util_visit_namespaceObject.visit)(tree, "listItem", (node)=>{
|
|
843
850
|
const text = (0, external_mdast_util_to_string_.toString)(node).trim();
|
|
844
|
-
if (!text) file.message(
|
|
851
|
+
if (!text) file.message(`Empty list item. Each list item must contain descriptive text (e.g., "- Fixed login timeout issue"). See: ${RULE_DOCS.CSH003}`, node);
|
|
845
852
|
});
|
|
846
853
|
});
|
|
847
854
|
const HeadingHierarchyRule = (0, external_unified_lint_rule_namespaceObject.lintRule)("remark-lint:changeset-heading-hierarchy", (tree, file)=>{
|
|
848
855
|
let prevDepth = 0;
|
|
849
856
|
(0, external_unist_util_visit_namespaceObject.visit)(tree, "heading", (node)=>{
|
|
850
|
-
if (1 === node.depth) return void file.message(
|
|
851
|
-
if (prevDepth > 0 && node.depth > prevDepth + 1) file.message(`Heading level skipped: expected h${prevDepth + 1} or lower, found h${node.depth}`, node);
|
|
857
|
+
if (1 === node.depth) return void file.message(`h1 headings are not allowed in changeset files. Use h2 (##) for top-level sections like "## Features" or "## Bug Fixes". h1 is reserved for the version title generated by the changelog formatter. See: ${RULE_DOCS.CSH001}`, node);
|
|
858
|
+
if (prevDepth > 0 && node.depth > prevDepth + 1) file.message(`Heading level skipped: expected h${prevDepth + 1} or lower, found h${node.depth}. Headings must increase sequentially (h2 → h3 → h4). Add the missing intermediate level or reduce this heading's depth. See: ${RULE_DOCS.CSH001}`, node);
|
|
852
859
|
prevDepth = node.depth;
|
|
853
860
|
});
|
|
854
861
|
});
|
|
@@ -856,7 +863,7 @@ var __webpack_exports__ = {};
|
|
|
856
863
|
(0, external_unist_util_visit_namespaceObject.visit)(tree, "heading", (node)=>{
|
|
857
864
|
if (2 !== node.depth) return;
|
|
858
865
|
const text = (0, external_mdast_util_to_string_.toString)(node);
|
|
859
|
-
if (!(0, categories.Lr)(text)) file.message(`Unknown section "${text}". Valid
|
|
866
|
+
if (!(0, categories.Lr)(text)) file.message(`Unknown section "${text}". Valid h2 headings are: ${(0, categories.Rr)().join(", ")}. Heading comparison is case-insensitive. See: ${RULE_DOCS.CSH002}`, node);
|
|
860
867
|
});
|
|
861
868
|
});
|
|
862
869
|
const IGNORED_TYPES = new Set([
|
|
@@ -869,7 +876,7 @@ var __webpack_exports__ = {};
|
|
|
869
876
|
const UncategorizedContentRule = (0, external_unified_lint_rule_namespaceObject.lintRule)("remark-lint:changeset-uncategorized-content", (tree, file)=>{
|
|
870
877
|
for (const node of tree.children){
|
|
871
878
|
if ("heading" === node.type && 2 === node.depth) break;
|
|
872
|
-
if (isContentNode(node)) file.message(
|
|
879
|
+
if (isContentNode(node)) file.message(`Content must be placed under a category heading (## heading). Move this content under an appropriate section like "## Features" or "## Bug Fixes". If it doesn't fit an existing category, use "## Other". See: ${RULE_DOCS.CSH004}`, node);
|
|
873
880
|
}
|
|
874
881
|
});
|
|
875
882
|
function stripFrontmatter(content) {
|
|
@@ -1228,14 +1235,14 @@ var __webpack_exports__ = {};
|
|
|
1228
1235
|
description: external_effect_.Schema.String
|
|
1229
1236
|
});
|
|
1230
1237
|
const CommitHashSchema = external_effect_.Schema.String.pipe(external_effect_.Schema.pattern(/^[a-f0-9]{7,}$/, {
|
|
1231
|
-
message: ()=>
|
|
1238
|
+
message: ()=>'Commit hash must be 7 or more lowercase hexadecimal characters (0-9, a-f). Example: "a1b2c3d" or a full 40-character SHA like "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2". Uppercase letters are not allowed'
|
|
1232
1239
|
}));
|
|
1233
1240
|
const VersionTypeSchema = external_effect_.Schema.Literal("major", "minor", "patch", "none");
|
|
1234
1241
|
var primitives = __webpack_require__("./src/schemas/primitives.ts");
|
|
1235
1242
|
const ChangesetSummarySchema = external_effect_.Schema.String.pipe(external_effect_.Schema.minLength(1, {
|
|
1236
|
-
message: ()=>
|
|
1243
|
+
message: ()=>'Changeset summary cannot be empty. Provide a 1-1000 character description of the change (e.g., "Fix authentication timeout in login flow")'
|
|
1237
1244
|
}), external_effect_.Schema.maxLength(1000, {
|
|
1238
|
-
message: ()=>"Changeset summary
|
|
1245
|
+
message: ()=>"Changeset summary exceeds the 1000 character limit. Shorten the summary to at most 1000 characters — use the changeset body for additional details"
|
|
1239
1246
|
}));
|
|
1240
1247
|
const ChangesetSchema = external_effect_.Schema.Struct({
|
|
1241
1248
|
summary: ChangesetSummarySchema,
|
package/cjs/markdownlint.cjs
CHANGED
|
@@ -30,6 +30,13 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
30
30
|
default: ()=>markdownlint,
|
|
31
31
|
RequiredSectionsRule: ()=>RequiredSectionsRule
|
|
32
32
|
});
|
|
33
|
+
const DOCS_BASE = "https://github.com/savvy-web/changesets/blob/main/docs/rules";
|
|
34
|
+
const RULE_DOCS = {
|
|
35
|
+
CSH001: `${DOCS_BASE}/CSH001.md`,
|
|
36
|
+
CSH002: `${DOCS_BASE}/CSH002.md`,
|
|
37
|
+
CSH003: `${DOCS_BASE}/CSH003.md`,
|
|
38
|
+
CSH004: `${DOCS_BASE}/CSH004.md`
|
|
39
|
+
};
|
|
33
40
|
function getHeadingLevel(heading) {
|
|
34
41
|
const sequence = heading.children.find((c)=>"atxHeadingSequence" === c.type);
|
|
35
42
|
return sequence ? sequence.text.length : 0;
|
|
@@ -64,7 +71,7 @@ const ContentStructureRule = {
|
|
|
64
71
|
const nextIdx = i + 1 < h2Indices.length ? h2Indices[i + 1] : tokens.length;
|
|
65
72
|
if (!hasContentBetween(tokens, currentIdx, nextIdx)) onError({
|
|
66
73
|
lineNumber: tokens[currentIdx].startLine,
|
|
67
|
-
detail:
|
|
74
|
+
detail: `Empty section: heading has no content before the next section or end of file. Add a list of changes (e.g., "- Added feature X") under this heading, or remove the empty heading. See: ${RULE_DOCS.CSH003}`
|
|
68
75
|
});
|
|
69
76
|
}
|
|
70
77
|
for (const token of tokens){
|
|
@@ -73,7 +80,7 @@ const ContentStructureRule = {
|
|
|
73
80
|
const hasInfo = openingFence?.children.some((c)=>"codeFencedFenceInfo" === c.type) ?? false;
|
|
74
81
|
if (!hasInfo) onError({
|
|
75
82
|
lineNumber: token.startLine,
|
|
76
|
-
detail:
|
|
83
|
+
detail: `Code block is missing a language identifier. Add a language after the opening fence (e.g., \`\`\`ts, \`\`\`json, \`\`\`bash). See: ${RULE_DOCS.CSH003}`
|
|
77
84
|
});
|
|
78
85
|
}
|
|
79
86
|
for (const token of tokens){
|
|
@@ -91,7 +98,7 @@ const ContentStructureRule = {
|
|
|
91
98
|
}
|
|
92
99
|
if (!hasContent) onError({
|
|
93
100
|
lineNumber: children[i].startLine,
|
|
94
|
-
detail:
|
|
101
|
+
detail: `Empty list item. Each list item must contain descriptive text (e.g., "- Fixed login timeout issue"). See: ${RULE_DOCS.CSH003}`
|
|
95
102
|
});
|
|
96
103
|
}
|
|
97
104
|
}
|
|
@@ -115,13 +122,13 @@ const HeadingHierarchyRule = {
|
|
|
115
122
|
if (1 === depth) {
|
|
116
123
|
onError({
|
|
117
124
|
lineNumber: token.startLine,
|
|
118
|
-
detail:
|
|
125
|
+
detail: `h1 headings are not allowed in changeset files. Use h2 (##) for top-level sections like "## Features" or "## Bug Fixes". h1 is reserved for the version title generated by the changelog formatter. See: ${RULE_DOCS.CSH001}`
|
|
119
126
|
});
|
|
120
127
|
continue;
|
|
121
128
|
}
|
|
122
129
|
if (prevDepth > 0 && depth > prevDepth + 1) onError({
|
|
123
130
|
lineNumber: token.startLine,
|
|
124
|
-
detail: `Heading level skipped: expected h${prevDepth + 1} or lower, found h${depth}`
|
|
131
|
+
detail: `Heading level skipped: expected h${prevDepth + 1} or lower, found h${depth}. Headings must increase sequentially (h2 → h3 → h4). Add the missing intermediate level or reduce this heading's depth. See: ${RULE_DOCS.CSH001}`
|
|
125
132
|
});
|
|
126
133
|
prevDepth = depth;
|
|
127
134
|
}
|
|
@@ -272,7 +279,7 @@ const RequiredSectionsRule = {
|
|
|
272
279
|
const text = getHeadingText(token);
|
|
273
280
|
if (!isValidHeading(text)) onError({
|
|
274
281
|
lineNumber: token.startLine,
|
|
275
|
-
detail: `Unknown section "${text}". Valid
|
|
282
|
+
detail: `Unknown section "${text}". Valid h2 headings are: ${allHeadings().join(", ")}. Heading comparison is case-insensitive. See: ${RULE_DOCS.CSH002}`
|
|
276
283
|
});
|
|
277
284
|
}
|
|
278
285
|
}
|
|
@@ -294,7 +301,7 @@ const UncategorizedContentRule = {
|
|
|
294
301
|
if ("lineEnding" !== token.type && "lineEndingBlank" !== token.type && "htmlFlow" !== token.type) {
|
|
295
302
|
if ("atxHeading" !== token.type) onError({
|
|
296
303
|
lineNumber: token.startLine,
|
|
297
|
-
detail:
|
|
304
|
+
detail: `Content must be placed under a category heading (## heading). Move this content under an appropriate section like "## Features" or "## Bug Fixes". If it doesn't fit an existing category, use "## Other". See: ${RULE_DOCS.CSH004}`
|
|
298
305
|
});
|
|
299
306
|
}
|
|
300
307
|
}
|
package/cjs/remark.cjs
CHANGED
|
@@ -474,25 +474,32 @@ const ReorderSectionsPlugin = ()=>(tree)=>{
|
|
|
474
474
|
}
|
|
475
475
|
};
|
|
476
476
|
const external_unified_lint_rule_namespaceObject = require("unified-lint-rule");
|
|
477
|
+
const DOCS_BASE = "https://github.com/savvy-web/changesets/blob/main/docs/rules";
|
|
478
|
+
const RULE_DOCS = {
|
|
479
|
+
CSH001: `${DOCS_BASE}/CSH001.md`,
|
|
480
|
+
CSH002: `${DOCS_BASE}/CSH002.md`,
|
|
481
|
+
CSH003: `${DOCS_BASE}/CSH003.md`,
|
|
482
|
+
CSH004: `${DOCS_BASE}/CSH004.md`
|
|
483
|
+
};
|
|
477
484
|
const ContentStructureRule = (0, external_unified_lint_rule_namespaceObject.lintRule)("remark-lint:changeset-content-structure", (tree, file)=>{
|
|
478
485
|
(0, external_unist_util_visit_namespaceObject.visit)(tree, "heading", (node, index, parent)=>{
|
|
479
486
|
if (2 !== node.depth || null == parent || null == index) return;
|
|
480
487
|
const next = parent.children[index + 1];
|
|
481
|
-
if (!next || "heading" === next.type && 2 === next.depth) file.message(
|
|
488
|
+
if (!next || "heading" === next.type && 2 === next.depth) file.message(`Empty section: heading has no content before the next section or end of file. Add a list of changes (e.g., "- Added feature X") under this heading, or remove the empty heading. See: ${RULE_DOCS.CSH003}`, node);
|
|
482
489
|
});
|
|
483
490
|
(0, external_unist_util_visit_namespaceObject.visit)(tree, "code", (node)=>{
|
|
484
|
-
if (!node.lang) file.message(
|
|
491
|
+
if (!node.lang) file.message(`Code block is missing a language identifier. Add a language after the opening fence (e.g., \`\`\`ts, \`\`\`json, \`\`\`bash). See: ${RULE_DOCS.CSH003}`, node);
|
|
485
492
|
});
|
|
486
493
|
(0, external_unist_util_visit_namespaceObject.visit)(tree, "listItem", (node)=>{
|
|
487
494
|
const text = (0, external_mdast_util_to_string_namespaceObject.toString)(node).trim();
|
|
488
|
-
if (!text) file.message(
|
|
495
|
+
if (!text) file.message(`Empty list item. Each list item must contain descriptive text (e.g., "- Fixed login timeout issue"). See: ${RULE_DOCS.CSH003}`, node);
|
|
489
496
|
});
|
|
490
497
|
});
|
|
491
498
|
const HeadingHierarchyRule = (0, external_unified_lint_rule_namespaceObject.lintRule)("remark-lint:changeset-heading-hierarchy", (tree, file)=>{
|
|
492
499
|
let prevDepth = 0;
|
|
493
500
|
(0, external_unist_util_visit_namespaceObject.visit)(tree, "heading", (node)=>{
|
|
494
|
-
if (1 === node.depth) return void file.message(
|
|
495
|
-
if (prevDepth > 0 && node.depth > prevDepth + 1) file.message(`Heading level skipped: expected h${prevDepth + 1} or lower, found h${node.depth}`, node);
|
|
501
|
+
if (1 === node.depth) return void file.message(`h1 headings are not allowed in changeset files. Use h2 (##) for top-level sections like "## Features" or "## Bug Fixes". h1 is reserved for the version title generated by the changelog formatter. See: ${RULE_DOCS.CSH001}`, node);
|
|
502
|
+
if (prevDepth > 0 && node.depth > prevDepth + 1) file.message(`Heading level skipped: expected h${prevDepth + 1} or lower, found h${node.depth}. Headings must increase sequentially (h2 → h3 → h4). Add the missing intermediate level or reduce this heading's depth. See: ${RULE_DOCS.CSH001}`, node);
|
|
496
503
|
prevDepth = node.depth;
|
|
497
504
|
});
|
|
498
505
|
});
|
|
@@ -500,7 +507,7 @@ const RequiredSectionsRule = (0, external_unified_lint_rule_namespaceObject.lint
|
|
|
500
507
|
(0, external_unist_util_visit_namespaceObject.visit)(tree, "heading", (node)=>{
|
|
501
508
|
if (2 !== node.depth) return;
|
|
502
509
|
const text = (0, external_mdast_util_to_string_namespaceObject.toString)(node);
|
|
503
|
-
if (!isValidHeading(text)) file.message(`Unknown section "${text}". Valid
|
|
510
|
+
if (!isValidHeading(text)) file.message(`Unknown section "${text}". Valid h2 headings are: ${allHeadings().join(", ")}. Heading comparison is case-insensitive. See: ${RULE_DOCS.CSH002}`, node);
|
|
504
511
|
});
|
|
505
512
|
});
|
|
506
513
|
const IGNORED_TYPES = new Set([
|
|
@@ -513,7 +520,7 @@ function isContentNode(node) {
|
|
|
513
520
|
const UncategorizedContentRule = (0, external_unified_lint_rule_namespaceObject.lintRule)("remark-lint:changeset-uncategorized-content", (tree, file)=>{
|
|
514
521
|
for (const node of tree.children){
|
|
515
522
|
if ("heading" === node.type && 2 === node.depth) break;
|
|
516
|
-
if (isContentNode(node)) file.message(
|
|
523
|
+
if (isContentNode(node)) file.message(`Content must be placed under a category heading (## heading). Move this content under an appropriate section like "## Features" or "## Bug Fixes". If it doesn't fit an existing category, use "## Other". See: ${RULE_DOCS.CSH004}`, node);
|
|
517
524
|
}
|
|
518
525
|
});
|
|
519
526
|
const SilkChangesetPreset = [
|
package/esm/160.js
CHANGED
|
@@ -143,7 +143,7 @@ function isValidUrl(value) {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
const UsernameSchema = Schema.String.pipe(Schema.pattern(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/, {
|
|
146
|
-
message: ()=>
|
|
146
|
+
message: ()=>'Invalid GitHub username format. Usernames must contain only alphanumeric characters and hyphens, and cannot start or end with a hyphen. Example: "octocat" or "my-user-123"'
|
|
147
147
|
}));
|
|
148
148
|
const IssueNumberSchema = PositiveInteger.annotations({
|
|
149
149
|
title: "IssueNumber",
|
|
@@ -154,7 +154,7 @@ const UrlOrMarkdownLinkSchema = Schema.String.pipe(Schema.filter((value)=>{
|
|
|
154
154
|
const match = MARKDOWN_LINK_PATTERN.exec(value);
|
|
155
155
|
return match?.[2] ? isValidUrl(match[2]) : false;
|
|
156
156
|
}, {
|
|
157
|
-
message: ()=>
|
|
157
|
+
message: ()=>'Value must be a valid URL or a markdown link. Expected a plain URL (e.g., "https://github.com/owner/repo/pull/42") or a markdown link (e.g., "[#42](https://github.com/owner/repo/pull/42)")'
|
|
158
158
|
}));
|
|
159
159
|
const GitHubInfoSchema = Schema.Struct({
|
|
160
160
|
user: Schema.optional(UsernameSchema),
|
package/esm/260.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const DOCS_BASE = "https://github.com/savvy-web/changesets/blob/main/docs/rules";
|
|
2
|
+
const RULE_DOCS = {
|
|
3
|
+
CSH001: `${DOCS_BASE}/CSH001.md`,
|
|
4
|
+
CSH002: `${DOCS_BASE}/CSH002.md`,
|
|
5
|
+
CSH003: `${DOCS_BASE}/CSH003.md`,
|
|
6
|
+
CSH004: `${DOCS_BASE}/CSH004.md`
|
|
7
|
+
};
|
|
8
|
+
export { RULE_DOCS };
|
package/esm/622.js
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import { lintRule } from "unified-lint-rule";
|
|
2
2
|
import { SKIP, visit } from "unist-util-visit";
|
|
3
3
|
import { external_mdast_util_to_string_toString } from "./689.js";
|
|
4
|
+
import { RULE_DOCS } from "./260.js";
|
|
4
5
|
import { isValidHeading, fromHeading, allHeadings } from "./60.js";
|
|
5
6
|
const ContentStructureRule = lintRule("remark-lint:changeset-content-structure", (tree, file)=>{
|
|
6
7
|
visit(tree, "heading", (node, index, parent)=>{
|
|
7
8
|
if (2 !== node.depth || null == parent || null == index) return;
|
|
8
9
|
const next = parent.children[index + 1];
|
|
9
|
-
if (!next || "heading" === next.type && 2 === next.depth) file.message(
|
|
10
|
+
if (!next || "heading" === next.type && 2 === next.depth) file.message(`Empty section: heading has no content before the next section or end of file. Add a list of changes (e.g., "- Added feature X") under this heading, or remove the empty heading. See: ${RULE_DOCS.CSH003}`, node);
|
|
10
11
|
});
|
|
11
12
|
visit(tree, "code", (node)=>{
|
|
12
|
-
if (!node.lang) file.message(
|
|
13
|
+
if (!node.lang) file.message(`Code block is missing a language identifier. Add a language after the opening fence (e.g., \`\`\`ts, \`\`\`json, \`\`\`bash). See: ${RULE_DOCS.CSH003}`, node);
|
|
13
14
|
});
|
|
14
15
|
visit(tree, "listItem", (node)=>{
|
|
15
16
|
const text = external_mdast_util_to_string_toString(node).trim();
|
|
16
|
-
if (!text) file.message(
|
|
17
|
+
if (!text) file.message(`Empty list item. Each list item must contain descriptive text (e.g., "- Fixed login timeout issue"). See: ${RULE_DOCS.CSH003}`, node);
|
|
17
18
|
});
|
|
18
19
|
});
|
|
19
20
|
const HeadingHierarchyRule = lintRule("remark-lint:changeset-heading-hierarchy", (tree, file)=>{
|
|
20
21
|
let prevDepth = 0;
|
|
21
22
|
visit(tree, "heading", (node)=>{
|
|
22
|
-
if (1 === node.depth) return void file.message(
|
|
23
|
-
if (prevDepth > 0 && node.depth > prevDepth + 1) file.message(`Heading level skipped: expected h${prevDepth + 1} or lower, found h${node.depth}`, node);
|
|
23
|
+
if (1 === node.depth) return void file.message(`h1 headings are not allowed in changeset files. Use h2 (##) for top-level sections like "## Features" or "## Bug Fixes". h1 is reserved for the version title generated by the changelog formatter. See: ${RULE_DOCS.CSH001}`, node);
|
|
24
|
+
if (prevDepth > 0 && node.depth > prevDepth + 1) file.message(`Heading level skipped: expected h${prevDepth + 1} or lower, found h${node.depth}. Headings must increase sequentially (h2 → h3 → h4). Add the missing intermediate level or reduce this heading's depth. See: ${RULE_DOCS.CSH001}`, node);
|
|
24
25
|
prevDepth = node.depth;
|
|
25
26
|
});
|
|
26
27
|
});
|
|
@@ -28,7 +29,7 @@ const RequiredSectionsRule = lintRule("remark-lint:changeset-required-sections",
|
|
|
28
29
|
visit(tree, "heading", (node)=>{
|
|
29
30
|
if (2 !== node.depth) return;
|
|
30
31
|
const text = external_mdast_util_to_string_toString(node);
|
|
31
|
-
if (!isValidHeading(text)) file.message(`Unknown section "${text}". Valid
|
|
32
|
+
if (!isValidHeading(text)) file.message(`Unknown section "${text}". Valid h2 headings are: ${allHeadings().join(", ")}. Heading comparison is case-insensitive. See: ${RULE_DOCS.CSH002}`, node);
|
|
32
33
|
});
|
|
33
34
|
});
|
|
34
35
|
const IGNORED_TYPES = new Set([
|
|
@@ -41,7 +42,7 @@ function isContentNode(node) {
|
|
|
41
42
|
const UncategorizedContentRule = lintRule("remark-lint:changeset-uncategorized-content", (tree, file)=>{
|
|
42
43
|
for (const node of tree.children){
|
|
43
44
|
if ("heading" === node.type && 2 === node.depth) break;
|
|
44
|
-
if (isContentNode(node)) file.message(
|
|
45
|
+
if (isContentNode(node)) file.message(`Content must be placed under a category heading (## heading). Move this content under an appropriate section like "## Features" or "## Bug Fixes". If it doesn't fit an existing category, use "## Other". See: ${RULE_DOCS.CSH004}`, node);
|
|
45
46
|
}
|
|
46
47
|
});
|
|
47
48
|
function getVersionBlocks(tree) {
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { Args, Command, Options } from "@effect/cli";
|
|
3
3
|
import { NodeContext, NodeRuntime } from "@effect/platform-node";
|
|
4
4
|
import { execSync } from "node:child_process";
|
|
5
|
+
import { applyEdits, modify, parse } from "jsonc-parser";
|
|
5
6
|
import { findProjectRoot, getWorkspaceInfos } from "workspace-tools";
|
|
6
|
-
import { parse } from "jsonc-parser";
|
|
7
7
|
import { globSync } from "tinyglobby";
|
|
8
8
|
import { readFileSync, ChangelogTransformer, ChangesetLinter, resolve, existsSync, relative, mkdirSync, writeFileSync, join } from "../273.js";
|
|
9
9
|
import { VersionFilesSchema, Schema, VersionFileError, Data, Effect } from "../795.js";
|
|
@@ -92,9 +92,10 @@ function detectGitHubRepo(cwd) {
|
|
|
92
92
|
} catch {}
|
|
93
93
|
return null;
|
|
94
94
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
const JSONC_FORMAT = {
|
|
96
|
+
tabSize: 1,
|
|
97
|
+
insertSpaces: false
|
|
98
|
+
};
|
|
98
99
|
function resolveWorkspaceRoot(cwd) {
|
|
99
100
|
return findProjectRoot(cwd) ?? cwd;
|
|
100
101
|
}
|
|
@@ -156,13 +157,38 @@ function handleBaseMarkdownlint(root) {
|
|
|
156
157
|
const foundPath = findMarkdownlintConfig(root);
|
|
157
158
|
if (!foundPath) return `Warning: no markdownlint config found (checked ${MARKDOWNLINT_CONFIG_PATHS.join(", ")})`;
|
|
158
159
|
const fullPath = join(root, foundPath);
|
|
159
|
-
|
|
160
|
-
const parsed =
|
|
161
|
-
if (!Array.isArray(parsed.customRules)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
160
|
+
let text = readFileSync(fullPath, "utf-8");
|
|
161
|
+
const parsed = parse(text);
|
|
162
|
+
if (!Array.isArray(parsed.customRules) || !parsed.customRules.includes(CUSTOM_RULES_ENTRY)) {
|
|
163
|
+
const edits = modify(text, [
|
|
164
|
+
"customRules",
|
|
165
|
+
-1
|
|
166
|
+
], CUSTOM_RULES_ENTRY, {
|
|
167
|
+
formattingOptions: JSONC_FORMAT,
|
|
168
|
+
isArrayInsertion: true
|
|
169
|
+
});
|
|
170
|
+
text = applyEdits(text, edits);
|
|
171
|
+
}
|
|
172
|
+
const currentConfig = parse(text).config;
|
|
173
|
+
if ("object" != typeof currentConfig || null === currentConfig) {
|
|
174
|
+
const edits = modify(text, [
|
|
175
|
+
"config"
|
|
176
|
+
], {}, {
|
|
177
|
+
formattingOptions: JSONC_FORMAT
|
|
178
|
+
});
|
|
179
|
+
text = applyEdits(text, edits);
|
|
180
|
+
}
|
|
181
|
+
const config = parse(text).config;
|
|
182
|
+
for (const rule of RULE_NAMES)if (!(rule in config)) {
|
|
183
|
+
const edits = modify(text, [
|
|
184
|
+
"config",
|
|
185
|
+
rule
|
|
186
|
+
], false, {
|
|
187
|
+
formattingOptions: JSONC_FORMAT
|
|
188
|
+
});
|
|
189
|
+
text = applyEdits(text, edits);
|
|
190
|
+
}
|
|
191
|
+
writeFileSync(fullPath, text);
|
|
166
192
|
return `Updated ${foundPath}`;
|
|
167
193
|
},
|
|
168
194
|
catch: (error)=>new InitError({
|
|
@@ -248,7 +274,7 @@ function checkBaseMarkdownlint(root) {
|
|
|
248
274
|
];
|
|
249
275
|
try {
|
|
250
276
|
const raw = readFileSync(join(root, foundPath), "utf-8");
|
|
251
|
-
const parsed =
|
|
277
|
+
const parsed = parse(raw);
|
|
252
278
|
const issues = [];
|
|
253
279
|
if (!Array.isArray(parsed.customRules) || !parsed.customRules.includes(CUSTOM_RULES_ENTRY)) issues.push({
|
|
254
280
|
file: foundPath,
|
|
@@ -759,7 +785,7 @@ const rootCommand = Command.make("savvy-changesets").pipe(Command.withSubcommand
|
|
|
759
785
|
]));
|
|
760
786
|
const cli = Command.run(rootCommand, {
|
|
761
787
|
name: "savvy-changesets",
|
|
762
|
-
version: "0.
|
|
788
|
+
version: "0.4.0"
|
|
763
789
|
});
|
|
764
790
|
function runCli() {
|
|
765
791
|
const main = Effect.suspend(()=>cli(process.argv)).pipe(Effect.provide(NodeContext.layer));
|
package/esm/index.js
CHANGED
|
@@ -48,13 +48,13 @@ const SectionCategorySchema = Schema.Struct({
|
|
|
48
48
|
description: Schema.String
|
|
49
49
|
});
|
|
50
50
|
const CommitHashSchema = Schema.String.pipe(Schema.pattern(/^[a-f0-9]{7,}$/, {
|
|
51
|
-
message: ()=>
|
|
51
|
+
message: ()=>'Commit hash must be 7 or more lowercase hexadecimal characters (0-9, a-f). Example: "a1b2c3d" or a full 40-character SHA like "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2". Uppercase letters are not allowed'
|
|
52
52
|
}));
|
|
53
53
|
const VersionTypeSchema = Schema.Literal("major", "minor", "patch", "none");
|
|
54
54
|
const ChangesetSummarySchema = Schema.String.pipe(Schema.minLength(1, {
|
|
55
|
-
message: ()=>
|
|
55
|
+
message: ()=>'Changeset summary cannot be empty. Provide a 1-1000 character description of the change (e.g., "Fix authentication timeout in login flow")'
|
|
56
56
|
}), Schema.maxLength(1000, {
|
|
57
|
-
message: ()=>"Changeset summary
|
|
57
|
+
message: ()=>"Changeset summary exceeds the 1000 character limit. Shorten the summary to at most 1000 characters — use the changeset body for additional details"
|
|
58
58
|
}));
|
|
59
59
|
const ChangesetSchema = Schema.Struct({
|
|
60
60
|
summary: ChangesetSummarySchema,
|
package/esm/markdownlint.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RULE_DOCS } from "./260.js";
|
|
1
2
|
import { isValidHeading, allHeadings } from "./60.js";
|
|
2
3
|
function getHeadingLevel(heading) {
|
|
3
4
|
const sequence = heading.children.find((c)=>"atxHeadingSequence" === c.type);
|
|
@@ -33,7 +34,7 @@ const ContentStructureRule = {
|
|
|
33
34
|
const nextIdx = i + 1 < h2Indices.length ? h2Indices[i + 1] : tokens.length;
|
|
34
35
|
if (!hasContentBetween(tokens, currentIdx, nextIdx)) onError({
|
|
35
36
|
lineNumber: tokens[currentIdx].startLine,
|
|
36
|
-
detail:
|
|
37
|
+
detail: `Empty section: heading has no content before the next section or end of file. Add a list of changes (e.g., "- Added feature X") under this heading, or remove the empty heading. See: ${RULE_DOCS.CSH003}`
|
|
37
38
|
});
|
|
38
39
|
}
|
|
39
40
|
for (const token of tokens){
|
|
@@ -42,7 +43,7 @@ const ContentStructureRule = {
|
|
|
42
43
|
const hasInfo = openingFence?.children.some((c)=>"codeFencedFenceInfo" === c.type) ?? false;
|
|
43
44
|
if (!hasInfo) onError({
|
|
44
45
|
lineNumber: token.startLine,
|
|
45
|
-
detail:
|
|
46
|
+
detail: `Code block is missing a language identifier. Add a language after the opening fence (e.g., \`\`\`ts, \`\`\`json, \`\`\`bash). See: ${RULE_DOCS.CSH003}`
|
|
46
47
|
});
|
|
47
48
|
}
|
|
48
49
|
for (const token of tokens){
|
|
@@ -60,7 +61,7 @@ const ContentStructureRule = {
|
|
|
60
61
|
}
|
|
61
62
|
if (!hasContent) onError({
|
|
62
63
|
lineNumber: children[i].startLine,
|
|
63
|
-
detail:
|
|
64
|
+
detail: `Empty list item. Each list item must contain descriptive text (e.g., "- Fixed login timeout issue"). See: ${RULE_DOCS.CSH003}`
|
|
64
65
|
});
|
|
65
66
|
}
|
|
66
67
|
}
|
|
@@ -84,13 +85,13 @@ const HeadingHierarchyRule = {
|
|
|
84
85
|
if (1 === depth) {
|
|
85
86
|
onError({
|
|
86
87
|
lineNumber: token.startLine,
|
|
87
|
-
detail:
|
|
88
|
+
detail: `h1 headings are not allowed in changeset files. Use h2 (##) for top-level sections like "## Features" or "## Bug Fixes". h1 is reserved for the version title generated by the changelog formatter. See: ${RULE_DOCS.CSH001}`
|
|
88
89
|
});
|
|
89
90
|
continue;
|
|
90
91
|
}
|
|
91
92
|
if (prevDepth > 0 && depth > prevDepth + 1) onError({
|
|
92
93
|
lineNumber: token.startLine,
|
|
93
|
-
detail: `Heading level skipped: expected h${prevDepth + 1} or lower, found h${depth}`
|
|
94
|
+
detail: `Heading level skipped: expected h${prevDepth + 1} or lower, found h${depth}. Headings must increase sequentially (h2 → h3 → h4). Add the missing intermediate level or reduce this heading's depth. See: ${RULE_DOCS.CSH001}`
|
|
94
95
|
});
|
|
95
96
|
prevDepth = depth;
|
|
96
97
|
}
|
|
@@ -113,7 +114,7 @@ const RequiredSectionsRule = {
|
|
|
113
114
|
const text = getHeadingText(token);
|
|
114
115
|
if (!isValidHeading(text)) onError({
|
|
115
116
|
lineNumber: token.startLine,
|
|
116
|
-
detail: `Unknown section "${text}". Valid
|
|
117
|
+
detail: `Unknown section "${text}". Valid h2 headings are: ${allHeadings().join(", ")}. Heading comparison is case-insensitive. See: ${RULE_DOCS.CSH002}`
|
|
117
118
|
});
|
|
118
119
|
}
|
|
119
120
|
}
|
|
@@ -135,7 +136,7 @@ const UncategorizedContentRule = {
|
|
|
135
136
|
if ("lineEnding" !== token.type && "lineEndingBlank" !== token.type && "htmlFlow" !== token.type) {
|
|
136
137
|
if ("atxHeading" !== token.type) onError({
|
|
137
138
|
lineNumber: token.startLine,
|
|
138
|
-
detail:
|
|
139
|
+
detail: `Content must be placed under a category heading (## heading). Move this content under an appropriate section like "## Features" or "## Bug Fixes". If it doesn't fit an existing category, use "## Other". See: ${RULE_DOCS.CSH004}`
|
|
139
140
|
});
|
|
140
141
|
}
|
|
141
142
|
}
|
package/package.json
CHANGED
|
@@ -1,98 +1,98 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
2
|
+
"name": "@savvy-web/changesets",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Custom changelog formatter and markdown processing pipeline for the Silk Suite. Provides structured changeset sections, remark-based validation and transformation, and an Effect CLI.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"changesets",
|
|
8
|
+
"changelog",
|
|
9
|
+
"release-notes",
|
|
10
|
+
"remark",
|
|
11
|
+
"markdown",
|
|
12
|
+
"effect",
|
|
13
|
+
"silk-suite"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/savvy-web/changesets#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/savvy-web/changesets/issues"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/savvy-web/changesets.git"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": {
|
|
25
|
+
"name": "C. Spencer Beggs",
|
|
26
|
+
"email": "spencer@savvyweb.systems",
|
|
27
|
+
"url": "https://savvyweb.systems"
|
|
28
|
+
},
|
|
29
|
+
"type": "module",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./esm/index.d.ts",
|
|
33
|
+
"import": "./esm/index.js",
|
|
34
|
+
"require": "./cjs/index.cjs"
|
|
35
|
+
},
|
|
36
|
+
"./changelog": {
|
|
37
|
+
"types": "./esm/changelog.d.ts",
|
|
38
|
+
"import": "./esm/changelog.js",
|
|
39
|
+
"require": "./cjs/changelog.cjs"
|
|
40
|
+
},
|
|
41
|
+
"./markdownlint": {
|
|
42
|
+
"types": "./esm/markdownlint.d.ts",
|
|
43
|
+
"import": "./esm/markdownlint.js",
|
|
44
|
+
"require": "./cjs/markdownlint.cjs"
|
|
45
|
+
},
|
|
46
|
+
"./remark": {
|
|
47
|
+
"types": "./esm/remark.d.ts",
|
|
48
|
+
"import": "./esm/remark.js",
|
|
49
|
+
"require": "./cjs/remark.cjs"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"bin": {
|
|
53
|
+
"savvy-changesets": "./esm/bin/savvy-changesets.js"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@changesets/get-github-info": "^0.8.0",
|
|
57
|
+
"@effect/cli": "^0.73.2",
|
|
58
|
+
"@effect/platform": "^0.94.5",
|
|
59
|
+
"@effect/platform-node": "^0.104.1",
|
|
60
|
+
"effect": "^3.19.19",
|
|
61
|
+
"jsonc-parser": "^3.3.1",
|
|
62
|
+
"mdast-util-heading-range": "^4.0.0",
|
|
63
|
+
"mdast-util-to-string": "^4.0.0",
|
|
64
|
+
"remark-gfm": "^4.0.1",
|
|
65
|
+
"remark-parse": "^11.0.0",
|
|
66
|
+
"remark-stringify": "^11.0.0",
|
|
67
|
+
"tinyglobby": "^0.2.15",
|
|
68
|
+
"unified": "^11.0.5",
|
|
69
|
+
"unified-lint-rule": "^3.0.1",
|
|
70
|
+
"unist-util-visit": "^5.1.0",
|
|
71
|
+
"workspace-tools": "^0.41.0"
|
|
72
|
+
},
|
|
73
|
+
"peerDependencies": {
|
|
74
|
+
"@changesets/cli": "^2.30.0"
|
|
75
|
+
},
|
|
76
|
+
"peerDependenciesMeta": {
|
|
77
|
+
"@changesets/cli": {
|
|
78
|
+
"optional": false
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"engines": {
|
|
82
|
+
"node": ">=24.0.0"
|
|
83
|
+
},
|
|
84
|
+
"scripts": {
|
|
85
|
+
"postinstall": "savvy-changesets init --check"
|
|
86
|
+
},
|
|
87
|
+
"files": [
|
|
88
|
+
"!changesets.api.json",
|
|
89
|
+
"!tsconfig.json",
|
|
90
|
+
"!tsdoc.json",
|
|
91
|
+
"LICENSE",
|
|
92
|
+
"README.md",
|
|
93
|
+
"cjs",
|
|
94
|
+
"esm",
|
|
95
|
+
"package.json",
|
|
96
|
+
"tsdoc-metadata.json"
|
|
97
|
+
]
|
|
98
|
+
}
|