refactor(website): migrate to Next.js 15 + Cloudflare Workers CI by Jocs · Pull Request #4305 · marktext/marktext · GitHub
Skip to content

refactor(website): migrate to Next.js 15 + Cloudflare Workers CI - #4305

Merged
Jocs merged 5 commits into
developfrom
worktree-tender-hugging-horizon
May 29, 2026
Merged

Jocs merged 5 commits into
developfrom
worktree-tender-hugging-horizon

Conversation

@Jocs

@Jocs Jocs commented May 29, 2026

Copy link
Copy Markdown
Member

Summary

  • Migrate packages/website/ from Vite + React 18 SPA to Next.js 15 App Router (React 19) with SSR — full SEO surface: Metadata API (OG/Twitter), JSON-LD SoftwareApplication, sitemap + robots routes.
  • Markdown demos are pre-rendered at build time (scripts/prerender-markdown.ts using marked + prismjs + KaTeX) so crawlers see real HTML on first paint and the runtime bundle no longer ships the markdown engine. Mermaid still hydrates client-side.
  • Ship .github/workflows/website-deploy.yml: push to develop → production deploy via wrangler deploy; PRs touching packages/website/** → preview upload + sticky comment with preview URL. Powered by @opennextjs/cloudflare.
  • Bind marktext.me as a Worker Custom Domain in wrangler.toml.

What changed

  • New: src/app/ (layout, page, sitemap, robots), src/lib/markdown.ts, src/generated/* (build-time output, git-ignored), scripts/prerender-markdown.ts, next.config.ts, open-next.config.ts, wrangler.toml, .eslintrc.json, .github/workflows/website-deploy.yml.
  • Removed: index.html, vite.config.ts, src/main.tsx, src/App.tsx, src/utils/markdownToHtml.ts, tsconfig.node.json, vite-env.d.ts.
  • Components: Sponsor stays a Server Component; TitleBar / Theme / Footer / Feature marked 'use client'. CSS imports centralized in app/layout.tsx (App Router requirement for global CSS).
  • Assets moved src/assets/* → public/assets/*; CSS url() refs updated to absolute /assets/....
  • pnpm-workspace.yaml: sharp, unrs-resolver, workerd added to allowBuilds.

Required before this can deploy

Repo secrets (already configured):

  • CLOUDFLARE_API_TOKEN — scoped to Workers Scripts Edit + Workers Routes Edit + Account Settings Read
  • CLOUDFLARE_ACCOUNT_ID

Test plan

  • pnpm install succeeds (sharp / workerd / unrs-resolver builds approved)
  • pnpm --filter marktext-website run type-check passes
  • pnpm --filter marktext-website run lint passes
  • pnpm --filter marktext-website run build produces a successful Next build (all routes static)
  • pnpm --filter marktext-website run cf:build emits .open-next/worker.js + .open-next/assets/
  • CI Deploy website workflow runs green on this PR
  • Preview URL appears as a sticky comment on this PR
  • Preview URL renders all 5 sections, switches Features, switches Themes, draws Mermaid diagrams, no console errors
  • view-source: on preview URL shows OG / Twitter meta, JSON-LD, prerendered demo markdown (not empty divs)

Notes on Cloudflare custom domain

The first production deploy will bind marktext.me as a Worker Custom Domain. If the apex currently has a conflicting A/AAAA/CNAME record, the deploy will fail until that record is removed in the Cloudflare DNS dashboard.

🤖 Generated with Claude Code

Replace the Vite + React 18 SPA with a Next.js 15 App Router app (React 19)
deployed to Cloudflare Workers via @opennextjs/cloudflare. The site is now
SSR-rendered with full SEO surface area: Metadata API with OG/Twitter cards,
JSON-LD SoftwareApplication schema, sitemap and robots routes.

Markdown demos are pre-rendered at build time by scripts/prerender-markdown.ts
(marked + prismjs + KaTeX) so crawlers receive real HTML on first paint and
the runtime bundle no longer carries the markdown engine. Theme CSS is
similarly baked into a generated module to remove the Vite-only ?inline
imports. Mermaid still hydrates client-side because it needs DOM.

Component split: Sponsor stays a Server Component; TitleBar, Theme, Footer,
and Feature are marked 'use client' for state / animations / Mermaid runs.
Per-component CSS imports moved to layout.tsx (Next.js App Router only
allows global CSS from there). Assets moved from src/assets/ to public/
and referenced by string path.

CI: new .github/workflows/website-deploy.yml runs lint + typecheck + Next
build + OpenNext bundle on every push to develop and on PRs that touch
packages/website (or its inputs). develop deploys to production via
'wrangler deploy'; PR builds upload a versioned preview and post a sticky
comment with the preview URL. Requires CLOUDFLARE_API_TOKEN and
CLOUDFLARE_ACCOUNT_ID repo secrets.

wrangler.toml binds marktext.me as a Worker Custom Domain so Cloudflare
manages the route + DNS record automatically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 29, 2026 03:45
…build

The root postinstall.ts rebuilds Electron-native modules (native-keymap,
keytar, ced) on every pnpm install. On Ubuntu runners these need libx11-dev,
libxkbfile-dev, and libsecret-1-dev or node-gyp fails before the website
build even starts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates the website package from a Vite/React SPA to a Next.js App Router site with build-time markdown prerendering and Cloudflare Workers deployment support.

Changes:

  • Adds Next.js app structure, metadata routes, prerendered markdown/theme generation, and OpenNext/Workers config.
  • Updates website components to use client/server component boundaries and public asset paths.
  • Adds website deployment CI and updates website dependencies/tooling.

Reviewed changes

Copilot reviewed 29 out of 51 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
.github/workflows/website-deploy.yml Adds website build, preview, and production deploy workflow.
pnpm-workspace.yaml Allows build scripts for new website/Workers-related native packages.
packages/website/package.json Replaces Vite scripts/deps with Next.js, OpenNext, Wrangler, and prerender tooling.
packages/website/.gitignore Ignores Next/OpenNext/generated outputs.
packages/website/.eslintrc.json Adds website ESLint config.
packages/website/next.config.ts Adds Next.js configuration.
packages/website/open-next.config.ts Adds OpenNext Cloudflare config.
packages/website/wrangler.toml Adds Cloudflare Worker, assets, route, and observability config.
packages/website/tsconfig.json Updates TypeScript settings for Next.js.
packages/website/tsconfig.node.json Removes Vite-specific TS config.
packages/website/index.html Removes Vite HTML entry point.
packages/website/src/main.tsx Removes Vite React entry point.
packages/website/src/App.tsx Replaced by Next.js app/page.tsx.
packages/website/src/vite-env.d.ts Removes Vite asset/module declarations.
packages/website/src/types/assets.d.ts Adds remaining inline CSS module declaration.
packages/website/src/app/layout.tsx Adds root layout, metadata, global CSS imports, and JSON-LD.
packages/website/src/app/page.tsx Adds homepage composition under App Router.
packages/website/src/app/robots.ts Adds robots metadata route.
packages/website/src/app/sitemap.ts Adds sitemap metadata route.
packages/website/scripts/prerender-markdown.ts Adds build-time markdown/theme prerender generation.
packages/website/src/lib/markdown.ts Exports generated markdown HTML.
packages/website/src/utils/markdownToHtml.ts Removes runtime markdown rendering.
packages/website/src/utils/themeColor.ts Reads generated theme CSS strings.
packages/website/src/components/TitleBar.tsx Converts to client component and public asset paths.
packages/website/src/components/Feature.tsx Uses prerendered markdown and Mermaid hydration.
packages/website/src/components/Feature.css Updates asset URL path.
packages/website/src/components/Theme.tsx Uses prerendered theme markdown and theme switching.
packages/website/src/components/Sponsor.tsx Converts asset references to public paths.
packages/website/src/components/Footer.tsx Converts asset references and cleans up canvas animation lifecycle.
packages/website/src/themes/material-dark.theme.css Updates commented asset URLs.
packages/website/public/assets/* Moves website assets into Next.js public assets.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/website/package.json Outdated
"dev": "next dev --port 3000",
"build": "next build",
"start": "next start",
"lint": "next lint --max-warnings 0",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 0866c24 — switched the lint script to eslint . --max-warnings 0 and added a flat ESLint 9 config (eslint.config.mjs) that re-exports the next/core-web-vitals preset via @eslint/eslintrc FlatCompat. .eslintrc.json removed.

"build": "next build",
"start": "next start",
"lint": "next lint --max-warnings 0",
"type-check": "tsc --noEmit",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Already fixed in earlier commit b4cc5c8 — workflow now runs tsx scripts/prerender-markdown.ts as a dedicated "Prerender content" step before lint / type-check / build, so the gitignored src/generated/* modules exist when tsc reads them.

cache: 'pnpm'

# The root postinstall script rebuilds the desktop app's Electron-native
# modules (native-keymap, keytar, ced). They need these system libraries

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Already fixed in earlier commit 70e43f2 — added an "Install desktop-rebuild system libs" step that apt-get installs libx11-dev libxkbfile-dev libsecret-1-dev before pnpm install, so the root postinstall's Electron rebuild of native-keymap/keytar/ced succeeds on the Ubuntu runner.

/<pre><code class="language-mermaid">__MERMAID_BLOCK__([A-Za-z0-9+/=]+)__END__\s*<\/code><\/pre>/g,
(_, b64) => {
const source = Buffer.from(b64, 'base64').toString('utf-8')
return `<div class="mermaid">${escapeHtml(source.trim())}</div>`

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 0866c24 — the prerender script now routes both code paths through a mermaidDiv(source) helper that emits <div class="mermaid" data-source="...">, so Theme.tsx can restore the diagram text after mermaid.run() swaps .textContent for an <svg>.

.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.trim()
return MERMAID_HEAD.test(text) ? `<div class="mermaid">${escapeHtml(text)}</div>` : full

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 0866c24 — the fallback regex path now also goes through the shared mermaidDiv() helper, so detected-by-content Mermaid blocks get data-source too.

Comment on lines +21 to +23
"next": "^15.5.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

pnpm-lock.yaml was committed alongside the manifest changes in 045ac77 and updated again in 0866c24 after adding @eslint/eslintrc. The deploy workflow uses pnpm install --frozen-lockfile and has passed install on subsequent runs.

{ title: 'Diagram', description: 'Support Flowchart, Sequence diagram, Gantt diagram, Vega chart.', key: 'diagram' },
{ title: 'Inline Formats', description: 'Support CommonMark and GitHub Flavored Markdown Spec.', key: 'inlineFormats' },
{ title: 'Math Formula', description: 'Markdown extensions math expressions (KaTeX)', key: 'mathFormula' },
{ title: 'Code Block', description: 'Support GFM code fense, highlight by prismjs.', key: 'codeBlock' }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 0866c24 — "fense" → "fence".

src/generated/{markdown-html,theme-css}.ts are gitignored and only created
by the prerender script; without an explicit step before type-check, tsc
fails on the missing modules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented May 29, 2026 •

Copy link
Copy Markdown
Contributor

Build artifacts for PR #4305:

Run: https://github.com/marktext/marktext/actions/runs/26622440621

Artifact Size Link
marktext-windows-arm64 270.4 MB Download
marktext-linux 592.6 MB Download
marktext-macos-x64 271.4 MB Download
marktext-windows-x64 271.7 MB Download
marktext-macos-arm64 261.2 MB Download

- Mermaid prerender now emits data-source on each <div class="mermaid"> so
  Theme.tsx can restore the diagram text after mermaid.run() swaps it for
  an <svg>. Both the marked-emitted path and the fallback regex-detected
  path go through the same mermaidDiv() helper.
- Switch lint script from deprecated `next lint` to `eslint` CLI with a
  flat ESLint 9 config (eslint.config.mjs) that re-exports the
  next/core-web-vitals preset via @eslint/eslintrc FlatCompat. Removes the
  legacy .eslintrc.json.
- Feature.tsx: fix typo "fense" -> "fence".
- website-deploy.yml: handle the first-ever PR run, when the Worker hasn't
  been bootstrapped on Cloudflare yet. `wrangler versions upload` only
  updates existing Workers; on that specific error we now soft-fail and
  post a sticky comment explaining that the next push to develop will
  bootstrap the Worker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented May 29, 2026 •

Copy link
Copy Markdown
Contributor

- Move the mermaid runtime behind a single dynamic import in a shared
  packages/website/src/lib/mermaid.ts helper. Mermaid is ~500 KB and was
  previously bundled into the initial JS for `/`; Feature and Theme now both
  use `useMermaidRender` (or `runMermaid` + `setMermaidTheme`) and the chunk
  only loads when a diagram actually appears.
- Theme.tsx: split the page-CSS swap and the mermaid repaint into separate
  effects. Mermaid only repaints when the theme darkness flips, not on every
  light↔light switch, since mermaid's 'default' / 'dark' palettes don't
  distinguish individual themes.
- Drop `src/lib/markdown.ts` (a zero-value re-export) and import the
  generated map directly from `@/generated/markdown-html`.
- Drop `src/types/assets.d.ts` (declares the Vite-only `?inline` CSS query
  that no source file uses anymore).
- prerender-markdown.ts: ship only the prism-javascript grammar; bash / ts
  / json / jsx / tsx / css / markdown / yaml were imported but the demo
  files don't use any of them.

Bundle impact: `/` First Load JS drops from 274 kB to 115 kB.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Jocs
Jocs merged commit 300c7b3 into develop May 29, 2026
12 checks passed
@Jocs
Jocs deleted the worktree-tender-hugging-horizon branch May 29, 2026 06:59
thimbleberrysystems pushed a commit to thimbleberrysystems/WordBird that referenced this pull request Jun 21, 2026
…ktext#4305)

Migrates packages/website from Vite + React 18 SPA to Next.js 15 App Router (React 19) with SSR on Cloudflare Workers via @opennextjs/cloudflare. Markdown demos are prerendered at build time (marked + prismjs + KaTeX) for SEO; mermaid is dynamic-imported. Adds .github/workflows/website-deploy.yml: develop push → production via wrangler deploy; PRs touching packages/website/** → preview upload + sticky comment with preview URL. Binds marktext.me as a Worker Custom Domain.

Full SEO surface: Metadata API (OG/Twitter), JSON-LD SoftwareApplication, sitemap + robots routes.

Bundle: / First Load JS 274 kB → 115 kB.

Co-Authored-By: Claude Opus 4.7 (1M context) <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.

2 participants