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