@savvy-web/changesets 0.2.1 → 0.3.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 +14 -58
- package/cjs/index.cjs +14 -1
- package/cjs/index.d.cts +3 -3
- package/cjs/markdownlint.cjs +28 -1
- package/cjs/markdownlint.d.cts +9 -0
- package/cjs/remark.cjs +19 -2
- package/cjs/remark.d.cts +3 -1
- package/esm/273.js +2 -2
- package/esm/{234.js → 622.js} +14 -1
- package/esm/bin/savvy-changesets.js +3 -2
- package/esm/index.d.ts +3 -3
- package/esm/markdownlint.d.ts +9 -0
- package/esm/markdownlint.js +26 -2
- package/esm/remark.d.ts +3 -1
- package/esm/remark.js +4 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,19 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
[![npm version][npm-badge]][npm-url]
|
|
4
4
|
[![License: MIT][license-badge]][license-url]
|
|
5
|
+
[![Node.js >= 24][node-badge]][node-url]
|
|
5
6
|
|
|
6
7
|
Custom changelog formatter and markdown processing pipeline for the Silk Suite. Replaces the default `@changesets/cli/changelog` formatter with a three-layer architecture that validates changeset files, formats structured changelog entries, and post-processes the generated CHANGELOG.md.
|
|
7
8
|
|
|
8
9
|
## Features
|
|
9
10
|
|
|
10
|
-
- **Section-aware changesets** --
|
|
11
|
+
- **Section-aware changesets** -- Categorize changes with h2 headings (Features, Bug Fixes, Breaking Changes, etc.)
|
|
11
12
|
- **Three-layer pipeline** -- Pre-validation (remark-lint), changelog formatting (Changesets API), and post-processing (remark-transform)
|
|
12
|
-
- **
|
|
13
|
-
- **CLI tooling** -- `savvy-changesets` binary with init, lint, transform, check, and version subcommands for CI and local use
|
|
13
|
+
- **CLI tooling** -- `savvy-changesets` binary with init, lint, check, transform, and version subcommands
|
|
14
14
|
- **GitHub integration** -- Automatic PR links, commit references, and contributor attribution
|
|
15
|
-
- **Version file syncing** -- Bump version fields in additional JSON files
|
|
16
|
-
- **
|
|
17
|
-
- **markdownlint rules** -- Custom rules compatible with [markdownlint-cli2](https://www.npmjs.com/package/markdownlint-cli2) and the VS Code extension via `@savvy-web/changesets/markdownlint`
|
|
15
|
+
- **Version file syncing** -- Bump version fields in additional JSON files using glob patterns and JSONPath expressions
|
|
16
|
+
- **Editor support** -- markdownlint rules for real-time validation in VS Code and CI
|
|
18
17
|
|
|
19
18
|
## Installation
|
|
20
19
|
|
|
@@ -30,7 +29,7 @@ Bootstrap your repository:
|
|
|
30
29
|
savvy-changesets init
|
|
31
30
|
```
|
|
32
31
|
|
|
33
|
-
This creates `.changeset/config.json` with auto-detected GitHub repo settings. Or configure manually:
|
|
32
|
+
This creates `.changeset/config.json` with auto-detected GitHub repo settings and configures markdownlint rules. Or configure manually:
|
|
34
33
|
|
|
35
34
|
```json
|
|
36
35
|
{
|
|
@@ -41,7 +40,7 @@ This creates `.changeset/config.json` with auto-detected GitHub repo settings. O
|
|
|
41
40
|
}
|
|
42
41
|
```
|
|
43
42
|
|
|
44
|
-
Write
|
|
43
|
+
Write section-aware changeset files:
|
|
45
44
|
|
|
46
45
|
```markdown
|
|
47
46
|
---
|
|
@@ -58,58 +57,13 @@ Added a new authentication system with OAuth2 support.
|
|
|
58
57
|
- Updated integration test fixtures
|
|
59
58
|
```
|
|
60
59
|
|
|
61
|
-
## Version File Syncing
|
|
62
|
-
|
|
63
|
-
If your project has JSON files beyond `package.json` that contain version fields (e.g., `plugin.json`, `marketplace.json`), add `versionFiles` to your changelog options to keep them in sync during `changeset version`:
|
|
64
|
-
|
|
65
|
-
```json
|
|
66
|
-
{
|
|
67
|
-
"changelog": ["@savvy-web/changesets/changelog", {
|
|
68
|
-
"repo": "owner/repo",
|
|
69
|
-
"versionFiles": [
|
|
70
|
-
{ "glob": "plugin.json" },
|
|
71
|
-
{ "glob": ".claude-plugin/marketplace.json", "paths": ["$.metadata.version", "$.plugins[*].version"] }
|
|
72
|
-
]
|
|
73
|
-
}]
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
When `paths` is omitted it defaults to `["$.version"]`. In monorepos, each matched file inherits the version from its nearest workspace package. See [Configuration -- versionFiles](./docs/configuration.md#versionfiles-optional) for full details including supported JSONPath syntax.
|
|
78
|
-
|
|
79
|
-
## markdownlint Integration
|
|
80
|
-
|
|
81
|
-
Register the custom rules in your base config (e.g., `lib/configs/.markdownlint-cli2.jsonc`):
|
|
82
|
-
|
|
83
|
-
```jsonc
|
|
84
|
-
{
|
|
85
|
-
"customRules": [
|
|
86
|
-
"@savvy-web/changesets/markdownlint"
|
|
87
|
-
],
|
|
88
|
-
"config": {
|
|
89
|
-
"changeset-heading-hierarchy": false,
|
|
90
|
-
"changeset-required-sections": false,
|
|
91
|
-
"changeset-content-structure": false
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Then enable the rules only for changeset files by creating `.changeset/.markdownlint.json`:
|
|
97
|
-
|
|
98
|
-
```json
|
|
99
|
-
{
|
|
100
|
-
"extends": "../lib/configs/.markdownlint-cli2.jsonc",
|
|
101
|
-
"default": false,
|
|
102
|
-
"changeset-heading-hierarchy": true,
|
|
103
|
-
"changeset-required-sections": true,
|
|
104
|
-
"changeset-content-structure": true,
|
|
105
|
-
"MD041": false
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
60
|
## Documentation
|
|
110
61
|
|
|
111
|
-
- [Section-Aware Pipeline](./docs/section-aware-pipeline.md) -- End-to-end walkthrough of how section-aware changesets flow through the
|
|
112
|
-
- [
|
|
62
|
+
- [Section-Aware Pipeline](./docs/section-aware-pipeline.md) -- End-to-end walkthrough of how section-aware changesets flow through the pipeline
|
|
63
|
+
- [Configuration](./docs/configuration.md) -- Options, version files, markdownlint integration, CI scripts
|
|
64
|
+
- [CLI Reference](./docs/cli.md) -- All commands and options
|
|
65
|
+
- [API Reference](./docs/api.md) -- Classes, types, Effect services, remark plugins
|
|
66
|
+
- [Architecture](./docs/architecture.md) -- Three-layer pipeline design and export map
|
|
113
67
|
|
|
114
68
|
## License
|
|
115
69
|
|
|
@@ -119,3 +73,5 @@ MIT
|
|
|
119
73
|
[npm-url]: https://www.npmjs.com/package/@savvy-web/changesets
|
|
120
74
|
[license-badge]: https://img.shields.io/badge/License-MIT-yellow.svg
|
|
121
75
|
[license-url]: https://opensource.org/licenses/MIT
|
|
76
|
+
[node-badge]: https://img.shields.io/badge/node-%3E%3D24-brightgreen
|
|
77
|
+
[node-url]: https://nodejs.org/
|
package/cjs/index.cjs
CHANGED
|
@@ -859,6 +859,19 @@ var __webpack_exports__ = {};
|
|
|
859
859
|
if (!(0, categories.Lr)(text)) file.message(`Unknown section "${text}". Valid sections: ${(0, categories.Rr)().join(", ")}`, node);
|
|
860
860
|
});
|
|
861
861
|
});
|
|
862
|
+
const IGNORED_TYPES = new Set([
|
|
863
|
+
"html"
|
|
864
|
+
]);
|
|
865
|
+
function isContentNode(node) {
|
|
866
|
+
if ("heading" === node.type) return false;
|
|
867
|
+
return !IGNORED_TYPES.has(node.type);
|
|
868
|
+
}
|
|
869
|
+
const UncategorizedContentRule = (0, external_unified_lint_rule_namespaceObject.lintRule)("remark-lint:changeset-uncategorized-content", (tree, file)=>{
|
|
870
|
+
for (const node of tree.children){
|
|
871
|
+
if ("heading" === node.type && 2 === node.depth) break;
|
|
872
|
+
if (isContentNode(node)) file.message("Content must be placed under a category heading (## heading)", node);
|
|
873
|
+
}
|
|
874
|
+
});
|
|
862
875
|
function stripFrontmatter(content) {
|
|
863
876
|
return content.replace(/^---\n[\s\S]*?\n---\n?/, "");
|
|
864
877
|
}
|
|
@@ -869,7 +882,7 @@ var __webpack_exports__ = {};
|
|
|
869
882
|
}
|
|
870
883
|
static validateContent(content, filePath = "<input>") {
|
|
871
884
|
const body = stripFrontmatter(content);
|
|
872
|
-
const processor = (0, external_unified_.unified)().use(external_remark_parse_default()).use(external_remark_stringify_default()).use(HeadingHierarchyRule).use(RequiredSectionsRule).use(ContentStructureRule);
|
|
885
|
+
const processor = (0, external_unified_.unified)().use(external_remark_parse_default()).use(external_remark_stringify_default()).use(HeadingHierarchyRule).use(RequiredSectionsRule).use(ContentStructureRule).use(UncategorizedContentRule);
|
|
873
886
|
const file = processor.processSync(body);
|
|
874
887
|
return file.messages.map((msg)=>({
|
|
875
888
|
file: filePath,
|
package/cjs/index.d.cts
CHANGED
|
@@ -256,9 +256,9 @@ export declare interface Changeset extends Schema.Schema.Type<typeof ChangesetSc
|
|
|
256
256
|
/**
|
|
257
257
|
* Static class for linting changeset files.
|
|
258
258
|
*
|
|
259
|
-
* Runs the
|
|
260
|
-
* content-structure) against changeset markdown
|
|
261
|
-
* diagnostic messages.
|
|
259
|
+
* Runs the four remark-lint rules (heading-hierarchy, required-sections,
|
|
260
|
+
* content-structure, uncategorized-content) against changeset markdown
|
|
261
|
+
* and returns structured diagnostic messages.
|
|
262
262
|
*
|
|
263
263
|
* @example
|
|
264
264
|
* ```typescript
|
package/cjs/markdownlint.cjs
CHANGED
|
@@ -26,6 +26,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
27
|
ContentStructureRule: ()=>ContentStructureRule,
|
|
28
28
|
HeadingHierarchyRule: ()=>HeadingHierarchyRule,
|
|
29
|
+
UncategorizedContentRule: ()=>UncategorizedContentRule,
|
|
29
30
|
default: ()=>markdownlint,
|
|
30
31
|
RequiredSectionsRule: ()=>RequiredSectionsRule
|
|
31
32
|
});
|
|
@@ -276,20 +277,46 @@ const RequiredSectionsRule = {
|
|
|
276
277
|
}
|
|
277
278
|
}
|
|
278
279
|
};
|
|
280
|
+
const UncategorizedContentRule = {
|
|
281
|
+
names: [
|
|
282
|
+
"changeset-uncategorized-content",
|
|
283
|
+
"CSH004"
|
|
284
|
+
],
|
|
285
|
+
description: "All content must be placed under a category heading (## heading)",
|
|
286
|
+
tags: [
|
|
287
|
+
"changeset"
|
|
288
|
+
],
|
|
289
|
+
parser: "micromark",
|
|
290
|
+
function: function(params, onError) {
|
|
291
|
+
const tokens = params.parsers.micromark.tokens;
|
|
292
|
+
for (const token of tokens){
|
|
293
|
+
if ("atxHeading" === token.type && 2 === getHeadingLevel(token)) break;
|
|
294
|
+
if ("lineEnding" !== token.type && "lineEndingBlank" !== token.type && "htmlFlow" !== token.type) {
|
|
295
|
+
if ("atxHeading" !== token.type) onError({
|
|
296
|
+
lineNumber: token.startLine,
|
|
297
|
+
detail: "Content must be placed under a category heading (## heading)"
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
};
|
|
279
303
|
const SilkChangesetsRules = [
|
|
280
304
|
HeadingHierarchyRule,
|
|
281
305
|
RequiredSectionsRule,
|
|
282
|
-
ContentStructureRule
|
|
306
|
+
ContentStructureRule,
|
|
307
|
+
UncategorizedContentRule
|
|
283
308
|
];
|
|
284
309
|
const markdownlint = SilkChangesetsRules;
|
|
285
310
|
exports.ContentStructureRule = __webpack_exports__.ContentStructureRule;
|
|
286
311
|
exports.HeadingHierarchyRule = __webpack_exports__.HeadingHierarchyRule;
|
|
287
312
|
exports.RequiredSectionsRule = __webpack_exports__.RequiredSectionsRule;
|
|
313
|
+
exports.UncategorizedContentRule = __webpack_exports__.UncategorizedContentRule;
|
|
288
314
|
exports["default"] = __webpack_exports__["default"];
|
|
289
315
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
290
316
|
"ContentStructureRule",
|
|
291
317
|
"HeadingHierarchyRule",
|
|
292
318
|
"RequiredSectionsRule",
|
|
319
|
+
"UncategorizedContentRule",
|
|
293
320
|
"default"
|
|
294
321
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
295
322
|
Object.defineProperty(exports, '__esModule', {
|
package/cjs/markdownlint.d.cts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* - `changeset-heading-hierarchy` (CSH001): Enforce h2 start, no h1, no depth skips
|
|
9
9
|
* - `changeset-required-sections` (CSH002): Validate section headings match known categories
|
|
10
10
|
* - `changeset-content-structure` (CSH003): Content quality validation
|
|
11
|
+
* - `changeset-uncategorized-content` (CSH004): Reject content before first h2 heading
|
|
11
12
|
*
|
|
12
13
|
* @packageDocumentation
|
|
13
14
|
*/
|
|
@@ -58,4 +59,12 @@ export declare const RequiredSectionsRule: Rule;
|
|
|
58
59
|
declare const SilkChangesetsRules: Rule[];
|
|
59
60
|
export default SilkChangesetsRules;
|
|
60
61
|
|
|
62
|
+
/**
|
|
63
|
+
* markdownlint rule: changeset-uncategorized-content (CSH004)
|
|
64
|
+
*
|
|
65
|
+
* Detects content that appears before the first h2 heading in a changeset file.
|
|
66
|
+
* All content must be placed under a categorized section (## heading).
|
|
67
|
+
*/
|
|
68
|
+
export declare const UncategorizedContentRule: Rule;
|
|
69
|
+
|
|
61
70
|
export { }
|
package/cjs/remark.cjs
CHANGED
|
@@ -24,6 +24,7 @@ var __webpack_require__ = {};
|
|
|
24
24
|
var __webpack_exports__ = {};
|
|
25
25
|
__webpack_require__.r(__webpack_exports__);
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
UncategorizedContentRule: ()=>UncategorizedContentRule,
|
|
27
28
|
SilkChangesetTransformPreset: ()=>SilkChangesetTransformPreset,
|
|
28
29
|
NormalizeFormatPlugin: ()=>NormalizeFormatPlugin,
|
|
29
30
|
IssueLinkRefsPlugin: ()=>IssueLinkRefsPlugin,
|
|
@@ -502,10 +503,24 @@ const RequiredSectionsRule = (0, external_unified_lint_rule_namespaceObject.lint
|
|
|
502
503
|
if (!isValidHeading(text)) file.message(`Unknown section "${text}". Valid sections: ${allHeadings().join(", ")}`, node);
|
|
503
504
|
});
|
|
504
505
|
});
|
|
506
|
+
const IGNORED_TYPES = new Set([
|
|
507
|
+
"html"
|
|
508
|
+
]);
|
|
509
|
+
function isContentNode(node) {
|
|
510
|
+
if ("heading" === node.type) return false;
|
|
511
|
+
return !IGNORED_TYPES.has(node.type);
|
|
512
|
+
}
|
|
513
|
+
const UncategorizedContentRule = (0, external_unified_lint_rule_namespaceObject.lintRule)("remark-lint:changeset-uncategorized-content", (tree, file)=>{
|
|
514
|
+
for (const node of tree.children){
|
|
515
|
+
if ("heading" === node.type && 2 === node.depth) break;
|
|
516
|
+
if (isContentNode(node)) file.message("Content must be placed under a category heading (## heading)", node);
|
|
517
|
+
}
|
|
518
|
+
});
|
|
505
519
|
const SilkChangesetPreset = [
|
|
506
520
|
HeadingHierarchyRule,
|
|
507
521
|
RequiredSectionsRule,
|
|
508
|
-
ContentStructureRule
|
|
522
|
+
ContentStructureRule,
|
|
523
|
+
UncategorizedContentRule
|
|
509
524
|
];
|
|
510
525
|
const SilkChangesetTransformPreset = [
|
|
511
526
|
MergeSectionsPlugin,
|
|
@@ -526,6 +541,7 @@ exports.ReorderSectionsPlugin = __webpack_exports__.ReorderSectionsPlugin;
|
|
|
526
541
|
exports.RequiredSectionsRule = __webpack_exports__.RequiredSectionsRule;
|
|
527
542
|
exports.SilkChangesetPreset = __webpack_exports__.SilkChangesetPreset;
|
|
528
543
|
exports.SilkChangesetTransformPreset = __webpack_exports__.SilkChangesetTransformPreset;
|
|
544
|
+
exports.UncategorizedContentRule = __webpack_exports__.UncategorizedContentRule;
|
|
529
545
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
530
546
|
"ContentStructureRule",
|
|
531
547
|
"ContributorFootnotesPlugin",
|
|
@@ -537,7 +553,8 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
|
537
553
|
"ReorderSectionsPlugin",
|
|
538
554
|
"RequiredSectionsRule",
|
|
539
555
|
"SilkChangesetPreset",
|
|
540
|
-
"SilkChangesetTransformPreset"
|
|
556
|
+
"SilkChangesetTransformPreset",
|
|
557
|
+
"UncategorizedContentRule"
|
|
541
558
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
542
559
|
Object.defineProperty(exports, '__esModule', {
|
|
543
560
|
value: true
|
package/cjs/remark.d.cts
CHANGED
|
@@ -71,7 +71,7 @@ export declare const RequiredSectionsRule: Plugin_2<Root, unknown>;
|
|
|
71
71
|
*
|
|
72
72
|
* @public
|
|
73
73
|
*/
|
|
74
|
-
export declare const SilkChangesetPreset: readonly [Plugin_2<Root, unknown>, Plugin_2<Root, unknown>, Plugin_2<Root, unknown>];
|
|
74
|
+
export declare const SilkChangesetPreset: readonly [Plugin_2<Root, unknown>, Plugin_2<Root, unknown>, Plugin_2<Root, unknown>, Plugin_2<Root, unknown>];
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Ordered array of all transform plugins in the correct execution order.
|
|
@@ -89,4 +89,6 @@ export declare const SilkChangesetPreset: readonly [Plugin_2<Root, unknown>, Plu
|
|
|
89
89
|
*/
|
|
90
90
|
export declare const SilkChangesetTransformPreset: readonly [Plugin<[], Root>, Plugin<[], Root>, Plugin<[], Root>, Plugin<[], Root>, Plugin<[], Root>, Plugin<[], Root>];
|
|
91
91
|
|
|
92
|
+
export declare const UncategorizedContentRule: Plugin_2<Root, unknown>;
|
|
93
|
+
|
|
92
94
|
export { }
|
package/esm/273.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join, relative, resolve } from "node:path";
|
|
3
3
|
import { unified, remark_gfm, remark_stringify, remark_parse } from "./795.js";
|
|
4
|
-
import { MergeSectionsPlugin, DeduplicateItemsPlugin, IssueLinkRefsPlugin, ContentStructureRule, HeadingHierarchyRule, NormalizeFormatPlugin, ReorderSectionsPlugin, ContributorFootnotesPlugin, RequiredSectionsRule } from "./
|
|
4
|
+
import { UncategorizedContentRule, MergeSectionsPlugin, DeduplicateItemsPlugin, IssueLinkRefsPlugin, ContentStructureRule, HeadingHierarchyRule, NormalizeFormatPlugin, ReorderSectionsPlugin, ContributorFootnotesPlugin, RequiredSectionsRule } from "./622.js";
|
|
5
5
|
function stripFrontmatter(content) {
|
|
6
6
|
return content.replace(/^---\n[\s\S]*?\n---\n?/, "");
|
|
7
7
|
}
|
|
@@ -12,7 +12,7 @@ class ChangesetLinter {
|
|
|
12
12
|
}
|
|
13
13
|
static validateContent(content, filePath = "<input>") {
|
|
14
14
|
const body = stripFrontmatter(content);
|
|
15
|
-
const processor = unified().use(remark_parse).use(remark_stringify).use(HeadingHierarchyRule).use(RequiredSectionsRule).use(ContentStructureRule);
|
|
15
|
+
const processor = unified().use(remark_parse).use(remark_stringify).use(HeadingHierarchyRule).use(RequiredSectionsRule).use(ContentStructureRule).use(UncategorizedContentRule);
|
|
16
16
|
const file = processor.processSync(body);
|
|
17
17
|
return file.messages.map((msg)=>({
|
|
18
18
|
file: filePath,
|
package/esm/{234.js → 622.js}
RENAMED
|
@@ -31,6 +31,19 @@ const RequiredSectionsRule = lintRule("remark-lint:changeset-required-sections",
|
|
|
31
31
|
if (!isValidHeading(text)) file.message(`Unknown section "${text}". Valid sections: ${allHeadings().join(", ")}`, node);
|
|
32
32
|
});
|
|
33
33
|
});
|
|
34
|
+
const IGNORED_TYPES = new Set([
|
|
35
|
+
"html"
|
|
36
|
+
]);
|
|
37
|
+
function isContentNode(node) {
|
|
38
|
+
if ("heading" === node.type) return false;
|
|
39
|
+
return !IGNORED_TYPES.has(node.type);
|
|
40
|
+
}
|
|
41
|
+
const UncategorizedContentRule = lintRule("remark-lint:changeset-uncategorized-content", (tree, file)=>{
|
|
42
|
+
for (const node of tree.children){
|
|
43
|
+
if ("heading" === node.type && 2 === node.depth) break;
|
|
44
|
+
if (isContentNode(node)) file.message("Content must be placed under a category heading (## heading)", node);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
34
47
|
function getVersionBlocks(tree) {
|
|
35
48
|
const blocks = [];
|
|
36
49
|
for(let i = 0; i < tree.children.length; i++){
|
|
@@ -334,4 +347,4 @@ const ReorderSectionsPlugin = ()=>(tree)=>{
|
|
|
334
347
|
tree.children.splice(block.startIndex, blockLength, ...newChildren);
|
|
335
348
|
}
|
|
336
349
|
};
|
|
337
|
-
export { ContentStructureRule, ContributorFootnotesPlugin, DeduplicateItemsPlugin, HeadingHierarchyRule, IssueLinkRefsPlugin, MergeSectionsPlugin, NormalizeFormatPlugin, ReorderSectionsPlugin, RequiredSectionsRule };
|
|
350
|
+
export { ContentStructureRule, ContributorFootnotesPlugin, DeduplicateItemsPlugin, HeadingHierarchyRule, IssueLinkRefsPlugin, MergeSectionsPlugin, NormalizeFormatPlugin, ReorderSectionsPlugin, RequiredSectionsRule, UncategorizedContentRule };
|
|
@@ -48,7 +48,8 @@ const MARKDOWNLINT_CONFIG_PATHS = [
|
|
|
48
48
|
const RULE_NAMES = [
|
|
49
49
|
"changeset-heading-hierarchy",
|
|
50
50
|
"changeset-required-sections",
|
|
51
|
-
"changeset-content-structure"
|
|
51
|
+
"changeset-content-structure",
|
|
52
|
+
"changeset-uncategorized-content"
|
|
52
53
|
];
|
|
53
54
|
const DEFAULT_CONFIG = {
|
|
54
55
|
$schema: "https://unpkg.com/@changesets/config@3.1.1/schema.json",
|
|
@@ -758,7 +759,7 @@ const rootCommand = Command.make("savvy-changesets").pipe(Command.withSubcommand
|
|
|
758
759
|
]));
|
|
759
760
|
const cli = Command.run(rootCommand, {
|
|
760
761
|
name: "savvy-changesets",
|
|
761
|
-
version: "0.
|
|
762
|
+
version: "0.3.0"
|
|
762
763
|
});
|
|
763
764
|
function runCli() {
|
|
764
765
|
const main = Effect.suspend(()=>cli(process.argv)).pipe(Effect.provide(NodeContext.layer));
|
package/esm/index.d.ts
CHANGED
|
@@ -256,9 +256,9 @@ export declare interface Changeset extends Schema.Schema.Type<typeof ChangesetSc
|
|
|
256
256
|
/**
|
|
257
257
|
* Static class for linting changeset files.
|
|
258
258
|
*
|
|
259
|
-
* Runs the
|
|
260
|
-
* content-structure) against changeset markdown
|
|
261
|
-
* diagnostic messages.
|
|
259
|
+
* Runs the four remark-lint rules (heading-hierarchy, required-sections,
|
|
260
|
+
* content-structure, uncategorized-content) against changeset markdown
|
|
261
|
+
* and returns structured diagnostic messages.
|
|
262
262
|
*
|
|
263
263
|
* @example
|
|
264
264
|
* ```typescript
|
package/esm/markdownlint.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* - `changeset-heading-hierarchy` (CSH001): Enforce h2 start, no h1, no depth skips
|
|
9
9
|
* - `changeset-required-sections` (CSH002): Validate section headings match known categories
|
|
10
10
|
* - `changeset-content-structure` (CSH003): Content quality validation
|
|
11
|
+
* - `changeset-uncategorized-content` (CSH004): Reject content before first h2 heading
|
|
11
12
|
*
|
|
12
13
|
* @packageDocumentation
|
|
13
14
|
*/
|
|
@@ -58,4 +59,12 @@ export declare const RequiredSectionsRule: Rule;
|
|
|
58
59
|
declare const SilkChangesetsRules: Rule[];
|
|
59
60
|
export default SilkChangesetsRules;
|
|
60
61
|
|
|
62
|
+
/**
|
|
63
|
+
* markdownlint rule: changeset-uncategorized-content (CSH004)
|
|
64
|
+
*
|
|
65
|
+
* Detects content that appears before the first h2 heading in a changeset file.
|
|
66
|
+
* All content must be placed under a categorized section (## heading).
|
|
67
|
+
*/
|
|
68
|
+
export declare const UncategorizedContentRule: Rule;
|
|
69
|
+
|
|
61
70
|
export { }
|
package/esm/markdownlint.js
CHANGED
|
@@ -118,11 +118,35 @@ const RequiredSectionsRule = {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
};
|
|
121
|
+
const UncategorizedContentRule = {
|
|
122
|
+
names: [
|
|
123
|
+
"changeset-uncategorized-content",
|
|
124
|
+
"CSH004"
|
|
125
|
+
],
|
|
126
|
+
description: "All content must be placed under a category heading (## heading)",
|
|
127
|
+
tags: [
|
|
128
|
+
"changeset"
|
|
129
|
+
],
|
|
130
|
+
parser: "micromark",
|
|
131
|
+
function: function(params, onError) {
|
|
132
|
+
const tokens = params.parsers.micromark.tokens;
|
|
133
|
+
for (const token of tokens){
|
|
134
|
+
if ("atxHeading" === token.type && 2 === getHeadingLevel(token)) break;
|
|
135
|
+
if ("lineEnding" !== token.type && "lineEndingBlank" !== token.type && "htmlFlow" !== token.type) {
|
|
136
|
+
if ("atxHeading" !== token.type) onError({
|
|
137
|
+
lineNumber: token.startLine,
|
|
138
|
+
detail: "Content must be placed under a category heading (## heading)"
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
121
144
|
const SilkChangesetsRules = [
|
|
122
145
|
HeadingHierarchyRule,
|
|
123
146
|
RequiredSectionsRule,
|
|
124
|
-
ContentStructureRule
|
|
147
|
+
ContentStructureRule,
|
|
148
|
+
UncategorizedContentRule
|
|
125
149
|
];
|
|
126
150
|
const markdownlint = SilkChangesetsRules;
|
|
127
151
|
export default markdownlint;
|
|
128
|
-
export { ContentStructureRule, HeadingHierarchyRule, RequiredSectionsRule };
|
|
152
|
+
export { ContentStructureRule, HeadingHierarchyRule, RequiredSectionsRule, UncategorizedContentRule };
|
package/esm/remark.d.ts
CHANGED
|
@@ -71,7 +71,7 @@ export declare const RequiredSectionsRule: Plugin_2<Root, unknown>;
|
|
|
71
71
|
*
|
|
72
72
|
* @public
|
|
73
73
|
*/
|
|
74
|
-
export declare const SilkChangesetPreset: readonly [Plugin_2<Root, unknown>, Plugin_2<Root, unknown>, Plugin_2<Root, unknown>];
|
|
74
|
+
export declare const SilkChangesetPreset: readonly [Plugin_2<Root, unknown>, Plugin_2<Root, unknown>, Plugin_2<Root, unknown>, Plugin_2<Root, unknown>];
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Ordered array of all transform plugins in the correct execution order.
|
|
@@ -89,4 +89,6 @@ export declare const SilkChangesetPreset: readonly [Plugin_2<Root, unknown>, Plu
|
|
|
89
89
|
*/
|
|
90
90
|
export declare const SilkChangesetTransformPreset: readonly [Plugin<[], Root>, Plugin<[], Root>, Plugin<[], Root>, Plugin<[], Root>, Plugin<[], Root>, Plugin<[], Root>];
|
|
91
91
|
|
|
92
|
+
export declare const UncategorizedContentRule: Plugin_2<Root, unknown>;
|
|
93
|
+
|
|
92
94
|
export { }
|
package/esm/remark.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { NormalizeFormatPlugin, DeduplicateItemsPlugin, MergeSectionsPlugin, ContentStructureRule, IssueLinkRefsPlugin, ReorderSectionsPlugin, HeadingHierarchyRule, ContributorFootnotesPlugin, RequiredSectionsRule } from "./
|
|
1
|
+
import { UncategorizedContentRule, NormalizeFormatPlugin, DeduplicateItemsPlugin, MergeSectionsPlugin, ContentStructureRule, IssueLinkRefsPlugin, ReorderSectionsPlugin, HeadingHierarchyRule, ContributorFootnotesPlugin, RequiredSectionsRule } from "./622.js";
|
|
2
2
|
const SilkChangesetPreset = [
|
|
3
3
|
HeadingHierarchyRule,
|
|
4
4
|
RequiredSectionsRule,
|
|
5
|
-
ContentStructureRule
|
|
5
|
+
ContentStructureRule,
|
|
6
|
+
UncategorizedContentRule
|
|
6
7
|
];
|
|
7
8
|
const SilkChangesetTransformPreset = [
|
|
8
9
|
MergeSectionsPlugin,
|
|
@@ -12,5 +13,5 @@ const SilkChangesetTransformPreset = [
|
|
|
12
13
|
IssueLinkRefsPlugin,
|
|
13
14
|
NormalizeFormatPlugin
|
|
14
15
|
];
|
|
15
|
-
export { ContentStructureRule, ContributorFootnotesPlugin, DeduplicateItemsPlugin, HeadingHierarchyRule, IssueLinkRefsPlugin, MergeSectionsPlugin, NormalizeFormatPlugin, ReorderSectionsPlugin, RequiredSectionsRule } from "./
|
|
16
|
+
export { ContentStructureRule, ContributorFootnotesPlugin, DeduplicateItemsPlugin, HeadingHierarchyRule, IssueLinkRefsPlugin, MergeSectionsPlugin, NormalizeFormatPlugin, ReorderSectionsPlugin, RequiredSectionsRule, UncategorizedContentRule } from "./622.js";
|
|
16
17
|
export { SilkChangesetPreset, SilkChangesetTransformPreset };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/changesets",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
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
6
|
"keywords": [
|