Skip to content

Use native markdown dispatcher#113

Open
BurnedChris wants to merge 4 commits into
mainfrom
native-markdown-dispatcher
Open

Use native markdown dispatcher#113
BurnedChris wants to merge 4 commits into
mainfrom
native-markdown-dispatcher

Conversation

@BurnedChris

Copy link
Copy Markdown
Contributor

Summary

  • Move agent-facing markdown transforms under leadtype/markdown and keep leadtype/remark as compatibility/oracle surface.
  • Replace the built-in flattener stack with a single native component dispatcher while preserving byte-for-byte output.
  • Update docs, scripts, parity checks, and benchmarks for the native Sätteri markdown pipeline.

Test plan

  • bun x ultracite fix
  • bun run --filter leadtype check-types
  • bun run --filter leadtype test
  • bun run --filter leadtype build
  • bun run --filter tanstack test:engine-parity
  • BENCH_RUNS=5 BENCH_ENGINES=remark,satteri bun run --filter tanstack bench

Made with Cursor

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added a new leadtype/markdown entry point with default markdown transforms and stringifyMarkdown for MDX→Markdown output.
    • Introduced ./markdown as an exported package subpath for markdown tooling.
  • Bug Fixes
    • Improved MDX→Markdown consistency across conversion scripts, benchmarks, parity checks, and real-content runs.
    • Updated link-checking and generated artifacts to use the markdown transform pipeline.
  • Documentation
    • Refreshed CLI/API and guides to use markdownTransforms; remarkPlugins is deprecated as an alias.

Walkthrough

Adds a leadtype/markdown entry point, native markdown transforms, and engine-aware conversion. Conversion now uses markdownTransforms and defaults to satteri; the CLI --markdown-engine path is removed. Call sites, docs, tests, and compatibility exports are updated.

Changes

leadtype/markdown native transform pipeline

Layer / File(s) Summary
Module entry and core markdown runtime
packages/leadtype/package.json, packages/leadtype/rollup.config.ts, packages/leadtype/src/markdown/index.ts, packages/leadtype/src/markdown/builders.ts, packages/leadtype/src/markdown/stringify.ts, packages/leadtype/src/markdown/stringify.test.ts, packages/leadtype/src/internal/markdown-profile.ts
Adds the ./markdown export, builds the barrel, switches parsing to mdxToMdast, implements stringifyMarkdown, and adds markdown profiling support.
Dispatcher and default transform stack
packages/leadtype/src/markdown/default-transforms.ts, packages/leadtype/src/markdown/component-dispatcher.ts, packages/leadtype/src/internal/remark-phase.ts
Defines the default and legacy markdown transform stacks, the component dispatcher, and the phase tags used to order the built-in pipeline.
Plugin helper extraction and type-table cache
packages/leadtype/src/markdown/plugins/*, packages/leadtype/src/markdown/define-flattener.ts, packages/leadtype/src/markdown/define-flattener.test.ts, packages/leadtype/src/markdown/markdown-output.test.ts
Extracts JSX-to-markdown helpers, exports the type-table conversion helper, migrates define-flattener to mdast transforms, and updates markdown conversion tests.
Dual-engine conversion path and markdownTransforms
packages/leadtype/src/convert/convert.ts, packages/leadtype/src/cli/generate.ts, packages/leadtype/src/lint/runner.ts, packages/leadtype/src/convert/engine-parity.test.ts
Extends conversion options with markdownTransforms, defaults engine resolution to satteri, routes parsing and transforms through engine-aware helpers, and updates CLI/lint/parity coverage.
Remark compatibility and legacy include path
packages/leadtype/src/remark/*, packages/leadtype/src/mdx/*, packages/leadtype/src/index.ts, packages/leadtype/src/internal/package-surface.test.ts
Consolidates the remark barrel onto markdown exports, removes the old remark default plugins module, switches include parsing to mdxToMdast, and updates package-surface assertions.
Apps, docs, and examples migration
apps/c15t-example/scripts/*, apps/tanstack/scripts/*, apps/tanstack/src/..., apps/tanstack/tests/e2e/smoke.e2e.ts, apps/tanstack/tsconfig.json, docs/**/*.mdx, examples/shared-docs/flatteners.ts, packages/leadtype/scripts/generate-docs.ts, packages/leadtype/README.md, packages/leadtype/CHANGELOG.md
Switches call sites from remarkPlugins to markdownTransforms, updates benchmark labels and ordering, and refreshes docs, examples, aliases, and reference text.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant convertAllMdx
  participant prepareMdxConversion
  participant runMdxAstTransforms
  participant stringifyMarkdown
  Caller->>convertAllMdx: markdownTransforms + markdownEngine
  convertAllMdx->>prepareMdxConversion: resolve engine and transforms
  prepareMdxConversion-->>convertAllMdx: engine-aware conversion state
  convertAllMdx->>runMdxAstTransforms: transform mdast tree
  runMdxAstTransforms-->>convertAllMdx: transformed tree
  convertAllMdx->>stringifyMarkdown: serialize tree
  stringifyMarkdown-->>convertAllMdx: markdown output
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • inthhq/leadtype#106: Directly overlaps on the engine-parity and conversion-path changes around convertAllMdx and markdownTransforms.

Poem

🐇 I hopped from remark into markdown light,
With transforms in tow and satteri in sight.
The pipeline hums, the carrots align,
New paths for my scripts, all tidy and fine.
Hop-hop! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.42% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: introducing a native markdown dispatcher for the Leadtype markdown pipeline.
Description check ✅ Passed The description accurately summarizes the move to leadtype/markdown, the native dispatcher, and related docs, scripts, tests, and benchmarks.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch native-markdown-dispatcher

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/reference/convert.mdx (1)

63-68: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix the writeMdxFileAsMarkdown example signature.

This snippet still documents a non-existent object overload. writeMdxFileAsMarkdown takes the MDX file path as the first argument, and its config uses outDir, not srcPath / outPath. Copying this example will fail against the actual API.

Suggested fix
-await writeMdxFileAsMarkdown({
-  srcPath: "docs/quickstart.mdx",
-  outPath: "public/docs/quickstart.md",
-  markdownTransforms: defaultMarkdownTransforms,
-});
+await writeMdxFileAsMarkdown("docs/quickstart.mdx", {
+  outDir: "public/docs",
+  markdownTransforms: defaultMarkdownTransforms,
+});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reference/convert.mdx` around lines 63 - 68, The
`writeMdxFileAsMarkdown` example is using a stale object-style signature that
does not match the वास्तविक API. Update the docs snippet to show
`writeMdxFileAsMarkdown` being called with the MDX file path as the first
argument and the config using `outDir` instead of `srcPath`/`outPath`, so the
example aligns with the actual function signature and can be copied without
failing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/tanstack/scripts/bench.ts`:
- Around line 263-266: The stage lookup in the bench aggregation logic is
treating valid 0ms medians as missing because the truthy check on remark and
satteri rejects zero values. Update the guard in the stage-processing code that
uses byLabel.get for remark:${stage} and satteri:${stage} to test for undefined
explicitly instead of relying on truthiness, so zero-valued medians are still
included.
- Around line 35-50: The BENCH_ENGINES parsing in bench.ts can leave ENGINES
empty when the env var is an empty string or only contains commas/whitespace,
because the current defaulting and token filtering skip blanks. Update the
BENCH_ENGINES handling around requestedEngines/ENGINES to treat an empty
effective list as invalid, and fail fast with the same stderr/exit path before
benchmark execution continues. Use the existing validation flow in the loop that
checks rawEngine/engine and the ENGINES array to locate where to add the
empty-list guard.

In `@apps/tanstack/src/routes/playground.tsx`:
- Around line 31-35: The playground recipe copy is inconsistent with the example
because it still says “remark entry points” while the code uses
leadtype/markdown. Update the adjacent summary text in the playground card so it
matches the new entry point wording shown by the Markdown transform example in
the playground route.

In `@docs/concepts/architecture.mdx`:
- Line 16: The architecture table currently lists only leadtype/markdown, but
leadtype/remark is still a public compatibility export and should remain
documented. Update the architecture overview in the relevant markdown table to
include leadtype/remark alongside leadtype/markdown, either as its own row or a
compatibility note, so the documented surface matches the exported API. Use the
existing architecture table entry for leadtype/markdown as the place to add the
leadtype/remark reference.

In `@docs/package-docs/bundle.mdx`:
- Line 107: The convertAllMdx example currently uses only
defaultMarkdownTransforms, so copied scripts that rely on include tags will not
resolve partials. Update the example to use includeMarkdown together with
defaultMarkdownTransforms in the markdown transform list, and reference the
convertAllMdx setup so users can locate the change easily.

In `@docs/writing/components.mdx`:
- Line 33: The migration guidance still says “plugin stack” even though the code
and examples use markdownTransforms/defaultMarkdownTransforms. Update the
wording in the relevant docs section to “transform stack” so the terminology
matches the current API and the includeMarkdown placement guidance remains
consistent.

In `@packages/leadtype/src/convert/engine-parity.test.ts`:
- Around line 86-93: The legacy transform factory is inserting the TypeTable
node helper directly instead of the remark pluggable, which breaks the intended
parity override. Update createLegacyMarkdownTransforms to filter and re-add the
actual transform symbol used by the markdown pipeline (the wrapper from
packages/leadtype/src/markdown/plugins/type-table.ts, not typeTableToMarkdown),
so the legacy path still overrides TypeTable behavior through the pluggable
list.

In `@packages/leadtype/src/markdown/default-transforms.ts`:
- Around line 96-119: Add AutoTypeTable to the built-in recognized component
list in BUILTIN_FLATTENER_COMPONENT_NAMES. The lintDocs() recognizedComponents
set is derived from this array, so update the default-transforms list to include
AutoTypeTable alongside the other supported flattener components. Keep the
existing nativeMarkdownComponentsToMarkdown and typeTableToMarkdown support
aligned with this list so valid AutoTypeTable usage is no longer reported as
unflattened.

In `@packages/leadtype/src/markdown/define-flattener.ts`:
- Around line 102-114: `flattenChildren` is discarding the caller’s source path
by always passing an empty `filePath` into `runMdastTransformsSync`, which
breaks nested type resolution in `ExtractedTypeTable` and related built-ins.
Update `flattenChildren` in `define-flattener` to accept and thread through the
current VFile path (for example from `file?.path`) instead of hardcoding `""`,
so `typeTableToMarkdown` can still use its `basePath` fallback when resolving
relative types. Keep the change localized to the `flattenChildren` call path and
preserve existing markdown stringification behavior.

In `@packages/leadtype/src/markdown/markdown-output.test.ts`:
- Line 104: The TypeTable cases are using the low-level node converter instead
of the pluggable transform contract, so update the markdown-output tests to
register the actual TypeTable transform wrapper rather than
`typeTableToMarkdown` in `convertMdxToMarkdown`. Use the existing
`typeTableToMarkdown` helper only through the transform entry point, and make
the same adjustment in the other affected TypeTable test cases so they exercise
the migrated pipeline consistently.

