@savvy-web/silk 1.3.10 → 1.3.11

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.
Files changed (30) hide show
  1. package/changesets-changelog.cjs +371 -220
  2. package/changesets-changelog.d.cts +120 -29
  3. package/changesets-changelog.d.ts +120 -29
  4. package/changesets-markdownlint.cjs +371 -220
  5. package/changesets-markdownlint.d.cts +120 -29
  6. package/changesets-markdownlint.d.ts +120 -29
  7. package/package.json +11 -11
  8. package/packages/silk-effects/dist/dev/pkg/changesets/api/transformer.cjs +24 -30
  9. package/packages/silk-effects/dist/dev/pkg/changesets/api/transformer.js +24 -30
  10. package/packages/silk-effects/dist/dev/pkg/changesets/changelog/formatting.cjs +4 -10
  11. package/packages/silk-effects/dist/dev/pkg/changesets/changelog/formatting.js +4 -10
  12. package/packages/silk-effects/dist/dev/pkg/changesets/changelog/getDependencyReleaseLine.cjs +13 -9
  13. package/packages/silk-effects/dist/dev/pkg/changesets/changelog/getDependencyReleaseLine.js +13 -9
  14. package/packages/silk-effects/dist/dev/pkg/changesets/changelog/getReleaseLine.cjs +8 -8
  15. package/packages/silk-effects/dist/dev/pkg/changesets/changelog/getReleaseLine.js +8 -8
  16. package/packages/silk-effects/dist/dev/pkg/changesets/changelog/index.cjs +1 -1
  17. package/packages/silk-effects/dist/dev/pkg/changesets/changelog/index.js +1 -1
  18. package/packages/silk-effects/dist/dev/pkg/changesets/index.cjs +12 -2
  19. package/packages/silk-effects/dist/dev/pkg/changesets/index.js +9 -3
  20. package/packages/silk-effects/dist/dev/pkg/changesets/remark/plugins/deduplicate-items.cjs +11 -9
  21. package/packages/silk-effects/dist/dev/pkg/changesets/remark/plugins/deduplicate-items.js +11 -9
  22. package/packages/silk-effects/dist/dev/pkg/changesets/remark/plugins/maintenance-note.cjs +61 -0
  23. package/packages/silk-effects/dist/dev/pkg/changesets/remark/plugins/maintenance-note.js +61 -0
  24. package/packages/silk-effects/dist/dev/pkg/changesets/remark/presets.cjs +1 -1
  25. package/packages/silk-effects/dist/dev/pkg/changesets/remark/presets.js +1 -1
  26. package/packages/silk-effects/dist/dev/pkg/changesets/services/maintenance-reason.cjs +68 -0
  27. package/packages/silk-effects/dist/dev/pkg/changesets/services/maintenance-reason.js +66 -0
  28. package/packages/silk-effects/dist/dev/pkg/changesets/services/release-planner.cjs +44 -12
  29. package/packages/silk-effects/dist/dev/pkg/changesets/services/release-planner.js +45 -13
  30. package/tsconfig/node/dist/tsconfig.tsbuildinfo +1 -1
