@karaoke-cms/module-docs 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/index.ts +80 -31
- package/src/pages/doc.astro +26 -12
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@karaoke-cms/module-docs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.16.0",
|
|
5
5
|
"description": "Docs module for karaoke-cms — documentation pages with sidebar navigation",
|
|
6
6
|
"main": "./src/index.ts",
|
|
7
7
|
"exports": {
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
],
|
|
24
24
|
"peerDependencies": {
|
|
25
25
|
"astro": ">=6.0.0",
|
|
26
|
-
"@karaoke-cms/contracts": "^0.
|
|
26
|
+
"@karaoke-cms/contracts": "^0.16.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"astro": "^6.0.8",
|
|
30
30
|
"vitest": "^4.1.1",
|
|
31
|
-
"@karaoke-cms/astro": "0.
|
|
32
|
-
"@karaoke-cms/contracts": "0.
|
|
31
|
+
"@karaoke-cms/astro": "0.16.0",
|
|
32
|
+
"@karaoke-cms/contracts": "0.16.0"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"test": "vitest run test/docs-factory.test.js"
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { ModuleInstance } from '@karaoke-cms/contracts';
|
|
1
|
+
import type { DocsSection, ModuleInstance } from '@karaoke-cms/contracts';
|
|
2
2
|
import { cssContract } from './css-contract.js';
|
|
3
3
|
|
|
4
4
|
export { cssContract } from './css-contract.js';
|
|
5
|
+
export type { DocsSection } from '@karaoke-cms/contracts';
|
|
5
6
|
// docsSchema is exported from '@karaoke-cms/module-docs/schema' to avoid
|
|
6
7
|
// pulling zod into the config-loading context.
|
|
7
8
|
|
|
@@ -24,43 +25,26 @@ export interface DocsConfig {
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* Returns a ModuleInstance that karaoke() uses to code-generate per-instance
|
|
30
|
-
* .astro page files and inject them as routes. Multiple instances may be
|
|
31
|
-
* configured to serve separate docs sections from different vault folders.
|
|
32
|
-
*
|
|
33
|
-
* Publishes three routes per instance:
|
|
34
|
-
* - `{mount}` — home page listing all doc titles
|
|
35
|
-
* - `{mount}/list` — standalone full docs list
|
|
36
|
-
* - `{mount}/[slug]` — individual doc with left-sidebar nav
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* // karaoke.config.ts — default section
|
|
40
|
-
* import { docs } from '@karaoke-cms/module-docs';
|
|
41
|
-
* export default defineConfig({
|
|
42
|
-
* theme: themeDefault({ implements: [docs({ mount: '/docs' })] }),
|
|
43
|
-
* });
|
|
44
|
-
*
|
|
45
|
-
* @example
|
|
46
|
-
* // karaoke.config.ts — two independent sections
|
|
47
|
-
* export default defineConfig({
|
|
48
|
-
* theme: themeDefault({ implements: [docs({ mount: '/docs' })] }),
|
|
49
|
-
* modules: [docs({ mount: '/api-docs', folder: 'api-reference', label: 'API Reference' })],
|
|
50
|
-
* });
|
|
28
|
+
* Shared helper: derive mount, folder, id, and label from config fields.
|
|
29
|
+
* Used by both docsFromSingle and docsFromSection to keep derivation in sync.
|
|
51
30
|
*/
|
|
52
|
-
|
|
53
|
-
const mount = (
|
|
31
|
+
function computeDocsMeta(cfg: { mount?: string; folder?: string; id?: string; label?: string }) {
|
|
32
|
+
const mount = (cfg.mount ?? '/docs').replace(/\/$/, '');
|
|
54
33
|
const rawFolder = mount.replace(/^\//, '');
|
|
55
|
-
if (
|
|
34
|
+
if (cfg.folder === undefined && rawFolder === '') {
|
|
56
35
|
console.warn(
|
|
57
36
|
'[karaoke-cms] docs({ mount: \'/\' }): folder defaults to "docs". ' +
|
|
58
37
|
'Pass folder explicitly (e.g. docs({ mount: \'/\', folder: \'docs\' })) to suppress this warning.',
|
|
59
38
|
);
|
|
60
39
|
}
|
|
61
|
-
const folder =
|
|
62
|
-
const id =
|
|
63
|
-
const label =
|
|
40
|
+
const folder = cfg.folder ?? (rawFolder || 'docs');
|
|
41
|
+
const id = cfg.id ?? (folder.replace(/\//g, '-') || 'docs');
|
|
42
|
+
const label = cfg.label ?? id.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
|
|
43
|
+
return { mount, folder, id, label };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function docsFromSingle(config: DocsConfig = {}): ModuleInstance {
|
|
47
|
+
const { mount, folder, id, label } = computeDocsMeta(config);
|
|
64
48
|
return {
|
|
65
49
|
_type: 'module-instance',
|
|
66
50
|
id,
|
|
@@ -82,3 +66,68 @@ export function docs(config: DocsConfig = {}): ModuleInstance {
|
|
|
82
66
|
scaffoldPages: undefined,
|
|
83
67
|
};
|
|
84
68
|
}
|
|
69
|
+
|
|
70
|
+
function docsFromSection(s: DocsSection): ModuleInstance {
|
|
71
|
+
const { mount, folder, id, label } = computeDocsMeta(s);
|
|
72
|
+
return {
|
|
73
|
+
_type: 'module-instance',
|
|
74
|
+
id,
|
|
75
|
+
mount,
|
|
76
|
+
enabled: true,
|
|
77
|
+
collection: {
|
|
78
|
+
name: id,
|
|
79
|
+
folder,
|
|
80
|
+
label,
|
|
81
|
+
layout: s.layout ?? 'default',
|
|
82
|
+
sidebarStyle: s.sidebarStyle ?? 'tree',
|
|
83
|
+
},
|
|
84
|
+
// routes is empty — karaoke() generates per-instance pages via codegen
|
|
85
|
+
routes: [],
|
|
86
|
+
menuEntries: [{
|
|
87
|
+
id,
|
|
88
|
+
name: label,
|
|
89
|
+
path: mount,
|
|
90
|
+
section: 'main',
|
|
91
|
+
weight: s.weight ?? 20,
|
|
92
|
+
...(s.parent !== undefined ? { parent: s.parent } : {}),
|
|
93
|
+
}],
|
|
94
|
+
cssContract,
|
|
95
|
+
hasDefaultCss: false,
|
|
96
|
+
defaultCssPath: undefined,
|
|
97
|
+
scaffoldPages: undefined,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Docs module — documentation pages with sidebar navigation.
|
|
103
|
+
*
|
|
104
|
+
* Single-form: returns one ModuleInstance.
|
|
105
|
+
* Array-form: accepts DocsSection[] and returns ModuleInstance[] (one per active section).
|
|
106
|
+
* Sections with `enabled: false` are excluded from the returned array.
|
|
107
|
+
*
|
|
108
|
+
* Publishes three routes per instance:
|
|
109
|
+
* - `{mount}` — home page listing all doc titles
|
|
110
|
+
* - `{mount}/list` — standalone full docs list
|
|
111
|
+
* - `{mount}/[slug]` — individual doc with left-sidebar nav
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* // karaoke.config.ts — single section
|
|
115
|
+
* modules: [docs({ mount: '/docs' })]
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* // karaoke.config.ts — multiple sections with menu ordering
|
|
119
|
+
* modules: [
|
|
120
|
+
* docs([
|
|
121
|
+
* { id: 'docs', mount: '/docs', label: 'Docs', weight: 20 },
|
|
122
|
+
* { id: 'api-docs', mount: '/api-docs', folder: 'api-reference', label: 'API Reference', weight: 25, parent: 'docs' },
|
|
123
|
+
* ]),
|
|
124
|
+
* ]
|
|
125
|
+
*/
|
|
126
|
+
export function docs(config: DocsSection[]): ModuleInstance[];
|
|
127
|
+
export function docs(config?: DocsConfig): ModuleInstance;
|
|
128
|
+
export function docs(config?: DocsConfig | DocsSection[]): ModuleInstance | ModuleInstance[] {
|
|
129
|
+
if (Array.isArray(config)) {
|
|
130
|
+
return config.filter(s => s.enabled !== false).map(s => docsFromSection(s));
|
|
131
|
+
}
|
|
132
|
+
return docsFromSingle(config);
|
|
133
|
+
}
|
package/src/pages/doc.astro
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
---
|
|
2
|
-
//
|
|
3
|
-
//
|
|
2
|
+
// NOTE: this is a reference implementation only — it is NOT used as an injected route.
|
|
3
|
+
// The actual route entrypoint is generated by karaoke() into
|
|
4
|
+
// .astro/generated/karaoke-cms/{id}/doc.astro
|
|
5
|
+
// Keep this file in sync with generateDocPage() in:
|
|
6
|
+
// packages/astro/src/codegen/generate-docs-instance.ts
|
|
7
|
+
//
|
|
8
|
+
// Differences from the generated version:
|
|
9
|
+
// - mount is hardcoded to /docs (default instance only)
|
|
10
|
+
// - collection is hardcoded to 'docs'
|
|
11
|
+
// - multi-section switcher (docsSections) is omitted
|
|
4
12
|
import { getCollection, render } from 'astro:content';
|
|
5
13
|
import DefaultPage from '@karaoke-cms/astro/layouts/DefaultPage.astro';
|
|
6
14
|
import ModuleLoader from '@karaoke-cms/astro/components/ModuleLoader.astro';
|
|
15
|
+
import DocsTree from '@karaoke-cms/astro/components/DocsTree.astro';
|
|
7
16
|
import { resolveWikiImage } from '@karaoke-cms/astro';
|
|
8
17
|
|
|
9
18
|
export async function getStaticPaths() {
|
|
@@ -17,23 +26,28 @@ export async function getStaticPaths() {
|
|
|
17
26
|
const { entry } = Astro.props;
|
|
18
27
|
const { Content } = await render(entry);
|
|
19
28
|
|
|
20
|
-
// All docs for the sidebar, sorted alphabetically by title
|
|
21
29
|
const allDocs = (await getCollection('docs', ({ data }) => data.publish === true))
|
|
22
30
|
.sort((a, b) => a.data.title.localeCompare(b.data.title));
|
|
31
|
+
|
|
32
|
+
// Show the right sidebar only when the current entry is a directory (has child entries).
|
|
33
|
+
const isDirectory = allDocs.some(d => d.id.startsWith(entry.id + '/'));
|
|
23
34
|
---
|
|
24
35
|
|
|
25
36
|
<DefaultPage title={entry.data.title} description={entry.data.description} type="article">
|
|
26
37
|
<nav class="docs-sidebar" slot="left">
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{doc.data.title}
|
|
32
|
-
</a>
|
|
33
|
-
</li>
|
|
34
|
-
))}
|
|
35
|
-
</ul>
|
|
38
|
+
<details class="docs-sidebar-wrap" open>
|
|
39
|
+
<summary class="docs-sidebar-toggle">Docs</summary>
|
|
40
|
+
<DocsTree entries={allDocs} currentId={entry.id} mount="/docs" />
|
|
41
|
+
</details>
|
|
36
42
|
</nav>
|
|
43
|
+
{isDirectory && (
|
|
44
|
+
<aside class="docs-page-nav" slot="right">
|
|
45
|
+
<details class="docs-sidebar-wrap" open>
|
|
46
|
+
<summary class="docs-sidebar-toggle">In this section</summary>
|
|
47
|
+
<DocsTree entries={allDocs} currentId={entry.id} mount="/docs" rootPath={entry.id} maxDepth={1} />
|
|
48
|
+
</details>
|
|
49
|
+
</aside>
|
|
50
|
+
)}
|
|
37
51
|
<article class="docs-article">
|
|
38
52
|
{entry.data.featured_image && (
|
|
39
53
|
<img src={resolveWikiImage(entry.data.featured_image)} alt="" class="docs-article-image" />
|