In `@packages/leadtype/src/markdown/plugins/callout.ts`:
- Around line 105-110: Normalize string-literal expression props in
calloutToMarkdown before using them, because getAttributeValue() can return
quoted expression source for {....} attrs. Strip the wrapper for both
variant/type and title the same way audienceToMarkdown() does, so
variant={"warning"} matches variantLabelAndEmoji() correctly and title={"Heads
up"} is stored without extra quotes.

In `@packages/leadtype/src/markdown/plugins/cards.ts`:
- Around line 133-139: The Cards visitor in the markdown plugin mutates
parent.children during visit without returning the updated traversal state,
which can cause the next sibling to be skipped or the detached node to be
revisited. Update the visitor logic in cards.ts to return [SKIP, index]
immediately after each splice in the cardsToMarkdown handling path, using the
Cards visitor and its parent.children splice branch as the fix point so
traversal resumes from the adjusted index correctly.

In `@packages/leadtype/src/markdown/plugins/command-tabs.ts`:
- Around line 67-80: Normalize attribute values in CommandTabs before using
them: getAttributeValue() may return quoted expression source for {""}-style
props, so rawMode and rawCommand should unwrap string-literal expressions the
same way audienceToMarkdown() does. Update the CommandTabs parsing logic to
strip the extra quotes from command and mode before trimming, so
mode={"install"} resolves to install and command={"foo"} generates commands
without embedded quotes.

In `@packages/leadtype/src/markdown/plugins/details.ts`:
- Around line 58-60: The legacy details plugin only matches the lowercase tag,
which causes `<Details>` inputs to bypass the compatibility path even though the
native dispatcher and default transform registration already recognize both
variants. Update `remarkDetailsToMarkdown` in `details.ts` so the JSX component
processor also registers the capitalized `Details` name alongside `details`,
keeping the legacy flattener aligned with the native engine behavior.

In `@packages/leadtype/src/markdown/plugins/type-table.ts`:
- Around line 49-52: The __extractedTypeByKey cache in type-table.ts is
accumulating stale entries because the map key currently bakes in mtimeMs, so
each edit creates a new entry instead of replacing the old one. Update the
caching in the type extraction flow (around the logic that reads and stores
extracted types) so the map is keyed only by ${resolvedPath}\0${typeName}, and
move mtimeMs into the stored value or payload. Make sure the read/write path in
the type-table plugin reuses the same stable key so newer parses overwrite prior
results instead of leaving historical versions resident.

In `@packages/leadtype/src/markdown/stringify.test.ts`:
- Around line 5-73: Add regression coverage in stringifier tests for the new
native inline paths: extend stringifyMarkdown in stringify.test.ts with cases
for inlineCode values that contain backticks or leading/trailing significant
spaces, and for mdxJsxTextElement children to verify the fallback serialization
is preserved. Use the existing stringifyMarkdown test suite and Root fixtures to
locate the new assertions, and keep the expectations focused on the emitted
markdown text.

In `@packages/leadtype/src/markdown/stringify.ts`:
- Around line 95-109: The inlineCode() serializer is normalizing whitespace and
always using a single-backtick delimiter, which breaks round-tripping for mdast
code spans with repeated spaces or embedded backticks. Update inlineCode() so it
preserves the original content spacing for inline code values and chooses a safe
backtick delimiter length based on the content, keeping the existing padding
logic in stringify.ts intact.
- Around line 375-390: The MDX JSX serializer in stringify.ts treats
mdxJsxTextElement the same as mdxJsxFlowElement, which adds block-style newlines
and paragraph breaks to inline JSX. Update the stringifyNode branch for jsx
nodes to distinguish mdxJsxTextElement from mdxJsxFlowElement: keep
mdxJsxFlowElement using the current multiline wrapper behavior, but serialize
mdxJsxTextElement inline by joining children without paragraph separators and
without surrounding newlines. Use the existing helpers like stringifyNode,
hasChildren, and stringifyMdxAttribute to preserve attribute handling while
changing only the text-element formatting.

In `@packages/leadtype/src/markdown/transform.ts`:
- Around line 39-73: createMdastTransforms() currently only converts bare
attachers and misses the broader PluggableList behavior, so valid presets and
context-bound unified plugins can be handled incorrectly. Update toTransform()
and createMdastTransforms() to either explicitly restrict the API to leaf
attachers or invoke the plugins through a real unified processor so attachers
receive the proper this binding and presets are expanded before transformer
extraction. Use the existing sortRemarkPluginsByPhase(), toTransform(), and
createMdastTransforms() flow as the place to apply the fix.

---

Outside diff comments:
In `@docs/reference/convert.mdx`:
- Around line 63-68: The `writeMdxFileAsMarkdown` example is using a stale
object-style signature that does not match the वास्तविक API. Update the docs
snippet to show `writeMdxFileAsMarkdown` being called with the MDX file path as
the first argument and the config using `outDir` instead of `srcPath`/`outPath`,
so the example aligns with the actual function signature and can be copied
without failing.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d378c208-2e95-4f4a-a2a1-e4305f1fa3f4

📥 Commits

Reviewing files that changed from the base of the PR and between 2f52e21 and 6e75a4f.

📒 Files selected for processing (77)
  • apps/c15t-example/scripts/llm-generate-real.ts
  • apps/c15t-example/scripts/test-real.ts
  • apps/tanstack/README.md
  • apps/tanstack/scripts/bench.ts
  • apps/tanstack/scripts/convert-real.ts
  • apps/tanstack/scripts/mdx-convert.ts
  • apps/tanstack/scripts/search-bench.ts
  • apps/tanstack/scripts/test-engine-parity.ts
  • apps/tanstack/scripts/test-pipeline.ts
  • apps/tanstack/scripts/test-real.ts
  • apps/tanstack/src/lib/docs.ts
  • apps/tanstack/src/routes/playground.tsx
  • apps/tanstack/tests/e2e/smoke.e2e.ts
  • apps/tanstack/tsconfig.json
  • docs/concepts/architecture.mdx
  • docs/package-docs/bundle.mdx
  • docs/pipeline/generate-static-artifacts.mdx
  • docs/reference/cli.mdx
  • docs/reference/convert.mdx
  • docs/reference/llm.mdx
  • docs/reference/mdx.mdx
  • docs/reference/remark.mdx
  • docs/writing/components.mdx
  • docs/writing/write-for-agents.mdx
  • examples/shared-docs/flatteners.ts
  • packages/leadtype/CHANGELOG.md
  • packages/leadtype/README.md
  • packages/leadtype/package.json
  • packages/leadtype/rollup.config.ts
  • packages/leadtype/scripts/generate-docs.ts
  • packages/leadtype/src/cli/generate.ts
  • packages/leadtype/src/convert/convert.ts
  • packages/leadtype/src/convert/engine-parity.test.ts
  • packages/leadtype/src/index.ts
  • packages/leadtype/src/internal/package-surface.test.ts
  • packages/leadtype/src/internal/remark-phase.ts
  • packages/leadtype/src/lint/runner.ts
  • packages/leadtype/src/markdown/builders.ts
  • packages/leadtype/src/markdown/component-dispatcher.ts
  • packages/leadtype/src/markdown/default-transforms.ts
  • packages/leadtype/src/markdown/define-flattener.test.ts
  • packages/leadtype/src/markdown/define-flattener.ts
  • packages/leadtype/src/markdown/index.ts
  • packages/leadtype/src/markdown/libs/attributes.ts
  • packages/leadtype/src/markdown/libs/content-processor.ts
  • packages/leadtype/src/markdown/libs/generic-processor.ts
  • packages/leadtype/src/markdown/libs/guards.ts
  • packages/leadtype/src/markdown/libs/index.ts
  • packages/leadtype/src/markdown/libs/node-creators.ts
  • packages/leadtype/src/markdown/libs/text.ts
  • packages/leadtype/src/markdown/libs/types.ts
  • packages/leadtype/src/markdown/markdown-output.test.ts
  • packages/leadtype/src/markdown/plugins/accordion.ts
  • packages/leadtype/src/markdown/plugins/audience.ts
  • packages/leadtype/src/markdown/plugins/callout.ts
  • packages/leadtype/src/markdown/plugins/cards.ts
  • packages/leadtype/src/markdown/plugins/command-tabs.ts
  • packages/leadtype/src/markdown/plugins/details.ts
  • packages/leadtype/src/markdown/plugins/example.ts
  • packages/leadtype/src/markdown/plugins/file-tree.ts
  • packages/leadtype/src/markdown/plugins/mermaid.ts
  • packages/leadtype/src/markdown/plugins/prompt.ts
  • packages/leadtype/src/markdown/plugins/section.ts
  • packages/leadtype/src/markdown/plugins/steps.ts
  • packages/leadtype/src/markdown/plugins/tabs.ts
  • packages/leadtype/src/markdown/plugins/topic-switcher.ts
  • packages/leadtype/src/markdown/plugins/type-table.ts
  • packages/leadtype/src/markdown/stringify.test.ts
  • packages/leadtype/src/markdown/stringify.ts
  • packages/leadtype/src/markdown/transform.ts
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/mdx/source-preset.ts
  • packages/leadtype/src/remark/default-plugins.ts
  • packages/leadtype/src/remark/index.ts
  • packages/leadtype/src/remark/plugins/include.remark.ts
  • packages/leadtype/src/remark/plugins/prompt.remark.ts
  • packages/leadtype/src/remark/plugins/type-table-jsx.remark.ts
💤 Files with no reviewable changes (2)
  • packages/leadtype/src/remark/default-plugins.ts
  • packages/leadtype/src/remark/plugins/prompt.remark.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use const assertions (as const) for immutable values and literal types
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/leadtype/src/markdown/stringify.test.ts
  • packages/leadtype/src/index.ts
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/rollup.config.ts
  • packages/leadtype/src/internal/remark-phase.ts
  • packages/leadtype/src/mdx/source-preset.ts
  • apps/tanstack/src/routes/playground.tsx
  • packages/leadtype/src/markdown/plugins/section.ts
  • apps/tanstack/scripts/mdx-convert.ts
  • packages/leadtype/src/remark/plugins/type-table-jsx.remark.ts
  • examples/shared-docs/flatteners.ts
  • apps/tanstack/tests/e2e/smoke.e2e.ts
  • packages/leadtype/src/markdown/plugins/tabs.ts
  • packages/leadtype/src/markdown/plugins/details.ts
  • apps/c15t-example/scripts/llm-generate-real.ts
  • packages/leadtype/src/markdown/plugins/prompt.ts
  • apps/tanstack/scripts/convert-real.ts
  • apps/tanstack/scripts/search-bench.ts
  • packages/leadtype/scripts/generate-docs.ts
  • packages/leadtype/src/markdown/transform.ts
  • apps/tanstack/src/lib/docs.ts
  • packages/leadtype/src/internal/package-surface.test.ts
  • apps/c15t-example/scripts/test-real.ts
  • packages/leadtype/src/markdown/index.ts
  • packages/leadtype/src/markdown/plugins/accordion.ts
  • apps/tanstack/scripts/test-pipeline.ts
  • packages/leadtype/src/markdown/builders.ts
  • packages/leadtype/src/markdown/plugins/steps.ts
  • apps/tanstack/scripts/test-real.ts
  • packages/leadtype/src/markdown/plugins/callout.ts
  • packages/leadtype/src/markdown/plugins/audience.ts
  • apps/tanstack/scripts/test-engine-parity.ts
  • packages/leadtype/src/markdown/plugins/file-tree.ts
  • packages/leadtype/src/markdown/define-flattener.ts
  • packages/leadtype/src/markdown/plugins/mermaid.ts
  • packages/leadtype/src/remark/plugins/include.remark.ts
  • packages/leadtype/src/markdown/plugins/topic-switcher.ts
  • packages/leadtype/src/lint/runner.ts
  • packages/leadtype/src/markdown/plugins/cards.ts
  • packages/leadtype/src/markdown/stringify.ts
  • packages/leadtype/src/markdown/plugins/command-tabs.ts
  • packages/leadtype/src/markdown/plugins/example.ts
  • packages/leadtype/src/remark/index.ts
  • packages/leadtype/src/markdown/define-flattener.test.ts
  • packages/leadtype/src/markdown/default-transforms.ts
  • packages/leadtype/src/markdown/component-dispatcher.ts
  • packages/leadtype/src/markdown/plugins/type-table.ts
  • apps/tanstack/scripts/bench.ts
  • packages/leadtype/src/convert/engine-parity.test.ts
  • packages/leadtype/src/markdown/markdown-output.test.ts
  • packages/leadtype/src/convert/convert.ts
  • packages/leadtype/src/cli/generate.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, never var
Always await promises in async functions - don't forget to use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors
Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully - don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Don't use eval() or assign directly to document.cookie
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Use descriptive names for functions, variables, and types for meaningful naming
Add comments for complex logic, but prefer self-documenting code

Files:

  • packages/leadtype/src/markdown/stringify.test.ts
  • packages/leadtype/src/index.ts
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/rollup.config.ts
  • packages/leadtype/src/internal/remark-phase.ts
  • packages/leadtype/src/mdx/source-preset.ts
  • apps/tanstack/src/routes/playground.tsx
  • packages/leadtype/src/markdown/plugins/section.ts
  • apps/tanstack/scripts/mdx-convert.ts
  • packages/leadtype/src/remark/plugins/type-table-jsx.remark.ts
  • examples/shared-docs/flatteners.ts
  • apps/tanstack/tests/e2e/smoke.e2e.ts
  • packages/leadtype/src/markdown/plugins/tabs.ts
  • packages/leadtype/src/markdown/plugins/details.ts
  • apps/c15t-example/scripts/llm-generate-real.ts
  • packages/leadtype/src/markdown/plugins/prompt.ts
  • apps/tanstack/scripts/convert-real.ts
  • apps/tanstack/scripts/search-bench.ts
  • packages/leadtype/scripts/generate-docs.ts
  • packages/leadtype/src/markdown/transform.ts
  • apps/tanstack/src/lib/docs.ts
  • packages/leadtype/src/internal/package-surface.test.ts
  • apps/c15t-example/scripts/test-real.ts
  • packages/leadtype/src/markdown/index.ts
  • packages/leadtype/src/markdown/plugins/accordion.ts
  • apps/tanstack/scripts/test-pipeline.ts
  • packages/leadtype/src/markdown/builders.ts
  • packages/leadtype/src/markdown/plugins/steps.ts
  • apps/tanstack/scripts/test-real.ts
  • packages/leadtype/src/markdown/plugins/callout.ts
  • packages/leadtype/src/markdown/plugins/audience.ts
  • apps/tanstack/scripts/test-engine-parity.ts
  • packages/leadtype/src/markdown/plugins/file-tree.ts
  • packages/leadtype/src/markdown/define-flattener.ts
  • packages/leadtype/src/markdown/plugins/mermaid.ts
  • packages/leadtype/src/remark/plugins/include.remark.ts
  • packages/leadtype/src/markdown/plugins/topic-switcher.ts
  • packages/leadtype/src/lint/runner.ts
  • packages/leadtype/src/markdown/plugins/cards.ts
  • packages/leadtype/src/markdown/stringify.ts
  • packages/leadtype/src/markdown/plugins/command-tabs.ts
  • packages/leadtype/src/markdown/plugins/example.ts
  • packages/leadtype/src/remark/index.ts
  • packages/leadtype/src/markdown/define-flattener.test.ts
  • packages/leadtype/src/markdown/default-transforms.ts
  • packages/leadtype/src/markdown/component-dispatcher.ts
  • packages/leadtype/src/markdown/plugins/type-table.ts
  • apps/tanstack/scripts/bench.ts
  • packages/leadtype/src/convert/engine-parity.test.ts
  • packages/leadtype/src/markdown/markdown-output.test.ts
  • packages/leadtype/src/convert/convert.ts
  • packages/leadtype/src/cli/generate.ts
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write assertions inside it() or test() blocks
Avoid done callbacks in async tests - use async/await instead
Don't use .only or .skip in committed code
Keep test suites reasonably flat - avoid excessive describe nesting

Files:

  • packages/leadtype/src/markdown/stringify.test.ts
  • packages/leadtype/src/internal/package-surface.test.ts
  • packages/leadtype/src/markdown/define-flattener.test.ts
  • packages/leadtype/src/convert/engine-parity.test.ts
  • packages/leadtype/src/markdown/markdown-output.test.ts
**/index.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Avoid barrel files (index files that re-export everything)

Files:

  • packages/leadtype/src/index.ts
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/markdown/index.ts
  • packages/leadtype/src/remark/index.ts
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx}: Use function components over class components in React
Call hooks at the top level only, never conditionally
Specify all dependencies in hook dependency arrays correctly
Use the key prop for elements in iterables (prefer unique IDs over array indices)
Nest children between opening and closing tags instead of passing as props
Don't define components inside other components
Avoid dangerouslySetInnerHTML unless absolutely necessary
Use proper image components (e.g., Next.js <Image>) over <img> tags
Use Next.js <Image> component for images
Use next/head or App Router metadata API for head elements in Next.js
Use Server Components for async data fetching instead of async Client Components in Next.js
Use ref as a prop instead of React.forwardRef in React 19+

