Skip to content

fix(markdown): migrate to unified v11 ecosystem#178

Merged
jgroth merged 2 commits into
mainfrom
unified-ecosystem-upgrade
Apr 14, 2026
Merged

fix(markdown): migrate to unified v11 ecosystem#178
jgroth merged 2 commits into
mainfrom
unified-ecosystem-upgrade

Conversation

@jgroth
Copy link
Copy Markdown
Owner

@jgroth jgroth commented Apr 14, 2026

Summary

  • Upgrades the entire remark/rehype stack from unified v9 to v11 (ESM-only)
  • Replaces abandoned remark-admonitions with a custom plugin built on remark-directive, preserving the same HTML output and supporting both legacy (:::note title) and standard (:::note[title]) syntax
  • Replaces remark-parse-yaml with direct yaml parsing in the frontmatter plugin
  • Replaces unist-util-flatmap (unmaintained) with an inline tree transform in markdown-typelinks
  • Adds shared unist type guards (isElement, isParent, isTextNode) and TypeScript types across all markdown plugins
  • Adds remark-gfm for GitHub Flavored Markdown support
  • Configures Jest transforms in stencil.config.ts to handle ESM-only dependencies

Summary by CodeRabbit

  • New Features

    • Added support for admonition directives with bracket-based and default label syntax.
    • Legacy admonition formats are now automatically converted to the new syntax.
  • Bug Fixes

    • Type links in inline code no longer render inside fenced code blocks.
  • Chores

    • Updated dependencies and test configuration for improved maintainability.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 14, 2026

Warning

Rate limit exceeded

@jgroth has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 33 minutes and 1 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 33 minutes and 1 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 90138d70-a70c-478f-8abb-4046a571c616

📥 Commits

Reviewing files that changed from the base of the PR and between c86601d and 9a0d98f.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • package.json
  • src/kompendium/markdown-admonitions.ts
  • src/kompendium/markdown-code.ts
  • src/kompendium/markdown-frontmatter.ts
  • src/kompendium/markdown-nodes.ts
  • src/kompendium/markdown-typelinks.ts
  • src/kompendium/markdown.ts
  • src/kompendium/test/markdown.spec.ts
  • stencil.config.ts
📝 Walkthrough

Walkthrough

This pull request upgrades Markdown processing dependencies, introduces new TypeScript modules for AST node types and admonition handling, refactors existing markdown transformation modules with improved typing and utilities, and configures Jest preprocessing in the Stencil test setup.

Changes

Cohort / File(s) Summary
Dependency & Build Configuration
package.json, stencil.config.ts
Updated rehype/remark package versions to newer majors; added remark-directive, remark-gfm, and yaml; configured Jest preprocessor and transform ignore patterns for testing.
AST Node Utilities
src/kompendium/markdown-nodes.ts
New module introducing ElementNode and TextNode interfaces, plus three type guard predicates (isParent, isTextNode, isElement) for safe AST node narrowing.
Admonition Processing
src/kompendium/markdown-admonitions.ts
New module implementing legacy admonition normalization via regex rewrite and AST-based admonition directive transformation with heading+content restructuring.
Markdown Transformation Refactoring
src/kompendium/markdown-code.ts, src/kompendium/markdown-frontmatter.ts, src/kompendium/markdown-typelinks.ts
Type-safety improvements: explicit Node/VFile typing, replaced unist-util-flatmap with inline transformation pipeline, removed unist-util-visit default import, hardened element detection logic, and improved optional chaining for property access.
Markdown Pipeline Integration
src/kompendium/markdown.ts
Added preprocessing step for legacy admonition normalization; updated plugin chain to use remarkParse, remarkGfm, remarkFrontmatter, remarkDirective; changed output handling from explicit Promise wrapping to direct await pattern.
Test Coverage
src/kompendium/test/markdown.spec.ts
Added tests for bracket-based admonition labels, unlabeled admonition parsing, inline code type linking, and negative test for code inside fenced blocks.

Sequence Diagram(s)

sequenceDiagram
    participant Input as Input Text
    participant Normalize as Normalize Legacy
    participant Pipeline as Unified Pipeline
    participant Plugins as Plugins<br/>(Parse, GFM, FM, Directive)
    participant Admonitions as Admonition<br/>Transform
    participant Stringify as Stringify
    participant Output as HTML Output

    Input->>Normalize: raw markdown text
    Normalize->>Normalize: regex rewrite<br/>legacy admonitions
    Normalize->>Pipeline: normalized text
    Pipeline->>Plugins: feed to remark
    Plugins->>Plugins: parse & build AST
    Plugins->>Admonitions: AST with directives
    Admonitions->>Admonitions: transform directive<br/>nodes to divs
    Admonitions->>Stringify: modified AST
    Stringify->>Stringify: convert to HTML
    Stringify->>Output: final HTML string
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Hoppy refactors dance in the code,
New nodes guide the AST road,
Admonitions take their rightful stage,
Types emerge from testing's page,
Legacy transformed, the pipeline sings anew!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: migrating the markdown processing stack from unified v9 to v11, which is the primary objective of this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch unified-ecosystem-upgrade

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/kompendium/markdown-frontmatter.ts (1)

17-18: Consider error handling for malformed YAML frontmatter.

YAML.parse() can throw a YAMLParseError if the frontmatter contains invalid YAML syntax. Currently, this would propagate up and fail the entire markdownToHtml call.

Depending on the desired behavior, you might want to:

  1. Let it throw (current behavior) - strict validation
  2. Catch and set frontmatter to undefined with a warning - lenient mode
  3. Catch and include the error in the result

