@wrikka/create-docs 0.1.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/README.md +137 -0
- package/bin/cli.js +408 -0
- package/dist/index.cjs +20 -0
- package/dist/index.d.cts +475 -0
- package/dist/index.d.ts +475 -0
- package/dist/index.js +20 -0
- package/docs/_dir.yml +2 -0
- package/docs/config.md +75 -0
- package/docs/data-sources.md +88 -0
- package/docs/deploy.md +38 -0
- package/docs/features.md +51 -0
- package/docs/getting-started.md +42 -0
- package/docs/index.md +34 -0
- package/docs/markdown.md +154 -0
- package/docs/theming.md +53 -0
- package/package.json +142 -0
- package/scripts/generate-docs-index.ts +111 -0
- package/scripts/github-pull.ts +124 -0
- package/src/adapters/config/index.ts +1 -0
- package/src/adapters/config/node-config-loader.ts +36 -0
- package/src/adapters/fs/fs-docs-loader.ts +31 -0
- package/src/adapters/fs/index.ts +12 -0
- package/src/adapters/fs/memory-fs.ts +60 -0
- package/src/adapters/fs/node-fs.ts +116 -0
- package/src/adapters/git/index.ts +164 -0
- package/src/adapters/github/github-pull.ts +340 -0
- package/src/adapters/index.ts +6 -0
- package/src/adapters/velite.ts +112 -0
- package/src/adapters/vite/docs-watcher.ts +36 -0
- package/src/adapters/vite/index.ts +2 -0
- package/src/adapters/vite/polling-watcher.ts +89 -0
- package/src/code-block-enhancements/index.ts +3 -0
- package/src/config/index.ts +15 -0
- package/src/content/index.ts +11 -0
- package/src/content-validation/index.ts +5 -0
- package/src/index.ts +174 -0
- package/src/live-editor/index.ts +7 -0
- package/src/modules/analytics/application/plausible-adapter.ts +103 -0
- package/src/modules/analytics/application/umami-adapter.ts +109 -0
- package/src/modules/analytics/domain/events/index.ts +37 -0
- package/src/modules/analytics/domain/models/index.ts +35 -0
- package/src/modules/analytics/domain/operations/analytics-operations.ts +67 -0
- package/src/modules/analytics/domain/validators/index.ts +50 -0
- package/src/modules/analytics/index.ts +9 -0
- package/src/modules/analytics/ports/analytics-port.ts +17 -0
- package/src/modules/analytics/types/analytics.ts +16 -0
- package/src/modules/api-docs/application/type-docs-adapter.ts +34 -0
- package/src/modules/api-docs/domain/events/index.ts +42 -0
- package/src/modules/api-docs/domain/models/index.ts +40 -0
- package/src/modules/api-docs/domain/operations/api-docs-operations.ts +59 -0
- package/src/modules/api-docs/domain/validators/index.ts +56 -0
- package/src/modules/api-docs/index.ts +8 -0
- package/src/modules/api-docs/ports/api-docs-port.ts +21 -0
- package/src/modules/api-docs/types/api-docs.ts +15 -0
- package/src/modules/api-reference/openapi.ts +166 -0
- package/src/modules/api-reference/orpc.ts +94 -0
- package/src/modules/code-block-enhancements/application/usecases/index.ts +39 -0
- package/src/modules/code-block-enhancements/domain/code-operations.ts +72 -0
- package/src/modules/code-block-enhancements/domain/events/index.ts +58 -0
- package/src/modules/code-block-enhancements/domain/index.ts +11 -0
- package/src/modules/code-block-enhancements/domain/models/index.ts +36 -0
- package/src/modules/code-block-enhancements/domain/validators/index.ts +50 -0
- package/src/modules/code-block-enhancements/index.ts +10 -0
- package/src/modules/code-block-enhancements/ports/index.ts +18 -0
- package/src/modules/code-block-enhancements/types/index.ts +37 -0
- package/src/modules/code-execution/application/sandbox-adapter.ts +44 -0
- package/src/modules/code-execution/domain/events/index.ts +58 -0
- package/src/modules/code-execution/domain/models/index.ts +33 -0
- package/src/modules/code-execution/domain/operations/execution-operations.ts +60 -0
- package/src/modules/code-execution/domain/validators/index.ts +50 -0
- package/src/modules/code-execution/index.ts +8 -0
- package/src/modules/code-execution/ports/execution-port.ts +15 -0
- package/src/modules/code-execution/types/execution.ts +15 -0
- package/src/modules/comments/application/giscus-adapter.ts +55 -0
- package/src/modules/comments/domain/events/index.ts +55 -0
- package/src/modules/comments/domain/models/index.ts +30 -0
- package/src/modules/comments/domain/operations/comment-operations.ts +46 -0
- package/src/modules/comments/domain/validators/index.ts +50 -0
- package/src/modules/comments/index.ts +8 -0
- package/src/modules/comments/ports/comment-port.ts +18 -0
- package/src/modules/comments/types/comments.ts +19 -0
- package/src/modules/config/application/usecases/index.ts +1 -0
- package/src/modules/config/application/usecases/loadConfig.ts +38 -0
- package/src/modules/config/application/workflows/index.ts +1 -0
- package/src/modules/config/application/workflows/validateConfig.ts +14 -0
- package/src/modules/config/domain/events/index.ts +50 -0
- package/src/modules/config/domain/models/index.ts +9 -0
- package/src/modules/config/domain/operations/defaults.ts +74 -0
- package/src/modules/config/domain/operations/index.ts +2 -0
- package/src/modules/config/domain/operations/resolve.ts +40 -0
- package/src/modules/config/domain/validators/index.ts +152 -0
- package/src/modules/config/index.ts +23 -0
- package/src/modules/config/ports/index.ts +15 -0
- package/src/modules/config/types/index.ts +113 -0
- package/src/modules/config/types/public.ts +19 -0
- package/src/modules/content/application/cache/index.ts +6 -0
- package/src/modules/content/application/cache/parse-cache.ts +6 -0
- package/src/modules/content/application/cache/parseCache.ts +56 -0
- package/src/modules/content/application/usecases/index.ts +2 -0
- package/src/modules/content/application/usecases/load-doc-page.ts +1 -0
- package/src/modules/content/application/usecases/loadDocPage.ts +111 -0
- package/src/modules/content/application/usecases/scanDocsDir.ts +105 -0
- package/src/modules/content/application/workflows/index.ts +1 -0
- package/src/modules/content/application/workflows/processDocs.ts +51 -0
- package/src/modules/content/domain/events/index.ts +61 -0
- package/src/modules/content/domain/models/index.ts +13 -0
- package/src/modules/content/domain/operations/build-page.ts +146 -0
- package/src/modules/content/domain/operations/buildPage.ts +93 -0
- package/src/modules/content/domain/operations/index.ts +17 -0
- package/src/modules/content/domain/operations/parse.ts +241 -0
- package/src/modules/content/domain/validators/frontmatter.ts +82 -0
- package/src/modules/content/domain/validators/index.ts +6 -0
- package/src/modules/content/index.ts +27 -0
- package/src/modules/content/ports/index.ts +45 -0
- package/src/modules/content/ports/velite.ts +134 -0
- package/src/modules/content/types/index.ts +76 -0
- package/src/modules/content/types/public.ts +1 -0
- package/src/modules/content-linting/application/builtin-linting-adapter.ts +82 -0
- package/src/modules/content-linting/domain/events/index.ts +58 -0
- package/src/modules/content-linting/domain/models/index.ts +46 -0
- package/src/modules/content-linting/domain/operations/linting-operations.ts +82 -0
- package/src/modules/content-linting/domain/validators/index.ts +50 -0
- package/src/modules/content-linting/index.ts +8 -0
- package/src/modules/content-linting/ports/linting-port.ts +21 -0
- package/src/modules/content-linting/types/linting.ts +18 -0
- package/src/modules/content-validation/application/usecases/index.ts +18 -0
- package/src/modules/content-validation/domain/events/index.ts +7 -0
- package/src/modules/content-validation/domain/index.ts +16 -0
- package/src/modules/content-validation/domain/models/index.ts +33 -0
- package/src/modules/content-validation/domain/operations/index.ts +157 -0
- package/src/modules/content-validation/domain/types/index.ts +17 -0
- package/src/modules/content-validation/domain/validators/index.ts +8 -0
- package/src/modules/content-validation/index.ts +9 -0
- package/src/modules/content-validation/ports/index.ts +15 -0
- package/src/modules/content-validation/types/index.ts +5 -0
- package/src/modules/export/application/puppeteer-export-adapter.ts +63 -0
- package/src/modules/export/domain/events/index.ts +55 -0
- package/src/modules/export/domain/models/index.ts +38 -0
- package/src/modules/export/domain/operations/export-operations.ts +56 -0
- package/src/modules/export/domain/validators/index.ts +52 -0
- package/src/modules/export/index.ts +8 -0
- package/src/modules/export/ports/export-port.ts +15 -0
- package/src/modules/export/types/export.ts +15 -0
- package/src/modules/external-search/application/algolia-adapter.ts +182 -0
- package/src/modules/external-search/application/meilisearch-adapter.ts +161 -0
- package/src/modules/external-search/domain/events/index.ts +42 -0
- package/src/modules/external-search/domain/models/index.ts +42 -0
- package/src/modules/external-search/domain/operations/search-operations.ts +53 -0
- package/src/modules/external-search/domain/validators/index.ts +55 -0
- package/src/modules/external-search/index.ts +9 -0
- package/src/modules/external-search/ports/external-search-port.ts +23 -0
- package/src/modules/external-search/types/search.ts +19 -0
- package/src/modules/git-history/application/simple-git-adapter.ts +46 -0
- package/src/modules/git-history/domain/events/index.ts +34 -0
- package/src/modules/git-history/domain/models/index.ts +25 -0
- package/src/modules/git-history/domain/operations/git-history-operations.ts +56 -0
- package/src/modules/git-history/domain/validators/index.ts +52 -0
- package/src/modules/git-history/index.ts +8 -0
- package/src/modules/git-history/ports/git-history-port.ts +21 -0
- package/src/modules/git-history/types/git-history.ts +15 -0
- package/src/modules/i18n/application/fs-locale-loader.ts +39 -0
- package/src/modules/i18n/domain/events/index.ts +34 -0
- package/src/modules/i18n/domain/models/index.ts +34 -0
- package/src/modules/i18n/domain/operations/locale-operations.ts +74 -0
- package/src/modules/i18n/domain/validators/index.ts +52 -0
- package/src/modules/i18n/index.ts +8 -0
- package/src/modules/i18n/ports/locale-loader-port.ts +18 -0
- package/src/modules/i18n/types/locale.ts +17 -0
- package/src/modules/image-optimization/application/sharp-adapter.ts +58 -0
- package/src/modules/image-optimization/domain/events/index.ts +39 -0
- package/src/modules/image-optimization/domain/models/index.ts +27 -0
- package/src/modules/image-optimization/domain/operations/image-operations.ts +65 -0
- package/src/modules/image-optimization/domain/validators/index.ts +52 -0
- package/src/modules/image-optimization/index.ts +8 -0
- package/src/modules/image-optimization/ports/image-optimizer-port.ts +15 -0
- package/src/modules/image-optimization/types/image.ts +17 -0
- package/src/modules/live-editor/application/usecases/index.ts +60 -0
- package/src/modules/live-editor/domain/events/index.ts +37 -0
- package/src/modules/live-editor/domain/gitOperations.ts +110 -0
- package/src/modules/live-editor/domain/index.ts +15 -0
- package/src/modules/live-editor/domain/models/index.ts +21 -0
- package/src/modules/live-editor/domain/validators/index.ts +55 -0
- package/src/modules/live-editor/index.ts +14 -0
- package/src/modules/live-editor/ports/index.ts +49 -0
- package/src/modules/live-editor/types/index.ts +80 -0
- package/src/modules/live-preview/application/vite-preview-adapter.ts +20 -0
- package/src/modules/live-preview/domain/operations/preview-operations.ts +56 -0
- package/src/modules/live-preview/index.ts +8 -0
- package/src/modules/live-preview/ports/preview-port.ts +20 -0
- package/src/modules/live-preview/types/preview.ts +16 -0
- package/src/modules/mdx/application/mdx-js-adapter.ts +38 -0
- package/src/modules/mdx/domain/operations/parse-mdx.ts +43 -0
- package/src/modules/mdx/index.ts +8 -0
- package/src/modules/mdx/ports/mdx-parser-port.ts +15 -0
- package/src/modules/mdx/types/mdx.ts +14 -0
- package/src/modules/navigation/application/usecases/buildNav.ts +15 -0
- package/src/modules/navigation/application/usecases/buildSidebar.ts +45 -0
- package/src/modules/navigation/application/usecases/index.ts +2 -0
- package/src/modules/navigation/domain/events/index.ts +30 -0
- package/src/modules/navigation/domain/models/index.ts +7 -0
- package/src/modules/navigation/domain/operations/group.ts +79 -0
- package/src/modules/navigation/domain/operations/index.ts +10 -0
- package/src/modules/navigation/domain/operations/sort.ts +27 -0
- package/src/modules/navigation/domain/shared/utils/array.ts +39 -0
- package/src/modules/navigation/domain/validators/index.ts +88 -0
- package/src/modules/navigation/index.ts +15 -0
- package/src/modules/navigation/ports/index.ts +9 -0
- package/src/modules/navigation/types/index.ts +38 -0
- package/src/modules/navigation/types/public.ts +9 -0
- package/src/modules/openapi/application/swagger-parser-adapter.ts +178 -0
- package/src/modules/openapi/domain/operations/parse-openapi.ts +46 -0
- package/src/modules/openapi/index.ts +8 -0
- package/src/modules/openapi/ports/openapi-parser-port.ts +15 -0
- package/src/modules/openapi/types/openapi.ts +37 -0
- package/src/modules/plugin/application/usecases/build-virtual-modules.ts +53 -0
- package/src/modules/plugin/application/usecases/index.ts +1 -0
- package/src/modules/plugin/application/workflows/build-docs.ts +79 -0
- package/src/modules/plugin/application/workflows/index.ts +1 -0
- package/src/modules/plugin/domain/events/index.ts +62 -0
- package/src/modules/plugin/domain/models/index.ts +1 -0
- package/src/modules/plugin/domain/operations/build-virtual.ts +76 -0
- package/src/modules/plugin/domain/operations/index.ts +11 -0
- package/src/modules/plugin/domain/validators/index.ts +30 -0
- package/src/modules/plugin/index.ts +14 -0
- package/src/modules/plugin/ports/index.ts +21 -0
- package/src/modules/plugin/types/index.ts +27 -0
- package/src/modules/plugin/types/public.ts +1 -0
- package/src/modules/search/application/usecases/index.ts +1 -0
- package/src/modules/search/application/usecases/indexPages.ts +15 -0
- package/src/modules/search/domain/events/index.ts +32 -0
- package/src/modules/search/domain/models/index.ts +1 -0
- package/src/modules/search/domain/operations/build-index.ts +6 -0
- package/src/modules/search/domain/operations/buildIndex.ts +70 -0
- package/src/modules/search/domain/operations/index.ts +7 -0
- package/src/modules/search/domain/operations/query.ts +186 -0
- package/src/modules/search/domain/validators/index.ts +32 -0
- package/src/modules/search/index.ts +9 -0
- package/src/modules/search/types/index.ts +22 -0
- package/src/modules/search/types/public.ts +1 -0
- package/src/modules/search-analytics/application/local-storage-adapter.ts +68 -0
- package/src/modules/search-analytics/domain/operations/search-analytics-operations.ts +74 -0
- package/src/modules/search-analytics/index.ts +8 -0
- package/src/modules/search-analytics/ports/search-analytics-port.ts +20 -0
- package/src/modules/search-analytics/types/search-analytics.ts +16 -0
- package/src/modules/search-highlighting/application/dom-highlighting-adapter.ts +36 -0
- package/src/modules/search-highlighting/domain/operations/highlighting-operations.ts +72 -0
- package/src/modules/search-highlighting/index.ts +8 -0
- package/src/modules/search-highlighting/ports/highlighting-port.ts +14 -0
- package/src/modules/search-highlighting/types/highlighting.ts +15 -0
- package/src/modules/seo-optimization/application/usecases/index.ts +18 -0
- package/src/modules/seo-optimization/domain/index.ts +12 -0
- package/src/modules/seo-optimization/domain/seoOperations.ts +92 -0
- package/src/modules/seo-optimization/index.ts +8 -0
- package/src/modules/seo-optimization/ports/index.ts +18 -0
- package/src/modules/seo-optimization/types/index.ts +41 -0
- package/src/modules/theme/application/css-theme-adapter.ts +50 -0
- package/src/modules/theme/domain/operations/theme-operations.ts +68 -0
- package/src/modules/theme/index.ts +8 -0
- package/src/modules/theme/ports/theme-port.ts +8 -0
- package/src/modules/theme/types/theme.ts +12 -0
- package/src/modules/toc/application/markdown-toc-adapter.ts +106 -0
- package/src/modules/toc/domain/operations/toc-operations.ts +92 -0
- package/src/modules/toc/index.ts +8 -0
- package/src/modules/toc/ports/toc-port.ts +21 -0
- package/src/modules/toc/types/toc.ts +17 -0
- package/src/modules/toc-generator/application/usecases/index.ts +30 -0
- package/src/modules/toc-generator/domain/index.ts +12 -0
- package/src/modules/toc-generator/domain/tocOperations.ts +104 -0
- package/src/modules/toc-generator/index.ts +9 -0
- package/src/modules/toc-generator/ports/index.ts +20 -0
- package/src/modules/toc-generator/types/index.ts +50 -0
- package/src/modules/version-control/domain/events/index.ts +39 -0
- package/src/modules/version-control/domain/models/index.ts +25 -0
- package/src/modules/version-control/domain/validators/index.ts +50 -0
- package/src/modules/versioning/application/fs-version-loader.ts +44 -0
- package/src/modules/versioning/domain/operations/versioning-operations.ts +80 -0
- package/src/modules/versioning/index.ts +8 -0
- package/src/modules/versioning/ports/version-loader-port.ts +18 -0
- package/src/modules/versioning/types/versioning.ts +17 -0
- package/src/modules/workflow/domain/events/index.ts +55 -0
- package/src/modules/workflow/domain/models/index.ts +40 -0
- package/src/modules/workflow/domain/validators/index.ts +50 -0
- package/src/modules/yaml-parser/application/js-yaml-adapter.ts +34 -0
- package/src/modules/yaml-parser/domain/operations/parse-yaml.ts +58 -0
- package/src/modules/yaml-parser/index.ts +7 -0
- package/src/modules/yaml-parser/ports/yaml-parser-port.ts +18 -0
- package/src/navigation/index.ts +9 -0
- package/src/plugin/index.ts +4 -0
- package/src/presentation/index.ts +1 -0
- package/src/presentation/live-editor/index.ts +22 -0
- package/src/presentation/live-editor/live-editor-component.ts +219 -0
- package/src/presentation/live-editor/virtual-module.ts +16 -0
- package/src/presentation/solid/CodeBlock.tsx +120 -0
- package/src/presentation/solid/Toc.tsx +58 -0
- package/src/presentation/solid/index.ts +11 -0
- package/src/presentation/vite-plugin/docs-plugin.ts +212 -0
- package/src/presentation/vite-plugin/index.ts +9 -0
- package/src/presentation/vite-plugin/transform-markdown.ts +26 -0
- package/src/runtime/analytics.ts +59 -0
- package/src/runtime/api-diff.ts +115 -0
- package/src/runtime/app.tsx +32 -0
- package/src/runtime/components/AccentPicker.tsx +114 -0
- package/src/runtime/components/ApiPlayground.tsx +332 -0
- package/src/runtime/components/AskAiDialog.tsx +127 -0
- package/src/runtime/components/BackToTop.tsx +38 -0
- package/src/runtime/components/Banner.tsx +75 -0
- package/src/runtime/components/Breadcrumbs.tsx +42 -0
- package/src/runtime/components/CollectionDropdown.tsx +85 -0
- package/src/runtime/components/CustomizeDrawer.tsx +195 -0
- package/src/runtime/components/DocMarkdown.tsx +579 -0
- package/src/runtime/components/DocPrevNext.tsx +54 -0
- package/src/runtime/components/DocToc.tsx +147 -0
- package/src/runtime/components/DocsDropdown.tsx +184 -0
- package/src/runtime/components/Footer.tsx +52 -0
- package/src/runtime/components/GitHubAuth.tsx +82 -0
- package/src/runtime/components/GitHubStats.tsx +73 -0
- package/src/runtime/components/Head.tsx +225 -0
- package/src/runtime/components/LocaleDropdown.tsx +75 -0
- package/src/runtime/components/MobileBottomNav.tsx +111 -0
- package/src/runtime/components/NotFound.tsx +139 -0
- package/src/runtime/components/PageActions.tsx +144 -0
- package/src/runtime/components/RelatedDocs.tsx +50 -0
- package/src/runtime/components/ScrollProgress.tsx +32 -0
- package/src/runtime/components/SearchPalette.tsx +333 -0
- package/src/runtime/components/ShowcaseCard.tsx +87 -0
- package/src/runtime/components/SidebarNav.tsx +237 -0
- package/src/runtime/components/Skeleton.tsx +39 -0
- package/src/runtime/components/ThemeToggle.tsx +26 -0
- package/src/runtime/components/TopNav.tsx +270 -0
- package/src/runtime/components/VersionDropdown.tsx +189 -0
- package/src/runtime/components/markdown/components.ts +280 -0
- package/src/runtime/config.ts +225 -0
- package/src/runtime/content.ts +451 -0
- package/src/runtime/context.tsx +30 -0
- package/src/runtime/data.ts +93 -0
- package/src/runtime/github.ts +226 -0
- package/src/runtime/icons.ts +32 -0
- package/src/runtime/index.ts +153 -0
- package/src/runtime/layouts/DocsLayout.tsx +144 -0
- package/src/runtime/markdown-content.css +996 -0
- package/src/runtime/pages/AbTestPage.tsx +88 -0
- package/src/runtime/pages/AnalyticsPage.tsx +90 -0
- package/src/runtime/pages/ApiDiffPage.tsx +86 -0
- package/src/runtime/pages/ApiEndpointPage.tsx +248 -0
- package/src/runtime/pages/BuilderPage.tsx +150 -0
- package/src/runtime/pages/ChangelogPage.tsx +210 -0
- package/src/runtime/pages/CollectionPage.tsx +140 -0
- package/src/runtime/pages/CommunityPage.tsx +384 -0
- package/src/runtime/pages/CreatePage.tsx +448 -0
- package/src/runtime/pages/DocPage.tsx +193 -0
- package/src/runtime/pages/EditPage.tsx +201 -0
- package/src/runtime/pages/HomePage.tsx +187 -0
- package/src/runtime/pages/IssuesPage.tsx +140 -0
- package/src/runtime/pages/OAuthCallbackPage.tsx +80 -0
- package/src/runtime/pages/PluginsPage.tsx +249 -0
- package/src/runtime/pages/ShowcasePage.tsx +202 -0
- package/src/runtime/pwa.ts +39 -0
- package/src/runtime/router.tsx +192 -0
- package/src/runtime/seo.ts +192 -0
- package/src/runtime/theme.css +175 -0
- package/src/runtime/theme.ts +224 -0
- package/src/runtime/types.ts +217 -0
- package/src/search/index.ts +6 -0
- package/src/seo-optimization/index.ts +4 -0
- package/src/shared/constants/index.ts +26 -0
- package/src/shared/errors/index.ts +195 -0
- package/src/shared/index.ts +27 -0
- package/src/shared/types/brand.ts +14 -0
- package/src/shared/types/client.d.ts +45 -0
- package/src/shared/types/either.ts +38 -0
- package/src/shared/types/index.ts +42 -0
- package/src/shared/types/option.ts +52 -0
- package/src/shared/types/result.ts +81 -0
- package/src/shared/utils/array.ts +97 -0
- package/src/shared/utils/index.ts +46 -0
- package/src/shared/utils/object.ts +47 -0
- package/src/shared/utils/string.ts +74 -0
- package/src/toc-generator/index.ts +1 -0
- package/src/types/js-yaml.d.ts +42 -0
- package/src/worker.ts +462 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM highlighting adapter implementation.
|
|
3
|
+
* Provides client-side search highlighting.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
applyHighlighting,
|
|
8
|
+
findMatches,
|
|
9
|
+
} from "../domain/operations/highlighting-operations";
|
|
10
|
+
import type { HighlightingPort } from "../ports/highlighting-port";
|
|
11
|
+
import type { HighlightConfig } from "../types/highlighting";
|
|
12
|
+
|
|
13
|
+
export const createDomHighlightingAdapter = (): HighlightingPort => ({
|
|
14
|
+
highlight: async (content: string, query: string, config?: unknown) => {
|
|
15
|
+
const highlightConfig: HighlightConfig =
|
|
16
|
+
config && typeof config === "object"
|
|
17
|
+
? (config as HighlightConfig)
|
|
18
|
+
: {
|
|
19
|
+
enabled: true,
|
|
20
|
+
caseSensitive: false,
|
|
21
|
+
highlightClass: "search-highlight",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const matchesResult = findMatches(content, query, highlightConfig);
|
|
25
|
+
if (!matchesResult.ok) {
|
|
26
|
+
return content;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const highlighted = applyHighlighting(
|
|
30
|
+
content,
|
|
31
|
+
matchesResult.value,
|
|
32
|
+
highlightConfig.highlightClass,
|
|
33
|
+
);
|
|
34
|
+
return highlighted;
|
|
35
|
+
},
|
|
36
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure search highlighting operations.
|
|
3
|
+
* No I/O. No exceptions thrown. Returns Result types.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Result } from "@create-docs/shared/types";
|
|
7
|
+
import { err, ok } from "@create-docs/shared/types";
|
|
8
|
+
import type { HighlightConfig, HighlightMatch } from "../../types/highlighting";
|
|
9
|
+
|
|
10
|
+
export type HighlightingError = {
|
|
11
|
+
readonly kind: "highlighting-error";
|
|
12
|
+
readonly message: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Find all matches of a query in text.
|
|
17
|
+
*/
|
|
18
|
+
export const findMatches = (
|
|
19
|
+
text: string,
|
|
20
|
+
query: string,
|
|
21
|
+
config: HighlightConfig,
|
|
22
|
+
): Result<readonly HighlightMatch[], HighlightingError> => {
|
|
23
|
+
try {
|
|
24
|
+
if (query.length === 0) return ok([]);
|
|
25
|
+
|
|
26
|
+
const matches: HighlightMatch[] = [];
|
|
27
|
+
const searchRegex = new RegExp(
|
|
28
|
+
query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"),
|
|
29
|
+
config.caseSensitive ? "g" : "gi",
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
let match: RegExpExecArray | null = searchRegex.exec(text);
|
|
33
|
+
while (match !== null) {
|
|
34
|
+
matches.push({
|
|
35
|
+
text: match[0],
|
|
36
|
+
start: match.index,
|
|
37
|
+
end: match.index + match[0].length,
|
|
38
|
+
});
|
|
39
|
+
match = searchRegex.exec(text);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return ok(matches);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return err({
|
|
45
|
+
kind: "highlighting-error",
|
|
46
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Apply highlighting to text with matches.
|
|
53
|
+
*/
|
|
54
|
+
export const applyHighlighting = (
|
|
55
|
+
text: string,
|
|
56
|
+
matches: readonly HighlightMatch[],
|
|
57
|
+
highlightClass: string,
|
|
58
|
+
): string => {
|
|
59
|
+
if (matches.length === 0) return text;
|
|
60
|
+
|
|
61
|
+
let result = "";
|
|
62
|
+
let lastIndex = 0;
|
|
63
|
+
|
|
64
|
+
for (const match of matches) {
|
|
65
|
+
result += text.slice(lastIndex, match.start);
|
|
66
|
+
result += `<mark class="${highlightClass}">${match.text}</mark>`;
|
|
67
|
+
lastIndex = match.end;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
result += text.slice(lastIndex);
|
|
71
|
+
return result;
|
|
72
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API for the search-highlighting module.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { createDomHighlightingAdapter } from "./application/dom-highlighting-adapter";
|
|
6
|
+
export * from "./domain/operations/highlighting-operations";
|
|
7
|
+
export * from "./ports/highlighting-port";
|
|
8
|
+
export * from "./types/highlighting";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Port for search highlighting operations.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type HighlightingPort = {
|
|
6
|
+
/**
|
|
7
|
+
* Highlight search terms in content.
|
|
8
|
+
*/
|
|
9
|
+
readonly highlight: (
|
|
10
|
+
content: string,
|
|
11
|
+
query: string,
|
|
12
|
+
config: unknown,
|
|
13
|
+
) => Promise<string>;
|
|
14
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search highlighting types for highlighting search terms in content.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type HighlightConfig = {
|
|
6
|
+
readonly enabled: boolean;
|
|
7
|
+
readonly highlightClass: string;
|
|
8
|
+
readonly caseSensitive: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type HighlightMatch = {
|
|
12
|
+
readonly text: string;
|
|
13
|
+
readonly start: number;
|
|
14
|
+
readonly end: number;
|
|
15
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Optimization Use Cases
|
|
3
|
+
*
|
|
4
|
+
* Orchestration layer for SEO optimization
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { generateMetaTags } from "../../domain";
|
|
8
|
+
import type { MetaTags, SeoOptions } from "../../types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Optimize content for SEO
|
|
12
|
+
*/
|
|
13
|
+
export const optimizeSeo = (
|
|
14
|
+
content: string,
|
|
15
|
+
options: SeoOptions = {},
|
|
16
|
+
): MetaTags => {
|
|
17
|
+
return generateMetaTags(content, options);
|
|
18
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Operations
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for SEO optimization
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { MetaTags, SeoOptions } from "../types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Extract title from markdown content
|
|
11
|
+
*/
|
|
12
|
+
export const extractTitle = (content: string): string | null => {
|
|
13
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
14
|
+
return titleMatch?.[1] ? titleMatch[1].trim() : null;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Extract description from markdown content
|
|
19
|
+
*/
|
|
20
|
+
export const extractDescription = (
|
|
21
|
+
content: string,
|
|
22
|
+
maxLength = 160,
|
|
23
|
+
): string => {
|
|
24
|
+
const lines = content.split("\n");
|
|
25
|
+
const descriptionLines: string[] = [];
|
|
26
|
+
|
|
27
|
+
for (const line of lines) {
|
|
28
|
+
if (line.startsWith("#")) continue;
|
|
29
|
+
if (line.trim() === "") continue;
|
|
30
|
+
if (line.startsWith("```")) break;
|
|
31
|
+
|
|
32
|
+
descriptionLines.push(line.trim());
|
|
33
|
+
if (descriptionLines.join(" ").length >= maxLength) break;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const description = descriptionLines.join(" ");
|
|
37
|
+
return description.length > maxLength
|
|
38
|
+
? `${description.substring(0, maxLength - 3)}...`
|
|
39
|
+
: description;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Extract keywords from markdown content
|
|
44
|
+
*/
|
|
45
|
+
export const extractKeywords = (content: string): string[] => {
|
|
46
|
+
const words = content
|
|
47
|
+
.toLowerCase()
|
|
48
|
+
.replace(/[^\w\s]/g, "")
|
|
49
|
+
.split(/\s+/)
|
|
50
|
+
.filter((word) => word.length > 3);
|
|
51
|
+
|
|
52
|
+
const frequency: Record<string, number> = {};
|
|
53
|
+
for (const word of words) {
|
|
54
|
+
frequency[word] = (frequency[word] || 0) + 1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const sorted = Object.entries(frequency)
|
|
58
|
+
.sort(([, a], [, b]) => b - a)
|
|
59
|
+
.slice(0, 10)
|
|
60
|
+
.map(([word]) => word);
|
|
61
|
+
|
|
62
|
+
return sorted;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generate meta tags from content
|
|
67
|
+
*/
|
|
68
|
+
export const generateMetaTags = (
|
|
69
|
+
content: string,
|
|
70
|
+
options: SeoOptions = {},
|
|
71
|
+
): MetaTags => {
|
|
72
|
+
const title = extractTitle(content);
|
|
73
|
+
const description = extractDescription(content);
|
|
74
|
+
const keywords = extractKeywords(content);
|
|
75
|
+
|
|
76
|
+
const metaTags: MetaTags = {
|
|
77
|
+
title: title || undefined,
|
|
78
|
+
description,
|
|
79
|
+
keywords,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
if (options.generateOg) {
|
|
83
|
+
metaTags.ogTitle = title || undefined;
|
|
84
|
+
metaTags.ogDescription = description;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (options.generateTwitter) {
|
|
88
|
+
metaTags.twitterCard = "summary_large_image";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return metaTags;
|
|
92
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Optimization Ports
|
|
3
|
+
*
|
|
4
|
+
* Module-specific interfaces for SEO optimization
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { MetaTags, SeoOptions } from "../types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Meta tag generator interface
|
|
11
|
+
*/
|
|
12
|
+
export interface MetaTagGenerator {
|
|
13
|
+
/** Generate meta tags HTML */
|
|
14
|
+
generateMetaHtml(tags: MetaTags): string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Re-export types for convenience
|
|
18
|
+
export type { MetaTags, SeoOptions };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Optimization Types
|
|
3
|
+
*
|
|
4
|
+
* Domain types for SEO optimization
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Meta tags
|
|
9
|
+
*/
|
|
10
|
+
export interface MetaTags {
|
|
11
|
+
/** Title */
|
|
12
|
+
title?: string;
|
|
13
|
+
/** Description */
|
|
14
|
+
description?: string;
|
|
15
|
+
/** Keywords */
|
|
16
|
+
keywords?: string[];
|
|
17
|
+
/** Author */
|
|
18
|
+
author?: string;
|
|
19
|
+
/** Open Graph title */
|
|
20
|
+
ogTitle?: string;
|
|
21
|
+
/** Open Graph description */
|
|
22
|
+
ogDescription?: string;
|
|
23
|
+
/** Open Graph image */
|
|
24
|
+
ogImage?: string;
|
|
25
|
+
/** Twitter card */
|
|
26
|
+
twitterCard?: "summary" | "summary_large_image";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* SEO options
|
|
31
|
+
*/
|
|
32
|
+
export interface SeoOptions {
|
|
33
|
+
/** Generate meta tags */
|
|
34
|
+
generateMeta?: boolean;
|
|
35
|
+
/** Generate Open Graph tags */
|
|
36
|
+
generateOg?: boolean;
|
|
37
|
+
/** Generate Twitter card tags */
|
|
38
|
+
generateTwitter?: boolean;
|
|
39
|
+
/** Generate canonical URL */
|
|
40
|
+
generateCanonical?: boolean;
|
|
41
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS theme adapter implementation.
|
|
3
|
+
* Provides CSS-based theme application.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getCssVariables } from "../domain/operations/theme-operations";
|
|
7
|
+
import type { ThemePort } from "../ports/theme-port";
|
|
8
|
+
import type { ThemeConfig } from "../types/theme";
|
|
9
|
+
|
|
10
|
+
export const createCssThemeAdapter = (): ThemePort => ({
|
|
11
|
+
loadTheme: async (themePath: string) => {
|
|
12
|
+
if (typeof window === "undefined") {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const response = await fetch(themePath);
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
throw new Error(`Failed to load theme: ${response.statusText}`);
|
|
20
|
+
}
|
|
21
|
+
const content = await response.text();
|
|
22
|
+
return content;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error("Theme load error:", error);
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
applyTheme: async (config?: unknown) => {
|
|
30
|
+
if (typeof window === "undefined") {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const themeConfig = config as ThemeConfig;
|
|
35
|
+
if (!themeConfig) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const cssVarsResult = getCssVariables(themeConfig);
|
|
40
|
+
if (!cssVarsResult.ok) {
|
|
41
|
+
console.error("Failed to get CSS variables:", cssVarsResult.error);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const root = document.documentElement;
|
|
46
|
+
for (const [key, value] of Object.entries(cssVarsResult.value)) {
|
|
47
|
+
root.style.setProperty(key, value);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure theme operations.
|
|
3
|
+
* No I/O. No exceptions thrown. Returns Result types.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Result } from "@create-docs/shared/types";
|
|
7
|
+
import { err, ok } from "@create-docs/shared/types";
|
|
8
|
+
import type { ThemeConfig, ThemeVariant } from "../../types/theme";
|
|
9
|
+
|
|
10
|
+
export type ThemeError = {
|
|
11
|
+
readonly kind: "theme-error";
|
|
12
|
+
readonly message: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Validate theme config.
|
|
17
|
+
*/
|
|
18
|
+
export const validateThemeConfig = (config: ThemeConfig): boolean => {
|
|
19
|
+
if (!config.name) return false;
|
|
20
|
+
if (!config.colors || Object.keys(config.colors).length === 0) return false;
|
|
21
|
+
return true;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get CSS variables from theme config.
|
|
26
|
+
*/
|
|
27
|
+
export const getCssVariables = (
|
|
28
|
+
config: ThemeConfig,
|
|
29
|
+
): Result<Record<string, string>, ThemeError> => {
|
|
30
|
+
try {
|
|
31
|
+
const variables: Record<string, string> = {};
|
|
32
|
+
|
|
33
|
+
for (const [key, value] of Object.entries(config.colors)) {
|
|
34
|
+
variables[`--color-${key}`] = value;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (const [key, value] of Object.entries(config.spacing)) {
|
|
38
|
+
variables[`--spacing-${key}`] = `${value}px`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return ok(variables);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return err({
|
|
44
|
+
kind: "theme-error",
|
|
45
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Apply theme variant.
|
|
52
|
+
*/
|
|
53
|
+
export const applyThemeVariant = (
|
|
54
|
+
config: ThemeConfig,
|
|
55
|
+
variant: ThemeVariant,
|
|
56
|
+
): ThemeConfig => {
|
|
57
|
+
if (variant === "dark") {
|
|
58
|
+
return {
|
|
59
|
+
...config,
|
|
60
|
+
colors: {
|
|
61
|
+
...config.colors,
|
|
62
|
+
background: "#1a1a1a",
|
|
63
|
+
foreground: "#ffffff",
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return config;
|
|
68
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme types for custom theme system.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type ThemeConfig = {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly colors: Readonly<Record<string, string>>;
|
|
8
|
+
readonly fonts: Readonly<Record<string, string>>;
|
|
9
|
+
readonly spacing: Readonly<Record<string, number>>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type ThemeVariant = "light" | "dark" | "auto";
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown TOC adapter implementation.
|
|
3
|
+
* Provides TOC generation from markdown content using native string parsing.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { parseError, pluginError } from "@create-docs/shared/errors";
|
|
7
|
+
import {
|
|
8
|
+
buildHierarchicalToc,
|
|
9
|
+
generateTocId,
|
|
10
|
+
} from "../domain/operations/toc-operations";
|
|
11
|
+
import type { TocPort } from "../ports/toc-port";
|
|
12
|
+
import type { TocConfig, TocItemEnhanced } from "../types/toc";
|
|
13
|
+
|
|
14
|
+
const defaultTocConfig: TocConfig = {
|
|
15
|
+
enabled: true,
|
|
16
|
+
maxDepth: 6,
|
|
17
|
+
minHeadingLevel: 1,
|
|
18
|
+
includeSubheadings: true,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const resolveTocConfig = (config: unknown): TocConfig => {
|
|
22
|
+
const cfg = config as Partial<TocConfig> | undefined;
|
|
23
|
+
if (!cfg) return defaultTocConfig;
|
|
24
|
+
return {
|
|
25
|
+
enabled: cfg.enabled ?? defaultTocConfig.enabled,
|
|
26
|
+
maxDepth: cfg.maxDepth ?? defaultTocConfig.maxDepth,
|
|
27
|
+
minHeadingLevel: cfg.minHeadingLevel ?? defaultTocConfig.minHeadingLevel,
|
|
28
|
+
includeSubheadings:
|
|
29
|
+
cfg.includeSubheadings ?? defaultTocConfig.includeSubheadings,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const stripMarkdownLinks = (text: string): string =>
|
|
34
|
+
text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
|
35
|
+
|
|
36
|
+
const extractHeadingsFromMarkdown = (
|
|
37
|
+
content: string,
|
|
38
|
+
config: TocConfig,
|
|
39
|
+
): readonly TocItemEnhanced[] => {
|
|
40
|
+
const headings: TocItemEnhanced[] = [];
|
|
41
|
+
let inCodeBlock = false;
|
|
42
|
+
|
|
43
|
+
for (const raw of content.split(/\r?\n/)) {
|
|
44
|
+
const line = raw.trim();
|
|
45
|
+
|
|
46
|
+
if (line.startsWith("```")) {
|
|
47
|
+
inCodeBlock = !inCodeBlock;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (inCodeBlock) continue;
|
|
52
|
+
|
|
53
|
+
const match = line.match(/^(#{1,6})\s+(.+)$/);
|
|
54
|
+
if (!match) continue;
|
|
55
|
+
|
|
56
|
+
const level = match[1].length;
|
|
57
|
+
if (level < config.minHeadingLevel || level > config.maxDepth) continue;
|
|
58
|
+
|
|
59
|
+
const text = stripMarkdownLinks(match[2].trim());
|
|
60
|
+
headings.push({
|
|
61
|
+
id: generateTocId(text),
|
|
62
|
+
text,
|
|
63
|
+
level,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return headings;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const isTocItem = (value: unknown): value is TocItemEnhanced =>
|
|
71
|
+
value !== null &&
|
|
72
|
+
typeof value === "object" &&
|
|
73
|
+
"id" in value &&
|
|
74
|
+
"text" in value &&
|
|
75
|
+
"level" in value;
|
|
76
|
+
|
|
77
|
+
export const createMarkdownTocAdapter = (): TocPort => ({
|
|
78
|
+
extractHeadings: async (content: string, config?: unknown) => {
|
|
79
|
+
try {
|
|
80
|
+
const cfg = resolveTocConfig(config);
|
|
81
|
+
return extractHeadingsFromMarkdown(content, cfg);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
throw parseError("<input>", "Failed to extract headings", error);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
generateToc: async (headings: readonly unknown[], config?: unknown) => {
|
|
88
|
+
try {
|
|
89
|
+
const cfg = resolveTocConfig(config);
|
|
90
|
+
const typedHeadings = headings.filter(isTocItem);
|
|
91
|
+
const result = buildHierarchicalToc(typedHeadings, cfg);
|
|
92
|
+
|
|
93
|
+
if (!result.ok) {
|
|
94
|
+
throw pluginError("Failed to build hierarchical TOC", {
|
|
95
|
+
context: { error: String(result.error) },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return result.value;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
throw pluginError("Failed to generate TOC", {
|
|
102
|
+
context: { config, error: String(error) },
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure TOC operations.
|
|
3
|
+
* No I/O. No exceptions thrown. Returns Result types.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Result } from "@create-docs/shared/types";
|
|
7
|
+
import { err, ok } from "@create-docs/shared/types";
|
|
8
|
+
import type { TocConfig, TocItemEnhanced } from "../../types/toc";
|
|
9
|
+
|
|
10
|
+
export type TocError = {
|
|
11
|
+
readonly kind: "toc-error";
|
|
12
|
+
readonly message: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Build hierarchical TOC from flat headings.
|
|
17
|
+
*/
|
|
18
|
+
export const buildHierarchicalToc = (
|
|
19
|
+
headings: readonly TocItemEnhanced[],
|
|
20
|
+
config: TocConfig,
|
|
21
|
+
): Result<TocItemEnhanced[], TocError> => {
|
|
22
|
+
try {
|
|
23
|
+
const root: TocItemEnhanced[] = [];
|
|
24
|
+
const stack: TocItemEnhanced[] = [];
|
|
25
|
+
|
|
26
|
+
for (const heading of headings) {
|
|
27
|
+
if (heading.level < config.minHeadingLevel) continue;
|
|
28
|
+
if (heading.level > config.maxDepth) continue;
|
|
29
|
+
|
|
30
|
+
while (stack.length > 0) {
|
|
31
|
+
const top = stack[stack.length - 1];
|
|
32
|
+
if (top && top.level >= heading.level) {
|
|
33
|
+
stack.pop();
|
|
34
|
+
} else {
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const item: TocItemEnhanced = { ...heading, children: [] };
|
|
40
|
+
|
|
41
|
+
if (stack.length === 0) {
|
|
42
|
+
root.push(item);
|
|
43
|
+
} else {
|
|
44
|
+
const parent = stack[stack.length - 1];
|
|
45
|
+
if (parent?.children) {
|
|
46
|
+
parent.children = [...parent.children, item];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
stack.push(item);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return ok(root);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return err({
|
|
56
|
+
kind: "toc-error",
|
|
57
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generate TOC ID from heading text.
|
|
64
|
+
*/
|
|
65
|
+
export const generateTocId = (text: string): string => {
|
|
66
|
+
return text
|
|
67
|
+
.toLowerCase()
|
|
68
|
+
.replace(/[^a-z0-9\s-]/g, "")
|
|
69
|
+
.trim()
|
|
70
|
+
.replace(/\s+/g, "-");
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Flatten hierarchical TOC.
|
|
75
|
+
*/
|
|
76
|
+
export const flattenToc = (
|
|
77
|
+
toc: readonly TocItemEnhanced[],
|
|
78
|
+
): readonly TocItemEnhanced[] => {
|
|
79
|
+
const result: TocItemEnhanced[] = [];
|
|
80
|
+
|
|
81
|
+
const flatten = (items: readonly TocItemEnhanced[]) => {
|
|
82
|
+
for (const item of items) {
|
|
83
|
+
result.push({ ...item, children: undefined });
|
|
84
|
+
if (item.children) {
|
|
85
|
+
flatten(item.children);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
flatten(toc);
|
|
91
|
+
return result;
|
|
92
|
+
};
|