Files:

  • apps/tanstack/src/routes/playground.tsx
**/*.{jsx,tsx,html}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx,html}: Use semantic HTML and ARIA attributes for accessibility: provide meaningful alt text for images, use proper heading hierarchy, add labels for form inputs, include keyboard event handlers alongside mouse events, use semantic elements instead of divs with roles
Add rel="noopener" when using target="_blank" on links

Files:

  • apps/tanstack/src/routes/playground.tsx
🧠 Learnings (3)
📚 Learning: 2026-06-07T09:16:11.158Z
Learnt from: KayleeWilliams
Repo: inthhq/leadtype PR: 87
File: apps/next-example/tests/e2e/smoke.e2e.ts:6-25
Timestamp: 2026-06-07T09:16:11.158Z
Learning: In the leadtype repo’s framework example apps under `apps/`, treat each app as an intentionally self-contained, copy-pasteable reference. During code review, avoid introducing cross-app/cross-workspace imports (e.g., importing shared test utilities from sibling example apps), since that couples the examples and can break vendoring a single app. Allow duplicated helper implementations within an app’s tests (including E2E files) when they exist to keep the app standalone—e.g., duplicating helpers like `installWebMcpMock` across E2E test files is acceptable by design.

Applied to files:

  • apps/tanstack/tests/e2e/smoke.e2e.ts
📚 Learning: 2026-06-07T09:16:18.811Z
Learnt from: KayleeWilliams
Repo: inthhq/leadtype PR: 87
File: apps/nuxt-example/tests/e2e/smoke.e2e.ts:5-24
Timestamp: 2026-06-07T09:16:18.811Z
Learning: In the leadtype example apps’ e2e smoke tests (e.g., apps/*/tests/e2e/smoke.e2e.ts), duplicated helper functions (like installWebMcpMock) are intentional. Keep helpers within each example app’s test file so each example remains a standalone, copy-whole reference. Avoid cross-workspace imports from shared test utilities unless there’s a strong, concrete reason to change the standalone/coupling design.

Applied to files:

  • apps/tanstack/tests/e2e/smoke.e2e.ts
