From 50e66eb4e34294c286b0c93f2ed819c3641793b2 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Tue, 10 Feb 2026 11:05:54 -0700 Subject: [PATCH 1/5] feat: migrate markdown rendering to RSC with rehype-react - Use rehype-react to render markdown directly to JSX on server - Stream RSC content to client via renderServerComponent() - Remove markdown processor from client bundle - Fix code block visibility (CSS was hiding dual-theme Shiki blocks) - Fix hydration errors (nested button in DropdownTrigger) - Add 'use client' directives to interactive components - Move MarkdownHeading type to separate types.ts file - Simplify MarkdownContent to only accept RSC content --- package.json | 39 +- pnpm-lock.yaml | 718 ++++++++++++++---- src/components/Breadcrumbs.tsx | 4 +- src/components/CodeExampleCard.tsx | 14 +- src/components/CodeExplorer.tsx | 73 +- src/components/Doc.tsx | 16 +- src/components/DocBreadcrumb.tsx | 2 +- src/components/FeedEntry.tsx | 6 +- src/components/FeedEntryTimeline.tsx | 6 +- src/components/SearchModal.tsx | 4 +- src/components/SimpleMarkdown.tsx | 33 +- src/components/ToastProvider.tsx | 2 + src/components/Toc.tsx | 2 +- src/components/TocMobile.tsx | 2 +- src/components/admin/FeedEntryEditor.tsx | 6 +- src/components/markdown/BlogContent.tsx | 18 + src/components/markdown/CodeBlock.tsx | 435 ++++++----- src/components/markdown/DocContent.tsx | 14 + src/components/markdown/FileTabs.tsx | 2 + src/components/markdown/FrameworkContent.tsx | 2 + src/components/markdown/Markdown.tsx | 25 +- src/components/markdown/MarkdownContent.tsx | 20 +- .../markdown/MarkdownFrameworkHandler.tsx | 32 +- .../markdown/MarkdownHeadingContext.tsx | 2 +- src/components/markdown/MarkdownLink.tsx | 2 + .../markdown/MarkdownTabsHandler.tsx | 56 +- src/components/markdown/MdComponents.tsx | 184 +++++ .../markdown/PackageManagerTabs.tsx | 10 +- src/components/markdown/Tabs.tsx | 2 + src/components/markdown/index.ts | 3 +- src/routeTree.gen.ts | 78 +- src/routes/$libraryId/$version.docs.$.tsx | 5 +- .../$version.docs.framework.$framework.$.tsx | 5 +- src/routes/blog.$.tsx | 107 +-- src/routes/blog.index.tsx | 110 +-- src/routes/feed.$id.tsx | 36 +- src/routes/index.tsx | 42 +- src/styles/app.css | 39 +- src/utils/{docs.ts => docs.tsx} | 17 +- src/utils/headers.server.ts | 22 + src/utils/markdown/index.ts | 10 +- src/utils/markdown/processor.ts | 75 -- src/utils/markdown/processor.tsx | 432 +++++++++++ src/utils/markdown/types.ts | 8 + src/utils/renderBlogContent.tsx | 15 + vite.config.ts | 67 +- 46 files changed, 1978 insertions(+), 824 deletions(-) create mode 100644 src/components/markdown/BlogContent.tsx create mode 100644 src/components/markdown/DocContent.tsx create mode 100644 src/components/markdown/MdComponents.tsx rename src/utils/{docs.ts => docs.tsx} (81%) create mode 100644 src/utils/headers.server.ts delete mode 100644 src/utils/markdown/processor.ts create mode 100644 src/utils/markdown/processor.tsx create mode 100644 src/utils/markdown/types.ts create mode 100644 src/utils/renderBlogContent.tsx diff --git a/package.json b/package.json index 8fb81cd1e..8262952a0 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@sentry/node": "^10.33.0", "@sentry/tanstackstart-react": "^10.32.1", "@sentry/vite-plugin": "^4.6.1", + "@shikijs/rehype": "^3.22.0", "@stackblitz/sdk": "^1.11.0", "@tailwindcss/typography": "^0.5.13", "@tailwindcss/vite": "^4.1.11", @@ -52,16 +53,17 @@ "@tanstack/pacer": "^0.16.4", "@tanstack/react-pacer": "^0.17.4", "@tanstack/react-query": "^5.90.12", - "@tanstack/react-router": "1.157.16", - "@tanstack/react-router-devtools": "1.157.16", - "@tanstack/react-router-ssr-query": "1.157.16", - "@tanstack/react-start": "1.157.16", + "@tanstack/react-router": "file:../start-rsc/packages/react-router", + "@tanstack/react-router-devtools": "file:../start-rsc/packages/react-router-devtools", + "@tanstack/react-router-ssr-query": "file:../start-rsc/packages/react-router-ssr-query", + "@tanstack/react-start": "file:../start-rsc/packages/react-start", "@tanstack/react-table": "^8.21.3", "@types/d3": "^7.4.3", "@uploadthing/react": "^7.3.3", "@visx/hierarchy": "^2.10.0", "@visx/responsive": "^2.10.0", "@vitejs/plugin-react": "^4.3.3", + "@vitejs/plugin-rsc": "^0.5.15", "@webcontainer/api": "^1.6.1", "@xstate/react": "^6.0.0", "algoliasearch": "^5.23.4", @@ -74,6 +76,7 @@ "eslint-plugin-jsx-a11y": "^6.10.2", "gray-matter": "^4.0.3", "hast-util-is-element": "^3.0.0", + "hast-util-to-html": "^9.0.5", "hast-util-to-string": "^3.0.1", "hono": "^4.11.3", "html-react-parser": "^5.1.10", @@ -93,6 +96,7 @@ "rehype-callouts": "^2.1.2", "rehype-parse": "^9.0.1", "rehype-raw": "^7.0.0", + "rehype-react": "^8.0.0", "rehype-slug": "^6.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", @@ -100,7 +104,7 @@ "remark-rehype": "^11.1.2", "remove-markdown": "^0.5.0", "resend": "^6.6.0", - "shiki": "^1.4.0", + "shiki": "^3.22.0", "tailwind-merge": "^1.14.0", "three": "^0.182.0", "troika-three-text": "^0.52.4", @@ -119,7 +123,7 @@ "@content-collections/vite": "^0.2.4", "@eslint/js": "^9.39.1", "@playwright/test": "^1.57.0", - "@shikijs/transformers": "^1.10.3", + "@shikijs/transformers": "^3.22.0", "@types/express": "^5.0.3", "@types/hast": "^3.0.4", "@types/node": "^24.3.0", @@ -158,7 +162,28 @@ "jws": ">=3.2.3", "qs": ">=6.14.1", "js-yaml": "^3.14.2", - "brace-expansion": ">=1.1.12" + "brace-expansion": ">=1.1.12", + "@tanstack/history": "file:../start-rsc/packages/history", + "@tanstack/router-core": "file:../start-rsc/packages/router-core", + "@tanstack/react-router": "file:../start-rsc/packages/react-router", + "@tanstack/react-router-devtools": "file:../start-rsc/packages/react-router-devtools", + "@tanstack/router-devtools-core": "file:../start-rsc/packages/router-devtools-core", + "@tanstack/router-ssr-query-core": "file:../start-rsc/packages/router-ssr-query-core", + "@tanstack/react-router-ssr-query": "file:../start-rsc/packages/react-router-ssr-query", + "@tanstack/react-start": "file:../start-rsc/packages/react-start", + "@tanstack/react-start-client": "file:../start-rsc/packages/react-start-client", + "@tanstack/react-start-server": "file:../start-rsc/packages/react-start-server", + "@tanstack/react-start-rsc": "file:../start-rsc/packages/react-start-rsc", + "@tanstack/start-plugin-core": "file:../start-rsc/packages/start-plugin-core", + "@tanstack/start-client-core": "file:../start-rsc/packages/start-client-core", + "@tanstack/start-server-core": "file:../start-rsc/packages/start-server-core", + "@tanstack/start-storage-context": "file:../start-rsc/packages/start-storage-context", + "@tanstack/start-fn-stubs": "file:../start-rsc/packages/start-fn-stubs", + "@tanstack/router-utils": "file:../start-rsc/packages/router-utils", + "@tanstack/router-generator": "file:../start-rsc/packages/router-generator", + "@tanstack/router-plugin": "file:../start-rsc/packages/router-plugin", + "@tanstack/virtual-file-routes": "file:../start-rsc/packages/virtual-file-routes", + "@tanstack/valibot-adapter": "file:../start-rsc/packages/valibot-adapter" } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 114304ceb..c433ac994 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,6 +12,27 @@ overrides: qs: '>=6.14.1' js-yaml: ^3.14.2 brace-expansion: '>=1.1.12' + '@tanstack/history': file:../start-rsc/packages/history + '@tanstack/router-core': file:../start-rsc/packages/router-core + '@tanstack/react-router': file:../start-rsc/packages/react-router + '@tanstack/react-router-devtools': file:../start-rsc/packages/react-router-devtools + '@tanstack/router-devtools-core': file:../start-rsc/packages/router-devtools-core + '@tanstack/router-ssr-query-core': file:../start-rsc/packages/router-ssr-query-core + '@tanstack/react-router-ssr-query': file:../start-rsc/packages/react-router-ssr-query + '@tanstack/react-start': file:../start-rsc/packages/react-start + '@tanstack/react-start-client': file:../start-rsc/packages/react-start-client + '@tanstack/react-start-server': file:../start-rsc/packages/react-start-server + '@tanstack/react-start-rsc': file:../start-rsc/packages/react-start-rsc + '@tanstack/start-plugin-core': file:../start-rsc/packages/start-plugin-core + '@tanstack/start-client-core': file:../start-rsc/packages/start-client-core + '@tanstack/start-server-core': file:../start-rsc/packages/start-server-core + '@tanstack/start-storage-context': file:../start-rsc/packages/start-storage-context + '@tanstack/start-fn-stubs': file:../start-rsc/packages/start-fn-stubs + '@tanstack/router-utils': file:../start-rsc/packages/router-utils + '@tanstack/router-generator': file:../start-rsc/packages/router-generator + '@tanstack/router-plugin': file:../start-rsc/packages/router-plugin + '@tanstack/virtual-file-routes': file:../start-rsc/packages/virtual-file-routes + '@tanstack/valibot-adapter': file:../start-rsc/packages/valibot-adapter importers: @@ -37,7 +58,7 @@ importers: version: 0.1.0 '@netlify/vite-plugin-tanstack-start': specifier: ^1.0.2 - version: 1.0.2(@tanstack/react-start@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)))(babel-plugin-macros@3.1.0)(rollup@4.53.3)(uploadthing@7.7.4(express@5.2.1)(h3@1.15.4)(tailwindcss@4.1.11))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) + version: 1.0.2(@tanstack/react-start@file:../start-rsc/packages/react-start(@vitejs/plugin-rsc@0.5.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)))(babel-plugin-macros@3.1.0)(rollup@4.53.3)(uploadthing@7.7.4(express@5.2.1)(h3@1.15.4)(tailwindcss@4.1.11))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) '@number-flow/react': specifier: ^0.4.1 version: 0.4.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -74,6 +95,9 @@ importers: '@sentry/vite-plugin': specifier: ^4.6.1 version: 4.6.1 + '@shikijs/rehype': + specifier: ^3.22.0 + version: 3.22.0 '@stackblitz/sdk': specifier: ^1.11.0 version: 1.11.0 @@ -96,17 +120,17 @@ importers: specifier: ^5.90.12 version: 5.90.12(react@19.2.3) '@tanstack/react-router': - specifier: 1.157.16 - version: 1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: file:../start-rsc/packages/react-router + version: file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@tanstack/react-router-devtools': - specifier: 1.157.16 - version: 1.157.16(@tanstack/react-router@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.157.16)(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: file:../start-rsc/packages/react-router-devtools + version: file:../start-rsc/packages/react-router-devtools(@tanstack/react-router@file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@tanstack/react-router-ssr-query': - specifier: 1.157.16 - version: 1.157.16(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@tanstack/react-router@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.157.16)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: file:../start-rsc/packages/react-router-ssr-query + version: file:../start-rsc/packages/react-router-ssr-query(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@tanstack/react-router@file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@tanstack/react-start': - specifier: 1.157.16 - version: 1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) + specifier: file:../start-rsc/packages/react-start + version: file:../start-rsc/packages/react-start(@vitejs/plugin-rsc@0.5.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) '@tanstack/react-table': specifier: ^8.21.3 version: 8.21.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -125,6 +149,9 @@ importers: '@vitejs/plugin-react': specifier: ^4.3.3 version: 4.3.4(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) + '@vitejs/plugin-rsc': + specifier: ^0.5.15 + version: 0.5.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) '@webcontainer/api': specifier: ^1.6.1 version: 1.6.1 @@ -161,6 +188,9 @@ importers: hast-util-is-element: specifier: ^3.0.0 version: 3.0.0 + hast-util-to-html: + specifier: ^9.0.5 + version: 9.0.5 hast-util-to-string: specifier: ^3.0.1 version: 3.0.1 @@ -218,6 +248,9 @@ importers: rehype-raw: specifier: ^7.0.0 version: 7.0.0 + rehype-react: + specifier: ^8.0.0 + version: 8.0.0 rehype-slug: specifier: ^6.0.0 version: 6.0.0 @@ -240,8 +273,8 @@ importers: specifier: ^6.6.0 version: 6.6.0 shiki: - specifier: ^1.4.0 - version: 1.10.3 + specifier: ^3.22.0 + version: 3.22.0 tailwind-merge: specifier: ^1.14.0 version: 1.14.0 @@ -292,8 +325,8 @@ importers: specifier: ^1.57.0 version: 1.57.0 '@shikijs/transformers': - specifier: ^1.10.3 - version: 1.10.3 + specifier: ^3.22.0 + version: 3.22.0 '@types/express': specifier: ^5.0.3 version: 5.0.6 @@ -1868,6 +1901,7 @@ packages: '@netlify/vite-plugin-tanstack-start@1.0.2': resolution: {integrity: sha512-2v21F6K28Wc7HGrGfASzs04o8xrDprHWt323+J7b1ToH7r0eC5IGkM1l3rPQlcom+TVkNvOejVlyEuqfJsr05g==} + version: 1.0.2 engines: {node: ^22.12.0} peerDependencies: '@tanstack/react-start': '>=1.132.0' @@ -2730,6 +2764,9 @@ packages: '@rolldown/pluginutils@1.0.0-beta.40': resolution: {integrity: sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w==} + '@rolldown/pluginutils@1.0.0-rc.2': + resolution: {integrity: sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==} + '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} @@ -3006,11 +3043,32 @@ packages: resolution: {integrity: sha512-Qvys1y3o8/bfL3ikrHnJS9zxdjt0z3POshdBl3967UcflrTqBmnGNkcVk53SlmtJWIfh85fgmrLvGYwZ2YiqNg==} engines: {node: '>= 14'} - '@shikijs/core@1.10.3': - resolution: {integrity: sha512-D45PMaBaeDHxww+EkcDQtDAtzv00Gcsp72ukBtaLSmqRvh0WgGMq3Al0rl1QQBZfuneO75NXMIzEZGFitThWbg==} + '@shikijs/core@3.22.0': + resolution: {integrity: sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA==} + + '@shikijs/engine-javascript@3.22.0': + resolution: {integrity: sha512-jdKhfgW9CRtj3Tor0L7+yPwdG3CgP7W+ZEqSsojrMzCjD1e0IxIbwUMDDpYlVBlC08TACg4puwFGkZfLS+56Tw==} + + '@shikijs/engine-oniguruma@3.22.0': + resolution: {integrity: sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA==} + + '@shikijs/langs@3.22.0': + resolution: {integrity: sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA==} + + '@shikijs/rehype@3.22.0': + resolution: {integrity: sha512-69b2VPc6XBy/VmAJlpBU5By+bJSBdE2nvgRCZXav7zujbrjXuT0F60DIrjKuutjPqNufuizE+E8tIZr2Yn8Z+g==} - '@shikijs/transformers@1.10.3': - resolution: {integrity: sha512-MNjsyye2WHVdxfZUSr5frS97sLGe6G1T+1P41QjyBFJehZphMcr4aBlRLmq6OSPBslYe9byQPVvt/LJCOfxw8Q==} + '@shikijs/themes@3.22.0': + resolution: {integrity: sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g==} + + '@shikijs/transformers@3.22.0': + resolution: {integrity: sha512-E7eRV7mwDBjueLF6852n2oYeJYxBq3NSsDk+uyruYAXONv4U8holGmIrT+mPRJQ1J1SNOH6L8G19KRzmBawrFw==} + + '@shikijs/types@3.22.0': + resolution: {integrity: sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} '@sindresorhus/merge-streams@4.0.0': resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} @@ -3133,8 +3191,8 @@ packages: resolution: {integrity: sha512-RL1f5ZlfZMpghrCIdzl6mLOFLTuhqmPNblZgBaeKfdtk5rfbjykurv+VfYydOFXj0vxVIoA2d/zT7xfD7Ph8fw==} engines: {node: '>=18'} - '@tanstack/history@1.154.14': - resolution: {integrity: sha512-xyIfof8eHBuub1CkBnbKNKQXeRZC4dClhmzePHVOEel4G7lk/dW+TQ16da7CFdeNLv6u6Owf5VoBQxoo6DFTSA==} + '@tanstack/history@file:../start-rsc/packages/history': + resolution: {directory: ../start-rsc/packages/history, type: directory} engines: {node: '>=12'} '@tanstack/pacer@0.16.4': @@ -3156,20 +3214,20 @@ packages: peerDependencies: react: ^18 || ^19 - '@tanstack/react-router-devtools@1.157.16': - resolution: {integrity: sha512-g6ekyzumfLBX6T5e+Vu2r37Z2CFJKrWRFqIy3vZ6A3x7OcuPV8uXNjyrLSiT/IsGTiF8YzwI4nWJa4fyd7NlCw==} + '@tanstack/react-router-devtools@file:../start-rsc/packages/react-router-devtools': + resolution: {directory: ../start-rsc/packages/react-router-devtools, type: directory} engines: {node: '>=12'} peerDependencies: - '@tanstack/react-router': ^1.157.16 - '@tanstack/router-core': ^1.157.16 + '@tanstack/react-router': workspace:^ + '@tanstack/router-core': workspace:^ react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' peerDependenciesMeta: '@tanstack/router-core': optional: true - '@tanstack/react-router-ssr-query@1.157.16': - resolution: {integrity: sha512-emvm1t2fTZk/gdctuTwbNW2LeUCpPJGttq4N9I5YdTk2QmLmCD5mgiJYB/GXWwmuSq05dmO/7W9b8HNAWSv0FQ==} + '@tanstack/react-router-ssr-query@file:../start-rsc/packages/react-router-ssr-query': + resolution: {directory: ../start-rsc/packages/react-router-ssr-query, type: directory} engines: {node: '>=12'} peerDependencies: '@tanstack/query-core': '>=5.90.0' @@ -3178,34 +3236,49 @@ packages: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-router@1.157.16': - resolution: {integrity: sha512-xwFQa7S7dhBhm3aJYwU79cITEYgAKSrcL6wokaROIvl2JyIeazn8jueWqUPJzFjv+QF6Q8euKRlKUEyb5q2ymg==} + '@tanstack/react-router@file:../start-rsc/packages/react-router': + resolution: {directory: ../start-rsc/packages/react-router, type: directory} engines: {node: '>=12'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start-client@1.157.16': - resolution: {integrity: sha512-r3XTxYPJXZ/szhbloxqT6CQtsoEjw8DjbnZh/3ZsQv2PLKTOl925cy7YVdQc2cWZyXtn5e19Ig78R+8tsoTpig==} + '@tanstack/react-start-client@file:../start-rsc/packages/react-start-client': + resolution: {directory: ../start-rsc/packages/react-start-client, type: directory} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start-rsc@file:../start-rsc/packages/react-start-rsc': + resolution: {directory: ../start-rsc/packages/react-start-rsc, type: directory} engines: {node: '>=22.12.0'} peerDependencies: + '@vitejs/plugin-rsc': '>=0.5.15' react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' + peerDependenciesMeta: + '@vitejs/plugin-rsc': + optional: true - '@tanstack/react-start-server@1.157.16': - resolution: {integrity: sha512-1YkBss4SUQ+HqVC1yGN/j7VNwjvdHHd3K58fASe0bz+uf7GrkGJlRXPkMJdxJkkmefYHQfyBL+q7o723N4CMYA==} + '@tanstack/react-start-server@file:../start-rsc/packages/react-start-server': + resolution: {directory: ../start-rsc/packages/react-start-server, type: directory} engines: {node: '>=22.12.0'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start@1.157.16': - resolution: {integrity: sha512-FO6UYjsZyNaC0ickSSvClqfVZemp9/HWnbRJQU2dOKYQsI+wnznhLp9IkgG90iFBLcuMAWhcNHMiIuz603GJBg==} + '@tanstack/react-start@file:../start-rsc/packages/react-start': + resolution: {directory: ../start-rsc/packages/react-start, type: directory} engines: {node: '>=22.12.0'} peerDependencies: + '@vitejs/plugin-rsc': '*' react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' vite: '>=7.0.0' + peerDependenciesMeta: + '@vitejs/plugin-rsc': + optional: true '@tanstack/react-store@0.8.0': resolution: {integrity: sha512-1vG9beLIuB7q69skxK9r5xiLN3ztzIPfSQSs0GfeqWGO2tGIyInZx0x1COhpx97RKaONSoAb8C3dxacWksm1ow==} @@ -3220,30 +3293,30 @@ packages: react: '>=16.8' react-dom: '>=16.8' - '@tanstack/router-core@1.157.16': - resolution: {integrity: sha512-eJuVgM7KZYTTr4uPorbUzUflmljMVcaX2g6VvhITLnHmg9SBx9RAgtQ1HmT+72mzyIbRSlQ1q0fY/m+of/fosA==} + '@tanstack/router-core@file:../start-rsc/packages/router-core': + resolution: {directory: ../start-rsc/packages/router-core, type: directory} engines: {node: '>=12'} - '@tanstack/router-devtools-core@1.157.16': - resolution: {integrity: sha512-XBJTs/kMZYK6J2zhbGucHNuypwDB1t2vi8K5To+V6dUnLGBEyfQTf01fegiF4rpL1yXgomdGnP6aTiOFgldbVg==} + '@tanstack/router-devtools-core@file:../start-rsc/packages/router-devtools-core': + resolution: {directory: ../start-rsc/packages/router-devtools-core, type: directory} engines: {node: '>=12'} peerDependencies: - '@tanstack/router-core': ^1.157.16 + '@tanstack/router-core': workspace:^ csstype: ^3.0.10 peerDependenciesMeta: csstype: optional: true - '@tanstack/router-generator@1.157.16': - resolution: {integrity: sha512-Ae2M00VTFjjED7glSCi/mMLENRzhEym6NgjoOx7UVNbCC/rLU/5ASDe5VIlDa8QLEqP5Pj088Gi51gjmRuICvQ==} + '@tanstack/router-generator@file:../start-rsc/packages/router-generator': + resolution: {directory: ../start-rsc/packages/router-generator, type: directory} engines: {node: '>=12'} - '@tanstack/router-plugin@1.157.16': - resolution: {integrity: sha512-YQg7L06xyCJAYyrEJNZGAnDL8oChILU+G/eSDIwEfcWn5iLk+47x1Gcdxr82++47PWmOPhzuTo8edDQXWs7kAA==} + '@tanstack/router-plugin@file:../start-rsc/packages/router-plugin': + resolution: {directory: ../start-rsc/packages/router-plugin, type: directory} engines: {node: '>=12'} peerDependencies: '@rsbuild/core': '>=1.0.2' - '@tanstack/react-router': ^1.157.16 + '@tanstack/react-router': workspace:^ vite: '>=5.0.0 || >=6.0.0 || >=7.0.0' vite-plugin-solid: ^2.11.10 webpack: '>=5.92.0' @@ -3259,37 +3332,37 @@ packages: webpack: optional: true - '@tanstack/router-ssr-query-core@1.157.16': - resolution: {integrity: sha512-YuwNG4jdtn+r90yyti8yP27IKaVoflWmRezqnj0gyJxpRauBkK7MVLvWSNbJadnk88b9H+rdtNOF2k3owGaong==} + '@tanstack/router-ssr-query-core@file:../start-rsc/packages/router-ssr-query-core': + resolution: {directory: ../start-rsc/packages/router-ssr-query-core, type: directory} engines: {node: '>=12'} peerDependencies: '@tanstack/query-core': '>=5.90.0' '@tanstack/router-core': '>=1.127.0' - '@tanstack/router-utils@1.154.7': - resolution: {integrity: sha512-61bGx32tMKuEpVRseu2sh1KQe8CfB7793Mch/kyQt0EP3tD7X0sXmimCl3truRiDGUtI0CaSoQV1NPjAII1RBA==} + '@tanstack/router-utils@file:../start-rsc/packages/router-utils': + resolution: {directory: ../start-rsc/packages/router-utils, type: directory} engines: {node: '>=12'} - '@tanstack/start-client-core@1.157.16': - resolution: {integrity: sha512-O+7H133MWQTkOxmXJNhrLXiOhDcBlxvpEcCd/N25Ga6eyZ7/P5vvFzNkSSxeQNkZV+RiPWnA5B75gT+U+buz3w==} + '@tanstack/start-client-core@file:../start-rsc/packages/start-client-core': + resolution: {directory: ../start-rsc/packages/start-client-core, type: directory} engines: {node: '>=22.12.0'} - '@tanstack/start-fn-stubs@1.154.7': - resolution: {integrity: sha512-D69B78L6pcFN5X5PHaydv7CScQcKLzJeEYqs7jpuyyqGQHSUIZUjS955j+Sir8cHhuDIovCe2LmsYHeZfWf3dQ==} + '@tanstack/start-fn-stubs@file:../start-rsc/packages/start-fn-stubs': + resolution: {directory: ../start-rsc/packages/start-fn-stubs, type: directory} engines: {node: '>=22.12.0'} - '@tanstack/start-plugin-core@1.157.16': - resolution: {integrity: sha512-VmRXuvP5flryUAHeBM4Xb06n544qLtyA2cwmlQLRTUYtQiQEAdd9CvCGy8CPAly3f7eeXKqC7aX0v3MwWkLR8w==} + '@tanstack/start-plugin-core@file:../start-rsc/packages/start-plugin-core': + resolution: {directory: ../start-rsc/packages/start-plugin-core, type: directory} engines: {node: '>=22.12.0'} peerDependencies: vite: '>=7.0.0' - '@tanstack/start-server-core@1.157.16': - resolution: {integrity: sha512-PEltFleYfiqz6+KcmzNXxc1lXgT7VDNKP6G6i1TirdHBDbRJ9CIY+ASLPlhrRwqwA2PL9PpFjXZl8u5bH/+Q9A==} + '@tanstack/start-server-core@file:../start-rsc/packages/start-server-core': + resolution: {directory: ../start-rsc/packages/start-server-core, type: directory} engines: {node: '>=22.12.0'} - '@tanstack/start-storage-context@1.157.16': - resolution: {integrity: sha512-56izE0oihAw2YRwYUEds2H+uO5dyT2CahXCgWX62+l+FHou09M9mSep68n1lBKPdphC2ZU3cPV7wnvgeraJWHg==} + '@tanstack/start-storage-context@file:../start-rsc/packages/start-storage-context': + resolution: {directory: ../start-rsc/packages/start-storage-context, type: directory} engines: {node: '>=22.12.0'} '@tanstack/store@0.8.0': @@ -3299,8 +3372,8 @@ packages: resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} engines: {node: '>=12'} - '@tanstack/virtual-file-routes@1.154.7': - resolution: {integrity: sha512-cHHDnewHozgjpI+MIVp9tcib6lYEQK5MyUr0ChHpHFGBl8Xei55rohFK0I0ve/GKoHeioaK42Smd8OixPp6CTg==} + '@tanstack/virtual-file-routes@file:../start-rsc/packages/virtual-file-routes': + resolution: {directory: ../start-rsc/packages/virtual-file-routes, type: directory} engines: {node: '>=12'} '@tweenjs/tween.js@23.1.3': @@ -3429,6 +3502,9 @@ packages: '@types/draco3d@1.4.10': resolution: {integrity: sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==} + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -3538,6 +3614,9 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -3659,6 +3738,17 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + '@vitejs/plugin-rsc@0.5.19': + resolution: {integrity: sha512-YuRKVEOYQFq4OdLKIoGpLKL0y0fyhWjjEDVHEIvPsXGk+jQ+uVbuM6hzVseb6N95x8cbdDGUe3m+qNU1dPldrg==} + peerDependencies: + react: '*' + react-dom: '*' + react-server-dom-webpack: '*' + vite: '*' + peerDependenciesMeta: + react-server-dom-webpack: + optional: true + '@vue/compiler-core@3.5.22': resolution: {integrity: sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==} @@ -4055,6 +4145,9 @@ packages: character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} @@ -4857,6 +4950,9 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -4996,9 +5092,15 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -5356,8 +5458,8 @@ packages: h3@1.15.4: resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==} - h3@2.0.1-rc.11: - resolution: {integrity: sha512-2myzjCqy32c1As9TjZW9fNZXtLqNedjFSrdFy2AjFBQQ3LzrnGoDdFDYfC0tV2e4vcyfJ2Sfo/F6NQhO2Ly/Mw==} + h3@2.0.1-rc.7: + resolution: {integrity: sha512-qbrRu1OLXmUYnysWOCVrYhtC/m8ZuXu/zCbo3U/KyphJxbPFiC76jHYwVrmEcss9uNAHO5BoUguQ46yEpgI2PA==} engines: {node: '>=20.11.1'} peerDependencies: crossws: ^0.4.1 @@ -5423,6 +5525,9 @@ packages: hast-util-to-html@9.0.5: resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + hast-util-to-parse5@8.0.0: resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} @@ -5620,6 +5725,12 @@ packages: iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-array-buffer@3.0.4: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} @@ -5674,6 +5785,9 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} @@ -5708,6 +5822,9 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} @@ -5759,6 +5876,9 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -5909,6 +6029,9 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + js-yaml@3.14.2: resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} hasBin: true @@ -6200,6 +6323,9 @@ packages: magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@0.30.8: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} @@ -6247,6 +6373,15 @@ packages: mdast-util-gfm@3.1.0: resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + mdast-util-phrasing@4.1.0: resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} @@ -6647,6 +6782,12 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.4: + resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==} + open@7.4.2: resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} engines: {node: '>=8'} @@ -6712,6 +6853,9 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-gitignore@2.0.0: resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==} engines: {node: '>=14'} @@ -6807,6 +6951,9 @@ packages: pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + periscopic@4.0.2: + resolution: {integrity: sha512-sqpQDUy8vgB7ycLkendSKS6HnVz1Rneoc3Rc+ZBUCe2pbqlVuCC5vF52l0NJ1aiMg/r1qfYF9/myz8CZeI2rjA==} + pg-int8@1.0.1: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} engines: {node: '>=4.0.0'} @@ -7081,6 +7228,9 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@19.2.4: + resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==} + react-property@2.0.2: resolution: {integrity: sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==} @@ -7176,6 +7326,15 @@ packages: regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + regexp.prototype.flags@1.5.2: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} @@ -7197,6 +7356,9 @@ packages: rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + rehype-react@8.0.0: + resolution: {integrity: sha512-vzo0YxYbB2HE+36+9HWXVdxNoNDubx63r5LBzpxBGVWM8s9mdnMdbmuJBAX6TTyuGdZjZix6qU3GcSuKCIWivw==} + rehype-slug@6.0.0: resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==} @@ -7448,8 +7610,8 @@ packages: shell-quote@1.8.1: resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - shiki@1.10.3: - resolution: {integrity: sha512-eneCLncGuvPdTutJuLyUGS8QNPAVFO5Trvld2wgEq1e002mwctAhJKeMGWtWVXOIEzmlcLRqcgPSorR6AVzOmQ==} + shiki@3.22.0: + resolution: {integrity: sha512-LBnhsoYEe0Eou4e1VgJACes+O6S6QC0w71fCSp5Oya79inkwkm15gQ1UF6VtQ8j/taMDh79hAB49WUk8ALQW3g==} side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} @@ -7628,6 +7790,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + style-to-js@1.1.12: resolution: {integrity: sha512-tv+/FkgNYHI2fvCoBMsqPHh5xovwiw+C3X0Gfnss/Syau0Nr3IqGOJ9XiOYXoPnToHVbllKFf5qCNFJGwFg5mg==} @@ -7826,6 +7991,9 @@ packages: tunnel-rat@0.1.2: resolution: {integrity: sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==} + turbo-stream@3.1.0: + resolution: {integrity: sha512-tVI25WEXl4fckNEmrq70xU1XumxUwEx/FZD5AgEcV8ri7Wvrg2o7GEq8U7htrNx3CajciGm+kDyhRf5JB6t7/A==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -7935,6 +8103,9 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + universal-user-agent@6.0.0: resolution: {integrity: sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==} @@ -8383,6 +8554,9 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + zip-stream@6.0.1: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} @@ -8557,7 +8731,7 @@ snapshots: '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 @@ -9883,12 +10057,12 @@ snapshots: '@netlify/types@2.2.0': {} - '@netlify/vite-plugin-tanstack-start@1.0.2(@tanstack/react-start@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)))(babel-plugin-macros@3.1.0)(rollup@4.53.3)(uploadthing@7.7.4(express@5.2.1)(h3@1.15.4)(tailwindcss@4.1.11))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1))': + '@netlify/vite-plugin-tanstack-start@1.0.2(@tanstack/react-start@file:../start-rsc/packages/react-start(@vitejs/plugin-rsc@0.5.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)))(babel-plugin-macros@3.1.0)(rollup@4.53.3)(uploadthing@7.7.4(express@5.2.1)(h3@1.15.4)(tailwindcss@4.1.11))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1))': dependencies: '@netlify/vite-plugin': 2.6.1(babel-plugin-macros@3.1.0)(rollup@4.53.3)(uploadthing@7.7.4(express@5.2.1)(h3@1.15.4)(tailwindcss@4.1.11))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) vite: 7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1) optionalDependencies: - '@tanstack/react-start': 1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) + '@tanstack/react-start': file:../start-rsc/packages/react-start(@vitejs/plugin-rsc@0.5.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -9947,7 +10121,7 @@ snapshots: '@netlify/zip-it-and-ship-it@14.1.8(rollup@4.53.3)': dependencies: - '@babel/parser': 7.28.4 + '@babel/parser': 7.29.0 '@babel/types': 7.28.4 '@netlify/binary-info': 1.0.0 '@netlify/serverless-functions-api': 2.6.0 @@ -10853,6 +11027,8 @@ snapshots: '@rolldown/pluginutils@1.0.0-beta.40': {} + '@rolldown/pluginutils@1.0.0-rc.2': {} + '@rollup/pluginutils@5.3.0(rollup@4.53.3)': dependencies: '@types/estree': 1.0.8 @@ -11176,13 +11352,52 @@ snapshots: - encoding - supports-color - '@shikijs/core@1.10.3': + '@shikijs/core@3.22.0': dependencies: + '@shikijs/types': 3.22.0 + '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.22.0': + dependencies: + '@shikijs/types': 3.22.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.4 - '@shikijs/transformers@1.10.3': + '@shikijs/engine-oniguruma@3.22.0': dependencies: - shiki: 1.10.3 + '@shikijs/types': 3.22.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.22.0': + dependencies: + '@shikijs/types': 3.22.0 + + '@shikijs/rehype@3.22.0': + dependencies: + '@shikijs/types': 3.22.0 + '@types/hast': 3.0.4 + hast-util-to-string: 3.0.1 + shiki: 3.22.0 + unified: 11.0.5 + unist-util-visit: 5.1.0 + + '@shikijs/themes@3.22.0': + dependencies: + '@shikijs/types': 3.22.0 + + '@shikijs/transformers@3.22.0': + dependencies: + '@shikijs/core': 3.22.0 + '@shikijs/types': 3.22.0 + + '@shikijs/types@3.22.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} '@sindresorhus/merge-streams@4.0.0': {} @@ -11293,7 +11508,7 @@ snapshots: '@tanstack/devtools-event-client@0.3.5': {} - '@tanstack/history@1.154.14': {} + '@tanstack/history@file:../start-rsc/packages/history': {} '@tanstack/pacer@0.16.4': dependencies: @@ -11314,74 +11529,100 @@ snapshots: '@tanstack/query-core': 5.90.12 react: 19.2.3 - '@tanstack/react-router-devtools@1.157.16(@tanstack/react-router@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.157.16)(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@tanstack/react-router-devtools@file:../start-rsc/packages/react-router-devtools(@tanstack/react-router@file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@tanstack/react-router': 1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@tanstack/router-devtools-core': 1.157.16(@tanstack/router-core@1.157.16)(csstype@3.2.3) + '@tanstack/react-router': file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/router-devtools-core': file:../start-rsc/packages/router-devtools-core(csstype@3.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@tanstack/router-core': 1.157.16 transitivePeerDependencies: - csstype - '@tanstack/react-router-ssr-query@1.157.16(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@tanstack/react-router@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.157.16)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@tanstack/react-router-ssr-query@file:../start-rsc/packages/react-router-ssr-query(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(@tanstack/react-router@file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@tanstack/query-core': 5.90.12 '@tanstack/react-query': 5.90.12(react@19.2.3) - '@tanstack/react-router': 1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@tanstack/router-ssr-query-core': 1.157.16(@tanstack/query-core@5.90.12)(@tanstack/router-core@1.157.16) + '@tanstack/react-router': file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/router-ssr-query-core': file:../start-rsc/packages/router-ssr-query-core(@tanstack/query-core@5.90.12) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) transitivePeerDependencies: - '@tanstack/router-core' - '@tanstack/react-router@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@tanstack/react-router@file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@tanstack/history': 1.154.14 + '@tanstack/history': file:../start-rsc/packages/history '@tanstack/react-store': 0.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@tanstack/router-core': 1.157.16 + '@tanstack/router-core': file:../start-rsc/packages/router-core isbot: 5.1.31 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/react-start-client@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@tanstack/react-start-client@file:../start-rsc/packages/react-start-client(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@tanstack/react-router': 1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@tanstack/router-core': 1.157.16 - '@tanstack/start-client-core': 1.157.16 + '@tanstack/react-router': file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/router-core': file:../start-rsc/packages/router-core + '@tanstack/start-client-core': file:../start-rsc/packages/start-client-core react: 19.2.3 react-dom: 19.2.3(react@19.2.3) tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/react-start-server@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@tanstack/react-start-rsc@file:../start-rsc/packages/react-start-rsc(@vitejs/plugin-rsc@0.5.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1))': + dependencies: + '@tanstack/react-router': file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/react-start-server': file:../start-rsc/packages/react-start-server(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/router-core': file:../start-rsc/packages/router-core + '@tanstack/router-utils': file:../start-rsc/packages/router-utils + '@tanstack/start-client-core': file:../start-rsc/packages/start-client-core + '@tanstack/start-fn-stubs': file:../start-rsc/packages/start-fn-stubs + '@tanstack/start-plugin-core': file:../start-rsc/packages/start-plugin-core(@tanstack/react-router@file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) + '@tanstack/start-server-core': file:../start-rsc/packages/start-server-core + '@tanstack/start-storage-context': file:../start-rsc/packages/start-storage-context + pathe: 2.0.3 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-is: 19.2.4 + optionalDependencies: + '@vitejs/plugin-rsc': 0.5.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) + transitivePeerDependencies: + - '@rsbuild/core' + - crossws + - supports-color + - vite + - vite-plugin-solid + - webpack + + '@tanstack/react-start-server@file:../start-rsc/packages/react-start-server(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@tanstack/history': 1.154.14 - '@tanstack/react-router': 1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@tanstack/router-core': 1.157.16 - '@tanstack/start-client-core': 1.157.16 - '@tanstack/start-server-core': 1.157.16 + '@tanstack/history': file:../start-rsc/packages/history + '@tanstack/react-router': file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/router-core': file:../start-rsc/packages/router-core + '@tanstack/start-client-core': file:../start-rsc/packages/start-client-core + '@tanstack/start-server-core': file:../start-rsc/packages/start-server-core react: 19.2.3 react-dom: 19.2.3(react@19.2.3) transitivePeerDependencies: - crossws - '@tanstack/react-start@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1))': + '@tanstack/react-start@file:../start-rsc/packages/react-start(@vitejs/plugin-rsc@0.5.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1))': dependencies: - '@tanstack/react-router': 1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@tanstack/react-start-client': 1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@tanstack/react-start-server': 1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@tanstack/router-utils': 1.154.7 - '@tanstack/start-client-core': 1.157.16 - '@tanstack/start-plugin-core': 1.157.16(@tanstack/react-router@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) - '@tanstack/start-server-core': 1.157.16 + '@tanstack/react-router': file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/react-start-client': file:../start-rsc/packages/react-start-client(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/react-start-rsc': file:../start-rsc/packages/react-start-rsc(@vitejs/plugin-rsc@0.5.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) + '@tanstack/react-start-server': file:../start-rsc/packages/react-start-server(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/router-utils': file:../start-rsc/packages/router-utils + '@tanstack/start-client-core': file:../start-rsc/packages/start-client-core + '@tanstack/start-plugin-core': file:../start-rsc/packages/start-plugin-core(@tanstack/react-router@file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) + '@tanstack/start-server-core': file:../start-rsc/packages/start-server-core pathe: 2.0.3 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) vite: 7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1) + optionalDependencies: + '@vitejs/plugin-rsc': 0.5.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) transitivePeerDependencies: - '@rsbuild/core' - crossws @@ -11402,9 +11643,9 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - '@tanstack/router-core@1.157.16': + '@tanstack/router-core@file:../start-rsc/packages/router-core': dependencies: - '@tanstack/history': 1.154.14 + '@tanstack/history': file:../start-rsc/packages/history '@tanstack/store': 0.8.0 cookie-es: 2.0.0 seroval: 1.5.0 @@ -11412,20 +11653,19 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/router-devtools-core@1.157.16(@tanstack/router-core@1.157.16)(csstype@3.2.3)': + '@tanstack/router-devtools-core@file:../start-rsc/packages/router-devtools-core(csstype@3.2.3)': dependencies: - '@tanstack/router-core': 1.157.16 clsx: 2.1.1 goober: 2.1.16(csstype@3.2.3) tiny-invariant: 1.3.3 optionalDependencies: csstype: 3.2.3 - '@tanstack/router-generator@1.157.16': + '@tanstack/router-generator@file:../start-rsc/packages/router-generator': dependencies: - '@tanstack/router-core': 1.157.16 - '@tanstack/router-utils': 1.154.7 - '@tanstack/virtual-file-routes': 1.154.7 + '@tanstack/router-core': file:../start-rsc/packages/router-core + '@tanstack/router-utils': file:../start-rsc/packages/router-utils + '@tanstack/virtual-file-routes': file:../start-rsc/packages/virtual-file-routes prettier: 3.7.4 recast: 0.23.11 source-map: 0.7.6 @@ -11434,34 +11674,33 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.157.16(@tanstack/react-router@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1))': + '@tanstack/router-plugin@file:../start-rsc/packages/router-plugin(@tanstack/react-router@file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.29.0) '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.29.0) - '@babel/template': 7.27.2 + '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 - '@babel/types': 7.28.5 - '@tanstack/router-core': 1.157.16 - '@tanstack/router-generator': 1.157.16 - '@tanstack/router-utils': 1.154.7 - '@tanstack/virtual-file-routes': 1.154.7 + '@babel/types': 7.29.0 + '@tanstack/router-core': file:../start-rsc/packages/router-core + '@tanstack/router-generator': file:../start-rsc/packages/router-generator + '@tanstack/router-utils': file:../start-rsc/packages/router-utils + '@tanstack/virtual-file-routes': file:../start-rsc/packages/virtual-file-routes babel-dead-code-elimination: 1.0.12 chokidar: 3.6.0 unplugin: 2.3.10 zod: 3.25.76 optionalDependencies: - '@tanstack/react-router': 1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/react-router': file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3) vite: 7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1) transitivePeerDependencies: - supports-color - '@tanstack/router-ssr-query-core@1.157.16(@tanstack/query-core@5.90.12)(@tanstack/router-core@1.157.16)': + '@tanstack/router-ssr-query-core@file:../start-rsc/packages/router-ssr-query-core(@tanstack/query-core@5.90.12)': dependencies: '@tanstack/query-core': 5.90.12 - '@tanstack/router-core': 1.157.16 - '@tanstack/router-utils@1.154.7': + '@tanstack/router-utils@file:../start-rsc/packages/router-utils': dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.0 @@ -11473,29 +11712,29 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/start-client-core@1.157.16': + '@tanstack/start-client-core@file:../start-rsc/packages/start-client-core': dependencies: - '@tanstack/router-core': 1.157.16 - '@tanstack/start-fn-stubs': 1.154.7 - '@tanstack/start-storage-context': 1.157.16 + '@tanstack/router-core': file:../start-rsc/packages/router-core + '@tanstack/start-fn-stubs': file:../start-rsc/packages/start-fn-stubs + '@tanstack/start-storage-context': file:../start-rsc/packages/start-storage-context seroval: 1.5.0 tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/start-fn-stubs@1.154.7': {} + '@tanstack/start-fn-stubs@file:../start-rsc/packages/start-fn-stubs': {} - '@tanstack/start-plugin-core@1.157.16(@tanstack/react-router@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1))': + '@tanstack/start-plugin-core@file:../start-rsc/packages/start-plugin-core(@tanstack/react-router@file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1))': dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.29.0 - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 '@rolldown/pluginutils': 1.0.0-beta.40 - '@tanstack/router-core': 1.157.16 - '@tanstack/router-generator': 1.157.16 - '@tanstack/router-plugin': 1.157.16(@tanstack/react-router@1.157.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) - '@tanstack/router-utils': 1.154.7 - '@tanstack/start-client-core': 1.157.16 - '@tanstack/start-server-core': 1.157.16 + '@tanstack/router-core': file:../start-rsc/packages/router-core + '@tanstack/router-generator': file:../start-rsc/packages/router-generator + '@tanstack/router-plugin': file:../start-rsc/packages/router-plugin(@tanstack/react-router@file:../start-rsc/packages/react-router(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) + '@tanstack/router-utils': file:../start-rsc/packages/router-utils + '@tanstack/start-client-core': file:../start-rsc/packages/start-client-core + '@tanstack/start-server-core': file:../start-rsc/packages/start-server-core babel-dead-code-elimination: 1.0.12 cheerio: 1.1.2 exsolve: 1.0.7 @@ -11515,27 +11754,27 @@ snapshots: - vite-plugin-solid - webpack - '@tanstack/start-server-core@1.157.16': + '@tanstack/start-server-core@file:../start-rsc/packages/start-server-core': dependencies: - '@tanstack/history': 1.154.14 - '@tanstack/router-core': 1.157.16 - '@tanstack/start-client-core': 1.157.16 - '@tanstack/start-storage-context': 1.157.16 - h3-v2: h3@2.0.1-rc.11 + '@tanstack/history': file:../start-rsc/packages/history + '@tanstack/router-core': file:../start-rsc/packages/router-core + '@tanstack/start-client-core': file:../start-rsc/packages/start-client-core + '@tanstack/start-storage-context': file:../start-rsc/packages/start-storage-context + h3-v2: h3@2.0.1-rc.7 seroval: 1.5.0 tiny-invariant: 1.3.3 transitivePeerDependencies: - crossws - '@tanstack/start-storage-context@1.157.16': + '@tanstack/start-storage-context@file:../start-rsc/packages/start-storage-context': dependencies: - '@tanstack/router-core': 1.157.16 + '@tanstack/router-core': file:../start-rsc/packages/router-core '@tanstack/store@0.8.0': {} '@tanstack/table-core@8.21.3': {} - '@tanstack/virtual-file-routes@1.154.7': {} + '@tanstack/virtual-file-routes@file:../start-rsc/packages/virtual-file-routes': {} '@tweenjs/tween.js@23.1.3': {} @@ -11696,6 +11935,10 @@ snapshots: '@types/draco3d@1.4.10': {} + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + '@types/estree@1.0.8': {} '@types/express-serve-static-core@5.1.1': @@ -11818,6 +12061,8 @@ snapshots: '@types/trusted-types@2.0.7': optional: true + '@types/unist@2.0.11': {} + '@types/unist@3.0.3': {} '@types/webxr@0.5.24': {} @@ -11999,9 +12244,24 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitejs/plugin-rsc@0.5.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1))': + dependencies: + '@rolldown/pluginutils': 1.0.0-rc.2 + es-module-lexer: 2.0.0 + estree-walker: 3.0.3 + magic-string: 0.30.21 + periscopic: 4.0.2 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + srvx: 0.10.1 + strip-literal: 3.1.0 + turbo-stream: 3.1.0 + vite: 7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1) + vitefu: 1.1.1(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1)) + '@vue/compiler-core@3.5.22': dependencies: - '@babel/parser': 7.28.4 + '@babel/parser': 7.29.0 '@vue/shared': 3.5.22 entities: 4.5.0 estree-walker: 2.0.2 @@ -12014,7 +12274,7 @@ snapshots: '@vue/compiler-sfc@3.5.22': dependencies: - '@babel/parser': 7.28.4 + '@babel/parser': 7.29.0 '@vue/compiler-core': 3.5.22 '@vue/compiler-dom': 3.5.22 '@vue/compiler-ssr': 3.5.22 @@ -12309,9 +12569,9 @@ snapshots: babel-dead-code-elimination@1.0.12: dependencies: '@babel/core': 7.29.0 - '@babel/parser': 7.28.4 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.5 + '@babel/parser': 7.29.0 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color @@ -12468,6 +12728,8 @@ snapshots: character-entities@2.0.2: {} + character-reference-invalid@2.0.1: {} + cheerio-select@2.1.0: dependencies: boolbase: 1.0.0 @@ -13326,6 +13588,8 @@ snapshots: es-module-lexer@1.7.0: {} + es-module-lexer@2.0.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -13620,8 +13884,14 @@ snapshots: estraverse@5.3.0: {} + estree-util-is-identifier-name@3.0.0: {} + estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + esutils@2.0.3: {} etag@1.8.1: {} @@ -14024,7 +14294,7 @@ snapshots: ufo: 1.6.1 uncrypto: 0.1.3 - h3@2.0.1-rc.11: + h3@2.0.1-rc.7: dependencies: rou3: 0.7.12 srvx: 0.10.1 @@ -14119,6 +14389,26 @@ snapshots: stringify-entities: 4.0.4 zwitch: 2.0.4 + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.12 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + hast-util-to-parse5@8.0.0: dependencies: '@types/hast': 3.0.4 @@ -14370,6 +14660,13 @@ snapshots: iron-webcrypto@1.2.1: {} + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 @@ -14430,6 +14727,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-decimal@2.0.1: {} + is-docker@2.2.1: {} is-docker@3.0.0: {} @@ -14452,6 +14751,8 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-hexadecimal@2.0.1: {} + is-inside-container@1.0.0: dependencies: is-docker: 3.0.0 @@ -14485,6 +14786,10 @@ snapshots: is-promise@4.0.0: {} + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.8 + is-regex@1.1.4: dependencies: call-bind: 1.0.7 @@ -14627,6 +14932,8 @@ snapshots: js-tokens@4.0.0: {} + js-tokens@9.0.1: {} + js-yaml@3.14.2: dependencies: argparse: 1.0.10 @@ -14909,6 +15216,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + magic-string@0.30.8: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -15007,6 +15318,45 @@ snapshots: transitivePeerDependencies: - supports-color + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + mdast-util-phrasing@4.1.0: dependencies: '@types/mdast': 4.0.4 @@ -15021,7 +15371,7 @@ snapshots: micromark-util-sanitize-uri: 2.0.1 trim-lines: 3.0.1 unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 vfile: 6.0.3 mdast-util-to-markdown@2.1.2: @@ -15033,7 +15383,7 @@ snapshots: mdast-util-to-string: 4.0.0 micromark-util-classify-character: 2.0.1 micromark-util-decode-string: 2.0.1 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 zwitch: 2.0.4 mdast-util-to-string@4.0.0: @@ -15421,7 +15771,7 @@ snapshots: node-source-walk@7.0.1: dependencies: - '@babel/parser': 7.28.4 + '@babel/parser': 7.29.0 node-stream-zip@1.15.0: {} @@ -15556,6 +15906,14 @@ snapshots: dependencies: mimic-fn: 4.0.0 + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.4: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.1.0 + regex-recursion: 6.0.2 + open@7.4.2: dependencies: is-docker: 2.2.1 @@ -15624,6 +15982,16 @@ snapshots: dependencies: callsites: 3.1.0 + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.2.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + parse-gitignore@2.0.0: {} parse-imports@2.2.1: @@ -15708,6 +16076,12 @@ snapshots: pend@1.2.0: {} + periscopic@4.0.2: + dependencies: + '@types/estree': 1.0.8 + is-reference: 3.0.3 + zimmerframe: 1.1.4 + pg-int8@1.0.1: {} pg-numeric@1.0.2: {} @@ -15968,6 +16342,8 @@ snapshots: react-is@16.13.1: {} + react-is@19.2.4: {} + react-property@2.0.2: {} react-refresh@0.14.2: {} @@ -16082,6 +16458,16 @@ snapshots: regenerator-runtime@0.14.1: {} + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + regexp.prototype.flags@1.5.2: dependencies: call-bind: 1.0.7 @@ -16127,6 +16513,14 @@ snapshots: hast-util-raw: 9.1.0 vfile: 6.0.3 + rehype-react@8.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-jsx-runtime: 2.3.6 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + rehype-slug@6.0.0: dependencies: '@types/hast': 3.0.4 @@ -16462,9 +16856,15 @@ snapshots: shell-quote@1.8.1: {} - shiki@1.10.3: + shiki@3.22.0: dependencies: - '@shikijs/core': 1.10.3 + '@shikijs/core': 3.22.0 + '@shikijs/engine-javascript': 3.22.0 + '@shikijs/engine-oniguruma': 3.22.0 + '@shikijs/langs': 3.22.0 + '@shikijs/themes': 3.22.0 + '@shikijs/types': 3.22.0 + '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 side-channel-list@1.0.0: @@ -16690,6 +17090,10 @@ snapshots: strip-json-comments@3.1.1: {} + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + style-to-js@1.1.12: dependencies: style-to-object: 1.0.6 @@ -16890,6 +17294,8 @@ snapshots: - immer - react + turbo-stream@3.1.0: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -17043,6 +17449,12 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + universal-user-agent@6.0.0: {} unixify@1.0.0: @@ -17427,6 +17839,8 @@ snapshots: yoctocolors@2.1.2: {} + zimmerframe@1.1.4: {} + zip-stream@6.0.1: dependencies: archiver-utils: 5.0.2 diff --git a/src/components/Breadcrumbs.tsx b/src/components/Breadcrumbs.tsx index 70e5ae5b0..f12f031eb 100644 --- a/src/components/Breadcrumbs.tsx +++ b/src/components/Breadcrumbs.tsx @@ -1,7 +1,7 @@ import { Link } from '@tanstack/react-router' import { ChevronDown } from 'lucide-react' import { twMerge } from 'tailwind-merge' -import type { MarkdownHeading } from '~/utils/markdown/processor' +import type { MarkdownHeading } from '~/utils/markdown/types' import { Dropdown, DropdownTrigger, @@ -43,7 +43,7 @@ export function Breadcrumbs({ )} {showTocToggle && ( - + + + )} +
+        {children}
+      
+ + ) +} - const { highlighter, effectiveLang } = await getHighlighter(langStr) - // Trim trailing newlines to prevent empty lines at end of code block - const trimmedCode = (code || '').trimEnd() +type HighlightedCodeBlockProps = { + /** Pre-highlighted HTML from server-side Shiki */ + html: string + /** Optional title to display above code block */ + title?: string + /** Language for display in header */ + lang?: string + /** Whether to show the copy button */ + showCopyButton?: boolean + /** Whether the code block is embedded (e.g., in a file explorer) */ + isEmbedded?: boolean + className?: string + style?: React.CSSProperties +} - const htmls = await Promise.all( - themes.map(async (theme) => { - const output = highlighter.codeToHtml(trimmedCode, { - lang: effectiveLang, - theme, - transformers: [transformerNotationDiff()], - }) +/** + * HighlightedCodeBlock renders pre-highlighted HTML from server-side Shiki. + * Use this for code blocks outside of markdown (e.g., CodeExplorer, landing pages). + */ +export function HighlightedCodeBlock({ + html, + title, + lang, + showCopyButton = true, + isEmbedded, + className, + style, +}: HighlightedCodeBlockProps) { + const [copied, setCopied] = React.useState(false) + const ref = React.useRef(null) + const { notify } = useToast() - if (lang === 'mermaid') { - const preAttributes = extractPreAttributes(output) - let svgHtml = genSvgMap.get(trimmedCode) - if (!svgHtml) { - const mermaid = await getMermaid() - const { svg } = await mermaid.render('foo', trimmedCode) - genSvgMap.set(trimmedCode, svg) - svgHtml = svg - } - return `
${svgHtml}
` - } + const handleCopy = React.useCallback(() => { + const copyContent = ref.current?.innerText?.trimEnd() || '' - return output - }), - ) + navigator.clipboard.writeText(copyContent) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + notify( +
+ Copied code + + Code block copied to clipboard + +
, + ) + }, [notify]) - setCodeElement( -
pre]:h-full [&>pre]:rounded-none' : '', - )} - dangerouslySetInnerHTML={{ __html: htmls.join('') }} - ref={ref} - />, - ) - })() - }, [code, lang]) + // Display language in header + const displayLang = lang?.toLowerCase() === 'bash' ? 'sh' : (lang ?? '') return (
- {(title || showTypeCopyButton) && ( + {(title || showCopyButton) && (
- {title || (lang?.toLowerCase() === 'bash' ? 'sh' : (lang ?? ''))} + {title || displayLang}
+
+ )} +
pre]:h-full')} + dangerouslySetInnerHTML={{ __html: html }} + /> +
+ ) +} + +type PlainCodeBlockProps = { + /** Code content to display (no highlighting) */ + code: string + /** Optional title to display above code block */ + title?: string + /** Language for display in header */ + lang?: string + /** Whether to show the copy button */ + showCopyButton?: boolean + className?: string + style?: React.CSSProperties +} + +/** + * PlainCodeBlock displays code without syntax highlighting. + * Use this for dynamically generated code (e.g., package manager commands). + */ +export function PlainCodeBlock({ + code, + title, + lang, + showCopyButton = true, + className, + style, +}: PlainCodeBlockProps) { + const [copied, setCopied] = React.useState(false) + const { notify } = useToast() + + const handleCopy = React.useCallback(() => { + navigator.clipboard.writeText(code.trimEnd()) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + notify( +
+ Copied code + + Code block copied to clipboard + +
, + ) + }, [code, notify]) + + // Display language in header + const displayLang = lang?.toLowerCase() === 'bash' ? 'sh' : (lang ?? '') - if (copyContent.endsWith('\n')) { - copyContent = copyContent.slice(0, -1) - } + return ( +
+ {(title || showCopyButton) && ( +
+
+ {title || displayLang} +
- navigator.clipboard.writeText(copyContent) - setCopied(true) - setTimeout(() => setCopied(false), 2000) - notify( -
- Copied code - - Code block copied to clipboard - -
, - ) - }} +
)} - {codeElement} +
+        
+          {code}
+        
+      
) } diff --git a/src/components/markdown/DocContent.tsx b/src/components/markdown/DocContent.tsx new file mode 100644 index 000000000..5bff9f2bb --- /dev/null +++ b/src/components/markdown/DocContent.tsx @@ -0,0 +1,14 @@ +// Server component for doc markdown content +// This file does NOT have 'use client' - it's a server component. +// The children are JSX elements produced by renderMarkdownToJsx on the server, +// which already include client component references for interactive elements. + +import type { ReactNode } from 'react' + +type DocContentProps = { + children: ReactNode +} + +export function DocContent({ children }: DocContentProps) { + return <>{children} +} diff --git a/src/components/markdown/FileTabs.tsx b/src/components/markdown/FileTabs.tsx index d29923d0d..6170fb61d 100644 --- a/src/components/markdown/FileTabs.tsx +++ b/src/components/markdown/FileTabs.tsx @@ -1,3 +1,5 @@ +'use client' + import * as React from 'react' export type FileTabDefinition = { diff --git a/src/components/markdown/FrameworkContent.tsx b/src/components/markdown/FrameworkContent.tsx index 26601f0ae..bdb58d4c1 100644 --- a/src/components/markdown/FrameworkContent.tsx +++ b/src/components/markdown/FrameworkContent.tsx @@ -1,3 +1,5 @@ +'use client' + import { useLocalCurrentFramework } from '../FrameworkSelect' import { useCurrentUserQuery } from '~/hooks/useCurrentUser' import { useParams } from '@tanstack/react-router' diff --git a/src/components/markdown/Markdown.tsx b/src/components/markdown/Markdown.tsx index a5fa01be5..2315bd132 100644 --- a/src/components/markdown/Markdown.tsx +++ b/src/components/markdown/Markdown.tsx @@ -1,3 +1,5 @@ +'use client' + import type { HTMLProps } from 'react' import * as React from 'react' import { MarkdownLink } from './MarkdownLink' @@ -9,7 +11,6 @@ import parse, { HTMLReactParserOptions, } from 'html-react-parser' -import { renderMarkdown } from '~/utils/markdown' import { CodeBlock } from './CodeBlock' import { handleTabsComponent } from './MarkdownTabsHandler' import { handleFrameworkComponent } from './MarkdownFrameworkHandler' @@ -125,31 +126,17 @@ const options: HTMLReactParserOptions = { } type MarkdownProps = { - rawContent?: string - htmlMarkup?: string + htmlMarkup: string } export const Markdown = React.memo(function Markdown({ - rawContent, htmlMarkup, }: MarkdownProps) { - const rendered = React.useMemo(() => { - if (rawContent) { - return renderMarkdown(rawContent) - } - - if (htmlMarkup) { - return { markup: htmlMarkup, headings: [] } - } - - return { markup: '', headings: [] } - }, [rawContent, htmlMarkup]) - return React.useMemo(() => { - if (!rendered.markup) { + if (!htmlMarkup) { return null } - return parse(rendered.markup, options) - }, [rendered.markup]) + return parse(htmlMarkup, options) + }, [htmlMarkup]) }) diff --git a/src/components/markdown/MarkdownContent.tsx b/src/components/markdown/MarkdownContent.tsx index 86553d4a3..dd9169eda 100644 --- a/src/components/markdown/MarkdownContent.tsx +++ b/src/components/markdown/MarkdownContent.tsx @@ -2,7 +2,6 @@ import * as React from 'react' import { SquarePen } from 'lucide-react' import { twMerge } from 'tailwind-merge' import { DocTitle } from '~/components/DocTitle' -import { Markdown } from './Markdown' import { CopyPageDropdown } from '~/components/CopyPageDropdown' import { DocFeedbackProvider } from '~/components/DocFeedbackProvider' import { Button } from '~/ui' @@ -12,10 +11,8 @@ type MarkdownContentProps = { repo: string branch: string filePath: string - /** Pre-rendered HTML markup (from renderMarkdown). If not provided, rawContent will be rendered. */ - htmlMarkup?: string - /** Raw markdown content to render. Used if htmlMarkup is not provided. */ - rawContent?: string + /** RSC-rendered content */ + contentRsc: React.ReactNode /** Additional elements to render in the title bar (e.g., width toggle button) */ titleBarActions?: React.ReactNode /** Additional class names for the prose container */ @@ -33,8 +30,7 @@ export function MarkdownContent({ repo, branch, filePath, - htmlMarkup, - rawContent, + contentRsc, titleBarActions, proseClassName, containerRef, @@ -43,12 +39,6 @@ export function MarkdownContent({ pagePath, }: MarkdownContentProps) { const renderMarkdownContent = () => { - const markdownElement = htmlMarkup ? ( - - ) : rawContent ? ( - - ) : null - if (libraryId && libraryVersion && pagePath) { return ( - {markdownElement} + {contentRsc} ) } - return markdownElement + return contentRsc } return ( diff --git a/src/components/markdown/MarkdownFrameworkHandler.tsx b/src/components/markdown/MarkdownFrameworkHandler.tsx index de0b877a3..891066373 100644 --- a/src/components/markdown/MarkdownFrameworkHandler.tsx +++ b/src/components/markdown/MarkdownFrameworkHandler.tsx @@ -1,25 +1,7 @@ import * as React from 'react' import { domToReact, Element } from 'html-react-parser' import type { HTMLReactParserOptions } from 'html-react-parser' - -// Helper to resolve different module shapes (named export vs default) -function resolveModuleDefault(mod: any, key: string): React.ComponentType { - if (!mod) return undefined as any - if (mod[key] && typeof mod[key] === 'function') return mod[key] - if (mod.default) { - if (mod.default[key] && typeof mod.default[key] === 'function') - return mod.default[key] - if (typeof mod.default === 'function') return mod.default - } - if (typeof mod === 'function') return mod - return (mod as any).default ?? (mod as any) -} - -const FrameworkContent = React.lazy>(() => - import('./FrameworkContent').then((mod) => ({ - default: resolveModuleDefault(mod, 'FrameworkContent'), - })), -) +import { FrameworkContent } from './FrameworkContent' export function handleFrameworkComponent( domNode: Element, @@ -52,13 +34,11 @@ export function handleFrameworkComponent( }) return ( - Loading...
}> - - + ) } catch { return null diff --git a/src/components/markdown/MarkdownHeadingContext.tsx b/src/components/markdown/MarkdownHeadingContext.tsx index de4a2b6a9..a1e3e4389 100644 --- a/src/components/markdown/MarkdownHeadingContext.tsx +++ b/src/components/markdown/MarkdownHeadingContext.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { MarkdownHeading } from '~/utils/markdown/processor' +import type { MarkdownHeading } from '~/utils/markdown/types' const MarkdownHeadingContext = React.createContext<{ headings: MarkdownHeading[] diff --git a/src/components/markdown/MarkdownLink.tsx b/src/components/markdown/MarkdownLink.tsx index 488724b81..1adb3f1f2 100644 --- a/src/components/markdown/MarkdownLink.tsx +++ b/src/components/markdown/MarkdownLink.tsx @@ -1,3 +1,5 @@ +'use client' + import { Link } from '@tanstack/react-router' import type { HTMLProps } from 'react' diff --git a/src/components/markdown/MarkdownTabsHandler.tsx b/src/components/markdown/MarkdownTabsHandler.tsx index d453cbdb6..0facd4285 100644 --- a/src/components/markdown/MarkdownTabsHandler.tsx +++ b/src/components/markdown/MarkdownTabsHandler.tsx @@ -2,35 +2,9 @@ import * as React from 'react' import { domToReact, Element } from 'html-react-parser' import type { HTMLReactParserOptions } from 'html-react-parser' import type { Framework } from '~/libraries/types' - -// Helper to resolve different module shapes (named export vs default) -function resolveModuleDefault(mod: any, key: string): React.ComponentType { - if (!mod) return undefined as any - if (mod[key] && typeof mod[key] === 'function') return mod[key] - if (mod.default) { - if (mod.default[key] && typeof mod.default[key] === 'function') - return mod.default[key] - if (typeof mod.default === 'function') return mod.default - } - if (typeof mod === 'function') return mod - return (mod as any).default ?? (mod as any) -} - -const Tabs = React.lazy>(() => - import('./Tabs').then((mod) => ({ - default: resolveModuleDefault(mod, 'Tabs'), - })), -) -const PackageManagerTabs = React.lazy>(() => - import('./PackageManagerTabs').then((mod) => ({ - default: resolveModuleDefault(mod, 'PackageManagerTabs'), - })), -) -const FileTabs = React.lazy>(() => - import('./FileTabs').then((mod) => ({ - default: resolveModuleDefault(mod, 'FileTabs'), - })), -) +import { Tabs } from './Tabs' +import { PackageManagerTabs } from './PackageManagerTabs' +import { FileTabs } from './FileTabs' export function handleTabsComponent( domNode: Element, @@ -46,13 +20,11 @@ export function handleTabsComponent( const frameworks = Object.keys(packagesByFramework) as Framework[] return ( - Loading...
}> - - + ) } catch { // Fall through to default tabs if parsing fails @@ -74,11 +46,7 @@ export function handleTabsComponent( domToReact(panel.children as any, options), ) - return ( - Loading...}> - - - ) + return } catch { // Fall through to default tabs if parsing fails } @@ -102,9 +70,5 @@ export function handleTabsComponent( return <>{result} }) - return ( - Loading...}> - - - ) + return } diff --git a/src/components/markdown/MdComponents.tsx b/src/components/markdown/MdComponents.tsx new file mode 100644 index 000000000..6c1b2f9fe --- /dev/null +++ b/src/components/markdown/MdComponents.tsx @@ -0,0 +1,184 @@ +'use client' + +import * as React from 'react' +import type { Framework } from '~/libraries/types' +import { Tabs } from './Tabs' +import { PackageManagerTabs } from './PackageManagerTabs' +import { FileTabs } from './FileTabs' +import { FrameworkContent } from './FrameworkContent' + +type MdCommentComponentProps = { + 'data-component'?: string + 'data-attributes'?: string + 'data-package-manager-meta'?: string + 'data-files-meta'?: string + children?: React.ReactNode +} + +/** + * Handles md-comment-component elements from rehype-react. + * Maps to the appropriate tab/framework component based on data attributes. + */ +export function MdCommentComponent({ + 'data-component': componentName, + 'data-attributes': rawAttributes, + 'data-package-manager-meta': pmMeta, + 'data-files-meta': filesMeta, + children, +}: MdCommentComponentProps) { + const attributes: Record = React.useMemo(() => { + if (typeof rawAttributes === 'string') { + try { + return JSON.parse(rawAttributes) + } catch { + return {} + } + } + return {} + }, [rawAttributes]) + + const normalizedComponent = componentName?.toLowerCase() + + // Handle tabs component + if (normalizedComponent === 'tabs') { + // Handle package-manager variant + if (pmMeta) { + try { + const { packagesByFramework, mode } = JSON.parse(pmMeta) + const frameworks = Object.keys(packagesByFramework) as Framework[] + + return ( + + ) + } catch { + // Fall through to default tabs if parsing fails + } + } + + // Handle files variant + if (filesMeta) { + try { + const tabs = attributes.tabs || [] + // Children are already React nodes from rehype-react + const childArray = React.Children.toArray(children) + const panelChildren = childArray.filter( + (child): child is React.ReactElement => + React.isValidElement(child) && + (child.type === MdTabPanel || child.type === 'md-tab-panel'), + ) + + const tabContents = panelChildren.map((panel) => panel.props.children) + + return + } catch { + // Fall through to default tabs if parsing fails + } + } + + // Handle default tabs variant + const tabs = attributes.tabs + if (!tabs || !Array.isArray(tabs)) { + return
{children}
+ } + + // Children are already React nodes from rehype-react + const childArray = React.Children.toArray(children) + const panelChildren = childArray.filter( + (child): child is React.ReactElement => + React.isValidElement(child) && + (child.type === MdTabPanel || child.type === 'md-tab-panel'), + ) + + const tabContents = panelChildren.map((panel) => ( + <>{panel.props.children} + )) + + return + } + + // Handle framework component + if (normalizedComponent === 'framework') { + return + } + + // Default: just render children in a div + return
{children}
+} + +type MdTabPanelProps = { + 'data-tab-slug'?: string + 'data-tab-index'?: string + children?: React.ReactNode +} + +/** + * Wrapper for md-tab-panel elements. + * These are intermediate elements that hold tab content. + */ +export function MdTabPanel({ children }: MdTabPanelProps) { + // This component is mainly used as a marker for MdCommentComponent to find + return <>{children} +} + +type MdFrameworkPanelProps = { + 'data-framework'?: string + children?: React.ReactNode +} + +/** + * Wrapper for md-framework-panel elements. + * These hold framework-specific content. + */ +export function MdFrameworkPanel({ children }: MdFrameworkPanelProps) { + // This component is mainly used as a marker + return <>{children} +} + +type MdFrameworkComponentInnerProps = { + children?: React.ReactNode +} + +/** + * Inner component for framework switching. + * Extracts framework panels from children and renders FrameworkContent. + */ +function MdFrameworkComponentInner({ + children, +}: MdFrameworkComponentInnerProps) { + const childArray = React.Children.toArray(children) + + // Find all framework panel children + const panelChildren = childArray.filter( + (child): child is React.ReactElement => + React.isValidElement(child) && + (child.type === MdFrameworkPanel || child.type === 'md-framework-panel'), + ) + + // Build panelsByFramework map + const panelsByFramework: Record = {} + const availableFrameworks: string[] = [] + + panelChildren.forEach((panel) => { + const fw = panel.props['data-framework'] + if (fw) { + panelsByFramework[fw] = panel.props.children + availableFrameworks.push(fw) + } + }) + + if (availableFrameworks.length === 0) { + return
{children}
+ } + + return ( + + ) +} diff --git a/src/components/markdown/PackageManagerTabs.tsx b/src/components/markdown/PackageManagerTabs.tsx index 31bade627..5faaa5027 100644 --- a/src/components/markdown/PackageManagerTabs.tsx +++ b/src/components/markdown/PackageManagerTabs.tsx @@ -1,9 +1,11 @@ +'use client' + import { useLocalCurrentFramework } from '../FrameworkSelect' import { useCurrentUserQuery } from '~/hooks/useCurrentUser' import { useParams } from '@tanstack/react-router' import { create } from 'zustand' import { Tabs, type TabDefinition } from './Tabs' -import { CodeBlock } from './CodeBlock' +import { PlainCodeBlock } from './CodeBlock' import type { Framework } from '~/libraries/types' type PackageManager = 'bun' | 'npm' | 'pnpm' | 'yarn' @@ -190,11 +192,7 @@ export function PackageManagerTabs({ const children = PACKAGE_MANAGERS.map((pm) => { const commands = getInstallCommand(pm, packageGroups, mode) const commandText = commands.join('\n') - return ( - - {commandText} - - ) + return }) return ( diff --git a/src/components/markdown/Tabs.tsx b/src/components/markdown/Tabs.tsx index dd2867c1a..949c30227 100644 --- a/src/components/markdown/Tabs.tsx +++ b/src/components/markdown/Tabs.tsx @@ -1,3 +1,5 @@ +'use client' + import { useParams } from '@tanstack/react-router' import * as React from 'react' import { frameworkOptions } from '~/libraries/frameworks' diff --git a/src/components/markdown/index.ts b/src/components/markdown/index.ts index f4b3dc588..e1f3f01ad 100644 --- a/src/components/markdown/index.ts +++ b/src/components/markdown/index.ts @@ -1,11 +1,10 @@ -export { Markdown } from './Markdown' export { MarkdownLink } from './MarkdownLink' export { MarkdownContent } from './MarkdownContent' export { MarkdownHeadingProvider, useMarkdownHeadings, } from './MarkdownHeadingContext' -export { CodeBlock } from './CodeBlock' +export { CodeBlock, HighlightedCodeBlock, PlainCodeBlock } from './CodeBlock' export { Tabs } from './Tabs' export { FileTabs } from './FileTabs' export { PackageManagerTabs } from './PackageManagerTabs' diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 9a07b2e4a..bcee5ef97 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -732,9 +732,9 @@ export interface FileRoutesByFullPath { '/admin/': typeof AdminIndexRoute '/blog/': typeof BlogIndexRoute '/builder/': typeof BuilderIndexRoute - '/feed/': typeof FeedIndexRoute - '/showcase/': typeof ShowcaseIndexRoute - '/stats/': typeof StatsIndexRoute + '/feed': typeof FeedIndexRoute + '/showcase': typeof ShowcaseIndexRoute + '/stats': typeof StatsIndexRoute '/$libraryId/$version/docs': typeof LibraryIdVersionDocsRouteWithChildren '/admin/banners/$id': typeof AdminBannersIdRoute '/admin/feed/$id': typeof AdminFeedIdRoute @@ -763,14 +763,14 @@ export interface FileRoutesByFullPath { '/showcase/edit/$id': typeof ShowcaseEditIdRoute '/stats/npm/$packages': typeof StatsNpmPackagesRoute '/$libraryId/$version/': typeof LibraryIdVersionIndexRoute - '/admin/banners/': typeof AdminBannersIndexRoute - '/admin/feed/': typeof AdminFeedIndexRoute - '/admin/feedback/': typeof AdminFeedbackIndexRoute - '/admin/notes/': typeof AdminNotesIndexRoute - '/admin/roles/': typeof AdminRolesIndexRoute - '/admin/showcases/': typeof AdminShowcasesIndexRoute - '/api/mcp/': typeof ApiMcpIndexRoute - '/stats/npm/': typeof StatsNpmIndexRoute + '/admin/banners': typeof AdminBannersIndexRoute + '/admin/feed': typeof AdminFeedIndexRoute + '/admin/feedback': typeof AdminFeedbackIndexRoute + '/admin/notes': typeof AdminNotesIndexRoute + '/admin/roles': typeof AdminRolesIndexRoute + '/admin/showcases': typeof AdminShowcasesIndexRoute + '/api/mcp': typeof ApiMcpIndexRoute + '/stats/npm': typeof StatsNpmIndexRoute '/$libraryId/$version/docs/$': typeof LibraryIdVersionDocsSplatRoute '/$libraryId/$version/docs/community-resources': typeof LibraryIdVersionDocsCommunityResourcesRoute '/$libraryId/$version/docs/contributors': typeof LibraryIdVersionDocsContributorsRoute @@ -780,10 +780,10 @@ export interface FileRoutesByFullPath { '/api/builder/deploy/check-name': typeof ApiBuilderDeployCheckNameRoute '/api/builder/deploy/github': typeof ApiBuilderDeployGithubRoute '/$libraryId/$version/docs/': typeof LibraryIdVersionDocsIndexRoute - '/$libraryId/$version/docs/framework/': typeof LibraryIdVersionDocsFrameworkIndexRoute + '/$libraryId/$version/docs/framework': typeof LibraryIdVersionDocsFrameworkIndexRoute '/$libraryId/$version/docs/framework/$framework/$': typeof LibraryIdVersionDocsFrameworkFrameworkSplatRoute '/$libraryId/$version/docs/framework/$framework/{$}.md': typeof LibraryIdVersionDocsFrameworkFrameworkChar123Char125DotmdRoute - '/$libraryId/$version/docs/framework/$framework/': typeof LibraryIdVersionDocsFrameworkFrameworkIndexRoute + '/$libraryId/$version/docs/framework/$framework': typeof LibraryIdVersionDocsFrameworkFrameworkIndexRoute '/$libraryId/$version/docs/framework/$framework/examples/$': typeof LibraryIdVersionDocsFrameworkFrameworkExamplesSplatRoute } export interface FileRoutesByTo { @@ -1058,9 +1058,9 @@ export interface FileRouteTypes { | '/admin/' | '/blog/' | '/builder/' - | '/feed/' - | '/showcase/' - | '/stats/' + | '/feed' + | '/showcase' + | '/stats' | '/$libraryId/$version/docs' | '/admin/banners/$id' | '/admin/feed/$id' @@ -1089,14 +1089,14 @@ export interface FileRouteTypes { | '/showcase/edit/$id' | '/stats/npm/$packages' | '/$libraryId/$version/' - | '/admin/banners/' - | '/admin/feed/' - | '/admin/feedback/' - | '/admin/notes/' - | '/admin/roles/' - | '/admin/showcases/' - | '/api/mcp/' - | '/stats/npm/' + | '/admin/banners' + | '/admin/feed' + | '/admin/feedback' + | '/admin/notes' + | '/admin/roles' + | '/admin/showcases' + | '/api/mcp' + | '/stats/npm' | '/$libraryId/$version/docs/$' | '/$libraryId/$version/docs/community-resources' | '/$libraryId/$version/docs/contributors' @@ -1106,10 +1106,10 @@ export interface FileRouteTypes { | '/api/builder/deploy/check-name' | '/api/builder/deploy/github' | '/$libraryId/$version/docs/' - | '/$libraryId/$version/docs/framework/' + | '/$libraryId/$version/docs/framework' | '/$libraryId/$version/docs/framework/$framework/$' | '/$libraryId/$version/docs/framework/$framework/{$}.md' - | '/$libraryId/$version/docs/framework/$framework/' + | '/$libraryId/$version/docs/framework/$framework' | '/$libraryId/$version/docs/framework/$framework/examples/$' fileRoutesByTo: FileRoutesByTo to: @@ -1596,21 +1596,21 @@ declare module '@tanstack/react-router' { '/stats/': { id: '/stats/' path: '/stats' - fullPath: '/stats/' + fullPath: '/stats' preLoaderRoute: typeof StatsIndexRouteImport parentRoute: typeof rootRouteImport } '/showcase/': { id: '/showcase/' path: '/showcase' - fullPath: '/showcase/' + fullPath: '/showcase' preLoaderRoute: typeof ShowcaseIndexRouteImport parentRoute: typeof rootRouteImport } '/feed/': { id: '/feed/' path: '/feed' - fullPath: '/feed/' + fullPath: '/feed' preLoaderRoute: typeof FeedIndexRouteImport parentRoute: typeof rootRouteImport } @@ -1806,56 +1806,56 @@ declare module '@tanstack/react-router' { '/stats/npm/': { id: '/stats/npm/' path: '/stats/npm' - fullPath: '/stats/npm/' + fullPath: '/stats/npm' preLoaderRoute: typeof StatsNpmIndexRouteImport parentRoute: typeof rootRouteImport } '/api/mcp/': { id: '/api/mcp/' path: '/api/mcp' - fullPath: '/api/mcp/' + fullPath: '/api/mcp' preLoaderRoute: typeof ApiMcpIndexRouteImport parentRoute: typeof rootRouteImport } '/admin/showcases/': { id: '/admin/showcases/' path: '/showcases' - fullPath: '/admin/showcases/' + fullPath: '/admin/showcases' preLoaderRoute: typeof AdminShowcasesIndexRouteImport parentRoute: typeof AdminRouteRoute } '/admin/roles/': { id: '/admin/roles/' path: '/roles' - fullPath: '/admin/roles/' + fullPath: '/admin/roles' preLoaderRoute: typeof AdminRolesIndexRouteImport parentRoute: typeof AdminRouteRoute } '/admin/notes/': { id: '/admin/notes/' path: '/notes' - fullPath: '/admin/notes/' + fullPath: '/admin/notes' preLoaderRoute: typeof AdminNotesIndexRouteImport parentRoute: typeof AdminRouteRoute } '/admin/feedback/': { id: '/admin/feedback/' path: '/feedback' - fullPath: '/admin/feedback/' + fullPath: '/admin/feedback' preLoaderRoute: typeof AdminFeedbackIndexRouteImport parentRoute: typeof AdminRouteRoute } '/admin/feed/': { id: '/admin/feed/' path: '/feed' - fullPath: '/admin/feed/' + fullPath: '/admin/feed' preLoaderRoute: typeof AdminFeedIndexRouteImport parentRoute: typeof AdminRouteRoute } '/admin/banners/': { id: '/admin/banners/' path: '/banners' - fullPath: '/admin/banners/' + fullPath: '/admin/banners' preLoaderRoute: typeof AdminBannersIndexRouteImport parentRoute: typeof AdminRouteRoute } @@ -2121,14 +2121,14 @@ declare module '@tanstack/react-router' { '/$libraryId/$version/docs/framework/': { id: '/$libraryId/$version/docs/framework/' path: '/framework' - fullPath: '/$libraryId/$version/docs/framework/' + fullPath: '/$libraryId/$version/docs/framework' preLoaderRoute: typeof LibraryIdVersionDocsFrameworkIndexRouteImport parentRoute: typeof LibraryIdVersionDocsRoute } '/$libraryId/$version/docs/framework/$framework/': { id: '/$libraryId/$version/docs/framework/$framework/' path: '/framework/$framework' - fullPath: '/$libraryId/$version/docs/framework/$framework/' + fullPath: '/$libraryId/$version/docs/framework/$framework' preLoaderRoute: typeof LibraryIdVersionDocsFrameworkFrameworkIndexRouteImport parentRoute: typeof LibraryIdVersionDocsRoute } diff --git a/src/routes/$libraryId/$version.docs.$.tsx b/src/routes/$libraryId/$version.docs.$.tsx index c5437ed3f..e6f50bcbc 100644 --- a/src/routes/$libraryId/$version.docs.$.tsx +++ b/src/routes/$libraryId/$version.docs.$.tsx @@ -85,7 +85,7 @@ export const Route = createFileRoute('/$libraryId/$version/docs/$')({ function Docs() { const { version, libraryId, _splat } = Route.useParams() - const { title, content, filePath } = Route.useLoaderData() + const { title, contentRsc, headings, filePath } = Route.useLoaderData() const versionMatch = useMatch({ from: '/$libraryId/$version' }) const { config } = versionMatch.loaderData as { config: ConfigSchema } const library = getLibrary(libraryId) @@ -96,7 +96,8 @@ function Docs() { { +export const Route = createFileRoute('/blog/$')({ + staleTime: Infinity, + loader: async ({ params }) => { + const docsPath = params._splat if (!docsPath) { throw new Error('Invalid docs path') } @@ -33,40 +33,43 @@ const fetchBlogPost = createServerFn({ method: 'GET' }) handleRedirects(docsPath) const filePath = `src/blog/${docsPath}.md` - - const post = allPosts.find((post) => post.slug === docsPath) + const post = allPosts.find((p) => p.slug === docsPath) if (!post) { throw notFound() } - setResponseHeaders( - new Headers({ - 'Cache-Control': 'public, max-age=0, must-revalidate', - 'Netlify-CDN-Cache-Control': - 'public, max-age=300, durable, stale-while-revalidate=300', - }), - ) + const { setCacheHeaders } = await import('~/utils/headers.server') + setCacheHeaders() const now = new Date() const publishDate = new Date(post.published) const isUnpublished = post.draft || publishDate > now + const blogContent = `_by ${formatAuthors(post.authors)} on ${format( + new Date(post.published || 0), + 'MMMM d, yyyy', + )}._ + +${post.content}` + + const { renderMarkdownToJsx } = await import('~/utils/markdown') + const { headings } = await renderMarkdownToJsx(blogContent) + const { renderBlogContent } = await import('~/utils/renderBlogContent') + const ContentRsc = await renderBlogContent({ data: blogContent }) + return { title: post.title, description: post.description, published: post.published, - content: post.content, authors: post.authors, headerImage: post.headerImage, filePath, isUnpublished, + headings, + ContentRsc, } - }) - -export const Route = createFileRoute('/blog/$')({ - staleTime: Infinity, - loader: ({ params }) => fetchBlogPost({ data: params._splat }), + }, head: ({ loaderData }) => { // Generate optimized social media image URL using Netlify Image CDN const getSocialImageUrl = (headerImage?: string) => { @@ -103,19 +106,10 @@ export const Route = createFileRoute('/blog/$')({ }) function BlogPost() { - const { title, content, filePath, authors, published } = Route.useLoaderData() + const { headings, ContentRsc, title, filePath } = Route.useLoaderData() - const blogContent = `_by ${formatAuthors(authors)} on ${format( - new Date(published || 0), - 'MMMM d, yyyy', - )}._ - -${content}` - - const { headings, markup } = React.useMemo( - () => renderMarkdown(blogContent), - [blogContent], - ) + const repo = 'tanstack/tanstack.com' + const branch = 'main' const isTocVisible = headings.length > 1 @@ -164,9 +158,6 @@ ${content}` return () => observer.disconnect() }, [headings]) - const repo = 'tanstack/tanstack.com' - const branch = 'main' - return (
-
- +
+
+ {title} +
+ +
+
+
+
+
+ {ContentRsc} +
+
+
+ +
{isTocVisible && (
diff --git a/src/routes/blog.index.tsx b/src/routes/blog.index.tsx index 8db677b06..46cab45c8 100644 --- a/src/routes/blog.index.tsx +++ b/src/routes/blog.index.tsx @@ -2,40 +2,42 @@ import { Link, createFileRoute } from '@tanstack/react-router' import { Card } from '~/components/Card' import { formatAuthors, getPublishedPosts } from '~/utils/blog' import { SimpleMarkdown } from '~/components/SimpleMarkdown' +import { renderMarkdownAsync } from '~/utils/markdown' import { format } from '~/utils/dates' import { Footer } from '~/components/Footer' import { PostNotFound } from './blog' import { createServerFn } from '@tanstack/react-start' -import { setResponseHeaders } from '@tanstack/react-start/server' import { RssIcon } from 'lucide-react' type BlogFrontMatter = { slug: string title: string published: string - excerpt: string | undefined + excerptHtml: string | undefined authors: string[] } const fetchFrontMatters = createServerFn({ method: 'GET' }).handler( async () => { - setResponseHeaders( - new Headers({ - 'Cache-Control': 'public, max-age=0, must-revalidate', - 'Netlify-CDN-Cache-Control': - 'public, max-age=300, durable, stale-while-revalidate=300', + const { setCacheHeaders } = await import('~/utils/headers.server') + setCacheHeaders() + + const posts = getPublishedPosts() + const frontMatters: BlogFrontMatter[] = await Promise.all( + posts.map(async (post) => { + return { + slug: post.slug, + title: post.title, + published: post.published, + excerptHtml: post.excerpt + ? (await renderMarkdownAsync(post.excerpt)).markup + : undefined, + authors: post.authors, + } }), ) - return getPublishedPosts().map((post) => { - return { - slug: post.slug, - title: post.title, - published: post.published, - excerpt: post.excerpt, - authors: post.authors, - } - }) + return frontMatters // return json(frontMatters, { // headers: { @@ -84,45 +86,49 @@ function BlogIndex() {

- {frontMatters.map(({ slug, title, published, excerpt, authors }) => { - return ( - -
-
{title}
-
-

- by {formatAuthors(authors)} - {published ? ( - + {frontMatters.map( + ({ slug, title, published, excerptHtml, authors }) => { + return ( + +

+
{title}
+
+

+ by {formatAuthors(authors)} + {published ? ( + + ) : null} +

+
+
+ {excerptHtml ? ( + ) : null} -

-
-
- +
-
-
-
- Read More +
+
+ Read More +
-
- - ) - })} + + ) + }, + )}
diff --git a/src/routes/feed.$id.tsx b/src/routes/feed.$id.tsx index 3c72c1aa7..b7046e5af 100644 --- a/src/routes/feed.$id.tsx +++ b/src/routes/feed.$id.tsx @@ -1,11 +1,13 @@ import { Link, notFound, createFileRoute } from '@tanstack/react-router' +import { createServerFn } from '@tanstack/react-start' +import { renderServerComponent } from '@tanstack/react-start/rsc' import { Footer } from '~/components/Footer' import { seo } from '~/utils/seo' import { useCapabilities } from '~/hooks/useCapabilities' import { isAdmin } from '~/db/types' import * as v from 'valibot' import { format, formatDistanceToNow } from '~/utils/dates' -import { Markdown } from '~/components/markdown' +import { renderMarkdownToJsx } from '~/utils/markdown' import { libraries } from '~/libraries' import { partners } from '~/utils/partners' import { twMerge } from 'tailwind-merge' @@ -15,6 +17,18 @@ import { ArrowLeft } from 'lucide-react' const searchSchema = v.object({}) +// Server function that renders markdown content as RSC +const renderFeedContent = createServerFn({ method: 'GET' }) + .inputValidator((content: string) => content) + .handler(async ({ data: content }) => { + const { content: jsxContent } = await renderMarkdownToJsx(content) + return renderServerComponent( +
+ {jsxContent} +
, + ) + }) + export const Route = createFileRoute('/feed/$id')({ staleTime: 1000 * 60 * 5, // 5 minutes loader: async ({ params, context: { queryClient } }) => { @@ -29,12 +43,16 @@ export const Route = createFileRoute('/feed/$id')({ throw notFound() } + // Render markdown content as RSC + const ContentRsc = await renderFeedContent({ data: entry.content }) + // Check if entry is visible (unless admin) // Note: We'll check capabilities client-side since they depend on auth // For SSR, we'll allow the entry to load and handle visibility client-side return { entry, + ContentRsc, } }, headers: () => ({ @@ -88,7 +106,7 @@ export const Route = createFileRoute('/feed/$id')({ }) function FeedItemPage() { - const { entry } = Route.useLoaderData() + const { entry, ContentRsc } = Route.useLoaderData() const capabilities = useCapabilities() // Show not found if entry isn't visible (unless admin) @@ -118,10 +136,16 @@ function FeedItemPage() { ) } - return + return } -function FeedEntryView({ entry }: { entry: FeedEntry }) { +function FeedEntryView({ + entry, + ContentRsc, +}: { + entry: FeedEntry + ContentRsc: React.ReactNode +}) { // Get library info const entryLibraries = entry.libraryIds .map((id) => libraries.find((lib) => lib.id === id)) @@ -366,9 +390,7 @@ function FeedEntryView({ entry }: { entry: FeedEntry }) { )} {/* Content */} -
- -
+ {ContentRsc} {/* External Link */} {externalLink && ( diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 4503370ee..dfab6739e 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -18,9 +18,9 @@ import { useToast } from '~/components/ToastProvider' import { formatAuthors, getPublishedPosts } from '~/utils/blog' import { format } from '~/utils/dates' import { SimpleMarkdown } from '~/components/SimpleMarkdown' +import { renderMarkdownAsync } from '~/utils/markdown' import { NetlifyImage } from '~/components/NetlifyImage' import { createServerFn } from '@tanstack/react-start' -import { setResponseHeaders } from '@tanstack/react-start/server' import { AdGate } from '~/contexts/AdsContext' import { GamHeader } from '~/components/Gam' import { TrustedByMarquee } from '~/components/TrustedByMarquee' @@ -57,31 +57,29 @@ type BlogFrontMatter = { slug: string title: string published: string - excerpt: string | undefined + excerptHtml: string | undefined authors: string[] } const fetchRecentPosts = createServerFn({ method: 'GET' }).handler( async (): Promise => { - setResponseHeaders( - new Headers({ - 'Cache-Control': 'public, max-age=0, must-revalidate', - 'Netlify-CDN-Cache-Control': - 'public, max-age=300, durable, stale-while-revalidate=300', - }), + const { setCacheHeaders } = await import('~/utils/headers.server') + setCacheHeaders() + + const posts = getPublishedPosts().slice(0, 3) + const postsWithMarkup = await Promise.all( + posts.map(async (post) => ({ + slug: post.slug, + title: post.title, + published: post.published, + excerptHtml: post.excerpt + ? (await renderMarkdownAsync(post.excerpt)).markup + : undefined, + authors: post.authors, + })), ) - return getPublishedPosts() - .slice(0, 3) - .map((post) => { - return { - slug: post.slug, - title: post.title, - published: post.published, - excerpt: post.excerpt, - authors: post.authors, - } - }) + return postsWithMarkup }, ) @@ -393,7 +391,7 @@ function Index() {
{recentPosts.map( - ({ slug, title, published, excerpt, authors }) => { + ({ slug, title, published, excerptHtml, authors }) => { return (
- {excerpt && ( + {excerptHtml && (
- +
)}
diff --git a/src/styles/app.css b/src/styles/app.css index 5d87f124d..516253bad 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -669,14 +669,49 @@ pre .logger.log-log svg { margin-right: 9px; } -html:not(.dark) .shiki.vitesse-dark { +/* Hide single-theme Shiki blocks based on current theme. + These rules should NOT match dual-theme blocks (which have .shiki-themes class) */ +html:not(.dark) .shiki.vitesse-dark:not(.shiki-themes) { display: none; } -html.dark .shiki.github-light { +html.dark .shiki.github-light:not(.shiki-themes) { display: none; } +/* Shiki CSS variable theme support (server-side rehype-shiki with defaultColor: false) */ +.shiki.shiki-themes, +.shiki[style*='--shiki-'] { + /* Light mode: use --shiki-light values */ + background-color: var(--shiki-light-bg, #fff) !important; + color: var(--shiki-light, inherit); +} + +.shiki.shiki-themes span[style*='--shiki-'], +.shiki[style*='--shiki-'] span[style*='--shiki-'] { + color: var(--shiki-light); + background-color: var(--shiki-light-bg); + font-style: var(--shiki-light-font-style); + font-weight: var(--shiki-light-font-weight); + text-decoration: var(--shiki-light-text-decoration); +} + +html.dark .shiki.shiki-themes, +html.dark .shiki[style*='--shiki-'] { + /* Dark mode: use --shiki-dark values */ + background-color: var(--shiki-dark-bg, #0a0a0a) !important; + color: var(--shiki-dark, inherit); +} + +html.dark .shiki.shiki-themes span[style*='--shiki-'], +html.dark .shiki[style*='--shiki-'] span[style*='--shiki-'] { + color: var(--shiki-dark); + background-color: var(--shiki-dark-bg); + font-style: var(--shiki-dark-font-style); + font-weight: var(--shiki-dark-font-weight); + text-decoration: var(--shiki-dark-text-decoration); +} + /* Improve comment contrast in dark mode */ html.dark .shiki.vitesse-dark .token.comment, html.dark .shiki.vitesse-dark span[style*='color:#565F89'], diff --git a/src/utils/docs.ts b/src/utils/docs.tsx similarity index 81% rename from src/utils/docs.ts rename to src/utils/docs.tsx index 7a7b177a4..b116c05bf 100644 --- a/src/utils/docs.ts +++ b/src/utils/docs.tsx @@ -6,8 +6,11 @@ import { import removeMarkdown from 'remove-markdown' import { notFound } from '@tanstack/react-router' import { createServerFn } from '@tanstack/react-start' +import { renderServerComponent } from '@tanstack/react-start/rsc' import * as v from 'valibot' -import { setResponseHeader } from '@tanstack/react-start/server' +import { setResponseHeader } from '~/utils/headers.server' +import { renderMarkdownToJsx } from '~/utils/markdown' +import { DocContent } from '~/components/markdown/DocContent' export const loadDocs = async ({ repo, @@ -51,6 +54,14 @@ export const fetchDocs = createServerFn({ method: 'GET' }) const frontMatter = extractFrontMatter(file) const description = removeMarkdown(frontMatter.excerpt ?? '') + // Render markdown directly to JSX on the server + const { content, headings } = await renderMarkdownToJsx(frontMatter.content) + + // Wrap in RSC stream for client hydration + const contentRsc = await renderServerComponent( + {content}, + ) + // Cache for 5 minutes on shared cache // Revalidate in the background setResponseHeader('Cache-Control', 'public, max-age=0, must-revalidate') @@ -63,7 +74,9 @@ export const fetchDocs = createServerFn({ method: 'GET' }) title: frontMatter.data?.title, description, filePath, - content: frontMatter.content, + content: frontMatter.content, // Raw markdown content for .md routes + contentRsc, + headings, frontmatter: frontMatter.data, } }) diff --git a/src/utils/headers.server.ts b/src/utils/headers.server.ts new file mode 100644 index 000000000..829ba7d89 --- /dev/null +++ b/src/utils/headers.server.ts @@ -0,0 +1,22 @@ +import { + setResponseHeaders as _setResponseHeaders, + setResponseHeader as _setResponseHeader, +} from '@tanstack/react-start/server' + +export const setResponseHeaders = _setResponseHeaders +export const setResponseHeader = _setResponseHeader + +export function setCacheHeaders(opts?: { + maxAge?: number + cdnMaxAge?: number + staleWhileRevalidate?: number +}) { + const { maxAge = 0, cdnMaxAge = 300, staleWhileRevalidate = 300 } = opts ?? {} + + setResponseHeaders( + new Headers({ + 'Cache-Control': `public, max-age=${maxAge}, must-revalidate`, + 'Netlify-CDN-Cache-Control': `public, max-age=${cdnMaxAge}, durable, stale-while-revalidate=${staleWhileRevalidate}`, + }), + ) +} diff --git a/src/utils/markdown/index.ts b/src/utils/markdown/index.ts index 0aaf957fc..d683bdd05 100644 --- a/src/utils/markdown/index.ts +++ b/src/utils/markdown/index.ts @@ -1 +1,9 @@ -export { renderMarkdown } from './processor' +export { + renderMarkdown, + renderMarkdownAsync, + renderMarkdownToJsx, + highlightCode, + type MarkdownJsxResult, +} from './processor' + +export type { MarkdownHeading } from './types' diff --git a/src/utils/markdown/processor.ts b/src/utils/markdown/processor.ts deleted file mode 100644 index 9ea90c858..000000000 --- a/src/utils/markdown/processor.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { unified } from 'unified' -import remarkParse from 'remark-parse' -import remarkGfm from 'remark-gfm' -import remarkRehype from 'remark-rehype' -import rehypeCallouts from 'rehype-callouts' -import rehypeRaw from 'rehype-raw' -import rehypeSlug from 'rehype-slug' -import rehypeAutolinkHeadings from 'rehype-autolink-headings' -import rehypeStringify from 'rehype-stringify' - -import { - rehypeCollectHeadings, - rehypeParseCommentComponents, - rehypeTransformCommentComponents, - rehypeTransformFrameworkComponents, - type MarkdownHeading, -} from '~/utils/markdown/plugins' -import { extractCodeMeta } from '~/utils/markdown/plugins/extractCodeMeta' - -export type { MarkdownHeading } from '~/utils/markdown/plugins' - -export type MarkdownRenderResult = { - markup: string - headings: MarkdownHeading[] -} - -export function renderMarkdown(content: string): MarkdownRenderResult { - const headings: MarkdownHeading[] = [] - - const processor = unified() - .use(remarkParse) - .use(remarkGfm) - .use(remarkRehype, { allowDangerousHtml: true }) - .use(extractCodeMeta) - .use(rehypeRaw) - .use(rehypeParseCommentComponents) - .use(rehypeCallouts, { - theme: 'github', - props: { - containerProps: (_node: any, type: string) => ({ - className: `markdown-alert markdown-alert-${type}`, - }), - titleIconProps: () => ({ - className: 'octicon octicon-info mr-2', - }), - titleProps: () => ({ - className: 'markdown-alert-title', - }), - titleTextProps: () => ({ - className: 'markdown-alert-title', - }), - contentProps: () => ({ - className: 'markdown-alert-content', - }), - }, - } as any) - .use(rehypeSlug) - .use(rehypeTransformFrameworkComponents) - .use(rehypeTransformCommentComponents) - .use(rehypeAutolinkHeadings, { - behavior: 'wrap', - properties: { - className: ['anchor-heading'], - }, - }) - .use(() => rehypeCollectHeadings(headings)) - .use(rehypeStringify) - - const file = processor.processSync(content) - - return { - markup: String(file), - headings, - } -} diff --git a/src/utils/markdown/processor.tsx b/src/utils/markdown/processor.tsx new file mode 100644 index 000000000..ba8677d0d --- /dev/null +++ b/src/utils/markdown/processor.tsx @@ -0,0 +1,432 @@ +import * as React from 'react' +import { unified } from 'unified' +import remarkParse from 'remark-parse' +import remarkGfm from 'remark-gfm' +import remarkRehype from 'remark-rehype' +import rehypeCallouts from 'rehype-callouts' +import rehypeRaw from 'rehype-raw' +import rehypeSlug from 'rehype-slug' +import rehypeAutolinkHeadings from 'rehype-autolink-headings' +import rehypeStringify from 'rehype-stringify' +import rehypeReact from 'rehype-react' +import * as jsxRuntime from 'react/jsx-runtime' +import rehypeShiki from '@shikijs/rehype' +import { transformerNotationDiff } from '@shikijs/transformers' +import type { RehypeShikiOptions } from '@shikijs/rehype' +import { createHighlighter, type Highlighter } from 'shiki' + +import { + rehypeCollectHeadings, + rehypeParseCommentComponents, + rehypeTransformCommentComponents, + rehypeTransformFrameworkComponents, + type MarkdownHeading, +} from '~/utils/markdown/plugins' +import { extractCodeMeta } from '~/utils/markdown/plugins/extractCodeMeta' + +// Import markdown components for JSX rendering +import { MarkdownLink } from '~/components/markdown/MarkdownLink' +import { CodeBlock } from '~/components/markdown/CodeBlock' +import { InlineCode, MarkdownImg } from '~/ui' +import { + MdCommentComponent, + MdTabPanel, + MdFrameworkPanel, +} from '~/components/markdown/MdComponents' + +export type { MarkdownHeading } from '~/utils/markdown/plugins' + +export type MarkdownRenderResult = { + markup: string + headings: MarkdownHeading[] +} + +export type MarkdownJsxResult = { + content: React.ReactNode + headings: MarkdownHeading[] +} + +// Language aliases to normalize common variations +const LANG_ALIASES: Record = { + ts: 'typescript', + js: 'javascript', + sh: 'bash', + shell: 'bash', + console: 'bash', + zsh: 'bash', + cmd: 'bash', + md: 'markdown', + txt: 'text', + text: 'text', + plaintext: 'text', + yml: 'yaml', + json5: 'jsonc', + eslintrc: 'jsonc', +} + +const shikiOptions: RehypeShikiOptions = { + themes: { + light: 'github-light', + dark: 'vitesse-dark', + }, + defaultColor: false, + cssVariablePrefix: '--shiki-', + transformers: [transformerNotationDiff()], + defaultLanguage: 'typescript', + langs: [ + 'typescript', + 'javascript', + 'tsx', + 'jsx', + 'bash', + 'json', + 'html', + 'css', + 'markdown', + 'toml', + 'yaml', + 'sql', + 'diff', + 'vue', + 'svelte', + 'scss', + 'jsonc', + 'vue-html', + 'angular-html', + 'angular-ts', + ], + langAlias: LANG_ALIASES, + // Handle unknown languages gracefully + onError: (error) => { + console.warn('Shiki highlighting error:', error) + }, +} + +export async function renderMarkdownAsync( + content: string, +): Promise { + const headings: MarkdownHeading[] = [] + + const processor = unified() + .use(remarkParse) + .use(remarkGfm) + .use(remarkRehype, { allowDangerousHtml: true }) + .use(extractCodeMeta) + .use(rehypeRaw) + .use(rehypeParseCommentComponents) + .use(rehypeCallouts, { + theme: 'github', + props: { + containerProps: (_node: any, type: string) => ({ + className: `markdown-alert markdown-alert-${type}`, + }), + titleIconProps: () => ({ + className: 'octicon octicon-info mr-2', + }), + titleProps: () => ({ + className: 'markdown-alert-title', + }), + titleTextProps: () => ({ + className: 'markdown-alert-title', + }), + contentProps: () => ({ + className: 'markdown-alert-content', + }), + }, + } as any) + .use(rehypeShiki, shikiOptions) + .use(rehypeSlug) + .use(rehypeTransformFrameworkComponents) + .use(rehypeTransformCommentComponents) + .use(rehypeAutolinkHeadings, { + behavior: 'wrap', + properties: { + className: ['anchor-heading'], + }, + }) + .use(() => rehypeCollectHeadings(headings)) + .use(rehypeStringify) + + const file = await processor.process(content) + + return { + markup: String(file), + headings, + } +} + +// Synchronous version for backwards compatibility (doesn't include syntax highlighting) +export function renderMarkdown(content: string): MarkdownRenderResult { + const headings: MarkdownHeading[] = [] + + const processor = unified() + .use(remarkParse) + .use(remarkGfm) + .use(remarkRehype, { allowDangerousHtml: true }) + .use(extractCodeMeta) + .use(rehypeRaw) + .use(rehypeParseCommentComponents) + .use(rehypeCallouts, { + theme: 'github', + props: { + containerProps: (_node: any, type: string) => ({ + className: `markdown-alert markdown-alert-${type}`, + }), + titleIconProps: () => ({ + className: 'octicon octicon-info mr-2', + }), + titleProps: () => ({ + className: 'markdown-alert-title', + }), + titleTextProps: () => ({ + className: 'markdown-alert-title', + }), + contentProps: () => ({ + className: 'markdown-alert-content', + }), + }, + } as any) + .use(rehypeSlug) + .use(rehypeTransformFrameworkComponents) + .use(rehypeTransformCommentComponents) + .use(rehypeAutolinkHeadings, { + behavior: 'wrap', + properties: { + className: ['anchor-heading'], + }, + }) + .use(() => rehypeCollectHeadings(headings)) + .use(rehypeStringify) + + const file = processor.processSync(content) + + return { + markup: String(file), + headings, + } +} + +// Lazy-loaded highlighter singleton for standalone code highlighting +let highlighterPromise: Promise | null = null + +const SUPPORTED_LANGS = [ + 'typescript', + 'javascript', + 'tsx', + 'jsx', + 'bash', + 'json', + 'html', + 'css', + 'markdown', + 'toml', + 'yaml', + 'sql', + 'diff', + 'vue', + 'svelte', + 'scss', + 'jsonc', + 'vue-html', + 'angular-html', + 'angular-ts', + 'text', +] as const + +async function getHighlighter(): Promise { + if (!highlighterPromise) { + highlighterPromise = createHighlighter({ + themes: ['github-light', 'vitesse-dark'], + langs: [...SUPPORTED_LANGS], + }) + } + return highlighterPromise +} + +/** + * Highlight code with Shiki (server-side only). + * Returns HTML string with dual-theme CSS variables for light/dark mode. + */ +export async function highlightCode( + code: string, + lang: string, +): Promise { + const highlighter = await getHighlighter() + + // Normalize language alias + const normalizedLang = LANG_ALIASES[lang] || lang + + // Check if language is supported, fallback to text + const loadedLangs = highlighter.getLoadedLanguages() + const effectiveLang = loadedLangs.includes(normalizedLang as any) + ? normalizedLang + : 'text' + + const html = highlighter.codeToHtml(code.trimEnd(), { + lang: effectiveLang, + themes: { + light: 'github-light', + dark: 'vitesse-dark', + }, + defaultColor: false, + cssVariablePrefix: '--shiki-', + transformers: [transformerNotationDiff()], + }) + + return html +} + +// Custom heading component - rehype-autolink-headings already wraps with , +// so we just render the heading element with proper styling +function createHeadingComponent( + level: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6', +) { + const HeadingComponent = ({ + id, + children, + className, + ...props + }: React.HTMLAttributes) => { + const Tag = level + return ( + + {children} + + ) + } + HeadingComponent.displayName = `Heading${level.toUpperCase()}` + return HeadingComponent +} + +// Iframe component for markdown +function MarkdownIframe(props: React.IframeHTMLAttributes) { + return