| const themes = require('prism-react-renderer').themes; | |
| const versionsPlugin = require('./config/versions-plugin'); | |
| const recentBlogsPlugin = require('./config/recent-blogs-plugin.cjs'); | |
| const VERSIONS = require('./versions.json'); | |
| const { markdownBoldPlugin } = require('./config/markdown-bold-plugin'); | |
| const { DEFAULT_VERSION } = require('./src/constant/version'); | |
| const { ssrTemplate } = require('./config/ssrTemplate'); | |
| const customDocusaurusPlugin = require('./config/custom-docusaurus-plugin'); | |
| const REDIRECTS_DEV = require('./config/redirects-dev.json'); | |
| const REDIRECTS_4X = require('./config/redirects-4.x.json'); | |
| const path = require('path'); | |
| const translationDocsPlugin = require('./scripts/i18n/docs-plugin.cjs'); | |
| const translationStatusPlugin = require('./scripts/i18n/status-plugin.cjs'); | |
| const manualPlugins = require('./scripts/i18n/manual-plugins.cjs'); | |
| const {filterContentSitemap} = require('./scripts/i18n/sitemap.cjs'); | |
| const contentTranslationPlugin = require('./scripts/i18n/content-plugin.cjs'); | |
| const {translatedContentPages} = require('./scripts/i18n/source-groups.cjs'); | |
| const searchPlugin = require('./scripts/i18n/search-plugin.cjs'); | |
| const SOURCE_SEARCH_CONTEXTS = ['docs', | |
| {path:'community',label:{en:'Community','zh-CN':'社区'}}, 'course', | |
| {path:'releases',label:{en:'Release Notes','zh-CN':'发行说明'}}]; | |
| const {enabledLocales, enabledLocaleRecords} = require('./config/i18n'); | |
| const {managedLocales} = require('./scripts/i18n/state.cjs'); | |
| const DEFAULT_LOCALE = 'en'; | |
| const ENABLED_LOCALES = enabledLocales(); | |
| // Locales with translation state that are enabled in this build. Empty whenever | |
| // no localized content is being served, which disables the whole render path. | |
| const MANAGED_LOCALES = managedLocales(__dirname, ENABLED_LOCALES); | |
| // Utility routes that are never content: /search renders a noindex search page | |
| // in every locale, and /slack and /telegram are tracking redirects. The sitemap | |
| // filter drops them by exact path, so the set follows the enabled locales | |
| // instead of naming a language. | |
| const UTILITY_PATHS = new Set(ENABLED_LOCALES.flatMap(locale => { | |
| const prefix = locale === DEFAULT_LOCALE ? '' : `/${locale}`; | |
| return [`${prefix}/search`, `${prefix}/slack`, `${prefix}/telegram`]; | |
| })); | |
| // Root-entry selection precedes hydration; theme and course rail preferences | |
| // still apply before paint. Publish builds emit these exact bytes by hash. | |
| const BOOTSTRAPS = require('./scripts/publish/bootstrap.cjs').scripts(__dirname, | |
| {baseUrl:'/',defaultLocale:DEFAULT_LOCALE,locales:ENABLED_LOCALES}); | |
| // Per-document last-update timestamps, generated from git history by | |
| // scripts/last-update/generate.js. The 2-hourly Cron Deploy regenerates the map | |
| // before it builds, so published dates are always current; the committed copy | |
| // is what local builds use, and what a deploy falls back to if that refresh | |
| // fails. markdown.parseFrontMatter (below) injects the dates as `last_update` | |
| // front matter, sparing the build a per-file git lookup. Missing file / | |
| // not-yet-generated map → Docusaurus falls back to its own git lookup. | |
| let DOCS_LAST_UPDATE = {}; | |
| try { | |
| DOCS_LAST_UPDATE = require('./scripts/last-update/data.json'); | |
| } catch (e) { | |
| // Map not generated yet (e.g. first build before the workflow runs). | |
| } | |
| // Group every static in-site redirect by its target path so we can drive it | |
| // through @docusaurus/plugin-client-redirects' `createRedirects` callback. | |
| // The callback runs once per locale build with `existingPath` set to the | |
| // actual routes in that build — so each entry only fires when its target | |
| // truly exists. Plain `redirects:` array entries can't do that: the plugin | |
| // validates the whole array against the current locale's path list, which | |
| // rejects cross-locale targets (e.g. /zh-CN/... during an en-only build). | |
| function buildRedirectIndex() { | |
| const index = {}; | |
| const add = (to, ...froms) => { | |
| (index[to] = index[to] || []).push(...froms); | |
| }; | |
| // Every locale lands its bare docs route on the default stable version's | |
| // entry doc. `lastVersion: '4.x'` makes 4.x the stable version but its | |
| // `path: '4.x'` keeps it under /docs/4.x/, so the bare /docs route has no | |
| // generated page without this redirect. | |
| for (const locale of ENABLED_LOCALES) { | |
| const prefix = locale === 'en' ? '' : `/${locale}`; | |
| add(`${prefix}/docs/4.x/getting-started/what-is-apache-doris`, `${prefix}/docs`); | |
| // Community has no root document; use its first sidebar page as the | |
| // entry instead of exposing the deployment server's directory listing. | |
| add(`${prefix}/community/join-community`, `${prefix}/community`); | |
| } | |
| // Decommissioned standalone pages whose content moved into the | |
| // homepage/why-doris/download flows. | |
| add('/why-doris/users', '/users'); | |
| add('/zh-CN/why-doris/users', '/zh-CN/users'); | |
| add('/download', '/download-next'); | |
| add('/zh-CN/download', '/zh-CN/download-next'); | |
| // The hand-curated /learning sitemap page was retired; Doris 101 is now | |
| // the structured entry point for newcomers. | |
| add('/course', '/learning'); | |
| add('/zh-CN/course', '/zh-CN/learning'); | |
| // The hackathon task pages used to live under an event-specific | |
| // /course/hackathon/glasgow-2026 prefix. They are permanent now, so the | |
| // old URLs (posters, QR codes, bookmarks) redirect to the new ones. | |
| for (const locale of ENABLED_LOCALES) { | |
| const prefix = locale === 'en' ? '' : `/${locale}`; | |
| for (const page of ['a1-hybrid-search', 'a2-mcp', 'a3-log-search', 'a4-agent-traces', 'track-b']) { | |
| add(`${prefix}/course/hackathon/${page}`, `${prefix}/course/hackathon/glasgow-2026/${page}`); | |
| } | |
| } | |
| // /vendors lost its navigation entry in the new-homepage refactor and has | |
| // no obvious successor page, so it lands on the homepage. Its source is | |
| // parked at src/pages/_vendors (underscore = not routed). | |
| add('/', '/vendors'); | |
| add('/zh-CN', '/zh-CN/vendors'); | |
| // /ecosystem/* was retired; its closest spiritual home is the Dev tree's | |
| // Data Integration intro, which catalogs the same connector/tool families | |
| // the old /ecosystem/ pages did. | |
| add( | |
| '/docs/dev/connection-integration/data-integration/intro', | |
| '/ecosystem', | |
| '/ecosystem/cluster-management', | |
| '/ecosystem/connectors', | |
| '/ecosystem/data-loading', | |
| '/ecosystem/data-migration', | |
| '/ecosystem/distributions-and-packaging', | |
| ); | |
| add( | |
| '/zh-CN/docs/dev/connection-integration/data-integration/intro', | |
| '/zh-CN/ecosystem', | |
| '/zh-CN/ecosystem/cluster-management', | |
| '/zh-CN/ecosystem/connectors', | |
| '/zh-CN/ecosystem/data-loading', | |
| '/zh-CN/ecosystem/data-migration', | |
| '/zh-CN/ecosystem/distributions-and-packaging', | |
| ); | |
| // Share explicit Dev aliases with the source-level Markdown link checker. | |
| for (const { from, to } of REDIRECTS_DEV) { | |
| add(to, from); | |
| } | |
| // The DOG Stack deployment page was split into Docker Compose and | |
| // Kubernetes pages; its old URL lands on the Docker Compose page. | |
| for (const version of ['dev', '4.x', '3.x']) { | |
| for (const locale of ['', '/zh-CN']) { | |
| add( | |
| `${locale}/docs/${version}/observability/dogstack/docker`, | |
| `${locale}/docs/${version}/observability/dogstack/deployment`, | |
| ); | |
| } | |
| } | |
| // Row binlog was renamed to changelog before its first release | |
| // (apache/doris#69311), and its pages moved with it. Every locale serves | |
| // these pages, so the old URLs redirect in all of them. | |
| for (const locale of ENABLED_LOCALES) { | |
| const prefix = locale === 'en' ? '' : `/${locale}`; | |
| for (const [from, to] of [ | |
| ['dev/data-operate/incremental/row-binlog', 'dev/data-operate/incremental/changelog'], | |
| ['dev/sql-manual/sql-functions/table-valued-functions/binlog', | |
| 'dev/sql-manual/sql-functions/table-valued-functions/changelog'], | |
| ['dev/key-features/binlog-table-stream', 'dev/key-features/changelog-table-stream'], | |
| ['4.x/key-features/binlog-table-stream', '4.x/key-features/changelog-table-stream'], | |
| ]) { | |
| add(`${prefix}/docs/${to}`, `${prefix}/docs/${from}`); | |
| } | |
| } | |
| // Old 4.x slugs that have no 1:1 counterpart in the new 4.x tree | |
| // (which was reseeded from Dev). Generated by | |
| // scripts/migrate-4.x/build-redirects.py from the diff between | |
| // old-slugs.txt and new-slugs.txt. | |
| for (const { from, to } of REDIRECTS_4X) { | |
| add(to, from); | |
| } | |
| return index; | |
| } | |
| const REDIRECT_INDEX = buildRedirectIndex(); | |
| // Allow filtering doc versions via environment variable. | |
| // Usage: DOCS_VERSIONS="current,4.x" yarn docusaurus build | |
| // This uses Docusaurus's native onlyIncludeVersions option | |
| // instead of modifying versions.json. | |
| const ONLY_VERSIONS = process.env.DOCS_VERSIONS | |
| ? process.env.DOCS_VERSIONS.split(',').map(v => v.trim()).filter(Boolean) | |
| : null; | |
| // Landing-only dev mode: when working on src/components/home-next or | |
| // use-cases-next, no docs/blog/search routes need to load. Setting | |
| // LANDING_ONLY=true disables every heavy content plugin, which drops | |
| // `yarn start` peak memory from ~20GB to ~1-2GB. Individual SKIP_* env | |
| // vars allow finer-grained control. | |
| const LANDING_ONLY = process.env.LANDING_ONLY === 'true'; | |
| const SKIP_DOCS = LANDING_ONLY || process.env.SKIP_DOCS === 'true'; | |
| const SKIP_BLOG = LANDING_ONLY || process.env.SKIP_BLOG === 'true'; | |
| const SKIP_COMMUNITY = LANDING_ONLY || process.env.SKIP_COMMUNITY === 'true'; | |
| const SKIP_RELEASES = LANDING_ONLY || process.env.SKIP_RELEASES === 'true'; | |
| const SKIP_COURSE = LANDING_ONLY || process.env.SKIP_COURSE === 'true'; | |
| const SKIP_SEARCH = LANDING_ONLY || process.env.SKIP_SEARCH === 'true'; | |
| const LINK_BEHAVIOR_VALUES = new Set(['ignore', 'log', 'warn', 'throw']); | |
| function getBrokenLinkBehavior(envName, fallback) { | |
| const value = process.env[envName]; | |
| return LINK_BEHAVIOR_VALUES.has(value) ? value : fallback; | |
| } | |
| const DEFAULT_BROKEN_LINK_BEHAVIOR = 'warn'; | |
| const lightCodeTheme = themes.oneLight; | |
| const logoImg = '/images/logo-doris.svg'; | |
| function getDocsVersions() { | |
| const result = { | |
| current: { | |
| label: 'Dev', | |
| path: 'dev', | |
| banner: 'unreleased', | |
| }, | |
| }; | |
| VERSIONS.map(version => { | |
| result[version] = { | |
| banner: 'none', | |
| }; | |
| if (version === DEFAULT_VERSION) { | |
| result[version].label = DEFAULT_VERSION; | |
| result[version].path = DEFAULT_VERSION; | |
| } else { | |
| result[version].badge = false; | |
| } | |
| }); | |
| return result; | |
| } | |
| function getLatestVersion() { | |
| return VERSIONS.includes(DEFAULT_VERSION) ? DEFAULT_VERSION : VERSIONS[0]; | |
| } | |
| const MAIN_DOCS_OPTIONS = { | |
| includeCurrentVersion: true, | |
| sidebarPath: require.resolve('./sidebars.ts'), | |
| ...(ONLY_VERSIONS && { onlyIncludeVersions: ONLY_VERSIONS }), | |
| // When filtering versions, lastVersion must be in the | |
| // included list. Fall back to the first included version. | |
| lastVersion: ONLY_VERSIONS && !ONLY_VERSIONS.includes(getLatestVersion()) | |
| ? ONLY_VERSIONS[0] | |
| : getLatestVersion(), | |
| versions: getDocsVersions(), | |
| // editUrl: ({ locale, versionDocsDirPath, docPath }) => { | |
| // return `https://github.com/apache/doris-website/edit/master/docs/${locale}/docs/${docPath}`; | |
| // // if (versionDocsDirPath === 'versioned_docs/version-dev') { | |
| // // return `https://github.com/apache/doris-website/edit/master/docs/${locale}/docs/${docPath}`; | |
| // // } | |
| // }, | |
| showLastUpdateAuthor: false, | |
| // Date comes from the injected `last_update` front matter | |
| // (markdown.parseFrontMatter). The DocItem layout limits | |
| // which versions actually display it (Dev + latest stable). | |
| showLastUpdateTime: true, | |
| remarkPlugins: [markdownBoldPlugin, require('remark-math')], | |
| rehypePlugins: [ | |
| [ | |
| require('rehype-katex'), | |
| { | |
| strict: process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true' ? false : 'warn', | |
| } | |
| ] | |
| ] | |
| }; | |
| /** @type {import('@docusaurus/types').Config} */ | |
| const config = { | |
| title: 'Apache Doris', | |
| titleDelimiter: '-', | |
| tagline: 'Apache Doris', | |
| url: 'https://doris.apache.org', | |
| baseUrl: '/', | |
| onBrokenLinks: getBrokenLinkBehavior('DORIS_DOCUSAURUS_BROKEN_LINKS', DEFAULT_BROKEN_LINK_BEHAVIOR), | |
| onBrokenMarkdownLinks: getBrokenLinkBehavior('DORIS_DOCUSAURUS_BROKEN_MARKDOWN_LINKS', DEFAULT_BROKEN_LINK_BEHAVIOR), | |
| favicon: 'images/favicon.ico', | |
| organizationName: 'Apache', | |
| trailingSlash: false, | |
| markdown: { | |
| format: 'detect', | |
| // Inject each document's last-update date (from the git-history map in | |
| // scripts/last-update/data.json) as `last_update` front matter, so | |
| // showLastUpdateTime can render it without the deploy build touching | |
| // git. An explicit `last_update` in a file always wins. | |
| parseFrontMatter: async ({ defaultParseFrontMatter, filePath, fileContent }) => { | |
| const result = await defaultParseFrontMatter({ filePath, fileContent }); | |
| if (!result.frontMatter.last_update) { | |
| const rel = path.relative(__dirname, filePath).split(path.sep).join('/'); | |
| const date = DOCS_LAST_UPDATE[rel]; | |
| if (date) { | |
| result.frontMatter.last_update = { date }; | |
| } | |
| } | |
| return result; | |
| }, | |
| }, | |
| trailingSlash: true, | |
| i18n: { | |
| defaultLocale: DEFAULT_LOCALE, | |
| locales: ENABLED_LOCALES, | |
| localeConfigs: Object.fromEntries(enabledLocaleRecords() | |
| .map(({code, label, htmlLang}) => [code, {label, htmlLang}])), | |
| }, | |
| headTags: require('./scripts/publish/shared-assets.cjs').enabled() | |
| ? BOOTSTRAPS.map(script => ({ | |
| // Blocking head scripts retain the before-paint theme/rail setup. | |
| tagName:'script', attributes:{src:require('./config/asset-origin').getAssetOrigin()+'/'+script.file}, | |
| })) : BOOTSTRAPS.map(script => ({tagName:'script',attributes:{},innerHTML:script.content})), | |
| scripts: ['/js/custom-script.js', | |
| { | |
| async: true, | |
| src: 'https://widget.kapa.ai/kapa-widget.bundle.js', | |
| 'data-website-id': 'a5fb90df-217a-4097-95c0-80490220314b', | |
| 'data-modal-title': 'Apache Doris AI', | |
| 'data-project-name': 'Apache Doris Website', | |
| 'data-button-hide': "true", | |
| 'data-modal-override-open-selector': "#navbar-ask-ai-btn", | |
| 'data-project-logo': 'https://cdn.selectdb.com/static/doris_1_3c42247c63.png', | |
| 'data-modal-image': 'https://cdn.selectdb.com/static/doris_logo_cc5a30d886.png', | |
| 'data-project-color': '#11A679', | |
| 'data-modal-disclaimer': 'This is a custom LLM with access to all [Doris documentation](https://doris.apache.org/docs/4.x/gettingStarted/what-is-apache-doris).', | |
| 'data-consent-required': true, | |
| 'data-consent-screen-disclaimer': "By clicking "I agree, let's chat", you consent to the use of the AI assistant in accordance with kapa.ai's [Privacy Policy](https://www.kapa.ai/content/privacy-policy). This service uses reCAPTCHA, which requires your consent to Google's [Privacy Policy](https://policies.google.com/privacy) and [Terms of Service](https://policies.google.com/terms). By proceeding, you explicitly agree to both kapa.ai's and Google's privacy policies.", | |
| 'data-user-analytics-cookie-enabled': false, | |
| 'data-bot-protection-mechanism': "hcaptcha" | |
| } | |
| ], | |
| stylesheets: [ | |
| { | |
| href: '/css/katex.min.css', | |
| type: 'text/css', | |
| }, | |
| ], | |
| projectName: 'apache/doris-website', // Usually your repo name. | |
| customFields: { | |
| searchContexts: {source:SOURCE_SEARCH_CONTEXTS.map(item=>typeof item==='string'?item:item.path), | |
| managed:searchPlugin.contextsFor(__dirname)}, | |
| // Real Slack invite link. `/slack` (src/pages/slack.tsx) records the | |
| // click in Matomo and then forwards visitors here, so every public | |
| // channel keeps linking to https://doris.apache.org/slack and this is | |
| // the only value to update when the invite is rotated. | |
| slackInviteUrl: | |
| 'https://join.slack.com/t/apachedoriscommunity/shared_invite/zt-4ce61raf7-LPi99HBnrzHOZ7E0_bQZXA', | |
| // Keep public links on https://doris.apache.org/telegram. Update only | |
| // this destination when the Telegram community link changes. | |
| telegramInviteUrl: 'https://t.me/dorisapache', | |
| // The public HTTPS reverse proxy is the default browser entry point. | |
| // Local development can still replace this build-time value when needed. | |
| profileAnalysisApiBaseUrl: process.env.PROFILE_ANALYSIS_API_BASE_URL ?? 'https://agent.velodb.io', | |
| // hCaptcha site keys are public browser configuration. Never put the | |
| // matching secret in this repository or in a Docusaurus environment variable. | |
| profileAnalysisHCaptchaSiteKey: | |
| process.env.PROFILE_ANALYSIS_HCAPTCHA_SITE_KEY ?? '40f4820a-dc48-466a-b106-960a57ac5bd0', | |
| // Locale facts the browser needs, so UI code reads configuration rather | |
| // than naming a language. `translatedLocales` carry per-document | |
| // translation state and render its notices; `defaultLocalePageLocales` | |
| // show ordinary pages from the default locale, so a link to one must | |
| // target it directly. Blog indexing uses native per-route state. | |
| translatedLocales: MANAGED_LOCALES, | |
| defaultLocalePageLocales: enabledLocaleRecords() | |
| .filter(locale => locale.ordinaryPages === false).map(locale => locale.code), | |
| translatedPagePaths: Object.fromEntries(enabledLocaleRecords() | |
| .map(locale => [locale.code, [...(locale.translatedPages ?? []), ...translatedContentPages(__dirname, locale.code)]]) | |
| .filter(([, pages]) => pages.length)), | |
| }, | |
| future: { | |
| experimental_faster: true, | |
| }, | |
| plugins: [ | |
| contentTranslationPlugin, | |
| SKIP_DOCS ? null : [translationDocsPlugin, MAIN_DOCS_OPTIONS], | |
| [translationStatusPlugin, {browserData: MANAGED_LOCALES.length > 0}], | |
| [manualPlugins.pages, {}], | |
| SKIP_BLOG ? null : [manualPlugins.blog, { | |
| blogTitle: 'Apache Doris - Blog | Latest news and events ', | |
| blogDescription: | |
| 'Explore how Doris empower lakehouse, adhoc analysis, customer-facing analysis and various scenarios', | |
| postsPerPage: 'ALL', | |
| blogSidebarCount: 0, | |
| showReadingTime: false, | |
| onUntruncatedBlogPosts: 'ignore', | |
| onInlineAuthors: 'ignore', | |
| }], | |
| SKIP_BLOG ? null : recentBlogsPlugin, | |
| 'docusaurus-plugin-sass', | |
| require.resolve('./config/matomo-plugin/index.cjs'), | |
| // Use custom blog plugin | |
| versionsPlugin, | |
| SKIP_COURSE ? null : [ | |
| translationDocsPlugin, | |
| /** @type {import('@docusaurus/plugin-content-docs').Options} */ | |
| ({ | |
| id: 'course', | |
| path: 'course', | |
| routeBasePath: '/course', | |
| sidebarPath: require.resolve('./sidebarsCourse.ts'), | |
| showLastUpdateTime: true, | |
| }), | |
| ], | |
| SKIP_COMMUNITY ? null : [ | |
| translationDocsPlugin, | |
| /** @type {import('@docusaurus/plugin-content-docs').Options} */ | |
| ({ | |
| id: 'community', | |
| path: 'community', | |
| routeBasePath: '/community', | |
| sidebarPath: require.resolve('./sidebarsCommunity.json'), | |
| // Community docs are unversioned; the document header always shows their | |
| // last-updated date (see src/theme/DocItem/Layout). | |
| showLastUpdateTime: true, | |
| }), | |
| ], | |
| SKIP_RELEASES ? null : [ | |
| translationDocsPlugin, | |
| /** @type {import('@docusaurus/plugin-content-docs').Options} */ | |
| ({ | |
| id: 'releases', | |
| path: 'releasenotes', | |
| routeBasePath: '/releases', | |
| sidebarPath: require.resolve('./sidebarsReleases.json'), | |
| }), | |
| ], | |
| process.env.NODE_ENV === 'development' ? null : customDocusaurusPlugin, | |
| async function tailwindcssPlugin(context, options) { | |
| return { | |
| name: 'docusaurus-tailwindcss', | |
| configurePostCss(postcssOptions) { | |
| // Appends TailwindCSS and AutoPrefixer | |
| postcssOptions.plugins.push(require('tailwindcss')); | |
| postcssOptions.plugins.push(require('autoprefixer')); | |
| return postcssOptions; | |
| }, | |
| }; | |
| }, | |
| [ | |
| '@docusaurus/plugin-client-redirects', | |
| { | |
| fromExtensions: ['html', 'htm'], | |
| redirects: [ | |
| // Only external-target redirects belong here. Anything | |
| // pointing at an in-site path must go through | |
| // createRedirects below, otherwise plugin-client-redirects | |
| // rejects cross-locale entries during single-locale builds. | |
| // | |
| // `/slack` used to be listed here. It is now a real page | |
| // (src/pages/slack.tsx) that records the click in Matomo | |
| // before forwarding to customFields.slackInviteUrl. | |
| ], | |
| createRedirects(existingPath) { | |
| const redirects = [...require('./config/archived-docs-redirects.cjs')(existingPath)]; | |
| // Static redirects indexed by target. Trim trailing slash | |
| // for the lookup since Docusaurus' trailingSlash:true | |
| // gives us paths like /docs/4.x/foo/. The locale root | |
| // ('/' or '/zh-CN/') is the one path whose slash is the | |
| // whole path, so keep it as-is. | |
| const normalized = existingPath === '/' ? '/' : existingPath.replace(/\/$/, ''); | |
| if (REDIRECT_INDEX[normalized]) { | |
| redirects.push(...REDIRECT_INDEX[normalized]); | |
| } | |
| // Legacy `/gettingStarted/what-is-new` slug → renamed to | |
| // `/gettingStarted/what-is-apache-doris`. Register the old | |
| // path as a redirect for any existing version that has the | |
| // new slug. | |
| if (existingPath.includes('/gettingStarted/what-is-apache-doris')) { | |
| redirects.push( | |
| existingPath.replace( | |
| '/gettingStarted/what-is-apache-doris', | |
| '/gettingStarted/what-is-new', | |
| ), | |
| ); | |
| } | |
| // 3.x version-prefix variants: legacy URLs sometimes used | |
| // /docs/3.0/ (the dot release) or omitted the version | |
| // segment entirely. Map both shapes onto the canonical | |
| // /docs/3.x/ path. | |
| if (existingPath.startsWith('/docs/3.x/')) { | |
| redirects.push( | |
| existingPath.replace('/docs/3.x/', '/docs/'), | |
| existingPath.replace('/docs/3.x/', '/docs/3.0/'), | |
| ); | |
| } | |
| // Redirect old versioned releasenotes URLs to new /releases/ paths | |
| // e.g. /docs/4.x/releasenotes/v4.0/release-4.0.5 -> /releases/v4.0/release-4.0.5 | |
| if (existingPath.startsWith('/releases/')) { | |
| const releasePath = existingPath.replace('/releases/', ''); | |
| const oldVersionPrefixes = ['4.x', '3.x', '2.1', '2.0', '1.2', '4.0', '3.1', '3.0', 'dev']; | |
| for (const ver of oldVersionPrefixes) { | |
| redirects.push(`/docs/${ver}/releasenotes/${releasePath}`); | |
| } | |
| } | |
| return redirects.length > 0 ? redirects : undefined; | |
| }, | |
| }, | |
| ], | |
| ], | |
| presets: [ | |
| [ | |
| 'classic', | |
| /** @type {import('@docusaurus/preset-classic').Options} */ | |
| ({ | |
| docs: false, // The native docs plugin is wrapped below for translation state. | |
| blog: false, // Native blog metadata is wrapped for the content contract. | |
| pages: false, | |
| theme: { | |
| customCss: require.resolve('./src/scss/custom.scss'), | |
| }, | |
| sitemap: { | |
| changefreq: 'weekly', | |
| priority: 0.5, | |
| filename: 'sitemap.xml', | |
| // Last modification is the only field Google uses from a | |
| // sitemap for crawl scheduling; it reads the date from the | |
| // file's last commit. Deployments check out full history | |
| // (fetch-depth: 0) so the dates are real. | |
| lastmod: 'date', | |
| createSitemapItems: async params => { | |
| const { defaultCreateSitemapItems, ...rest } = params; | |
| const items = await defaultCreateSitemapItems(rest); | |
| const locale = ENABLED_LOCALES.find(locale => locale !== 'en' && params.siteConfig.baseUrl.endsWith(`/${locale}/`)) ?? 'en'; | |
| const filteredItems = filterContentSitemap(items, __dirname, locale, params.siteConfig.baseUrl).filter(item => { | |
| const pathname = new URL(item.url).pathname.replace(/\/+$/, ''); | |
| return !UTILITY_PATHS.has(pathname); | |
| }); | |
| for (let item of filteredItems) { | |
| if (item.url.includes('docs')) { | |
| item.changefreq = 'daily'; | |
| item.priority = 0.8; | |
| } | |
| if (item.url.includes('docs/1.2')) { | |
| item.priority = 0.2; | |
| } | |
| } | |
| return filteredItems; | |
| }, | |
| }, | |
| }), | |
| ], | |
| ], | |
| themes: SKIP_SEARCH ? [] : [ | |
| [ | |
| searchPlugin, | |
| { | |
| hashed: true, | |
| language: ['en', 'zh'], | |
| highlightSearchTermsOnTargetPage: true, | |
| // indexPages: true, | |
| indexDocs: true, | |
| docsRouteBasePath: ['docs', 'community', 'course', 'releases', 'zh-CN/docs', 'zh-CN/community', 'zh-CN/course', 'zh-CN/releases'], | |
| indexBlog: false, | |
| explicitSearchResultPath: true, | |
| searchBarShortcut: true, | |
| searchBarShortcutHint: true, | |
| searchResultLimits: 100, | |
| searchContextByPaths: SOURCE_SEARCH_CONTEXTS, | |
| useAllContextsWithNoSearchContext: false, | |
| ignoreFiles: [/^docs\/(?:[^/]+\/)?key-features\//], | |
| }, | |
| ], | |
| ], | |
| themeConfig: | |
| /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ | |
| ({ | |
| matomo: { | |
| matomoUrl: 'https://analytics.apache.org/', | |
| siteId: '43', | |
| phpLoader: 'matomo.php', | |
| jsLoader: 'matomo.js', | |
| }, | |
| // NavbarNext renders the actual top navigation (driven by the | |
| // home-next code, not themeConfig). This stub stays only because | |
| // Docusaurus' theme validator wants the key present. | |
| navbar: { | |
| items: [], | |
| }, | |
| footer: require('./config/footer'), | |
| docs: { | |
| sidebar: { | |
| autoCollapseCategories: true, | |
| }, | |
| }, | |
| prism: { | |
| theme: lightCodeTheme, | |
| additionalLanguages: ['java'], | |
| }, | |
| colorMode: { | |
| disableSwitch: true, | |
| }, | |
| // metadata: [ | |
| // { | |
| // name: 'viewport', | |
| // content: | |
| // 'width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no', | |
| // }, | |
| // ],s | |
| }), | |
| ssrTemplate | |
| }; | |
| module.exports = config; |