📚 Learning: 2026-06-07T09:16:22.079Z
Learnt from: KayleeWilliams
Repo: inthhq/leadtype PR: 87
File: apps/tanstack/tests/e2e/smoke.e2e.ts:14-33
Timestamp: 2026-06-07T09:16:22.079Z
Learning: In inthhq/leadtype, for each example app’s standalone E2E tests under apps/*/tests/e2e (e.g., smoke.e2e.ts), avoid review suggestions to extract or import shared test helpers across workspaces. Helpers such as installWebMcpMock (and similar utilities) may be intentionally duplicated within each app’s test files to prevent coupling between separate example apps.

Applied to files:

  • apps/tanstack/tests/e2e/smoke.e2e.ts
🪛 ast-grep (0.44.0)
packages/leadtype/src/markdown/stringify.ts

[warning] 63-65: Avoid hand-rolled HTML escaping (replacing characters with HTML entities); use a vetted encoder/sanitizer such as DOMPurify or sanitize-html.
Context: value
.replace(/\/g, "\\")
.replace(/\n (?=\S)/g, "\n&#x20;")
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

(manual-sanitization-typescript)


[warning] 350-350: Avoid hand-rolled HTML escaping (replacing characters with HTML entities); use a vetted encoder/sanitizer such as DOMPurify or sanitize-html.
Context: attribute.value.replace(/"/g, """)
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

(manual-sanitization-typescript)

packages/leadtype/src/convert/convert.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🪛 LanguageTool
docs/reference/remark.mdx

[uncategorized] ~55-~55: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...ypeTable>and→ markdown table. 14.remarkAccordionToMarkdown` ...

(MARKDOWN_NNP)

🔇 Additional comments (32)
packages/leadtype/src/convert/convert.ts (1)

25-30: LGTM!

Also applies to: 181-182, 320-345, 351-360, 405-409, 431-442, 467-486, 506-554, 888-896, 938-967, 1008-1023, 1093-1128, 1162-1168, 1311-1316, 1363-1390

packages/leadtype/src/cli/generate.ts (1)

8-8: LGTM!

Also applies to: 49-53, 144-179, 236-269, 364-448, 2570-2588

packages/leadtype/src/remark/index.ts (1)

3-45: LGTM!

packages/leadtype/src/remark/plugins/include.remark.ts (1)

18-18: LGTM!

Also applies to: 238-242, 291-291, 551-551

packages/leadtype/src/remark/plugins/type-table-jsx.remark.ts (1)

17-27: LGTM!

packages/leadtype/src/internal/remark-phase.ts (1)

2-5: LGTM!

Also applies to: 15-15

apps/c15t-example/scripts/llm-generate-real.ts (1)

9-9: LGTM!

Also applies to: 18-18

apps/c15t-example/scripts/test-real.ts (1)

12-12: LGTM!

Also applies to: 57-57

apps/tanstack/scripts/bench.ts (1)

15-23: LGTM!

Also applies to: 151-175, 203-218, 301-301

apps/tanstack/scripts/test-pipeline.ts (1)

6-8: LGTM!

Also applies to: 19-34

apps/tanstack/scripts/test-real.ts (1)

14-14: LGTM!

Also applies to: 59-59

apps/tanstack/src/lib/docs.ts (1)

78-80: LGTM!

examples/shared-docs/flatteners.ts (1)

1-1: LGTM!

packages/leadtype/src/index.ts (1)

6-6: LGTM!

apps/tanstack/README.md (1)

28-28: LGTM!

apps/tanstack/scripts/mdx-convert.ts (1)

14-37: LGTM!

Also applies to: 50-50

docs/pipeline/generate-static-artifacts.mdx (1)

174-186: LGTM!

docs/reference/cli.mdx (1)

418-418: LGTM!

docs/reference/convert.mdx (1)

23-29: LGTM!

Also applies to: 37-50, 80-88

docs/reference/llm.mdx (1)

236-241: LGTM!

docs/reference/mdx.mdx (1)

14-14: LGTM!

docs/reference/remark.mdx (1)

2-13: LGTM!

Also applies to: 32-32, 41-41, 55-55, 70-74, 99-99, 126-139, 181-207, 225-226

apps/tanstack/scripts/search-bench.ts (1)

26-26: LGTM!

Also applies to: 127-127

apps/tanstack/scripts/test-engine-parity.ts (1)

3-3: LGTM!

Also applies to: 13-17, 59-64, 91-91

apps/tanstack/tests/e2e/smoke.e2e.ts (1)

83-83: LGTM!

apps/tanstack/tsconfig.json (1)

25-25: LGTM!

packages/leadtype/package.json (1)

70-73: LGTM!

packages/leadtype/rollup.config.ts (1)

18-18: LGTM!

packages/leadtype/src/markdown/plugins/mermaid.ts (1)

1-1: LGTM!

Also applies to: 76-93

packages/leadtype/src/markdown/plugins/section.ts (1)

6-10: LGTM!

Also applies to: 24-24

packages/leadtype/src/markdown/plugins/tabs.ts (1)

181-192: LGTM!

packages/leadtype/src/markdown/plugins/topic-switcher.ts (1)

9-9: LGTM!

Also applies to: 116-151

Comment on lines +35 to 50
const requestedEngines = process.env.BENCH_ENGINES ?? "remark,satteri";
const ENGINES: BenchMarkdownEngine[] = [];
for (const rawEngine of (process.env.BENCH_ENGINES ?? "satteri").split(",")) {
for (const rawEngine of requestedEngines.split(",")) {
const engine = rawEngine.trim();
if (!engine) {
if (engine.length === 0) {
continue;
}
if (MARKDOWN_ENGINES.has(engine as BenchMarkdownEngine)) {
ENGINES.push(engine as BenchMarkdownEngine);
continue;
if (engine !== "remark" && engine !== "satteri") {
process.stderr.write(
`BENCH_ENGINES must contain remark,satteri values, got ${JSON.stringify(rawEngine)}\n`
);
process.exit(2);
}
if (!ENGINES.includes(engine)) {
ENGINES.push(engine);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject an empty effective BENCH_ENGINES list.

BENCH_ENGINES="" (or just commas/whitespace) leaves ENGINES empty because ?? does not treat an empty string as unset and the loop skips blank tokens. The script then produces an empty benchmark/report instead of failing fast.

Suggested fix
 const requestedEngines = process.env.BENCH_ENGINES ?? "remark,satteri";
 const ENGINES: BenchMarkdownEngine[] = [];
 for (const rawEngine of requestedEngines.split(",")) {
   const engine = rawEngine.trim();
   if (engine.length === 0) {
     continue;
   }
   if (engine !== "remark" && engine !== "satteri") {
     process.stderr.write(
       `BENCH_ENGINES must contain remark,satteri values, got ${JSON.stringify(rawEngine)}\n`
     );
     process.exit(2);
   }
   if (!ENGINES.includes(engine)) {
     ENGINES.push(engine);
   }
 }
+if (ENGINES.length === 0) {
+  process.stderr.write(
+    "BENCH_ENGINES must include at least one of: remark,satteri\n"
+  );
+  process.exit(2);
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const requestedEngines = process.env.BENCH_ENGINES ?? "remark,satteri";
const ENGINES: BenchMarkdownEngine[] = [];
for (const rawEngine of (process.env.BENCH_ENGINES ?? "satteri").split(",")) {
for (const rawEngine of requestedEngines.split(",")) {
const engine = rawEngine.trim();
if (!engine) {
if (engine.length === 0) {
continue;
}
if (MARKDOWN_ENGINES.has(engine as BenchMarkdownEngine)) {
ENGINES.push(engine as BenchMarkdownEngine);
continue;
if (engine !== "remark" && engine !== "satteri") {
process.stderr.write(
`BENCH_ENGINES must contain remark,satteri values, got ${JSON.stringify(rawEngine)}\n`
);
process.exit(2);
}
if (!ENGINES.includes(engine)) {
ENGINES.push(engine);
}
const requestedEngines = process.env.BENCH_ENGINES ?? "remark,satteri";
const ENGINES: BenchMarkdownEngine[] = [];
for (const rawEngine of requestedEngines.split(",")) {
const engine = rawEngine.trim();
if (engine.length === 0) {
continue;
}
if (engine !== "remark" && engine !== "satteri") {
process.stderr.write(
`BENCH_ENGINES must contain remark,satteri values, got ${JSON.stringify(rawEngine)}\n`
);
process.exit(2);
}
if (!ENGINES.includes(engine)) {
ENGINES.push(engine);
}
}
if (ENGINES.length === 0) {
process.stderr.write(
"BENCH_ENGINES must include at least one of: remark,satteri\n"
);
process.exit(2);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/tanstack/scripts/bench.ts` around lines 35 - 50, The BENCH_ENGINES
parsing in bench.ts can leave ENGINES empty when the env var is an empty string
or only contains commas/whitespace, because the current defaulting and token
filtering skip blanks. Update the BENCH_ENGINES handling around
requestedEngines/ENGINES to treat an empty effective list as invalid, and fail
fast with the same stderr/exit path before benchmark execution continues. Use
the existing validation flow in the loop that checks rawEngine/engine and the
ENGINES array to locate where to add the empty-list guard.

Comment thread apps/tanstack/scripts/bench.ts
Comment thread apps/tanstack/src/routes/playground.tsx
Comment thread docs/concepts/architecture.mdx Outdated
Comment thread docs/package-docs/bundle.mdx Outdated
Comment thread packages/leadtype/src/markdown/plugins/type-table.ts
Comment thread packages/leadtype/src/markdown/stringify.test.ts
Comment thread packages/leadtype/src/markdown/stringify.ts Outdated
Comment thread packages/leadtype/src/markdown/stringify.ts
Comment thread packages/leadtype/src/markdown/transform.ts Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Important

One e2e smoke assertion was missed during the leadtype/remarkleadtype/markdown rename and will fail in CI. The dispatcher refactor itself looks sound. Remaining items are informational.

Reviewed changes — initial review of the reapplied refactor that replaces the sequential remark flattener stack with a single native component dispatcher under a new leadtype/markdown surface, preserving output parity with the legacy stack.

  • Native dispatchermarkdown/component-dispatcher.ts (nativeMarkdownComponentsToMarkdown) walks the tree once with priority-windowed recursion (processChild/processInsertedChildren) to emulate sequential per-component flattening; mutation-during-iteration accounting traced correct.
  • New leadtype/markdown surface — package ./markdown export, rollup entry, tsconfig path, and package-surface.test.ts are wired consistently; leadtype/remark retained with @deprecated aliases.
  • Native stringifiermarkdown/stringify.ts (emphasis *, thematic break ***); per-component plugins moved to markdown/plugins/*, with prompt.ts now using native stringifyMarkdown in place of the deleted prompt.remark.ts.
  • Parity tests reworked — convert-level parity (legacy stack + remark engine vs dispatcher + satteri engine) retained over fixtures and the full docs corpus; the CLI cross-engine artifact test was replaced with a single-engine satteri smoke test.
  • Docs / scripts / benchmarks updated for the new transform nomenclature.

⚠️ E2e smoke test still asserts the old page heading

The reference page renamed its frontmatter title from "Remark plugins" to "Markdown transforms" in docs/reference/remark.mdx, and the smoke test was partially updated (the defaultRemarkPluginsdefaultMarkdownTransforms body assertion at smoke.e2e.ts:83). The heading assertion further down was not.

apps/tanstack/tests/e2e/smoke.e2e.ts:89 still requires a visible heading named exactly "Remark plugins", which no longer renders, so this test will fail against the built app.

Technical details
# E2e smoke test asserts a heading that no longer exists

## Affected sites
- `apps/tanstack/tests/e2e/smoke.e2e.ts:89``page.getByRole("heading", { name: "Remark plugins", exact: true })` — the page h1 now derives from `title: "Markdown transforms"`.
- `docs/reference/remark.mdx:2` — title changed to `"Markdown transforms"`.

## Required outcome
- The heading assertion matches the new rendered title so the smoke test passes.

## Suggested approach
- Update line 89 to `{ name: "Markdown transforms", exact: true }`.

ℹ️ CLI cross-engine artifact parity is no longer asserted

The prior generates matching CLI artifacts with both engines test ran generate under both --markdown-engine remark and satteri and byte-compared the emitted .md files, llms.txt, and search-index.json. Its replacement (generates CLI artifacts with the default Satteri parser) runs only the satteri path and asserts presence/substrings.

Convert-level parity over the docs corpus is still compared across engines, so this is a narrowing rather than a total loss — but llms.txt and search-index.json are no longer verified to match across the two engines at the CLI boundary. Worth confirming the narrowing is intentional.

Technical details
# CLI-level cross-engine artifact parity dropped

## Affected sites
- `packages/leadtype/src/convert/engine-parity.test.ts` (new lines ~249-303) — single-engine satteri smoke test replaces the removed cross-engine `compareTrees` + `llms.txt` + `search-index.json` comparison.

## Required outcome
- Confirm that convert-level corpus parity is considered sufficient coverage, or restore a cross-engine assertion for `llms.txt` / `search-index.json` at the CLI boundary.

## Open questions for the human
- Is dropping CLI-level cross-engine artifact parity intentional, given the convert-level corpus test still compares `.md` output?

ℹ️ Nitpicks

  • docs/writing/components.mdx:71 links to /docs/reference/remark#remarkinclude, but that section heading was renamed to ### includeMarkdown — the anchor is now #includemarkdown and the current link lands at the top of the page.
  • docs/reference/cli.mdx link text reads `leadtype/markdown` but points at /docs/reference/remark (no /docs/reference/markdown page exists) — functional, but the text/URL mismatch is confusing.
  • <Prompt> bodies now stringify with the native markers (*emphasis*, *** rule) instead of the deleted prompt.remark.ts settings (_emphasis_, ---); both compared pipelines share the new prompt.ts so parity tests pass, but prompt-body output changes versus pre-refactor — confirm intended.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Claude Opus𝕏

Comment thread packages/leadtype/src/markdown/component-dispatcher.ts
- Introduced a new module for markdown profiling to track performance metrics during markdown processing.
- Integrated profiling functions into the component dispatcher, transform functions, and include plugin to record execution times.
- Enhanced the overall performance monitoring of markdown transformations, allowing for better optimization and debugging.

This change aims to improve the observability of markdown processing performance within the leadtype package.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes — re-review of the single new commit since the prior pullfrog review (6e75a4f), which adds env-gated markdown profiling and, alongside it, a substantial rewrite of the include resolver with caching and parallel candidate processing.

  • Markdown profiling instrumentation — new internal/markdown-profile.ts aggregates per-stage timings behind LEADTYPE_MARKDOWN_PROFILE=1 and prints a tab-separated table to stderr on beforeExit; hooks added to component-dispatcher.ts, transform.ts, and include.remark.ts are zero-cost when disabled.
  • Per-transform profile namestransform.ts now tags each prepared transform with a profileName derived from the plugin function name, used only for profiling labels.
  • Include transform gatingdefault-transforms.ts tags includeMarkdown with its component names (import/include-c15t/include) so shouldRunTransform can skip it when no include tags are present.
  • Include resolver rewriteinclude.remark.ts adds module-global content/path caches plus an in-flight dedup map, collects candidates with grandparent location tracking, processes them via Promise.all, then splices sequentially; compiler integrations are deliberately routed to the uncached path for hot-reload freshness.

ℹ️ Include caches have no invalidation or reset path

The rewrite adds three process-global maps in include.remark.ts — cached file bytes, cached path resolutions, and an in-flight dedup map. For one-shot CLI builds this is the intended win, and the dev-server/hot-reload path stays correct because compiler integrations are routed to the uncached resolver. The remaining exposure is narrow but real.

  • Long-lived non-compiler processes that re-read an included file after it changes on disk will serve the first read's bytes, since includeContentCache is keyed by resolved path and never invalidated.
  • Test isolation — there is no beforeEach/reset hook, so once a test exercises the cached path, a later test reusing the same resolved path (e.g. a recreated temp file) silently inherits the earlier content.
Technical details
# Include caches have no invalidation or reset path

## Affected sites
- `packages/leadtype/src/remark/plugins/include.remark.ts:32-34``includeContentCache`, `includePathCache`, `includeResolutionInflight` declared module-level; never cleared.
- `packages/leadtype/src/remark/plugins/include.remark.ts:463-477``readIncludeFileCached` caches `readFileSync` output by resolved path with no staleness check.
- `packages/leadtype/src/remark/plugins/include.remark.ts:449-461``resolveIncludePathCached` also caches `existsSync`-based negative resolutions (fallback to `basePaths[0]`), so a file created later in the same process keeps resolving to the stale path.

## Required outcome
- Decide and document whether these caches are intentionally process-lifetime (correct for one-shot builds) and, if any long-lived non-compiler caller exists, give them a way to bypass or clear the caches.
- Provide a reset affordance for tests if/when a test exercises `remarkInclude` (cached path) so state doesn't leak across cases.

## Open questions for the human
- Is any non-compiler caller (watch mode, repeated in-process conversion, the bench runner against mutable fixtures) expected to observe file edits within a single process? If not, this is mergeable as-is.

Pullfrog  | View workflow run | Using Claude Opus𝕏

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/leadtype/src/remark/plugins/include.remark.ts (1)

225-263: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Splice root replacements into the containing array.

When an include under root, listItem, blockquote, etc. expands to multiple nodes, this still falls through to Object.assign(node, { type: "root", ... }), which leaves an invalid nested root in mdast. Only the paragraph case needs promotion to the grandparent; every other root replacement should replace the include node in its parent container.

Proposed fix
 function replaceTarget(
   tree: Root,
   node: Record<string, unknown>,
   parent: Record<string, unknown> | null,
   replacement:
     | { type: "root"; children: unknown[] }
     | { type: "paragraph"; children: unknown[] },
   parentLocation?: Pick<IncludeCandidate, "parentContainer" | "parentIndex">
 ) {
+  if (replacement.type === "root" && parent && !isParagraph(parent)) {
+    const container = parent.children as Record<string, unknown>[] | undefined;
+    const index = container?.indexOf(node) ?? -1;
+    if (index >= 0) {
+      container.splice(
+        index,
+        1,
+        ...(replacement.children as Record<string, unknown>[])
+      );
+      return;
+    }
+  }
+
   // If the include lives inside a paragraph but the replacement is a root
   // (multiple top-level nodes), splice the replacement children into the
   // grandparent's children in place of the whole paragraph. Previously we
   // mutated the paragraph into `{ type: "root" }`, producing invalid mdast.
   if (parent && isParagraph(parent) && replacement.type === "root") {
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/leadtype/src/remark/plugins/include.remark.ts`:
- Around line 32-35: The include caches are currently module-global, so stale
include contents can leak across separate conversions. Move includeContentCache,
includePathCache, and includeResolutionInflight into the transformer flow in
include.remark.ts so each transformer invocation gets its own cache state
instead of reusing it across the module lifetime. Update the logic around the
include plugin’s main transform entrypoint to create and pass these maps per
run.
- Around line 463-477: The include file cache in readIncludeFileCached should
not use readFileSync, since that turns the awaited resolveIncludeInflight path
into blocking I/O and kills concurrency. Update the include loading flow in
include.remark.ts to cache and return the async readFile() result instead,
keeping the existing includeContentCache and profiling behavior intact while
preserving parallel reads under Promise.all.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8b315ec6-3f5f-4dee-a187-d4c87084efa3

📥 Commits

Reviewing files that changed from the base of the PR and between 6e75a4f and eca25df.

📒 Files selected for processing (5)
  • packages/leadtype/src/internal/markdown-profile.ts
  • packages/leadtype/src/markdown/component-dispatcher.ts
  • packages/leadtype/src/markdown/default-transforms.ts
  • packages/leadtype/src/markdown/transform.ts
  • packages/leadtype/src/remark/plugins/include.remark.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use const assertions (as const) for immutable values and literal types
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/leadtype/src/internal/markdown-profile.ts
  • packages/leadtype/src/markdown/component-dispatcher.ts
  • packages/leadtype/src/markdown/default-transforms.ts
  • packages/leadtype/src/markdown/transform.ts
  • packages/leadtype/src/remark/plugins/include.remark.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, never var
Always await promises in async functions - don't forget to use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors
Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully - don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Don't use eval() or assign directly to document.cookie
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Use descriptive names for functions, variables, and types for meaningful naming
Add comments for complex logic, but prefer self-documenting code

Files:

  • packages/leadtype/src/internal/markdown-profile.ts
  • packages/leadtype/src/markdown/component-dispatcher.ts
  • packages/leadtype/src/markdown/default-transforms.ts
  • packages/leadtype/src/markdown/transform.ts
  • packages/leadtype/src/remark/plugins/include.remark.ts
🔇 Additional comments (4)
packages/leadtype/src/markdown/transform.ts (1)

53-64: Still bypassing unified's full Pluggable contract.

toTransform() still drops preset objects and invokes attachers directly, so presets and plugins that rely on processor.use(...) semantics remain unsupported here.

In unified 11, does `Pluggable` include preset objects, and are plugin attachers executed with the processor instance bound as `this` when registered through `processor.use()`?
packages/leadtype/src/internal/markdown-profile.ts (1)

1-49: LGTM!

packages/leadtype/src/markdown/component-dispatcher.ts (1)

1-8: LGTM!

Also applies to: 173-181

packages/leadtype/src/markdown/default-transforms.ts (1)

36-36: LGTM!

Comment on lines +32 to +35
const includeContentCache = new Map<string, string>();
const includePathCache = new Map<string, string>();
const includeResolutionInflight = new Map<string, Promise<IncludeResolution>>();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Scope these include caches to a single transform run.

These maps live for the lifetime of the module, so a later conversion in the same process can reuse stale include contents after the source file changes. Keep the cache per transformer invocation instead of module-global.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/remark/plugins/include.remark.ts` around lines 32 - 35,
The include caches are currently module-global, so stale include contents can
leak across separate conversions. Move includeContentCache, includePathCache,
and includeResolutionInflight into the transformer flow in include.remark.ts so
each transformer invocation gets its own cache state instead of reusing it
across the module lifetime. Update the logic around the include plugin’s main
transform entrypoint to create and pass these maps per run.

Comment on lines +463 to +477
function readIncludeFileCached(resolvedPath: string): string {
const cached = includeContentCache.get(resolvedPath);
if (cached) {
return cached;
}

const profileEnabled = isMarkdownProfileEnabled();
const readStartedAt = profileEnabled ? performance.now() : 0;
const raw = readFileSync(resolvedPath, "utf8");
if (profileEnabled) {
recordMarkdownProfile("include:read", performance.now() - readStartedAt);
}
includeContentCache.set(resolvedPath, raw);
return raw;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Don't switch the async include path back to synchronous I/O.

resolveIncludeInflight() is awaited under Promise.all(...), but readFileSync() serializes those reads on the event loop and erases most of the concurrency benefit. Cache the async readFile() result instead.

Proposed fix
-const includeContentCache = new Map<string, string>();
+const includeContentCache = new Map<string, Promise<string>>();
-function readIncludeFileCached(resolvedPath: string): string {
+async function readIncludeFileCached(resolvedPath: string): Promise<string> {
   const cached = includeContentCache.get(resolvedPath);
   if (cached) {
-    return cached;
+    return await cached;
   }
 
   const profileEnabled = isMarkdownProfileEnabled();
   const readStartedAt = profileEnabled ? performance.now() : 0;
-  const raw = readFileSync(resolvedPath, "utf8");
-  if (profileEnabled) {
-    recordMarkdownProfile("include:read", performance.now() - readStartedAt);
-  }
-  includeContentCache.set(resolvedPath, raw);
-  return raw;
+  const pending = readFile(resolvedPath, "utf8");
+  includeContentCache.set(resolvedPath, pending);
+  try {
+    const raw = await pending;
+    if (profileEnabled) {
+      recordMarkdownProfile("include:read", performance.now() - readStartedAt);
+    }
+    return raw;
+  } catch (error) {
+    includeContentCache.delete(resolvedPath);
+    throw error;
+  }
 }
-    const raw = readIncludeFileCached(resolvedPath);
+    const raw = await readIncludeFileCached(resolvedPath);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function readIncludeFileCached(resolvedPath: string): string {
const cached = includeContentCache.get(resolvedPath);
if (cached) {
return cached;
}
const profileEnabled = isMarkdownProfileEnabled();
const readStartedAt = profileEnabled ? performance.now() : 0;
const raw = readFileSync(resolvedPath, "utf8");
if (profileEnabled) {
recordMarkdownProfile("include:read", performance.now() - readStartedAt);
}
includeContentCache.set(resolvedPath, raw);
return raw;
}
async function readIncludeFileCached(resolvedPath: string): Promise<string> {
const cached = includeContentCache.get(resolvedPath);
if (cached) {
return await cached;
}
const profileEnabled = isMarkdownProfileEnabled();
const readStartedAt = profileEnabled ? performance.now() : 0;
const pending = readFile(resolvedPath, "utf8");
includeContentCache.set(resolvedPath, pending);
try {
const raw = await pending;
if (profileEnabled) {
recordMarkdownProfile("include:read", performance.now() - readStartedAt);
}
return raw;
} catch (error) {
includeContentCache.delete(resolvedPath);
throw error;
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/remark/plugins/include.remark.ts` around lines 463 -
477, The include file cache in readIncludeFileCached should not use
readFileSync, since that turns the awaited resolveIncludeInflight path into
blocking I/O and kills concurrency. Update the include loading flow in
include.remark.ts to cache and return the async readFile() result instead,
keeping the existing includeContentCache and profiling behavior intact while
preserving parallel reads under Promise.all.

- Updated various scripts and documentation to replace `leadtype/remark` with `leadtype/markdown`, aligning with the new markdown transforms architecture.
- Adjusted conversion functions to utilize `markdownTransforms` instead of `remarkPlugins`.
- Improved documentation to reflect the transition and clarify usage of markdown transforms.

This change aims to standardize the markdown processing approach across the codebase.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ℹ️ No new issues — the changes since the last review are correctness-focused and sound. One minor duplication nit inline.

Reviewed changes — incremental review of commit 99fafd3 ("Refactor markdown processing to enhance clarity and maintainability"), a contained pass over the leadtype/markdown subsystem plus docs wording, landing several correctness fixes on top of the dispatcher refactor.

  • Native stringifier hardeningstringify.ts inlineCode now picks the backtick delimiter from the longest internal backtick run and preserves internal whitespace (CommonMark-correct); mdxJsxTextElement now serializes inline (<Badge ...>New</Badge>) rather than as a block. Covered by new stringify.test.ts cases.
  • Transform schedulingtransform.ts adds expandPluggables to flatten nested unified preset ({ plugins: [...] }) entries, and invokes plugin factories as pluginFactory.call(unified(), …) so plugins that read this get a processor.
  • File-path propagationdefine-flattener.ts threads the source filePath into flattenChildren and the sub-transform context (was hardcoded ""), fixing nested base-path resolution.
  • String-literal expression propscallout.ts and command-tabs.ts unwrap quoted expression attributes (variant={"warning"}, command={"leadtype"}); new test asserts the cleaned output.
  • Visitor + cache fixescards.ts returns [SKIP, index] after splicing; type-table.ts switches its TS-program and extracted-type caches to mtime-keyed invalidation.
  • Details parity aligned — legacy remarkDetailsToMarkdown now matches ["Details", "details"] (with a new test), aligning it with the dispatcher and resolving the prior parity thread.
  • MiscAutoTypeTable added to the legacy flattener-name/lint contract; generate-docs.ts prepends includeMarkdown; bench.ts uses an explicit === undefined check.

ℹ️ Nitpicks

  • unwrapStringLiteralExpression is now duplicated verbatim in packages/leadtype/src/markdown/plugins/callout.ts, command-tabs.ts, and audience.ts. The markdown/libs module already hosts shared helpers (getAttributeValue, extractNodeText); hoisting it there would keep the three call sites in sync.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Claude Opus𝕏

}
}

function unwrapStringLiteralExpression(value: string): string {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ℹ️ Nit: unwrapStringLiteralExpression is duplicated verbatim here, in command-tabs.ts, and in audience.ts. Since markdown/libs already holds the shared attribute helpers, hoisting this one there would avoid three copies drifting apart.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/leadtype/src/markdown/plugins/type-table.ts (1)

131-155: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Invalidate the TypeScript cache when imported files change. getTypeScriptProgramForFile() and extractTypeFromFile() only key off the entry file’s mtimeMs, so edits in transitive imports can keep reusing a stale ts.Program and stale extracted table in long-lived processes. Scope this cache to a single conversion pass or include dependency changes in the invalidation key.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/markdown/plugins/type-table.ts` around lines 131 - 155,
The TypeScript program cache in getTypeScriptProgramForFile() is only
invalidated by the root file’s mtimeMs, so changes in imported dependencies can
leave extractTypeFromFile() using a stale ts.Program. Update the caching
strategy in getTypeScriptProgramForFile() / __tsProgramByRootFile so it is
scoped to a single conversion pass or also invalidates when transitive imports
change, ensuring extracted table types are recomputed after dependency edits.
docs/reference/remark.mdx (1)

18-60: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

The documented defaultMarkdownTransforms stack is stale.

This chart and ordered list still describe the legacy per-plugin flattener chain, but the new default export is the resolve-phase plugins followed by nativeMarkdownComponentsToMarkdown. As written, readers will think the deprecated individual remark flatteners still run by default. Please either rewrite this section around the native dispatcher or relabel the detailed order as legacyDefaultMarkdownTransforms.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reference/remark.mdx` around lines 18 - 60, The
`defaultMarkdownTransforms` documentation is outdated and still describes the
old per-plugin flattening pipeline. Update the `remark.mdx` section to match the
current default export behavior by documenting the resolve-phase plugins
followed by `nativeMarkdownComponentsToMarkdown`, or rename the detailed
sequence to `legacyDefaultMarkdownTransforms` so it is not presented as the
default. Use the `defaultMarkdownTransforms` heading and the Mermaid
flowchart/list in this file to locate and revise the stale ordering.
apps/tanstack/scripts/bench.ts (1)

268-271: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Guard zero-median stages before formatting a speedup.

These medians are rounded to milliseconds, so either side can be 0. remark / satteri then renders Infinityx or NaNx, which makes the benchmark summary misleading for fast stages. Skip those rows or render n/a when the denominator is zero.

Suggested fix
     const speedup = remark / satteri;
     const delta = remark - satteri;
+    const speedupLabel =
+      satteri === 0 ? "n/a" : `${speedup.toFixed(2)}x`;
     lines.push(
-      `- \`${stage}\`: ${speedup.toFixed(2)}x (${delta >= 0 ? "-" : "+"}${Math.abs(delta)}ms)`
+      `- \`${stage}\`: ${speedupLabel} (${delta >= 0 ? "-" : "+"}${Math.abs(delta)}ms)`
     );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/tanstack/scripts/bench.ts` around lines 268 - 271, The benchmark summary
formatting in the stage result block can emit Infinityx or NaNx when either
median rounds to 0, so update the logic around the speedup calculation to guard
zero-denominator cases before formatting. In the code that computes `speedup`
and appends to `lines`, add a zero check for `satteri` (and any other zero
median inputs) and either skip that row or render a safe `n/a` value instead of
dividing and calling `toFixed` on an invalid result.
♻️ Duplicate comments (2)
packages/leadtype/src/markdown/stringify.ts (1)

95-108: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve spaces around inline-code line breaks.

/\s*\n\s*/g still eats the spaces on both sides of a newline, so a value like a \n b serializes as a b. That means the native stringifier still cannot round-trip inline code with significant spacing across line breaks.

