@savvy-web/silk-effects 9.6.0 → 10.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +11 -18
- package/tsdoc-metadata.json +1 -1
- 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/silk-effects",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared Effect library for Silk Suite conventions",
|
|
6
6
|
"homepage": "https://github.com/savvy-web/systems/tree/main/packages/silk-effects",
|
|
@@ -39,28 +39,21 @@
|
|
|
39
39
|
"@changesets/read": "^1.0.1",
|
|
40
40
|
"@effected/github-references": "^0.11.0",
|
|
41
41
|
"@effected/glob": "^0.14.0",
|
|
42
|
-
"@effected/jsonc": "^0.
|
|
43
|
-
"@effected/markdown": "^0.
|
|
44
|
-
"@effected/package-json": "^0.
|
|
45
|
-
"@effected/toml": "^0.
|
|
46
|
-
"@effected/walker": "^0.
|
|
47
|
-
"@effected/yaml": "^0.
|
|
42
|
+
"@effected/jsonc": "^0.20.0",
|
|
43
|
+
"@effected/markdown": "^0.23.0",
|
|
44
|
+
"@effected/package-json": "^0.25.2",
|
|
45
|
+
"@effected/toml": "^0.16.0",
|
|
46
|
+
"@effected/walker": "^0.20.0",
|
|
47
|
+
"@effected/yaml": "^0.25.1",
|
|
48
48
|
"@manypkg/get-packages": "^3.1.0",
|
|
49
|
-
"@savvy-web/silk-core": "^0.5.
|
|
50
|
-
"
|
|
51
|
-
"remark-gfm": "^4.0.1",
|
|
52
|
-
"remark-parse": "^11.0.0",
|
|
53
|
-
"remark-stringify": "^11.0.0",
|
|
54
|
-
"shell-quote": "^1.12.0",
|
|
55
|
-
"unified": "^11.0.5",
|
|
56
|
-
"unified-lint-rule": "^3.0.1",
|
|
57
|
-
"unist-util-visit": "^5.1.0"
|
|
49
|
+
"@savvy-web/silk-core": "^0.5.1",
|
|
50
|
+
"shell-quote": "^1.12.0"
|
|
58
51
|
},
|
|
59
52
|
"peerDependencies": {
|
|
60
53
|
"@effected/commands": "^0.15.0",
|
|
61
|
-
"@effected/git": "^0.
|
|
54
|
+
"@effected/git": "^0.25.0",
|
|
62
55
|
"@effected/templates": "^0.14.0",
|
|
63
|
-
"@effected/workspaces": "^0.
|
|
56
|
+
"@effected/workspaces": "^0.38.0",
|
|
64
57
|
"effect": "^4.0.0"
|
|
65
58
|
}
|
|
66
59
|
}
|
package/tsdoc-metadata.json
CHANGED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { collapseDependencyRows, parseDependencyTable, serializeDependencyTable, sortDependencyRows } from "../../utils/dependency-table.js";
|
|
2
|
-
import { getBlockSections, getHeadingText, getVersionBlocks } from "../../utils/version-blocks.js";
|
|
3
|
-
|
|
4
|
-
// src/changesets/remark/plugins/aggregate-dependency-tables.ts
|
|
5
|
-
/**
|
|
6
|
-
* Unwrap dependency tables that an earlier (broken) formatter nested inside
|
|
7
|
-
* list items.
|
|
8
|
-
*
|
|
9
|
-
* @remarks
|
|
10
|
-
* Historical CHANGELOG blocks (e.g. silk\@3.10.0) carry an authored
|
|
11
|
-
* dependency table bullet-wrapped into a list item, which the aggregation
|
|
12
|
-
* pass otherwise cannot recognize. Each list item's tables that parse as
|
|
13
|
-
* dependency tables contribute their rows; ONLY those tables leave the
|
|
14
|
-
* list. The non-table items (e.g. an explanatory bullet alongside the
|
|
15
|
-
* wrapped table) keep their bullet structure: they survive as a List node
|
|
16
|
-
* in the legacy content rather than being flattened to bare paragraphs.
|
|
17
|
-
* When no table in the list parses, the caller keeps the whole list as
|
|
18
|
-
* legacy content untouched.
|
|
19
|
-
*
|
|
20
|
-
* @param list - A list node found inside a `### Dependencies` section
|
|
21
|
-
* @returns Parsed rows plus the remaining non-table content (a pruned copy
|
|
22
|
-
* of the list, when any items survive)
|
|
23
|
-
*
|
|
24
|
-
* @internal
|
|
25
|
-
*/
|
|
26
|
-
function extractTablesFromList(list) {
|
|
27
|
-
const rows = [];
|
|
28
|
-
const keptItems = [];
|
|
29
|
-
for (const item of list.children) {
|
|
30
|
-
const keptChildren = [];
|
|
31
|
-
for (const child of item.children) {
|
|
32
|
-
if (child.type === "table") try {
|
|
33
|
-
rows.push(...parseDependencyTable(child));
|
|
34
|
-
continue;
|
|
35
|
-
} catch {}
|
|
36
|
-
keptChildren.push(child);
|
|
37
|
-
}
|
|
38
|
-
if (keptChildren.length > 0) keptItems.push({
|
|
39
|
-
...item,
|
|
40
|
-
children: keptChildren
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
rows,
|
|
45
|
-
rest: keptItems.length > 0 ? [{
|
|
46
|
-
...list,
|
|
47
|
-
children: keptItems
|
|
48
|
-
}] : []
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
const AggregateDependencyTablesPlugin = () => {
|
|
52
|
-
return (tree) => {
|
|
53
|
-
const blocks = getVersionBlocks(tree);
|
|
54
|
-
for (let b = blocks.length - 1; b >= 0; b--) {
|
|
55
|
-
const depSections = getBlockSections(tree, blocks[b]).filter((s) => getHeadingText(s.heading).toLowerCase() === "dependencies");
|
|
56
|
-
if (depSections.length === 0) continue;
|
|
57
|
-
const allRows = [];
|
|
58
|
-
const legacyContent = [];
|
|
59
|
-
for (const section of depSections) for (const node of section.contentNodes) if (node.type === "table") try {
|
|
60
|
-
const rows = parseDependencyTable(node);
|
|
61
|
-
allRows.push(...rows);
|
|
62
|
-
} catch {
|
|
63
|
-
legacyContent.push(node);
|
|
64
|
-
}
|
|
65
|
-
else if (node.type === "list") {
|
|
66
|
-
const extracted = extractTablesFromList(node);
|
|
67
|
-
if (extracted.rows.length > 0) {
|
|
68
|
-
allRows.push(...extracted.rows);
|
|
69
|
-
legacyContent.push(...extracted.rest);
|
|
70
|
-
} else legacyContent.push(node);
|
|
71
|
-
} else legacyContent.push(node);
|
|
72
|
-
const collapsed = sortDependencyRows(collapseDependencyRows(allRows));
|
|
73
|
-
const indicesToRemove = [];
|
|
74
|
-
for (const section of depSections) {
|
|
75
|
-
indicesToRemove.push(section.headingIndex);
|
|
76
|
-
for (let c = 0; c < section.contentNodes.length; c++) indicesToRemove.push(section.headingIndex + 1 + c);
|
|
77
|
-
}
|
|
78
|
-
indicesToRemove.sort((a, b) => b - a);
|
|
79
|
-
for (const idx of indicesToRemove) tree.children.splice(idx, 1);
|
|
80
|
-
if (collapsed.length === 0 && legacyContent.length === 0) continue;
|
|
81
|
-
const insertAt = depSections[0].headingIndex;
|
|
82
|
-
const newNodes = [];
|
|
83
|
-
newNodes.push({
|
|
84
|
-
type: "heading",
|
|
85
|
-
depth: 3,
|
|
86
|
-
children: [{
|
|
87
|
-
type: "text",
|
|
88
|
-
value: "Dependencies"
|
|
89
|
-
}]
|
|
90
|
-
});
|
|
91
|
-
if (collapsed.length > 0) newNodes.push(serializeDependencyTable(collapsed));
|
|
92
|
-
newNodes.push(...legacyContent);
|
|
93
|
-
tree.children.splice(insertAt, 0, ...newNodes);
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
export { AggregateDependencyTablesPlugin };
|
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
import { getVersionBlocks } from "../../utils/version-blocks.js";
|
|
2
|
-
import { visit } from "unist-util-visit";
|
|
3
|
-
|
|
4
|
-
// src/changesets/remark/plugins/contributor-footnotes.ts
|
|
5
|
-
/**
|
|
6
|
-
* Pattern matching `Thanks @user!` at end of text.
|
|
7
|
-
*
|
|
8
|
-
* @internal
|
|
9
|
-
*/
|
|
10
|
-
const ATTRIBUTION_PLAIN_RE = /\s*Thanks @(\w[\w-]*)!$/;
|
|
11
|
-
/**
|
|
12
|
-
* Pattern matching `@user` mentions inside an existing Thanks section.
|
|
13
|
-
*
|
|
14
|
-
* @internal
|
|
15
|
-
*/
|
|
16
|
-
const MENTION_RE = /@(\w[\w-]*)/g;
|
|
17
|
-
/**
|
|
18
|
-
* Try to extract a linked attribution from the end of a paragraph's children.
|
|
19
|
-
* Pattern: text "...Thanks " + link "\@user" + text "!"
|
|
20
|
-
*
|
|
21
|
-
* @param children - The phrasing content children of a paragraph node
|
|
22
|
-
* @returns The contributor and the index to start removal from, or `undefined`
|
|
23
|
-
*
|
|
24
|
-
* @internal
|
|
25
|
-
*/
|
|
26
|
-
function extractLinkedAttribution(children) {
|
|
27
|
-
if (children.length < 3) return void 0;
|
|
28
|
-
const last = children[children.length - 1];
|
|
29
|
-
const secondLast = children[children.length - 2];
|
|
30
|
-
const thirdLast = children[children.length - 3];
|
|
31
|
-
if (last.type !== "text" || last.value !== "!") return void 0;
|
|
32
|
-
if (secondLast.type !== "link") return void 0;
|
|
33
|
-
const linkNode = secondLast;
|
|
34
|
-
if (linkNode.children.length !== 1 || linkNode.children[0].type !== "text") return void 0;
|
|
35
|
-
const linkText = linkNode.children[0].value;
|
|
36
|
-
if (!linkText.startsWith("@")) return void 0;
|
|
37
|
-
const username = linkText.slice(1);
|
|
38
|
-
if (thirdLast.type !== "text") return void 0;
|
|
39
|
-
const textNode = thirdLast;
|
|
40
|
-
if (!/\s*Thanks $/.test(textNode.value)) return void 0;
|
|
41
|
-
return {
|
|
42
|
-
contributor: {
|
|
43
|
-
username,
|
|
44
|
-
url: linkNode.url
|
|
45
|
-
},
|
|
46
|
-
removeFrom: children.length - 3
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Strip a trailing attribution (linked or plain) from a paragraph, adding
|
|
51
|
-
* the contributor to the map.
|
|
52
|
-
*
|
|
53
|
-
* @param para - The paragraph to inspect and mutate
|
|
54
|
-
* @param contributors - The per-block contributor accumulator
|
|
55
|
-
* @returns `true` when an attribution was found and removed
|
|
56
|
-
*
|
|
57
|
-
* @internal
|
|
58
|
-
*/
|
|
59
|
-
function stripAttribution(para, contributors) {
|
|
60
|
-
const linked = extractLinkedAttribution(para.children);
|
|
61
|
-
if (linked) {
|
|
62
|
-
const key = linked.contributor.username.toLowerCase();
|
|
63
|
-
if (!contributors.has(key)) contributors.set(key, linked.contributor);
|
|
64
|
-
const textNode = para.children[linked.removeFrom];
|
|
65
|
-
textNode.value = textNode.value.replace(/\s*Thanks $/, "");
|
|
66
|
-
para.children.splice(linked.removeFrom + 1, 2);
|
|
67
|
-
if (textNode.value === "") para.children.splice(linked.removeFrom, 1);
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
const last = para.children[para.children.length - 1];
|
|
71
|
-
if (last?.type === "text") {
|
|
72
|
-
const textNode = last;
|
|
73
|
-
const match = textNode.value.match(ATTRIBUTION_PLAIN_RE);
|
|
74
|
-
if (match) {
|
|
75
|
-
const username = match[1];
|
|
76
|
-
const key = username.toLowerCase();
|
|
77
|
-
if (!contributors.has(key)) contributors.set(key, {
|
|
78
|
-
username,
|
|
79
|
-
url: void 0
|
|
80
|
-
});
|
|
81
|
-
textNode.value = textNode.value.replace(ATTRIBUTION_PLAIN_RE, "");
|
|
82
|
-
if (textNode.value === "") para.children.pop();
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Harvest every `@user` mention (linked or plain) from a node subtree.
|
|
90
|
-
* Used to absorb an existing `### Thanks` section so re-running the plugin
|
|
91
|
-
* is idempotent.
|
|
92
|
-
*
|
|
93
|
-
* @internal
|
|
94
|
-
*/
|
|
95
|
-
function harvestMentions(node, contributors) {
|
|
96
|
-
visit(node, (child) => {
|
|
97
|
-
if (child.type === "link") {
|
|
98
|
-
const link = child;
|
|
99
|
-
const only = link.children.length === 1 ? link.children[0] : void 0;
|
|
100
|
-
if (only?.type === "text" && only.value.startsWith("@")) {
|
|
101
|
-
const username = only.value.slice(1);
|
|
102
|
-
const key = username.toLowerCase();
|
|
103
|
-
if (!contributors.has(key)) contributors.set(key, {
|
|
104
|
-
username,
|
|
105
|
-
url: link.url
|
|
106
|
-
});
|
|
107
|
-
return "skip";
|
|
108
|
-
}
|
|
109
|
-
} else if (child.type === "text") for (const match of child.value.matchAll(MENTION_RE)) {
|
|
110
|
-
const key = match[1].toLowerCase();
|
|
111
|
-
if (!contributors.has(key)) contributors.set(key, {
|
|
112
|
-
username: match[1],
|
|
113
|
-
url: void 0
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Build the `Thanks to ... for their contributions!` summary paragraph.
|
|
120
|
-
*
|
|
121
|
-
* @internal
|
|
122
|
-
*/
|
|
123
|
-
function buildSummaryParagraph(contributors) {
|
|
124
|
-
const sorted = [...contributors.values()].sort((a, b) => a.username.toLowerCase().localeCompare(b.username.toLowerCase()));
|
|
125
|
-
const phrasingChildren = [];
|
|
126
|
-
phrasingChildren.push({
|
|
127
|
-
type: "text",
|
|
128
|
-
value: "Thanks to "
|
|
129
|
-
});
|
|
130
|
-
for (let i = 0; i < sorted.length; i++) {
|
|
131
|
-
const contrib = sorted[i];
|
|
132
|
-
if (i > 0 && sorted.length > 2) phrasingChildren.push({
|
|
133
|
-
type: "text",
|
|
134
|
-
value: ", "
|
|
135
|
-
});
|
|
136
|
-
if (i > 0 && i === sorted.length - 1) phrasingChildren.push({
|
|
137
|
-
type: "text",
|
|
138
|
-
value: sorted.length === 2 ? " and " : "and "
|
|
139
|
-
});
|
|
140
|
-
if (contrib.url) phrasingChildren.push({
|
|
141
|
-
type: "link",
|
|
142
|
-
url: contrib.url,
|
|
143
|
-
children: [{
|
|
144
|
-
type: "text",
|
|
145
|
-
value: `@${contrib.username}`
|
|
146
|
-
}]
|
|
147
|
-
});
|
|
148
|
-
else phrasingChildren.push({
|
|
149
|
-
type: "text",
|
|
150
|
-
value: `@${contrib.username}`
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
phrasingChildren.push({
|
|
154
|
-
type: "text",
|
|
155
|
-
value: " for their contributions!"
|
|
156
|
-
});
|
|
157
|
-
return {
|
|
158
|
-
type: "paragraph",
|
|
159
|
-
children: phrasingChildren
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Remove list items (recursively) whose content was emptied by attribution
|
|
164
|
-
* stripping — a bullet that held ONLY `Thanks @user!` must not survive as a
|
|
165
|
-
* bare `- ` husk. Runs in both the `thanks: true` and `thanks: false` paths,
|
|
166
|
-
* since stripping is unconditional.
|
|
167
|
-
*
|
|
168
|
-
* @internal
|
|
169
|
-
*/
|
|
170
|
-
function pruneEmptiedListItems(list) {
|
|
171
|
-
list.children = list.children.filter((item) => {
|
|
172
|
-
item.children = item.children.filter((child) => {
|
|
173
|
-
if (child.type === "paragraph") return child.children.length > 0;
|
|
174
|
-
if (child.type === "list") {
|
|
175
|
-
const nested = child;
|
|
176
|
-
pruneEmptiedListItems(nested);
|
|
177
|
-
return nested.children.length > 0;
|
|
178
|
-
}
|
|
179
|
-
return true;
|
|
180
|
-
});
|
|
181
|
-
return item.children.length > 0;
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Text form of a paragraph: concatenated text values, with single-text link
|
|
186
|
-
* children flattened to their label. Returns `undefined` when the paragraph
|
|
187
|
-
* holds anything other than text and simple text-labelled links.
|
|
188
|
-
*
|
|
189
|
-
* @internal
|
|
190
|
-
*/
|
|
191
|
-
function flattenToText(para) {
|
|
192
|
-
let out = "";
|
|
193
|
-
for (const child of para.children) {
|
|
194
|
-
if (child.type === "text") {
|
|
195
|
-
out += child.value;
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
if (child.type === "link") {
|
|
199
|
-
const link = child;
|
|
200
|
-
const only = link.children.length === 1 ? link.children[0] : void 0;
|
|
201
|
-
if (only?.type === "text") {
|
|
202
|
-
out += only.value;
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
return out;
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Pattern matching the plugin's own merged summary paragraph, e.g.
|
|
212
|
-
* `Thanks to @alice, @bob, and @carol for their contributions!`.
|
|
213
|
-
*
|
|
214
|
-
* @internal
|
|
215
|
-
*/
|
|
216
|
-
const MERGED_SUMMARY_RE = /^Thanks to @\w[\w-]*(?:(?:, | and |, and |,and )@\w[\w-]*)* for their contributions!$/;
|
|
217
|
-
/**
|
|
218
|
-
* Whether a paragraph consists ENTIRELY of attribution content the plugin
|
|
219
|
-
* owns: either its own merged `Thanks to ... for their contributions!`
|
|
220
|
-
* summary, or one or more trailing `Thanks @user!` / `Thanks [@user](url)!`
|
|
221
|
-
* attributions with nothing else. Checked against a clone so the real node
|
|
222
|
-
* is never mutated — a paragraph carrying anything beyond pure attribution
|
|
223
|
-
* is not the plugin's to touch.
|
|
224
|
-
*
|
|
225
|
-
* @internal
|
|
226
|
-
*/
|
|
227
|
-
function isPureAttributionParagraph(para) {
|
|
228
|
-
const text = flattenToText(para);
|
|
229
|
-
if (text !== void 0 && MERGED_SUMMARY_RE.test(text)) return true;
|
|
230
|
-
const clone = structuredClone(para);
|
|
231
|
-
const scratch = /* @__PURE__ */ new Map();
|
|
232
|
-
while (clone.children.length > 0 && stripAttribution(clone, scratch));
|
|
233
|
-
return clone.children.length === 0;
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Whether a Thanks-section body node is a pure attribution shape the plugin
|
|
237
|
-
* may harvest and remove: an attribution-only paragraph, or a list whose
|
|
238
|
-
* every item holds only attribution-only paragraphs. Anything else
|
|
239
|
-
* (mixed prose+mention, plain-name lists, code blocks, arbitrary prose) is
|
|
240
|
-
* preserved untouched and never mined for mentions.
|
|
241
|
-
*
|
|
242
|
-
* @internal
|
|
243
|
-
*/
|
|
244
|
-
function isPureAttributionNode(node) {
|
|
245
|
-
if (node.type === "paragraph") return isPureAttributionParagraph(node);
|
|
246
|
-
if (node.type === "list") {
|
|
247
|
-
const list = node;
|
|
248
|
-
return list.children.length > 0 && list.children.every((item) => item.children.length > 0 && item.children.every((child) => child.type === "paragraph" && isPureAttributionParagraph(child)));
|
|
249
|
-
}
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Whether a node is a depth-3 heading whose text is `Thanks`.
|
|
254
|
-
*
|
|
255
|
-
* @internal
|
|
256
|
-
*/
|
|
257
|
-
function isThanksHeading(node) {
|
|
258
|
-
if (node.type !== "heading" || node.depth !== 3) return false;
|
|
259
|
-
const heading = node;
|
|
260
|
-
const only = heading.children.length === 1 ? heading.children[0] : void 0;
|
|
261
|
-
return only?.type === "text" && only.value.trim().toLowerCase() === "thanks";
|
|
262
|
-
}
|
|
263
|
-
const ContributorFootnotesPlugin = (options) => {
|
|
264
|
-
const emitThanks = options?.thanks !== false;
|
|
265
|
-
return (tree) => {
|
|
266
|
-
const blocks = getVersionBlocks(tree);
|
|
267
|
-
for (let b = blocks.length - 1; b >= 0; b--) {
|
|
268
|
-
const block = blocks[b];
|
|
269
|
-
const contributors = /* @__PURE__ */ new Map();
|
|
270
|
-
const indicesToRemove = [];
|
|
271
|
-
let preservedThanksAnchor;
|
|
272
|
-
for (let i = block.startIndex; i < block.endIndex; i++) {
|
|
273
|
-
const node = tree.children[i];
|
|
274
|
-
if (node.type === "list") {
|
|
275
|
-
const listNode = node;
|
|
276
|
-
visit(listNode, "paragraph", (para) => {
|
|
277
|
-
stripAttribution(para, contributors);
|
|
278
|
-
});
|
|
279
|
-
pruneEmptiedListItems(listNode);
|
|
280
|
-
if (listNode.children.length === 0) indicesToRemove.push(i);
|
|
281
|
-
continue;
|
|
282
|
-
}
|
|
283
|
-
if (node.type === "paragraph") {
|
|
284
|
-
const para = node;
|
|
285
|
-
if (stripAttribution(para, contributors) && para.children.length === 0) indicesToRemove.push(i);
|
|
286
|
-
continue;
|
|
287
|
-
}
|
|
288
|
-
if (isThanksHeading(node)) {
|
|
289
|
-
const headingIndex = i;
|
|
290
|
-
const harvested = [];
|
|
291
|
-
let lastPreserved;
|
|
292
|
-
let j = i + 1;
|
|
293
|
-
for (; j < block.endIndex; j++) {
|
|
294
|
-
const contentNode = tree.children[j];
|
|
295
|
-
if (contentNode.type === "heading" && [2, 3].includes(contentNode.depth)) break;
|
|
296
|
-
if (contentNode.type === "definition") continue;
|
|
297
|
-
if (isPureAttributionNode(contentNode)) {
|
|
298
|
-
harvestMentions(contentNode, contributors);
|
|
299
|
-
harvested.push(j);
|
|
300
|
-
} else lastPreserved = contentNode;
|
|
301
|
-
}
|
|
302
|
-
if (lastPreserved === void 0) indicesToRemove.push(headingIndex, ...harvested);
|
|
303
|
-
else {
|
|
304
|
-
indicesToRemove.push(...harvested);
|
|
305
|
-
preservedThanksAnchor ??= lastPreserved;
|
|
306
|
-
}
|
|
307
|
-
i = j - 1;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
const uniqueIndices = [...new Set(indicesToRemove)].sort((a, b) => b - a);
|
|
311
|
-
for (const idx of uniqueIndices) tree.children.splice(idx, 1);
|
|
312
|
-
if (!emitThanks || contributors.size === 0) continue;
|
|
313
|
-
if (preservedThanksAnchor !== void 0) {
|
|
314
|
-
const anchorIndex = tree.children.indexOf(preservedThanksAnchor);
|
|
315
|
-
if (anchorIndex !== -1) {
|
|
316
|
-
tree.children.splice(anchorIndex + 1, 0, buildSummaryParagraph(contributors));
|
|
317
|
-
continue;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
let insertAt = block.endIndex - uniqueIndices.length;
|
|
321
|
-
while (insertAt > block.startIndex && tree.children[insertAt - 1]?.type === "definition") insertAt--;
|
|
322
|
-
tree.children.splice(insertAt, 0, {
|
|
323
|
-
type: "heading",
|
|
324
|
-
depth: 3,
|
|
325
|
-
children: [{
|
|
326
|
-
type: "text",
|
|
327
|
-
value: "Thanks"
|
|
328
|
-
}]
|
|
329
|
-
}, buildSummaryParagraph(contributors));
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
export { ContributorFootnotesPlugin };
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { getBlockSections, getVersionBlocks } from "../../utils/version-blocks.js";
|
|
2
|
-
import { toString } from "mdast-util-to-string";
|
|
3
|
-
|
|
4
|
-
// src/changesets/remark/plugins/deduplicate-items.ts
|
|
5
|
-
const DeduplicateItemsPlugin = () => {
|
|
6
|
-
return (tree) => {
|
|
7
|
-
const blocks = getVersionBlocks(tree);
|
|
8
|
-
for (const block of blocks) {
|
|
9
|
-
const sections = getBlockSections(tree, block);
|
|
10
|
-
for (const section of sections) {
|
|
11
|
-
const seen = /* @__PURE__ */ new Set();
|
|
12
|
-
for (const node of section.contentNodes) {
|
|
13
|
-
if (node.type !== "list") continue;
|
|
14
|
-
const list = node;
|
|
15
|
-
list.children = list.children.filter((item) => {
|
|
16
|
-
const text = toString(item);
|
|
17
|
-
if (seen.has(text)) return false;
|
|
18
|
-
seen.add(text);
|
|
19
|
-
return true;
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
tree.children = tree.children.filter((node) => {
|
|
25
|
-
if (node.type === "list") return node.children.length > 0;
|
|
26
|
-
return true;
|
|
27
|
-
});
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export { DeduplicateItemsPlugin };
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { getVersionBlocks } from "../../utils/version-blocks.js";
|
|
2
|
-
import { SKIP, visit } from "unist-util-visit";
|
|
3
|
-
|
|
4
|
-
// src/changesets/remark/plugins/issue-link-refs.ts
|
|
5
|
-
/**
|
|
6
|
-
* Pattern matching issue numbers like `#123`.
|
|
7
|
-
*
|
|
8
|
-
* @internal
|
|
9
|
-
*/
|
|
10
|
-
const ISSUE_RE = /^#\d+$/;
|
|
11
|
-
const IssueLinkRefsPlugin = () => {
|
|
12
|
-
return (tree) => {
|
|
13
|
-
const blocks = getVersionBlocks(tree);
|
|
14
|
-
for (let b = blocks.length - 1; b >= 0; b--) {
|
|
15
|
-
const block = blocks[b];
|
|
16
|
-
const definitions = /* @__PURE__ */ new Map();
|
|
17
|
-
for (let i = block.startIndex; i < block.endIndex; i++) {
|
|
18
|
-
const node = tree.children[i];
|
|
19
|
-
visit(node, "link", (linkNode, index, parent) => {
|
|
20
|
-
if (!parent || index === void 0) return;
|
|
21
|
-
if (linkNode.children.length !== 1 || linkNode.children[0].type !== "text") return;
|
|
22
|
-
const text = linkNode.children[0].value;
|
|
23
|
-
if (!ISSUE_RE.test(text)) return;
|
|
24
|
-
const label = text;
|
|
25
|
-
if (!definitions.has(label)) definitions.set(label, {
|
|
26
|
-
label,
|
|
27
|
-
url: linkNode.url
|
|
28
|
-
});
|
|
29
|
-
const ref = {
|
|
30
|
-
type: "linkReference",
|
|
31
|
-
identifier: label,
|
|
32
|
-
label,
|
|
33
|
-
referenceType: "full",
|
|
34
|
-
children: [{
|
|
35
|
-
type: "text",
|
|
36
|
-
value: label
|
|
37
|
-
}]
|
|
38
|
-
};
|
|
39
|
-
parent.children[index] = ref;
|
|
40
|
-
return SKIP;
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
if (definitions.size === 0) continue;
|
|
44
|
-
const defNodes = [...definitions.values()].sort((a, b) => {
|
|
45
|
-
return Number.parseInt(a.label.slice(1), 10) - Number.parseInt(b.label.slice(1), 10);
|
|
46
|
-
}).map((def) => ({
|
|
47
|
-
type: "definition",
|
|
48
|
-
identifier: def.label,
|
|
49
|
-
label: def.label,
|
|
50
|
-
url: def.url
|
|
51
|
-
}));
|
|
52
|
-
tree.children.splice(block.endIndex, 0, ...defNodes);
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export { IssueLinkRefsPlugin };
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { getHeadingText, getVersionBlocks } from "../../utils/version-blocks.js";
|
|
2
|
-
import { toString } from "mdast-util-to-string";
|
|
3
|
-
|
|
4
|
-
// src/changesets/remark/plugins/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. Older
|
|
8
|
-
* engine versions left such a block structurally empty.
|
|
9
|
-
*/
|
|
10
|
-
const ENGINE_PLACEHOLDER = "No changes in this release.";
|
|
11
|
-
/** True when the block's only content is the engine's empty-release placeholder. */
|
|
12
|
-
function isPlaceholderOnly(tree, block) {
|
|
13
|
-
if (block.endIndex - block.startIndex !== 1) return false;
|
|
14
|
-
const node = tree.children[block.startIndex];
|
|
15
|
-
return node.type === "paragraph" && toString(node).trim() === ENGINE_PLACEHOLDER;
|
|
16
|
-
}
|
|
17
|
-
function buildNoteChildren(reason) {
|
|
18
|
-
if (reason.kind === "unspecified" || reason.triggers.length === 0) return [{
|
|
19
|
-
type: "text",
|
|
20
|
-
value: "Version-only release to keep workspace versions consistent; no changes to this package."
|
|
21
|
-
}];
|
|
22
|
-
const label = reason.kind === "fixed" ? "fixed version group" : "linked version group";
|
|
23
|
-
const children = [{
|
|
24
|
-
type: "text",
|
|
25
|
-
value: "Released in lockstep with "
|
|
26
|
-
}];
|
|
27
|
-
reason.triggers.forEach((trigger, index) => {
|
|
28
|
-
if (index > 0) children.push({
|
|
29
|
-
type: "text",
|
|
30
|
-
value: ", "
|
|
31
|
-
});
|
|
32
|
-
children.push({
|
|
33
|
-
type: "inlineCode",
|
|
34
|
-
value: `${trigger.name}@${trigger.version}`
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
children.push({
|
|
38
|
-
type: "text",
|
|
39
|
-
value: ` (${label}).`
|
|
40
|
-
});
|
|
41
|
-
return children;
|
|
42
|
-
}
|
|
43
|
-
const MaintenanceNotePlugin = (options) => {
|
|
44
|
-
return (tree) => {
|
|
45
|
-
const block = getVersionBlocks(tree).find((b) => getHeadingText(tree.children[b.headingIndex]) === options.version);
|
|
46
|
-
if (!block) return;
|
|
47
|
-
const placeholderOnly = isPlaceholderOnly(tree, block);
|
|
48
|
-
if (block.endIndex > block.startIndex && !placeholderOnly) return;
|
|
49
|
-
const heading = {
|
|
50
|
-
type: "heading",
|
|
51
|
-
depth: 3,
|
|
52
|
-
children: [{
|
|
53
|
-
type: "text",
|
|
54
|
-
value: "Maintenance"
|
|
55
|
-
}]
|
|
56
|
-
};
|
|
57
|
-
const list = {
|
|
58
|
-
type: "list",
|
|
59
|
-
ordered: false,
|
|
60
|
-
spread: false,
|
|
61
|
-
children: [{
|
|
62
|
-
type: "listItem",
|
|
63
|
-
spread: false,
|
|
64
|
-
children: [{
|
|
65
|
-
type: "paragraph",
|
|
66
|
-
children: buildNoteChildren(options.reason)
|
|
67
|
-
}]
|
|
68
|
-
}]
|
|
69
|
-
};
|
|
70
|
-
tree.children.splice(block.startIndex, placeholderOnly ? 1 : 0, heading, list);
|
|
71
|
-
};
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export { MaintenanceNotePlugin };
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { fromHeading } from "../../categories/index.js";
|
|
2
|
-
import { getBlockSections, getHeadingText, getVersionBlocks } from "../../utils/version-blocks.js";
|
|
3
|
-
|
|
4
|
-
// src/changesets/remark/plugins/merge-sections.ts
|
|
5
|
-
const MergeSectionsPlugin = () => {
|
|
6
|
-
return (tree) => {
|
|
7
|
-
const blocks = getVersionBlocks(tree);
|
|
8
|
-
for (let b = blocks.length - 1; b >= 0; b--) {
|
|
9
|
-
const sections = getBlockSections(tree, blocks[b]);
|
|
10
|
-
if (sections.length <= 1) continue;
|
|
11
|
-
const groups = /* @__PURE__ */ new Map();
|
|
12
|
-
for (let s = 0; s < sections.length; s++) {
|
|
13
|
-
const text = getHeadingText(sections[s].heading);
|
|
14
|
-
const category = fromHeading(text);
|
|
15
|
-
const key = category ? category.heading.toLowerCase() : text.toLowerCase();
|
|
16
|
-
const existing = groups.get(key);
|
|
17
|
-
if (existing) existing.push(s);
|
|
18
|
-
else groups.set(key, [s]);
|
|
19
|
-
}
|
|
20
|
-
const indicesToRemove = [];
|
|
21
|
-
for (const indices of groups.values()) {
|
|
22
|
-
if (indices.length <= 1) continue;
|
|
23
|
-
const target = sections[indices[0]];
|
|
24
|
-
const targetEndIndex = target.headingIndex + 1 + target.contentNodes.length;
|
|
25
|
-
const mergedContent = [];
|
|
26
|
-
for (let d = 1; d < indices.length; d++) {
|
|
27
|
-
const dup = sections[indices[d]];
|
|
28
|
-
mergedContent.push(...dup.contentNodes);
|
|
29
|
-
indicesToRemove.push(dup.headingIndex);
|
|
30
|
-
for (let c = 0; c < dup.contentNodes.length; c++) indicesToRemove.push(dup.headingIndex + 1 + c);
|
|
31
|
-
}
|
|
32
|
-
tree.children.splice(targetEndIndex, 0, ...mergedContent);
|
|
33
|
-
const shift = mergedContent.length;
|
|
34
|
-
for (let r = 0; r < indicesToRemove.length; r++) if (indicesToRemove[r] >= targetEndIndex) indicesToRemove[r] += shift;
|
|
35
|
-
}
|
|
36
|
-
indicesToRemove.sort((a, b) => b - a);
|
|
37
|
-
for (const idx of indicesToRemove) tree.children.splice(idx, 1);
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export { MergeSectionsPlugin };
|