Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f94deb8
chore: add Backlog.md project for Sätteri migration plan
claude Jun 6, 2026
4a9523c
chore: revise Sätteri migration backlog tasks
claude Jun 6, 2026
d13bca6
feat: add Sätteri processor support and migrate @astrojs/mdx to unifi…
claude Jun 6, 2026
535fa52
chore: add backlog tasks for Sätteri integration review findings
claude Jun 6, 2026
560edb5
fix: enable directive parsing and use plugin builder for Sätteri path
claude Jun 6, 2026
3b29f99
chore: update TASK-3 status to In Progress with current Astro version…
claude Jun 6, 2026
72b52f6
chore: mark TASK-3 as Done — migration to new processor API is complete
claude Jun 6, 2026
57cd112
feat: auto-inherit markdown.processor: satteri() from global Astro co…
claude Jun 6, 2026
a49de75
chore: add TASK-10 — replace evaluate() remark/rehype pipeline with s…
claude Jun 6, 2026
c5de764
feat(notro-loader): replace remark/rehype pipeline with string-level …
claude Jun 6, 2026
95b19ba
refactor(notro-loader): remove unified() from integration; pass plugi…
claude Jun 6, 2026
facec2d
chore: add tasks 11-15 for Sätteri-as-default migration
claude Jun 7, 2026
715270a
feat!: switch notro() default processor to Sätteri; add satteriMermai…
claude Jun 7, 2026
8403a21
fix: restore code block styling and fix shikiConfig route to mdx()
claude Jun 7, 2026
6145015
fix: update astro to ^6.4.4 in blank, basics, gallery templates
claude Jun 7, 2026
392daa1
fix: protect inline code spans in Fix 14 element rename and populateToc
claude Jun 7, 2026
1d533d0
chore: add tasks 16-17 for Astro update tracking and compat guards
claude Jun 10, 2026
b8eb3ee
chore: add tasks 18-20 for Sätteri-first monorepo restructuring
claude Jun 10, 2026
be472cc
chore: add task-21 package consolidation; supersede task-18/19
claude Jun 10, 2026
9238a9d
chore: add maintainability backlog tasks (TASK-22 to TASK-25)
claude Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .changeset/satteri-as-default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
"notro-loader": major
"rehype-beautiful-mermaid": minor
---

Switch default processor to Sätteri; replace remark/rehype plugin API with Sätteri MDASTP/HAST plugins.

**Breaking changes in `notro-loader`:**

- `NotroOptions.remarkPlugins` removed — remark plugins are no longer supported
- `NotroOptions.rehypePlugins` removed — rehype plugins are no longer supported
- `NotroOptions.processor` removed — Sätteri is now always the processor for static `.mdx` files
- `@astrojs/markdown-remark` removed from dependencies

**New API in `notro-loader`:**

- `NotroOptions.mdastPlugins` — Sätteri MDASTP plugins for static `.mdx` files
- `NotroOptions.hastPlugins` — Sätteri HAST plugins for static `.mdx` files
- `NotroOptions.shikiConfig` — unchanged; now routed through Astro's `markdown.shikiConfig` for the Sätteri pipeline

**Migration guide:**

```js
// Before
import { rehypeMermaid } from 'rehype-beautiful-mermaid';
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';

notro({
shikiConfig: { theme: 'github-dark' },
remarkPlugins: [remarkMath],
rehypePlugins: [[rehypeMermaid, { theme: 'github-dark' }], rehypeKatex],
})

// After
import { satteriMermaidPlugin } from 'rehype-beautiful-mermaid/satteri';

notro({
shikiConfig: { theme: 'github-dark' },
hastPlugins: [satteriMermaidPlugin({ theme: 'github-dark' })],
// Note: math in static .mdx files (remark-math + rehype-katex) has no
// Sätteri equivalent yet. Notion content supports math via string-level
// preprocessing regardless.
})
```

**New in `rehype-beautiful-mermaid`:**

- New entry point `rehype-beautiful-mermaid/satteri` exports `satteriMermaidPlugin()` — a Sätteri HAST plugin equivalent of `rehypeMermaid` for projects using `@astrojs/mdx` with Sätteri
- The existing `rehypeMermaid` (rehype API) is unchanged
11 changes: 11 additions & 0 deletions .changeset/satteri-processor-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"notro-loader": minor
---

Add `processor` option to `notro()` integration for Sätteri support.

- `notro({ processor: satteri() })` opts into Sätteri's Rust-based Markdown pipeline for faster static `.mdx` file builds
- notro automatically injects its callout MDASTP plugin so `:::callout{...}` directives work in `.mdx` files with Sätteri
- `remarkPlugins`, `rehypePlugins`, and `shikiConfig` continue to apply to the Notion content runtime path (`evaluate()`); a warning is emitted if these are set alongside `processor: satteri()` since they do not apply to `.mdx` files under Sätteri
- The default behavior (no `processor` option) is unchanged — notro uses `unified()` with its full remark/rehype pipeline
- Migrate `@astrojs/mdx` configuration from deprecated top-level `remarkPlugins`/`rehypePlugins` to `processor: unified(...)` API (required since `@astrojs/mdx@6.0.0`)
51 changes: 22 additions & 29 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,38 +323,34 @@ The `notro-loader` package exposes four entry points, each designed for a specif