Suggested fix
-  const content = value.replace(/\s*\n\s*/g, " ");
+  const content = value.replace(/\r?\n|\r/g, " ");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/markdown/stringify.ts` around lines 95 - 108, The
inlineCode helper in stringify.ts is collapsing spaces around line breaks
because the current newline replacement removes surrounding whitespace, so
significant spacing inside inline code is lost. Update inlineCode to preserve
spaces adjacent to newlines when normalizing content, while still converting the
line break itself into a single separator, and keep the existing
delimiter/padding logic unchanged. Use the inlineCode function and its content
normalization step as the place to fix the round-trip behavior for inline code
with spaces across line breaks.
packages/leadtype/src/markdown/transform.ts (1)

39-62: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

PluggableList entries still lose preset settings and shared processor state.

expandPluggables() strips presets down to .plugins, and toTransform() attaches each entry against a fresh unified() instance. That still diverges from processor.use(pluggables): preset settings never flow through, and attachers cannot observe processor data established by earlier plugins. If this API is meant to keep accepting generic PluggableList, it needs a single configured processor for the whole list or a narrower input type.

#!/bin/bash
set -euo pipefail

printf '== attachers using processor state ==\n'
rg -n -C2 '\bthis\.(data|use|parser|compiler)\b' packages/leadtype/src

printf '\n== preset settings in repo ==\n'
rg -n -C2 '\bsettings\s*:' packages/leadtype/src apps docs

Also applies to: 87-110

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/markdown/transform.ts` around lines 39 - 62,
`expandPluggables()` and `toTransform()` still break `PluggableList` semantics
by flattening presets to only `.plugins` and then applying each item to a fresh
`unified()` instance. Update the transform flow so the whole list is applied on
a single configured processor, preserving preset `settings` and shared processor
state across `use()` calls, or otherwise narrow the accepted input type to
exclude preset behavior. Use `expandPluggables`, `isPluggablePreset`, and
`toTransform` as the key symbols when refactoring.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/tanstack/scripts/bench.ts`:
- Around line 268-271: The benchmark summary formatting in the stage result
block can emit Infinityx or NaNx when either median rounds to 0, so update the
logic around the speedup calculation to guard zero-denominator cases before
formatting. In the code that computes `speedup` and appends to `lines`, add a
zero check for `satteri` (and any other zero median inputs) and either skip that
row or render a safe `n/a` value instead of dividing and calling `toFixed` on an
invalid result.

In `@docs/reference/remark.mdx`:
- Around line 18-60: The `defaultMarkdownTransforms` documentation is outdated
and still describes the old per-plugin flattening pipeline. Update the
`remark.mdx` section to match the current default export behavior by documenting
the resolve-phase plugins followed by `nativeMarkdownComponentsToMarkdown`, or
rename the detailed sequence to `legacyDefaultMarkdownTransforms` so it is not
presented as the default. Use the `defaultMarkdownTransforms` heading and the
Mermaid flowchart/list in this file to locate and revise the stale ordering.

In `@packages/leadtype/src/markdown/plugins/type-table.ts`:
- Around line 131-155: The TypeScript program cache in
getTypeScriptProgramForFile() is only invalidated by the root file’s mtimeMs, so
changes in imported dependencies can leave extractTypeFromFile() using a stale
ts.Program. Update the caching strategy in getTypeScriptProgramForFile() /
__tsProgramByRootFile so it is scoped to a single conversion pass or also
invalidates when transitive imports change, ensuring extracted table types are
recomputed after dependency edits.

---

Duplicate comments:
In `@packages/leadtype/src/markdown/stringify.ts`:
- Around line 95-108: The inlineCode helper in stringify.ts is collapsing spaces
around line breaks because the current newline replacement removes surrounding
whitespace, so significant spacing inside inline code is lost. Update inlineCode
to preserve spaces adjacent to newlines when normalizing content, while still
converting the line break itself into a single separator, and keep the existing
delimiter/padding logic unchanged. Use the inlineCode function and its content
normalization step as the place to fix the round-trip behavior for inline code
with spaces across line breaks.

In `@packages/leadtype/src/markdown/transform.ts`:
- Around line 39-62: `expandPluggables()` and `toTransform()` still break
`PluggableList` semantics by flattening presets to only `.plugins` and then
applying each item to a fresh `unified()` instance. Update the transform flow so
the whole list is applied on a single configured processor, preserving preset
`settings` and shared processor state across `use()` calls, or otherwise narrow
the accepted input type to exclude preset behavior. Use `expandPluggables`,
`isPluggablePreset`, and `toTransform` as the key symbols when refactoring.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2102a012-6ee6-4ce4-ba86-645a124bd47e

📥 Commits

Reviewing files that changed from the base of the PR and between eca25df and 99fafd3.

📒 Files selected for processing (22)
  • apps/tanstack/scripts/bench.ts
  • apps/tanstack/src/routes/playground.tsx
  • docs/concepts/architecture.mdx
  • docs/package-docs/bundle.mdx
  • docs/reference/convert.mdx
  • docs/reference/nlweb.mdx
  • docs/reference/remark.mdx
  • docs/search/agent-tools.mdx
  • docs/search/ai-answers.mdx
  • docs/writing/components.mdx
  • packages/leadtype/src/convert/engine-parity.test.ts
  • packages/leadtype/src/markdown/default-transforms.ts
  • packages/leadtype/src/markdown/define-flattener.ts
  • packages/leadtype/src/markdown/markdown-output.test.ts
  • packages/leadtype/src/markdown/plugins/callout.ts
  • packages/leadtype/src/markdown/plugins/cards.ts
  • packages/leadtype/src/markdown/plugins/command-tabs.ts
  • packages/leadtype/src/markdown/plugins/details.ts
  • packages/leadtype/src/markdown/plugins/type-table.ts
  • packages/leadtype/src/markdown/stringify.test.ts
  • packages/leadtype/src/markdown/stringify.ts
  • packages/leadtype/src/markdown/transform.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use const assertions (as const) for immutable values and literal types
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • apps/tanstack/src/routes/playground.tsx
  • packages/leadtype/src/markdown/plugins/details.ts
  • packages/leadtype/src/markdown/stringify.test.ts
  • packages/leadtype/src/markdown/define-flattener.ts
  • packages/leadtype/src/markdown/plugins/callout.ts
  • packages/leadtype/src/markdown/plugins/command-tabs.ts
  • packages/leadtype/src/markdown/plugins/cards.ts
  • packages/leadtype/src/markdown/default-transforms.ts
  • packages/leadtype/src/markdown/transform.ts
  • apps/tanstack/scripts/bench.ts
  • packages/leadtype/src/markdown/plugins/type-table.ts
  • packages/leadtype/src/convert/engine-parity.test.ts
  • packages/leadtype/src/markdown/stringify.ts
  • packages/leadtype/src/markdown/markdown-output.test.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, never var
Always await promises in async functions - don't forget to use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors
Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully - don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Don't use eval() or assign directly to document.cookie
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Use descriptive names for functions, variables, and types for meaningful naming
Add comments for complex logic, but prefer self-documenting code

Files:

  • apps/tanstack/src/routes/playground.tsx
  • packages/leadtype/src/markdown/plugins/details.ts
  • packages/leadtype/src/markdown/stringify.test.ts
  • packages/leadtype/src/markdown/define-flattener.ts
  • packages/leadtype/src/markdown/plugins/callout.ts
  • packages/leadtype/src/markdown/plugins/command-tabs.ts
  • packages/leadtype/src/markdown/plugins/cards.ts
  • packages/leadtype/src/markdown/default-transforms.ts
  • packages/leadtype/src/markdown/transform.ts
  • apps/tanstack/scripts/bench.ts
  • packages/leadtype/src/markdown/plugins/type-table.ts
  • packages/leadtype/src/convert/engine-parity.test.ts
  • packages/leadtype/src/markdown/stringify.ts
  • packages/leadtype/src/markdown/markdown-output.test.ts
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx}: Use function components over class components in React
Call hooks at the top level only, never conditionally
Specify all dependencies in hook dependency arrays correctly
Use the key prop for elements in iterables (prefer unique IDs over array indices)
Nest children between opening and closing tags instead of passing as props
Don't define components inside other components
Avoid dangerouslySetInnerHTML unless absolutely necessary
Use proper image components (e.g., Next.js <Image>) over <img> tags
Use Next.js <Image> component for images
Use next/head or App Router metadata API for head elements in Next.js
Use Server Components for async data fetching instead of async Client Components in Next.js
Use ref as a prop instead of React.forwardRef in React 19+

Files:

  • apps/tanstack/src/routes/playground.tsx
**/*.{jsx,tsx,html}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx,html}: Use semantic HTML and ARIA attributes for accessibility: provide meaningful alt text for images, use proper heading hierarchy, add labels for form inputs, include keyboard event handlers alongside mouse events, use semantic elements instead of divs with roles
Add rel="noopener" when using target="_blank" on links

Files:

  • apps/tanstack/src/routes/playground.tsx
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write assertions inside it() or test() blocks
Avoid done callbacks in async tests - use async/await instead
Don't use .only or .skip in committed code
Keep test suites reasonably flat - avoid excessive describe nesting

Files:

  • packages/leadtype/src/markdown/stringify.test.ts
  • packages/leadtype/src/convert/engine-parity.test.ts
  • packages/leadtype/src/markdown/markdown-output.test.ts
🪛 LanguageTool
docs/reference/remark.mdx

[uncategorized] ~52-~52: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...mandTabsToMarkdown→ markdown table. 11.remarkStepsToMarkdown<...

(MARKDOWN_NNP)


[uncategorized] ~55-~55: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...ypeTable>and→ markdown table. 14.remarkAccordionToMarkdown` ...

