@savvy-web/silk-effects 9.6.0 → 10.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/changesets/api/dependency-table.js +12 -12
- package/changesets/api/linter.js +23 -19
- package/changesets/api/transformer.js +38 -33
- package/changesets/categories/index.js +1 -1
- package/changesets/categories/types.js +2 -2
- package/changesets/changelog/getDependencyReleaseLine.js +1 -1
- package/changesets/changelog/index.js +2 -2
- package/changesets/constants.js +1 -1
- package/changesets/errors.js +1 -1
- package/changesets/index.js +11 -40
- package/changesets/markdownlint/rules/content-structure.js +2 -2
- package/changesets/markdownlint/rules/heading-hierarchy.js +2 -2
- package/changesets/markdownlint/rules/required-sections.js +2 -2
- package/changesets/markdownlint/rules/uncategorized-content.js +2 -2
- package/changesets/markdownlint/rules/utils.js +6 -6
- package/changesets/rules/content-structure.js +44 -0
- package/changesets/rules/dependency-table-format.js +54 -0
- package/changesets/rules/heading-hierarchy.js +37 -0
- package/changesets/rules/required-sections.js +34 -0
- package/changesets/rules/types.js +20 -0
- package/changesets/rules/uncategorized-content.js +30 -0
- package/changesets/schemas/changeset.js +1 -1
- package/changesets/transforms/aggregate-dependency-tables.js +96 -0
- package/changesets/transforms/contributor-footnotes.js +290 -0
- package/changesets/transforms/deduplicate-items.js +47 -0
- package/changesets/transforms/issue-link-refs.js +47 -0
- package/changesets/transforms/maintenance-note.js +65 -0
- package/changesets/transforms/merge-sections.js +56 -0
- package/changesets/transforms/normalize-format.js +53 -0
- package/changesets/transforms/reorder-sections.js +53 -0
- package/changesets/utils/blocks.js +67 -0
- package/changesets/utils/dependency-table.js +29 -36
- package/changesets/utils/markdown-emit.js +11 -36
- package/index.d.ts +54 -1199
- package/package.json +10 -17
- package/changesets/remark/plugins/aggregate-dependency-tables.js +0 -98
- package/changesets/remark/plugins/contributor-footnotes.js +0 -334
- package/changesets/remark/plugins/deduplicate-items.js +0 -31
- package/changesets/remark/plugins/issue-link-refs.js +0 -57
- package/changesets/remark/plugins/maintenance-note.js +0 -74
- package/changesets/remark/plugins/merge-sections.js +0 -42
- package/changesets/remark/plugins/normalize-format.js +0 -46
- package/changesets/remark/plugins/reorder-sections.js +0 -48
- package/changesets/remark/presets.js +0 -118
- package/changesets/remark/rules/content-structure.js +0 -21
- package/changesets/remark/rules/dependency-table-format.js +0 -37
- package/changesets/remark/rules/heading-hierarchy.js +0 -18
- package/changesets/remark/rules/required-sections.js +0 -16
- package/changesets/remark/rules/uncategorized-content.js +0 -30
- package/changesets/utils/remark-pipeline.js +0 -35
- package/changesets/utils/version-blocks.js +0 -107
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { getHeadingText, getVersionBlocks, isHeadingOfDepth } from "../utils/blocks.js";
|
|
2
|
+
import { Heading, InlineCode, List, ListItem, Mdast, Paragraph, Root, Text } from "@effected/markdown";
|
|
3
|
+
|
|
4
|
+
// src/changesets/transforms/maintenance-note.ts
|
|
5
|
+
/**
|
|
6
|
+
* Placeholder sentence `@changesets/apply-release-plan` (>= 8.1.0) emits as the
|
|
7
|
+
* body of a version block that would otherwise render as a bare heading.
|
|
8
|
+
*/
|
|
9
|
+
const ENGINE_PLACEHOLDER = "No changes in this release.";
|
|
10
|
+
/** True when the block's only content is the engine's empty-release placeholder. */
|
|
11
|
+
function isPlaceholderOnly(children, block) {
|
|
12
|
+
if (block.endIndex - block.startIndex !== 1) return false;
|
|
13
|
+
const node = children[block.startIndex];
|
|
14
|
+
return node.type === "paragraph" && Mdast.toText(node).trim() === ENGINE_PLACEHOLDER;
|
|
15
|
+
}
|
|
16
|
+
function buildNoteChildren(reason) {
|
|
17
|
+
if (reason.kind === "unspecified" || reason.triggers.length === 0) return [new Text({ value: "Version-only release to keep workspace versions consistent; no changes to this package." })];
|
|
18
|
+
const label = reason.kind === "fixed" ? "fixed version group" : "linked version group";
|
|
19
|
+
const nodes = [new Text({ value: "Released in lockstep with " })];
|
|
20
|
+
reason.triggers.forEach((trigger, index) => {
|
|
21
|
+
if (index > 0) nodes.push(new Text({ value: ", " }));
|
|
22
|
+
nodes.push(new InlineCode({ value: `${trigger.name}@${trigger.version}` }));
|
|
23
|
+
});
|
|
24
|
+
nodes.push(new Text({ value: ` (${label}).` }));
|
|
25
|
+
return nodes;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build a transform that adds a Maintenance note to the targeted empty version block.
|
|
29
|
+
*
|
|
30
|
+
* @param options - The target version and the release reason
|
|
31
|
+
* @returns A transform; a no-op when the block is absent or non-empty
|
|
32
|
+
*
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
function maintenanceNote(options) {
|
|
36
|
+
return (root) => {
|
|
37
|
+
const children = [...root.children];
|
|
38
|
+
const block = getVersionBlocks(children).find((b) => {
|
|
39
|
+
const node = children[b.headingIndex];
|
|
40
|
+
return isHeadingOfDepth(node, [2]) && getHeadingText(node) === options.version;
|
|
41
|
+
});
|
|
42
|
+
if (!block) return root;
|
|
43
|
+
const placeholderOnly = isPlaceholderOnly(children, block);
|
|
44
|
+
if (block.endIndex > block.startIndex && !placeholderOnly) return root;
|
|
45
|
+
const heading = new Heading({
|
|
46
|
+
depth: 3,
|
|
47
|
+
children: [new Text({ value: "Maintenance" })]
|
|
48
|
+
});
|
|
49
|
+
const list = new List({
|
|
50
|
+
ordered: false,
|
|
51
|
+
spread: false,
|
|
52
|
+
children: [new ListItem({
|
|
53
|
+
spread: false,
|
|
54
|
+
children: [new Paragraph({ children: buildNoteChildren(options.reason) })]
|
|
55
|
+
})]
|
|
56
|
+
});
|
|
57
|
+
children.splice(block.startIndex, placeholderOnly ? 1 : 0, heading, list);
|
|
58
|
+
return new Root({
|
|
59
|
+
...root,
|
|
60
|
+
children
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { maintenanceNote };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { fromHeading } from "../categories/index.js";
|
|
2
|
+
import { getBlockSections, getHeadingText, getVersionBlocks } from "../utils/blocks.js";
|
|
3
|
+
import { Root } from "@effected/markdown";
|
|
4
|
+
|
|
5
|
+
// src/changesets/transforms/merge-sections.ts
|
|
6
|
+
/**
|
|
7
|
+
* Changelog transform: merge duplicate h3 section headings.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* Kit transform. Groups h3 sections within each version
|
|
11
|
+
* block by normalized heading text (via the category system's `fromHeading`)
|
|
12
|
+
* and moves the content of duplicates under the first occurrence, then drops
|
|
13
|
+
* the duplicate headings. Runs after dependency-table aggregation and before
|
|
14
|
+
* section reordering.
|
|
15
|
+
*
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Merge duplicate h3 sections within each version block.
|
|
20
|
+
*
|
|
21
|
+
* @param root - The changelog tree
|
|
22
|
+
* @returns A tree with a single section per heading in each version block
|
|
23
|
+
*
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
const mergeSections = (root) => {
|
|
27
|
+
const children = [...root.children];
|
|
28
|
+
const blocks = getVersionBlocks(children);
|
|
29
|
+
for (let b = blocks.length - 1; b >= 0; b--) {
|
|
30
|
+
const sections = getBlockSections(children, blocks[b]);
|
|
31
|
+
if (sections.length <= 1) continue;
|
|
32
|
+
const groups = /* @__PURE__ */ new Map();
|
|
33
|
+
for (let s = 0; s < sections.length; s++) {
|
|
34
|
+
const text = getHeadingText(sections[s].heading);
|
|
35
|
+
const category = fromHeading(text);
|
|
36
|
+
const key = category ? category.heading.toLowerCase() : text.toLowerCase();
|
|
37
|
+
const existing = groups.get(key);
|
|
38
|
+
if (existing) existing.push(s);
|
|
39
|
+
else groups.set(key, [s]);
|
|
40
|
+
}
|
|
41
|
+
if (![...groups.values()].some((indices) => indices.length > 1)) continue;
|
|
42
|
+
const block = blocks[b];
|
|
43
|
+
const rebuilt = children.slice(block.startIndex, sections[0].headingIndex);
|
|
44
|
+
for (const indices of groups.values()) {
|
|
45
|
+
const [target, ...duplicates] = indices.map((s) => sections[s]);
|
|
46
|
+
rebuilt.push(target.heading, ...target.contentNodes, ...duplicates.flatMap((d) => d.contentNodes));
|
|
47
|
+
}
|
|
48
|
+
children.splice(block.startIndex, block.endIndex - block.startIndex, ...rebuilt);
|
|
49
|
+
}
|
|
50
|
+
return new Root({
|
|
51
|
+
...root,
|
|
52
|
+
children
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export { mergeSections };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { getVersionBlocks, isHeadingOfDepth } from "../utils/blocks.js";
|
|
2
|
+
import { Mdast, Root } from "@effected/markdown";
|
|
3
|
+
|
|
4
|
+
// src/changesets/transforms/normalize-format.ts
|
|
5
|
+
/**
|
|
6
|
+
* Changelog transform: remove empty sections and empty lists.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Kit transform. Within each version block, drops empty
|
|
10
|
+
* lists and h3 sections with no content before the next h2/h3 (or the end of
|
|
11
|
+
* the block), along with whitespace-only paragraphs that trail them.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Remove empty h3 sections and empty lists from each version block.
|
|
17
|
+
*
|
|
18
|
+
* @param root - The changelog tree
|
|
19
|
+
* @returns The normalized tree
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
const normalizeFormat = (root) => {
|
|
24
|
+
const children = [...root.children];
|
|
25
|
+
const indicesToRemove = /* @__PURE__ */ new Set();
|
|
26
|
+
for (const block of getVersionBlocks(children)) for (let i = block.startIndex; i < block.endIndex; i++) {
|
|
27
|
+
const node = children[i];
|
|
28
|
+
if (node.type === "list" && node.children.length === 0) {
|
|
29
|
+
indicesToRemove.add(i);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (!isHeadingOfDepth(node, [3])) continue;
|
|
33
|
+
let hasContent = false;
|
|
34
|
+
for (let j = i + 1; j < block.endIndex; j++) {
|
|
35
|
+
const next = children[j];
|
|
36
|
+
if (next.type === "paragraph" && Mdast.toText(next).trim() === "") {
|
|
37
|
+
indicesToRemove.add(j);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (isHeadingOfDepth(next, [2, 3])) break;
|
|
41
|
+
if (next.type === "definition") continue;
|
|
42
|
+
hasContent = true;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
if (!hasContent) indicesToRemove.add(i);
|
|
46
|
+
}
|
|
47
|
+
return new Root({
|
|
48
|
+
...root,
|
|
49
|
+
children: children.filter((_, i) => !indicesToRemove.has(i))
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export { normalizeFormat };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { fromHeading } from "../categories/index.js";
|
|
2
|
+
import { getBlockSections, getHeadingText, getVersionBlocks } from "../utils/blocks.js";
|
|
3
|
+
import { Root } from "@effected/markdown";
|
|
4
|
+
|
|
5
|
+
// src/changesets/transforms/reorder-sections.ts
|
|
6
|
+
/**
|
|
7
|
+
* Changelog transform: reorder h3 sections by category priority.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* Kit transform. Sorts sections within each version
|
|
11
|
+
* block so Breaking Changes come first and Other last; unknown headings sort
|
|
12
|
+
* after every category and a `Thanks` section is pinned to the very end.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
/** Default priority for unrecognized headings (after all known categories). */
|
|
17
|
+
const UNKNOWN_PRIORITY = 999;
|
|
18
|
+
/** Priority for the `Thanks` section; closes the version block. */
|
|
19
|
+
const THANKS_PRIORITY = 1e3;
|
|
20
|
+
/** Resolve the sort priority for a section heading. */
|
|
21
|
+
function headingPriority(text) {
|
|
22
|
+
if (text.trim().toLowerCase() === "thanks") return THANKS_PRIORITY;
|
|
23
|
+
return fromHeading(text)?.priority ?? UNKNOWN_PRIORITY;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Reorder h3 sections within each version block by category priority.
|
|
27
|
+
*
|
|
28
|
+
* @param root - The changelog tree
|
|
29
|
+
* @returns A tree with sections in priority order
|
|
30
|
+
*
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
const reorderSections = (root) => {
|
|
34
|
+
const children = [...root.children];
|
|
35
|
+
const blocks = getVersionBlocks(children);
|
|
36
|
+
for (let b = blocks.length - 1; b >= 0; b--) {
|
|
37
|
+
const block = blocks[b];
|
|
38
|
+
const sections = getBlockSections(children, block);
|
|
39
|
+
if (sections.length <= 1) continue;
|
|
40
|
+
const preamble = children.slice(block.startIndex, sections[0].headingIndex);
|
|
41
|
+
const sorted = [...sections].sort((x, y) => headingPriority(getHeadingText(x.heading)) - headingPriority(getHeadingText(y.heading)));
|
|
42
|
+
if (sorted.every((s, i) => s.headingIndex === sections[i].headingIndex)) continue;
|
|
43
|
+
const rebuilt = [...preamble];
|
|
44
|
+
for (const section of sorted) rebuilt.push(section.heading, ...section.contentNodes);
|
|
45
|
+
children.splice(block.startIndex, block.endIndex - block.startIndex, ...rebuilt);
|
|
46
|
+
}
|
|
47
|
+
return new Root({
|
|
48
|
+
...root,
|
|
49
|
+
children
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export { reorderSections };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Mdast } from "@effected/markdown";
|
|
2
|
+
|
|
3
|
+
// src/changesets/utils/blocks.ts
|
|
4
|
+
/**
|
|
5
|
+
* Whether a node is a heading of one of the given depths.
|
|
6
|
+
*
|
|
7
|
+
* @param node - The node to test
|
|
8
|
+
* @param depths - Accepted heading depths
|
|
9
|
+
* @returns True (narrowing to `Heading`) when the node matches
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
function isHeadingOfDepth(node, depths) {
|
|
14
|
+
return node.type === "heading" && depths.includes(node.depth);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Extract all version (h2) blocks from a children array.
|
|
18
|
+
*
|
|
19
|
+
* @param children - The root children
|
|
20
|
+
* @returns Version blocks in document order
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
function getVersionBlocks(children) {
|
|
25
|
+
const headings = [];
|
|
26
|
+
for (let i = 0; i < children.length; i++) if (isHeadingOfDepth(children[i], [2])) headings.push(i);
|
|
27
|
+
return headings.map((headingIndex, n) => ({
|
|
28
|
+
headingIndex,
|
|
29
|
+
startIndex: headingIndex + 1,
|
|
30
|
+
endIndex: n + 1 < headings.length ? headings[n + 1] : children.length
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Extract h3 sections within a version block; content before the first h3 is excluded.
|
|
35
|
+
*
|
|
36
|
+
* @param children - The root children
|
|
37
|
+
* @param block - The version block to scan
|
|
38
|
+
* @returns Sections in document order
|
|
39
|
+
*
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
function getBlockSections(children, block) {
|
|
43
|
+
const sections = [];
|
|
44
|
+
for (let i = block.startIndex; i < block.endIndex; i++) {
|
|
45
|
+
const node = children[i];
|
|
46
|
+
if (isHeadingOfDepth(node, [3])) sections.push({
|
|
47
|
+
heading: node,
|
|
48
|
+
headingIndex: i,
|
|
49
|
+
contentNodes: []
|
|
50
|
+
});
|
|
51
|
+
else if (sections.length > 0) sections[sections.length - 1].contentNodes.push(node);
|
|
52
|
+
}
|
|
53
|
+
return sections;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Plain text of a heading.
|
|
57
|
+
*
|
|
58
|
+
* @param heading - The heading node
|
|
59
|
+
* @returns Concatenated inline text
|
|
60
|
+
*
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
function getHeadingText(heading) {
|
|
64
|
+
return Mdast.toText(heading);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { getBlockSections, getHeadingText, getVersionBlocks, isHeadingOfDepth };
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { DependencyTableRowSchema } from "../schemas/dependency-table.js";
|
|
2
|
-
import {
|
|
2
|
+
import { emitKitMarkdown } from "./markdown-emit.js";
|
|
3
3
|
import { Schema } from "effect";
|
|
4
|
-
import {
|
|
4
|
+
import { Mdast, Root, Table, TableCell, TableRow, Text } from "@effected/markdown";
|
|
5
5
|
|
|
6
6
|
// src/changesets/utils/dependency-table.ts
|
|
7
7
|
/**
|
|
8
8
|
* Dependency table utilities for parsing, serializing, collapsing, and sorting
|
|
9
|
-
* dependency table rows between
|
|
9
|
+
* dependency table rows between `@effected/markdown` `Table` nodes and typed
|
|
10
|
+
* representations.
|
|
10
11
|
*
|
|
11
12
|
* @remarks
|
|
12
13
|
* This module provides the low-level functional primitives that the
|
|
13
14
|
* {@link DependencyTable} class wraps with a stateful, fluent API.
|
|
14
|
-
* It operates on arrays of `DependencyTableRow` objects and
|
|
15
|
+
* It operates on arrays of `DependencyTableRow` objects and kit `Table`
|
|
15
16
|
* nodes, converting between the two representations.
|
|
16
17
|
*
|
|
17
18
|
* The collapse algorithm merges rows sharing the same `dependency + type`
|
|
@@ -55,14 +56,14 @@ const COLUMN_KEYS = [
|
|
|
55
56
|
/** Schema decoder for validating raw cell values into a typed row. */
|
|
56
57
|
const decode = Schema.decodeUnknownSync(DependencyTableRowSchema);
|
|
57
58
|
/**
|
|
58
|
-
* Parse
|
|
59
|
+
* Parse a kit table node into validated dependency table rows.
|
|
59
60
|
*
|
|
60
61
|
* @remarks
|
|
61
62
|
* Validates the header row against {@link COLUMN_HEADERS} (case-insensitive),
|
|
62
63
|
* then decodes each data row through the `DependencyTableRowSchema` for
|
|
63
64
|
* type-safe validation of action values, version strings, and dependency types.
|
|
64
65
|
*
|
|
65
|
-
* @param table - An
|
|
66
|
+
* @param table - An `@effected/markdown` `Table` node
|
|
66
67
|
* @returns Array of validated `DependencyTableRow` objects
|
|
67
68
|
* @throws If the table has fewer than 2 rows (header + at least one data row)
|
|
68
69
|
* @throws If the header columns do not match the expected column names
|
|
@@ -71,11 +72,11 @@ const decode = Schema.decodeUnknownSync(DependencyTableRowSchema);
|
|
|
71
72
|
* @example
|
|
72
73
|
* ```typescript
|
|
73
74
|
* import { parseDependencyTable } from "../utils/dependency-table.js";
|
|
74
|
-
* import {
|
|
75
|
-
* import type { Table } from "
|
|
75
|
+
* import { parseKitMarkdown } from "../utils/markdown-emit.js";
|
|
76
|
+
* import type { Table } from "\@effected/markdown";
|
|
76
77
|
*
|
|
77
|
-
* const tree =
|
|
78
|
-
* const table = tree.children.find((n) => n.type === "table")
|
|
78
|
+
* const tree = parseKitMarkdown("| Dependency | Type | Action | From | To |\n| --- | --- | --- | --- | --- |\n| foo | dependency | added | — | 1.0.0 |");
|
|
79
|
+
* const table = tree.children.find((n): n is Table => n.type === "table");
|
|
79
80
|
* const rows = parseDependencyTable(table);
|
|
80
81
|
* // rows[0].dependency === "foo"
|
|
81
82
|
* ```
|
|
@@ -85,14 +86,17 @@ const decode = Schema.decodeUnknownSync(DependencyTableRowSchema);
|
|
|
85
86
|
function parseDependencyTable(table) {
|
|
86
87
|
const rows = table.children;
|
|
87
88
|
if (rows.length < 2) throw new Error("Dependency table must have at least one data row");
|
|
88
|
-
const headers = rows[0].children.map((cell) =>
|
|
89
|
+
const headers = rows[0].children.map((cell) => Mdast.toText(cell).trim().toLowerCase());
|
|
89
90
|
const expected = COLUMN_HEADERS.map((h) => h.toLowerCase());
|
|
90
91
|
if (headers.length !== expected.length || !headers.every((h, i) => h === expected[i])) throw new Error(`Table must have columns: ${COLUMN_HEADERS.join(", ")}. Got: ${headers.join(", ")}`);
|
|
91
92
|
const result = [];
|
|
92
93
|
for (let i = 1; i < rows.length; i++) {
|
|
93
94
|
const cells = rows[i].children;
|
|
94
95
|
const raw = {};
|
|
95
|
-
for (let c = 0; c < COLUMN_KEYS.length; c++)
|
|
96
|
+
for (let c = 0; c < COLUMN_KEYS.length; c++) {
|
|
97
|
+
const cell = cells[c];
|
|
98
|
+
raw[COLUMN_KEYS[c]] = cell === void 0 ? "" : Mdast.toText(cell).trim();
|
|
99
|
+
}
|
|
96
100
|
result.push(decode(raw));
|
|
97
101
|
}
|
|
98
102
|
return result;
|
|
@@ -113,36 +117,29 @@ function parseDependencyTable(table) {
|
|
|
113
117
|
* through parse-and-reserialize regardless of which spelling produced them.
|
|
114
118
|
*
|
|
115
119
|
* @param text - The cell text content
|
|
116
|
-
* @returns
|
|
120
|
+
* @returns A kit `TableCell` node
|
|
117
121
|
*
|
|
118
122
|
* @internal
|
|
119
123
|
*/
|
|
120
124
|
function makeCell(text) {
|
|
121
|
-
return {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
value: text,
|
|
126
|
-
escapeStyle: "literal"
|
|
127
|
-
}]
|
|
128
|
-
};
|
|
125
|
+
return new TableCell({ children: [new Text({
|
|
126
|
+
value: text,
|
|
127
|
+
escapeStyle: "literal"
|
|
128
|
+
})] });
|
|
129
129
|
}
|
|
130
130
|
/**
|
|
131
131
|
* Create a table row from an array of cell texts.
|
|
132
132
|
*
|
|
133
133
|
* @param texts - Array of cell text values
|
|
134
|
-
* @returns
|
|
134
|
+
* @returns A kit `TableRow` node
|
|
135
135
|
*
|
|
136
136
|
* @internal
|
|
137
137
|
*/
|
|
138
138
|
function makeRow(texts) {
|
|
139
|
-
return {
|
|
140
|
-
type: "tableRow",
|
|
141
|
-
children: texts.map(makeCell)
|
|
142
|
-
};
|
|
139
|
+
return new TableRow({ children: texts.map(makeCell) });
|
|
143
140
|
}
|
|
144
141
|
/**
|
|
145
|
-
* Serialize dependency table rows into
|
|
142
|
+
* Serialize dependency table rows into a kit `Table` node.
|
|
146
143
|
*
|
|
147
144
|
* @remarks
|
|
148
145
|
* Creates a well-formed GFM table with the canonical header row
|
|
@@ -150,7 +147,7 @@ function makeRow(texts) {
|
|
|
150
147
|
* The inverse of {@link parseDependencyTable}.
|
|
151
148
|
*
|
|
152
149
|
* @param rows - Array of `DependencyTableRow` objects
|
|
153
|
-
* @returns
|
|
150
|
+
* @returns A kit `Table` node ready for insertion into a kit tree
|
|
154
151
|
*
|
|
155
152
|
* @example
|
|
156
153
|
* ```typescript
|
|
@@ -168,10 +165,9 @@ function makeRow(texts) {
|
|
|
168
165
|
* @internal
|
|
169
166
|
*/
|
|
170
167
|
function serializeDependencyTable(rows) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
};
|
|
168
|
+
const headerRow = makeRow([...COLUMN_HEADERS]);
|
|
169
|
+
const dataRows = rows.map((row) => makeRow(COLUMN_KEYS.map((key) => row[key])));
|
|
170
|
+
return new Table({ children: [headerRow, ...dataRows] });
|
|
175
171
|
}
|
|
176
172
|
/**
|
|
177
173
|
* Serialize dependency table rows to a markdown table string.
|
|
@@ -200,10 +196,7 @@ function serializeDependencyTable(rows) {
|
|
|
200
196
|
*/
|
|
201
197
|
function serializeDependencyTableToMarkdown(rows) {
|
|
202
198
|
const table = serializeDependencyTable(rows);
|
|
203
|
-
return
|
|
204
|
-
type: "root",
|
|
205
|
-
children: [table]
|
|
206
|
-
}).trim();
|
|
199
|
+
return emitKitMarkdown(new Root({ children: [table] })).trim();
|
|
207
200
|
}
|
|
208
201
|
/**
|
|
209
202
|
* Sort priority for dependency actions: removed first, then updated, then added.
|
|
@@ -7,8 +7,8 @@ import { Code, Markdown, MarkdownTransform, Mdast } from "@effected/markdown";
|
|
|
7
7
|
* into a defect.
|
|
8
8
|
*
|
|
9
9
|
* @remarks
|
|
10
|
-
* The trees this pipeline emits are either parsed by our own
|
|
11
|
-
*
|
|
10
|
+
* The trees this pipeline emits are either parsed by our own parser or
|
|
11
|
+
* synthesized by our own transforms, so a decode or stringify failure is a
|
|
12
12
|
* programmer error, not an operational condition — the sync call sites treat
|
|
13
13
|
* it as a defect with the typed error preserved as `cause`.
|
|
14
14
|
*
|
|
@@ -26,18 +26,17 @@ function unwrap(result, context) {
|
|
|
26
26
|
*
|
|
27
27
|
* @remarks
|
|
28
28
|
* The kit's canonical form emits a code node carrying neither `lang` nor
|
|
29
|
-
* `fenceChar` as an INDENTED block — and
|
|
30
|
-
*
|
|
31
|
-
* pass every language-less snippet in
|
|
29
|
+
* `fenceChar` as an INDENTED block — and that is exactly what a ``` fence with
|
|
30
|
+
* no info string becomes once {@link parseKitMarkdown}'s mdast round trip
|
|
31
|
+
* strips fidelity fields, so without this pass every language-less snippet in
|
|
32
|
+
* changelog content would change shape.
|
|
32
33
|
* Worse, the indented default is neighbor-dependent: the serializer takes a
|
|
33
34
|
* representability escape back to a backtick fence when the indented form
|
|
34
35
|
* would misparse (e.g. code immediately after a list), so the same node
|
|
35
36
|
* spells differently depending on what precedes it — inconsistent raw
|
|
36
37
|
* output. House policy is fenced code everywhere, so the ONE emit boundary defaults
|
|
37
|
-
* the kit's own per-node fidelity field, `fenceChar`,
|
|
38
|
-
*
|
|
39
|
-
* trees, so a `fenceChar` set on the plain tree would never survive the
|
|
40
|
-
* boundary. `MarkdownTransform.map` rebuilds only the paths to a changed
|
|
38
|
+
* the kit's own per-node fidelity field, `fenceChar`, at emit time.
|
|
39
|
+
* `MarkdownTransform.map` rebuilds only the paths to a changed
|
|
41
40
|
* code node; every other subtree is returned by reference.
|
|
42
41
|
*
|
|
43
42
|
* @internal
|
|
@@ -49,34 +48,11 @@ function ensureFencedCode(node) {
|
|
|
49
48
|
}) : code });
|
|
50
49
|
}
|
|
51
50
|
/**
|
|
52
|
-
* Serialize a plain MDAST tree to canonical markdown.
|
|
53
|
-
*
|
|
54
|
-
* @remarks
|
|
55
|
-
* The single emit chokepoint for the changesets pipeline: decodes the plain
|
|
56
|
-
* mdast tree into `@effected/markdown`'s node classes via
|
|
57
|
-
* `Mdast.fromMdastResult` and serializes it with `Markdown.stringifyResult`.
|
|
58
|
-
* Synchronous on purpose — every call site is a sync string pipeline — and
|
|
59
|
-
* failures (an undecodable node type, the nesting-depth hardening guard) are
|
|
60
|
-
* defects on self-built trees, so they throw rather than surface typed.
|
|
61
|
-
*
|
|
62
|
-
* The output always ends with exactly one trailing newline, matching the
|
|
63
|
-
* kit's document-level canonical rule.
|
|
64
|
-
*
|
|
65
|
-
* @param tree - The plain mdast root to serialize
|
|
66
|
-
* @returns Canonical markdown text
|
|
67
|
-
*
|
|
68
|
-
* @internal
|
|
69
|
-
*/
|
|
70
|
-
function emitMarkdown(tree) {
|
|
71
|
-
const fenced = ensureFencedCode(unwrap(Mdast.fromMdastResult(tree), "markdown emit: mdast tree failed to decode"));
|
|
72
|
-
return unwrap(Markdown.stringifyResult(fenced), "markdown emit: canonical stringify failed");
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
51
|
* Parse markdown text straight into `@effected/markdown`'s node classes (GFM
|
|
76
52
|
* dialect, frontmatter capture off).
|
|
77
53
|
*
|
|
78
54
|
* @remarks
|
|
79
|
-
* Synchronous
|
|
55
|
+
* Synchronous because every call site is a sync string pipeline; the kit's parser
|
|
80
56
|
* is total over text, so a failure here is a defect (the nesting-depth
|
|
81
57
|
* hardening guard), not an operational condition.
|
|
82
58
|
*
|
|
@@ -93,8 +69,7 @@ function parseKitMarkdown(text) {
|
|
|
93
69
|
* Serialize a kit root, or a single kit block node, to canonical markdown.
|
|
94
70
|
*
|
|
95
71
|
* @remarks
|
|
96
|
-
*
|
|
97
|
-
* then `Markdown.stringifyResult`. A lone block node emits byte-identically
|
|
72
|
+
* Applies the fence normalization, then `Markdown.stringifyResult`. A lone block node emits byte-identically
|
|
98
73
|
* to a one-child root, trailing newline included.
|
|
99
74
|
*
|
|
100
75
|
* @param node - The kit root or block node to serialize
|
|
@@ -106,4 +81,4 @@ function emitKitMarkdown(node) {
|
|
|
106
81
|
return unwrap(Markdown.stringifyResult(ensureFencedCode(node)), "markdown emit: canonical stringify failed");
|
|
107
82
|
}
|
|
108
83
|
|
|
109
|
-
export { emitKitMarkdown,
|
|
84
|
+
export { emitKitMarkdown, parseKitMarkdown };
|