### `notro()` Astro Integration

`notro()` is an Astro integration that registers `@astrojs/mdx` with notro's core plugin suite. It is required for two reasons:
`notro()` is an Astro integration that registers `@astrojs/mdx` with the Sätteri processor. It is required for two reasons:

1. **`astro:jsx` renderer** — `@astrojs/mdx` registers the `astro:jsx` renderer that `@mdx-js/mdx`'s `evaluate()` depends on to produce Astro VNodes. Without it, `NotroContent` fails at runtime.
2. **Static `.mdx` files** — if the project uses `.mdx` files alongside Notion content, `notro()` ensures they are processed with the same plugin pipeline as dynamically compiled Notion markdown.
2. **Static `.mdx` files** — if the project uses `.mdx` files alongside Notion content, `notro()` configures `@astrojs/mdx` with Sätteri's Rust-based Markdown pipeline and notro's core MDASTP plugins.

The interface mirrors `@astrojs/mdx`. Available options:
Available options:

| Option | Type | Purpose |
|---|---|---|
| `remarkPlugins` | `PluggableList` | Additional remark plugins (e.g. `[remarkMath]`) |
| `rehypePlugins` | `PluggableList` | Additional rehype plugins (e.g. `[rehypeKatex, [rehypeMermaid, { theme: 'github-dark' }]]`) |
| `shikiConfig` | `Record<string, unknown>` | Injects `@shikijs/rehype` as the last plugin (requires `npm i @shikijs/rehype`). Example: `{ theme: 'github-dark' }` |
| `mdastPlugins` | `MdastPluginDefinition[]` | Sätteri MDASTP plugins for static `.mdx` files |
| `hastPlugins` | `HastPluginDefinition[]` | Sätteri HAST plugins for static `.mdx` files (e.g. `[satteriMermaidPlugin()]`) |
| `shikiConfig` | `Record<string, unknown>` | Shiki syntax highlighting config. Routed through Astro's `markdown.shikiConfig` for the Sätteri pipeline. Example: `{ theme: 'github-dark' }` |
| `viteExternals` | `string[]` | Packages to add to Vite's `ssr.external` (for native binaries or dynamic imports) |
| `extendMarkdownConfig` | `boolean` | Whether to extend Astro's base markdown config (default: `false`) |
| `extendMarkdownConfig` | `boolean` | Whether to extend Astro's base markdown config. Defaults to `true` when `shikiConfig` is set. |

