Skip to content

Migrer dokumentasjon fra Sanity til MDX#1795

Draft
oscarcarlstrom wants to merge 8 commits into
mainfrom
feat/migrate-docs-to-mdx
Draft

Migrer dokumentasjon fra Sanity til MDX#1795
oscarcarlstrom wants to merge 8 commits into
mainfrom
feat/migrate-docs-to-mdx

Conversation

@oscarcarlstrom
Copy link
Copy Markdown
Contributor

@oscarcarlstrom oscarcarlstrom commented May 28, 2026

Oppsummering

Flytter alt dokumentasjonsinnhold fra Sanity inn i apps/docs/src/content/ som MDX-filer, slik at docs kan skrives, reviewes og merges i samme PR som koden. Løser AB#140852 under epicen Fase ut Sanity for Grunnmuren-dokumentasjonen (AB#140851).

Hvorfor

  • Docs og kode i samme PR: Selv om Sanity-utkast kan skrives via MCP samtidig med koden, betyr en separat redigerings-flate ekstra round-trip og dobbelt vedlikehold når review-feedback endrer koden — derfor blir docs i praksis ofte skrevet etter at PR-en er merget. Når innholdet bor sammen med koden får vi: docs reviewes som del av PR-en, ingen «hindring» til release når PR-en merges, og ingen risiko for å glemme docs helt.
  • Komponenter under utvikling: Live-eksempler bruker workspace-bygget av Grunnmuren (react-live importerer @obosbbl/grunnmuren-react), så vi kan dokumentere ureleasede komponenter allerede før første npm-release. componentState: unreleased skjuler dem fra meny/oversikt, og ci:version flipper til beta/new automatisk ved release.
  • Storybook-embeds: build:storybook bygger inn i samme docs-deploy, så en ny story shippes i samme PR som koden og MDX-en. Ingen «deploy story first»-friksjon.
  • Tailwind v4 JIT: Scanner-en fanger ikke opp klasser som lever som strenger inne i Sanity-innholdet. Når kode-eksempler bor i scannede .mdx-filer, havner alle klasser i CSS-bundlen naturlig.
  • Mindre kompleksitet: Sanity-laget (CMS, studio, preview-infra, ~15 ganske store npm-pakker) gir ingen reell gevinst når alle som vedlikeholder docs har repo-tilgang.

Hvordan

  • MDX-pipeline: @mdx-js/rollup + remark-gfm + frontmatter. To egne remark-plugins:
    • (a) Kode-fences (```tsx live / ```tsx) gjøres om til ComponentPreview (live, react-live) eller Code (statisk, react-shiki) ved build.
    • (b) Overskrifter får id-er (så innholdsfortegnelsen kan lenke til dem), og samme plugin eksporterer en liste over overskriftene som innholdsfortegnelse-komponenten leser. Begge gjøres i samme gjennomgang — slik at lenkene og id-ene aldri kommer i utakt.
  • Sanity-fallback: alle eksisterende komponent- og info-sider i Sanity er migrert i denne PR-en, så fallback-en trigges ikke i normaltilfellet. Rutene prøver MDX først og faller tilbake til Sanity hvis sluggen mangler — sikkerhetsnett i tilfelle noen legger inn nytt innhold i Sanity før Sanity-integrasjonen er fjernet i oppfølger-PR-en. Sanity styrer fortsatt menystrukturen; den biten flyttes til en repo-config i oppfølgeren.
  • Bilder: lokal WebP, maksgrenser ≤ 250 KB / ≤ 2000px / .webp.svg kun. To uavhengige mekanismer:
    • Optimalisering ved migrering: apps/docs/migration/sanity-to-mdx.ts encoder hvert bilde først på WebP-kvalitet 80, og re-encoder samme bilde på 72 → 64 → 56 → 48 til det enkelte bildet er ≤ 250 KB. Nye bilder lagt til manuelt etter migreringen pre-optimaliseres av utvikleren (cwebp/Squoosh — se CONTRIBUTING).
    • Validering i CI: pnpm lint:images sjekker hvert committet bilde og feiler hvis noen bryter en maksgrense. Trinnet er gated av et eget paths-filter-steg som detekterer hvilke filer som er endret i pushen/PR-en — selve lint-trinnet kjører bare når innholdsbilder eller lint-scriptet er blant endringene, ellers hoppes det over.
  • Skjul-til-release: componentState: unreleased skjuler siden fra meny/oversikt, men URL-en virker. Ved release flipper scripts/strip-unreleased.ts (hektet i ci:version) til beta (hvis noen propsComponents har UNSAFE_-prefiks) eller new — så Version Packages-PR-en bærer både versjons-bumpen og synligheten i samme commit.