(MARKDOWN_NNP)

🔇 Additional comments (12)
docs/concepts/architecture.mdx (1)

16-16: LGTM!

docs/reference/convert.mdx (1)

64-67: LGTM!

Also applies to: 77-87

packages/leadtype/src/markdown/plugins/callout.ts (1)

61-74: LGTM!

Also applies to: 120-166

packages/leadtype/src/markdown/plugins/command-tabs.ts (1)

53-66: LGTM!

Also applies to: 76-103

packages/leadtype/src/markdown/plugins/cards.ts (1)

11-11: LGTM!

Also applies to: 133-139

packages/leadtype/src/markdown/default-transforms.ts (1)

97-121: LGTM!

packages/leadtype/src/markdown/stringify.test.ts (1)

74-96: LGTM!

Also applies to: 98-123

docs/package-docs/bundle.mdx (1)

107-107: LGTM!

Also applies to: 119-119

docs/writing/components.mdx (1)

15-18: LGTM!

Also applies to: 21-23, 33-38, 322-322

packages/leadtype/src/markdown/define-flattener.ts (1)

102-118: LGTM!

Also applies to: 206-214

packages/leadtype/src/convert/engine-parity.test.ts (1)

22-22: LGTM!

Also applies to: 86-94

packages/leadtype/src/markdown/stringify.ts (1)

