diff --git a/README.md b/README.md
index 74eac42c..b0138ff4 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,28 @@ In order to debug algolia searches, you need to provide two additional environme
Put them in `.env.development` to make them take effect. (Ref: )
+## Filesystem Documentation
+
+TiDB Cloud Filesystem appears after TiDB Cloud Lake in the Product menu. Its
+English documentation is published at `/tidbcloud-filesystem/` with its own
+sidebar and Preview badge.
+
+The source content comes from `pingcap/docs` and is published by
+`pingcap/docs-staging` under
+`markdown-pages/en/tidb-cloud-filesystem/master/`. The staging tree contains
+`tidb-cloud-filesystem/` and `TOC-tidb-cloud-filesystem.md`, and requires a
+separate `tidb-cloud-filesystem` entry in the `pingcap/docs-staging`
+`docs.json`, similar to TiDB Cloud Lake. After the staging submodule is
+updated, that file is available in this checkout as `docs/docs.json`. The
+source pages and staging configuration must reach `docs-staging` before a
+website deployment can publish them. The menu links to English without
+changing the selected Cloud database plan or implying that translated
+Filesystem pages exist.
+
+`gatsby/__tests__/filesystem-routing.test.ts` checks published URLs, sidebar
+selection, TOC membership, and links to the existing AI command reference.
+`gatsby/__tests__/filesystem-header.test.ts` checks menu order and selection.
+
## Workflow
Because of most of our text data stored in GitHub. It's needed to apply a GitHub API token in development **when you are prompted for `rate-limiting`**.
diff --git a/gatsby-config.js b/gatsby-config.js
index 73f8db87..4b8d43ab 100644
--- a/gatsby-config.js
+++ b/gatsby-config.js
@@ -93,7 +93,7 @@ module.exports = {
{
matchPath: `/:lang?/(${Object.keys(docs.docs).join(
"|"
- )}|developer|best-practices|api|ai|releases|tidbcloudlake)/(.*)`,
+ )}|developer|best-practices|api|ai|releases|tidbcloudlake|tidbcloud-filesystem)/(.*)`,
getLanguageFromPath: true,
},
{
diff --git a/gatsby/URL_MAPPING_ARCHITECTURE.md b/gatsby/URL_MAPPING_ARCHITECTURE.md
index 421e91f7..aaf2bbe4 100644
--- a/gatsby/URL_MAPPING_ARCHITECTURE.md
+++ b/gatsby/URL_MAPPING_ARCHITECTURE.md
@@ -3,6 +3,7 @@
## Overview
This document describes how the project handles URL mapping across three key areas:
+
1. **Page URL Mapping**: Converting source file paths to published page URLs during build
2. **TOC Mapping**: Resolving links in TOC (Table of Contents) files
3. **Article Link Mapping**: Transforming internal links within markdown articles
@@ -16,12 +17,14 @@ The system uses two core resolvers (`url-resolver` and `link-resolver`) that wor
**Location**: `gatsby/create-pages/create-docs.ts`
**Process**:
+
1. Gatsby queries all MDX files from the GraphQL data layer
2. For each file, `calculateFileUrl()` from `url-resolver` converts the source path to a published URL
3. `getTOCNamespace()` from `toc-namespace` determines the page's TOC namespace for navigation/context
4. The resolved URL is used to create the Gatsby page with `createPage()`
**Example**:
+
```typescript
// Source file: docs/markdown-pages/en/tidb/master/alert-rules.md
// Slug: "en/tidb/master/alert-rules"
@@ -31,6 +34,7 @@ const path = calculateFileUrl(node.slug, true);
```
**Key Points**:
+
- Uses `url-resolver` to transform source paths to URLs
- Default language (`en`) is omitted from URLs (`omitDefaultLanguage: true`)
- Only files referenced in TOC files are built (filtered by `filterNodesByToc`)
@@ -40,6 +44,7 @@ const path = calculateFileUrl(node.slug, true);
**Location**: `gatsby/toc.ts` and `gatsby/toc-filter.ts`
**Process**:
+
1. Gatsby queries all TOC files (files matching `/TOC.*md$/`)
2. For each TOC file, `mdxAstToToc()` parses the markdown AST
3. Links within TOC are resolved using `resolveMarkdownLink()` from `link-resolver`
@@ -48,16 +53,21 @@ const path = calculateFileUrl(node.slug, true);
- Generate navigation menus for pages
**Example**:
+
```typescript
// TOC file: docs/markdown-pages/en/tidb/stable/TOC.md
// Contains link: [Getting Started](/develop/getting-started)
// TOC path: "/en/tidb/stable" (resolved from TOC file slug)
-const resolvedLink = resolveMarkdownLink("/develop/getting-started", "/en/tidb/stable");
+const resolvedLink = resolveMarkdownLink(
+ "/develop/getting-started",
+ "/en/tidb/stable"
+);
// Result: "/developer/getting-started"
// Used in navigation menu
```
**Key Points**:
+
- Uses `link-resolver` to resolve links in TOC files
- TOC links are resolved relative to the TOC file's own URL
- Resolved links are used to build a whitelist of files to include in the build
@@ -67,12 +77,14 @@ const resolvedLink = resolveMarkdownLink("/develop/getting-started", "/en/tidb/s
**Location**: `gatsby/plugin/content/index.ts`
**Process**:
+
1. During markdown processing, Gatsby's MDX plugin processes each article
2. For each link in the markdown AST, `resolveMarkdownLink()` resolves the link path
3. The resolved link is converted to a Gatsby `` component
4. External links (`http://`, `https://`) are kept as-is with `target="_blank"`
**Example**:
+
```typescript
// Article: docs/markdown-pages/en/tidb/stable/overview.md
// Contains link: [Upgrade Guide](/upgrade/upgrade-tidb-using-tiup)
@@ -86,6 +98,7 @@ const resolvedPath = resolveMarkdownLink(
```
**Key Points**:
+
- Uses `link-resolver` to resolve links based on current page context
- Links are resolved relative to the current article's URL
- Hash fragments (`#section`) are preserved automatically
@@ -134,21 +147,25 @@ Final HTML/JSX
**Scenario**: Building a TiDB article with links
1. **Source File**: `docs/markdown-pages/en/tidb/master/alert-rules.md`
+
- Contains link: `[Vector Search](/develop/vector-search)`
2. **Page URL Resolution** (`create-docs.ts`):
+
```typescript
const pageUrl = calculateFileUrl("en/tidb/master/alert-rules", true);
// Result: "/tidb/dev/alert-rules"
```
3. **TOC Processing** (`toc-filter.ts`):
+
- TOC file: `en/tidb/stable/TOC.md`
- Contains link to `alert-rules`
- Link resolved: `/tidb/dev/alert-rules`
- File added to whitelist: `en/tidb/stable -> Set(["alert-rules"])`
4. **Page Creation** (`create-docs.ts`):
+
- File matches TOC whitelist → page is created
- Page URL: `/tidb/dev/alert-rules`
- Namespace: `TOCNamespace.TiDB`
@@ -157,7 +174,7 @@ Final HTML/JSX
- Current page URL: `/en/tidb/dev/alert-rules`
- Link `/develop/vector-search` resolved:
```typescript
- resolveMarkdownLink("/develop/vector-search", "/en/tidb/dev/alert-rules")
+ resolveMarkdownLink("/develop/vector-search", "/en/tidb/dev/alert-rules");
// Result: "/developer/vector-search"
```
- Rendered as: `Vector Search`
@@ -172,7 +189,37 @@ The following sections describe the effects of each configuration rule in order
Rules are evaluated in order; the first matching rule wins.
-### Rule 1: TiDBCloud Dedicated Index
+### Rule 1: TiDB Cloud Filesystem Namespace
+
+**Effect**: Maps TiDB Cloud Filesystem pages from their independent staging tree to the English `/tidbcloud-filesystem` namespace.
+
+**Source Patterns**:
+
+- `/{lang}/tidb-cloud-filesystem/{branch}/tidb-cloud-filesystem/{...folders}/{filename}`
+- `/{lang}/tidb-cloud-filesystem/{branch}/{...folders}/{filename}`
+
+**Target Pattern**:
+
+- For `_index`: `/{lang}/tidbcloud-filesystem/{folders}` (keeps folder structure)
+- For other files: `/{lang}/tidbcloud-filesystem/{filename}` (flattens folder structure)
+
+**Filename Transform**:
+
+- `ignoreIf: ["_index"]`
+- `conditionalTarget.keepIf: ["_index"]`
+
+**Example**:
+
+- Source: `en/tidb-cloud-filesystem/master/tidb-cloud-filesystem/_index.md`
+- Target: `/tidbcloud-filesystem`
+- Source: `en/tidb-cloud-filesystem/master/tidb-cloud-filesystem/filesystem-quick-start.md`
+- Target: `/tidbcloud-filesystem/filesystem-quick-start`
+
+**Use Case**: TiDB Cloud Filesystem is sourced from its own English-only docs tree while using a product URL that omits the hyphen between `tidb` and `cloud`.
+
+---
+
+### Rule 2: TiDBCloud Dedicated Index
**Effect**: Maps TiDBCloud dedicated `_index.md` files to the TiDBCloud root URL.
@@ -183,6 +230,7 @@ Rules are evaluated in order; the first matching rule wins.
**Conditions**: `filename = "_index"`
**Example**:
+
- Source: `en/tidbcloud/master/tidb-cloud/dedicated/_index.md`
- Target: `/tidbcloud` (or `/en/tidbcloud` if default language not omitted)
@@ -190,7 +238,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 2: TiDBCloud Releases Index
+### Rule 3: TiDBCloud Releases Index
**Effect**: Maps TiDBCloud releases `_index.md` to the releases namespace.
@@ -201,6 +249,7 @@ Rules are evaluated in order; the first matching rule wins.
**Conditions**: `filename = "_index"`
**Example**:
+
- Source: `en/tidbcloud/master/tidb-cloud/releases/_index.md`
- Target: `/releases/tidb-cloud`
@@ -208,7 +257,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 3: TiDB Releases Index (Stable)
+### Rule 4: TiDB Releases Index (Stable)
**Effect**: Maps the stable TiDB releases `_index.md` file to the shared releases namespace.
@@ -219,6 +268,7 @@ Rules are evaluated in order; the first matching rule wins.
**Conditions**: `filename = "_index"`
**Example**:
+
- Source: `en/tidb/release-8.5/releases/_index.md`
- Target: `/releases/tidb-self-managed`
@@ -226,7 +276,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 4: TiDB-in-Kubernetes Releases Index
+### Rule 5: TiDB-in-Kubernetes Releases Index
**Effect**: Maps TiDB-in-Kubernetes releases `_index.md` to the releases namespace.
@@ -237,6 +287,7 @@ Rules are evaluated in order; the first matching rule wins.
**Conditions**: `filename = "_index"`
**Example**:
+
- Source: `en/tidb-in-kubernetes/main/releases/_index.md`
- Target: `/releases/tidb-operator`
@@ -244,21 +295,24 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 5: TiDBCloud with Prefix
+### Rule 6: TiDBCloud with Prefix
**Effect**: Maps TiDBCloud pages with prefixes (dedicated, starter, essential) to TiDBCloud URLs.
**Source Pattern**: `/{lang}/tidbcloud/{branch}/tidb-cloud/{...prefixes}/{filename}`
**Target Pattern**:
+
- For `_index`: `/{lang}/tidbcloud/{prefixes}` (keeps prefixes)
- For other files: `/{lang}/tidbcloud/{filename}` (removes prefixes)
**Filename Transform**:
+
- `ignoreIf: ["_index"]` - Filename removed from URL for non-index files
- `conditionalTarget.keepIf: ["_index"]` - Uses alternative pattern for `_index` files
**Example**:
+
- Source: `en/tidbcloud/master/tidb-cloud/dedicated/starter/_index.md`
- Target: `/tidbcloud/dedicated/starter`
- Source: `en/tidbcloud/master/tidb-cloud/dedicated/starter/getting-started.md`
@@ -268,23 +322,26 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 6: Developer Namespace
+### Rule 7: Developer Namespace
**Effect**: Maps stable TiDB pages under the `develop` folder (published as `developer`) to the shared `/developer` namespace.
**Source Pattern**: `/{lang}/tidb/{stable}/{folder}/{...folders}/{filename}`
**Target Pattern**:
+
- For `_index`: `/{lang}/developer/{folders}` (keeps folder structure)
- For other files: `/{lang}/developer/{filename}` (flattens folder structure)
**Conditions**: `folder = ["develop"]`
**Filename Transform**:
+
- `ignoreIf: ["_index"]`
- `conditionalTarget.keepIf: ["_index"]`
**Example**:
+
- Source: `en/tidb/release-8.5/develop/subfolder/_index.md`
- Target: `/developer/subfolder`
- Source: `en/tidb/release-8.5/develop/subfolder/vector-search.md`
@@ -294,23 +351,26 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 7: Best-Practices/API/AI Namespace
+### Rule 8: Best-Practices/API/AI Namespace
**Effect**: Maps stable TiDB pages under `best-practices`, `api`, and `ai` to their corresponding shared namespaces.
**Source Pattern**: `/{lang}/tidb/{stable}/{folder}/{...folders}/{filename}`
**Target Pattern**:
+
- For `_index`: `/{lang}/{folder}/{folders}` (keeps folder structure)
- For other files: `/{lang}/{folder}/{filename}` (flattens folder structure)
**Conditions**: `folder = ["best-practices", "api", "ai"]`
**Filename Transform**:
+
- `ignoreIf: ["_index"]`
- `conditionalTarget.keepIf: ["_index"]`
**Example**:
+
- Source: `en/tidb/release-8.5/ai/subfolder/_index.md`
- Target: `/ai/subfolder`
- Source: `en/tidb/release-8.5/api/overview.md`
@@ -320,21 +380,24 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 8: TiDB Cloud Lake Namespace
+### Rule 9: TiDB Cloud Lake Namespace
**Effect**: Maps TiDB Cloud Lake pages to the `/tidbcloudlake` namespace.
**Source Pattern**: `/{lang}/tidb-cloud-lake/{branch}/{...folders}/{filename}`
**Target Pattern**:
+
- For `_index`: `/{lang}/tidbcloudlake/{folders}` (keeps folder structure)
- For other files: `/{lang}/tidbcloudlake/{filename}` (flattens folder structure)
**Filename Transform**:
+
- `ignoreIf: ["_index"]`
- `conditionalTarget.keepIf: ["_index"]`
**Example**:
+
- Source: `en/tidb-cloud-lake/master/_index.md`
- Target: `/tidbcloudlake`
- Source: `en/tidb-cloud-lake/master/tidb-cloud-lake/_index.md`
@@ -346,7 +409,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 9: TiDB Index Pages with Folders
+### Rule 10: TiDB Index Pages with Folders
**Effect**: Maps TiDB `_index.md` pages to URLs that keep their folder path, preventing multiple `_index.md` files from collapsing to the same `/tidb/{branch}` URL.
@@ -357,6 +420,7 @@ Rules are evaluated in order; the first matching rule wins.
**Conditions**: `filename = "_index"`
**Example**:
+
- Source: `en/tidb/master/develop/_index.md`
- Target: `/tidb/dev/develop`
- Source: `en/tidb/master/releases/_index.md`
@@ -366,9 +430,9 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 10: TiDB with Branch Alias
+### Rule 11: TiDB with Branch Alias
-**Effect**: Maps TiDB pages with branch aliasing (master → dev, release-* → v*).
+**Effect**: Maps TiDB pages with branch aliasing (master → dev, release-_ → v_).
**Source Pattern**: `/{lang}/tidb/{branch}/{...folders}/{filename}`
@@ -377,11 +441,13 @@ Rules are evaluated in order; the first matching rule wins.
**Filename Transform**: `ignoreIf: ["_index", "_docHome"]`
**Alias Mapping** (`branch-alias-tidb`):
+
- `master` → `dev`
- `{stable}` → `stable` (exact match)
- `release-*` → `v*` (wildcard pattern)
**Example**:
+
- Source: `en/tidb/master/alert-rules.md`
- Target: `/tidb/dev/alert-rules`
- Source: `en/tidb/release-8.5/alert-rules.md`
@@ -391,7 +457,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 11: TiDB-in-Kubernetes Release Notes from Main
+### Rule 12: TiDB-in-Kubernetes Release Notes from Main
**Effect**: Publishes TiDB-in-Kubernetes release notes from `main` at stable URLs so they override the copies from the configured stable release branch.
@@ -400,6 +466,7 @@ Rules are evaluated in order; the first matching rule wins.
**Target Pattern**: `/{lang}/tidb-in-kubernetes/stable/{filename}`
**Example**:
+
- Source: `en/tidb-in-kubernetes/main/releases/release-2.0.0.md`
- Target: `/tidb-in-kubernetes/stable/release-2.0.0`
- Source: `zh/tidb-in-kubernetes/main/releases/release-2.0.0.md`
@@ -409,9 +476,9 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 12: TiDB-in-Kubernetes with Branch Alias
+### Rule 13: TiDB-in-Kubernetes with Branch Alias
-**Effect**: Maps TiDB-in-Kubernetes pages with branch aliasing (main → dev, release-* → v*).
+**Effect**: Maps TiDB-in-Kubernetes pages with branch aliasing (main → dev, release-_ → v_).
**Source Pattern**: `/{lang}/tidb-in-kubernetes/{branch}/{...folders}/{filename}`
@@ -420,11 +487,13 @@ Rules are evaluated in order; the first matching rule wins.
**Filename Transform**: `ignoreIf: ["_index", "_docHome"]`
**Alias Mapping** (`branch-alias-tidb-in-kubernetes`):
+
- `main` → `dev`
- `{stable}` → `stable` (exact match)
- `release-*` → `v*` (wildcard pattern)
**Example**:
+
- Source: `en/tidb-in-kubernetes/main/deploy/deploy-tidb-on-kubernetes.md`
- Target: `/tidb-in-kubernetes/dev/deploy-tidb-on-kubernetes`
- Source: `en/tidb-in-kubernetes/release-1.6/deploy/deploy-tidb-on-kubernetes.md`
@@ -434,7 +503,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 13: Fallback Rule
+### Rule 14: Fallback Rule
**Effect**: Generic fallback for any remaining paths.
@@ -445,6 +514,7 @@ Rules are evaluated in order; the first matching rule wins.
**Filename Transform**: `ignoreIf: ["_index", "_docHome"]`
**Example**:
+
- Source: `en/dm/release-5.3/migration/migrate-data.md`
- Target: `/en/dm/migrate-data`
@@ -456,7 +526,34 @@ Rules are evaluated in order; the first matching rule wins.
Rules are evaluated in order; the first matching rule wins.
-### Rule 1: Releases Index Links
+### Rule 1: TiDB Cloud Filesystem Links (Direct Mapping)
+
+**Effect**: Resolves Filesystem source links to the English `/tidbcloud-filesystem` namespace, regardless of the current page language.
+
+**Link Patterns**:
+
+- `/tidb-cloud-filesystem/{...folders}/_index`
+- `/tidb-cloud-filesystem/{...folders}/{docname}`
+
+**Target Patterns**:
+
+- `/tidbcloud-filesystem/{folders}` for `_index`
+- `/tidbcloud-filesystem/{docname}` for article pages
+
+**Example**:
+
+- Link: `/tidb-cloud-filesystem/_index`
+- Current Page: `/zh/tidb/stable/overview`
+- Result: `/tidbcloud-filesystem`
+- Link: `/tidb-cloud-filesystem/guides/filesystem-mount`
+- Current Page: Any page
+- Result: `/tidbcloud-filesystem/filesystem-mount`
+
+**Use Case**: Filesystem is currently published in English only, so cross-product links must not inherit `/zh` or `/ja`.
+
+---
+
+### Rule 2: Releases Index Links
**Effect**: Resolves `/releases/_index` links to TiDB self-managed releases page.
@@ -465,6 +562,7 @@ Rules are evaluated in order; the first matching rule wins.
**Target Pattern**: `/{curLang}/releases/tidb-self-managed`
**Example**:
+
- Link: `/releases/_index`
- Current Page: Any page
- Result: `/releases/tidb-self-managed` (or `/en/releases/tidb-self-managed` if default language not omitted)
@@ -473,7 +571,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 2: TiDB Cloud Releases Index Links
+### Rule 3: TiDB Cloud Releases Index Links
**Effect**: Resolves `/tidb-cloud/releases/_index` links to TiDB Cloud releases page.
@@ -482,6 +580,7 @@ Rules are evaluated in order; the first matching rule wins.
**Target Pattern**: `/{curLang}/releases/tidb-cloud`
**Example**:
+
- Link: `/tidb-cloud/releases/_index`
- Current Page: Any page
- Result: `/releases/tidb-cloud`
@@ -490,7 +589,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 3: TiDB-in-Kubernetes Releases Index Links (Path-Based)
+### Rule 4: TiDB-in-Kubernetes Releases Index Links (Path-Based)
**Effect**: Resolves `/tidb-in-kubernetes/releases/_index` links from TiDB-in-Kubernetes pages.
@@ -501,6 +600,7 @@ Rules are evaluated in order; the first matching rule wins.
**Target Pattern**: `/{curLang}/releases/tidb-operator`
**Example**:
+
- Current Page: `/tidb-in-kubernetes/stable/deploy`
- Link: `/tidb-in-kubernetes/releases/_index`
- Result: `/releases/tidb-operator`
@@ -509,7 +609,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 4: Links from TiDB Releases Landing Page (Path-Based)
+### Rule 5: Links from TiDB Releases Landing Page (Path-Based)
**Effect**: Resolves `/releases/*` links from the releases landing page to TiDB stable branch URLs.
@@ -520,6 +620,7 @@ Rules are evaluated in order; the first matching rule wins.
**Target Pattern**: `/{lang}/tidb/stable/{docname}`
**Example**:
+
- Current Page: `/releases/tidb-self-managed`
- Link: `/releases/release-8.5.4`
- Result: `/tidb/stable/release-8.5.4` (or `/en/tidb/stable/release-8.5.4` if default language not omitted)
@@ -528,7 +629,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 5: Links from TiDB Operator Releases Landing Page (Path-Based, /releases/*)
+### Rule 6: Links from TiDB Operator Releases Landing Page (Path-Based, /releases/\*)
**Effect**: Resolves `/releases/*` links from the operator releases landing page to TiDB-in-Kubernetes `stable` URLs.
@@ -539,6 +640,7 @@ Rules are evaluated in order; the first matching rule wins.
**Target Pattern**: `/{lang}/tidb-in-kubernetes/stable/{docname}`
**Example**:
+
- Current Page: `/releases/tidb-operator`
- Link: `/releases/release-2.0.0`
- Result: `/tidb-in-kubernetes/stable/release-2.0.0` (or `/en/tidb-in-kubernetes/stable/release-2.0.0` if default language not omitted)
@@ -547,7 +649,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 6: TiDB-in-Kubernetes Main TOC Release Links (Path-Based)
+### Rule 7: TiDB-in-Kubernetes Main TOC Release Links (Path-Based)
**Effect**: Resolves release-note links from the `main` TiDB-in-Kubernetes TOC to the stable URLs that publish the corresponding `main` release-note files.
@@ -558,6 +660,7 @@ Rules are evaluated in order; the first matching rule wins.
**Target Pattern**: `/{lang}/tidb-in-kubernetes/stable/{docname}`
**Example**:
+
- Current TOC: `/tidb-in-kubernetes/dev/TOC-tidb-operator-releases`
- Link: `/releases/release-2.0.0`
- Result: `/tidb-in-kubernetes/stable/release-2.0.0`
@@ -566,7 +669,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 7: Namespace Index Links (Direct Mapping)
+### Rule 8: Namespace Index Links (Direct Mapping)
**Effect**: Resolves namespace index links (ending with `/_index`) to namespace URLs (published as `/developer`, `/best-practices`, `/api`, `/ai`, `/tidbcloud`, `/tidbcloudlake`).
@@ -577,11 +680,13 @@ Rules are evaluated in order; the first matching rule wins.
**Conditions**: `namespace = ["tidb-cloud", "tidb-cloud-lake", "develop", "best-practices", "api", "ai"]`
**Namespace Transform**:
+
- `tidb-cloud` → `tidbcloud`
- `tidb-cloud-lake` → `tidbcloudlake`
- `develop` → `developer`
**Example**:
+
- Link: `/develop/_index`
- Current Page: Any page
- Result: `/developer`
@@ -596,7 +701,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 8: Namespace Links (Direct Mapping)
+### Rule 9: Namespace Links (Direct Mapping)
**Effect**: Resolves namespace links (`develop`, `best-practices`, `api`, `ai`, `tidb-cloud`, `tidb-cloud-lake`) to namespace URLs (published as `/developer`, `/best-practices`, `/api`, `/ai`, `/tidbcloud`, `/tidbcloudlake`).
@@ -607,11 +712,13 @@ Rules are evaluated in order; the first matching rule wins.
**Conditions**: `namespace = ["tidb-cloud", "tidb-cloud-lake", "develop", "best-practices", "api", "ai"]`
**Namespace Transform**:
+
- `tidb-cloud` → `tidbcloud`
- `tidb-cloud-lake` → `tidbcloudlake`
- `develop` → `developer`
**Example**:
+
- Link: `/develop/vector-search`
- Current Page: Any page
- Result: `/developer/vector-search`
@@ -623,7 +730,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 9: TiDBCloud Page Links (Path-Based)
+### Rule 10: TiDBCloud Page Links (Path-Based)
**Effect**: Resolves relative links from TiDBCloud pages to TiDBCloud URLs.
@@ -634,6 +741,7 @@ Rules are evaluated in order; the first matching rule wins.
**Target Pattern**: `/{lang}/tidbcloud/{docname}`
**Example**:
+
- Current Page: `/tidbcloud/dedicated`
- Link: `/getting-started`
- Result: `/tidbcloud/getting-started`
@@ -645,7 +753,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 10: TiDB Cloud Lake Page Links (Path-Based)
+### Rule 11: TiDB Cloud Lake Page Links (Path-Based)
**Effect**: Resolves relative links from TiDB Cloud Lake pages to `/tidbcloudlake/*` URLs.
@@ -656,6 +764,7 @@ Rules are evaluated in order; the first matching rule wins.
**Target Pattern**: `/{lang}/tidbcloudlake/{docname}`
**Example**:
+
- Current Page: `/tidbcloudlake`
- Link: `/guides/dashboards`
- Result: `/tidbcloudlake/dashboards`
@@ -664,7 +773,27 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 11: Developer/Best-Practices/API/AI Namespace Page Links (Path-Based)
+### Rule 12: TiDB Cloud Filesystem Page Links (Path-Based)
+
+**Effect**: Resolves relative links from Filesystem pages to English `/tidbcloud-filesystem/*` URLs.
+
+**Path Pattern**: `/{lang}/tidbcloud-filesystem/{...any}`
+
+**Link Pattern**: `/{...folders}/{docname}`
+
+**Target Pattern**: `/tidbcloud-filesystem/{docname}`
+
+**Example**:
+
+- Current Page: `/tidbcloud-filesystem/filesystem-quick-start`
+- Link: `/guides/filesystem-mount`
+- Result: `/tidbcloud-filesystem/filesystem-mount`
+
+**Use Case**: Relative Filesystem links stay in the English product namespace; explicit namespace links such as `/ai/*` are handled by earlier direct rules.
+
+---
+
+### Rule 13: Developer/Best-Practices/API/AI Namespace Page Links (Path-Based)
**Effect**: Resolves relative links from namespace pages to TiDB stable branch URLs.
@@ -677,6 +806,7 @@ Rules are evaluated in order; the first matching rule wins.
**Target Pattern**: `/{lang}/tidb/stable/{docname}`
**Example**:
+
- Current Page: `/developer/overview`
- Link: `/vector-search`
- Result: `/tidb/stable/vector-search`
@@ -688,7 +818,7 @@ Rules are evaluated in order; the first matching rule wins.
---
-### Rule 12: TiDB/TiDB-in-Kubernetes Page Links (Path-Based)
+### Rule 14: TiDB/TiDB-in-Kubernetes Page Links (Path-Based)
**Effect**: Resolves relative links from TiDB or TiDB-in-Kubernetes pages, preserving branch/version.
@@ -697,10 +827,12 @@ Rules are evaluated in order; the first matching rule wins.
**Path Conditions**: `repo = ["tidb", "tidb-in-kubernetes"]`
**Link Pattern / Target Pattern**:
+
- Index links: `/{...folders}/_index` → `/{lang}/{repo}/{branch}/{folders}`
- Other links: `/{...any}/{docname}` → `/{lang}/{repo}/{branch}/{docname}`
**Example**:
+
- Current Page: `/tidb/stable/upgrade`
- Link: `/upgrade-tidb-using-tiup`
- Result: `/tidb/stable/upgrade-tidb-using-tiup`
@@ -724,7 +856,7 @@ The URL mapping system provides:
1. **Consistent URL Structure**: Source files are mapped to clean, SEO-friendly URLs
2. **Context-Aware Link Resolution**: Links are resolved based on the current page's context
-3. **Namespace Support**: Special namespaces (`developer`, `best-practices`, `api`, `ai`, `tidbcloudlake`) have their own URL structure
+3. **Namespace Support**: Special namespaces (`developer`, `best-practices`, `api`, `ai`, `tidbcloudlake`, `tidbcloud-filesystem`) have their own URL structure
4. **Branch Aliasing**: Internal branch names are transformed to user-friendly versions
5. **Default Language Omission**: Default language (`en`) is omitted from URLs for cleaner paths
6. **TOC-Driven Build**: Only files referenced in TOC files are built, reducing build size
diff --git a/gatsby/__tests__/filesystem-header.test.ts b/gatsby/__tests__/filesystem-header.test.ts
new file mode 100644
index 00000000..66d9c886
--- /dev/null
+++ b/gatsby/__tests__/filesystem-header.test.ts
@@ -0,0 +1,54 @@
+jest.mock("shared/interface", () => require("../../src/shared/interface"), {
+ virtual: true,
+});
+jest.mock("shared/useCloudPlan", () => ({ CLOUD_MODE_KEY: "cloud-mode" }), {
+ virtual: true,
+});
+jest.mock("components/Badge/PreviewBadge", () => () => null, { virtual: true });
+jest.mock("media/icons/cloud-03.svg", () => () => null, { virtual: true });
+jest.mock("media/icons/layers-three-01.svg", () => () => null, {
+ virtual: true,
+});
+
+import { generateNavConfig } from "../../src/components/Layout/Header/HeaderNavConfigData";
+import { getSelectedNavItem } from "../../src/components/Layout/Header/getSelectedNavItem";
+import { CloudPlan, TOCNamespace } from "../../src/shared/interface";
+
+describe("Filesystem product menu", () => {
+ it("appears immediately after Lake and links to English documentation", () => {
+ const nav = generateNavConfig(
+ (key) => key,
+ CloudPlan.Starter,
+ "prod",
+ "zh"
+ );
+ const product = nav[0];
+ if (product.type !== "group") throw new Error("Missing Product menu");
+ const cloud = product.children[0];
+ if (cloud.type !== "group") throw new Error("Missing Cloud products");
+ const lakeIndex = cloud.children.findIndex(
+ (item) => item.type === "item" && item.to === "/tidbcloudlake"
+ );
+ expect(lakeIndex).toBeGreaterThanOrEqual(0);
+ const filesystem = cloud.children[lakeIndex + 1];
+ expect(filesystem).toMatchObject({
+ type: "item",
+ label: "navbar.tidbCloudFilesystem",
+ to: "/tidbcloud-filesystem",
+ isI18n: false,
+ });
+ if (filesystem.type !== "item") throw new Error("Missing Filesystem item");
+ expect(filesystem.endIcon).toBeTruthy();
+ expect(getSelectedNavItem(nav, TOCNamespace.TiDBCloudFilesystem)).toBe(
+ filesystem
+ );
+ expect(filesystem.onClick).toBeUndefined();
+ });
+
+ it("does not add Filesystem to the archived documentation site", () => {
+ const nav = generateNavConfig((key) => key, null, "archive", "en");
+ expect(
+ getSelectedNavItem(nav, TOCNamespace.TiDBCloudFilesystem)
+ ).toBeNull();
+ });
+});
diff --git a/gatsby/__tests__/filesystem-routing.test.ts b/gatsby/__tests__/filesystem-routing.test.ts
new file mode 100644
index 00000000..413da0e6
--- /dev/null
+++ b/gatsby/__tests__/filesystem-routing.test.ts
@@ -0,0 +1,137 @@
+import CONFIG from "../../docs/docs.json";
+import { TOCNamespace, TOCNamespaceSlugMap } from "../../src/shared/interface";
+import { resolveMarkdownLink } from "../link-resolver";
+import { generateConfig, generateNavTOCPath } from "../path";
+import { mdxAstToToc } from "../toc";
+import { filterNodesByToc, getFilesFromTocs } from "../toc-filter";
+import { getTOCNamespace } from "../toc-namespace";
+import { calculateFileUrl } from "../url-resolver";
+
+const source = "en/tidb-cloud-filesystem/master";
+const stableTidbSource = `en/tidb/${CONFIG.docs.tidb.stable}`;
+const tocSlug = `${source}/TOC-tidb-cloud-filesystem`;
+const tocAST = [
+ {
+ type: "list",
+ ordered: false,
+ children: [
+ ["Introduction", "/tidb-cloud-filesystem/_index.md"],
+ ["Quick Start", "/tidb-cloud-filesystem/filesystem-quick-start.md"],
+ ["CLI Commands", "/ai/ti/reference/ti-filesystem.md"],
+ ].map(([label, url]) => ({
+ type: "listItem",
+ children: [
+ {
+ type: "paragraph",
+ children: [
+ { type: "link", url, children: [{ type: "text", value: label }] },
+ ],
+ },
+ ],
+ })),
+ },
+];
+
+describe("Filesystem product routing", () => {
+ it.each([
+ ["_index", "/tidbcloud-filesystem"],
+ ["filesystem-quick-start", "/tidbcloud-filesystem/filesystem-quick-start"],
+ ["guides/filesystem-mount", "/tidbcloud-filesystem/filesystem-mount"],
+ ])("publishes %s with its own namespace and TOC", (name, expected) => {
+ const slug = `${source}/tidb-cloud-filesystem/${name}`;
+ expect(calculateFileUrl(slug, true)).toBe(expected);
+ const namespace = getTOCNamespace(slug);
+ expect(namespace).toBe(TOCNamespace.TiDBCloudFilesystem);
+ expect(
+ generateNavTOCPath(
+ generateConfig(slug).config,
+ TOCNamespaceSlugMap[namespace!]
+ )
+ ).toBe(tocSlug);
+ });
+
+ it("does not take over TiDB or AI documentation", () => {
+ expect(
+ getTOCNamespace("en/tidb/master/tidb-cloud-filesystem/filesystem-mount")
+ ).toBe(TOCNamespace.TiDB);
+ expect(
+ calculateFileUrl(
+ "en/tidb/master/tidb-cloud-filesystem/filesystem-mount",
+ true
+ )
+ ).toBe("/tidb/dev/filesystem-mount");
+ expect(
+ getTOCNamespace(`${stableTidbSource}/ai/ti/reference/ti-filesystem`)
+ ).toBe(TOCNamespace.AI);
+ expect(
+ calculateFileUrl(
+ `${stableTidbSource}/ai/ti/reference/ti-filesystem`,
+ true
+ )
+ ).toBe("/ai/ti-filesystem");
+ });
+
+ it.each([
+ ["/tidb-cloud-filesystem/_index", "/ai", "/tidbcloud-filesystem"],
+ [
+ "/tidb-cloud-filesystem/filesystem-mount#finish-safely",
+ "/ai/ti-quick-start",
+ "/tidbcloud-filesystem/filesystem-mount#finish-safely",
+ ],
+ [
+ "/tidb-cloud-filesystem/filesystem-mount",
+ "/zh/ai",
+ "/tidbcloud-filesystem/filesystem-mount",
+ ],
+ [
+ "/ai/ti/reference/ti-filesystem",
+ "/tidbcloud-filesystem",
+ "/ai/ti-filesystem",
+ ],
+ [
+ "/tidb-cloud/manage-api-keys",
+ "/tidbcloud-filesystem",
+ "/tidbcloud/manage-api-keys",
+ ],
+ [
+ "filesystem-mount#finish-safely",
+ "/tidbcloud-filesystem/filesystem-quick-start",
+ "/tidbcloud-filesystem/filesystem-mount#finish-safely",
+ ],
+ ])("resolves %s from %s", (link, current, expected) => {
+ expect(resolveMarkdownLink(link, current)).toBe(expected);
+ });
+
+ it("builds pages from the Filesystem TOC and keeps cross-product links", async () => {
+ const nav = mdxAstToToc(tocAST as any, tocSlug);
+ expect(nav.map((item) => item.link)).toEqual([
+ "/tidbcloud-filesystem",
+ "/tidbcloud-filesystem/filesystem-quick-start",
+ "/ai/ti-filesystem",
+ ]);
+ const graphql = jest.fn().mockResolvedValue({
+ data: {
+ allMdx: {
+ nodes: [
+ {
+ id: "filesystem-toc",
+ slug: tocSlug,
+ mdxAST: { children: tocAST },
+ parent: { relativePath: `${tocSlug}.md` },
+ },
+ ],
+ },
+ },
+ });
+ const { tocFilesMap, tocNamesByFileMap } = await getFilesFromTocs(graphql);
+ const nodes = ["filesystem-quick-start", "unlisted-page"].map((name) => {
+ const slug = `${source}/tidb-cloud-filesystem/${name}`;
+ return { name, slug, pathConfig: generateConfig(slug).config };
+ });
+ const included = filterNodesByToc(nodes, tocFilesMap, tocNamesByFileMap);
+ expect(included.map((node) => node.name)).toEqual([
+ "filesystem-quick-start",
+ ]);
+ expect(included[0].tocNames).toEqual(["TOC-tidb-cloud-filesystem"]);
+ });
+});
diff --git a/gatsby/__tests__/toc-namespace.test.ts b/gatsby/__tests__/toc-namespace.test.ts
index 02da2f16..bd447439 100644
--- a/gatsby/__tests__/toc-namespace.test.ts
+++ b/gatsby/__tests__/toc-namespace.test.ts
@@ -20,6 +20,20 @@ describe("getTOCNamespace", () => {
);
});
+ it("maps TiDB Cloud Filesystem docs to the Filesystem namespace", () => {
+ expect(
+ getTOCNamespace(
+ "en/tidb-cloud-filesystem/master/tidb-cloud-filesystem/filesystem-quick-start"
+ )
+ ).toBe(TOCNamespace.TiDBCloudFilesystem);
+ });
+
+ it("maps the root-level Filesystem index to the Filesystem namespace", () => {
+ expect(getTOCNamespace("en/tidb-cloud-filesystem/master/_index")).toBe(
+ TOCNamespace.TiDBCloudFilesystem
+ );
+ });
+
it("keeps other TiDB stable docs in the TiDB namespace", () => {
expect(getTOCNamespace("en/tidb/release-8.5/alert-rules")).toBe(
TOCNamespace.TiDB
diff --git a/gatsby/link-resolver/__tests__/link-resolver.test.ts b/gatsby/link-resolver/__tests__/link-resolver.test.ts
index 62732b2f..66003df8 100644
--- a/gatsby/link-resolver/__tests__/link-resolver.test.ts
+++ b/gatsby/link-resolver/__tests__/link-resolver.test.ts
@@ -182,6 +182,14 @@ describe("resolveMarkdownLink", () => {
expect(result).toBe("/tidbcloudlake");
});
+ it("should resolve tidb-cloud-filesystem/_index links to the English filesystem root", () => {
+ const result = resolveMarkdownLink(
+ "/tidb-cloud-filesystem/_index",
+ "/zh/tidb/stable/alert-rules"
+ );
+ expect(result).toBe("/tidbcloud-filesystem");
+ });
+
it("should resolve best-practices namespace links (en - default language omitted)", () => {
const result = resolveMarkdownLink(
"/best-practices/optimization/query-optimization",
@@ -584,6 +592,26 @@ describe("resolveMarkdownLink", () => {
});
});
+ describe("linkMappingsByPath - tidbcloud-filesystem pages", () => {
+ it("should resolve nested relative links in the English filesystem namespace", () => {
+ const result = resolveMarkdownLink(
+ "guides/filesystem-mount#finish-safely",
+ "/tidbcloud-filesystem/filesystem-quick-start"
+ );
+ expect(result).toBe(
+ "/tidbcloud-filesystem/filesystem-mount#finish-safely"
+ );
+ });
+
+ it("should keep explicit AI links in the AI namespace", () => {
+ const result = resolveMarkdownLink(
+ "/ai/ti/reference/ti-filesystem",
+ "/tidbcloud-filesystem"
+ );
+ expect(result).toBe("/ai/ti-filesystem");
+ });
+ });
+
describe("linkMappingsByPath - tidb pages with branch", () => {
it("should resolve links from tidb pages with stable branch", () => {
const result = resolveMarkdownLink(
diff --git a/gatsby/link-resolver/config.ts b/gatsby/link-resolver/config.ts
index e89ba417..73d1950e 100644
--- a/gatsby/link-resolver/config.ts
+++ b/gatsby/link-resolver/config.ts
@@ -11,6 +11,16 @@ export const defaultLinkResolverConfig: LinkResolverConfig = {
languages: ["en", "zh", "ja"],
linkMappings: [
+ // Filesystem documentation is currently published in English only, so
+ // direct links intentionally omit the current page language.
+ {
+ linkPattern: "/tidb-cloud-filesystem/{...folders}/_index",
+ targetPattern: "/tidbcloud-filesystem/{folders}",
+ },
+ {
+ linkPattern: "/tidb-cloud-filesystem/{...folders}/{docname}",
+ targetPattern: "/tidbcloud-filesystem/{docname}",
+ },
{
linkPattern: "/releases/_index",
targetPattern: "/{curLang}/releases/tidb-self-managed",
@@ -46,7 +56,7 @@ export const defaultLinkResolverConfig: LinkResolverConfig = {
linkPattern: "/releases/{docname}",
targetPattern: "/{lang}/tidb-in-kubernetes/stable/{docname}",
},
- // Rule 1: Links starting with specific namespaces (direct link mapping)
+ // Links starting with specific namespaces (direct link mapping)
// Special handling for namespace index links:
// /develop/_index -> /developer
// /best-practices/_index -> /best-practices
@@ -93,7 +103,7 @@ export const defaultLinkResolverConfig: LinkResolverConfig = {
develop: "developer",
},
},
- // Rule 2: tidbcloud with prefix pages (path-based mapping)
+ // tidbcloud with prefix pages (path-based mapping)
// Current page: /{lang}/tidbcloud/{...any}
// Link: /{...any}/{docname} -> /{lang}/tidbcloud/{docname}
{
@@ -101,7 +111,7 @@ export const defaultLinkResolverConfig: LinkResolverConfig = {
linkPattern: "/{...any}/{docname}",
targetPattern: "/{lang}/tidbcloud/{docname}",
},
- // Rule 3: tidbcloudlake pages (path-based mapping)
+ // tidbcloudlake pages (path-based mapping)
// Current page: /{lang}/tidbcloudlake/{...any}
// Link: /{...any}/{docname} -> /{lang}/tidbcloudlake/{docname}
{
@@ -109,7 +119,14 @@ export const defaultLinkResolverConfig: LinkResolverConfig = {
linkPattern: "/{...any}/{docname}",
targetPattern: "/{lang}/tidbcloudlake/{docname}",
},
- // Rule 4: developer, best-practices, api, ai namespace in tidb folder
+ // Relative Filesystem links stay in the English product namespace.
+ // Explicit AI and other namespace links are handled by the rules above.
+ {
+ pathPattern: "/{lang}/tidbcloud-filesystem/{...any}",
+ linkPattern: "/{...folders}/{docname}",
+ targetPattern: "/tidbcloud-filesystem/{docname}",
+ },
+ // developer, best-practices, api, ai namespace in tidb folder
// Current page: /{lang}/{namespace}/{...any}
// Link: /{...any}/{docname} -> /{lang}/{namespace}/{docname}
{
@@ -120,7 +137,7 @@ export const defaultLinkResolverConfig: LinkResolverConfig = {
linkPattern: "/{...any}/{docname}",
targetPattern: "/{lang}/tidb/stable/{docname}",
},
- // Rule 4: versioned docs with branch pages (path-based mapping)
+ // Versioned docs with branch pages (path-based mapping)
// Current page: /{lang}/{repo}/{branch}/{...any} (branch is already aliased, e.g., "stable", "v8.5")
// Link: /{...any}/{docname} -> /{lang}/{repo}/{branch}/{docname}
{
diff --git a/gatsby/path/index.ts b/gatsby/path/index.ts
index 96ebaafb..725ae33c 100644
--- a/gatsby/path/index.ts
+++ b/gatsby/path/index.ts
@@ -6,6 +6,15 @@ import {
} from "../../src/shared/interface";
import CONFIG from "../../docs/docs.json";
+type DocsConfigByRepo = Record<
+ string,
+ {
+ languages: Record;
+ }
+>;
+
+const DOCS_CONFIG = CONFIG.docs as unknown as DocsConfigByRepo;
+
// @deprecated, use calculateFileUrl instead
export function generateUrl(filename: string, config: PathConfig) {
const lang = config.locale === Locale.en ? "" : `/${config.locale}`;
@@ -97,15 +106,16 @@ function branchToVersion(repo: Repo, branch: string) {
case Repo.tidbcloud:
case Repo.tidbcloudlake:
+ case Repo.tidbcloudfilesystem:
return null;
}
}
-export const AllVersion = Object.keys(CONFIG.docs).reduce((acc, val) => {
+export const AllVersion = Object.keys(DOCS_CONFIG).reduce((acc, val) => {
const repo = val as Repo;
- acc[repo] = Object.keys(CONFIG.docs[repo].languages).reduce((acc, val) => {
+ acc[repo] = Object.keys(DOCS_CONFIG[repo].languages).reduce((acc, val) => {
const locale = val as Locale.en;
- acc[locale] = CONFIG.docs[repo].languages[locale].versions.map((v) =>
+ acc[locale] = DOCS_CONFIG[repo].languages[locale].versions.map((v) =>
branchToVersion(repo, v)
);
return acc;
@@ -114,10 +124,14 @@ export const AllVersion = Object.keys(CONFIG.docs).reduce((acc, val) => {
}, {} as Record>);
export function getRepo(config: PathConfig) {
- const { languages } = CONFIG.docs[config.repo];
+ const repoConfig = DOCS_CONFIG[config.repo];
+ if (!repoConfig) {
+ throw new Error(`no config for repo ${config.repo}`);
+ }
+ const { languages } = repoConfig;
if (config.locale in languages) {
- return languages[config.locale as Locale.en].repo;
+ return languages[config.locale].repo;
}
throw new Error(`no ${config.locale} in repo ${config.repo}`);
diff --git a/gatsby/toc-namespace/index.ts b/gatsby/toc-namespace/index.ts
index 31fdcec4..adfbd24b 100644
--- a/gatsby/toc-namespace/index.ts
+++ b/gatsby/toc-namespace/index.ts
@@ -24,6 +24,10 @@ export interface NamespaceRule {
* Add new rules here to extend namespace matching logic
*/
const SHARED_NAMESPACE_RULES: NamespaceRule[] = [
+ {
+ namespace: TOCNamespace.TiDBCloudFilesystem,
+ repo: Repo.tidbcloudfilesystem,
+ },
{
namespace: TOCNamespace.AI,
repo: Repo.tidb,
diff --git a/gatsby/url-resolver/__tests__/url-resolver.test.ts b/gatsby/url-resolver/__tests__/url-resolver.test.ts
index 05c2b1e0..d6d702f9 100644
--- a/gatsby/url-resolver/__tests__/url-resolver.test.ts
+++ b/gatsby/url-resolver/__tests__/url-resolver.test.ts
@@ -322,6 +322,33 @@ describe("calculateFileUrl", () => {
expect(url).toBe("/en/tidbcloudlake/dashboards/");
});
+ it("should resolve tidb cloud filesystem _index", () => {
+ const absolutePath = path.join(
+ sourceBasePath,
+ "en/tidb-cloud-filesystem/master/tidb-cloud-filesystem/_index.md"
+ );
+ const url = calculateFileUrlWithConfig(absolutePath, testConfig);
+ expect(url).toBe("/en/tidbcloud-filesystem");
+ });
+
+ it("should resolve tidb cloud filesystem guide pages", () => {
+ const absolutePath = path.join(
+ sourceBasePath,
+ "en/tidb-cloud-filesystem/master/tidb-cloud-filesystem/guides/filesystem-mount.md"
+ );
+ const url = calculateFileUrlWithConfig(absolutePath, testConfig);
+ expect(url).toBe("/en/tidbcloud-filesystem/filesystem-mount/");
+ });
+
+ it("should resolve tidb cloud filesystem root _index", () => {
+ const absolutePath = path.join(
+ sourceBasePath,
+ "en/tidb-cloud-filesystem/master/_index.md"
+ );
+ const url = calculateFileUrlWithConfig(absolutePath, testConfig);
+ expect(url).toBe("/en/tidbcloud-filesystem");
+ });
+
it("should resolve releases folder", () => {
const absolutePath = path.join(
sourceBasePath,
@@ -595,6 +622,19 @@ describe("calculateFileUrl with defaultLanguage: 'en'", () => {
expect(url).toBe("/tidbcloudlake/dashboards");
});
+ it("should omit /en/ prefix for English tidb cloud filesystem files", () => {
+ const absolutePath = path.join(
+ sourceBasePath,
+ "en/tidb-cloud-filesystem/master/tidb-cloud-filesystem/filesystem-quick-start.md"
+ );
+ const url = calculateFileUrlWithConfig(
+ absolutePath,
+ configWithDefaultLang,
+ true
+ );
+ expect(url).toBe("/tidbcloud-filesystem/filesystem-quick-start");
+ });
+
it("should omit /en/ prefix for English release branch files", () => {
const absolutePath = path.join(
sourceBasePath,
diff --git a/gatsby/url-resolver/config.ts b/gatsby/url-resolver/config.ts
index 2b8e12d7..0c21aadf 100644
--- a/gatsby/url-resolver/config.ts
+++ b/gatsby/url-resolver/config.ts
@@ -14,6 +14,32 @@ export const defaultUrlResolverConfig: UrlResolverConfig = {
trailingSlash: "never",
pathMappings: [
+ // TiDB Cloud Filesystem is sourced from its own docs tree and published
+ // under the tidbcloud-filesystem namespace.
+ {
+ sourcePattern:
+ "/{lang}/tidb-cloud-filesystem/{branch}/tidb-cloud-filesystem/{...folders}/{filename}",
+ targetPattern: "/{lang}/tidbcloud-filesystem/{filename}",
+ filenameTransform: {
+ ignoreIf: ["_index"],
+ conditionalTarget: {
+ keepIf: ["_index"],
+ keepTargetPattern: "/{lang}/tidbcloud-filesystem/{folders}",
+ },
+ },
+ },
+ {
+ sourcePattern:
+ "/{lang}/tidb-cloud-filesystem/{branch}/{...folders}/{filename}",
+ targetPattern: "/{lang}/tidbcloud-filesystem/{filename}",
+ filenameTransform: {
+ ignoreIf: ["_index"],
+ conditionalTarget: {
+ keepIf: ["_index"],
+ keepTargetPattern: "/{lang}/tidbcloud-filesystem/{folders}",
+ },
+ },
+ },
// tidbcloud dedicated _index
// /en/tidbcloud/master/tidb-cloud/dedicated/_index.md -> /en/tidbcloud/dedicated/
{
diff --git a/locale/en/translation.json b/locale/en/translation.json
index 2cf23afd..83e9e686 100644
--- a/locale/en/translation.json
+++ b/locale/en/translation.json
@@ -36,6 +36,7 @@
"tidbCloudPremium": "TiDB Cloud Premium",
"tidbCloudDedicated": "TiDB Cloud Dedicated",
"tidbCloudLake": "TiDB Cloud Lake",
+ "tidbCloudFilesystem": "TiDB Cloud Filesystem",
"tidbShortTerm": "TiDB",
"tidbOnKubernetes": "TiDB on Kubernetes",
"tidbCloudReleases": "TiDB Cloud Releases",
diff --git a/locale/ja/translation.json b/locale/ja/translation.json
index f91b5f4f..2afa7c33 100644
--- a/locale/ja/translation.json
+++ b/locale/ja/translation.json
@@ -36,6 +36,7 @@
"tidbCloudPremium": "TiDB Cloud Premium",
"tidbCloudDedicated": "TiDB Cloud Dedicated",
"tidbCloudLake": "TiDB Cloud Lake",
+ "tidbCloudFilesystem": "TiDB Cloud Filesystem",
"tidbShortTerm": "TiDB",
"tidbOnKubernetes": "TiDB on Kubernetes",
"tidbCloudReleases": "TiDB Cloud リリース",
diff --git a/locale/zh/translation.json b/locale/zh/translation.json
index 684466e6..356506c9 100644
--- a/locale/zh/translation.json
+++ b/locale/zh/translation.json
@@ -34,6 +34,7 @@
"tidbCloudPremium": "TiDB Cloud Premium",
"tidbCloudDedicated": "TiDB Cloud Dedicated",
"tidbCloudLake": "TiDB Cloud Lake",
+ "tidbCloudFilesystem": "TiDB Cloud Filesystem",
"tidbShortTerm": "TiDB",
"tidbOnKubernetes": "TiDB on Kubernetes",
"tidbCloudReleases": "TiDB Cloud 发布记录",
diff --git a/src/components/Layout/Header/HeaderNavConfigData.tsx b/src/components/Layout/Header/HeaderNavConfigData.tsx
index 6a0b1e32..ade5916d 100644
--- a/src/components/Layout/Header/HeaderNavConfigData.tsx
+++ b/src/components/Layout/Header/HeaderNavConfigData.tsx
@@ -93,6 +93,15 @@ const getDefaultNavConfig = (
}
},
},
+ {
+ type: "item",
+ label: t("navbar.tidbCloudFilesystem"),
+ endIcon: ,
+ to: "/tidbcloud-filesystem",
+ isI18n: false,
+ selected: (namespace) =>
+ namespace === TOCNamespace.TiDBCloudFilesystem,
+ },
],
},
{
diff --git a/src/components/Layout/Header/index.tsx b/src/components/Layout/Header/index.tsx
index 4000bb81..e58d7c3a 100644
--- a/src/components/Layout/Header/index.tsx
+++ b/src/components/Layout/Header/index.tsx
@@ -416,6 +416,8 @@ const HeaderBanner = (props: HeaderProps) => {
? `/tidbcloud/${name}`
: namespace === TOCNamespace.TiDBCloudLake
? `/tidbcloudlake/${name}`
+ : namespace === TOCNamespace.TiDBCloudFilesystem
+ ? `/tidbcloud-filesystem/${name}`
: `/${props.pathConfig?.repo}/${
props.pathConfig?.version || "stable"
}/${name}`;
diff --git a/src/shared/interface.ts b/src/shared/interface.ts
index 5f6171b2..e6fcde9f 100644
--- a/src/shared/interface.ts
+++ b/src/shared/interface.ts
@@ -17,6 +17,7 @@ export enum TOCNamespace {
TiDB = "tidb",
TiDBCloud = "tidb-cloud",
TiDBCloudLake = "tidb-cloud-lake",
+ TiDBCloudFilesystem = "tidb-cloud-filesystem",
TiDBInKubernetes = "tidb-in-kubernetes",
AI = "ai",
Develop = "develop",
@@ -35,6 +36,7 @@ export const TOCNamespaceSlugMap: Record = {
[TOCNamespace.TiDB]: "",
[TOCNamespace.TiDBCloud]: "",
[TOCNamespace.TiDBCloudLake]: "tidb-cloud-lake",
+ [TOCNamespace.TiDBCloudFilesystem]: "tidb-cloud-filesystem",
[TOCNamespace.TiDBInKubernetes]: "",
[TOCNamespace.AI]: "ai",
[TOCNamespace.Develop]: "develop",
@@ -51,6 +53,7 @@ export enum Repo {
operator = "tidb-in-kubernetes",
tidbcloud = "tidbcloud",
tidbcloudlake = "tidb-cloud-lake",
+ tidbcloudfilesystem = "tidb-cloud-filesystem",
}
export enum Locale {
diff --git a/src/shared/utils/index.ts b/src/shared/utils/index.ts
index 65e412a0..82e6c8e5 100644
--- a/src/shared/utils/index.ts
+++ b/src/shared/utils/index.ts
@@ -33,6 +33,15 @@ import {
TiDBCloudBanner,
} from "components/Icons/LearingPathIcon";
+type DocsConfigByRepo = Record<
+ string,
+ {
+ languages: Record;
+ }
+>;
+
+const DOCS_CONFIG = CONFIG.docs as unknown as DocsConfigByRepo;
+
export function generateDocsHomeUrl(lang?: string) {
switch (lang) {
case "ja":
@@ -159,10 +168,14 @@ export function calcPDFUrl(config: PathConfig) {
}
export function getRepoFromPathCfg(config: PathConfig) {
- const { languages } = CONFIG.docs[config.repo];
+ const repoConfig = DOCS_CONFIG[config.repo];
+ if (!repoConfig) {
+ throw new Error(`no config for repo ${config.repo}`);
+ }
+ const { languages } = repoConfig;
if (config.locale in languages) {
- return languages[config.locale as Locale.en].repo;
+ return languages[config.locale].repo;
}
throw new Error(`no ${config.locale} in repo ${config.repo}`);
@@ -199,15 +212,17 @@ function branchToVersion(repo: Repo, branch: string) {
return branch.replace("release-", "v");
case Repo.tidbcloud:
+ case Repo.tidbcloudlake:
+ case Repo.tidbcloudfilesystem:
return null;
}
}
-export const AllVersion = Object.keys(CONFIG.docs).reduce((acc, val) => {
+export const AllVersion = Object.keys(DOCS_CONFIG).reduce((acc, val) => {
const repo = val as Repo;
- acc[repo] = Object.keys(CONFIG.docs[repo].languages).reduce((acc, val) => {
+ acc[repo] = Object.keys(DOCS_CONFIG[repo].languages).reduce((acc, val) => {
const locale = val as Locale.en;
- acc[locale] = CONFIG.docs[repo].languages[locale].versions.map((v) =>
+ acc[locale] = DOCS_CONFIG[repo].languages[locale].versions.map((v) =>
branchToVersion(repo, v)
);
return acc;