Skip to content

Cache include resolution per conversion run#111

Merged
KayleeWilliams merged 1 commit into
mainfrom
feat/include-resolution-cache
Jun 29, 2026
Merged

Cache include resolution per conversion run#111
KayleeWilliams merged 1 commit into
mainfrom
feat/include-resolution-cache

Conversation

@KayleeWilliams

Copy link
Copy Markdown
Collaborator

Summary

  • Add a scoped include-resolution cache for repeated <include> / <import> targets.
  • Thread one include cache through each convertAllMdx() batch while keeping single-file conversions scoped to their own cache.
  • Export createIncludeResolutionCache() and cache types from leadtype/remark and leadtype/mdx for instrumentation.
  • Add a changeset and regression tests for raw read reuse, parser cache reuse, and section-specific extraction.

Notes

Current docs and the c15t fixture do not contain repeated real include nodes, so the current repo workload does not show a material production-docs speedup. A synthetic 200-page repeated-include benchmark showed one raw read, one markdown parse, and about a 5.9x speedup in include expansion time.

Fixes #109.

Validation

  • bun x ultracite fix packages/leadtype/src/remark/plugins/include.remark.ts packages/leadtype/src/remark/plugins/include.remark.test.ts packages/leadtype/src/convert/convert.ts packages/leadtype/src/remark/index.ts packages/leadtype/src/mdx/index.ts .changeset/cache-include-resolution.md
  • bun test packages/leadtype/src/remark/plugins/include.remark.test.ts packages/leadtype/src/convert/convert.test.ts
  • bun run check-types from packages/leadtype
  • bun run test from packages/leadtype

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added caching for repeated MDX <include>/<import> resolution within a single conversion batch to speed up shared partials.
    • Exposed a public include-resolution cache factory plus cache stats, and re-exported related types for easier integration.
  • Bug Fixes
    • Ensures section-anchor handling remains correct across different anchors by reusing parsed ASTs safely.
    • Preserves remark plugin VFile.data behavior when caching isn’t provided.
  • Documentation
    • Documented the new conversion performance behavior and observed impact.
  • Tests
    • Added coverage validating cache hits/reads and correct rendered output.

Walkthrough

Adds a scoped include-resolution cache to MDX include handling and threads it through conversion, public exports, tests, and release notes.

Changes

Include-resolution caching

Layer / File(s) Summary
Cache types and include resolution
packages/leadtype/src/remark/plugins/include.remark.ts
Defines the cache contract and factory, then updates include reading, markdown parsing, include node processing, and plugin error reporting to use the shared cache and source-path context.
Cache threading in conversion
packages/leadtype/src/convert/convert.ts
Extends conversion options and remark execution to carry cache-backed file data, then creates and shares cache instances across single-file and batch conversion paths.
Public API exports
packages/leadtype/src/remark/index.ts, packages/leadtype/src/mdx/index.ts
Expands the remark and mdx entry points to re-export the cache factory and related cache types.
Tests and release notes
packages/leadtype/src/remark/plugins/include.remark.test.ts, packages/leadtype/src/convert/convert.test.ts, docs/changelog/0-4.mdx, .changeset/cache-include-resolution.md
Adds cache-focused tests for include reuse and conversion behavior, plus the changeset and changelog updates describing the new caching path.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hop through includes, one read at a time,
A cache in my pocket keeps parses in rhyme.
One batch, one soft burrow, no duplicate sniffs,
Shared ASTs go purring through sectioned page drifts.
Tally the hits—what a tidy surprise! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% 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 clearly summarizes the main change: caching include resolution during a conversion run.
Description check ✅ Passed The description matches the changeset and summarizes the cache, exports, tests, and benchmark notes.
Linked Issues check ✅ Passed The PR implements scoped include-resolution caching, preserves section handling and diagnostics, and adds tests and instrumentation aligned with #109.
Out of Scope Changes check ✅ Passed The changes are limited to the cache feature, related exports, tests, and release/changelog docs.
✨ 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 feat/include-resolution-cache

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 markdownlint-cli2 (0.22.1)
.changeset/cache-include-resolution.md

markdownlint-cli2 wrapper config was not available before execution


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

@KayleeWilliams KayleeWilliams marked this pull request as ready for review June 29, 2026 15:36

@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 regression to fix before merge: on the resolveMdxFrontmatter path (no cache), this PR makes file.data become undefined instead of the usual {}, which can break any remark plugin that reads file.data. Details inline.

Reviewed changes — adds a per-conversion-run cache so repeated <include> / <import> targets share one raw file read and one parsed markdown AST.

  • Scoped include cache — new IncludeResolutionCache + createIncludeResolutionCache() in include.remark.ts; readIncludedFile() caches raw reads as Promise<string>, parseIncludedMarkdown() caches ASTs in a WeakMap<parser, Map<path, Root>> and returns structuredClone copies so callers mutate freely without corrupting the cache.
  • Pipeline threadingconvert.ts forwards the cache through processor.run via the VFile data._leadtypeIncludeCache key; convertAllMdx shares one cache per batch, convertMdxFile creates its own when none is supplied, writeMdxFileAsMarkdown passes a fresh one.
  • Wider remarkInclude signature — accepts string[] | { basePaths?, cache? } (array form unchanged); new symbols re-exported from leadtype/remark and leadtype/mdx.
  • Tests + changeset — covers raw-read reuse, parse reuse, and per-section extraction from a shared AST.

ℹ️ VFile cache-injection path is untested

The new tests exercise only the pluginCache path (remarkInclude({ cache })). The path the production pipeline actually uses — cache delivered through file.data._leadtypeIncludeCache and read back via the fileCache branch in remarkInclude — has no direct coverage. That gap is why the file.data regression flagged inline slips past the suite.

Technical details
# VFile cache-injection path is untested

## Affected sites
- `packages/leadtype/src/remark/plugins/include.remark.test.ts:165-215` — only covers `.use(remarkInclude, { cache })`.
- `packages/leadtype/src/remark/plugins/include.remark.ts:682-686` — the `fileData` / `fileCache` branch, exercised only indirectly.

## Required outcome
- A test that drives the cache through `file.data._leadtypeIncludeCache` (i.e. via `convertMdxFile` / `convertAllMdx`, or by setting `data` on the run options) and asserts include expansion still works and stats accumulate.
- That test should also cover the no-cache case so a regression that sets `file.data` to `undefined` is caught.

ℹ️ Nitpicks

  • packages/leadtype/src/remark/plugins/include.remark.ts:5 — module JSDoc still describes remarkInclude(basePaths?); the signature is now remarkInclude(options: string[] | { basePaths?, cache? }).
  • packages/leadtype/src/remark/plugins/include.remark.ts:279rawFileHits is incremented before return await cached, so a cached read that rejects still counts as a hit. Instrumentation-only, but the increment belongs after a successful await.

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

Comment thread packages/leadtype/src/convert/convert.ts Outdated
@KayleeWilliams KayleeWilliams force-pushed the feat/include-resolution-cache branch from 01f76f9 to 5f8b082 Compare June 29, 2026 17:43

@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-reviewed the commits since the prior pullfrog review (01f76f99). All three earlier findings are now addressed, and the new test and nit fixes introduce no regressions.

  • Fixed the file.data clobberrunRemarkAst now builds run options as ...(fileData ? { data: fileData } : {}), so the no-cache resolveMdxFrontmatter path no longer passes data: undefined and file.data keeps the vfile default {}.
  • Closed the VFile coverage gapconvert.test.ts adds "preserves VFile data as an object when no include cache is supplied" (regression guard for the bug above) and "shares include cache through VFile data during conversion" (drives the cache via file.data._leadtypeIncludeCache and asserts rawFileReads/rawFileHits/markdownParses/markdownParseHits).
  • Resolved both nits — module JSDoc now reads remarkInclude(options?) with the legacy-array note, and rawFileHits is incremented after await cached so a rejected cached read no longer counts as a hit.

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: 1

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/convert/convert.ts (1)

1334-1370: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

The batch cache still reparses the same include once per file.

convertAllMdx() now shares one includeResolutionCache, but prepareMdxConversion() still creates a fresh remark processor per file. Because include.remark.ts keys markdownAstByParser by parser identity, identical includes used by different files cannot hit the same parsed-AST entry. That means the new batch cache deduplicates raw reads, but not markdown parsing, which misses the stated per-batch parser-cache objective. Reuse a single processor/parser for the batch, or change the parsed-AST cache to use a stable parser key instead of the per-file parser object.

🤖 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/convert/convert.ts` around lines 1334 - 1370, The
batch-level include cache in convertAllMdx still misses parsed-AST reuse because
prepareMdxConversion creates a fresh remark processor/parser for each file, so
include.remark.ts cannot share markdownAstByParser across files. Update the
batch flow to reuse a single processor/parser for the whole conversion run, or
change the parsed-AST cache keying to a stable parser identifier instead of the
per-file parser object, and ensure the shared includeResolutionCache is used
with that same batch-scoped parser in convertMdxToMarkdown/prepareMdxConversion.
🤖 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 657-663: The nested include error reporting is using only the
top-level file.path, so failures inside included partials lose the immediate
source context. Update includeContentAsMarkdown and the nested include metadata
handling to thread resolution.resolvedPath alongside baseDir, and prefer that
path when setting sourcePath for logs and failures. Make the same change in the
nested-include flow referenced by includeContentAsMarkdown so the reporting uses
the partial that contains the failing tag instead of always the outer page.

---

Outside diff comments:
In `@packages/leadtype/src/convert/convert.ts`:
- Around line 1334-1370: The batch-level include cache in convertAllMdx still
misses parsed-AST reuse because prepareMdxConversion creates a fresh remark
processor/parser for each file, so include.remark.ts cannot share
markdownAstByParser across files. Update the batch flow to reuse a single
processor/parser for the whole conversion run, or change the parsed-AST cache
keying to a stable parser identifier instead of the per-file parser object, and
ensure the shared includeResolutionCache is used with that same batch-scoped
parser in convertMdxToMarkdown/prepareMdxConversion.
🪄 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: 7229a7ae-51d1-419b-b244-6265c9e4d89c

📥 Commits

Reviewing files that changed from the base of the PR and between 2f52e21 and 5f8b082.

📒 Files selected for processing (7)
  • .changeset/cache-include-resolution.md
  • packages/leadtype/src/convert/convert.test.ts
  • packages/leadtype/src/convert/convert.ts
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/remark/index.ts
  • packages/leadtype/src/remark/plugins/include.remark.test.ts
  • packages/leadtype/src/remark/plugins/include.remark.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{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/remark/index.ts
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/convert/convert.test.ts
  • packages/leadtype/src/remark/plugins/include.remark.test.ts
  • packages/leadtype/src/convert/convert.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/remark/index.ts
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/convert/convert.test.ts
  • packages/leadtype/src/remark/plugins/include.remark.test.ts
  • packages/leadtype/src/convert/convert.ts
  • packages/leadtype/src/remark/plugins/include.remark.ts
**/index.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • packages/leadtype/src/remark/index.ts
  • packages/leadtype/src/mdx/index.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/convert/convert.test.ts
  • packages/leadtype/src/remark/plugins/include.remark.test.ts
🧠 Learnings (1)
📚 Learning: 2026-06-09T18:30:08.038Z
Learnt from: KayleeWilliams
Repo: inthhq/leadtype PR: 97
File: .changeset/search-prototype-safety-and-scaling.md:5-5
Timestamp: 2026-06-09T18:30:08.038Z
Learning: In this repo, `.changeset/*.md` files must not start the body with an H1/first-line heading (`#`) immediately after the YAML frontmatter. The changesets tool inlines the body as bullet entries into `CHANGELOG.md` during release, and a leading `#` heading would break the generated changelog format. As a result, MD041 (`first-line-heading`) warnings for files under `.changeset/` are expected false positives and should be ignored.

Applied to files:

  • .changeset/cache-include-resolution.md
🪛 ast-grep (0.44.0)
packages/leadtype/src/convert/convert.test.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)

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)

🪛 markdownlint-cli2 (0.22.1)
.changeset/cache-include-resolution.md

[warning] 5-5: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

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

25-28: LGTM!

Also applies to: 407-407, 494-504, 915-926, 981-988, 1052-1058, 1291-1291

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

47-53: LGTM!

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

33-39: LGTM!

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

4-11: LGTM!

Also applies to: 149-162, 165-216

packages/leadtype/src/convert/convert.test.ts (1)

9-17: LGTM!

Also applies to: 352-393

.changeset/cache-include-resolution.md (1)

1-17: LGTM!

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

5-8: LGTM!

Also applies to: 32-59, 270-291, 298-323, 371-387, 481-482, 527-527, 566-568, 600-614, 666-688


294-295: 🩺 Stability & Availability

No issue: structuredClone is available on the declared Node 22+ runtime.

			> Likely an incorrect or invalid review comment.

Comment thread packages/leadtype/src/remark/plugins/include.remark.ts
@KayleeWilliams KayleeWilliams force-pushed the feat/include-resolution-cache branch from 5f8b082 to e02aa33 Compare June 29, 2026 18:01
@KayleeWilliams KayleeWilliams merged commit 4e743a6 into main Jun 29, 2026
3 of 4 checks passed
@KayleeWilliams KayleeWilliams deleted the feat/include-resolution-cache branch June 29, 2026 18:08

@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: 1

🤖 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/convert/convert.test.ts`:
- Around line 358-360: The assertion in the dataReaderPlugin callback is too
strict because resolveMdxFrontmatter forwards prepared.data and VFile.data may
contain unrelated plugin metadata; update the test to verify the shared
file.data bag is an object and that includeResolutionCache is not present,
rather than expecting exact emptiness. Keep the change localized to the
convert.test.ts case around dataReaderPlugin and the resolveMdxFrontmatter flow.
🪄 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: cae60748-e4e1-4e4d-b867-ec9b039097c4

📥 Commits

Reviewing files that changed from the base of the PR and between 5f8b082 and e02aa33.

📒 Files selected for processing (8)
  • .changeset/cache-include-resolution.md
  • docs/changelog/0-4.mdx
  • packages/leadtype/src/convert/convert.test.ts
  • packages/leadtype/src/convert/convert.ts
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/remark/index.ts
  • packages/leadtype/src/remark/plugins/include.remark.test.ts
  • packages/leadtype/src/remark/plugins/include.remark.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{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/remark/index.ts
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/remark/plugins/include.remark.test.ts
  • packages/leadtype/src/convert/convert.test.ts
  • packages/leadtype/src/convert/convert.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/remark/index.ts
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/remark/plugins/include.remark.test.ts
  • packages/leadtype/src/convert/convert.test.ts
  • packages/leadtype/src/convert/convert.ts
  • packages/leadtype/src/remark/plugins/include.remark.ts
**/index.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • packages/leadtype/src/remark/index.ts
  • packages/leadtype/src/mdx/index.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/remark/plugins/include.remark.test.ts
  • packages/leadtype/src/convert/convert.test.ts
🧠 Learnings (1)
📚 Learning: 2026-06-09T18:30:08.038Z
Learnt from: KayleeWilliams
Repo: inthhq/leadtype PR: 97
File: .changeset/search-prototype-safety-and-scaling.md:5-5
Timestamp: 2026-06-09T18:30:08.038Z
Learning: In this repo, `.changeset/*.md` files must not start the body with an H1/first-line heading (`#`) immediately after the YAML frontmatter. The changesets tool inlines the body as bullet entries into `CHANGELOG.md` during release, and a leading `#` heading would break the generated changelog format. As a result, MD041 (`first-line-heading`) warnings for files under `.changeset/` are expected false positives and should be ignored.

Applied to files:

  • .changeset/cache-include-resolution.md
🪛 ast-grep (0.44.0)
packages/leadtype/src/convert/convert.test.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)

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)

🔇 Additional comments (12)
packages/leadtype/src/remark/plugins/include.remark.test.ts (1)

4-11: LGTM!

Also applies to: 149-216

packages/leadtype/src/convert/convert.test.ts (1)

9-17: LGTM!

Also applies to: 369-393

.changeset/cache-include-resolution.md (1)

1-17: LGTM!

docs/changelog/0-4.mdx (1)

16-16: LGTM!

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

657-663: 🩺 Stability & Availability

Nested include failures still report the top-level page, not the failing partial.

sourcePath is threaded as file.path (Line 740) and includeContentAsMarkdown only annotates nested includes with baseDir, not resolution.resolvedPath. So a failure inside a nested partial still logs the outer page as source. Thread resolution.resolvedPath into the nested-include metadata (alongside baseDir) and prefer it when setting sourcePath.

Also applies to: 738-740


32-59: LGTM!

Also applies to: 270-292


666-688: LGTM!


294-324: 🩺 Stability & Availability

No action needed for structuredClone here. The package targets Node 22+, so this global is available.

			> Likely an incorrect or invalid review comment.
packages/leadtype/src/convert/convert.ts (2)

489-508: LGTM!


915-926: LGTM!

Also applies to: 981-988, 1052-1058, 1291-1291, 1334-1334, 1370-1370

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

47-53: LGTM!

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

33-39: LGTM!

Comment on lines +358 to +360
const dataReaderPlugin =
() => (_tree: unknown, file: { data: unknown }) => {
expect(file.data).toEqual({});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Assert the missing cache key, not exact empty file.data.

resolveMdxFrontmatter() forwards prepared.data, and VFile.data is a shared plugin bag. toEqual({}) will fail as soon as any unrelated metadata is added, even though the no-cache contract still holds. Assert that file.data is an object and that includeResolutionCache is absent instead.

Suggested assertion change
     const dataReaderPlugin =
       () => (_tree: unknown, file: { data: unknown }) => {
-        expect(file.data).toEqual({});
+        expect(file.data).toEqual(expect.any(Object));
+        expect(file.data).not.toHaveProperty("includeResolutionCache");
         sawVFileData = true;
       };
📝 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 dataReaderPlugin =
() => (_tree: unknown, file: { data: unknown }) => {
expect(file.data).toEqual({});
const dataReaderPlugin =
() => (_tree: unknown, file: { data: unknown }) => {
expect(file.data).toEqual(expect.any(Object));
expect(file.data).not.toHaveProperty("includeResolutionCache");
sawVFileData = true;
};
🧰 Tools
🪛 ast-grep (0.44.0)

[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)

🤖 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/convert/convert.test.ts` around lines 358 - 360, The
assertion in the dataReaderPlugin callback is too strict because
resolveMdxFrontmatter forwards prepared.data and VFile.data may contain
unrelated plugin metadata; update the test to verify the shared file.data bag is
an object and that includeResolutionCache is not present, rather than expecting
exact emptiness. Keep the change localized to the convert.test.ts case around
dataReaderPlugin and the resolveMdxFrontmatter flow.

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.

Investigate caching include resolution within a conversion run

1 participant