refactor: drop the remaining docusaurus names from identifiers and comments (#589)

Follow-up to #588.

- rename `DocusaurusSidebarItem` -> `SidebarSourceItem`, `fromDocusaurus` -> `fromSidebarSource`, `docusaurusSidebars` -> `sidebarSources`
- reword comments and READMEs that described behaviour by comparison with docusaurus; fix the stale note in `sites/docs/src/config/api.ts`
- drop `.docusaurus/` from `.gitignore`

No functional change. `pnpm check` has 0 errors and `pnpm build` still builds 6690 pages.

<!-- cloudflare-preview --> Preview: https://pr-589.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/589
Reviewed-by: silverwind <[email protected]>
This commit is contained in:
Lunny Xiao committed 2026-10-15 17:39:24 +00:00
1 parent 2275bef3bd
commit b555a247e0
15 files changed
+46 -48

No files matched your search

-1
View File
@@ -6,7 +6,6 @@ sites/*/dist
sites/*/.astro sites/*/.astro
sites/*/.cache sites/*/.cache
build/ build/
.docusaurus/
# Misc # Misc
.DS_Store .DS_Store
+2 -2
View File
@@ -2,8 +2,8 @@
# is copied into the build output by the publish workflow, so that it is not # is copied into the build output by the publish workflow, so that it is not
# uploaded to S3/CloudFront where it would be publicly readable at /_headers. # uploaded to S3/CloudFront where it would be publicly readable at /_headers.
# #
# Unlike the docusaurus build, astro emits a single content hashed asset # The build emits a single content hashed asset directory shared by every
# directory shared by every language, so no per locale rules are needed. # language, so no per locale rules are needed.
# Content hashed build output (js, css, fonts). The file name changes with the # Content hashed build output (js, css, fonts). The file name changes with the
# content, so these can be cached forever. # content, so these can be cached forever.
+1 -1
View File
@@ -1,7 +1,7 @@
# Redirects for legacy URLs and routes the site no longer serves directly. # Redirects for legacy URLs and routes the site no longer serves directly.
# The api reference and the runner docs are separate products, published in # The api reference and the runner docs are separate products, published in
# english only. Docusaurus served an empty localized copy of them. # english only, so they have no localized copy.
/zh-cn/api/* /api/:splat 301 /zh-cn/api/* /api/:splat 301
/zh-tw/api/* /api/:splat 301 /zh-tw/api/* /api/:splat 301
/zh-cn/runner/* /runner/:splat 301 /zh-cn/runner/* /runner/:splat 301
+2 -2
View File
@@ -2,7 +2,7 @@ import { promises as fs } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import type { Segment } from './segments.js'; import type { Segment } from './segments.js';
/** `_category_.json`, the docusaurus per directory sidebar metadata. */ /** `_category_.json`, the per directory sidebar metadata. */
export interface CategoryMeta { export interface CategoryMeta {
label?: string; label?: string;
position?: number; position?: number;
@@ -41,7 +41,7 @@ export function slugifyLabel(label: string): string {
} }
/** /**
* Route id of the index page docusaurus generates for a category. * Route id of the index page generated for a category.
* *
* Top level categories are declared by `sidebars.js` without a slug and end up * Top level categories are declared by `sidebars.js` without a slug and end up
* under `/category/<label>`; nested ones keep the slug of their * under `/category/<label>`; nested ones keep the slug of their
+4 -4
View File
@@ -74,7 +74,7 @@ const transformVersion = '2';
/** /**
* Loads the gitea documentation straight out of the content directories, which * Loads the gitea documentation straight out of the content directories, which
* keep the layout the docusaurus site used, instead of copying them into * keep their original layout, instead of copying them into
* `src/content/docs`. Every source file is mapped to its final route by the * `src/content/docs`. Every source file is mapped to its final route by the
* product matrix, so the release and translation workflows are untouched. * product matrix, so the release and translation workflows are untouched.
*/ */
@@ -190,7 +190,7 @@ async function parseFile(
const substituted = applyVariables(contents, segment.version.variables); const substituted = applyVariables(contents, segment.version.variables);
const { frontmatter, body: rawBody } = splitFrontmatter(substituted); const { frontmatter, body: rawBody } = splitFrontmatter(substituted);
// a translated `slug` moves the page, exactly like it did under docusaurus // a `slug` moves the page
const id = routeIdOf(segment, routeSuffix(segment, file, asString(frontmatter.slug))); const id = routeIdOf(segment, routeSuffix(segment, file, asString(frontmatter.slug)));
const { title: headingTitle, body } = extractTitle(rawBody); const { title: headingTitle, body } = extractTitle(rawBody);
const title = asString(frontmatter.title) ?? headingTitle; const title = asString(frontmatter.title) ?? headingTitle;
@@ -289,7 +289,7 @@ async function renderPage(
return broken; return broken;
} }
/** The index page docusaurus generated for a category. */ /** The index page generated for a category. */
interface ParsedCategory { interface ParsedCategory {
segment: Segment; segment: Segment;
id: string; id: string;
@@ -301,7 +301,7 @@ interface ParsedCategory {
} }
/** /**
* Recreates the index pages docusaurus generated from `_category_.json`, so * Recreates the index pages generated from `_category_.json`, so
* that `/category/installation/` and `/usage/actions/` keep resolving. * that `/category/installation/` and `/usage/actions/` keep resolving.
*/ */
async function resolveCategories( async function resolveCategories(
+2 -2
View File
@@ -1,6 +1,6 @@
/** /**
* Rewrites the relative markdown links of the sources to the urls of the built * Rewrites the relative markdown links of the sources to the urls of the built
* site. Docusaurus resolved `../administration/config-cheat-sheet.md` against * site. A link such as `../administration/config-cheat-sheet.md` is resolved against
* the directory of the file it appeared in; astro serves urls, so the target * the directory of the file it appeared in; astro serves urls, so the target
* has to be looked up in the route table of the version. * has to be looked up in the route table of the version.
*/ */
@@ -35,7 +35,7 @@ function rewriteTarget(target: string, context: LinkContext): string {
const [pathname = '', hash = ''] = splitHash(target); const [pathname = '', hash = ''] = splitHash(target);
if (!/\.mdx?$/.test(pathname)) return target; if (!/\.mdx?$/.test(pathname)) return target;
// docusaurus resolved a link against the directory of the file first and // a link is resolved against the directory of the file first and
// against the root of the version second, both spellings are in the sources // against the root of the version second, both spellings are in the sources
const route = const route =
context.resolve(normalize(`${context.dir}/${pathname}`)) ?? context.resolve(normalize(pathname)); context.resolve(normalize(`${context.dir}/${pathname}`)) ?? context.resolve(normalize(pathname));
+3 -3
View File
@@ -16,8 +16,8 @@ export function splitFrontmatter(contents: string): ParsedFile {
} }
/** /**
* Replaces the `@name@` placeholders with the values of the version, the same * Replaces the `@name@` placeholders with the values of the version, which
* substitution the docusaurus markdown preprocessor did. * is how the versioned docs reference release specific values.
*/ */
export function applyVariables(contents: string, variables?: VersionVariables): string { export function applyVariables(contents: string, variables?: VersionVariables): string {
if (!variables) return contents; if (!variables) return contents;
@@ -45,7 +45,7 @@ export function extractTitle(body: string): { title?: string; body: string } {
} }
/** /**
* Docusaurus admonitions to starlight asides. The two vocabularies overlap for * Admonitions (`:::note`) to starlight asides. The two vocabularies overlap for
* `note`, `tip`, `caution` and `danger`; `warning` and `info` have to be mapped * `note`, `tip`, `caution` and `danger`; `warning` and `info` have to be mapped
* and titles move from `:::note Title` to `:::note[Title]`. * and titles move from `:::note Title` to `:::note[Title]`.
*/ */
+1 -1
View File
@@ -60,7 +60,7 @@ export interface ProductDef {
/** Languages the product is published in, in display order. */ /** Languages the product is published in, in display order. */
locales: LocaleId[]; locales: LocaleId[];
versions: VersionDef[]; versions: VersionDef[];
/** Recreate the docusaurus `_category_.json` index pages for the product. */ /** Recreate the `_category_.json` index pages for the product. */
categoryPages?: boolean; categoryPages?: boolean;
/** Set for products served by another deployment (enterprise). */ /** Set for products served by another deployment (enterprise). */
externalBaseUrl?: string; externalBaseUrl?: string;
+2 -3
View File
@@ -84,7 +84,7 @@ export function segmentOf(segments: Segment[], file: string): Segment | undefine
} }
/** /**
* Route id of a source file, mirroring the docusaurus routing rules: * Route id of a source file, following these rules:
* `index` files address their directory, a relative `slug` replaces the last * `index` files address their directory, a relative `slug` replaces the last
* path segment and `slug: /` addresses the root of the version. * path segment and `slug: /` addresses the root of the version.
*/ */
@@ -106,8 +106,7 @@ export function routeSuffix(segment: Segment, file: string, slug?: string): stri
/** /**
* Route id of a source file. Translations follow the route of their english * Route id of a source file. Translations follow the route of their english
* counterpart, a translated `slug` never moves a page, which is how docusaurus * counterpart, a translated `slug` never moves a page.
* treated the localized trees as well.
*/ */
export function routeIdOf(segment: Segment, suffix: string[]): string { export function routeIdOf(segment: Segment, suffix: string[]): string {
// the data store rejects empty ids, starlight maps `index` to the root // the data store rejects empty ids, starlight maps `index` to the root
+15 -15
View File
@@ -1,6 +1,6 @@
/** /**
* Builds one sidebar per (product, version, language) out of the loaded pages, * Builds one sidebar per (product, version, language) out of the loaded pages,
* reproducing what docusaurus derived from `_category_.json` and from the * deriving it from `_category_.json` and from the
* handwritten sidebar files of the runner docs. * handwritten sidebar files of the runner docs.
*/ */
@@ -44,12 +44,12 @@ export interface SidebarGroup {
export type SidebarEntry = SidebarLink | SidebarGroup; export type SidebarEntry = SidebarLink | SidebarGroup;
/** A docusaurus sidebar file, as used by the runner docs. */ /** A hand written sidebar file, as used by the runner docs. */
export type DocusaurusSidebarItem = export type SidebarSourceItem =
| string | string
| { type: 'doc'; id: string; label?: string } | { type: 'doc'; id: string; label?: string }
| { type: 'autogenerated'; dirName: string } | { type: 'autogenerated'; dirName: string }
| { type: 'category'; label: string; collapsed?: boolean; items: DocusaurusSidebarItem[] }; | { type: 'category'; label: string; collapsed?: boolean; items: SidebarSourceItem[] };
const last = Number.MAX_SAFE_INTEGER; const last = Number.MAX_SAFE_INTEGER;
@@ -66,11 +66,11 @@ function labelFromDirname(name: string): string {
/** /**
* Builds the sidebars of every group of pages, keyed by the route prefix of the * Builds the sidebars of every group of pages, keyed by the route prefix of the
* group. `manual` provides the docusaurus sidebar of the groups that ship one. * group. `manual` provides the hand written sidebar of the groups that ship one.
*/ */
export function buildSidebars( export function buildSidebars(
pages: SidebarPage[], pages: SidebarPage[],
manual: Map<string, DocusaurusSidebarItem[]> = new Map(), manual: Map<string, SidebarSourceItem[]> = new Map(),
): Map<string, SidebarEntry[]> { ): Map<string, SidebarEntry[]> {
const groups = new Map<string, SidebarPage[]>(); const groups = new Map<string, SidebarPage[]>();
for (const page of pages) { for (const page of pages) {
@@ -84,19 +84,19 @@ export function buildSidebars(
const key = `${groupPages[0]!.meta.product}@${groupPages[0]!.meta.version}`; const key = `${groupPages[0]!.meta.product}@${groupPages[0]!.meta.version}`;
const items = manual.get(key); const items = manual.get(key);
const tree = treeOf(groupPages); const tree = treeOf(groupPages);
sidebars.set(prefix, items ? fromDocusaurus(items, groupPages, tree) : tree.build('')); sidebars.set(prefix, items ? fromSidebarSource(items, groupPages, tree) : tree.build(''));
} }
return sidebars; return sidebars;
} }
interface Tree { interface Tree {
/** Entries of a directory, ordered as docusaurus ordered them. */ /** Entries of a directory, ordered by their position. */
build(dir: string): SidebarEntry[]; build(dir: string): SidebarEntry[];
/** Localized label of a directory, from its `_category_.json`. */ /** Localized label of a directory, from its `_category_.json`. */
label(dir: string): string; label(dir: string): string;
} }
/** Sidebar of a directory tree, ordered by `sidebar_position` as docusaurus did. */ /** Sidebar of a directory tree, ordered by `sidebar_position`. */
function treeOf(pages: SidebarPage[]): Tree { function treeOf(pages: SidebarPage[]): Tree {
const categories = new Map<string, SidebarPage>(); const categories = new Map<string, SidebarPage>();
for (const page of pages) { for (const page of pages) {
@@ -148,9 +148,9 @@ function treeOf(pages: SidebarPage[]): Tree {
return { build, label }; return { build, label };
} }
/** Sidebar described by a docusaurus sidebar file, used by the runner docs. */ /** Sidebar described by a hand written sidebar file, used by the runner docs. */
function fromDocusaurus( function fromSidebarSource(
items: DocusaurusSidebarItem[], items: SidebarSourceItem[],
pages: SidebarPage[], pages: SidebarPage[],
tree: Tree, tree: Tree,
): SidebarEntry[] { ): SidebarEntry[] {
@@ -159,7 +159,7 @@ function fromDocusaurus(
byDocId.set([page.meta.dir, page.meta.name].filter(Boolean).join('/'), page); byDocId.set([page.meta.dir, page.meta.name].filter(Boolean).join('/'), page);
} }
const convert = (item: DocusaurusSidebarItem): SidebarEntry | undefined => { const convert = (item: SidebarSourceItem): SidebarEntry | undefined => {
if (typeof item === 'string') { if (typeof item === 'string') {
const page = byDocId.get(item); const page = byDocId.get(item);
return page && link(page); return page && link(page);
@@ -167,7 +167,7 @@ function fromDocusaurus(
if (item.type === 'doc') { if (item.type === 'doc') {
const page = byDocId.get(item.id); const page = byDocId.get(item.id);
// the label of the sidebar file is english, the title of a translated // the label of the sidebar file is english, the title of a translated
// page is already localized (docusaurus translated it from the json) // page is already localized (translated from the json)
return page && link(page, page.meta.locale === defaultLocale ? (item.label ?? page.title) : page.title); return page && link(page, page.meta.locale === defaultLocale ? (item.label ?? page.title) : page.title);
} }
if (item.type === 'autogenerated') { if (item.type === 'autogenerated') {
@@ -182,7 +182,7 @@ function fromDocusaurus(
} }
// a category whose only child is an autogenerated directory keeps the // a category whose only child is an autogenerated directory keeps the
// translated label of the directory, like the docusaurus sidebars did // translated label of the directory, like the sidebar files do
const single = const single =
item.items.length === 1 && typeof item.items[0] === 'object' && 'dirName' in item.items[0] item.items.length === 1 && typeof item.items[0] === 'object' && 'dirName' in item.items[0]
? item.items[0] ? item.items[0]
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env node #!/usr/bin/env node
/** /**
* Cuts a documentation version, the replacement for `docusaurus docs:version`. * Cuts a documentation version: freezes the current docs as a released version.
* *
* node scripts/cut-version.mjs docs 1.28 # freezes docs/ as 1.28 * node scripts/cut-version.mjs docs 1.28 # freezes docs/ as 1.28
* node scripts/cut-version.mjs runner 4 # freezes runner-docs/ as 4 * node scripts/cut-version.mjs runner 4 # freezes runner-docs/ as 4
+3 -3
View File
@@ -49,17 +49,17 @@ of every file, the second normalizes and renders it:
- substitutes the version variables (`@version@`, `@dockerVersion@`, ...) - substitutes the version variables (`@version@`, `@dockerVersion@`, ...)
- lifts the leading `# heading` into the starlight `title` - lifts the leading `# heading` into the starlight `title`
- maps `sidebar_position` to `sidebar.order` and honours the docusaurus `slug`, - maps `sidebar_position` to `sidebar.order` and honours the `slug`,
including the translations that moved a page including the translations that moved a page
- rewrites the relative `*.md` links to urls, resolving them against the file - rewrites the relative `*.md` links to urls, resolving them against the file
and against the version root, and reports the ones that do not resolve and against the version root, and reports the ones that do not resolve
- turns the docusaurus admonitions into starlight asides (`:::warning` to - turns the admonitions into starlight asides (`:::warning` to
`:::caution`, `:::info` to `:::note`, `:::note Title` to `:::note[Title]`) `:::caution`, `:::info` to `:::note`, `:::note Title` to `:::note[Title]`)
- recreates the index pages of `_category_.json` (`/category/installation/`, - recreates the index pages of `_category_.json` (`/category/installation/`,
`/usage/actions/`) `/usage/actions/`)
Sidebars are built per (product, version, language) from `_category_.json` and Sidebars are built per (product, version, language) from `_category_.json` and
from the docusaurus sidebar files (`sidebars.js`, `versioned_sidebars/`, from the sidebar files (`sidebars.js`, `versioned_sidebars/`,
`runner-sidebars.js`, `runner-docs_versioned_sidebars/`), and selected in `runner-sidebars.js`, `runner-docs_versioned_sidebars/`), and selected in
`src/routeData.ts`, since starlight only supports a single static sidebar. `src/routeData.ts`, since starlight only supports a single static sidebar.
@@ -4,7 +4,7 @@ import { t } from '../config/strings';
import { searchFilters } from '../lib/search'; import { searchFilters } from '../lib/search';
/** /**
* Translated pages carry the notice the docusaurus site showed: a translation * Translated pages carry the notice: a translation
* can lag behind the english original, so point at it and at the translation * can lag behind the english original, so point at it and at the translation
* guide. Fallback pages already get starlight's own notice. * guide. Fallback pages already get starlight's own notice.
* *
+1 -1
View File
@@ -81,7 +81,7 @@ function hyphenateOperationIds(document: SwaggerDocument): void {
* `update_api_docs.sh` rewrites `basePath` of the swagger documents to the full * `update_api_docs.sh` rewrites `basePath` of the swagger documents to the full
* `https://gitea.com/api/v1` url, which redoc accepted but is not valid swagger * `https://gitea.com/api/v1` url, which redoc accepted but is not valid swagger
* 2.0: the host belongs into `host` and the scheme into `schemes`. Normalize a * 2.0: the host belongs into `host` and the scheme into `schemes`. Normalize a
* copy instead of touching the sources, which the docusaurus site still reads. * copy instead of touching the sources, which other tooling still reads.
* *
* The openapi 3.0 documents keep the url in `servers`, where a full url is * The openapi 3.0 documents keep the url in `servers`, where a full url is
* valid, so only the operation ids and the tags are touched for those. * valid, so only the operation ids and the tags are touched for those.
+8 -8
View File
@@ -4,7 +4,7 @@ import path from 'node:path';
import { import {
buildSidebars, buildSidebars,
products, products,
type DocusaurusSidebarItem, type SidebarSourceItem,
type SidebarEntry, type SidebarEntry,
type SidebarPage, type SidebarPage,
} from '@gitea-docs/content-loader'; } from '@gitea-docs/content-loader';
@@ -12,21 +12,21 @@ import { getCollection } from 'astro:content';
import { repoRoot } from '../config/paths'; import { repoRoot } from '../config/paths';
/** /**
* Sidebar files of the docusaurus site: the top level order of the docs and the * Sidebar files: the top level order of the docs and the
* handwritten sidebars of the runner, one per version. Read as is, so cutting a * handwritten sidebars of the runner, one per version. Read as is, so cutting a
* version keeps needing no change here. * version keeps needing no change here.
*/ */
function docusaurusSidebars(): Map<string, DocusaurusSidebarItem[]> { function sidebarSources(): Map<string, SidebarSourceItem[]> {
const sidebars = new Map<string, DocusaurusSidebarItem[]>(); const sidebars = new Map<string, SidebarSourceItem[]>();
const require = createRequire(import.meta.url); const require = createRequire(import.meta.url);
const read = (file: string): Record<string, DocusaurusSidebarItem[]> | undefined => { const read = (file: string): Record<string, SidebarSourceItem[]> | undefined => {
const full = path.join(repoRoot, file); const full = path.join(repoRoot, file);
if (!existsSync(full)) return undefined; if (!existsSync(full)) return undefined;
try { try {
return file.endsWith('.json') return file.endsWith('.json')
? (JSON.parse(readFileSync(full, 'utf-8')) as Record<string, DocusaurusSidebarItem[]>) ? (JSON.parse(readFileSync(full, 'utf-8')) as Record<string, SidebarSourceItem[]>)
: (require(full) as Record<string, DocusaurusSidebarItem[]>); : (require(full) as Record<string, SidebarSourceItem[]>);
} catch (error) { } catch (error) {
// an unreadable sidebar would silently fall back to the directory tree, // an unreadable sidebar would silently fall back to the directory tree,
// which reorders the whole navigation without any other sign // which reorders the whole navigation without any other sign
@@ -73,4 +73,4 @@ const pages: SidebarPage[] = entries.flatMap((entry) => {
}); });
/** Sidebars of every (product, version, language), keyed by their route prefix. */ /** Sidebars of every (product, version, language), keyed by their route prefix. */
export const sidebars: Map<string, SidebarEntry[]> = buildSidebars(pages, docusaurusSidebars()); export const sidebars: Map<string, SidebarEntry[]> = buildSidebars(pages, sidebarSources());