Avhengigheter - oversikt

Antall deklarerte deps i apps/docs/package.json
Før denne PR-en (main) 43
Etter denne PR-en 53 (+10 MDX-/migrerings-/lint-deps)
Etter Sanity-fjernings-PR-en (oppfølger) 38 (-14 Sanity-relaterte, -1 sharp som hører til migreringsscriptet)

Sluttbalansen: -5 deklarerte deps, men vi bytter ut 14 tunge Sanity-pakker (selve sanity med studio, @sanity/visual-editing, @sanity/assist, styled-components, @portabletext/react, m.fl.) med 9 små build-time-plugins (remark-plugins, @mdx-js/rollup, image-size).

Sanity-deps som fjernes (14): sanity, @sanity/client, @sanity/vision, @sanity/visual-editing, @sanity/preview-url-secret, @sanity/image-url, @sanity/asset-utils, @sanity/code-input, @sanity/assist, @sanity/table, @portabletext/react, groq, styled-components, @code-obos/sanity-auth.

MDX/build-deps som blir værende (9): @mdx-js/rollup, remark-frontmatter, remark-gfm, remark-mdx-frontmatter, mdast-util-to-string, unist-util-visit, @types/unist, estree-util-value-to-estree, image-size.

Kode som er viktig å se på

  • apps/docs/vite.config.ts — kobler MDX-støtten inn i Vite. Må kjøres før React-pluginet, ellers vil React ikke kjenne igjen den ferdig kompilerte MDX-en.
  • apps/docs/src/lib/mdx/remark-code-blocks.ts — sørger for at kodeblokker i MDX-en (de som starter med ```tsx) vises som ComponentPreview eller Code i ferdig dokumentasjon. Gjør at man kan skrive eksempler som vanlige kodeblokker i stedet for å pakke koden inn som en streng-verdi på en komponent — det siste hadde betydd at man måtte skrive om alle anførselstegn og spesialtegn for hvert eneste eksempel.
  • apps/docs/src/lib/mdx/remark-headings.ts — gir hver overskrift en id som innholdsfortegnelsen kan lenke til, og lager samtidig selve listen over overskrifter. Det skjer i samme gjennomgang av siden, så lenkene i innholdsfortegnelsen og id-ene i teksten kan ikke komme i utakt.
  • apps/docs/src/lib/content.ts — oversikten over alle migrerte MDX-filer. Hver rute slår opp her for å finne riktig side. Alle filer lastes inn samtidig ved build for å holde rute-koden enkel; vi kan dele dem opp senere hvis nedlastingen blir for tung.
  • apps/docs/src/ui/mdx-doc.tsx — selve rammen rundt en docs-side (h1-tittel, ressurs-lenker, innholdsfortegnelse, status-alertbox inkl. unreleased, props-tabeller). Det er det som vises rundt selve teksten på siden.
  • apps/docs/src/routes/_docs/$slug.tsx + komponenter/$slug.tsx — det som bestemmer hva som vises når noen besøker en docs-URL: prøver først å finne siden som en migrert MDX-fil, faller tilbake til Sanity hvis den ikke (enda) ligger i repoet.
  • apps/docs/src/ui/main-nav.tsx + komponenter/index.tsx — venstremenyen og komponentoversikten skjuler sider som er merket componentState: unreleased.
  • apps/docs/scripts/lint-images.ts — selve bilde-sjekken (kontrollerer format, filstørrelse og bredde).
  • apps/docs/scripts/strip-unreleased.ts — kjøres når vi lager en ny release og endrer componentState: unreleased til beta eller new i hver MDX-fil. Det gjør at en ny komponent dukker opp i menyen samme dag som den publiseres på npm. Hektet inn i root ci:version (det changesets kaller for å lage Version Packages-PR-en).
  • .github/workflows/main.yml — CI-trinnene som kjører vite build av docs-appen (fanger MDX-feil og andre build-regresjoner før merge — cd.yml bygger først ved deploy, som ville vært for sent) og lint:images.
  • apps/docs/AGENTS.md + apps/docs/CONTRIBUTING.md — konvensjoner for både agenter og mennesker som skriver eller endrer docs.

Kode som ikke trenger samme detaljgransking

  • apps/docs/migration/sanity-to-mdx.tsmigreringsscriptet. Brukes for å produsere innholdet, forsvinner med Sanity-integrasjonen i en oppfølger-PR.
  • 30 komponent-MDX + 40 info-MDX i apps/docs/src/content/ — alt er migrert fra Sanity. Hver fil er migrert med scriptet. Jeg har gått gjennom alle sider manuelt og det er kun små diffs på layout (spacing) og bilder (oppløsning/kvalitet).
  • ~236 WebP-bilder i src/content/**/images/ — alle lastet fra Sanity og optimalisert lokalt. 8.3 MB totalt, alle innenfor maksgrensene.

Gjenstår (oppfølgere under epicen)

  • Erstatt Sanity-drevet navigasjon/meny med repo-config (i dag er filteret repo-drevet, men listen er fortsatt Sanity-drevet).
  • Fjern hele Sanity-integrasjonen — studio, preview-ruter, visual editing, env-variabler, ~15 npm-deps, migreringsscriptet.
  • Slett grunnmuren-datasettet i Sanity — siste steg, etter prod-verifisering.
  • /document-component-skill for å scaffolde nye komponentsider (er skissert og parkert lokalt hos meg).

🤖 Generert med Claude Code, kvalitetssikret av Oscar Carlström (kan være manuelt redigert).

oscarcarlstrom and others added 3 commits May 28, 2026 13:54
Move all documentation content into apps/docs/src/content/ so docs can be
authored, reviewed and merged in the same PR as the code they describe.
Routes prefer migrated MDX and fall back to Sanity for any unmigrated
slug, so existing pages keep working until Sanity is fully retired in a
follow-up.

Adds the MDX rendering pipeline (remark plugins for fenced code blocks
and heading anchors+toc, mdx components map, local Image component),
the image-budget lint, the MDX compile check (both gated in CI), and
the unreleased-component lifecycle (componentState: unreleased →
beta/new via the ci:version hook).

Co-Authored-By: Claude <noreply@anthropic.com>
Collapse the duplicate `return 'new'` branches by inverting the
predicate — easier to read.

Co-Authored-By: Claude <noreply@anthropic.com>
strip-unreleased previously flipped `unreleased → beta` whenever any
`propsComponents` entry started with `UNSAFE_`. The auto-generated
`component-props.ts` also picks up React Aria Components exports, so an
RAC `UNSAFE_PortalProvider` leaking in would incorrectly mark a stable
Grunnmuren component as beta.

Read packages/react/src directly to build the authoritative set of
`UNSAFE_`-prefixed exports from `@obosbbl/grunnmuren-react`, and check
propsComponents against that set instead.

Co-Authored-By: Claude <noreply@anthropic.com>
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 28, 2026

⚠️ No Changeset found

Latest commit: b5be069

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

oscarcarlstrom and others added 5 commits May 28, 2026 15:41
Adding the Grunnmuren-exports check reintroduced the duplicate
return 'new' branches. Fold the early return into the main flow so
the default value is only spelled once.

Co-Authored-By: Claude <noreply@anthropic.com>
remark-headings only needed a function to turn heading text into a
URL-safe id (with dedupe). github-slugger is a whole package for that
— a tiny inline function with Unicode-aware character classes does the
same job and avoids the dependency.

Verified output matches the previous github-slugger run on the sample
of headings in the migrated content (`Om komponenten` →
`om-komponenten`, `Produkt/Tjenester` → `produkttjenester`, Norwegian
characters preserved, duplicate-heading dedupe still works).

Co-Authored-By: Claude <noreply@anthropic.com>
`æ`/`å` → `a`, `ø` → `o`, plus NFD normalisation to strip other accents
(`é` → `e` etc.) so heading ids stay ASCII and URL-safe.

Co-Authored-By: Claude <noreply@anthropic.com>
The MDX path had a duplicate `MdxToc` that rendered identical markup.
Make `TableOfContentsNav` source-agnostic by taking a pre-built
`sections: { href, text }[]` prop, and have both call sites build
their sections inline (from Sanity blocks vs. from the MDX `toc`
export).

Co-Authored-By: Claude <noreply@anthropic.com>
`check-mdx.ts` ran the same MDX compile pipeline as the app, so it was
a partial reimplementation of what `vite build` already does. Replace
with a `Build docs` step in the PR workflow so MDX errors and any other
build regressions are caught together. The deploy build in cd.yml stays
the same; this just moves the catch from deploy-time to PR-time.

Drops `check-mdx.ts`, the `check:content` script, and the `@mdx-js/mdx`
devDep (only used by the script — the app uses `@mdx-js/rollup`).

Co-Authored-By: Claude <noreply@anthropic.com>
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