If strict validation is intended, this is fine as-is.

🛡️ Optional: Add error handling for lenient parsing
-const storeData = (file: VFile) => (item: Node & { value?: string }) => {
-    file.data.frontmatter = item.value ? YAML.parse(item.value) : undefined;
+const storeData = (file: VFile) => (item: Node & { value?: string }) => {
+    if (!item.value) {
+        file.data.frontmatter = undefined;
+        return;
+    }
+    try {
+        file.data.frontmatter = YAML.parse(item.value);
+    } catch {
+        console.warn('Failed to parse frontmatter YAML');
+        file.data.frontmatter = undefined;
+    }
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/kompendium/markdown-frontmatter.ts` around lines 17 - 18, The YAML.parse
call in storeData can throw for malformed frontmatter causing markdownToHtml to
fail; wrap the YAML.parse(item.value) call in a try-catch inside storeData
(function name: storeData) and on parse error set file.data.frontmatter =
undefined and record the error (e.g. file.data.frontmatterError = error.message
or attach the full error) and optionally emit a warning (console.warn or
existing logger) so callers can choose lenient behavior; if strict behavior is
desired, keep current behavior but document that YAMLParseError will propagate
from storeData/markdownToHtml.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/kompendium/markdown-admonitions.ts`:
- Around line 24-26: normalizeLegacyAdmonitions currently applies LEGACY_SYNTAX
replacement across the entire input, which rewrites text inside fenced code
blocks; change normalizeLegacyAdmonitions so it skips fenced code segments by
detecting fences (``` or ~~~ with optional info string) and only applying
text.replace(LEGACY_SYNTAX, '$1[$2]') to non-fence regions—e.g., split/iterate
the string by fence boundaries or run a simple state machine toggling an inFence
flag when encountering a fence marker, preserving fence markers and their
contents unchanged while performing the legacy-syntax replacement only when
inFence is false.
- Around line 30-33: The admonition type check currently relies on a truthy
lookup (const ifmClass = ADMONITION_TYPES[node.name]) which can accept inherited
properties like toString; change the guard to only accept own properties by
checking ownership first (e.g., use
Object.prototype.hasOwnProperty.call(ADMONITION_TYPES, node.name) or
Object.hasOwn) and only then read ADMONITION_TYPES[node.name] into ifmClass so
visit's containerDirective handler rejects non-own keys; update the code around
visit(..., (node: DirectiveNode) => { ... }) and the ADMONITION_TYPES lookup
accordingly.
- Around line 96-99: The current label extraction only collects direct text
children of labelNode into textParts, which breaks for nested/inline formatted
labels; update the logic in markdown-admonitions where textParts is built
(around labelNode and isTextNode) to recursively traverse labelNode.children (or
use an existing helper) and collect text from all descendant text nodes, join
them into the label string, and keep the existing fallback to node.name when the
resulting label is empty. Ensure you update the reference where textParts is
used so the new recursive extraction replaces the direct-child-only approach.

---

Nitpick comments:
In `@src/kompendium/markdown-frontmatter.ts`:
- Around line 17-18: The YAML.parse call in storeData can throw for malformed
frontmatter causing markdownToHtml to fail; wrap the YAML.parse(item.value) call
in a try-catch inside storeData (function name: storeData) and on parse error
set file.data.frontmatter = undefined and record the error (e.g.
file.data.frontmatterError = error.message or attach the full error) and
optionally emit a warning (console.warn or existing logger) so callers can
choose lenient behavior; if strict behavior is desired, keep current behavior
but document that YAMLParseError will propagate from storeData/markdownToHtml.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3f761e0f-13d5-4d8a-bc90-c4022f75483e

📥 Commits

Reviewing files that changed from the base of the PR and between 611a1c4 and c86601d.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • package.json
  • src/kompendium/markdown-admonitions.ts
  • src/kompendium/markdown-code.ts
  • src/kompendium/markdown-frontmatter.ts
  • src/kompendium/markdown-nodes.ts
  • src/kompendium/markdown-typelinks.ts
  • src/kompendium/markdown.ts
  • src/kompendium/test/markdown.spec.ts
  • stencil.config.ts

Comment thread src/kompendium/markdown-admonitions.ts
Comment thread src/kompendium/markdown-admonitions.ts
Comment thread src/kompendium/markdown-admonitions.ts Outdated
jgroth and others added 2 commits April 14, 2026 12:58
unified v9 and several of its plugins are abandoned or incompatible
with current ESM-only tooling. Replaces the entire remark/rehype stack
with v11 equivalents and swaps unmaintained plugins
(`remark-admonitions`, `remark-parse-yaml`, `unist-util-flatmap`) for
custom implementations backed by `remark-directive` and `yaml`.

Co-Authored-By: Claude <noreply@anthropic.com>
PR #166 added ~60 new tests against the old unified v9 pipeline.
Merging them required fixing admonition types (`danger` -> `caution`,
`info` -> `important`), flipping the code-block type-link test to
match the v11 fix, and flattening the `describe` nesting.

Co-Authored-By: Claude <noreply@anthropic.com>
@jgroth jgroth force-pushed the unified-ecosystem-upgrade branch from c86601d to 9a0d98f Compare April 14, 2026 10:58
@jgroth jgroth merged commit 957e301 into main Apr 14, 2026
5 checks passed
@jgroth jgroth deleted the unified-ecosystem-upgrade branch April 14, 2026 11:05
@jgroth
Copy link
Copy Markdown
Owner Author

jgroth commented Apr 14, 2026

🎉 This PR is included in version 1.1.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant