@karaoke-cms/theme-default 0.9.8 → 0.11.3
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/README.md +5 -0
- package/package.json +7 -6
- package/src/index.ts +23 -14
- package/src/pages/tags/[tag].astro +18 -8
- package/src/pages/tags/index.astro +9 -6
package/README.md
CHANGED
|
@@ -78,6 +78,11 @@ layout: {
|
|
|
78
78
|
|
|
79
79
|
Available: `'header'`, `'main-menu'`, `'search'`, `'recent-posts'`, `'footer'`.
|
|
80
80
|
|
|
81
|
+
## What's new in 0.10.2
|
|
82
|
+
|
|
83
|
+
- **Section-aware tags and RSS** — `/tags`, `/tags/[tag]`, and `/rss.xml` now aggregate entries across all active docs sections, not just the default `/docs` section.
|
|
84
|
+
- No other user-facing visual changes in this release.
|
|
85
|
+
|
|
81
86
|
## What's new in 0.9.5
|
|
82
87
|
|
|
83
88
|
- **`themeDefault()` function API** replaces the old string `theme: '@karaoke-cms/theme-default'`
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@karaoke-cms/theme-default",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.11.3",
|
|
5
5
|
"description": "Default theme for karaoke-cms — two-column knowledge base with blog and docs",
|
|
6
6
|
"main": "./src/index.ts",
|
|
7
7
|
"exports": {
|
|
@@ -17,17 +17,18 @@
|
|
|
17
17
|
"karaoke-cms"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@karaoke-cms/module-blog": "
|
|
20
|
+
"@karaoke-cms/module-blog": "0.11.3"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
23
|
"astro": ">=6.0.0",
|
|
24
|
-
"@karaoke-cms/
|
|
24
|
+
"@karaoke-cms/contracts": "^0.11.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"
|
|
28
|
-
"astro": "
|
|
27
|
+
"astro": "^6.0.8",
|
|
28
|
+
"@karaoke-cms/astro": "0.11.3",
|
|
29
|
+
"@karaoke-cms/contracts": "0.11.3"
|
|
29
30
|
},
|
|
30
31
|
"scripts": {
|
|
31
32
|
"test": "echo \"Stub — no tests\""
|
|
32
33
|
}
|
|
33
|
-
}
|
|
34
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AstroIntegration } from 'astro';
|
|
2
|
-
import type { ModuleInstance } from '@karaoke-cms/
|
|
2
|
+
import type { ModuleInstance, ThemeInstance } from '@karaoke-cms/contracts';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { existsSync } from 'fs';
|
|
5
5
|
|
|
@@ -9,20 +9,31 @@ const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
|
9
9
|
// Exported separately so it can be called by both the new factory (themeDefault)
|
|
10
10
|
// and the legacy default export without triggering a @karaoke-cms/astro import.
|
|
11
11
|
//
|
|
12
|
-
// When
|
|
13
|
-
//
|
|
14
|
-
function buildIntegration(
|
|
12
|
+
// When modules provide a docs collection, theme-default's hardcoded /docs fallback
|
|
13
|
+
// routes are skipped — karaoke() generates per-instance pages via codegen instead.
|
|
14
|
+
function buildIntegration(implementedModules: ModuleInstance[] = [], activeModules: ModuleInstance[] = []): AstroIntegration {
|
|
15
|
+
const allModules = [...implementedModules, ...activeModules];
|
|
16
|
+
const hasBlog = allModules.some(m => m.id === 'blog');
|
|
17
|
+
// Suppress the /docs fallback only when a docs() instance is mounted at /docs specifically.
|
|
18
|
+
// Checking allModules (both theme implements and config.modules) ensures:
|
|
19
|
+
// - docs() in config.modules at /docs → suppresses fallback (codegen handles it)
|
|
20
|
+
// - docs() at /api-docs only → keeps the /docs fallback intact
|
|
21
|
+
const hasDocsCollection = allModules.some(
|
|
22
|
+
m => m.collection != null && m.mount.replace(/\/$/, '') === '/docs'
|
|
23
|
+
);
|
|
24
|
+
|
|
15
25
|
return {
|
|
16
26
|
name: '@karaoke-cms/theme-default',
|
|
17
27
|
hooks: {
|
|
18
28
|
'astro:config:setup': ({ injectRoute, updateConfig, config: astroConfig }) => {
|
|
19
|
-
// Blog post route: fall back to module-blog post page when blog is
|
|
20
|
-
if (!
|
|
29
|
+
// Blog post route: fall back to module-blog post page when no blog module is active.
|
|
30
|
+
if (!hasBlog) {
|
|
21
31
|
injectRoute({ pattern: '/blog/[slug]', entrypoint: '@karaoke-cms/module-blog/pages/post' });
|
|
22
32
|
}
|
|
23
33
|
|
|
24
|
-
// Docs routes:
|
|
25
|
-
|
|
34
|
+
// Docs routes: skip when any module owns a docs collection.
|
|
35
|
+
// The karaoke() core generates per-instance pages via codegen.
|
|
36
|
+
if (!hasDocsCollection) {
|
|
26
37
|
injectRoute({ pattern: '/docs', entrypoint: `${__dirname}pages/docs/index.astro` });
|
|
27
38
|
injectRoute({ pattern: '/docs/[slug]', entrypoint: `${__dirname}pages/docs/[slug].astro` });
|
|
28
39
|
}
|
|
@@ -58,16 +69,14 @@ function buildIntegration(implementedModuleIds: string[] = []): AstroIntegration
|
|
|
58
69
|
* theme: themeDefault({ implements: [blog({ mount: '/blog' })] }),
|
|
59
70
|
* });
|
|
60
71
|
*/
|
|
61
|
-
export function themeDefault(config: { implements?: ModuleInstance[] } = {}) {
|
|
72
|
+
export function themeDefault(config: { implements?: ModuleInstance[] } = {}): ThemeInstance {
|
|
62
73
|
const implementedModules = config.implements ?? [];
|
|
63
|
-
const implementedModuleIds = implementedModules.map(m => m.id);
|
|
64
74
|
return {
|
|
65
|
-
_type: 'theme-instance'
|
|
75
|
+
_type: 'theme-instance',
|
|
66
76
|
id: 'theme-default',
|
|
67
|
-
implementedModuleIds,
|
|
68
77
|
implementedModules,
|
|
69
78
|
toAstroIntegration: (activeModules: ModuleInstance[] = []) =>
|
|
70
|
-
buildIntegration(
|
|
79
|
+
buildIntegration(implementedModules, activeModules),
|
|
71
80
|
};
|
|
72
81
|
}
|
|
73
82
|
|
|
@@ -80,5 +89,5 @@ export function themeDefault(config: { implements?: ModuleInstance[] } = {}) {
|
|
|
80
89
|
* Injects blog routes directly (legacy path — blog module not in modules[]).
|
|
81
90
|
*/
|
|
82
91
|
export default function themeDefaultLegacy(_config: unknown): AstroIntegration {
|
|
83
|
-
return buildIntegration(
|
|
92
|
+
return buildIntegration();
|
|
84
93
|
}
|
|
@@ -2,16 +2,23 @@
|
|
|
2
2
|
import { getCollection } from 'astro:content';
|
|
3
3
|
import DefaultPage from '@karaoke-cms/astro/layouts/DefaultPage.astro';
|
|
4
4
|
import { siteTitle } from 'virtual:karaoke-cms/config';
|
|
5
|
+
import { docsSections } from 'virtual:karaoke-cms/docs-sections';
|
|
5
6
|
|
|
6
7
|
export async function getStaticPaths() {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
const blogEntries = await getCollection('blog', ({ data }) => data.publish === true);
|
|
9
|
+
|
|
10
|
+
// Fetch from all active docs collections; allSettled so a missing collection
|
|
11
|
+
// doesn't abort the entire tags page (e.g., if content.config.ts wasn't updated yet).
|
|
12
|
+
const docsSettled = await Promise.allSettled(
|
|
13
|
+
docsSections.map(s => getCollection(s.collection as any, ({ data }) => data.publish === true))
|
|
14
|
+
);
|
|
15
|
+
const docsEntries = docsSettled.flatMap(r => r.status === 'fulfilled' ? r.value : []);
|
|
16
|
+
|
|
17
|
+
const allEntries = [...blogEntries, ...docsEntries];
|
|
11
18
|
|
|
12
19
|
// Collect all unique tags
|
|
13
20
|
const tags = new Set<string>();
|
|
14
|
-
for (const entry of
|
|
21
|
+
for (const entry of allEntries) {
|
|
15
22
|
for (const tag of entry.data.tags ?? []) tags.add(tag);
|
|
16
23
|
}
|
|
17
24
|
|
|
@@ -19,7 +26,7 @@ export async function getStaticPaths() {
|
|
|
19
26
|
params: { tag },
|
|
20
27
|
props: {
|
|
21
28
|
tag,
|
|
22
|
-
entries:
|
|
29
|
+
entries: allEntries
|
|
23
30
|
.filter(e => e.data.tags?.includes(tag))
|
|
24
31
|
.sort((a, b) => (b.data.date?.valueOf() ?? 0) - (a.data.date?.valueOf() ?? 0)),
|
|
25
32
|
},
|
|
@@ -28,9 +35,12 @@ export async function getStaticPaths() {
|
|
|
28
35
|
|
|
29
36
|
const { tag, entries } = Astro.props;
|
|
30
37
|
|
|
31
|
-
//
|
|
38
|
+
// Map collection name → mount path for correct href generation
|
|
39
|
+
const mountByCollection = Object.fromEntries(docsSections.map(s => [s.collection, s.mount]));
|
|
40
|
+
|
|
32
41
|
function href(entry: { collection: string; id: string }) {
|
|
33
|
-
|
|
42
|
+
const mount = mountByCollection[entry.collection];
|
|
43
|
+
return mount ? `${mount}/${entry.id}` : `/${entry.collection}/${entry.id}`;
|
|
34
44
|
}
|
|
35
45
|
---
|
|
36
46
|
|
|
@@ -2,15 +2,18 @@
|
|
|
2
2
|
import { getCollection } from 'astro:content';
|
|
3
3
|
import DefaultPage from '@karaoke-cms/astro/layouts/DefaultPage.astro';
|
|
4
4
|
import { siteTitle } from 'virtual:karaoke-cms/config';
|
|
5
|
+
import { docsSections } from 'virtual:karaoke-cms/docs-sections';
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
-
getCollection('blog', ({ data }) => data.publish === true),
|
|
8
|
-
getCollection('docs', ({ data }) => data.publish === true),
|
|
9
|
-
]);
|
|
7
|
+
const blog = await getCollection('blog', ({ data }) => data.publish === true);
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
const docsSettled = await Promise.allSettled(
|
|
10
|
+
docsSections.map(s => getCollection(s.collection as any, ({ data }) => data.publish === true))
|
|
11
|
+
);
|
|
12
|
+
const docsEntries = docsSettled.flatMap(r => r.status === 'fulfilled' ? r.value : []);
|
|
13
|
+
|
|
14
|
+
// Count occurrences of each tag across all collections
|
|
12
15
|
const counts = new Map<string, number>();
|
|
13
|
-
for (const entry of [...blog, ...
|
|
16
|
+
for (const entry of [...blog, ...docsEntries]) {
|
|
14
17
|
for (const tag of entry.data.tags ?? []) {
|
|
15
18
|
counts.set(tag, (counts.get(tag) ?? 0) + 1);
|
|
16
19
|
}
|