382-390: LGTM!

- Updated the `renderSpeedups` function to handle division by zero gracefully, displaying "n/a" when applicable.
- Refactored markdown transformation functions to improve clarity and maintainability, including the introduction of a legacy preset for compatibility.
- Added new utility functions for managing TypeScript program dependencies, enhancing caching mechanisms for improved performance.

These changes aim to standardize markdown processing and improve the overall performance and reliability of the leadtype package.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/leadtype/src/markdown/plugins/type-table.ts`:
- Around line 144-160: The freshness check in collectProgramDependencies is
skipping all declaration files, which can leave extracted tables stale when
project .d.ts dependencies change. Update this helper so project-owned
declaration files are included in the dependency list while still excluding
default lib declarations, and ensure the collected entries continue to feed the
__tsProgramByRootFile and __extractedTypeByKey invalidation logic.

In `@packages/leadtype/src/markdown/transform.ts`:
- Around line 116-137: The flattening logic in addTransforms() is reordering
sibling plugins because it recurses into preset.plugins immediately instead of
preserving the original list order. Update addTransforms() in transform.ts so it
first expands presets while keeping siblings in sequence, then runs
sortRemarkPluginsByPhase() once over the fully collected leaf plugins before
calling toTransform(). Use addTransforms(), isPluggablePreset(), and
sortRemarkPluginsByPhase() as the key points to adjust.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d9d9e954-a00c-432f-b1e4-9e3ca7301ead

📥 Commits

Reviewing files that changed from the base of the PR and between 99fafd3 and 05c7e53.

📒 Files selected for processing (6)
  • apps/tanstack/scripts/bench.ts
  • docs/reference/remark.mdx
  • packages/leadtype/src/markdown/plugins/type-table.ts
  • packages/leadtype/src/markdown/stringify.test.ts
  • packages/leadtype/src/markdown/stringify.ts
  • packages/leadtype/src/markdown/transform.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use const assertions (as const) for immutable values and literal types
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/leadtype/src/markdown/stringify.test.ts
  • packages/leadtype/src/markdown/stringify.ts
  • packages/leadtype/src/markdown/transform.ts
  • apps/tanstack/scripts/bench.ts
  • packages/leadtype/src/markdown/plugins/type-table.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, never var
Always await promises in async functions - don't forget to use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors
Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully - don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Don't use eval() or assign directly to document.cookie
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Use descriptive names for functions, variables, and types for meaningful naming
Add comments for complex logic, but prefer self-documenting code

Files:

  • packages/leadtype/src/markdown/stringify.test.ts
  • packages/leadtype/src/markdown/stringify.ts
  • packages/leadtype/src/markdown/transform.ts
  • apps/tanstack/scripts/bench.ts
  • packages/leadtype/src/markdown/plugins/type-table.ts
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write assertions inside it() or test() blocks
Avoid done callbacks in async tests - use async/await instead
Don't use .only or .skip in committed code
Keep test suites reasonably flat - avoid excessive describe nesting

Files:

  • packages/leadtype/src/markdown/stringify.test.ts
🔇 Additional comments (5)
packages/leadtype/src/markdown/stringify.test.ts (1)

5-31: LGTM!

Also applies to: 33-72, 74-99, 100-125

docs/reference/remark.mdx (1)

12-35: LGTM!

Also applies to: 107-185

packages/leadtype/src/markdown/stringify.ts (1)

95-109: LGTM!

packages/leadtype/src/markdown/transform.ts (1)

3-108: LGTM!

apps/tanstack/scripts/bench.ts (1)

248-274: LGTM!

Comment on lines +144 to +160
function collectProgramDependencies(
program: ts.Program
): Array<{ filePath: string; mtimeMs: number }> {
const dependencies: Array<{ filePath: string; mtimeMs: number }> = [];
for (const sourceFile of program.getSourceFiles()) {
if (sourceFile.isDeclarationFile) {
continue;
}
try {
dependencies.push({
filePath: sourceFile.fileName,
mtimeMs: statSync(sourceFile.fileName).mtimeMs,
});
} catch {
// Virtual files cannot be used to validate freshness.
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

TypeScript Program isSourceFileDefaultLibrary sourceFile declaration dependency API

💡 Result:

In the TypeScript Compiler API, isSourceFileDefaultLibrary is a method available on the Program interface [1][2]. It accepts a SourceFile object and returns a boolean value indicating whether that source file is part of the default library (such as lib.d.ts) [1][2]. This method is useful for tools and plugins that need to distinguish between user-defined source files and the built-in library files provided by TypeScript [1]. Key technical points include: - Usage: It is typically used while iterating over all source files in a program to identify or exclude default library files [1]. - Implementation context: The method is defined within the TypeScript compiler's Program implementation [3][4]. - Limitations: Users have noted that it may return false for files loaded from external packages, even when those files are intended to serve as the default library in specific project configurations [5]. - Behavior and Bugs: As an API, it relies on the internal state of the program. Issues have occasionally been reported where the method returns false negatives due to changes in how program structure is managed or reused [6]. Example usage: for (const sourceFile of program.getSourceFiles) { if (program.isSourceFileDefaultLibrary(sourceFile)) { console.log(Default lib: ${sourceFile.fileName}); } }

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the relevant file and the cache usage around it.
git ls-files | rg '^packages/leadtype/src/markdown/plugins/type-table\.ts$|^packages/leadtype/src/markdown/plugins/'

echo '--- outline ---'
ast-grep outline packages/leadtype/src/markdown/plugins/type-table.ts --view expanded || true

echo '--- key references ---'
rg -n "__tsProgramByRootFile|__extractedTypeByKey|collectProgramDependencies|isDeclarationFile|isSourceFileDefaultLibrary|mtimeMs|dependencies" packages/leadtype/src/markdown/plugins/type-table.ts

Repository: inthhq/leadtype

Length of output: 4958


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the surrounding implementation in manageable chunks.
wc -l packages/leadtype/src/markdown/plugins/type-table.ts
sed -n '1,260p' packages/leadtype/src/markdown/plugins/type-table.ts

Repository: inthhq/leadtype

Length of output: 7661


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the surrounding implementation in the target file.
sed -n '1,260p' packages/leadtype/src/markdown/plugins/type-table.ts

Repository: inthhq/leadtype

Length of output: 7603


Include project .d.ts files in freshness checks. Skipping every declaration file means edits to imported declaration dependencies won’t invalidate __tsProgramByRootFile or __extractedTypeByKey, so extracted tables can stay stale until restart. Keep default libs out, but let project declarations participate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/markdown/plugins/type-table.ts` around lines 144 - 160,
The freshness check in collectProgramDependencies is skipping all declaration
files, which can leave extracted tables stale when project .d.ts dependencies
change. Update this helper so project-owned declaration files are included in
the dependency list while still excluding default lib declarations, and ensure
the collected entries continue to feed the __tsProgramByRootFile and
__extractedTypeByKey invalidation logic.

