jamdesk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +323 -0
- package/bin/jamdesk.js +76 -0
- package/dist/__tests__/integration/deprecated-components.integration.test.d.ts +8 -0
- package/dist/__tests__/integration/deprecated-components.integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration/deprecated-components.integration.test.js +165 -0
- package/dist/__tests__/integration/deprecated-components.integration.test.js.map +1 -0
- package/dist/__tests__/integration/migrate.integration.test.d.ts +2 -0
- package/dist/__tests__/integration/migrate.integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration/migrate.integration.test.js +64 -0
- package/dist/__tests__/integration/migrate.integration.test.js.map +1 -0
- package/dist/__tests__/integration/prepublish.integration.test.d.ts +2 -0
- package/dist/__tests__/integration/prepublish.integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration/prepublish.integration.test.js +27 -0
- package/dist/__tests__/integration/prepublish.integration.test.js.map +1 -0
- package/dist/__tests__/integration/validate.integration.test.d.ts +2 -0
- package/dist/__tests__/integration/validate.integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration/validate.integration.test.js +56 -0
- package/dist/__tests__/integration/validate.integration.test.js.map +1 -0
- package/dist/__tests__/unit/deploy-templates.test.d.ts +2 -0
- package/dist/__tests__/unit/deploy-templates.test.d.ts.map +1 -0
- package/dist/__tests__/unit/deploy-templates.test.js +124 -0
- package/dist/__tests__/unit/deploy-templates.test.js.map +1 -0
- package/dist/__tests__/unit/deprecated-components-sync.test.d.ts +2 -0
- package/dist/__tests__/unit/deprecated-components-sync.test.d.ts.map +1 -0
- package/dist/__tests__/unit/deprecated-components-sync.test.js +69 -0
- package/dist/__tests__/unit/deprecated-components-sync.test.js.map +1 -0
- package/dist/__tests__/unit/deps-sync.test.d.ts +14 -0
- package/dist/__tests__/unit/deps-sync.test.d.ts.map +1 -0
- package/dist/__tests__/unit/deps-sync.test.js +166 -0
- package/dist/__tests__/unit/deps-sync.test.js.map +1 -0
- package/dist/__tests__/unit/docs-config.test.d.ts +2 -0
- package/dist/__tests__/unit/docs-config.test.d.ts.map +1 -0
- package/dist/__tests__/unit/docs-config.test.js +288 -0
- package/dist/__tests__/unit/docs-config.test.js.map +1 -0
- package/dist/__tests__/unit/errors.test.d.ts +2 -0
- package/dist/__tests__/unit/errors.test.d.ts.map +1 -0
- package/dist/__tests__/unit/errors.test.js +27 -0
- package/dist/__tests__/unit/errors.test.js.map +1 -0
- package/dist/__tests__/unit/extract-hooks.test.d.ts +5 -0
- package/dist/__tests__/unit/extract-hooks.test.d.ts.map +1 -0
- package/dist/__tests__/unit/extract-hooks.test.js +205 -0
- package/dist/__tests__/unit/extract-hooks.test.js.map +1 -0
- package/dist/__tests__/unit/frontmatter-sync.test.d.ts +8 -0
- package/dist/__tests__/unit/frontmatter-sync.test.d.ts.map +1 -0
- package/dist/__tests__/unit/frontmatter-sync.test.js +26 -0
- package/dist/__tests__/unit/frontmatter-sync.test.js.map +1 -0
- package/dist/__tests__/unit/mdx-validator.test.d.ts +2 -0
- package/dist/__tests__/unit/mdx-validator.test.d.ts.map +1 -0
- package/dist/__tests__/unit/mdx-validator.test.js +264 -0
- package/dist/__tests__/unit/mdx-validator.test.js.map +1 -0
- package/dist/__tests__/unit/migrate-convert.test.d.ts +2 -0
- package/dist/__tests__/unit/migrate-convert.test.d.ts.map +1 -0
- package/dist/__tests__/unit/migrate-convert.test.js +297 -0
- package/dist/__tests__/unit/migrate-convert.test.js.map +1 -0
- package/dist/__tests__/unit/migrate-detect.test.d.ts +2 -0
- package/dist/__tests__/unit/migrate-detect.test.d.ts.map +1 -0
- package/dist/__tests__/unit/migrate-detect.test.js +35 -0
- package/dist/__tests__/unit/migrate-detect.test.js.map +1 -0
- package/dist/__tests__/unit/migrate-mdx.test.d.ts +2 -0
- package/dist/__tests__/unit/migrate-mdx.test.d.ts.map +1 -0
- package/dist/__tests__/unit/migrate-mdx.test.js +158 -0
- package/dist/__tests__/unit/migrate-mdx.test.js.map +1 -0
- package/dist/__tests__/unit/openapi.test.d.ts +2 -0
- package/dist/__tests__/unit/openapi.test.d.ts.map +1 -0
- package/dist/__tests__/unit/openapi.test.js +52 -0
- package/dist/__tests__/unit/openapi.test.js.map +1 -0
- package/dist/__tests__/unit/package-config.test.d.ts +2 -0
- package/dist/__tests__/unit/package-config.test.d.ts.map +1 -0
- package/dist/__tests__/unit/package-config.test.js +63 -0
- package/dist/__tests__/unit/package-config.test.js.map +1 -0
- package/dist/__tests__/unit/port.test.d.ts +2 -0
- package/dist/__tests__/unit/port.test.d.ts.map +1 -0
- package/dist/__tests__/unit/port.test.js +20 -0
- package/dist/__tests__/unit/port.test.js.map +1 -0
- package/dist/__tests__/unit/vendored-sync.test.d.ts +14 -0
- package/dist/__tests__/unit/vendored-sync.test.d.ts.map +1 -0
- package/dist/__tests__/unit/vendored-sync.test.js +90 -0
- package/dist/__tests__/unit/vendored-sync.test.js.map +1 -0
- package/dist/commands/broken-links.d.ts +11 -0
- package/dist/commands/broken-links.d.ts.map +1 -0
- package/dist/commands/broken-links.js +95 -0
- package/dist/commands/broken-links.js.map +1 -0
- package/dist/commands/clean.d.ts +7 -0
- package/dist/commands/clean.d.ts.map +1 -0
- package/dist/commands/clean.js +59 -0
- package/dist/commands/clean.js.map +1 -0
- package/dist/commands/deploy/cloudflare.d.ts +12 -0
- package/dist/commands/deploy/cloudflare.d.ts.map +1 -0
- package/dist/commands/deploy/cloudflare.js +409 -0
- package/dist/commands/deploy/cloudflare.js.map +1 -0
- package/dist/commands/deploy/templates.d.ts +23 -0
- package/dist/commands/deploy/templates.d.ts.map +1 -0
- package/dist/commands/deploy/templates.js +179 -0
- package/dist/commands/deploy/templates.js.map +1 -0
- package/dist/commands/deploy/types.d.ts +19 -0
- package/dist/commands/deploy/types.d.ts.map +1 -0
- package/dist/commands/deploy/types.js +5 -0
- package/dist/commands/deploy/types.js.map +1 -0
- package/dist/commands/dev.d.ts +14 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +817 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/doctor.d.ts +7 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +159 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +96 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/migrate/convert-mdx.d.ts +50 -0
- package/dist/commands/migrate/convert-mdx.d.ts.map +1 -0
- package/dist/commands/migrate/convert-mdx.js +108 -0
- package/dist/commands/migrate/convert-mdx.js.map +1 -0
- package/dist/commands/migrate/convert.d.ts +80 -0
- package/dist/commands/migrate/convert.d.ts.map +1 -0
- package/dist/commands/migrate/convert.js +158 -0
- package/dist/commands/migrate/convert.js.map +1 -0
- package/dist/commands/migrate/detect.d.ts +31 -0
- package/dist/commands/migrate/detect.d.ts.map +1 -0
- package/dist/commands/migrate/detect.js +62 -0
- package/dist/commands/migrate/detect.js.map +1 -0
- package/dist/commands/migrate/extract-hooks.d.ts +71 -0
- package/dist/commands/migrate/extract-hooks.d.ts.map +1 -0
- package/dist/commands/migrate/extract-hooks.js +473 -0
- package/dist/commands/migrate/extract-hooks.js.map +1 -0
- package/dist/commands/migrate/index.d.ts +17 -0
- package/dist/commands/migrate/index.d.ts.map +1 -0
- package/dist/commands/migrate/index.js +282 -0
- package/dist/commands/migrate/index.js.map +1 -0
- package/dist/commands/migrate/prompts.d.ts +22 -0
- package/dist/commands/migrate/prompts.d.ts.map +1 -0
- package/dist/commands/migrate/prompts.js +67 -0
- package/dist/commands/migrate/prompts.js.map +1 -0
- package/dist/commands/migrate/types.d.ts +22 -0
- package/dist/commands/migrate/types.d.ts.map +1 -0
- package/dist/commands/migrate/types.js +26 -0
- package/dist/commands/migrate/types.js.map +1 -0
- package/dist/commands/openapi-check.d.ts +11 -0
- package/dist/commands/openapi-check.d.ts.map +1 -0
- package/dist/commands/openapi-check.js +88 -0
- package/dist/commands/openapi-check.js.map +1 -0
- package/dist/commands/rename.d.ts +10 -0
- package/dist/commands/rename.d.ts.map +1 -0
- package/dist/commands/rename.js +125 -0
- package/dist/commands/rename.js.map +1 -0
- package/dist/commands/update.d.ts +10 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +57 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate.d.ts +12 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +163 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +334 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +7 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +18 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/deprecated-components.d.ts +72 -0
- package/dist/lib/deprecated-components.d.ts.map +1 -0
- package/dist/lib/deprecated-components.js +138 -0
- package/dist/lib/deprecated-components.js.map +1 -0
- package/dist/lib/deps.d.ts +17 -0
- package/dist/lib/deps.d.ts.map +1 -0
- package/dist/lib/deps.js +186 -0
- package/dist/lib/deps.js.map +1 -0
- package/dist/lib/docs-config.d.ts +67 -0
- package/dist/lib/docs-config.d.ts.map +1 -0
- package/dist/lib/docs-config.js +294 -0
- package/dist/lib/docs-config.js.map +1 -0
- package/dist/lib/errors.d.ts +23 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +32 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/frontmatter-utils.d.ts +25 -0
- package/dist/lib/frontmatter-utils.d.ts.map +1 -0
- package/dist/lib/frontmatter-utils.js +64 -0
- package/dist/lib/frontmatter-utils.js.map +1 -0
- package/dist/lib/mdx-validator.d.ts +27 -0
- package/dist/lib/mdx-validator.d.ts.map +1 -0
- package/dist/lib/mdx-validator.js +148 -0
- package/dist/lib/mdx-validator.js.map +1 -0
- package/dist/lib/navigation-validator.d.ts +31 -0
- package/dist/lib/navigation-validator.d.ts.map +1 -0
- package/dist/lib/navigation-validator.js +75 -0
- package/dist/lib/navigation-validator.js.map +1 -0
- package/dist/lib/normalize-config.d.ts +57 -0
- package/dist/lib/normalize-config.d.ts.map +1 -0
- package/dist/lib/normalize-config.js +63 -0
- package/dist/lib/normalize-config.js.map +1 -0
- package/dist/lib/openapi/cache.d.ts +40 -0
- package/dist/lib/openapi/cache.d.ts.map +1 -0
- package/dist/lib/openapi/cache.js +76 -0
- package/dist/lib/openapi/cache.js.map +1 -0
- package/dist/lib/openapi/errors.d.ts +36 -0
- package/dist/lib/openapi/errors.d.ts.map +1 -0
- package/dist/lib/openapi/errors.js +162 -0
- package/dist/lib/openapi/errors.js.map +1 -0
- package/dist/lib/openapi/index.d.ts +10 -0
- package/dist/lib/openapi/index.d.ts.map +1 -0
- package/dist/lib/openapi/index.js +12 -0
- package/dist/lib/openapi/index.js.map +1 -0
- package/dist/lib/openapi/types.d.ts +198 -0
- package/dist/lib/openapi/types.d.ts.map +1 -0
- package/dist/lib/openapi/types.js +8 -0
- package/dist/lib/openapi/types.js.map +1 -0
- package/dist/lib/openapi/validator.d.ts +45 -0
- package/dist/lib/openapi/validator.d.ts.map +1 -0
- package/dist/lib/openapi/validator.js +128 -0
- package/dist/lib/openapi/validator.js.map +1 -0
- package/dist/lib/openapi.d.ts +7 -0
- package/dist/lib/openapi.d.ts.map +1 -0
- package/dist/lib/openapi.js +7 -0
- package/dist/lib/openapi.js.map +1 -0
- package/dist/lib/output.d.ts +14 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +19 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/path-security.d.ts +23 -0
- package/dist/lib/path-security.d.ts.map +1 -0
- package/dist/lib/path-security.js +35 -0
- package/dist/lib/path-security.js.map +1 -0
- package/dist/lib/port.d.ts +18 -0
- package/dist/lib/port.d.ts.map +1 -0
- package/dist/lib/port.js +65 -0
- package/dist/lib/port.js.map +1 -0
- package/dist/lib/spinner.d.ts +4 -0
- package/dist/lib/spinner.d.ts.map +1 -0
- package/dist/lib/spinner.js +16 -0
- package/dist/lib/spinner.js.map +1 -0
- package/dist/lib/version.d.ts +2 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/lib/version.js +49 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/utils/update-checker.d.ts +34 -0
- package/dist/utils/update-checker.d.ts.map +1 -0
- package/dist/utils/update-checker.js +142 -0
- package/dist/utils/update-checker.js.map +1 -0
- package/package.json +125 -0
- package/templates/docs.json +11 -0
- package/templates/introduction.mdx +19 -0
- package/templates/quickstart.mdx +20 -0
- package/vendored/app/[[...slug]]/error.tsx +103 -0
- package/vendored/app/[[...slug]]/page.tsx +690 -0
- package/vendored/app/api/assets/[...path]/route.ts +78 -0
- package/vendored/app/api/ev/route.ts +61 -0
- package/vendored/app/api/isr-health/route.ts +66 -0
- package/vendored/app/api/mcp/[project]/route.ts +435 -0
- package/vendored/app/api/og/route.tsx +167 -0
- package/vendored/app/api/r2/[project]/[...path]/route.ts +214 -0
- package/vendored/app/api/revalidate/route.ts +76 -0
- package/vendored/app/globals.css +37 -0
- package/vendored/app/layout.tsx +571 -0
- package/vendored/app/not-found.tsx +47 -0
- package/vendored/components/CodeBlockCopyButton.tsx +146 -0
- package/vendored/components/HeaderLinkCopy.tsx +135 -0
- package/vendored/components/errors/NotFoundContent.tsx +147 -0
- package/vendored/components/layout/LayoutWrapper.tsx +128 -0
- package/vendored/components/mdx/Accordion.tsx +91 -0
- package/vendored/components/mdx/ApiCodePanel.tsx +51 -0
- package/vendored/components/mdx/ApiEndpoint.tsx +104 -0
- package/vendored/components/mdx/ApiPage.tsx +379 -0
- package/vendored/components/mdx/Badge.tsx +169 -0
- package/vendored/components/mdx/Callouts.tsx +140 -0
- package/vendored/components/mdx/Card.tsx +214 -0
- package/vendored/components/mdx/CodeGroup.tsx +136 -0
- package/vendored/components/mdx/Color.tsx +244 -0
- package/vendored/components/mdx/Columns.tsx +37 -0
- package/vendored/components/mdx/Expandable.tsx +37 -0
- package/vendored/components/mdx/Frame.tsx +51 -0
- package/vendored/components/mdx/Icon.tsx +132 -0
- package/vendored/components/mdx/Latex.tsx +75 -0
- package/vendored/components/mdx/MDXComponents.tsx +414 -0
- package/vendored/components/mdx/Mermaid.tsx +35 -0
- package/vendored/components/mdx/MermaidInner.tsx +342 -0
- package/vendored/components/mdx/OpenApiEndpoint.tsx +971 -0
- package/vendored/components/mdx/Panel.tsx +26 -0
- package/vendored/components/mdx/PanelWrapper.tsx +100 -0
- package/vendored/components/mdx/ParamField.tsx +75 -0
- package/vendored/components/mdx/RequestExample.tsx +91 -0
- package/vendored/components/mdx/ResponseExample.tsx +145 -0
- package/vendored/components/mdx/ResponseField.tsx +109 -0
- package/vendored/components/mdx/Steps.tsx +173 -0
- package/vendored/components/mdx/Table.tsx +352 -0
- package/vendored/components/mdx/Tabs.tsx +147 -0
- package/vendored/components/mdx/Tile.tsx +127 -0
- package/vendored/components/mdx/Tooltip.tsx +111 -0
- package/vendored/components/mdx/Tree.tsx +484 -0
- package/vendored/components/mdx/Update.tsx +90 -0
- package/vendored/components/mdx/View.tsx +354 -0
- package/vendored/components/mdx/YouTube.tsx +35 -0
- package/vendored/components/mdx/ZoomableImage.tsx +83 -0
- package/vendored/components/navigation/Breadcrumb.tsx +241 -0
- package/vendored/components/navigation/DefaultLogo.tsx +81 -0
- package/vendored/components/navigation/Header.tsx +512 -0
- package/vendored/components/navigation/LanguageSelector.tsx +249 -0
- package/vendored/components/navigation/PageNavigation.tsx +174 -0
- package/vendored/components/navigation/Sidebar.tsx +713 -0
- package/vendored/components/navigation/SocialFooter.tsx +186 -0
- package/vendored/components/navigation/TableOfContents.tsx +435 -0
- package/vendored/components/navigation/TabsNav.tsx +182 -0
- package/vendored/components/search/LazySearchModal.tsx +19 -0
- package/vendored/components/search/SearchModal.tsx +573 -0
- package/vendored/components/snippets/ProjectSnippets.tsx +4 -0
- package/vendored/components/theme/ThemeProvider.tsx +31 -0
- package/vendored/components/theme/ThemeToggle.tsx +134 -0
- package/vendored/components/ui/CodePanel.tsx +517 -0
- package/vendored/components/ui/CodePanelModal.tsx +342 -0
- package/vendored/contexts/TabSyncContext.tsx +30 -0
- package/vendored/hooks/useFocusTrap.ts +42 -0
- package/vendored/hooks/useHashNavigation.ts +39 -0
- package/vendored/hooks/useShikiHighlight.ts +101 -0
- package/vendored/lib/analytics-client.ts +77 -0
- package/vendored/lib/build/cache.ts +138 -0
- package/vendored/lib/build/error-parser.ts +690 -0
- package/vendored/lib/build/estimation.ts +113 -0
- package/vendored/lib/build/index.ts +17 -0
- package/vendored/lib/build/page-file-map.ts +48 -0
- package/vendored/lib/build/r2-upload.ts +179 -0
- package/vendored/lib/cache-keys.ts +117 -0
- package/vendored/lib/code-utils.ts +42 -0
- package/vendored/lib/content-loader.ts +176 -0
- package/vendored/lib/deprecated-components.ts +185 -0
- package/vendored/lib/docs-isr.ts +180 -0
- package/vendored/lib/docs-types.ts +874 -0
- package/vendored/lib/docs.ts +203 -0
- package/vendored/lib/domain-helpers.ts +107 -0
- package/vendored/lib/email-notifier.ts +102 -0
- package/vendored/lib/email-templates/build-failure.tsx +193 -0
- package/vendored/lib/email-templates/components/base-layout.tsx +150 -0
- package/vendored/lib/email-templates/components/error-box.tsx +88 -0
- package/vendored/lib/email-templates/components/info-row.tsx +63 -0
- package/vendored/lib/email-templates/index.ts +13 -0
- package/vendored/lib/empty-polyfill.js +3 -0
- package/vendored/lib/extract-highlights.ts +124 -0
- package/vendored/lib/fonts.ts +227 -0
- package/vendored/lib/frontmatter-utils.ts +77 -0
- package/vendored/lib/fs-utils.ts +20 -0
- package/vendored/lib/git-utils.ts +87 -0
- package/vendored/lib/health-checks.ts +224 -0
- package/vendored/lib/icon-utils.ts +492 -0
- package/vendored/lib/infer-page-type.ts +14 -0
- package/vendored/lib/isr-build-executor.ts +185 -0
- package/vendored/lib/language-icons.ts +152 -0
- package/vendored/lib/language-utils.ts +338 -0
- package/vendored/lib/latex-config.ts +64 -0
- package/vendored/lib/link-prefix-context.tsx +32 -0
- package/vendored/lib/logger.ts +63 -0
- package/vendored/lib/mcp-search.ts +255 -0
- package/vendored/lib/mdx-inline-components.ts +155 -0
- package/vendored/lib/mdx.ts +100 -0
- package/vendored/lib/middleware-helpers.ts +519 -0
- package/vendored/lib/navigation-resolver.ts +621 -0
- package/vendored/lib/navigation-utils.ts +103 -0
- package/vendored/lib/normalize-config.ts +94 -0
- package/vendored/lib/openapi/cache.ts +92 -0
- package/vendored/lib/openapi/code-examples.ts +389 -0
- package/vendored/lib/openapi/errors.ts +253 -0
- package/vendored/lib/openapi/generator.ts +230 -0
- package/vendored/lib/openapi/index.ts +84 -0
- package/vendored/lib/openapi/parser.ts +474 -0
- package/vendored/lib/openapi/types.ts +232 -0
- package/vendored/lib/openapi/validator.ts +156 -0
- package/vendored/lib/openapi-isr.ts +121 -0
- package/vendored/lib/page-isr-helpers.ts +137 -0
- package/vendored/lib/path-safety.ts +130 -0
- package/vendored/lib/paths.ts +35 -0
- package/vendored/lib/preprocess-mdx.ts +951 -0
- package/vendored/lib/process-mdx-with-exports.ts +75 -0
- package/vendored/lib/project-resolver.ts +165 -0
- package/vendored/lib/r2-content.ts +60 -0
- package/vendored/lib/r2-manifest.ts +84 -0
- package/vendored/lib/recent-searches.ts +41 -0
- package/vendored/lib/recma-compound-components.ts +84 -0
- package/vendored/lib/redirect-compiler.ts +160 -0
- package/vendored/lib/redirect-matcher.ts +296 -0
- package/vendored/lib/redis.ts +23 -0
- package/vendored/lib/rehype-class-to-classname.ts +31 -0
- package/vendored/lib/rehype-code-meta.ts +275 -0
- package/vendored/lib/rehype-nozoom-to-data.ts +45 -0
- package/vendored/lib/remark-extract-exports.ts +104 -0
- package/vendored/lib/resilience.ts +260 -0
- package/vendored/lib/revalidation-helpers.ts +200 -0
- package/vendored/lib/revalidation-trigger.ts +150 -0
- package/vendored/lib/screenshot-capture.ts +229 -0
- package/vendored/lib/search-client.ts +91 -0
- package/vendored/lib/search-suggestions.ts +38 -0
- package/vendored/lib/search.ts +158 -0
- package/vendored/lib/seo.ts +264 -0
- package/vendored/lib/shiki-client.ts +131 -0
- package/vendored/lib/shiki-config.ts +289 -0
- package/vendored/lib/shiki-css-theme.ts +46 -0
- package/vendored/lib/shiki-highlighter.ts +62 -0
- package/vendored/lib/shiki-transformers.ts +337 -0
- package/vendored/lib/slack-notifier.ts +248 -0
- package/vendored/lib/snippet-compiler-isr.ts +114 -0
- package/vendored/lib/snippet-loader-isr.ts +276 -0
- package/vendored/lib/static-artifacts.ts +375 -0
- package/vendored/lib/static-file-route.ts +72 -0
- package/vendored/lib/tracking-script.ts +19 -0
- package/vendored/lib/typography-config.ts +42 -0
- package/vendored/lib/validate-config.ts +268 -0
- package/vendored/next.config.js +45 -0
- package/vendored/postcss.config.js +6 -0
- package/vendored/schema/README.md +28 -0
- package/vendored/schema/docs-schema.json +4631 -0
- package/vendored/scripts/build-project.cjs +174 -0
- package/vendored/scripts/build-search-index.cjs +347 -0
- package/vendored/scripts/compile-snippets.cjs +488 -0
- package/vendored/scripts/copy-files.cjs +295 -0
- package/vendored/scripts/dev-project.cjs +534 -0
- package/vendored/scripts/enhance-navigation.cjs +354 -0
- package/vendored/scripts/validate-links.cjs +423 -0
- package/vendored/shared/constants.ts +6 -0
- package/vendored/shared/index.ts +19 -0
- package/vendored/shared/logger.ts +62 -0
- package/vendored/shared/memory-monitor.ts +190 -0
- package/vendored/shared/navigation-validator.ts +101 -0
- package/vendored/shared/path-security.ts +39 -0
- package/vendored/shared/status-reporter.ts +199 -0
- package/vendored/shared/timer.ts +51 -0
- package/vendored/shared/types.ts +102 -0
- package/vendored/tailwind.config.ts +39 -0
- package/vendored/themes/base.css +1311 -0
- package/vendored/themes/index.ts +119 -0
- package/vendored/themes/jam/variables.css +835 -0
- package/vendored/themes/nebula/variables.css +282 -0
- package/vendored/themes/pulsar/variables.css +1009 -0
- package/vendored/themes/types.ts +89 -0
- package/vendored/tsconfig.json +48 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redirect Handling Module
|
|
3
|
+
*
|
|
4
|
+
* Fetches and matches redirects from _redirects.json in R2.
|
|
5
|
+
* Uses Upstash Redis for caching with 5-minute TTL.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { redis } from './redis';
|
|
9
|
+
import { getFileBufferFromR2 } from './r2';
|
|
10
|
+
|
|
11
|
+
// Cache TTL in seconds (5 minutes)
|
|
12
|
+
const CACHE_TTL = 300;
|
|
13
|
+
|
|
14
|
+
// Redis cache key prefix
|
|
15
|
+
const CACHE_PREFIX = 'redirects:';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Redirect rule as stored in _redirects.json
|
|
19
|
+
*/
|
|
20
|
+
export interface RedirectRule {
|
|
21
|
+
source: string;
|
|
22
|
+
destination: string;
|
|
23
|
+
permanent: boolean;
|
|
24
|
+
pattern: string;
|
|
25
|
+
hasWildcard: boolean;
|
|
26
|
+
// Pre-compiled regex for performance (added at runtime, not stored in JSON)
|
|
27
|
+
_compiledRegex?: RegExp;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Redirects file structure
|
|
32
|
+
*/
|
|
33
|
+
interface RedirectsFile {
|
|
34
|
+
version: number;
|
|
35
|
+
generatedAt?: string;
|
|
36
|
+
redirects: RedirectRule[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Match result with resolved destination
|
|
41
|
+
*/
|
|
42
|
+
export interface RedirectMatch {
|
|
43
|
+
destination: string;
|
|
44
|
+
permanent: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Pre-compile regex patterns for wildcard rules.
|
|
49
|
+
* Mutates the rules array in place for performance.
|
|
50
|
+
*/
|
|
51
|
+
function compileRedirectPatterns(rules: RedirectRule[]): void {
|
|
52
|
+
for (const rule of rules) {
|
|
53
|
+
if (!rule.hasWildcard || !rule.pattern || rule._compiledRegex) continue;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
rule._compiledRegex = new RegExp(rule.pattern);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
console.error('[Redirects] Invalid regex pattern:', rule.pattern, e);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Fetch redirects for a project from R2 with Redis caching
|
|
65
|
+
* Returns null if no redirects file or error (fail open)
|
|
66
|
+
*/
|
|
67
|
+
export async function getRedirects(projectName: string): Promise<RedirectRule[] | null> {
|
|
68
|
+
const cacheKey = `${CACHE_PREFIX}${projectName}`;
|
|
69
|
+
|
|
70
|
+
// Try cache first (if Redis is available)
|
|
71
|
+
if (redis) {
|
|
72
|
+
try {
|
|
73
|
+
const cached = await redis.get<RedirectsFile>(cacheKey);
|
|
74
|
+
if (cached && cached.redirects) {
|
|
75
|
+
// Pre-compile regex patterns (not serialized in Redis)
|
|
76
|
+
compileRedirectPatterns(cached.redirects);
|
|
77
|
+
return cached.redirects;
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error('[Redirects] Redis cache read failed:', error);
|
|
81
|
+
// Continue to fetch from R2
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Fetch from R2
|
|
86
|
+
try {
|
|
87
|
+
const buffer = await getFileBufferFromR2(projectName, '_redirects.json');
|
|
88
|
+
if (!buffer) {
|
|
89
|
+
// No redirects file - cache the "no redirects" state to avoid repeated R2 lookups
|
|
90
|
+
if (redis) {
|
|
91
|
+
try {
|
|
92
|
+
await redis.set(cacheKey, { version: 1, redirects: [] }, { ex: CACHE_TTL });
|
|
93
|
+
} catch {
|
|
94
|
+
// Ignore cache write errors
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const content = buffer.toString('utf-8');
|
|
101
|
+
const data: RedirectsFile = JSON.parse(content);
|
|
102
|
+
|
|
103
|
+
if (!data.redirects || !Array.isArray(data.redirects)) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Pre-compile regex patterns for wildcard rules
|
|
108
|
+
compileRedirectPatterns(data.redirects);
|
|
109
|
+
|
|
110
|
+
// Cache in Redis
|
|
111
|
+
if (redis) {
|
|
112
|
+
try {
|
|
113
|
+
await redis.set(cacheKey, data, { ex: CACHE_TTL });
|
|
114
|
+
} catch {
|
|
115
|
+
// Ignore cache write errors
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return data.redirects;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error('[Redirects] Failed to fetch redirects for', projectName, error);
|
|
122
|
+
return null; // Fail open
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Normalize a path by stripping trailing slash (except for root)
|
|
128
|
+
*/
|
|
129
|
+
function normalizePath(pathname: string): string {
|
|
130
|
+
if (pathname === '/') return pathname;
|
|
131
|
+
return pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Paths that should never be redirected (system paths).
|
|
136
|
+
* These are protected to prevent redirect loops and system breakage.
|
|
137
|
+
*/
|
|
138
|
+
const PROTECTED_PATHS = [
|
|
139
|
+
'/api/',
|
|
140
|
+
'/_next/',
|
|
141
|
+
'/.well-known/',
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Check if a redirect destination is invalid.
|
|
146
|
+
* Invalid destinations include system paths and malformed patterns.
|
|
147
|
+
*/
|
|
148
|
+
export function isInvalidDestination(destination: string): boolean {
|
|
149
|
+
// Destinations to system paths are invalid (would break the site)
|
|
150
|
+
for (const protected_path of PROTECTED_PATHS) {
|
|
151
|
+
if (destination.startsWith(protected_path)) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Destinations with unresolved wildcards are likely misconfigured
|
|
156
|
+
if (destination.includes('*')) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Match a pathname against redirect rules
|
|
164
|
+
* Returns the matched redirect with resolved destination, or null if no match
|
|
165
|
+
*/
|
|
166
|
+
// Max path length to process (limits ReDoS attack surface)
|
|
167
|
+
const MAX_PATH_LENGTH = 2048;
|
|
168
|
+
|
|
169
|
+
export function matchRedirect(
|
|
170
|
+
pathname: string,
|
|
171
|
+
redirects: RedirectRule[]
|
|
172
|
+
): RedirectMatch | null {
|
|
173
|
+
// Never redirect protected system paths
|
|
174
|
+
for (const protected_path of PROTECTED_PATHS) {
|
|
175
|
+
if (pathname.startsWith(protected_path)) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Limit path length to reduce ReDoS attack surface
|
|
181
|
+
if (pathname.length > MAX_PATH_LENGTH) {
|
|
182
|
+
console.warn('[Redirects] Path too long, skipping redirect matching:', pathname.length);
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Normalize incoming path (strip trailing slash for matching)
|
|
187
|
+
const normalizedPath = normalizePath(pathname);
|
|
188
|
+
|
|
189
|
+
for (const rule of redirects) {
|
|
190
|
+
// Normalize rule source for matching
|
|
191
|
+
const normalizedSource = normalizePath(rule.source);
|
|
192
|
+
|
|
193
|
+
// Try exact match first (fast path)
|
|
194
|
+
if (!rule.hasWildcard && normalizedPath === normalizedSource) {
|
|
195
|
+
// Skip redirects with invalid destinations
|
|
196
|
+
if (isInvalidDestination(rule.destination)) {
|
|
197
|
+
console.warn('[Redirects] Skipping invalid destination:', normalizedPath, '->', rule.destination);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
// Detect circular redirect (destination same as source)
|
|
201
|
+
const normalizedDest = normalizePath(rule.destination);
|
|
202
|
+
if (normalizedDest === normalizedPath) {
|
|
203
|
+
console.warn('[Redirects] Skipping circular redirect:', normalizedPath, '->', rule.destination);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
destination: rule.destination,
|
|
208
|
+
permanent: rule.permanent,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Try pattern match for wildcards
|
|
213
|
+
if (rule.hasWildcard) {
|
|
214
|
+
// Use pre-compiled regex, skip rule if compilation failed
|
|
215
|
+
if (!rule._compiledRegex) continue;
|
|
216
|
+
|
|
217
|
+
// Safety: wrap regex match in try-catch to handle malformed patterns
|
|
218
|
+
// Note: This doesn't protect against ReDoS (catastrophic backtracking).
|
|
219
|
+
// Patterns should be validated at build time for production safety.
|
|
220
|
+
let match: RegExpMatchArray | null = null;
|
|
221
|
+
try {
|
|
222
|
+
match = normalizedPath.match(rule._compiledRegex);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error('[Redirects] Regex match failed:', rule.pattern, error);
|
|
225
|
+
continue; // Skip this rule
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (match) {
|
|
229
|
+
// Replace captured groups in destination
|
|
230
|
+
let destination = rule.destination;
|
|
231
|
+
|
|
232
|
+
// Handle :slug* style replacements
|
|
233
|
+
// The pattern captures groups, and destination uses :slug* placeholders
|
|
234
|
+
if (match.length > 1) {
|
|
235
|
+
// Replace :slug* in destination with captured value
|
|
236
|
+
const captured = match[1] || '';
|
|
237
|
+
destination = destination.replace(/:(\w+)\*/g, captured);
|
|
238
|
+
|
|
239
|
+
// Also handle trailing * in destination
|
|
240
|
+
if (destination.endsWith('/*')) {
|
|
241
|
+
destination = destination.slice(0, -2) + '/' + captured;
|
|
242
|
+
} else if (destination.endsWith('*')) {
|
|
243
|
+
destination = destination.slice(0, -1) + captured;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Skip redirects with invalid destinations
|
|
248
|
+
if (isInvalidDestination(destination)) {
|
|
249
|
+
console.warn('[Redirects] Skipping invalid destination:', normalizedPath, '->', destination);
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Detect circular redirect (destination same as source after substitution)
|
|
254
|
+
const normalizedDest = normalizePath(destination);
|
|
255
|
+
if (normalizedDest === normalizedPath) {
|
|
256
|
+
console.warn('[Redirects] Skipping circular redirect:', normalizedPath, '->', destination);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
destination,
|
|
262
|
+
permanent: rule.permanent,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Merge query strings from source URL with destination
|
|
273
|
+
* Source query params take precedence on conflicts
|
|
274
|
+
*/
|
|
275
|
+
export function mergeQueryStrings(
|
|
276
|
+
sourceSearch: string,
|
|
277
|
+
destinationPath: string
|
|
278
|
+
): string {
|
|
279
|
+
// Parse source query string
|
|
280
|
+
const sourceParams = new URLSearchParams(sourceSearch);
|
|
281
|
+
if (sourceParams.toString() === '') {
|
|
282
|
+
return destinationPath; // No source params to merge
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Check if destination has query string
|
|
286
|
+
const [destPath, destSearch] = destinationPath.split('?');
|
|
287
|
+
const destParams = new URLSearchParams(destSearch || '');
|
|
288
|
+
|
|
289
|
+
// Merge: source params override destination params
|
|
290
|
+
for (const [key, value] of sourceParams) {
|
|
291
|
+
destParams.set(key, value);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const mergedSearch = destParams.toString();
|
|
295
|
+
return mergedSearch ? `${destPath}?${mergedSearch}` : destPath;
|
|
296
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// DO NOT EDIT — this file is auto-synced from shared/. Edit the source in shared/ and run ./scripts/sync-shared.sh
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared Redis Client Module
|
|
5
|
+
*
|
|
6
|
+
* Provides a singleton Upstash Redis client.
|
|
7
|
+
* Supports both UPSTASH_REDIS_* and KV_* environment variable naming conventions.
|
|
8
|
+
*
|
|
9
|
+
* SYNC TARGET: This file is the source of truth.
|
|
10
|
+
* Synced to: builder/build-service/lib, proxy/lib
|
|
11
|
+
*/
|
|
12
|
+
import { Redis } from '@upstash/redis';
|
|
13
|
+
|
|
14
|
+
const redisUrl = process.env.UPSTASH_REDIS_REST_URL || process.env.KV_REST_API_URL;
|
|
15
|
+
const redisToken = process.env.UPSTASH_REDIS_REST_TOKEN || process.env.KV_REST_API_TOKEN;
|
|
16
|
+
|
|
17
|
+
export const redis = redisUrl && redisToken && redisUrl.startsWith('https://')
|
|
18
|
+
? new Redis({ url: redisUrl, token: redisToken })
|
|
19
|
+
: null;
|
|
20
|
+
|
|
21
|
+
export function isRedisConfigured(): boolean {
|
|
22
|
+
return redis !== null;
|
|
23
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { visit } from 'unist-util-visit';
|
|
2
|
+
import type { Root, Element } from 'hast';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Rehype plugin to convert HTML `class` attributes to React `className`
|
|
6
|
+
*
|
|
7
|
+
* This fixes the React warning: "Invalid DOM property `class`. Did you mean `className`?"
|
|
8
|
+
* when MDX content contains raw HTML elements with class attributes.
|
|
9
|
+
*/
|
|
10
|
+
export function rehypeClassToClassName() {
|
|
11
|
+
return (tree: Root) => {
|
|
12
|
+
visit(tree, 'element', (node: Element) => {
|
|
13
|
+
if (node.properties) {
|
|
14
|
+
// Check for both 'class' and 'className' as MDX might pass either
|
|
15
|
+
if ('class' in node.properties) {
|
|
16
|
+
const classValue = node.properties.class;
|
|
17
|
+
// Merge with existing className if present
|
|
18
|
+
if (node.properties.className) {
|
|
19
|
+
const existing = Array.isArray(node.properties.className)
|
|
20
|
+
? node.properties.className.join(' ')
|
|
21
|
+
: node.properties.className;
|
|
22
|
+
node.properties.className = `${existing} ${classValue}`;
|
|
23
|
+
} else {
|
|
24
|
+
node.properties.className = classValue;
|
|
25
|
+
}
|
|
26
|
+
delete node.properties.class;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { visit } from 'unist-util-visit';
|
|
2
|
+
import type { Root, Element } from 'hast';
|
|
3
|
+
import type { Plugin } from 'unified';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parsed code fence metadata
|
|
7
|
+
*/
|
|
8
|
+
export interface CodeMeta {
|
|
9
|
+
/** Custom icon class (e.g., "fa-solid fa-terminal") */
|
|
10
|
+
icon?: string;
|
|
11
|
+
/** Title/filename to display in header (e.g., "utils.js") */
|
|
12
|
+
title?: string;
|
|
13
|
+
/** Line numbers to highlight (e.g., [1, 3, 4, 5, 10]) */
|
|
14
|
+
highlightLines?: number[];
|
|
15
|
+
/** Whether to show line numbers */
|
|
16
|
+
showLineNumbers?: boolean;
|
|
17
|
+
/** Starting line number (default: 1) */
|
|
18
|
+
startLine?: number;
|
|
19
|
+
/** Remaining meta string (status codes, etc.) */
|
|
20
|
+
meta?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parse icon attribute from meta string
|
|
25
|
+
* Supports: icon="fa-solid fa-terminal" or icon='custom-icon'
|
|
26
|
+
*/
|
|
27
|
+
function parseIcon(meta: string): string | undefined {
|
|
28
|
+
const match = meta.match(/icon=["']([^"']+)["']/);
|
|
29
|
+
return match ? match[1] : undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Parse title attribute from meta string
|
|
34
|
+
* Supports: title="filename.js" or title='utils.ts'
|
|
35
|
+
*/
|
|
36
|
+
function parseTitle(meta: string): string | undefined {
|
|
37
|
+
const match = meta.match(/title=["']([^"']+)["']/);
|
|
38
|
+
return match ? match[1] : undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Parse line highlight ranges from meta string
|
|
43
|
+
* Supports: {1}, {1,3}, {1-5}, {1,3-5,10}
|
|
44
|
+
* Returns array of individual line numbers
|
|
45
|
+
*/
|
|
46
|
+
function parseHighlightLines(meta: string): number[] | undefined {
|
|
47
|
+
const match = meta.match(/\{([^}]+)\}/);
|
|
48
|
+
if (!match) return undefined;
|
|
49
|
+
|
|
50
|
+
const lines: number[] = [];
|
|
51
|
+
const parts = match[1].split(',');
|
|
52
|
+
|
|
53
|
+
for (const part of parts) {
|
|
54
|
+
const trimmed = part.trim();
|
|
55
|
+
if (trimmed.includes('-')) {
|
|
56
|
+
// Range: "3-5" -> [3, 4, 5]
|
|
57
|
+
const [start, end] = trimmed.split('-').map(Number);
|
|
58
|
+
if (!isNaN(start) && !isNaN(end)) {
|
|
59
|
+
for (let i = start; i <= end; i++) {
|
|
60
|
+
lines.push(i);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
// Single number: "3" -> [3]
|
|
65
|
+
const num = Number(trimmed);
|
|
66
|
+
if (!isNaN(num)) {
|
|
67
|
+
lines.push(num);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return lines.length > 0 ? lines : undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Parse showLineNumbers flag from meta string
|
|
77
|
+
*/
|
|
78
|
+
function parseShowLineNumbers(meta: string): boolean {
|
|
79
|
+
return /\bshowLineNumbers\b/.test(meta);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Parse startLine attribute from meta string
|
|
84
|
+
* Supports: startLine=10 or startLine="10"
|
|
85
|
+
*/
|
|
86
|
+
function parseStartLine(meta: string): number | undefined {
|
|
87
|
+
const match = meta.match(/startLine=["']?(\d+)["']?/);
|
|
88
|
+
if (match) {
|
|
89
|
+
const num = Number(match[1]);
|
|
90
|
+
return !isNaN(num) ? num : undefined;
|
|
91
|
+
}
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Remove parsed attributes from meta string
|
|
97
|
+
*/
|
|
98
|
+
function cleanMeta(meta: string): string {
|
|
99
|
+
return meta
|
|
100
|
+
.replace(/\s*icon=["'][^"']+["']\s*/g, ' ')
|
|
101
|
+
.replace(/\s*title=["'][^"']+["']\s*/g, ' ')
|
|
102
|
+
.replace(/\s*\{[^}]+\}\s*/g, ' ')
|
|
103
|
+
.replace(/\s*showLineNumbers\s*/g, ' ')
|
|
104
|
+
.replace(/\s*startLine=["']?\d+["']?\s*/g, ' ')
|
|
105
|
+
.trim();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Parse all code fence metadata from meta string
|
|
110
|
+
*/
|
|
111
|
+
export function parseCodeMeta(meta: string): CodeMeta {
|
|
112
|
+
const explicitTitle = parseTitle(meta);
|
|
113
|
+
const cleanedMeta = cleanMeta(meta);
|
|
114
|
+
|
|
115
|
+
// If no explicit title= attribute but there's remaining text after parsing,
|
|
116
|
+
// use that text as the title (e.g., "Header" in ```json Header)
|
|
117
|
+
const fallbackTitle = !explicitTitle && cleanedMeta ? cleanedMeta : undefined;
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
icon: parseIcon(meta),
|
|
121
|
+
title: explicitTitle || fallbackTitle,
|
|
122
|
+
highlightLines: parseHighlightLines(meta),
|
|
123
|
+
showLineNumbers: parseShowLineNumbers(meta),
|
|
124
|
+
startLine: parseStartLine(meta),
|
|
125
|
+
// Only include meta if we didn't use it as the title
|
|
126
|
+
meta: explicitTitle ? cleanedMeta || undefined : undefined,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Rehype plugin to pass code fence meta strings as data attributes
|
|
132
|
+
*
|
|
133
|
+
* Parses:
|
|
134
|
+
* - icon="fa-solid fa-terminal" → data-icon
|
|
135
|
+
* - title="filename.js" → data-title
|
|
136
|
+
* - {1,3-5} → data-highlight-lines (JSON array)
|
|
137
|
+
* - showLineNumbers → data-show-line-numbers
|
|
138
|
+
* - startLine=10 → data-start-line
|
|
139
|
+
* - Remaining text → data-meta (e.g., "200: Success")
|
|
140
|
+
*
|
|
141
|
+
* Attributes are added to both the code element and parent pre element
|
|
142
|
+
* so they're accessible from both React component mappings.
|
|
143
|
+
*/
|
|
144
|
+
export function rehypeCodeMeta() {
|
|
145
|
+
return (tree: Root) => {
|
|
146
|
+
visit(tree, 'element', (node: Element, _index, parent) => {
|
|
147
|
+
if (node.tagName === 'code' && node.data?.meta) {
|
|
148
|
+
const meta = node.data.meta as string;
|
|
149
|
+
const parsed = parseCodeMeta(meta);
|
|
150
|
+
|
|
151
|
+
// DEBUG: Log what we're parsing
|
|
152
|
+
|
|
153
|
+
node.properties = node.properties || {};
|
|
154
|
+
|
|
155
|
+
// Store parsed data in node.data so Shiki transformers can access it
|
|
156
|
+
node.data = node.data || {};
|
|
157
|
+
(node.data as any).parsedMeta = parsed;
|
|
158
|
+
|
|
159
|
+
if (parsed.icon) {
|
|
160
|
+
node.properties['data-icon'] = parsed.icon;
|
|
161
|
+
}
|
|
162
|
+
if (parsed.title) {
|
|
163
|
+
node.properties['data-title'] = parsed.title;
|
|
164
|
+
}
|
|
165
|
+
if (parsed.highlightLines) {
|
|
166
|
+
node.properties['data-highlight-lines'] = JSON.stringify(parsed.highlightLines);
|
|
167
|
+
}
|
|
168
|
+
if (parsed.showLineNumbers) {
|
|
169
|
+
node.properties['data-show-line-numbers'] = 'true';
|
|
170
|
+
}
|
|
171
|
+
if (parsed.startLine !== undefined) {
|
|
172
|
+
node.properties['data-start-line'] = String(parsed.startLine);
|
|
173
|
+
}
|
|
174
|
+
if (parsed.meta) {
|
|
175
|
+
node.properties['data-meta'] = parsed.meta;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Also add title to parent pre element for easier access in MDX components
|
|
179
|
+
if (parsed.title && parent && (parent as Element).tagName === 'pre') {
|
|
180
|
+
const preNode = parent as Element;
|
|
181
|
+
preNode.properties = preNode.properties || {};
|
|
182
|
+
preNode.properties['data-title'] = parsed.title;
|
|
183
|
+
// Store in parent data as well
|
|
184
|
+
preNode.data = preNode.data || {};
|
|
185
|
+
(preNode.data as any).parsedTitle = parsed.title;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Common language names and their variants used in CodeGroup tabs
|
|
194
|
+
* These should NOT be converted to titles
|
|
195
|
+
*/
|
|
196
|
+
const LANGUAGE_LABELS = new Set([
|
|
197
|
+
// Language names
|
|
198
|
+
'javascript', 'typescript', 'python', 'java', 'ruby', 'php', 'go', 'rust',
|
|
199
|
+
'c', 'cpp', 'csharp', 'c#', 'swift', 'kotlin', 'scala', 'r', 'perl',
|
|
200
|
+
'bash', 'shell', 'sh', 'powershell', 'cmd',
|
|
201
|
+
'html', 'css', 'scss', 'sass', 'less',
|
|
202
|
+
'json', 'xml', 'yaml', 'yml', 'toml', 'ini', 'csv',
|
|
203
|
+
'sql', 'graphql', 'markdown', 'md',
|
|
204
|
+
// Short forms and variants
|
|
205
|
+
'js', 'ts', 'py', 'rb', 'cs', 'rs', 'kt',
|
|
206
|
+
// Tool names often used as labels
|
|
207
|
+
'curl', 'wget', 'npm', 'yarn', 'pip', 'gem', 'cargo', 'gradle', 'maven',
|
|
208
|
+
'docker', 'git', 'node', 'deno', 'bun',
|
|
209
|
+
]);
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Check if a meta string looks like a language/tool label vs. a title
|
|
213
|
+
*/
|
|
214
|
+
function isLanguageLabel(meta: string, language?: string): boolean {
|
|
215
|
+
const normalized = meta.toLowerCase().trim();
|
|
216
|
+
|
|
217
|
+
// Check if it's in our language labels set
|
|
218
|
+
if (LANGUAGE_LABELS.has(normalized)) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Check if it matches the language (e.g., "JavaScript" for lang="javascript")
|
|
223
|
+
if (language && normalized === language.toLowerCase()) {
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Rehype plugin that restores data-title attributes after Shiki processing
|
|
232
|
+
*
|
|
233
|
+
* This plugin runs AFTER Shiki and converts data-meta to data-title for standalone code blocks.
|
|
234
|
+
*
|
|
235
|
+
* IMPORTANT: Preserves data-meta as tab labels for CodeGroup by detecting when data-meta
|
|
236
|
+
* contains language/tool names (e.g., "JavaScript", "cURL", "Python") vs. actual titles
|
|
237
|
+
* (e.g., "Header", "Request Headers", "Configuration").
|
|
238
|
+
*/
|
|
239
|
+
/**
|
|
240
|
+
* Check if meta string is a code fence feature (not a title)
|
|
241
|
+
* Features like showLineNumbers, {1,3-5}, startLine=N should not become titles
|
|
242
|
+
*/
|
|
243
|
+
function isCodeFenceFeature(meta: string): boolean {
|
|
244
|
+
const trimmed = meta.trim();
|
|
245
|
+
// Line highlighting: {1}, {1,3-5}, etc.
|
|
246
|
+
if (/^\{[\d,\-\s]+\}$/.test(trimmed)) return true;
|
|
247
|
+
// showLineNumbers flag
|
|
248
|
+
if (/^showLineNumbers(\s|$)/.test(trimmed)) return true;
|
|
249
|
+
// startLine=N
|
|
250
|
+
if (/^startLine=/.test(trimmed)) return true;
|
|
251
|
+
// Multiple features combined (no actual title)
|
|
252
|
+
if (/^(showLineNumbers|\{[\d,\-\s]+\}|startLine=\d+)(\s+(showLineNumbers|\{[\d,\-\s]+\}|startLine=\d+))*$/.test(trimmed)) return true;
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export const rehypeRestoreDataTitle: Plugin<[], Root> = () => {
|
|
257
|
+
return (tree: Root) => {
|
|
258
|
+
visit(tree, 'element', (node: Element) => {
|
|
259
|
+
if (node.tagName === 'pre' && node.properties) {
|
|
260
|
+
const dataMeta = node.properties['data-meta'] as string | undefined;
|
|
261
|
+
const dataLanguage = node.properties['data-language'] as string | undefined;
|
|
262
|
+
|
|
263
|
+
// Only convert data-meta to data-title if:
|
|
264
|
+
// 1. data-meta exists
|
|
265
|
+
// 2. It doesn't look like a status code (no colon)
|
|
266
|
+
// 3. It's not a language/tool label (which CodeGroup uses for tabs)
|
|
267
|
+
// 4. It's not just code fence features (showLineNumbers, {1,3-5}, startLine=N)
|
|
268
|
+
if (dataMeta && !dataMeta.includes(':') && !isLanguageLabel(dataMeta, dataLanguage) && !isCodeFenceFeature(dataMeta)) {
|
|
269
|
+
node.properties['data-title'] = dataMeta;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { visit } from 'unist-util-visit';
|
|
2
|
+
import type { Root, Element } from 'hast';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Rehype plugin to convert custom image attributes to data attributes.
|
|
6
|
+
*
|
|
7
|
+
* Converts:
|
|
8
|
+
* - `noZoom` → `data-no-zoom` (disables image zoom on click)
|
|
9
|
+
* - `noStyle` → `data-no-style` (disables default image styling)
|
|
10
|
+
*
|
|
11
|
+
* Fixes React warning: "React does not recognize the `noZoom` prop on a DOM element"
|
|
12
|
+
* when MDX content uses <img noZoom ... /> or <img noStyle ... /> syntax.
|
|
13
|
+
*
|
|
14
|
+
* Note: This is a backup mechanism. The primary conversion happens in
|
|
15
|
+
* preprocess-mdx.ts at the text level before MDX parsing.
|
|
16
|
+
*/
|
|
17
|
+
export function rehypeNoZoomToData() {
|
|
18
|
+
return (tree: Root) => {
|
|
19
|
+
visit(tree, 'element', (node: Element) => {
|
|
20
|
+
if (node.properties) {
|
|
21
|
+
// Convert noZoom to data-no-zoom
|
|
22
|
+
// Check both camelCase (JSX) and lowercase (HTML) versions
|
|
23
|
+
if ('noZoom' in node.properties) {
|
|
24
|
+
node.properties['data-no-zoom'] = node.properties.noZoom;
|
|
25
|
+
delete node.properties.noZoom;
|
|
26
|
+
}
|
|
27
|
+
if ('nozoom' in node.properties) {
|
|
28
|
+
node.properties['data-no-zoom'] = node.properties.nozoom;
|
|
29
|
+
delete node.properties.nozoom;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Convert noStyle to data-no-style
|
|
33
|
+
// Check both camelCase (JSX) and lowercase (HTML) versions
|
|
34
|
+
if ('noStyle' in node.properties) {
|
|
35
|
+
node.properties['data-no-style'] = node.properties.noStyle;
|
|
36
|
+
delete node.properties.noStyle;
|
|
37
|
+
}
|
|
38
|
+
if ('nostyle' in node.properties) {
|
|
39
|
+
node.properties['data-no-style'] = node.properties.nostyle;
|
|
40
|
+
delete node.properties.nostyle;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
}
|