kantord / kantord/optative

mdx: lower .op.mdx on import (MdxLoader) and export frontmatter

Open
#103 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
1
Forks
0
Avg merge
3d 5h
Merged PRs (30d)
17

Description

## Problem

`.op.mdx` files can only be the *entry* script. `esto run` lowers the entry explicitly (`optative-script-mdx` dispatches on the entry's extension), but an **imported** `.op.mdx` goes through the default `ScriptLoader`, whose transform check only knows `.jsx/.tsx/.ts/.mts` — the raw markdown is declared as a JS module and fails with a syntax error.

Importing should inherently lower, symmetric with how importing a `.tsx` file transparently runs the JSX transform:

```tsx
// build.op.tsx
const posts = await Promise.all(
ls("content/posts").map((f) => import(`../content/posts/${f}`)),
);
```

(Dynamic `import()` with top-level await and computed specifiers already works in the engine — verified against esto 0.0.9. The loader branch is the only missing piece.)

## Proposed change

An mdx-aware loader in `optative-script-mdx` (it can't live in `optative-script`, which has no markdown knowledge — same layering reason `run_script` dispatch lives here):

```rust
fn load(&mut self, ctx, name, attrs) -> Result {
if name.ends_with(".op.mdx") {
let src = fs::read_to_string(name)?;
let (fm, tsx) = lower::lower_to_tsx_with_frontmatter(&src, name)?;
let tsx = format!("export const frontmatter = {};\n{tsx}", fm_to_json(fm));
let js = transform_source(&tsx, name); // same oxc pass imported .tsx gets
return Module::declare(ctx, name, js);
}
self.inner.load(ctx, name, attrs) // delegate everything else
}
```

Two sub-parts:

1. **The loader itself**, delegating to an inner loader for every other extension, and wired into `esto run` so imports lower regardless of the entry's own format.
2. **`export const frontmatter`** — `lower_to_tsx_with_frontmatter` already exists and is tested but isn't reachable from `run_script` (`lib.rs` still calls plain `lower_to_tsx`). Synthesizing the frontmatter as a named export gives importers `import render, { frontmatter } from "./post.op.mdx"`, which also finally wires the extraction up for entries.

## Motivating consumer

A static site builder as an esto script (personal-blog experiment): posts are `.op.mdx` modules; the build script dynamically imports them all, reads `frontmatter` for the index page, and calls each default export only when that page's input signature moved. The imported module shape the current lowering produces (hoisted user imports + synthesized `export default () => (tree)`) is already exactly right for this — only the load path is missing.

Related follow-up (separate issue to come): a document-mode lowering that emits prose as intrinsic JSX nodes (`

`, `

`, …) in document order instead of merged `` strings, for consumers that render content rather than ground prompts.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in optative-script-mdx and lib.rs, tracing run_script, the existing lower_to_tsx_with_frontmatter path, and the loader used for imported modules. Verify the existing frontmatter lowering tests first, then exercise an imported .op.mdx module and an entry with another format. Done means imports lower transparently, frontmatter is exported for entries and imports, and other extensions still delegate to the inner loader.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
build-system
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.