Usage in `astro.config.mjs`:
```js
import { notro } from "notro-loader/integration";
import { notionImageService } from "notro-loader/image-service";
import { rehypeMermaid } from "rehype-beautiful-mermaid";
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
import { satteriMermaidPlugin } from "rehype-beautiful-mermaid/satteri";

export default defineConfig({
image: { service: notionImageService },
integrations: [
notro({
shikiConfig: { theme: "github-dark" },
remarkPlugins: [remarkMath],
rehypePlugins: [
[rehypeMermaid, { theme: "github-dark" }],
rehypeKatex,
hastPlugins: [
satteriMermaidPlugin({ theme: "github-dark" }),
],
}),
sitemap(),
Expand All @@ -371,24 +367,21 @@ export default defineConfig({

### MDX Compile Pipeline

Defined in `packages/notro-loader/src/utils/mdx-pipeline.ts` via `@mdx-js/mdx`'s `evaluate()` (called from `compile-mdx.ts`). The pipeline is shared between the runtime Notion content path and static `.mdx` files via the `notro()` integration.
There are two separate pipelines:

**Core remark plugins** (always active):
- `remarkNfm` (from `remark-nfm`) — bundles pre-parse normalization (`preprocessNotionMarkdown`), directive syntax + GFM strikethrough/task-list support, and callout conversion in one plugin
**1. Notion content (runtime path)** — `compile-mdx.ts` → `@mdx-js/mdx`'s `evaluate()`

**User-provided remark plugins** (opt-in via `notro({ remarkPlugins })`):
- e.g. `remark-math` — enables `$...$` inline and `$$...$$` block math syntax
Notion markdown is preprocessed entirely at string level before `evaluate()` is called. No remark or rehype plugins run. Transforms applied:
- `preprocessNotionMarkdown()` — 19 fixes for Notion API markdown quirks (callout→JSX, element renaming, color→className, etc.)
- `applyMdxContext()` — heading IDs, TOC population, page link resolution

**Core rehype plugins** (always active, in order):
1. `rehypeRaw` — converts raw HTML strings from Notion markdown into hast nodes; passes through Notion custom elements (`callout`, `columns`, `video`, etc.)
2. `rehypeNotionColorPlugin` — converts `color="gray_bg"` / `underline="true"` attributes on `<p>`, `<h1-h6>`, `<span>` elements to `notro-*` CSS classes
3. `rehypeBlockElementsPlugin` — renames Notion block elements from lowercase to PascalCase so MDX routes them through the `components` map (e.g. `video` → `Video`, `table_of_contents` → `TableOfContents`)
4. `rehypeInlineMentionsPlugin` — same rename for inline mention elements (`mention-user` → `MentionUser`, etc.)
5. _(user-provided rehype plugins run here)_
6. `rehypeShiki` — injected automatically when `shikiConfig` is set (runs last so other plugins go first)
7. `rehypeSlug` — adds `id` attributes to h1–h4 headings
8. `rehypeTocPlugin` — populates `<TableOfContents>` with anchor links to all headings
9. `resolvePageLinksPlugin` — resolves Notion `notion.so` URLs in `<PageRef>`, `<DatabaseRef>`, mention elements, and `<a href>` using the `linkToPages` map
**2. Static `.mdx` files** — `@astrojs/mdx` with Sätteri processor

Configured by `notro()` in `astro.config.mjs`. Uses Sätteri's Rust-based Markdown pipeline:
- `notroCalloutPlugin` (MDASTP, always active) — converts `:::callout{...}` directives to `<callout>` MDX JSX elements
- User-provided `mdastPlugins` and `hastPlugins` (opt-in via `notro({ mdastPlugins, hastPlugins })`)
- Shiki syntax highlighting — enabled via `notro({ shikiConfig: { theme: '...' } })`
- Mermaid diagram rendering — via `satteriMermaidPlugin()` from `rehype-beautiful-mermaid/satteri`

**Component mapping** (HTML elements → Astro components):
- After `evaluate()`, `<Content components={notionComponents} />` maps every Notion block type (callout, toggle, columns, images, table, TOC, etc.) and standard HTML element to an Astro component
Expand Down
15 changes: 15 additions & 0 deletions backlog/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
project_name: "Sätteri Migration"
default_status: "To Do"
statuses: ["To Do", "In Progress", "Done"]
labels: []
date_format: yyyy-mm-dd
max_column_width: 20
auto_open_browser: true
default_port: 6420
remote_operations: true
auto_commit: false
filesystem_only: false
bypass_git_hooks: false
check_active_branches: true
active_branch_days: 30
task_prefix: "task"
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
id: TASK-1
title: 'Migrate @astrojs/mdx integration to processor: unified() API'
status: Done
assignee: []
created_date: '2026-06-06 00:13'
updated_date: '2026-06-06 00:51'
labels: []
dependencies: []
priority: high
ordinal: 1000
---

## Description

<!-- SECTION:DESCRIPTION:BEGIN -->
## Background

Astro 6.4 deprecated the top-level `remarkPlugins` / `rehypePlugins` options on `@astrojs/mdx`. These will be removed in Astro 8.0.

The new API wraps plugins inside `processor: unified({ remarkPlugins, rehypePlugins })`.

## Current code (packages/notro-loader/src/integration.ts:149)

```ts
updateConfig({
integrations: [mdx({
remarkPlugins: [remarkNfm, ...remarkPlugins], // deprecated
rehypePlugins: allRehypePlugins, // deprecated
extendMarkdownConfig,
})],
})
```

## Target

```ts
import { unified } from '@astrojs/markdown-remark';

updateConfig({
integrations: [mdx({
// explicit processor: unified() serves double duty:
// 1. uses the non-deprecated API (remarkPlugins/rehypePlugins on mdx() are removed in Astro 8.0)
// 2. guards against inheriting markdown.processor: satteri() from the user's Astro config —
// notro's pipeline requires remark/rehype plugins that Sätteri doesn't support
processor: unified({
remarkPlugins: [remarkNfm, ...remarkPlugins],
rehypePlugins: allRehypePlugins,
}),
extendMarkdownConfig: false,
})],
})
```

## Why both processor: unified() and extendMarkdownConfig: false

`@astrojs/mdx`'s `processor` option defaults to inheriting `markdown.processor` from the Astro config.
If a user sets `markdown.processor: satteri()` for `.md` files, MDX would also switch to Sätteri —
breaking notro because Sätteri doesn't support remark/rehype plugins.

`extendMarkdownConfig: false` prevents inheriting legacy `markdown.remarkPlugins` etc.,
while `processor: unified({...})` explicitly pins the MDX processor regardless of the user's
top-level `markdown.processor` setting.

## Acceptance criteria

- [x] `integration.ts` uses `processor: unified()` instead of deprecated top-level options
- [x] No deprecation warnings in `pnpm run build` output
- [x] `pnpm run build` passes
- [x] Comment added explaining why `processor: unified()` is explicit (not just default)
- [x] `NotroOptions` JSDoc updated to mention Sätteri incompatibility
- [x] Changeset added (patch for notro-loader)
<!-- SECTION:DESCRIPTION:END -->
Loading