Comment on lines +116 to +137
function addTransforms(entries: PluggableList): void {
const leafEntries: Pluggable[] = [];
for (const entry of entries) {
if (isPluggablePreset(entry)) {
applyPresetSettings(processor, entry);
addTransforms(entry.plugins ?? []);
continue;
}
leafEntries.push(entry);
}

for (const entry of sortRemarkPluginsByPhase(leafEntries)) {
const transform = toTransform(entry, processor);
if (transform) {
transforms.push(transform);
}
}
}

addTransforms(plugins);
return transforms;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

unified PluggableList preset plugin order Processor.use preset plugins settings unified 11

💡 Result:

In the unified ecosystem, the order of plugin application is crucial and strictly maintained [1][2]. When you configure a processor using the processor.use method, plugins, presets, and lists are applied in the exact sequence in which they are called or defined [1][2]. Key points regarding plugin order and presets in unified: 1. Sequential Execution: The order of.use calls determines the execution order of plugins [1][2]. Plugins added earlier run before plugins added later [2][3]. 2. Preset Structure: A Preset is an object that can contain a plugins property (a PluggableList) and/or a settings property [4][5]. The plugins defined within the plugins array of a preset are applied in the order they appear in that array [1][2]. 3. Configuration and Merging: If you pass the same plugin to.use multiple times, unified does not add it twice; instead, it merges the new options with the existing configuration [1][6][7]. 4. Presets and Options: Presets themselves cannot receive options [1]. If you need to configure a specific plugin contained within a preset, you must apply that plugin again after the preset using the desired options [1]. 5. Frozen State: Plugins are not executed when.use is called [6]. They are executed when the processor is "frozen" (i.e., when a processing method like.process or.run is invoked) [6][3]. At this stage, the transformer functions returned by the plugins are collected and executed sequentially in the order they were defined [3]. In summary, unified treats the pipeline as an ordered list where each subsequent call to.use appends new plugins (or updates existing ones) to the end of the existing chain [1][6].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the target file and nearby references.
git ls-files 'packages/leadtype/src/markdown/transform.ts'
printf '\n--- outline ---\n'
ast-grep outline packages/leadtype/src/markdown/transform.ts --view expanded

printf '\n--- relevant symbols/searches ---\n'
rg -n "sortRemarkPluginsByPhase|isPluggablePreset|applyPresetSettings|toTransform|PluggableList|plugins\\?\\?" packages/leadtype/src/markdown/transform.ts packages/leadtype/src -g '!**/node_modules/**'

printf '\n--- file excerpt ---\n'
sed -n '1,240p' packages/leadtype/src/markdown/transform.ts

Repository: inthhq/leadtype

Length of output: 13862


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n--- remark-phase outline ---\n'
ast-grep outline packages/leadtype/src/internal/remark-phase.ts --view expanded

printf '\n--- remark-phase excerpt ---\n'
sed -n '1,260p' packages/leadtype/src/internal/remark-phase.ts

printf '\n--- usages of createMdastTransforms ---\n'
rg -n "createMdastTransforms\\(" packages/leadtype/src -g '!**/node_modules/**'

Repository: inthhq/leadtype

Length of output: 5433


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n--- createMdastTransforms call sites ---\n'
sed -n '880,980p' packages/leadtype/src/convert/convert.ts
printf '\n--- define-flattener excerpt ---\n'
sed -n '1,180p' packages/leadtype/src/markdown/define-flattener.ts

printf '\n--- tests / mentions of preset order ---\n'
rg -n "preset|createMdastTransforms|sortRemarkPluginsByPhase|markdownTransforms" packages/leadtype/src -g '*test.*' -g '*.ts' -g '*.tsx'

Repository: inthhq/leadtype

Length of output: 14646


Preserve sibling order when expanding presets. addTransforms() recurses into preset.plugins immediately, so [pluginA, presetB] is flattened as [presetB.plugins..., pluginA]. Expand the whole list first, then phase-sort once.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/markdown/transform.ts` around lines 116 - 137, The
flattening logic in addTransforms() is reordering sibling plugins because it
recurses into preset.plugins immediately instead of preserving the original list
order. Update addTransforms() in transform.ts so it first expands presets while
keeping siblings in sequence, then runs sortRemarkPluginsByPhase() once over the
fully collected leaf plugins before calling toTransform(). Use addTransforms(),
isPluggablePreset(), and sortRemarkPluginsByPhase() as the key points to adjust.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ℹ️ No new issues in this commit — the changes are correctness-focused and sound. Not approving only because a previously-flagged CI-breaking smoke assertion remains unaddressed (see below).

Reviewed changes — incremental review of commit 05c7e53 ("Enhance markdown processing and performance metrics"), a contained pass over the leadtype/markdown subsystem, docs, and the benchmark script.

  • Inline-code whitespace fixstringify.ts inlineCode now uses LINE_BREAK_REGEX (/\r?\n/g) to convert only line breaks to a single space, preserving surrounding internal whitespace (was collapsing \s*\n\s* to one space). CommonMark-aligned; covered by a new stringify.test.ts case ("a \n b"`a b`).
  • Type-table cache invalidationtype-table.ts switches the TS-program and extracted-type caches from single-root-mtime keying to dependency-mtime validation (programDependenciesAreFresh / collectProgramDependencies re-stat every non-declaration program source), so the cache now invalidates when any imported type file changes, not just the root.
  • Transform schedulingtransform.ts replaces expandPluggables with a nested addTransforms that threads preset settings into a single shared unified() processor and invokes all plugin factories against that one processor.
  • Benchmark + docsbench.ts guards satteri === 0 ("n/a" instead of dividing); docs/reference/remark.mdx rewrites the pipeline diagram and ordered list to reflect the single native dispatcher and documents legacyDefaultMarkdownTransforms.

⚠️ E2e smoke test still asserts the old page heading (carried over, not new)

This was flagged at the initial review and remains unaddressed by every commit since. The reference page title is "Markdown transforms", but the smoke test still requires a visible heading named exactly "Remark plugins", so it will fail against the built app. It is the only reason this review is not an approval — nothing in 05c7e53 introduced it or touched the file.

Technical details
# E2e smoke test asserts a heading that no longer exists

## Affected sites
- `apps/tanstack/tests/e2e/smoke.e2e.ts:89``page.getByRole("heading", { name: "Remark plugins", exact: true })`. The page h1 now derives from `title: "Markdown transforms"`.
- `docs/reference/remark.mdx:2``title: "Markdown transforms"`.

## Required outcome
- The heading assertion matches the rendered title so the smoke test passes.

## Suggested approach
- Update line 89 to `{ name: "Markdown transforms", exact: true }`.

Pullfrog  | Fix it ➔View workflow run | Using Claude Opus𝕏

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant