Skip to content
Content
Skill

/migrate-mdc-to-comark

Port a legacy MDC-based PR onto the new comark-based `main`. Use when a contributor's PR was opened against the old `@nuxtjs/mdc` API (any commit before `feat(mdc): comark migration #355`) and now needs to be rebased / merged with the current Nuxt Studio main branch, which uses

From plugin
nuxt-studio
7082 skills
Install
$ npx -y skills add nuxt-content/nuxt-studio --skill migrate-mdc-to-comark --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/migrate-mdc-to-comark

Context preview

The summary Claude sees to decide when to auto-load this skill.

Port a legacy MDC-based PR onto the new comark-based `main`. Use when a contributor's PR was opened against the old `@nuxtjs/mdc` API (any commit before `feat(mdc): comark migration #355`) and now needs to be rebased / merged with the current Nuxt Studio main branch, which uses

SKILL.md

migrate-mdc-to-comark.SKILL.md
name: migrate-mdc-to-comark
description: Port a legacy MDC-based PR onto the new comark-based `main`. Use when a contributor's PR was opened against the old `@nuxtjs/mdc` API (any commit before `feat(mdc): comark migration #355`) and now needs to be rebased / merged with the current Nuxt Studio main branch, which uses `comark` everywhere.
allowed-tools: Read, Write, Edit, Glob, Grep, Bash

Migrating a Legacy MDC PR to Comark

Since commit `5464103e feat(mdc): comark migration (#355)` (merged 2026-05-13), Nuxt Studio's `main` branch no longer uses `@nuxtjs/mdc`. The entire conversion layer, document utilities, and TipTap bridges were rewritten on top of [`comark`](https://github.com/nuxt-content/comark).

Any PR opened against `main` **before** this commit will conflict on merge. Do **not** simply resolve the conflicts mechanically — most legacy API calls have semantic equivalents in comark that must be re-implemented.

This skill is the reverse of the previous "merge-main-on-comark" skill: the comark branch is now `main`, and legacy PRs need to be ported forward.

1. Identify whether the PR is legacy

Run these checks at the root of the PR branch:

# Legacy imports — any match means the PR predates the comark migration
grep -rE "from '@nuxtjs/mdc'|from 'remark-mdc'|from 'unist-util-visit'|from '@nuxt/content/runtime'" src/

# Legacy function names
grep -rE "generateDocumentFromContent|generateDocumentFromMarkdownContent|generateDocumentFromYAMLContent|generateDocumentFromJSONContent|generateContentFromDocument|generateContentFromMarkdownDocument|generateContentFromYAMLDocument|generateContentFromJSONDocument|mdcToTiptap|tiptapToMDC|compressTree|decompressTree|parseMarkdown|stringifyMarkdown|parseFrontMatter|stringifyFrontMatter|remarkEmojiPlugin" src/

# Legacy file references (these files were deleted on main)
ls src/app/src/utils/tiptap/mdcToTiptap.ts src/app/src/utils/tiptap/tiptapToMdc.ts 2>/dev/null

If anything matches, the PR is legacy.

2. Conceptual Mapping: Legacy MDC → Comark

Types

| Legacy (pre-#355) | Comark (current main) | |---|---| | `MDCRoot`, `MarkdownRoot` from `@nuxt/content` | `ComarkTree` from `comark` | | `MDCNode`, `MDCElement`, `MDCText`, `MDCComment` from `@nuxtjs/mdc` | `ComarkNode`, `ComarkElement`, `ComarkComment` from `comark` | | MDC element: `{ type: 'element', tag: 'note', props: {…}, children: [...] }` | Tuple: `['note', attrs, ...children]` | | MDC text: `{ type: 'text', value: 'hello' }` | Plain string: `'hello'` | | MDC comment: `{ type: 'comment', value: 'txt' }` | Tuple: `[null, {}, 'txt']` |

Functions (document utilities)

Located in `src/module/src/runtime/utils/document/generate.ts`:

| Legacy | Comark | |---|---| | `generateDocumentFromContent(id, content, opts)` | `documentFromContent(id, content, opts)` | | `generateDocumentFromMarkdownContent(...)` | `documentFromMarkdownContent(...)` | | `generateDocumentFromYAMLContent(...)` | `documentFromYAMLContent(...)` | | `generateDocumentFromJSONContent(...)` | `documentFromJSONContent(...)` | | `generateContentFromDocument(doc)` | `contentFromDocument(doc)` | | `generateContentFromMarkdownDocument(doc)` | `contentFromMarkdownDocument(doc)` | | `generateContentFromYAMLDocument(doc)` | `contentFromYAMLDocument(doc)` | | `generateContentFromJSONDocument(doc)` | `contentFromJSONDocument(doc)` |

The shape `generate-X-from-Y` was reversed to `Y-from-X` for symmetry.

TipTap bridges

Located in `src/app/src/utils/tiptap/`:

| Legacy file (deleted on main) | Comark replacement | |---|---| | `mdcToTiptap.ts` | `comarkToTiptap.ts` | | `tiptapToMdc.ts` | `tiptapToComark.ts` | | `mdcToTiptap(body, frontmatterData, opts)` | `comarkToTiptap(tree, opts)` — options collapsed to a single object; frontmatter is part of the tree | | `tiptapToMDC(json, opts) → { body, data }` | `tiptapToComark(json, opts) → ComarkTree` — returns one tree with `frontmatter` embedded |

Parser / stringifier

| Legacy | Comark | |---|---| | `import { parseMarkdown } from '@nuxtjs/mdc/runtime/parser/index'` | `import { parse } from 'comark'` | | `import { stringifyMarkdown } from '@nuxtjs/mdc/runtime'` | `import { renderMarkdown } from 'comark/render'` | | `parseFrontMatter` / `stringifyFrontMatter` from `remark-mdc` | `js-yaml` (`yaml.load` / `yaml.dump`) for pure YAML/JSON files; for Markdown, frontmatter is now embedded in `ComarkTree.frontmatter` | | `compressTree` / `decompressTree` from `@nuxt/content/runtime` | No longer used in app code — see §4 "Legacy bridge" | | `visit` from `unist-util-visit` | Walk the tuple tree manually using the helpers from `src/app/src/utils/comark.ts` |

Plugin / option shape

// Legacy
parseMarkdown(content, {
  contentHeading: …,
  highlight: { theme },
  remark: {
    plugins: {
      'emoji': { instance: remarkEmojiPlugin },
      'remark-mdc': { options: { autoUnwrap: true } },
    },
  },
})

// Comark
import { parse } from 'comark'
import comarkEmoji from 'comark/plugins/emoji'
import highlight from 'comark/plugins/highlight'
import tocPlugin from 'comark/plugins/toc'

parse(content, {
  autoClose: false,
  autoUnwrap: true,
  plugins: [
    comarkEmoji(),
    highlight({ themes: { default, dark, light } }),
    tocPlugin({ depth: 2, searchDepth: 2, title: '', links: [] }),
  ],
})

For rendering back to markdown:

// Legacy
const markdown = await stringifyMarkdown(body, data, {
  frontMatter: { options: { lineWidth: 0 } },
  plugins: { remarkMDC: { options: { autoUnwrap: true } } },
})

// Comark
const markdown = await renderMarkdown(tree, {
  blockAttributesStyle: 'frontmatter',
  components: { br: () => ':br' },
})

Dependencies (`package.json`)

Removed from runtime deps: `@nuxtjs/mdc`, `remark-mdc` (moved to devDeps), `unist-util-visit`.

Added: `comark` (pulled from `pkg.pr.new` while it stabilises). `js-yaml` is now used directly for YAML.

`minimark` is also a devDep only — do not import it in runtime code.

If the

Read more
Ships withnuxt-studio

![npm version][npm-version-href] ![npm downloads][npm-downloads-href] ![License][license-href] Visual edition in production for your Nuxt Content website.

Get the whole plugin
Stats
708
Stars
138
Forks
Active
Maintenance
TypeScript
Language
MIT
License
5h ago
Last commit
11mo ago
Created

Repo: nuxt-content/nuxt-studio