@@ -70011,11 +70011,14 @@ const GitHubInfoSchema = effect.Schema.Struct({
70011
70011
  * handles the "Updated dependencies" section that Changesets appends
70012
70012
  * when a package's dependencies are bumped as part of a release.
70013
70013
  *
70014
- * The output is a GFM (GitHub Flavored Markdown) table with columns:
70015
- * `Dependency`, `Type`, `Action`, `From`, `To`. The dependency type
70016
- * is inferred from the consuming package's `package.json` fields
70017
- * (`dependencies`, `devDependencies`, `peerDependencies`,
70018
- * `optionalDependencies`) via the {@link inferDependencyType} helper.
70014
+ * The output is a `### Dependencies` h3 heading followed by a GFM
70015
+ * (GitHub Flavored Markdown) table with columns: `Dependency`, `Type`,
70016
+ * `Action`, `From`, `To`. The dependency type is inferred from the
70017
+ * consuming package's `package.json` fields (`dependencies`,
70018
+ * `devDependencies`, `peerDependencies`, `optionalDependencies`) via
70019
+ * the {@link inferDependencyType} helper. Downstream, the heading is
70020
+ * consumed by `AggregateDependencyTablesPlugin`, which locates and
70021
+ * merges per-package dependency tables during changelog assembly.
70019
70022
  *
70020
70023
  * ### Dependency type inference
70021
70024
  *
@@ -70081,7 +70084,8 @@ function inferDependencyType(dep) {
70081
70084
  * The function maps each `ModCompWithPackage` entry to a `DependencyTableRow`,
70082
70085
  * inferring the dependency type from the consuming package's `package.json`,
70083
70086
  * then delegates to `serializeDependencyTableToMarkdown` for GFM table
70084
- * rendering. Returns an empty string when no dependencies were updated.
70087
+ * rendering, prefixed with a `### Dependencies` heading. Returns an empty
70088
+ * string when no dependencies were updated.
70085
70089
  *
70086
70090
  * The `_changesets` and `_options` parameters are part of the Changesets API
70087
70091
  * contract but are not used in the table format. They are retained for
@@ -70090,19 +70094,19 @@ function inferDependencyType(dep) {
70090
70094
  * @param _changesets - Changesets that caused the dependency updates (unused in table format)
70091
70095
  * @param dependenciesUpdated - The list of dependencies that were updated, including old/new versions
70092
70096
  * @param _options - Validated configuration options (unused in table format)
70093
- * @returns An `Effect` that resolves to a formatted markdown table string, or empty string if no dependencies were updated
70097
+ * @returns An `Effect` that resolves to a `### Dependencies` heading followed by a formatted markdown table string, or empty string if no dependencies were updated
70094
70098
  */
70095
70099
  function getDependencyReleaseLine(_changesets, dependenciesUpdated, _options) {
70096
70100
  return effect.Effect.gen(function* () {
70097
70101
  if (dependenciesUpdated.length === 0) return "";
70098
70102
  yield* GitHubService;
70099
- return serializeDependencyTableToMarkdown(dependenciesUpdated.map((dep) => ({
70103
+ return `### Dependencies\n\n${serializeDependencyTableToMarkdown(dependenciesUpdated.map((dep) => ({
70100
70104
  dependency: dep.name,
70101
70105
  type: inferDependencyType(dep),
70102
70106
  action: "updated",
70103
70107
  from: dep.oldVersion,
70104
70108
  to: dep.newVersion
70105
- })));
70109
+ })))}`;
70106
70110
  });
70107
70111
  }
70108
70112
  //#endregion
@@ -70445,8 +70449,8 @@ const ISSUE_CATEGORIES = [
70445
70449
  /**
70446
70450
  * Format a changelog entry into a markdown string with GitHub links.
70447
70451
  *
70448
- * Produces a commit-link prefix (shortened to 7 characters) followed by the
70449
- * summary text and any issue references, each rendered as GitHub links.
70452
+ * Produces the summary text followed by any issue references, each
70453
+ * rendered as GitHub links.
70450
70454
  *
70451
70455
  * @remarks
70452
70456
  * The output does **not** include a leading `- ` list marker — the caller
@@ -70456,13 +70460,11 @@ const ISSUE_CATEGORIES = [
70456
70460
  *
70457
70461
  * Output format examples:
70458
70462
  *
70459
- * With commit: `[short-hash](commit-url) Summary text`
70463
+ * Without issues: `Summary text`
70460
70464
  *
70461
70465
  * With issues: `Summary text` followed by `Closes: [#1](issue-url)`
70462
70466
  *
70463
- * With both: `[short-hash](commit-url) Summary text` followed by `Fixes: [#2](issue-url)`
70464
- *
70465
- * @param entry - The changelog entry containing commit, summary, and issue data
70467
+ * @param entry - The changelog entry containing summary and issue data
70466
70468
  * @param options - Must include `repo` in `owner/repo` format for link generation
70467
70469
  * @returns Formatted markdown string (without leading `- `)
70468
70470
  *
@@ -70470,10 +70472,6 @@ const ISSUE_CATEGORIES = [
70470
70472
  */
70471
70473
  function formatChangelogEntry(entry, options) {
70472
70474
  const parts = [];
70473
- if (entry.commit) {
70474
- const shortHash = entry.commit.substring(0, 7);
70475
- parts.push(`[\`${shortHash}\`](https://github.com/${options.repo}/commit/${entry.commit})`);
70476
- }
70477
70475
  parts.push(entry.summary.trim());
70478
70476
  const issueLinks = [];
70479
70477
  for (const { key, label } of ISSUE_CATEGORIES) {
@@ -70547,7 +70545,7 @@ function formatPRAndUserAttribution(pr, user, links) {
70547
70545
  *
70548
70546
  * 1. **Section-aware changesets** — When the changeset summary contains h2
70549
70547
  * headings (e.g., `## Features`, `## Bug Fixes`), each section is rendered
70550
- * as an h3 heading with commit-linked list items beneath it. This mode
70548
+ * as an h3 heading with list items beneath it. This mode
70551
70549
  * produces multi-line output suitable for rich changelogs.
70552
70550
  *
70553
70551
  * 2. **Flat-text changesets** — When the summary is plain text without section
@@ -70557,7 +70555,6 @@ function formatPRAndUserAttribution(pr, user, links) {
70557
70555
  *
70558
70556
  * In both modes, the formatter:
70559
70557
  * - Fetches GitHub metadata (PR number, author) via {@link GitHubService}
70560
- * - Generates shortened commit hash links (`[abc1234](...)`)
70561
70558
  * - Extracts and renders issue references (Closes, Fixes, Refs)
70562
70559
  * - Appends PR and user attribution when available
70563
70560
  *
@@ -70588,7 +70585,7 @@ function formatPRAndUserAttribution(pr, user, links) {
70588
70585
  * `Fixes #N`, and `Refs #N` patterns.
70589
70586
  * 5. **Build attribution** — Format PR link and user credit from GitHub info.
70590
70587
  * 6. **Section-aware output** — If sections were found, render each as an
70591
- * h3 heading with commit-linked list items.
70588
+ * h3 heading with list items.
70592
70589
  * 7. **Flat-text fallback** — Otherwise, produce a single `- entry` line
70593
70590
  * with the resolved category heading.
70594
70591
  *
@@ -70622,14 +70619,16 @@ function getReleaseLine(changeset, versionType, options) {
70622
70619
  for (const section of parsed.sections) {
70623
70620
  lines.push(`### ${section.category.heading}`);
70624
70621
  lines.push("");
70625
- const commitPrefix = changeset.commit ? `[\`${changeset.commit.substring(0, 7)}\`](https://github.com/${options.repo}/commit/${changeset.commit}) ` : "";
70626
70622
  if (section.content) {
70627
70623
  const contentLines = section.content.split("\n");
70628
70624
  const firstContentLine = contentLines[0];
70629
70625
  if (firstContentLine.startsWith("- ") || firstContentLine.startsWith("* ")) {
70630
- lines.push(`${firstContentLine.substring(0, 2)}${commitPrefix}${firstContentLine.substring(2)}`);
70626
+ lines.push(firstContentLine);
70631
70627
  lines.push(...contentLines.slice(1));
70632
- } else lines.push(`- ${commitPrefix}${section.content}`);
70628
+ } else {
70629
+ lines.push(`- ${firstContentLine}`);
70630
+ lines.push(...contentLines.slice(1).map((line) => line.length > 0 ? ` ${line}` : line));
70631
+ }
70633
70632
  }
70634
70633
  lines.push("");
70635
70634
  }
@@ -70638,8 +70637,7 @@ function getReleaseLine(changeset, versionType, options) {
70638
70637
  return `- ${formatChangelogEntry({
70639
70638
  type: resolveCommitType(commitMsg.type ?? versionType, commitMsg.scope, commitMsg.breaking).heading,
70640
70639
  summary: changeset.summary,
70641
- issues: issueRefs,
70642
- ...changeset.commit ? { commit: changeset.commit } : {}
70640
+ issues: issueRefs
70643
70641
  }, { repo: options.repo })}${attribution}`;
70644
70642
  });
70645
70643
  }
@@ -70679,7 +70677,7 @@ function getReleaseLine(changeset, versionType, options) {
70679
70677
  * config is decoded through `ChangesetOptionsSchema`.
70680
70678
  * 2. **Release line formatting** — each changeset is formatted by
70681
70679
  * `getReleaseLine`, which resolves GitHub metadata, parses sections,
70682
- * and produces structured markdown with commit links and attribution.
70680
+ * and produces structured markdown with attribution.
70683
70681
  * 3. **Dependency table formatting** — bulk dependency updates are
70684
70682
  * formatted by `getDependencyReleaseLine` into a markdown table.
70685
70683
  *
@@ -71577,6 +71575,105 @@ function getHeadingText$1(heading) {
71577
71575
  return toString(heading);
71578
71576
  }
71579
71577
  //#endregion
71578
+ //#region ../silk-effects/dist/dev/pkg/changesets/remark/plugins/maintenance-note.js
71579
+ function buildNoteChildren(reason) {
71580
+ if (reason.kind === "unspecified" || reason.triggers.length === 0) return [{
71581
+ type: "text",
71582
+ value: "Version-only release to keep workspace versions consistent; no changes to this package."
71583
+ }];
71584
+ const label = reason.kind === "fixed" ? "fixed version group" : "linked version group";
71585
+ const children = [{
71586
+ type: "text",
71587
+ value: "Released in lockstep with "
71588
+ }];
71589
+ reason.triggers.forEach((trigger, index) => {
71590
+ if (index > 0) children.push({
71591
+ type: "text",
71592
+ value: ", "
71593
+ });
71594
+ children.push({
71595
+ type: "inlineCode",
71596
+ value: `${trigger.name}@${trigger.version}`
71597
+ });
71598
+ });
71599
+ children.push({
71600
+ type: "text",
71601
+ value: ` (${label}).`
71602
+ });
71603
+ return children;
71604
+ }
71605
+ const MaintenanceNotePlugin = (options) => {
71606
+ return (tree) => {
71607
+ const block = getVersionBlocks(tree).find((b) => getHeadingText$1(tree.children[b.headingIndex]) === options.version);
71608
+ if (!block) return;
71609
+ if (block.endIndex > block.startIndex) return;
71610
+ const heading = {
71611
+ type: "heading",
71612
+ depth: 3,
71613
+ children: [{
71614
+ type: "text",
71615
+ value: "Maintenance"
71616
+ }]
71617
+ };
71618
+ const list = {
71619
+ type: "list",
71620
+ ordered: false,
71621
+ spread: false,
71622
+ children: [{
71623
+ type: "listItem",
71624
+ spread: false,
71625
+ children: [{
71626
+ type: "paragraph",
71627
+ children: buildNoteChildren(options.reason)
71628
+ }]
71629
+ }]
71630
+ };
71631
+ tree.children.splice(block.startIndex, 0, heading, list);
71632
+ };
71633
+ };
71634
+ //#endregion
71635
+ //#region ../silk-effects/dist/dev/pkg/changesets/remark/plugins/aggregate-dependency-tables.js
71636
+ const AggregateDependencyTablesPlugin = () => {
71637
+ return (tree) => {
71638
+ const blocks = getVersionBlocks(tree);
71639
+ for (let b = blocks.length - 1; b >= 0; b--) {
71640
+ const depSections = getBlockSections(tree, blocks[b]).filter((s) => getHeadingText$1(s.heading).toLowerCase() === "dependencies");
71641
+ if (depSections.length === 0) continue;
71642
+ const allRows = [];
71643
+ const legacyContent = [];
71644
+ for (const section of depSections) for (const node of section.contentNodes) if (node.type === "table") try {
71645
+ const rows = parseDependencyTable(node);
71646
+ allRows.push(...rows);
71647
+ } catch {
71648
+ legacyContent.push(node);
71649
+ }
71650
+ else legacyContent.push(node);
71651
+ const collapsed = sortDependencyRows(collapseDependencyRows(allRows));
71652
+ const indicesToRemove = [];
71653
+ for (const section of depSections) {
71654
+ indicesToRemove.push(section.headingIndex);
71655
+ for (let c = 0; c < section.contentNodes.length; c++) indicesToRemove.push(section.headingIndex + 1 + c);
71656
+ }
71657
+ indicesToRemove.sort((a, b) => b - a);
71658
+ for (const idx of indicesToRemove) tree.children.splice(idx, 1);
71659
+ if (collapsed.length === 0 && legacyContent.length === 0) continue;
71660
+ const insertAt = depSections[0].headingIndex;
71661
+ const newNodes = [];
71662
+ newNodes.push({
71663
+ type: "heading",
71664
+ depth: 3,
71665
+ children: [{
71666
+ type: "text",
71667
+ value: "Dependencies"
71668
+ }]
71669
+ });
71670
+ if (collapsed.length > 0) newNodes.push(serializeDependencyTable(collapsed));
71671
+ newNodes.push(...legacyContent);
71672
+ tree.children.splice(insertAt, 0, ...newNodes);
71673
+ }
71674
+ };
71675
+ };
71676
+ //#endregion
71580
71677
  //#region ../silk-effects/dist/dev/pkg/changesets/remark/plugins/contributor-footnotes.js
71581
71678
  /**
71582
71679
  * Pattern matching `Thanks @user!` at end of text.
@@ -71701,16 +71798,18 @@ const DeduplicateItemsPlugin = () => {
71701
71798
  const blocks = getVersionBlocks(tree);
71702
71799
  for (const block of blocks) {
71703
71800
  const sections = getBlockSections(tree, block);
71704
- for (const section of sections) for (const node of section.contentNodes) {
71705
- if (node.type !== "list") continue;
71706
- const list = node;
71801
+ for (const section of sections) {
71707
71802
  const seen = /* @__PURE__ */ new Set();
71708
- list.children = list.children.filter((item) => {
71709
- const text = toString(item);
71710
- if (seen.has(text)) return false;
71711
- seen.add(text);
71712
- return true;
71713
- });
71803
+ for (const node of section.contentNodes) {
71804
+ if (node.type !== "list") continue;
71805
+ const list = node;
71806
+ list.children = list.children.filter((item) => {
71807
+ const text = toString(item);
71808
+ if (seen.has(text)) return false;
71809
+ seen.add(text);
71810
+ return true;
71811
+ });
71812
+ }
71714
71813
  }
71715
71814
  }
71716
71815
  tree.children = tree.children.filter((node) => {
@@ -71882,6 +71981,110 @@ const ReorderSectionsPlugin = () => {
71882
71981
  };
71883
71982
  };
71884
71983
  //#endregion
71984
+ //#region ../silk-effects/dist/dev/pkg/changesets/remark/presets.js
71985
+ /**
71986
+ * Remark preset collections for changeset lint rules and transform plugins.
71987
+ *
71988
+ * @remarks
71989
+ * Presets bundle related remark plugins into ordered arrays for convenient
71990
+ * consumption. Each preset is a `readonly` tuple so that TypeScript can
71991
+ * narrow element types. Iterate and `.use()` each entry with `unified()`.
71992
+ *
71993
+ * @see {@link SilkChangesetPreset} for lint rules
71994
+ * @see {@link SilkChangesetTransformPreset} for transform plugins
71995
+ */
71996
+ /**
71997
+ * Preset combining all changeset lint rules for convenient consumption.
71998
+ *
71999
+ * @remarks
72000
+ * Includes the following rules in order:
72001
+ *
72002
+ * 1. {@link HeadingHierarchyRule} (CSH001) -- no h1, no depth skips
72003
+ * 2. {@link RequiredSectionsRule} (CSH002) -- h2 headings must match a known category
72004
+ * 3. {@link ContentStructureRule} (CSH003) -- non-empty sections, code fence languages, non-empty list items
72005
+ * 4. {@link UncategorizedContentRule} (CSH004) -- no content before the first h2 heading
72006
+ * 5. {@link DependencyTableFormatRule} (CSH005) -- dependency table column/value validation
72007
+ *
72008
+ * Rule execution order is not significant for lint rules; all rules run
72009
+ * independently over the same AST and report warnings to the virtual file.
72010
+ *
72011
+ * @example
72012
+ * ```typescript
72013
+ * import { SilkChangesetPreset } from "\@savvy-web/changesets/remark";
72014
+ * import remarkParse from "remark-parse";
72015
+ * import { unified } from "unified";
72016
+ * import { read } from "to-vfile";
72017
+ *
72018
+ * const processor = unified().use(remarkParse);
72019
+ * for (const rule of SilkChangesetPreset) {
72020
+ * processor.use(rule);
72021
+ * }
72022
+ *
72023
+ * const file = await read("changeset.md");
72024
+ * const result = await processor.process(file);
72025
+ * console.log(result.messages); // lint warnings
72026
+ * ```
72027
+ *
72028
+ * @see {@link SilkChangesetTransformPreset} for the corresponding transform preset
72029
+ *
72030
+ * @public
72031
+ */
72032
+ const SilkChangesetPreset = [
72033
+ HeadingHierarchyRule$1,
72034
+ RequiredSectionsRule$1,
72035
+ ContentStructureRule$1,
72036
+ UncategorizedContentRule$1,
72037
+ DependencyTableFormatRule$1
72038
+ ];
72039
+ /**
72040
+ * Ordered array of all transform plugins in the correct execution order.
72041
+ *
72042
+ * @remarks
72043
+ * Plugin ordering is significant -- each plugin may depend on the output of
72044
+ * earlier plugins in the pipeline:
72045
+ *
72046
+ * 1. {@link AggregateDependencyTablesPlugin} -- merge duplicate dependency sections (must run first so downstream plugins see a single Dependencies section)
72047
+ * 2. {@link MergeSectionsPlugin} -- merge duplicate h3 headings (must run before reorder so that priority is computed on consolidated sections)
72048
+ * 3. {@link ReorderSectionsPlugin} -- sort sections by category priority (Breaking Changes first, Other last)
72049
+ * 4. {@link DeduplicateItemsPlugin} -- remove duplicate list items within each section
72050
+ * 5. {@link ContributorFootnotesPlugin} -- extract inline `Thanks \@user!` attributions and aggregate them into a summary paragraph per version block
72051
+ * 6. {@link IssueLinkRefsPlugin} -- convert inline `[#N](url)` links to reference-style `[#N]` with definitions at the end of each version block
72052
+ * 7. {@link NormalizeFormatPlugin} -- final cleanup removing empty sections and empty lists
72053
+ *
72054
+ * @example
72055
+ * ```typescript
72056
+ * import { SilkChangesetTransformPreset } from "\@savvy-web/changesets/remark";
72057
+ * import remarkGfm from "remark-gfm";
72058
+ * import remarkParse from "remark-parse";
72059
+ * import remarkStringify from "remark-stringify";
72060
+ * import { unified } from "unified";
72061
+ * import { read } from "to-vfile";
72062
+ *
72063
+ * const processor = unified().use(remarkParse).use(remarkGfm);
72064
+ * for (const plugin of SilkChangesetTransformPreset) {
72065
+ * processor.use(plugin);
72066
+ * }
72067
+ * processor.use(remarkStringify);
72068
+ *
72069
+ * const file = await read("CHANGELOG.md");
72070
+ * const result = await processor.process(file);
72071
+ * console.log(String(result));
72072
+ * ```
72073
+ *
72074
+ * @see {@link SilkChangesetPreset} for the corresponding lint preset
72075
+ *
72076
+ * @public
72077
+ */
72078
+ const SilkChangesetTransformPreset = [
72079
+ AggregateDependencyTablesPlugin,
72080
+ MergeSectionsPlugin,
72081
+ ReorderSectionsPlugin,
72082
+ DeduplicateItemsPlugin,
72083
+ ContributorFootnotesPlugin,
72084
+ IssueLinkRefsPlugin,
72085
+ NormalizeFormatPlugin
72086
+ ];
72087
+ //#endregion
71885
72088
  //#region ../silk-effects/dist/dev/pkg/changesets/api/transformer.js
71886
72089
  /**
71887
72090
  * Class-based API wrapper for changelog transformation.
@@ -71896,23 +72099,13 @@ const ReorderSectionsPlugin = () => {
71896
72099
  * Static class for post-processing CHANGELOG.md files.
71897
72100
  *
71898
72101
  * Implements the third layer of the three-layer pipeline by running
71899
- * six remark transform plugins in a fixed order to clean up, normalize,
71900
- * and enhance changelog output produced by the formatter layer.
72102
+ * the {@link SilkChangesetTransformPreset} plugins (currently seven) in a
72103
+ * fixed order to clean up, normalize, and enhance changelog output produced
72104
+ * by the formatter layer.
71901
72105
  *
71902
72106
  * @remarks
71903
- * The six plugins run in this order:
71904
- *
71905
- * 1. **MergeSectionsPlugin** -- merges duplicate section headings (e.g., two
71906
- * "Features" sections from separate changesets are combined into one)
71907
- * 2. **ReorderSectionsPlugin** -- reorders sections by category priority
71908
- * (Breaking Changes first, Other last) using the {@link Categories} priority values
71909
- * 3. **DeduplicateItemsPlugin** -- removes duplicate list items within a section
71910
- * 4. **ContributorFootnotesPlugin** -- converts inline contributor mentions
71911
- * into footnote references for cleaner formatting
71912
- * 5. **IssueLinkRefsPlugin** -- converts inline issue/PR links into markdown
71913
- * reference-style links collected at the bottom of the document
71914
- * 6. **NormalizeFormatPlugin** -- applies consistent formatting (spacing,
71915
- * trailing newlines, heading levels)
72107
+ * See {@link SilkChangesetTransformPreset} for the ordered plugin list and
72108
+ * the rationale behind each plugin's position.
71916
72109
  *
71917
72110
  * The transformer operates on the full CHANGELOG.md content (all versions),
71918
72111
  * not just the latest release block. It is idempotent -- running it multiple
@@ -71972,20 +72165,27 @@ var ChangelogTransformer = class ChangelogTransformer {
71972
72165
  /* v8 ignore next -- private constructor prevents direct instantiation */
71973
72166
  constructor() {}
71974
72167
  /**
71975
- * Transform CHANGELOG markdown content by running all six transform plugins.
72168
+ * Transform CHANGELOG markdown content by running the
72169
+ * {@link SilkChangesetTransformPreset} plugins.
71976
72170
  *
71977
72171
  * @remarks
71978
72172
  * The input is parsed with `remark-parse` and `remark-gfm` (for table
71979
- * support), processed through all six plugins in order, and stringified
71980
- * back to markdown. The operation is synchronous and idempotent.
72173
+ * support), processed through every plugin in {@link SilkChangesetTransformPreset}
72174
+ * in order, and stringified back to markdown. The operation is synchronous
72175
+ * and idempotent.
71981
72176
  *
71982
72177
  * @param content - Raw CHANGELOG markdown string (may contain multiple
71983
72178
  * version blocks, GFM tables, footnotes, and reference links)
71984
- * @returns The transformed markdown string with sections merged, reordered,
71985
- * deduplicated, and normalized
71986
- */
71987
- static transformContent(content) {
71988
- const file = unified().use(remarkParse).use(remarkGfm).use(MergeSectionsPlugin).use(ReorderSectionsPlugin).use(DeduplicateItemsPlugin).use(ContributorFootnotesPlugin).use(IssueLinkRefsPlugin).use(NormalizeFormatPlugin).use(remarkStringify).processSync(content);
72179
+ * @param options - Optional transformation options, including maintenance note configuration
72180
+ * @returns The transformed markdown string with dependency tables aggregated,
72181
+ * sections merged, reordered, deduplicated, and normalized
72182
+ */
72183
+ static transformContent(content, options) {
72184
+ const processor = unified().use(remarkParse).use(remarkGfm);
72185
+ for (const plugin of SilkChangesetTransformPreset) processor.use(plugin);
72186
+ if (options?.maintenance) processor.use(MaintenanceNotePlugin, options.maintenance);
72187
+ processor.use(remarkStringify);
72188
+ const file = processor.processSync(content);
71989
72189
  return String(file);
71990
72190
  }
71991
72191
  /**
@@ -72001,10 +72201,11 @@ var ChangelogTransformer = class ChangelogTransformer {
72001
72201
  * when invoked without the `--dry-run` or `--check` flags.
72002
72202
  *
72003
72203
  * @param filePath - Absolute or relative path to the CHANGELOG.md file
72204
+ * @param options - Optional transformation options, including maintenance note configuration
72004
72205
  */
72005
- static transformFile(filePath) {
72206
+ static transformFile(filePath, options) {
72006
72207
  const content = (0, node_fs.readFileSync)(filePath, "utf-8");
72007
- (0, node_fs.writeFileSync)(filePath, ChangelogTransformer.transformContent(content), "utf-8");
72208
+ (0, node_fs.writeFileSync)(filePath, ChangelogTransformer.transformContent(content, options), "utf-8");
72008
72209
  }
72009
72210
  };
72010
72211
  //#endregion
@@ -76018,6 +76219,67 @@ const ConfigGraph = ChangesetConfigLive.pipe(effect.Layer.provide(ChangesetConfi
76018
76219
  */
76019
76220
  const DepsRegenDefault = DepsRegenLive.pipe(effect.Layer.provide(PointInTimeWorkspaceLive.pipe(effect.Layer.provide(WorkspaceGraph))), effect.Layer.provide(ConfigInspectorLive.pipe(effect.Layer.provide(effect.Layer.mergeAll(ChangesetConfigReaderLive, WorkspaceGraph)))), effect.Layer.provide(PublishabilityDetectorAdaptiveLive.pipe(effect.Layer.provide(ConfigGraph))), effect.Layer.provide(ConfigGraph), effect.Layer.provide(WorkspaceGraph));
76020
76221
  //#endregion
76222
+ //#region ../silk-effects/dist/dev/pkg/changesets/services/maintenance-reason.js
76223
+ /**
76224
+ * A group co-member whose own changesets forced this release.
76225
+ *
76226
+ * @public
76227
+ */
76228
+ const MaintenanceTriggerSchema = effect.Schema.Struct({
76229
+ /** Package name of the triggering co-member. */
76230
+ name: effect.Schema.String,
76231
+ /** The co-member's new version in the same release plan. */
76232
+ version: effect.Schema.String
76233
+ });
76234
+ /**
76235
+ * Why a package is releasing with no changesets of its own.
76236
+ *
76237
+ * @public
76238
+ */
76239
+ const MaintenanceReasonSchema = effect.Schema.Struct({
76240
+ /** Coupling that forced the release; `"unspecified"` when undetermined. */
76241
+ kind: effect.Schema.Literal("fixed", "linked", "unspecified"),
76242
+ /** Triggering co-members; empty for `"unspecified"`. */
76243
+ triggers: effect.Schema.Array(MaintenanceTriggerSchema)
76244
+ });
76245
+ /**
76246
+ * Derive the {@link MaintenanceReason} for a release, or `undefined` when the
76247
+ * release has its own changesets (not a maintenance release).
76248
+ *
76249
+ * @param release - The release to classify.
76250
+ * @param plan - The full release plan (source of group co-members).
76251
+ * @param config - Resolved changesets config (`fixed` / `linked` groups).
76252
+ * @returns The reason, or `undefined` for releases with their own changesets.
76253
+ *
76254
+ * @remarks
76255
+ * Group entries are matched with {@link ChangesetConfig.matches} — exact names
76256
+ * and trailing `"@scope/*"` prefixes only, a subset of the micromatch globs
76257
+ * changesets accepts. A group entry using richer glob syntax (e.g. `"pkg-*"`)
76258
+ * will not match here; the release then degrades gracefully to the
76259
+ * `"unspecified"` fallback sentence instead of naming its triggers.
76260
+ *
76261
+ * @public
76262
+ */
76263
+ function deriveMaintenanceReason(release, plan, config) {
76264
+ if (release.changesets.length > 0) return void 0;
76265
+ const groupKinds = [["fixed", config.fixed], ["linked", config.linked]];
76266
+ for (const [kind, groups] of groupKinds) for (const group of groups) {
76267
+ if (!group.some((pattern) => ChangesetConfig.matches(release.name, pattern))) continue;
76268
+ const triggers = plan.releases.filter((r) => r.name !== release.name && r.changesets.length > 0 && group.some((pattern) => ChangesetConfig.matches(r.name, pattern))).map((r) => ({
76269
+ name: r.name,
76270
+ version: r.newVersion
76271
+ }));
76272
+ if (triggers.length > 0) return {
76273
+ kind,
76274
+ triggers
76275
+ };
76276
+ }
76277
+ return {
76278
+ kind: "unspecified",
76279
+ triggers: []
76280
+ };
76281
+ }
76282
+ //#endregion
76021
76283
  //#region ../silk-effects/dist/dev/pkg/changesets/utils/jsonpath.js
76022
76284
  /**
76023
76285
  * Tokenize a JSONPath string into segments.
@@ -109556,6 +109818,16 @@ function extractVersionBlock(changelog, version) {
109556
109818
  }
109557
109819
  return lines.slice(start, end).join("\n").trim();
109558
109820
  }
109821
+ /** Maintenance reasons for every changeset-less release in the plan, keyed by package name. */
109822
+ function maintenanceReasons(plan, config) {
109823
+ const reasons = /* @__PURE__ */ new Map();
109824
+ for (const r of plan.releases) {
109825
+ if (r.type === "none") continue;
109826
+ const reason = deriveMaintenanceReason(r, plan, config);
109827
+ if (reason) reasons.set(r.name, reason);
109828
+ }
109829
+ return reasons;
109830
+ }
109559
109831
  /**
109560
109832
  * Render a non-destructive preview by redirecting every write into a
109561
109833
  * scope-managed temp directory (cleaned up automatically when the scope
@@ -109577,6 +109849,7 @@ function previewEffect(root, fs) {
109577
109849
  reason: errMsg(e)
109578
109850
  })
109579
109851
  });
109852
+ const reasonByName = maintenanceReasons(plan, config);
109580
109853
  const preMode = plan.preState ? plan.preState.mode : null;
109581
109854
  const changesets = plan.changesets.map((cs) => ({
109582
109855
  id: cs.id,
@@ -109645,8 +109918,12 @@ function previewEffect(root, fs) {
109645
109918
  if (!dir) continue;
109646
109919
  const clPath = (0, node_path$1.join)(dir, "CHANGELOG.md");
109647
109920
  if (!(yield* fs.exists(clPath))) continue;
109921
+ const reason = reasonByName.get(r.name);
109648
109922
  yield* effect.Effect.try({
109649
- try: () => ChangelogTransformer.transformFile(clPath),
109923
+ try: () => ChangelogTransformer.transformFile(clPath, reason ? { maintenance: {
109924
+ version: r.newVersion,
109925
+ reason
109926
+ } } : void 0),
109650
109927
  catch: (e) => new ReleasePlanError({
109651
109928
  phase: "preview",
109652
109929
  reason: errMsg(e)
@@ -109700,17 +109977,33 @@ function applyEffect(root, dryRun, inspector, fs) {
109700
109977
  newVersion: r.newVersion
109701
109978
  }));
109702
109979
  let touchedFiles = [];
109703
- if (!dryRun) touchedFiles = yield* effect.Effect.tryPromise({
109704
- try: async () => {
109705
- const touched = await applyReleasePlan(plan, packages, config);
109706
- for (const f of touched) if (f.endsWith("CHANGELOG.md")) ChangelogTransformer.transformFile(f);
109707
- return touched;
109708
- },
109709
- catch: (e) => new ReleasePlanError({
109710
- phase: "apply",
109711
- reason: errMsg(e)
109712
- })
109713
- });
109980
+ if (!dryRun) {
109981
+ const reasonByName = maintenanceReasons(plan, config);
109982
+ const versionByPkgName = new Map(plan.releases.map((r) => [r.name, r.newVersion]));
109983
+ const nameByDir = /* @__PURE__ */ new Map();
109984
+ for (const p of packages.packages) nameByDir.set(p.dir, p.packageJson.name);
109985
+ if (packages.root.packageJson.name) nameByDir.set(packages.root.dir, packages.root.packageJson.name);
109986
+ touchedFiles = yield* effect.Effect.tryPromise({
109987
+ try: async () => {
109988
+ const touched = await applyReleasePlan(plan, packages, config);
109989
+ for (const f of touched) {
109990
+ if (!f.endsWith("CHANGELOG.md")) continue;
109991
+ const pkgName = nameByDir.get((0, node_path$1.dirname)(f));
109992
+ const reason = pkgName ? reasonByName.get(pkgName) : void 0;
109993
+ const newVersion = pkgName ? versionByPkgName.get(pkgName) : void 0;
109994
+ ChangelogTransformer.transformFile(f, reason && newVersion ? { maintenance: {
109995
+ version: newVersion,
109996
+ reason
109997
+ } } : void 0);
109998
+ }
109999
+ return touched;
110000
+ },
110001
+ catch: (e) => new ReleasePlanError({
110002
+ phase: "apply",
110003
+ reason: errMsg(e)
110004
+ })
110005
+ });
110006
+ }
109714
110007
  const newVersionByName = new Map(plan.releases.map((r) => [r.name, r.newVersion]));
109715
110008
  const inspected = yield* inspector.inspect(root).pipe(effect.Effect.catchAll((error) => effect.Effect.logWarning(`Skipping versionFiles update: ${errMsg(error)}`).pipe(effect.Effect.as(null))));
109716
110009
  let versionFileUpdates = [];
@@ -110514,152 +110807,6 @@ const SilkChangesetsRules = [
110514
110807
  DependencyTableFormatRule
110515
110808
  ];
110516
110809
  //#endregion
110517
- //#region ../silk-effects/dist/dev/pkg/changesets/remark/plugins/aggregate-dependency-tables.js
110518
- const AggregateDependencyTablesPlugin = () => {
110519
- return (tree) => {
110520
- const blocks = getVersionBlocks(tree);
110521
- for (let b = blocks.length - 1; b >= 0; b--) {
110522
- const depSections = getBlockSections(tree, blocks[b]).filter((s) => getHeadingText$1(s.heading).toLowerCase() === "dependencies");
110523
- if (depSections.length === 0) continue;
110524
- const allRows = [];
110525
- const legacyContent = [];
110526
- for (const section of depSections) for (const node of section.contentNodes) if (node.type === "table") try {
110527
- const rows = parseDependencyTable(node);
110528
- allRows.push(...rows);
110529
- } catch {
110530
- legacyContent.push(node);
110531
- }
110532
- else legacyContent.push(node);
110533
- const collapsed = sortDependencyRows(collapseDependencyRows(allRows));
110534
- const indicesToRemove = [];
110535
- for (const section of depSections) {
110536
- indicesToRemove.push(section.headingIndex);
110537
- for (let c = 0; c < section.contentNodes.length; c++) indicesToRemove.push(section.headingIndex + 1 + c);
110538
- }
110539
- indicesToRemove.sort((a, b) => b - a);
110540
- for (const idx of indicesToRemove) tree.children.splice(idx, 1);
110541
- if (collapsed.length === 0 && legacyContent.length === 0) continue;
110542
- const insertAt = depSections[0].headingIndex;
110543
- const newNodes = [];
110544
- newNodes.push({
110545
- type: "heading",
110546
- depth: 3,
110547
- children: [{
110548
- type: "text",
110549
- value: "Dependencies"
110550
- }]
110551
- });
110552
- if (collapsed.length > 0) newNodes.push(serializeDependencyTable(collapsed));
110553
- newNodes.push(...legacyContent);
110554
- tree.children.splice(insertAt, 0, ...newNodes);
110555
- }
110556
- };
110557
- };
110558
- //#endregion
110559
- //#region ../silk-effects/dist/dev/pkg/changesets/remark/presets.js
110560
- /**
110561
- * Remark preset collections for changeset lint rules and transform plugins.
110562
- *
110563
- * @remarks
110564
- * Presets bundle related remark plugins into ordered arrays for convenient
110565
- * consumption. Each preset is a `readonly` tuple so that TypeScript can
110566
- * narrow element types. Iterate and `.use()` each entry with `unified()`.
110567
- *
110568
- * @see {@link SilkChangesetPreset} for lint rules
110569
- * @see {@link SilkChangesetTransformPreset} for transform plugins
110570
- */
110571
- /**
110572
- * Preset combining all changeset lint rules for convenient consumption.
110573
- *
110574
- * @remarks
110575
- * Includes the following rules in order:
110576
- *
110577
- * 1. {@link HeadingHierarchyRule} (CSH001) -- no h1, no depth skips
110578
- * 2. {@link RequiredSectionsRule} (CSH002) -- h2 headings must match a known category
110579
- * 3. {@link ContentStructureRule} (CSH003) -- non-empty sections, code fence languages, non-empty list items
110580
- * 4. {@link UncategorizedContentRule} (CSH004) -- no content before the first h2 heading
110581
- * 5. {@link DependencyTableFormatRule} (CSH005) -- dependency table column/value validation
110582
- *
110583
- * Rule execution order is not significant for lint rules; all rules run
110584
- * independently over the same AST and report warnings to the virtual file.
110585
- *
110586
- * @example
110587
- * ```typescript
110588
- * import { SilkChangesetPreset } from "\@savvy-web/changesets/remark";
110589
- * import remarkParse from "remark-parse";
110590
- * import { unified } from "unified";
110591
- * import { read } from "to-vfile";
110592
- *
110593
- * const processor = unified().use(remarkParse);
110594
- * for (const rule of SilkChangesetPreset) {
110595
- * processor.use(rule);
110596
- * }
110597
- *
110598
- * const file = await read("changeset.md");
110599
- * const result = await processor.process(file);
110600
- * console.log(result.messages); // lint warnings
110601
- * ```
110602
- *
110603
- * @see {@link SilkChangesetTransformPreset} for the corresponding transform preset
110604
- *
110605
- * @public
110606
- */
110607
- const SilkChangesetPreset = [
110608
- HeadingHierarchyRule$1,
110609
- RequiredSectionsRule$1,
110610
- ContentStructureRule$1,
110611
- UncategorizedContentRule$1,
110612
- DependencyTableFormatRule$1
110613
- ];
110614
- /**
110615
- * Ordered array of all transform plugins in the correct execution order.
110616
- *
110617
- * @remarks
110618
- * Plugin ordering is significant -- each plugin may depend on the output of
110619
- * earlier plugins in the pipeline:
110620
- *
110621
- * 1. {@link AggregateDependencyTablesPlugin} -- merge duplicate dependency sections (must run first so downstream plugins see a single Dependencies section)
110622
- * 2. {@link MergeSectionsPlugin} -- merge duplicate h3 headings (must run before reorder so that priority is computed on consolidated sections)
110623
- * 3. {@link ReorderSectionsPlugin} -- sort sections by category priority (Breaking Changes first, Other last)
110624
- * 4. {@link DeduplicateItemsPlugin} -- remove duplicate list items within each section
110625
- * 5. {@link ContributorFootnotesPlugin} -- extract inline `Thanks \@user!` attributions and aggregate them into a summary paragraph per version block
110626
- * 6. {@link IssueLinkRefsPlugin} -- convert inline `[#N](url)` links to reference-style `[#N]` with definitions at the end of each version block
110627
- * 7. {@link NormalizeFormatPlugin} -- final cleanup removing empty sections and empty lists
110628
- *
110629
- * @example
110630
- * ```typescript
110631
- * import { SilkChangesetTransformPreset } from "\@savvy-web/changesets/remark";
110632
- * import remarkGfm from "remark-gfm";
110633
- * import remarkParse from "remark-parse";
110634
- * import remarkStringify from "remark-stringify";
110635
- * import { unified } from "unified";
110636
- * import { read } from "to-vfile";
110637
- *
110638
- * const processor = unified().use(remarkParse).use(remarkGfm);
110639
- * for (const plugin of SilkChangesetTransformPreset) {
110640
- * processor.use(plugin);
110641
- * }
110642
- * processor.use(remarkStringify);
110643
- *
110644
- * const file = await read("CHANGELOG.md");
110645
- * const result = await processor.process(file);
110646
- * console.log(String(result));
110647
- * ```
110648
- *
110649
- * @see {@link SilkChangesetPreset} for the corresponding lint preset
110650
- *
110651
- * @public
110652
- */
110653
- const SilkChangesetTransformPreset = [
110654
- AggregateDependencyTablesPlugin,
110655
- MergeSectionsPlugin,
110656
- ReorderSectionsPlugin,
110657
- DeduplicateItemsPlugin,
110658
- ContributorFootnotesPlugin,
110659
- IssueLinkRefsPlugin,
110660
- NormalizeFormatPlugin
110661
- ];
110662
- //#endregion
110663
110810
  //#region src/changesets/changelog.ts
110664
110811
  /**
110665
110812
  * Changesets API changelog formatter shim for \@savvy-web/silk.
@@ -110734,6 +110881,9 @@ const changelogFunctions = (/* @__PURE__ */ __exportAll({
110734
110881
  JsonPathSchema: () => JsonPathSchema,
110735
110882
  LegacyVersionFileConfigSchema: () => LegacyVersionFileConfigSchema,
110736
110883
  LegacyVersionFilesSchema: () => LegacyVersionFilesSchema,
110884
+ MaintenanceNotePlugin: () => MaintenanceNotePlugin,
110885
+ MaintenanceReasonSchema: () => MaintenanceReasonSchema,
110886
+ MaintenanceTriggerSchema: () => MaintenanceTriggerSchema,
110737
110887
  MarkdownLive: () => MarkdownLive,
110738
110888
  MarkdownParseError: () => MarkdownParseError,
110739
110889
  MarkdownParseErrorBase: () => MarkdownParseErrorBase,
@@ -110780,6 +110930,7 @@ const changelogFunctions = (/* @__PURE__ */ __exportAll({
110780
110930
  VersionTypeSchema: () => VersionTypeSchema,
110781
110931
  changelogFunctions: () => changelogFunctions$1,
110782
110932
  computeWorkspaceDependencyDiffs: () => computeWorkspaceDependencyDiffs,
110933
+ deriveMaintenanceReason: () => deriveMaintenanceReason,
110783
110934
  gitMergeBase: () => gitMergeBase,
110784
110935
  isPureDependencyChangeset: () => isPureDependencyChangeset,
110785
110936
  listPublishablePackageNames: () => listPublishablePackageNames,