Merge branch 'master' into feat/web-loader-plugin-inventory-settings
This commit is contained in:
@@ -5,7 +5,7 @@ import { existsSync, globSync, mkdirSync, mkdtempSync, readFileSync, realpathSyn
|
||||
import { tmpdir } from 'node:os'
|
||||
import { basename, join, resolve } from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { docsPages, type DocsPage } from '../website/docs.ts'
|
||||
import { docsPages, landingLink, routeLink, sectionSpec, type DocsPage } from '../website/docs.ts'
|
||||
import {
|
||||
addProjectionFrontmatter, projectedPageContent, publishableImage, rewriteMarkdown,
|
||||
} from './project-doc-site.ts'
|
||||
@@ -364,6 +364,63 @@ describe('docsPages locale routes', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('sidebar ordering', () => {
|
||||
it('places every section a sidebar collection owns', () => {
|
||||
for (const page of docsPages) {
|
||||
if (page.sidebar === null) continue
|
||||
expect(() => sectionSpec(page.locale, page.section), page.route).not.toThrow()
|
||||
}
|
||||
})
|
||||
|
||||
it('refuses a section with no declared placement', () => {
|
||||
expect(() => sectionSpec('root', '数据结构'))
|
||||
.toThrow('Sidebar section "数据结构" has no placement in the root locale.')
|
||||
})
|
||||
|
||||
it('declares placements per locale rather than in one shared list', () => {
|
||||
// `SDK` labels a group in both locales, so one shared list would have to
|
||||
// rank it against `入门` and against `Guide` at the same position.
|
||||
expect(sectionSpec('root', 'SDK').index).toBeGreaterThan(sectionSpec('root', '入门').index)
|
||||
expect(sectionSpec('en', 'SDK').index).toBeGreaterThan(sectionSpec('en', 'Guide').index)
|
||||
expect(() => sectionSpec('en', '入门')).toThrow()
|
||||
expect(() => sectionSpec('root', 'Guide')).toThrow()
|
||||
})
|
||||
|
||||
it('lands every navigation item on a page the manifest publishes', () => {
|
||||
// The navigation bar named `/guide/` while the manifest published the guide's
|
||||
// first page at `guide/quickstart.md`, so the item served a 404.
|
||||
const collections = [
|
||||
['root', 'zh-guide'], ['root', 'zh-develop'], ['root', 'zh-reference'],
|
||||
['en', 'en-guide'], ['en', 'en-develop'], ['en', 'en-reference'],
|
||||
] as const
|
||||
const published = new Set(docsPages.map(page => routeLink(page.route)))
|
||||
for (const [locale, collection] of collections) {
|
||||
expect(published, `${locale}/${collection}`).toContain(landingLink(locale, collection))
|
||||
}
|
||||
})
|
||||
|
||||
it('collapses the subsystem groups and leaves the smaller ones open', () => {
|
||||
expect(sectionSpec('root', '执行与工具').collapsed).toBe(true)
|
||||
expect(sectionSpec('en', 'Execution and tools').collapsed).toBe(true)
|
||||
expect(sectionSpec('root', '概念').collapsed).toBeUndefined()
|
||||
})
|
||||
|
||||
it('gives each page its own position within a section', () => {
|
||||
// Sidebar entries sort by order alone, so a shared value leaves the two
|
||||
// pages ranked by whichever manifest block happens to be concatenated
|
||||
// first rather than by an intent the manifest states.
|
||||
const taken = new Map<string, string>()
|
||||
const collisions: string[] = []
|
||||
for (const page of docsPages) {
|
||||
const slot = `${page.locale}/${String(page.sidebar)}/${page.section}#${page.order}`
|
||||
const holder = taken.get(slot)
|
||||
if (holder === undefined) taken.set(slot, page.label)
|
||||
else collisions.push(`${slot}: ${holder} / ${page.label}`)
|
||||
}
|
||||
expect(collisions).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('addProjectionFrontmatter', () => {
|
||||
it('adds frontmatter to an ordinary Markdown page', () => {
|
||||
expect(addProjectionFrontmatter('# Guide\n', { source: 'docs/guide.md' })).toBe(
|
||||
@@ -411,6 +468,25 @@ describe('projectedPageContent', () => {
|
||||
expect(projectedPageContent(markdown, page('zh-guide'))).toBe(markdown)
|
||||
})
|
||||
|
||||
it('drops the language switcher the navigation bar already offers', () => {
|
||||
expect(projectedPageContent('# Guide\n\nEnglish | [中文](./en/guide)\n\nBody.\n', page('zh-guide')))
|
||||
.toBe('# Guide\n\nBody.\n')
|
||||
expect(projectedPageContent('# 指南\n\n[English](./en/guide) | 中文\n\n正文。\n', page('zh-guide')))
|
||||
.toBe('# 指南\n\n正文。\n')
|
||||
})
|
||||
|
||||
it('drops the repository badge every page links from its footer', () => {
|
||||
const badge = '[](https://github.com/deepseek-ai/deepseek-harness)'
|
||||
expect(projectedPageContent(`# Guide\n\nBody.\n\n${badge}\n`, page('zh-guide')))
|
||||
.toBe('# Guide\n\nBody.\n')
|
||||
})
|
||||
|
||||
it('keeps a switcher-shaped line that is not the page header', () => {
|
||||
// A tutorial showing the convention must still render the example.
|
||||
const sample = '# Guide\n\nA\n\nB\n\nC\n\nD\n\nE\n\nEnglish | [中文](./x)\n'
|
||||
expect(projectedPageContent(sample, page('zh-guide'))).toBe(sample)
|
||||
})
|
||||
|
||||
it('rejects a locale home source without frontmatter', () => {
|
||||
expect(() => projectedPageContent('# Harness\n', page(null)))
|
||||
.toThrow('locale home source "docs/index.zh.md" must start with YAML frontmatter')
|
||||
|
||||
@@ -292,6 +292,37 @@ export function addProjectionFrontmatter(markdown: string, page: Pick<DocsPage,
|
||||
return `---\n${fields}\n---\n\n${markdown}`
|
||||
}
|
||||
|
||||
/** The switcher line a canonical page carries so its GitHub reader can reach the other language. */
|
||||
const LANGUAGE_SWITCHER = /^(?:English \| \[中文\]\([^)]*\)|\[English\]\([^)]*\) \| 中文)$/
|
||||
|
||||
/** The repository badge a canonical page carries for its GitHub reader. */
|
||||
const REPOSITORY_BADGE = /^\[!\[[^\]]*\]\(https:\/\/img\.shields\.io\/[^)]*\)\]\([^)]*\)$/
|
||||
|
||||
/**
|
||||
* Drop the lines that address a canonical page's GitHub reader.
|
||||
*
|
||||
* The site carries a locale switcher in its navigation bar and links the
|
||||
* repository from every page, so projecting these lines would repeat both — the
|
||||
* switcher as the first element under each heading.
|
||||
*
|
||||
* @param markdown Rewritten canonical Markdown content.
|
||||
* @returns The content without the switcher line or the repository badge.
|
||||
*/
|
||||
function withoutRepositoryChrome(markdown: string): string {
|
||||
const lines = markdown.split('\n')
|
||||
const switcher = lines.findIndex(line => LANGUAGE_SWITCHER.test(line))
|
||||
// Only the switcher introducing the page qualifies; further down the same
|
||||
// text is prose or a sample rather than the page's own header.
|
||||
if (switcher !== -1 && switcher < 8) {
|
||||
lines.splice(switcher, lines[switcher + 1] === '' ? 2 : 1)
|
||||
}
|
||||
const badge = lines.findLastIndex(line => REPOSITORY_BADGE.test(line))
|
||||
if (badge !== -1) {
|
||||
lines.splice(lines[badge - 1] === '' ? badge - 1 : badge, lines[badge - 1] === '' ? 2 : 1)
|
||||
}
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the Markdown rendered for one published page.
|
||||
*
|
||||
@@ -300,7 +331,7 @@ export function addProjectionFrontmatter(markdown: string, page: Pick<DocsPage,
|
||||
* @returns Full Markdown for ordinary pages or frontmatter-only Markdown for a locale home page.
|
||||
*/
|
||||
export function projectedPageContent(markdown: string, page: DocsPage): string {
|
||||
if (page.sidebar !== null) return markdown
|
||||
if (page.sidebar !== null) return withoutRepositoryChrome(markdown)
|
||||
if (!markdown.startsWith('---\n')) {
|
||||
throw new Error(`project-doc-site: locale home source ${JSON.stringify(page.source)} must start with YAML frontmatter.`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user