diff --git a/packages/plugin-docusaurus-v3/package.json b/packages/plugin-docusaurus-v3/package.json
index a0997a5fe..0cd9dd7cf 100644
--- a/packages/plugin-docusaurus-v3/package.json
+++ b/packages/plugin-docusaurus-v3/package.json
@@ -1,16 +1,16 @@
{
"name": "@orama/plugin-docusaurus-v3",
- "version": "3.0.0-rc-2",
- "description": "Docusaurus plugin for local search powered by orama",
+ "version": "2.1.1",
+ "description": "Docusaurus plugin for local search powered by Orama",
"keywords": ["orama", "docusaurus"],
"license": "Apache-2.0",
"bugs": {
- "url": "https://github.com/orama/orama/issues"
+ "url": "https://github.com/askorama/orama/issues"
},
- "homepage": "https://docs.oramasearch.com/open-source/plugins/plugin-docusaurus.html",
+ "homepage": "https://docs.orama.com/cloud/data-sources/native-integrations/docusaurus",
"repository": {
"type": "git",
- "url": "git+https://github.com/orama/orama.git"
+ "url": "git+https://github.com/askorama/orama.git"
},
"sideEffects": false,
"main": "dist/index.js",
@@ -23,10 +23,11 @@
"dependencies": {
"@orama/highlight": "^0.1.5",
"@orama/orama": "workspace:*",
+ "@orama/react-components": "^0.0.26",
"@orama/plugin-analytics": "workspace:*",
"@orama/plugin-parsedoc": "workspace:*",
- "@orama/searchbox": "^1.0.0-rc53",
- "@oramacloud/client": "^1.0.14",
+ "@orama/switch": "workspace:*",
+ "@oramacloud/client": "^1.3.16",
"github-slugger": "^2.0.0",
"gray-matter": "^4.0.3",
"jsdom": "^23.2.0",
diff --git a/packages/plugin-docusaurus-v3/src/index.ts b/packages/plugin-docusaurus-v3/src/index.ts
index a13c2dd5d..0bc8572bd 100644
--- a/packages/plugin-docusaurus-v3/src/index.ts
+++ b/packages/plugin-docusaurus-v3/src/index.ts
@@ -4,12 +4,11 @@ import { cp } from 'node:fs/promises'
import { gzip } from 'pako'
import { resolve } from 'node:path'
// @ts-ignore
-import { presets } from '@orama/searchbox'
import { create, insertMultiple, save } from '@orama/orama'
import { JSDOM } from 'jsdom'
import MarkdownIt from 'markdown-it'
import matter from 'gray-matter'
-import { createSnapshot, deployIndex, fetchEndpointConfig } from './utils'
+import { createSnapshot, deployIndex, DOCS_PRESET_SCHEMA, fetchEndpointConfig } from "./utils"
import { parseMarkdownHeadingId, writeMarkdownHeadingId } from '@docusaurus/utils'
enum DeployType {
@@ -31,7 +30,8 @@ type PluginOptions = {
apiKey: string
indexId: string
}
- cloud?: CloudConfig
+ cloud?: CloudConfig,
+ searchbox?: { [key:string]: any }
}
export default function OramaPluginDocusaurus(
@@ -60,7 +60,7 @@ export default function OramaPluginDocusaurus(
async allContentLoaded({ actions, allContent }) {
const isDevelopment =
- process.env.NODE_ENV === 'development' || (options.cloud && !options.cloud?.oramaCloudAPIKey)
+ process.env.NODE_ENV === 'development' || !options.cloud?.oramaCloudAPIKey || !options.cloud?.deploy
const docsInstances: string[] = []
const oramaCloudAPIKey = options.cloud?.oramaCloudAPIKey
const searchDataConfig = [
@@ -161,7 +161,8 @@ export default function OramaPluginDocusaurus(
actions.setGlobalData({
searchData: Object.fromEntries([['current', readFileSync(indexPath(ctx.generatedFilesDir, 'current'))]]),
docsInstances,
- availableVersions: versions
+ availableVersions: versions,
+ searchBoxCustomConfig: options.searchbox ?? {}
})
} else {
const deployConfig = options.cloud && {
@@ -181,6 +182,7 @@ export default function OramaPluginDocusaurus(
docsInstances,
availableVersions: versions,
analytics: options.analytics,
+ searchBoxCustomConfig: options.searchbox ?? {},
endpoint: {
url: endpointConfig?.endpoint,
key: endpointConfig?.public_api_key
@@ -328,7 +330,7 @@ async function deployData({
}
} else {
const db = await create({
- schema: { ...presets.docs.schema, version: 'enum' }
+ schema: { ...DOCS_PRESET_SCHEMA, version: 'enum' }
})
await insertMultiple(db, oramaDocs as any)
diff --git a/packages/plugin-docusaurus-v3/src/theme/SearchBar/index.tsx b/packages/plugin-docusaurus-v3/src/theme/SearchBar/index.tsx
index 9c20c17e5..90d4ca999 100644
--- a/packages/plugin-docusaurus-v3/src/theme/SearchBar/index.tsx
+++ b/packages/plugin-docusaurus-v3/src/theme/SearchBar/index.tsx
@@ -1,73 +1,74 @@
// @ts-nocheck
-import React from 'react'
-import { useLocation } from '@docusaurus/router'
-import { useActiveVersion, useVersions } from '@docusaurus/plugin-content-docs/client'
-import { useDocsPreferredVersion } from '@docusaurus/theme-common'
-import { usePluginData } from '@docusaurus/useGlobalData'
-import { SearchBox, SearchButton } from '@orama/searchbox'
-import { useOrama } from './useOrama'
-
-interface PluginData {
- searchData: {
- current: { data: ArrayBuffer } | null
- }
- endpoint: { url: string; key: string } | null
- analytics: { apiKey: string; indexId: string; enabled: boolean } | null
- docsInstances: string[]
-}
+import React from "react"
+import { useLocation } from "@docusaurus/router"
+import { useActiveVersion, useVersions } from "@docusaurus/plugin-content-docs/client"
+import { useDocsPreferredVersion } from "@docusaurus/theme-common"
+import { usePluginData } from "@docusaurus/useGlobalData"
+import { OramaSearchBox, OramaSearchButton } from "@orama/react-components"
+import { useOrama, PluginData } from "./useOrama"
export function OramaSearchNoDocs() {
- const { searchBoxConfig, colorMode } = useOrama()
+ const { searchBoxConfig, colorMode } = useOrama()
- return (
-
-
- {searchBoxConfig && (
-
- )}
-
- )
+ return (
+
+
+ {searchBoxConfig.basic && (
+
+ )}
+
+ )
}
export function OramaSearchWithDocs({ pluginId }: { pluginId: string }) {
- const versions = useVersions(pluginId)
- const activeVersion = useActiveVersion(pluginId)
- const { preferredVersion } = useDocsPreferredVersion(pluginId)
- const currentVersion = activeVersion || preferredVersion || versions[0]
- const { searchBoxConfig, colorMode } = useOrama()
- const searchParams = {
- ...(currentVersion && {
- where: {
- version: { eq: currentVersion.name }
- }
- })
- }
+ const versions = useVersions(pluginId)
+ const activeVersion = useActiveVersion(pluginId)
+ const { preferredVersion } = useDocsPreferredVersion(pluginId)
+ const currentVersion = activeVersion || preferredVersion || versions[0]
+ const { searchBoxConfig, colorMode } = useOrama()
+
+ const searchParams = {
+ ...(currentVersion && {
+ where: {
+ version: { eq: currentVersion.name }
+ }
+ })
+ }
- return (
-
-
- {searchBoxConfig && (
-
- )}
-
- )
+ return (
+
+
+ {searchBoxConfig.basic && (
+
+ )}
+
+ )
}
export default function OramaSearchWrapper() {
- const { pathname } = useLocation()
- const { docsInstances }: PluginData = usePluginData('@orama/plugin-docusaurus-v3') as PluginData
- const pluginId = docsInstances.filter((id: string) => pathname.includes(id))[0] || docsInstances[0]
- if (!pluginId) {
- return
- }
- return
+ const { pathname } = useLocation()
+ const { docsInstances }: PluginData = usePluginData("@orama/plugin-docusaurus-v3") as PluginData
+ const pluginId = docsInstances.filter((id: string) => pathname.includes(id))[0] || docsInstances[0]
+
+ if (!pluginId) {
+ return
+ }
+
+ return
}
diff --git a/packages/plugin-docusaurus-v3/src/theme/SearchBar/useOrama.ts b/packages/plugin-docusaurus-v3/src/theme/SearchBar/useOrama.ts
index 9204e64ef..77ec4cc3b 100644
--- a/packages/plugin-docusaurus-v3/src/theme/SearchBar/useOrama.ts
+++ b/packages/plugin-docusaurus-v3/src/theme/SearchBar/useOrama.ts
@@ -1,31 +1,45 @@
-// @ts-nocheck
+//@ts-nocheck
import { useEffect, useState } from 'react'
+import { Switch } from '@orama/switch'
import useBaseUrl from '@docusaurus/useBaseUrl'
import useIsBrowser from '@docusaurus/useIsBrowser'
import { useColorMode } from '@docusaurus/theme-common'
import { usePluginData } from '@docusaurus/useGlobalData'
import { ungzip } from 'pako'
-import { presets } from '@orama/searchbox'
import { OramaClient } from '@oramacloud/client'
import { create, insertMultiple } from '@orama/orama'
import { pluginAnalytics } from '@orama/plugin-analytics'
-import '@orama/searchbox/dist/index.css'
+import { DOCS_PRESET_SCHEMA } from '../../utils'
+
+export interface PluginData {
+ searchData: {
+ current: { data: ArrayBuffer } | null
+ },
+ searchBoxCustomConfig?: { [key:string]: any }
+ endpoint: { url: string; key: string } | null
+ analytics: { apiKey: string; indexId: string; enabled: boolean } | null
+ docsInstances: string[]
+}
export const useOrama = () => {
- const [searchBoxConfig, setSearchBoxConfig] = useState(null)
+ const [searchBoxConfig, setSearchBoxConfig] = useState({
+ basic: null,
+ custom: null
+ })
const { colorMode } = useColorMode()
- const { searchData, endpoint, analytics }: PluginData = usePluginData('@orama/plugin-docusaurus-v3') as PluginData
+ const { searchData, endpoint, analytics, searchBoxCustomConfig }: PluginData = usePluginData('@orama/plugin-docusaurus-v3') as PluginData
const baseURL = useBaseUrl('orama-search-index-current.json.gz')
const isBrowser = useIsBrowser()
+
useEffect(() => {
async function loadOrama() {
+ let oramaInstance = null
+
if (endpoint?.url) {
- setSearchBoxConfig({
- oramaInstance: new OramaClient({
- endpoint: endpoint.url,
- api_key: endpoint.key
- })
+ oramaInstance = new OramaClient({
+ endpoint: endpoint.url,
+ api_key: endpoint.key
})
} else {
let buffer
@@ -48,26 +62,32 @@ export const useOrama = () => {
const parsedDeflated = JSON.parse(deflated)
const db = await create({
- schema: { ...presets.docs.schema, version: 'enum' },
+ schema: { ...DOCS_PRESET_SCHEMA, version: 'enum' },
plugins: [
...(analytics
? [
- pluginAnalytics({
- apiKey: analytics.apiKey,
- indexId: analytics.indexId,
- enabled: analytics.enabled
- })
- ]
+ pluginAnalytics({
+ apiKey: analytics.apiKey,
+ indexId: analytics.indexId,
+ enabled: analytics.enabled
+ })
+ ]
: [])
]
})
await insertMultiple(db, Object.values(parsedDeflated.docs.docs))
- setSearchBoxConfig({
- oramaInstance: db
- })
+ oramaInstance = new Switch(db)
}
+
+ setSearchBoxConfig({
+ basic: {
+ clientInstance: oramaInstance,
+ disableChat: !endpoint?.url
+ },
+ custom: searchBoxCustomConfig
+ })
}
if (!isBrowser) {
@@ -79,5 +99,5 @@ export const useOrama = () => {
})
}, [isBrowser])
- return { searchBoxConfig, colorMode }
+ return { searchBoxConfig, colorMode, clientMode: endpoint?.url ? 'cloud' : 'oss' }
}
diff --git a/packages/plugin-docusaurus-v3/src/utils.ts b/packages/plugin-docusaurus-v3/src/utils.ts
index 618006c64..f398582e9 100644
--- a/packages/plugin-docusaurus-v3/src/utils.ts
+++ b/packages/plugin-docusaurus-v3/src/utils.ts
@@ -82,3 +82,12 @@ export async function deployIndex(baseUrl: string, APIKey: string, indexId: stri
'End: Deploy index (success)'
)
}
+
+export const DOCS_PRESET_SCHEMA = {
+ title: 'string',
+ content: 'string',
+ path: 'string',
+ section: 'string',
+ category: 'enum',
+ version: 'enum'
+}
diff --git a/packages/plugin-docusaurus/.editorconfig b/packages/plugin-docusaurus/.editorconfig
deleted file mode 100644
index 257894844..000000000
--- a/packages/plugin-docusaurus/.editorconfig
+++ /dev/null
@@ -1,6 +0,0 @@
-[*]
-end_of_line = lf
-insert_final_newline = true
-charset = utf-8
-indent_style = space
-indent_size = 2
\ No newline at end of file
diff --git a/packages/plugin-docusaurus/.eslintrc.cjs b/packages/plugin-docusaurus/.eslintrc.cjs
deleted file mode 100644
index bdf7a31ba..000000000
--- a/packages/plugin-docusaurus/.eslintrc.cjs
+++ /dev/null
@@ -1,45 +0,0 @@
-module.exports = {
- env: {
- node: true,
- browser: true
- },
- parserOptions: {
- project: 'tsconfig.test.json',
- sourceType: 'module'
- },
- globals: {
- Deno: 'readonly'
- },
- root: true,
- parser: '@typescript-eslint/parser',
- // plugins: ['import'],
- extends: [
- 'plugin:import/typescript',
- // 'standard-with-typescript',
- 'plugin:react/recommended',
- 'plugin:react/jsx-runtime',
- 'react-app'
- ],
- rules: {
- 'import/extensions': [2, 'always', { ignorePackages: true }], // This is required for proper ESM use
- 'no-async-promise-executor': 0,
- 'space-before-function-paren': 0, // This is inserted to make this compatible with prettier.
- '@typescript-eslint/promise-function-async': 0,
- '@typescript-eslint/return-await': 0,
- '@typescript-eslint/space-before-function-paren': 0, // This is inserted to make this compatible with prettier.
- '@typescript-eslint/strict-boolean-expressions': 0
- },
- overrides: [
- {
- files: ['test/**/*.ts'],
- rules: {
- '@typescript-eslint/no-floating-promises': 0,
- '@typescript-eslint/no-non-null-assertion': 0
- }
- },
- {
- files: ['test/ci/**/*.js'],
- parser: 'espree'
- }
- ]
-}
diff --git a/packages/plugin-docusaurus/.swcrc b/packages/plugin-docusaurus/.swcrc
deleted file mode 100644
index 9beaad1a9..000000000
--- a/packages/plugin-docusaurus/.swcrc
+++ /dev/null
@@ -1,59 +0,0 @@
-[
- {
- "test": ".*\\.ts$",
- "jsc": {
- "target": "es2022",
- "parser": {
- "syntax": "typescript",
- "tsx": false,
- "dynamicImport": true,
- "importAssertions": true
- },
- "experimental": {
- "keepImportAssertions": true
- }
- },
- "sourceMaps": true
- },
- {
- "test": ".*\\.tsx$",
- "jsc": {
- "target": "es2022",
- "parser": {
- "syntax": "typescript",
- "tsx": true,
- "dynamicImport": true,
- "importAssertions": true
- },
- "transform": {
- "react": {
- "runtime": "automatic"
- }
- },
- "experimental": {
- "keepImportAssertions": true
- }
- },
- "sourceMaps": true
- },
- {
- "test": ".*\\.cts$",
- "module": {
- "type": "commonjs",
- "ignoreDynamic": true
- },
- "jsc": {
- "target": "es2022",
- "parser": {
- "syntax": "typescript",
- "tsx": false,
- "dynamicImport": true,
- "importAssertions": true
- },
- "experimental": {
- "keepImportAssertions": true
- }
- },
- "sourceMaps": true
- }
-]
diff --git a/packages/plugin-docusaurus/package.json b/packages/plugin-docusaurus/package.json
index 2f3f4091e..cfe60d530 100644
--- a/packages/plugin-docusaurus/package.json
+++ b/packages/plugin-docusaurus/package.json
@@ -1,97 +1,55 @@
{
"name": "@orama/plugin-docusaurus",
- "version": "3.0.0-rc-2",
- "description": "Docusaurus plugin for local search powered by orama",
+ "version": "2.1.1",
+ "description": "Docusaurus plugin for local search powered by Orama",
"keywords": ["orama", "docusaurus"],
"license": "Apache-2.0",
"bugs": {
- "url": "https://github.com/orama/plugin-docusaurus/issues"
+ "url": "https://github.com/askorama/orama/issues"
},
- "homepage": "https://github.com/orama/plugin-docusaurus#readme",
+ "homepage": "https://docs.orama.com/cloud/data-sources/native-integrations/docusaurus",
"repository": {
"type": "git",
- "url": "git+https://github.com/orama/plugin-docusaurus.git"
+ "url": "git+https://github.com/askorama/orama.git"
},
- "type": "module",
"sideEffects": false,
- "exports": {
- ".": {
- "types": "./dist/server/index.d.ts",
- "import": "./dist/server/index.js",
- "require": "./dist/server/commonjs.cjs"
- },
- "./types": {
- "types": "./dist/server/types.d.ts",
- "import": "./dist/server/types.js",
- "require": "./dist/server/types.cjs"
- }
- },
- "types": "./dist/server/index.d.ts",
- "files": ["dist"],
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
"scripts": {
- "dev": "swc --delete-dir-on-start -s -w --extensions .ts,.tsx,.cts -d dist src",
- "prebuild": "npm run lint",
- "build": "swc --delete-dir-on-start --extensions .ts,.tsx,.cts -d dist src",
+ "build": "rm -rf dist && tsc",
"postbuild": "sh scripts/postbuild.sh",
- "pretest": "node ../../scripts/test-pack.mjs plugin-docusaurus",
- "test": "echo \"Docusaurus v2 will become unmaintained soon. Use Docusaurus v3.\" # node --loader=tsx --no-warnings=loader --test ./test/integration.ts | tap-mocha-reporter spec",
- "format": "prettier -w src",
- "lint": "eslint src --ext .js,.ts,.tsx,.cts",
- "changelog": "auto-changelog -p"
+ "watch": "tsc --watch"
},
"dependencies": {
- "@algolia/autocomplete-js": "^1.7.2",
- "@algolia/autocomplete-theme-classic": "^1.7.3",
- "@docusaurus/theme-common": "^2.4.3",
- "@docusaurus/plugin-content-docs": "^2.4.3",
- "@orama/highlight": "^0.1.2",
+ "@orama/highlight": "^0.1.5",
"@orama/orama": "workspace:*",
+ "@orama/react-components": "^0.0.26",
"@orama/plugin-analytics": "workspace:*",
"@orama/plugin-parsedoc": "workspace:*",
+ "@orama/switch": "workspace:*",
+ "@oramacloud/client": "^1.3.16",
"github-slugger": "^2.0.0",
+ "gray-matter": "^4.0.3",
+ "jsdom": "^23.2.0",
+ "markdown-it": "^13.0.2",
"pako": "^2.1.0",
- "vfile-message": "^3.1.3"
+ "tslib": "^2.6.2",
+ "vfile-message": "^3.1.4"
},
"devDependencies": {
- "@commitlint/cli": "^17.4.0",
- "@commitlint/config-conventional": "^17.4.0",
- "@docusaurus/module-type-aliases": "^2.4.3",
- "@docusaurus/types": "^2.4.3",
- "@swc/cli": "^0.1.59",
- "@swc/core": "^1.3.27",
- "@types/node": "^18.11.17",
- "@types/pako": "^2.0.0",
- "@types/react": "^18.0.26",
- "@types/react-dom": "^18.0.9",
- "@typescript-eslint/eslint-plugin": "^6.4.1",
- "@typescript-eslint/parser": "^6.4.1",
- "eslint": "^8.48.0",
- "eslint-config-react": "^1.1.7",
- "eslint-config-react-app": "^7.0.1",
- "eslint-config-standard": "^17.0.0",
- "eslint-config-standard-with-typescript": "^39.0.0",
- "eslint-plugin-import": "^2.28.1",
- "eslint-plugin-n": "^16.0.2",
- "eslint-plugin-promise": "^6.1.1",
- "eslint-plugin-react": "^7.33.2",
- "husky": "^8.0.3",
- "lint-staged": "^13.1.0",
- "prettier": "^2.8.1",
- "react": "^17.0.2",
- "react-dom": "^17.0.2",
- "tap-mocha-reporter": "^5.0.3",
- "tsx": "^3.12.2",
- "typescript": "^5.0.0",
- "webpack": "^5.75.0"
+ "@types/jsdom": "^21.1.6",
+ "@types/markdown-it": "^13.0.7",
+ "@types/pako": "^2.0.3"
},
"peerDependencies": {
- "react": "^17.0.2",
- "react-dom": "^17.0.2"
+ "@docusaurus/plugin-content-docs": "2.x",
+ "@docusaurus/theme-common": "2.x",
+ "@docusaurus/types": "2.x",
+ "@docusaurus/utils": "2.x",
+ "react": "17.x",
+ "react-dom": "17.x"
},
"publishConfig": {
"access": "public"
- },
- "lint-staged": {
- "*.{ts, tsx}": "eslint ./src --cache --fix"
}
}
diff --git a/packages/plugin-docusaurus/prettier.config.cjs b/packages/plugin-docusaurus/prettier.config.cjs
deleted file mode 100644
index 731687114..000000000
--- a/packages/plugin-docusaurus/prettier.config.cjs
+++ /dev/null
@@ -1,8 +0,0 @@
-module.exports = {
- printWidth: 120,
- semi: false,
- singleQuote: true,
- bracketSpacing: true,
- trailingComma: 'none',
- arrowParens: 'avoid'
-}
diff --git a/packages/plugin-docusaurus/scripts/postbuild.sh b/packages/plugin-docusaurus/scripts/postbuild.sh
index 9eacfd08a..1eb091979 100755
--- a/packages/plugin-docusaurus/scripts/postbuild.sh
+++ b/packages/plugin-docusaurus/scripts/postbuild.sh
@@ -1,15 +1,4 @@
#!/bin/bash
-set -x -e
-
-# tsc -p . --emitDeclarationOnly
-# tsc -p tsconfig.cjs.json --emitDeclarationOnly
-# tsc --module CommonJS --outDir dist/server/cjs/ src/server/types.ts
-
-mv dist/server/commonjs.js dist/server/commonjs.cjs
-mv dist/server/commonjs.js.map dist/server/commonjs.cjs.map
-mv dist/server/cjs/types.js dist/server/types.cjs && rm -r dist/server/cjs
-
-cp -a src/translationMessages dist/translationMessages
-cp src/client/theme/SearchBar/*.css dist/client/theme/SearchBar
-cp src/client/theme/SearchBarFooter/*.css dist/client/theme/SearchBarFooter
+mkdir -p dist/theme/SearchBar
+cp src/theme/SearchBar/*.css dist/theme/SearchBar
\ No newline at end of file
diff --git a/packages/plugin-docusaurus/src/client/theme/OramaLogo/index.tsx b/packages/plugin-docusaurus/src/client/theme/OramaLogo/index.tsx
deleted file mode 100644
index 7cab02b9a..000000000
--- a/packages/plugin-docusaurus/src/client/theme/OramaLogo/index.tsx
+++ /dev/null
@@ -1,673 +0,0 @@
-const logoDarkStyle = `
-.st0{fill:#F1F1F1;}
-.st1{clip-path:url(#SVGID_00000173879314932002601690000004467558796121400750_);fill:#FB81B8;}
-.st2{clip-path:url(#SVGID_00000110465739266569342360000009994806409646629266_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000109749856959687867430000005922655113651493051_);}
-.st3{clip-path:url(#SVGID_00000083772220377844668640000014270627887136862871_);fill:url(#SVGID_00000023970480408666843750000009139365288006549676_);}
-.st4{clip-path:url(#SVGID_00000007387021808622077600000008231332769069855412_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000096037922040833498710000000345973257815506326_);}
-.st5{clip-path:url(#SVGID_00000155131937825044918480000012477285865566750606_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000085956322674693249330000002897762106153069457_);}
-.st6{clip-path:url(#SVGID_00000007427917354384892590000008323849365716177816_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000100354159152759772890000018306988680613241258_);}
-.st7{clip-path:url(#SVGID_00000117676152446614577000000003290884631243360662_);fill-rule:evenodd;clip-rule:evenodd;fill:#F97CBF;}
-.st8{clip-path:url(#SVGID_00000045603176312755833640000016830543301894725252_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000055669823316971348430000007456605679816995478_);}
-.st9{clip-path:url(#SVGID_00000050637087109987908300000001655297821271179658_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000121276425135176461840000001411911869491139213_);}
-.st10{clip-path:url(#SVGID_00000155862216285633165090000011392877503296698766_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000028307555131462038650000001206007083441086101_);}
-`
-
-const logoLightStyle = `
-.st0{fill:#3A393D;}
-.st1{clip-path:url(#SVGID_00000121237917302734654570000007802433009826490514_);fill:#FB81B8;}
-.st2{clip-path:url(#SVGID_00000121237917302734654570000007802433009826490514_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000067951042823557912200000003375363423373398917_);}
-.st3{clip-path:url(#SVGID_00000121237917302734654570000007802433009826490514_);fill:url(#SVGID_00000173158817079393216730000015416942586975534773_);}
-.st4{clip-path:url(#SVGID_00000121237917302734654570000007802433009826490514_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000093858169607081702240000004176952752693074346_);}
-.st5{clip-path:url(#SVGID_00000121237917302734654570000007802433009826490514_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000070090918966372163940000003253904251979675325_);}
-.st6{clip-path:url(#SVGID_00000121237917302734654570000007802433009826490514_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000132776349315605697860000002688660824302203828_);}
-.st7{clip-path:url(#SVGID_00000121237917302734654570000007802433009826490514_);fill-rule:evenodd;clip-rule:evenodd;fill:#F97CBF;}
-.st8{clip-path:url(#SVGID_00000121237917302734654570000007802433009826490514_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000016755478859824984880000007136540152028842637_);}
-.st9{clip-path:url(#SVGID_00000121237917302734654570000007802433009826490514_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000016785315492321026870000005125100075421436076_);}
-.st10{clip-path:url(#SVGID_00000121237917302734654570000007802433009826490514_);fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_00000164475283253175513550000014604273281455785608_);}
-`
-
-// Keep up to date with: https://docs.oramasearch.com/logo/logo-orama-dark.svg
-export function OramaLogoDark(): JSX.Element {
- return (
-
- )
-}
-
-// Keep up to date with: https://docs.oramasearch.com/logo/logo-orama-light.svg
-export function OramaLogoLight(): JSX.Element {
- return (
-
- )
-}
diff --git a/packages/plugin-docusaurus/src/client/theme/SearchBar/index.tsx b/packages/plugin-docusaurus/src/client/theme/SearchBar/index.tsx
deleted file mode 100644
index 4667e9cf2..000000000
--- a/packages/plugin-docusaurus/src/client/theme/SearchBar/index.tsx
+++ /dev/null
@@ -1,204 +0,0 @@
-import { autocomplete } from '@algolia/autocomplete-js'
-import '@algolia/autocomplete-theme-classic/dist/theme.min.css'
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error
-// @ts-ignore Will fail in CJS compilation
-import { GlobalVersion, useActiveVersion, useVersions } from '@docusaurus/plugin-content-docs/client'
-import { useColorMode, useDocsPreferredVersion } from '@docusaurus/theme-common'
-import useBaseUrl from '@docusaurus/useBaseUrl'
-import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
-import { usePluginData } from '@docusaurus/useGlobalData'
-import useIsBrowser from '@docusaurus/useIsBrowser'
-import { AnyDocument, OramaPlugin, create, load, Orama, RawData, search as oramaSearch } from '@orama/orama'
-import { pluginAnalytics, PluginAnalyticsParams } from '@orama/plugin-analytics'
-import { Highlight } from '@orama/highlight'
-import { ungzip } from 'pako'
-import { createElement, Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'
-import { render } from 'react-dom'
-// @ts-expect-error Resolve at runtime
-import { SearchNoResults } from '@theme/SearchNoResults'
-// @ts-expect-error Resolve at runtime
-import { SearchResults } from '@theme/SearchResults'
-// @ts-expect-error Resolve at runtime
-import { SearchResult } from '@theme/SearchResult'
-import { Hit, INDEX_FILE, PLUGIN_NAME, PluginData, schema } from '../../../server/types.js'
-import { useLocation } from '@docusaurus/router'
-
-const highlighter = new Highlight({
- CSSClass: 'aa-ItemContentHighlight',
- HTMLTag: 'span'
-})
-
-export default function SearchBar(): JSX.Element {
- const isBrowser = useIsBrowser()
- const { searchData, analytics, pluginContentDocsIds } = usePluginData(PLUGIN_NAME) as PluginData & {
- analytics: PluginAnalyticsParams
- }
- const { siteConfig } = useDocusaurusContext()
- const { pathname } = useLocation()
- const pluginId = pluginContentDocsIds.filter((id: string) => pathname.includes(id))[0] || pluginContentDocsIds[0]
- const containerRef = useRef(null)
- const { colorMode } = useColorMode()
- const [database, setDatabase] = useState>()
- const searchBaseUrl = useBaseUrl(INDEX_FILE)
- const versions = useVersions(pluginId)
- const activeVersion = useActiveVersion(pluginId)
- const { preferredVersion } = useDocsPreferredVersion(pluginId)
-
- const version = useMemo(() => {
- if (!isBrowser) {
- return undefined
- } else if (activeVersion) {
- return activeVersion
- } else if (preferredVersion) {
- return preferredVersion
- }
-
- // Fallback - Return the latest version or the first one existing
- return versions.find((v: GlobalVersion) => v.isLast) ?? versions[0]
- }, [isBrowser, activeVersion, preferredVersion, versions])
-
- const onKeyDown = useCallback(
- function (setIsOpen: (value: boolean) => void, event: KeyboardEvent) {
- const isOpen = containerRef.current?.querySelector('[role="combobox"]')?.getAttribute('aria-expanded') === 'true'
-
- if (
- (event.key?.toLowerCase() === 'escape' && isOpen) ||
- (event.key?.toLowerCase() === 'k' && (event.metaKey || event.ctrlKey))
- ) {
- event.preventDefault()
- setIsOpen(!isOpen)
- }
- },
- [containerRef]
- )
-
- useEffect(() => {
- if (!containerRef.current || !isBrowser || !database) {
- return undefined
- }
-
- const search = autocomplete({
- placeholder: 'Search ...',
- container: containerRef.current,
- // @ts-expect-error render typing here is for preact, react also works
- renderer: { createElement, Fragment, render },
- openOnFocus: true,
- detachedMediaQuery: '', // always detached
- async getSources({ query: term }): Promise {
- return [
- {
- sourceId: 'orama',
- async getItems() {
- if (!term) {
- return []
- }
-
- const results = await oramaSearch(database, {
- term,
- mode: 'fulltext',
- properties: ['sectionTitle', 'sectionContent', 'type']
- })
-
- return results.hits.flatMap((hit) => {
- return {
- ...hit,
- document: {
- ...hit.document,
- sectionContent: highlighter.highlight(hit.document.sectionContent, term).trim(20)
- }
- }
- })
- },
- getItemUrl({ item }: { item: Hit }) {
- return item.document.pageRoute
- },
- templates: {
- item({ item }: { item: Hit }) {
- return
- }
- }
- }
- ]
- },
- render({ sections, render }, root) {
- render(, root)
- },
- renderNoResults({ render, state }, root) {
- render(, root)
- }
- })
-
- const handler = onKeyDown.bind(null, search.setIsOpen)
- window.addEventListener('keydown', handler)
-
- // Move keyboard instructions at the end - Apparently this is only possible manually
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const button = containerRef.current.querySelector('.aa-DetachedSearchButton')!
- const icons = containerRef.current.querySelectorAll('kbd')
-
- for (const icon of Array.from(icons)) {
- button.appendChild(icon.cloneNode(true))
- }
-
- return () => {
- window.removeEventListener('keydown', handler)
- search.destroy()
- }
- }, [isBrowser, siteConfig, database, colorMode, onKeyDown])
-
- useEffect(() => {
- async function loadDatabase(version: GlobalVersion): Promise {
- let buffer: ArrayBuffer
-
- if (searchData[version.name]) {
- buffer = searchData[version.name].data
- } else {
- const searchResponse = await fetch(searchBaseUrl.replace('@VERSION@', version.name))
-
- if (searchResponse.status === 0) {
- throw new Error(`Network error: ${await searchResponse.text()}`)
- } else if (searchResponse.status !== 200) {
- throw new Error(`HTTP error ${searchResponse.status}: ${await searchResponse.text()}`)
- }
-
- buffer = await searchResponse.arrayBuffer()
- }
-
- const deflated = ungzip(buffer, { to: 'string' })
- const data: RawData = JSON.parse(deflated)
-
- const pluginAnalyticsConfig = analytics ? pluginAnalytics(analytics) : ({} as OramaPlugin)
-
- const _db = await create({
- schema,
- plugins: [pluginAnalyticsConfig]
- })
-
- await load(_db, data)
-
- setDatabase(_db)
- }
-
- if (!isBrowser || !version) {
- return
- }
-
- loadDatabase(version).catch((error) => {
- console.error('Cannot load search index.', error)
- })
- }, [isBrowser, searchData, searchBaseUrl, version])
-
- useEffect(() => {
- colorMode === 'dark' ? document.body.classList.add(colorMode) : document.body.classList.remove('dark')
- }, [colorMode])
-
- return (
-
- {/* We need to use a template here since apparently there is no easy way to customize the input box */}
-
- ⌘
- K
-
-
- )
-}
diff --git a/packages/plugin-docusaurus/src/client/theme/SearchBar/logo-orama-dark.svg b/packages/plugin-docusaurus/src/client/theme/SearchBar/logo-orama-dark.svg
deleted file mode 100644
index 164257214..000000000
--- a/packages/plugin-docusaurus/src/client/theme/SearchBar/logo-orama-dark.svg
+++ /dev/null
@@ -1,223 +0,0 @@
-
-
-
diff --git a/packages/plugin-docusaurus/src/client/theme/SearchBar/logo-orama-light.svg b/packages/plugin-docusaurus/src/client/theme/SearchBar/logo-orama-light.svg
deleted file mode 100644
index d1621b7bd..000000000
--- a/packages/plugin-docusaurus/src/client/theme/SearchBar/logo-orama-light.svg
+++ /dev/null
@@ -1,148 +0,0 @@
-
-
-
diff --git a/packages/plugin-docusaurus/src/client/theme/SearchBar/style.css b/packages/plugin-docusaurus/src/client/theme/SearchBar/style.css
deleted file mode 100644
index 64b54d8df..000000000
--- a/packages/plugin-docusaurus/src/client/theme/SearchBar/style.css
+++ /dev/null
@@ -1,85 +0,0 @@
-:root {
- /* TODO relate this to docusarus css vars (which are hex values) */
- --aa-primary-color-rgb: var(--aa-text-color-rgb);
- --aa-footer-height: 3.5rem;
-}
-
-.aa-DetachedSearchButton {
- /* shadow and gradient are taken from docsearch */
- --key-shadow: inset 0 -2px 0 0 #cdcde6, inset 0 0 1px 1px #fff, 0 1px 2px 1px rgba(30, 35, 90, 0.4);
- --key-gradient: linear-gradient(-225deg, #d5dbe4, #f8f8f8);
- border-radius: 9999px;
- height: 36px;
- background: var(--ifm-color-emphasis-200);
- border: none;
- color: initial;
- display: flex;
- flex-direction: row;
- align-items: center;
- gap: 0.5rem;
- padding: 0 1.5rem;
-}
-
-.aa-DetachedSearchButton kbd {
- align-items: center;
- background: var(--key-gradient);
- border-radius: 2px;
- box-shadow: var(--key-shadow);
- display: flex;
- height: 18px;
- justify-content: center;
- padding: 0 0 1px;
- border: 0;
- width: 20px;
-}
-
-.aa-DetachedSearchButton kbd:last-child {
- margin-right: 0.5em;
-}
-
-.aa-DetachedSearchButton .aa-DetachedSearchButtonPlaceholder,
-.aa-DetachedSearchButton .aa-DetachedSearchButtonQuery {
- flex: 1;
- white-space: nowrap;
-}
-
-/* shadow and gradient are taken from docsearch */
-[data-theme='dark'] .aa-DetachedSearchButton {
- --key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, 0.3);
- --key-gradient: linear-gradient(-26.5deg, #565872, #31355b);
-}
-
-.navbar .aa-DetachedSearchButton {
- width: 200px;
- height: 36px;
-}
-
-.aa-DetachedSearchButtonIcon {
- color: inherit;
- width: unset;
-}
-
-.aa-DetachedContainer .aa-PanelLayout {
- bottom: var(--aa-footer-height);
-}
-
-.aa-NoResults {
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 2rem;
- font-size: 0.9em;
- color: var(--ifm-color-emphasis-600);
-}
-
-.aa-ItemLink {
- margin-bottom: .6rem;
- text-decoration: none !important;
- color: rgba(var(--aa-primary-color-rgb), 1) !important;
- padding: calc(var(--aa-spacing-half)/1.2);
-}
-
-.aa-ItemContentHighlight {
- color: rgba(var(--aa-primary-color-rgb), 1);
- font-weight: bold;
-}
\ No newline at end of file
diff --git a/packages/plugin-docusaurus/src/client/theme/SearchBarFooter/index.tsx b/packages/plugin-docusaurus/src/client/theme/SearchBarFooter/index.tsx
deleted file mode 100644
index 0e75200ab..000000000
--- a/packages/plugin-docusaurus/src/client/theme/SearchBarFooter/index.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-// @ts-expect-error Resolve at runtime
-import { OramaLogoDark, OramaLogoLight } from '@theme/OramaLogo'
-
-import { useColorMode } from '@docusaurus/theme-common'
-
-export interface SearchBarFooterTranslations {
- selectText?: string
- selectKeyAriaLabel?: string
- navigateText?: string
- navigateUpKeyAriaLabel?: string
- navigateDownKeyAriaLabel?: string
- closeText?: string
- closeKeyAriaLabel?: string
- searchByText?: string
-}
-
-interface SearchBarFooterProps {
- translations?: SearchBarFooterTranslations
-}
-
-interface CommandIconProps {
- children: React.ReactNode
- ariaLabel: string
-}
-
-function CommandIcon(props: CommandIconProps): JSX.Element {
- return (
-
- )
-}
-
-export function SearchBarFooter({ translations = {} }: SearchBarFooterProps): JSX.Element {
- const {
- selectText = 'to select',
- selectKeyAriaLabel = 'Enter key',
- navigateText = 'to navigate',
- navigateUpKeyAriaLabel = 'Arrow up',
- navigateDownKeyAriaLabel = 'Arrow down',
- closeText = 'to close',
- closeKeyAriaLabel = 'Escape key',
- searchByText = 'Powered by'
- } = translations
-
- const { colorMode } = useColorMode()
-
- return (
-
- )
-}
diff --git a/packages/plugin-docusaurus/src/client/theme/SearchBarFooter/style.css b/packages/plugin-docusaurus/src/client/theme/SearchBarFooter/style.css
deleted file mode 100644
index f0509f62d..000000000
--- a/packages/plugin-docusaurus/src/client/theme/SearchBarFooter/style.css
+++ /dev/null
@@ -1,76 +0,0 @@
-/* shadow and gradient are taken from docsearch */
-.aa-Footer {
- --key-shadow: inset 0 -2px 0 0 #cdcde6, inset 0 0 1px 1px #fff, 0 1px 2px 1px rgba(30, 35, 90, 0.4);
- --key-gradient: linear-gradient(-225deg, #d5dbe4, #f8f8f8);
- align-items: center;
- display: flex;
- justify-content: space-between;
- border-top: 1px solid rgba(var(--aa-panel-border-color-rgb), var(--aa-panel-border-color-alpha));
- color: var(--ifm-color-emphasis-600);
- -webkit-user-select: none;
- user-select: none;
- padding: 1rem;
- width: 100%;
- font-size: 0.8rem;
- height: var(--aa-footer-height);
- position: absolute;
- background-color: rgba(var(--aa-background-color-rgb), var(--aa-background-color-alpha));
- bottom: 0;
-}
-
-.aa-Footer:first-child {
- border-top: 0;
-}
-
-.aa-DetachedContainer.aa-DetachedContainer--modal .aa-Footer {
- position: static;
-}
-
-/* shadow and gradient are taken from docsearch */
-[data-theme='dark'] .aa-Footer {
- --key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, 0.3);
- --key-gradient: linear-gradient(-26.5deg, #565872, #31355b);
-}
-
-ul.aa-FooterCommands {
- list-style: none;
- padding: 0;
- margin: 0;
- display: flex;
- gap: 1rem;
-}
-
-ul.aa-FooterCommands li {
- display: inherit;
-}
-
-.aa-FooterCommands kbd {
- align-items: center;
- background: var(--key-gradient);
- border-radius: 2px;
- box-shadow: var(--key-shadow);
- display: flex;
- height: 18px;
- justify-content: center;
- margin-right: 0.4em;
- padding: 0 0 1px;
- border: 0;
- width: 20px;
-}
-
-a.aa-FooterSearchCredit {
- display: flex;
- color: inherit;
- align-items: center;
- gap: 1ch;
- --ifm-link-hover-color: inherit;
- --ifm-link-hover-decoration: none;
-}
-
-a.aa-FooterSearchCredit span {
- white-space: nowrap;
-}
-
-a.aa-FooterSearchCredit svg {
- min-width: 7em;
-}
diff --git a/packages/plugin-docusaurus/src/client/theme/SearchNoResults/index.tsx b/packages/plugin-docusaurus/src/client/theme/SearchNoResults/index.tsx
deleted file mode 100644
index 798cebe74..000000000
--- a/packages/plugin-docusaurus/src/client/theme/SearchNoResults/index.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { ColorModeProvider } from '@docusaurus/theme-common/internal'
-// @ts-expect-error Resolve at runtime
-import { SearchBarFooter } from '@theme/SearchBarFooter'
-
-export interface SearchNoResultsProps {
- query: string
-}
-
-export function SearchNoResults({ query }: SearchNoResultsProps): JSX.Element {
- return (
-
- {query ? No results found.
: ''}
-
-
- )
-}
diff --git a/packages/plugin-docusaurus/src/client/theme/SearchResult/index.tsx b/packages/plugin-docusaurus/src/client/theme/SearchResult/index.tsx
deleted file mode 100644
index afd7f974a..000000000
--- a/packages/plugin-docusaurus/src/client/theme/SearchResult/index.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Result } from '@orama/orama'
-import { SectionSchema } from '../../../server/types.js'
-
-interface SearchResultProps {
- hit: Result
-}
-
-export function SearchResult({ hit }: SearchResultProps): JSX.Element {
- return (
-
-
-
-
-
{hit.document.sectionTitle as string}
-
-
-
-
-
- )
-}
diff --git a/packages/plugin-docusaurus/src/client/theme/SearchResults/index.tsx b/packages/plugin-docusaurus/src/client/theme/SearchResults/index.tsx
deleted file mode 100644
index 0436b51a0..000000000
--- a/packages/plugin-docusaurus/src/client/theme/SearchResults/index.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-// @ts-expect-error Resolve at runtime
-import { SearchBarFooter } from '@theme/SearchBarFooter'
-
-import type { VNode } from '@algolia/autocomplete-js'
-import { ColorModeProvider } from '@docusaurus/theme-common/internal'
-
-export interface SearchResultsProps {
- sections?: VNode[]
-}
-
-export function SearchResults({ sections }: SearchResultsProps): JSX.Element {
- return (
-
- {sections}
-
-
- )
-}
diff --git a/packages/plugin-docusaurus/src/index.ts b/packages/plugin-docusaurus/src/index.ts
new file mode 100644
index 000000000..a0f524ee6
--- /dev/null
+++ b/packages/plugin-docusaurus/src/index.ts
@@ -0,0 +1,344 @@
+import { readFileSync, writeFileSync } from 'node:fs'
+import type { Plugin } from '@docusaurus/types'
+import { cp } from 'node:fs/promises'
+import { gzip } from 'pako'
+import { resolve } from 'node:path'
+// @ts-ignore
+import { create, insertMultiple, save } from '@orama/orama'
+import { JSDOM } from 'jsdom'
+import MarkdownIt from 'markdown-it'
+import matter from 'gray-matter'
+import { createSnapshot, deployIndex, DOCS_PRESET_SCHEMA, fetchEndpointConfig } from "./utils"
+import { parseMarkdownHeadingId, writeMarkdownHeadingId } from '@docusaurus/utils'
+
+enum DeployType {
+ SNAPSHOT_ONLY = 'snapshot-only',
+ DEFAULT = 'default'
+}
+
+type CloudConfig = {
+ deploy: DeployType | false
+ endpoint: string
+ indexId: string
+ oramaCloudAPIKey?: string
+ public_api_key: string
+}
+
+type PluginOptions = {
+ analytics?: {
+ enabled: boolean
+ apiKey: string
+ indexId: string
+ }
+ cloud?: CloudConfig,
+ searchbox?: { [key:string]: any }
+}
+
+export default function OramaPluginDocusaurus(
+ ctx: {
+ siteDir: any
+ generatedFilesDir: any
+ },
+ options: PluginOptions
+): Plugin {
+ let versions: any[] = []
+
+ return {
+ name: '@orama/plugin-docusaurus',
+
+ getThemePath() {
+ return '../dist/theme'
+ },
+
+ getTypeScriptThemePath() {
+ return '../src/theme'
+ },
+
+ getClientModules() {
+ return ['../dist/theme/SearchBar/index.css']
+ },
+
+ async contentLoaded({ actions, allContent }) {
+ const isDevelopment =
+ process.env.NODE_ENV === 'development' || !options.cloud?.oramaCloudAPIKey || !options.cloud?.deploy
+ const docsInstances: string[] = []
+ const oramaCloudAPIKey = options.cloud?.oramaCloudAPIKey
+ const searchDataConfig = [
+ {
+ docs: allContent['docusaurus-plugin-content-docs']
+ },
+ {
+ blogs: allContent['docusaurus-plugin-content-blog']
+ },
+ {
+ pages: allContent['docusaurus-plugin-content-pages']
+ }
+ ]
+ const allOramaDocsPromises: Promise[] = []
+
+ searchDataConfig.forEach((config) => {
+ const [key, value] = Object.entries(config)[0]
+ switch (key) {
+ case 'docs':
+ if (!value) break
+ Object.keys(value).forEach((docsInstance: any) => {
+ const loadedVersions = value?.[docsInstance]?.loadedVersions
+ versions = loadedVersions.map((v: any) => v.versionName)
+ docsInstances.push(docsInstance)
+ versions.flatMap(async (version) => {
+ const currentVersion = loadedVersions.find((v: any) => v.versionName === version)
+ allOramaDocsPromises.push(
+ ...currentVersion.docs.map((data: any) =>
+ generateDocs({
+ siteDir: ctx.siteDir,
+ version,
+ category: docsInstance,
+ data
+ })
+ )
+ )
+ })
+ })
+ break
+ case 'blogs':
+ if (!value) break
+ Object.keys(value).forEach(async (instance) => {
+ const loadedInstance = value[instance]
+ allOramaDocsPromises.push(
+ ...loadedInstance.blogPosts.map(({ metadata }: any) => {
+ return generateDocs({
+ siteDir: ctx.siteDir,
+ version: 'current',
+ category: 'blogs',
+ data: metadata
+ })
+ })
+ )
+ })
+ break
+ case 'pages':
+ if (!value) break
+ Object.keys(value).forEach(async (instance) => {
+ const loadedInstance = value[instance]
+ allOramaDocsPromises.push(
+ ...loadedInstance.map((data: any) =>
+ generateDocs({
+ siteDir: ctx.siteDir,
+ version: 'current',
+ category: 'pages',
+ data
+ })
+ )
+ )
+ })
+
+ break
+ }
+ })
+
+ const oramaDocs = (await Promise.all(allOramaDocsPromises)).flat().reduce((acc, curr) => {
+ if (!!curr.title && !!curr.content) {
+ acc.push({
+ title: curr.title,
+ content: curr.content,
+ section: curr.originalTitle,
+ version: curr.version,
+ path: curr.path,
+ category: curr.category
+ })
+ }
+
+ return acc
+ }, [])
+
+ if (isDevelopment) {
+ await deployData({
+ oramaDocs,
+ generatedFilesDir: ctx.generatedFilesDir,
+ version: 'current'
+ })
+
+ actions.setGlobalData({
+ searchData: Object.fromEntries([['current', readFileSync(indexPath(ctx.generatedFilesDir, 'current'))]]),
+ docsInstances,
+ availableVersions: versions,
+ searchBoxCustomConfig: options.searchbox ?? {}
+ })
+ } else {
+ const deployConfig = options.cloud && {
+ indexId: options.cloud.indexId,
+ oramaCloudAPIKey,
+ type: options.cloud.deploy
+ }
+
+ const endpointConfig = await deployData({
+ oramaDocs,
+ generatedFilesDir: ctx.generatedFilesDir,
+ version: 'current',
+ deployConfig
+ })
+
+ actions.setGlobalData({
+ docsInstances,
+ availableVersions: versions,
+ analytics: options.analytics,
+ searchBoxCustomConfig: options.searchbox ?? {},
+ endpoint: {
+ url: endpointConfig?.endpoint,
+ key: endpointConfig?.public_api_key
+ }
+ })
+ }
+ },
+
+ async postBuild({ outDir }) {
+ !options.cloud && (await cp(indexPath(ctx.generatedFilesDir, 'current'), indexPath(outDir, 'current')))
+ }
+ }
+}
+
+async function generateDocs({
+ siteDir,
+ version,
+ category,
+ data
+}: {
+ siteDir: string
+ version: string
+ category: string
+ data: Record
+}) {
+ const { title, permalink, source } = data
+ const fileContent = readFileSync(source.replace('@site', siteDir), 'utf-8')
+ const contentWithoutFrontMatter = matter(fileContent).content
+ const contentWithIds = writeMarkdownHeadingId(contentWithoutFrontMatter)
+
+ return parseHTMLContent({
+ originalTitle: title,
+ version,
+ category,
+ html: new MarkdownIt().render(contentWithIds),
+ path: permalink
+ })
+}
+
+function parseHTMLContent({
+ html,
+ path,
+ originalTitle,
+ version,
+ category
+}: {
+ html: any
+ path: any
+ originalTitle: any
+ version: string
+ category: string
+}) {
+ const dom = new JSDOM(html)
+ const document = dom.window.document
+ const sections: {
+ originalTitle: any
+ title: string
+ header: string
+ content: string
+ version: string
+ category: string
+ path: any
+ }[] = []
+
+ const headers = document.querySelectorAll('h1, h2, h3, h4, h5, h6')
+ if (!headers.length) {
+ sections.push({
+ originalTitle,
+ title: originalTitle,
+ header: 'h1',
+ content: html,
+ version,
+ category,
+ path
+ })
+ }
+ headers.forEach((header) => {
+ const headerText = header.textContent?.trim() ?? ''
+ const headerTag = header.tagName.toLowerCase()
+
+ // Use parseMarkdownHeadingId to extract clean title and section ID
+ const { text: sectionTitle, id: sectionId } = parseMarkdownHeadingId(headerText)
+
+ let sectionContent = ''
+
+ let sibling = header.nextElementSibling
+ while (sibling && !['H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(sibling.tagName)) {
+ sectionContent += sibling.textContent?.trim() + '\n'
+ sibling = sibling.nextElementSibling
+ }
+
+ sections.push({
+ originalTitle,
+ title: sectionTitle ?? '',
+ header: headerTag,
+ content: sectionContent,
+ version,
+ category,
+ path: headerTag === 'h1' ? path : `${removeTrailingSlash(path)}#${sectionId}`
+ })
+ })
+
+ return sections
+}
+
+function removeTrailingSlash(str: string): string {
+ return str.endsWith('/') ? str.slice(0, -1) : str
+}
+
+function indexPath(outDir: string, version: string) {
+ return resolve(outDir, 'orama-search-index-@VERSION@.json.gz'.replace('@VERSION@', version))
+}
+
+async function deployData({
+ oramaDocs,
+ generatedFilesDir,
+ version,
+ deployConfig
+}: {
+ oramaDocs: any[]
+ generatedFilesDir: string
+ version: string
+ deployConfig?:
+ {
+ indexId: string
+ oramaCloudAPIKey: string | undefined
+ type: DeployType | false
+ }
+}) {
+ const { ORAMA_CLOUD_BASE_URL } = process.env
+ const baseUrl = ORAMA_CLOUD_BASE_URL || 'https://cloud.orama.com'
+
+ if (deployConfig?.type) {
+ if (deployConfig.type === DeployType.DEFAULT || deployConfig.type === DeployType.SNAPSHOT_ONLY) {
+ const endpointConfig = await fetchEndpointConfig(baseUrl, deployConfig.oramaCloudAPIKey!, deployConfig.indexId!)
+
+ await createSnapshot(baseUrl, deployConfig.oramaCloudAPIKey!, deployConfig.indexId!, oramaDocs)
+
+ if (deployConfig.type === DeployType.DEFAULT) {
+ await deployIndex(baseUrl, deployConfig.oramaCloudAPIKey!, deployConfig.indexId!)
+ }
+ return endpointConfig
+ } else {
+ throw new Error('Invalid deploy type')
+ }
+ } else {
+ const db = await create({
+ schema: { ...DOCS_PRESET_SCHEMA, version: 'enum' }
+ })
+
+ await insertMultiple(db, oramaDocs as any)
+
+ const serializedOrama = JSON.stringify(await save(db))
+ const gzipedOrama = gzip(serializedOrama)
+ writeFileSync(indexPath(generatedFilesDir, version), gzipedOrama)
+
+ return undefined
+ }
+}
diff --git a/packages/plugin-docusaurus/src/server/commonjs.cts b/packages/plugin-docusaurus/src/server/commonjs.cts
deleted file mode 100644
index 5dc74be24..000000000
--- a/packages/plugin-docusaurus/src/server/commonjs.cts
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { LoadContext, Plugin } from '@docusaurus/types'
-import { PluginOptions } from './types.js'
-
-export type { PluginData, PluginOptions, RawDataWithPositions, SectionSchema } from './types.js'
-
-export type DocusaurusOramaPlugin = (docusaurusContext: LoadContext, options: PluginOptions) => Plugin
-
-let _esmDocusaurusOramaPlugin: DocusaurusOramaPlugin
-
-// eslint-disable-next-line no-new-func, @typescript-eslint/no-implied-eval
-const importDynamic = new Function('modulePath', 'return import(modulePath)')
-
-export default async function docusaurusOramaPlugin(
- ...args: Parameters
-): Promise> {
- if (!_esmDocusaurusOramaPlugin) {
- const imported = await importDynamic('./index.js')
- _esmDocusaurusOramaPlugin = imported.default
- }
-
- return _esmDocusaurusOramaPlugin(...args)
-}
diff --git a/packages/plugin-docusaurus/src/server/index.ts b/packages/plugin-docusaurus/src/server/index.ts
deleted file mode 100644
index eee6f34b3..000000000
--- a/packages/plugin-docusaurus/src/server/index.ts
+++ /dev/null
@@ -1,242 +0,0 @@
-import type { LoadedContent, LoadedVersion } from '@docusaurus/plugin-content-docs'
-import type { LoadContext, Plugin } from '@docusaurus/types'
-import { create, insertMultiple, save } from '@orama/orama'
-import type { DefaultSchemaElement, NodeContent, PopulateFnContext } from '@orama/plugin-parsedoc'
-import { defaultHtmlSchema, populate } from '@orama/plugin-parsedoc'
-import * as githubSlugger from 'github-slugger'
-import { cp, readFile, writeFile } from 'node:fs/promises'
-import { resolve } from 'node:path'
-import { fileURLToPath } from 'node:url'
-import { promisify } from 'node:util'
-import { gzip as gzipCB } from 'node:zlib'
-import type { Configuration as WebpackConfiguration } from 'webpack'
-
-import { retrieveTranslationMessages } from './translationMessages.js'
-import { INDEX_FILE, PLUGIN_NAME, PluginOptions, SectionSchema, schema } from './types.js'
-
-export type { PluginData, PluginOptions, SectionSchema } from './types.js'
-
-const gzip = promisify(gzipCB)
-
-function indexPath(outDir: string, version: string): string {
- return resolve(outDir, INDEX_FILE.replace('@VERSION@', version))
-}
-
-export function transformFn(node: NodeContent, context: PopulateFnContext): NodeContent {
- let raw
-
- switch (node.tag) {
- case 'strong':
- case 'a':
- case 'time':
- case 'code':
- case 'span':
- case 'small':
- case 'b':
- case 'p':
- case 'ul':
- raw = `${node.content}
`
- break
- case 'h1':
- case 'h2':
- case 'h3':
- case 'h4':
- case 'h5':
- case 'h6':
- context.lastLink = node.properties?.id ?? githubSlugger.slug(node.content)
- break
- }
-
- const transformed = {
- ...node,
- additionalProperties: {
- hash: context.lastLink
- }
- }
-
- if (raw) {
- transformed.raw = raw
- }
-
- return transformed
-}
-
-export function defaultToSectionSchema(
- node: DefaultSchemaElement,
- pageRoute: string,
- sectionTitle: string,
- version: string
-): SectionSchema {
- const { content, type, properties } = node
-
- if (!sectionTitle) {
- sectionTitle = (pageRoute.split('/').pop() ?? '')
- .replace(/(-)+/g, ' ')
- .split(' ')
- .map((word) => word && `${word[0].toUpperCase()}${word.substring(1)}`)
- .join(' ')
- }
-
- return {
- pageRoute,
- hash: (properties?.hash as string) ?? '',
- sectionTitle: pageRoute ? sectionTitle : 'Home',
- sectionContent: content,
- type,
- version
- }
-}
-
-function isIndexable(doc: SectionSchema): boolean {
- return (
- !!doc.sectionContent && !!doc.sectionTitle && doc.type !== 'script' && !doc.pageRoute.startsWith('/blogs/tags/')
- )
-}
-
-async function generateDocument(
- siteDir: string,
- { title, version, permalink, source }: Record
-): Promise {
- // Parse the document
- const data = await readFile(source.replace('@site', siteDir))
- const fileType = source.split('.').at(-1)
- const db = await create({ schema: defaultHtmlSchema })
- await populate(db, data, fileType as 'html' | 'md', { transformFn })
-
- // Convert all the documents to a
- const sections = Object.values(db.data.docs.docs)
- .map((node) => {
- return defaultToSectionSchema(node, permalink.slice(1), title, version)
- })
- .filter(isIndexable)
-
- for (const section of sections) {
- if (!section.pageRoute.startsWith('/')) {
- section.pageRoute = '/' + section.pageRoute
- }
-
- if (section.hash) {
- section.pageRoute += `#${section.hash}`
- }
- }
-
- return sections
-}
-
-async function buildDevSearchData(
- siteDir: string,
- outDir: string,
- allContent: any,
- version: string,
- pluginContentDocsIds: string[]
-): Promise {
- const blogs: any[] = []
- const pages: any[] = []
- const docs: any[] = []
- pluginContentDocsIds.forEach((key) => {
- const loadedVersion = allContent['docusaurus-plugin-content-docs']?.[key]?.loadedVersions?.find(
- (v: LoadedVersion) => v.versionName === version
- )
- blogs.push(
- ...(allContent['docusaurus-plugin-content-blog']?.[key]?.blogPosts?.map(({ metadata }: any) => metadata) ?? [])
- )
- pages.push(...(allContent['docusaurus-plugin-content-pages']?.[key] ?? []))
- docs.push(...(loadedVersion?.docs ?? []))
- })
-
- const generator = generateDocument.bind(null, siteDir)
-
- // Gather all pages we want to index
- const documents = [
- ...(await Promise.all(docs.map(generator))),
- ...(await Promise.all(blogs.map(generator))),
- ...(await Promise.all(pages.map(generator)))
- ].flat()
-
- // Create the Orama database and then serialize it
- const _db = await create({
- schema
- })
-
- await insertMultiple(_db, documents)
-
- const serialized = await save(_db)
-
- await writeFile(indexPath(outDir, version), await gzip(JSON.stringify(serialized)))
-}
-
-function getThemePath(): string {
- return fileURLToPath(new URL('../client/theme', import.meta.url))
-}
-
-function docusaurusOramaPlugin(context: LoadContext, options: PluginOptions): Plugin {
- let versions: string[] = []
-
- return {
- name: PLUGIN_NAME,
- getThemePath,
- getPathsToWatch() {
- return [getThemePath()]
- },
- getDefaultCodeTranslationMessages: async () => {
- return retrieveTranslationMessages(context)
- },
- getClientModules() {
- return [resolve(getThemePath(), 'SearchBar/style.css'), resolve(getThemePath(), 'SearchBarFooter/style.css')]
- },
- // @ts-ignore
- configureWebpack(): WebpackConfiguration {
- return {
- resolve: {
- alias: {
- 'react/jsx-dev-runtime': 'react/jsx-dev-runtime.js',
- 'react/jsx-runtime': 'react/jsx-runtime.js'
- }
- }
- }
- },
- async contentLoaded({ actions, allContent }) {
- const isDevelopment = process.env.NODE_ENV === 'development'
- const pluginContentDocsIds = Object.keys(allContent['docusaurus-plugin-content-docs'] ?? {})
- const loadedVersions = (allContent['docusaurus-plugin-content-docs']?.[pluginContentDocsIds[0]] as LoadedContent)
- ?.loadedVersions
- versions = loadedVersions.map((v) => v.versionName)
-
- // Build all versions
- await Promise.all(
- versions.map((version) =>
- buildDevSearchData(context.siteDir, context.generatedFilesDir, allContent, version, pluginContentDocsIds)
- )
- )
-
- for (const name of versions) {
- await buildDevSearchData(context.siteDir, context.generatedFilesDir, allContent, name, pluginContentDocsIds)
- }
-
- if (isDevelopment) {
- actions.setGlobalData({
- pluginContentDocsIds,
- analytics: options.analytics,
- searchData: Object.fromEntries(
- await Promise.all(
- versions.map(async (version) => {
- return [version, await readFile(indexPath(context.generatedFilesDir, version))]
- })
- )
- )
- })
- } else {
- actions.setGlobalData({ pluginContentDocsIds, searchData: {} })
- }
- },
- async postBuild({ outDir }: { outDir: string }) {
- await Promise.all(
- versions.map(async (version) => {
- return cp(indexPath(context.generatedFilesDir, version), indexPath(outDir, version))
- })
- )
- }
- }
-}
-
-export default docusaurusOramaPlugin
diff --git a/packages/plugin-docusaurus/src/server/translationMessages.ts b/packages/plugin-docusaurus/src/server/translationMessages.ts
deleted file mode 100644
index 777cd94f7..000000000
--- a/packages/plugin-docusaurus/src/server/translationMessages.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import type { LoadContext } from '@docusaurus/types'
-import fs from 'fs'
-import path from 'path'
-import { fileURLToPath } from 'url'
-
-function codeTranslationLocalesToTry(locale: string): string[] {
- const intlLocale = new Intl.Locale(locale)
- const maximizedLocale = intlLocale.maximize()
- return [
- locale,
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- `${maximizedLocale.language}-${maximizedLocale.region!}`,
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- `${maximizedLocale.language}-${maximizedLocale.script!}`,
- maximizedLocale.language
- ]
-}
-
-async function retrieveObjectContent(filePath: string): Promise