@stackbox/cms 0.0.5 → 0.1.1
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 +158 -10
- package/dist/AGENTS.md +218 -0
- package/dist/blocks.d.ts +6 -13
- package/dist/blocks.d.ts.map +1 -1
- package/dist/blocks.js +2 -0
- package/dist/blocks.js.map +1 -1
- package/dist/build.d.ts +14 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +145 -0
- package/dist/build.js.map +1 -0
- package/dist/cache.d.ts +14 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +93 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +16 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +12 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/link.d.ts +3 -7
- package/dist/link.d.ts.map +1 -1
- package/dist/link.js +12 -19
- package/dist/link.js.map +1 -1
- package/dist/merge-hooks.d.ts +3 -0
- package/dist/merge-hooks.d.ts.map +1 -0
- package/dist/merge-hooks.js +54 -0
- package/dist/merge-hooks.js.map +1 -0
- package/dist/pages.d.ts +15 -46
- package/dist/pages.d.ts.map +1 -1
- package/dist/pages.js +2 -0
- package/dist/pages.js.map +1 -1
- package/dist/plugin.d.ts +22 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +156 -0
- package/dist/plugin.js.map +1 -0
- package/dist/plugins/blog/index.d.ts +6 -5
- package/dist/plugins/blog/index.d.ts.map +1 -1
- package/dist/plugins/blog/index.js +16 -0
- package/dist/plugins/blog/index.js.map +1 -1
- package/dist/plugins/random-quote/assets/quotes.json +230 -0
- package/dist/plugins/random-quote/index.d.ts +3 -1
- package/dist/plugins/random-quote/index.d.ts.map +1 -1
- package/dist/plugins/random-quote/index.js +15 -1
- package/dist/plugins/random-quote/index.js.map +1 -1
- package/dist/plugins/sitemap/index.d.ts +14 -0
- package/dist/plugins/sitemap/index.d.ts.map +1 -0
- package/dist/plugins/sitemap/index.js +119 -0
- package/dist/plugins/sitemap/index.js.map +1 -0
- package/dist/render-head.d.ts +2 -2
- package/dist/render-head.d.ts.map +1 -1
- package/dist/render-page.d.ts +2 -4
- package/dist/render-page.d.ts.map +1 -1
- package/dist/render-page.js +7 -3
- package/dist/render-page.js.map +1 -1
- package/dist/site.d.ts +10 -17
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +155 -7
- package/dist/site.js.map +1 -1
- package/dist/slot-content.d.ts +3 -19
- package/dist/slot-content.d.ts.map +1 -1
- package/dist/slot-content.js.map +1 -1
- package/dist/slot-handle.d.ts +7 -25
- package/dist/slot-handle.d.ts.map +1 -1
- package/dist/slot-handle.js +29 -14
- package/dist/slot-handle.js.map +1 -1
- package/dist/stackbox/context.d.ts +2 -51
- package/dist/stackbox/context.d.ts.map +1 -1
- package/dist/stackbox/context.js.map +1 -1
- package/dist/templates.d.ts +7 -57
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js.map +1 -1
- package/dist/types.d.ts +281 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +17 -6
- package/src/plugins/blog/AGENTS.md +5 -1
- package/src/plugins/blog/index.ts +22 -5
- package/src/plugins/random-quote/AGENTS.md +30 -7
- package/src/plugins/random-quote/index.ts +17 -1
- package/src/plugins/random-quote/public_assets/widget.css +5 -0
- package/src/plugins/sitemap/AGENTS.md +82 -0
- package/src/plugins/sitemap/index.ts +156 -0
- package/AGENTS.md +0 -87
- package/dist/modules/blog/module.d.ts +0 -20
- package/dist/modules/blog/module.d.ts.map +0 -1
- package/dist/modules/blog/module.js +0 -52
- package/dist/modules/blog/module.js.map +0 -1
- package/dist/modules/blog/posts.d.ts +0 -15
- package/dist/modules/blog/posts.d.ts.map +0 -1
- package/dist/modules/blog/posts.js +0 -46
- package/dist/modules/blog/posts.js.map +0 -1
- package/dist/modules.d.ts +0 -18
- package/dist/modules.d.ts.map +0 -1
- package/dist/modules.js +0 -25
- package/dist/modules.js.map +0 -1
- package/dist/plugins/blog/module.d.ts +0 -20
- package/dist/plugins/blog/module.d.ts.map +0 -1
- package/dist/plugins/blog/module.js +0 -52
- package/dist/plugins/blog/module.js.map +0 -1
- package/dist/plugins/blog/plugin.d.ts +0 -20
- package/dist/plugins/blog/plugin.d.ts.map +0 -1
- package/dist/plugins/blog/plugin.js +0 -52
- package/dist/plugins/blog/plugin.js.map +0 -1
- package/dist/plugins/random-quote/module.d.ts +0 -9
- package/dist/plugins/random-quote/module.d.ts.map +0 -1
- package/dist/plugins/random-quote/module.js +0 -31
- package/dist/plugins/random-quote/module.js.map +0 -1
- package/dist/plugins/random-quote/plugin.d.ts +0 -8
- package/dist/plugins/random-quote/plugin.d.ts.map +0 -1
- package/dist/plugins/random-quote/plugin.js +0 -31
- package/dist/plugins/random-quote/plugin.js.map +0 -1
- package/dist/plugins/random-quote/quotes.json +0 -230
- package/dist/public/_sb/plugins/random-quote/quotes.json +0 -230
- package/src/plugins/random-quote/public_assets/quotes.json +0 -230
- /package/{dist/plugins/random-quote/public_assets → src/plugins/random-quote/assets}/quotes.json +0 -0
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import { html } from "@hyperspan/html";
|
|
2
2
|
import { createBlock } from "../../blocks.js";
|
|
3
|
-
import
|
|
3
|
+
import { createPlugin } from "../../plugin.js";
|
|
4
|
+
import bundledQuotes from "./assets/quotes.json" with { type: "json" };
|
|
5
|
+
|
|
6
|
+
export default createPlugin({
|
|
7
|
+
name: "sb-random-quote",
|
|
8
|
+
description:
|
|
9
|
+
"A block that renders a random inspirational quote, using bundled defaults or a site-supplied list.",
|
|
10
|
+
version: "1.0.0",
|
|
11
|
+
keywords: [
|
|
12
|
+
"random quote",
|
|
13
|
+
"quote of the day",
|
|
14
|
+
"inspirational quote",
|
|
15
|
+
"sidebar quote",
|
|
16
|
+
"quotation",
|
|
17
|
+
],
|
|
18
|
+
root: import.meta.dirname,
|
|
19
|
+
});
|
|
4
20
|
|
|
5
21
|
export type Quote = {
|
|
6
22
|
text: string;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Sitemap plugin — agent playbook
|
|
2
|
+
|
|
3
|
+
## Use when
|
|
4
|
+
|
|
5
|
+
The user asks for any of:
|
|
6
|
+
|
|
7
|
+
- sitemap, sitemap.xml, XML sitemap
|
|
8
|
+
- SEO crawl list, search engine indexing URLs
|
|
9
|
+
- static sitemap file in the public folder
|
|
10
|
+
|
|
11
|
+
## Do not
|
|
12
|
+
|
|
13
|
+
- Hand-roll sitemap XML — use `createSitemap()` or the default export
|
|
14
|
+
- Put `/sitemap.xml` in `createPage()` — the plugin registers that route
|
|
15
|
+
- Forget to register the plugin on `createSite({ plugins })`
|
|
16
|
+
|
|
17
|
+
## Exports
|
|
18
|
+
|
|
19
|
+
- **default export** — `createSitemap()` registration; pass to `createSite({ plugins })`
|
|
20
|
+
- `createSitemap(options?)` — factory when you need `baseUrl`, `extraUrls`, or `exclude`
|
|
21
|
+
- `renderSitemapXml(site, options?)` — shared XML renderer (fetch + build use this)
|
|
22
|
+
- `collectSitemapUrls(site, options?)` — absolute `<loc>` URLs
|
|
23
|
+
- `SitemapOptions` — type
|
|
24
|
+
|
|
25
|
+
## API
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import sitemapPlugin, { createSitemap } from "@stackbox/cms/plugins/sitemap";
|
|
29
|
+
|
|
30
|
+
createSitemap({
|
|
31
|
+
baseUrl?: string, // defaults to siteConfig.config.url
|
|
32
|
+
extraUrls?: string[], // site-relative or absolute
|
|
33
|
+
exclude?: string[] | ((path) => boolean),
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**URLs included**
|
|
38
|
+
|
|
39
|
+
- Every `createPage()` path (origin + path)
|
|
40
|
+
- `extraUrls`
|
|
41
|
+
- Pages with `meta.robots` containing `noindex` are excluded
|
|
42
|
+
- Paths in `exclude` are dropped
|
|
43
|
+
|
|
44
|
+
**Serving and static file**
|
|
45
|
+
|
|
46
|
+
- `fetch("/sitemap.xml")` when the plugin is registered
|
|
47
|
+
- `stackbox-cms build` writes `{publicDir}/sitemap.xml` via the plugin `build` hook
|
|
48
|
+
|
|
49
|
+
## Files to create or update
|
|
50
|
+
|
|
51
|
+
| File | Action |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| `server.ts` | Register the default export or `createSitemap(...)` in `createSite({ plugins })` |
|
|
54
|
+
| `site.config.ts` | Ensure `url` is set (used as sitemap origin unless `baseUrl` is passed) |
|
|
55
|
+
| `package.json` | Add `"build": "stackbox-cms build"` |
|
|
56
|
+
|
|
57
|
+
## Wiring example
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
// server.ts
|
|
61
|
+
import sitemapPlugin from "@stackbox/cms/plugins/sitemap";
|
|
62
|
+
|
|
63
|
+
export default createSite(siteConfig, {
|
|
64
|
+
pages: [homePage, aboutPage],
|
|
65
|
+
plugins: [sitemapPlugin],
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
// With extra URLs and exclusions
|
|
71
|
+
import { createSitemap } from "@stackbox/cms/plugins/sitemap";
|
|
72
|
+
|
|
73
|
+
export default createSite(siteConfig, {
|
|
74
|
+
pages: [homePage, aboutPage],
|
|
75
|
+
plugins: [
|
|
76
|
+
createSitemap({
|
|
77
|
+
extraUrls: ["/llms.txt"],
|
|
78
|
+
exclude: ["/thanks"],
|
|
79
|
+
}),
|
|
80
|
+
],
|
|
81
|
+
});
|
|
82
|
+
```
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
createPlugin,
|
|
5
|
+
} from "../../plugin.js";
|
|
6
|
+
import { normalizePathname } from "../../routing.js";
|
|
7
|
+
import type { Stackbox as SB } from "../../types.js";
|
|
8
|
+
|
|
9
|
+
export type SitemapOptions = {
|
|
10
|
+
/** Site origin. Defaults to `siteConfig.config.url`. */
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
/** Extra site-relative or absolute URLs. */
|
|
13
|
+
extraUrls?: readonly string[];
|
|
14
|
+
exclude?: readonly string[] | ((path: string) => boolean);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function resolveBaseUrl(site: SB.Site, options: SitemapOptions): string {
|
|
18
|
+
const raw = options.baseUrl ?? site.siteConfig.config.url;
|
|
19
|
+
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
20
|
+
throw new Error("sb-sitemap: baseUrl or siteConfig.url is required");
|
|
21
|
+
}
|
|
22
|
+
const url = raw.trim().replace(/\/+$/, "");
|
|
23
|
+
try {
|
|
24
|
+
new URL(url);
|
|
25
|
+
} catch {
|
|
26
|
+
throw new Error(`sb-sitemap: invalid base URL "${raw}"`);
|
|
27
|
+
}
|
|
28
|
+
return url;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isExcluded(path: string, options: SitemapOptions): boolean {
|
|
32
|
+
const normalized = normalizePathname(path);
|
|
33
|
+
if (!options.exclude) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (typeof options.exclude === "function") {
|
|
37
|
+
return options.exclude(normalized);
|
|
38
|
+
}
|
|
39
|
+
return options.exclude.some(
|
|
40
|
+
(entry) => normalizePathname(entry) === normalized,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isNoindexPage(page: { meta?: { robots?: string } }): boolean {
|
|
45
|
+
const robots = page.meta?.robots?.toLowerCase() ?? "";
|
|
46
|
+
return robots.includes("noindex");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function collectSitemapUrls(
|
|
50
|
+
site: SB.Site,
|
|
51
|
+
options: SitemapOptions = {},
|
|
52
|
+
): string[] {
|
|
53
|
+
const base = resolveBaseUrl(site, options);
|
|
54
|
+
const locs = new Set<string>();
|
|
55
|
+
|
|
56
|
+
for (const page of site.pages) {
|
|
57
|
+
if (isNoindexPage(page)) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (isExcluded(page.path, options)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
locs.add(`${base}${normalizePathname(page.path)}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const extra of options.extraUrls ?? []) {
|
|
67
|
+
if (extra.startsWith("http://") || extra.startsWith("https://")) {
|
|
68
|
+
if (!isExcluded(extra, options)) {
|
|
69
|
+
locs.add(extra);
|
|
70
|
+
}
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const path = normalizePathname(extra);
|
|
74
|
+
if (!isExcluded(path, options)) {
|
|
75
|
+
locs.add(`${base}${path}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return [...locs].sort();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function escapeXml(text: string): string {
|
|
83
|
+
return text
|
|
84
|
+
.replace(/&/g, "&")
|
|
85
|
+
.replace(/</g, "<")
|
|
86
|
+
.replace(/>/g, ">")
|
|
87
|
+
.replace(/"/g, """)
|
|
88
|
+
.replace(/'/g, "'");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function renderSitemapXml(
|
|
92
|
+
site: SB.Site,
|
|
93
|
+
options: SitemapOptions = {},
|
|
94
|
+
): string {
|
|
95
|
+
const urls = collectSitemapUrls(site, options);
|
|
96
|
+
const body = urls
|
|
97
|
+
.map((loc) => ` <url>\n <loc>${escapeXml(loc)}</loc>\n </url>`)
|
|
98
|
+
.join("\n");
|
|
99
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${body}\n</urlset>\n`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function sitemapResponse(
|
|
103
|
+
request: globalThis.Request,
|
|
104
|
+
xml: string,
|
|
105
|
+
): globalThis.Response {
|
|
106
|
+
const headers = { "content-type": "application/xml; charset=utf-8" };
|
|
107
|
+
if (request.method === "HEAD") {
|
|
108
|
+
const bytes = new TextEncoder().encode(xml);
|
|
109
|
+
return new globalThis.Response(null, {
|
|
110
|
+
status: 200,
|
|
111
|
+
headers: { ...headers, "content-length": String(bytes.byteLength) },
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return new globalThis.Response(xml, { status: 200, headers });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function createSitemapRoutes(
|
|
118
|
+
ctx: SB.PluginRouteContext,
|
|
119
|
+
options: SitemapOptions,
|
|
120
|
+
) {
|
|
121
|
+
return [
|
|
122
|
+
{
|
|
123
|
+
path: "/sitemap.xml",
|
|
124
|
+
fetch(request: globalThis.Request) {
|
|
125
|
+
const xml = renderSitemapXml(ctx.site, options);
|
|
126
|
+
return sitemapResponse(request, xml);
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
] as const;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function createSitemap(options: SitemapOptions = {}): SB.Plugin {
|
|
133
|
+
return createPlugin({
|
|
134
|
+
name: "sb-sitemap",
|
|
135
|
+
description:
|
|
136
|
+
"Generate sitemap.xml from site pages for search engines and static deploys.",
|
|
137
|
+
version: "1.0.0",
|
|
138
|
+
keywords: [
|
|
139
|
+
"sitemap",
|
|
140
|
+
"sitemap.xml",
|
|
141
|
+
"seo",
|
|
142
|
+
"search engines",
|
|
143
|
+
"xml sitemap",
|
|
144
|
+
],
|
|
145
|
+
root: import.meta.dirname,
|
|
146
|
+
routes(ctx) {
|
|
147
|
+
return createSitemapRoutes(ctx, options);
|
|
148
|
+
},
|
|
149
|
+
build(ctx) {
|
|
150
|
+
const xml = renderSitemapXml(ctx.site, options);
|
|
151
|
+
writeFileSync(join(ctx.publicDir, "sitemap.xml"), xml, "utf8");
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export default createSitemap();
|
package/AGENTS.md
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# @stackbox/cms — agent instructions
|
|
2
|
-
|
|
3
|
-
Stackbox is a code-first CMS that assembles TypeScript into a **fetch-handler-compatible server**. Use these conventions when building or extending a site.
|
|
4
|
-
|
|
5
|
-
## Site conventions
|
|
6
|
-
|
|
7
|
-
| Primitive | Factory | Purpose |
|
|
8
|
-
| --- | --- | --- |
|
|
9
|
-
| Site config | `createSiteConfig(config)` | Definition-time settings shared by templates and pages |
|
|
10
|
-
| Site | `createSite(siteConfig, { pages })` | Runtime router with `fetch(request, env)` |
|
|
11
|
-
| Template | `createTemplate({ siteConfig, slots, render })` | Reusable layout with named slots |
|
|
12
|
-
| Page | `createPage(template, { path, title, slots })` | A single URL built from a template + slot content |
|
|
13
|
-
| Block | `createBlock({ name, render })` | A content block placed into a slot at request time |
|
|
14
|
-
|
|
15
|
-
**Project layout:**
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
site.config.ts # createSiteConfig
|
|
19
|
-
server.ts # createSite(...) — default export is the fetch handler
|
|
20
|
-
templates/ # shared createTemplate() layouts
|
|
21
|
-
pages/ # createPage() per route or feature
|
|
22
|
-
content/ # markdown or other bundle-time content (per feature)
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
The default export from `server.ts` implements `fetch(request, env)` and returns a `Response`. It works on Cloudflare Workers, Bun, Deno, and any runtime that speaks the fetch-handler pattern.
|
|
26
|
-
|
|
27
|
-
## Plugins vs blocks
|
|
28
|
-
|
|
29
|
-
- **Plugin** — a packaged use-case under `@stackbox/cms/plugins/<name>` (e.g. blog). May export factories, content objects, blocks, types, and helpers.
|
|
30
|
-
- **Block** — a core primitive via `createBlock()`. Renders HTML into a template slot. Usable inside or outside plugins.
|
|
31
|
-
|
|
32
|
-
Slot content is `Block | string`. Plugin content arrays can interleave blocks and HTML strings.
|
|
33
|
-
|
|
34
|
-
## Plugin catalog
|
|
35
|
-
|
|
36
|
-
Before implementing a use-case (blog, newsletter, docs, …), check this table. If the user's request matches a row, **read only that plugin's AGENTS.md** and follow it. Do not invent a parallel implementation.
|
|
37
|
-
|
|
38
|
-
| Plugin | Import | Read when the user mentions | Instructions |
|
|
39
|
-
| --- | --- | --- | --- |
|
|
40
|
-
| blog | `@stackbox/cms/plugins/blog` | blog, posts, articles, journal, markdown posts, blog listing, blog page | `src/plugins/blog/AGENTS.md` |
|
|
41
|
-
| random-quote | `@stackbox/cms/plugins/random-quote` | random quote, quote of the day, inspirational quote, sidebar quote | `src/plugins/random-quote/AGENTS.md` |
|
|
42
|
-
|
|
43
|
-
When adding a new plugin to this package, add a row here and create `src/plugins/<name>/AGENTS.md` using the same heading structure as the blog plugin. Document all export kinds (factory, types, blocks, helpers).
|
|
44
|
-
|
|
45
|
-
## Plugin public assets
|
|
46
|
-
|
|
47
|
-
Runtime files that must be **publicly available** (images, CSS, fonts, JSON served over HTTP) go in:
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
src/plugins/<name>/public_assets/
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
**Opt-in:** only plugins with a `public_assets/` folder get copied at build time. Do not edit the copy script when adding files.
|
|
54
|
-
|
|
55
|
-
Build copies each `public_assets/` folder to:
|
|
56
|
-
|
|
57
|
-
- `dist/public/_sb/plugins/<name>/` — served at `/_sb/plugins/<name>/...`
|
|
58
|
-
- `dist/plugins/<name>/public_assets/` — so `import "./public_assets/..." with { type: "json" }` works in compiled JS
|
|
59
|
-
|
|
60
|
-
Use `pluginAssetPath()` from `@stackbox/cms/link` for `<img src>`, `<link href>`, etc. Do not put site files under `/_sb/` — that prefix is reserved for Stackbox plugin assets.
|
|
61
|
-
|
|
62
|
-
```ts
|
|
63
|
-
import { pluginAssetPath } from "@stackbox/cms/link";
|
|
64
|
-
|
|
65
|
-
pluginAssetPath("random-quote", "photo.jpg");
|
|
66
|
-
// → "/_sb/plugins/random-quote/photo.jpg"
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
For data bundled into JS at build time, import from `./public_assets/...` in the plugin's `index.ts`.
|
|
70
|
-
|
|
71
|
-
## For site agents
|
|
72
|
-
|
|
73
|
-
If you are working in a **consumer site repo** (not this package), read this file from the installed package:
|
|
74
|
-
|
|
75
|
-
```
|
|
76
|
-
node_modules/@stackbox/cms/AGENTS.md
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Recommended snippet for the site's own `AGENTS.md`:
|
|
80
|
-
|
|
81
|
-
```md
|
|
82
|
-
This site uses @stackbox/cms. Before adding features, read
|
|
83
|
-
`node_modules/@stackbox/cms/AGENTS.md` and follow its plugin catalog.
|
|
84
|
-
Do not reimplement bundled plugins.
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
When a request matches a catalog row, open **only** that plugin's instructions file (e.g. `node_modules/@stackbox/cms/src/plugins/blog/AGENTS.md`).
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { PageMeta } from "../../pages.js";
|
|
2
|
-
import type { DefaultSlotContent } from "../../slot-content.js";
|
|
3
|
-
import { type BlogOptions } from "./posts.js";
|
|
4
|
-
export type { BlogPost, BlogOptions } from "./posts.js";
|
|
5
|
-
export type BlogPostContent = {
|
|
6
|
-
path: string;
|
|
7
|
-
title: string;
|
|
8
|
-
meta?: PageMeta;
|
|
9
|
-
content: DefaultSlotContent[];
|
|
10
|
-
};
|
|
11
|
-
export type BlogListingContent = {
|
|
12
|
-
path: string;
|
|
13
|
-
content: DefaultSlotContent[];
|
|
14
|
-
};
|
|
15
|
-
export type Blog = {
|
|
16
|
-
readonly posts: readonly BlogPostContent[];
|
|
17
|
-
readonly listings: readonly BlogListingContent[];
|
|
18
|
-
};
|
|
19
|
-
export declare function createBlog(options: BlogOptions): Blog;
|
|
20
|
-
//# sourceMappingURL=module.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../src/modules/blog/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAa,KAAK,WAAW,EAAiB,MAAM,YAAY,CAAC;AAExE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,IAAI,GAAG;IACjB,QAAQ,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAC;CAClD,CAAC;AAwDF,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAQrD"}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { loadPosts } from "./posts.js";
|
|
2
|
-
function postToContent(post) {
|
|
3
|
-
return {
|
|
4
|
-
path: post.path,
|
|
5
|
-
title: post.title,
|
|
6
|
-
meta: post.description ? { description: post.description } : undefined,
|
|
7
|
-
content: [
|
|
8
|
-
`<article data-content-type="article" data-slug="${post.slug}">
|
|
9
|
-
<h1>${post.title}</h1>
|
|
10
|
-
${post.bodyHtml}
|
|
11
|
-
</article>`,
|
|
12
|
-
],
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
function listingPath(pathPrefix, page) {
|
|
16
|
-
return page === 1 ? pathPrefix : `${pathPrefix}/page/${page}`;
|
|
17
|
-
}
|
|
18
|
-
function buildPostListHtml(posts) {
|
|
19
|
-
return `<ul>${posts
|
|
20
|
-
.map((post) => `<li><a href="${post.path}">${post.title}</a></li>`)
|
|
21
|
-
.join("")}</ul>`;
|
|
22
|
-
}
|
|
23
|
-
function buildListingPages(posts, pathPrefix, postsPerPage) {
|
|
24
|
-
if (posts.length === 0) {
|
|
25
|
-
return [
|
|
26
|
-
{
|
|
27
|
-
path: pathPrefix,
|
|
28
|
-
content: ["<p>No blog posts found.</p>"],
|
|
29
|
-
},
|
|
30
|
-
];
|
|
31
|
-
}
|
|
32
|
-
const perPage = postsPerPage && postsPerPage > 0 ? postsPerPage : posts.length;
|
|
33
|
-
const pages = [];
|
|
34
|
-
for (let i = 0; i < posts.length; i += perPage) {
|
|
35
|
-
const chunk = posts.slice(i, i + perPage);
|
|
36
|
-
const page = i / perPage + 1;
|
|
37
|
-
pages.push({
|
|
38
|
-
path: listingPath(pathPrefix, page),
|
|
39
|
-
content: [buildPostListHtml(chunk)],
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
return pages;
|
|
43
|
-
}
|
|
44
|
-
export function createBlog(options) {
|
|
45
|
-
const loaded = loadPosts(options);
|
|
46
|
-
const pathPrefix = options.pathPrefix.replace(/\/+$/, "") || "/";
|
|
47
|
-
return {
|
|
48
|
-
posts: loaded.map(postToContent),
|
|
49
|
-
listings: buildListingPages(loaded, pathPrefix, options.postsPerPage),
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
//# sourceMappingURL=module.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"module.js","sourceRoot":"","sources":["../../../src/modules/blog/module.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAmC,MAAM,YAAY,CAAC;AAqBxE,SAAS,aAAa,CAAC,IAAc;IACnC,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS;QACtE,OAAO,EAAE;YACP,mDAAmD,IAAI,CAAC,IAAI;QAC1D,IAAI,CAAC,KAAK;IACd,IAAI,CAAC,QAAQ;WACN;SACN;KACF,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,UAAkB,EAAE,IAAY;IACnD,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,SAAS,IAAI,EAAE,CAAC;AAChE,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAiB;IAC1C,OAAO,OAAO,KAAK;SAChB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,WAAW,CAAC;SAClE,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;AACrB,CAAC;AAED,SAAS,iBAAiB,CACxB,KAAiB,EACjB,UAAkB,EAClB,YAAqB;IAErB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;YACL;gBACE,IAAI,EAAE,UAAU;gBAChB,OAAO,EAAE,CAAC,6BAA6B,CAAC;aACzC;SACF,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GACX,YAAY,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;IACjE,MAAM,KAAK,GAAyB,EAAE,CAAC;IAEvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,OAAO,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,WAAW,CAAC,UAAU,EAAE,IAAI,CAAC;YACnC,OAAO,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;SACpC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAoB;IAC7C,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;IAClC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;IAEjE,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC;QAChC,QAAQ,EAAE,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,YAAY,CAAC;KACtE,CAAC;AACJ,CAAC"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export type BlogPost = {
|
|
2
|
-
slug: string;
|
|
3
|
-
title: string;
|
|
4
|
-
description?: string;
|
|
5
|
-
path: string;
|
|
6
|
-
bodyHtml: string;
|
|
7
|
-
};
|
|
8
|
-
export type BlogOptions = {
|
|
9
|
-
contentPath: string;
|
|
10
|
-
pathPrefix: string;
|
|
11
|
-
/** Max posts per listing page. Omit to put all posts on one page. */
|
|
12
|
-
postsPerPage?: number;
|
|
13
|
-
};
|
|
14
|
-
export declare function loadPosts(options: BlogOptions): BlogPost[];
|
|
15
|
-
//# sourceMappingURL=posts.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"posts.d.ts","sourceRoot":"","sources":["../../../src/modules/blog/posts.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AA8BF,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,QAAQ,EAAE,CAqB1D"}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { readFileSync, readdirSync } from "node:fs";
|
|
2
|
-
import { basename, extname, join, resolve } from "node:path";
|
|
3
|
-
import { marked } from "marked";
|
|
4
|
-
function parseFrontmatter(raw) {
|
|
5
|
-
if (!raw.startsWith("---")) {
|
|
6
|
-
return { meta: {}, body: raw };
|
|
7
|
-
}
|
|
8
|
-
const end = raw.indexOf("\n---", 3);
|
|
9
|
-
if (end === -1) {
|
|
10
|
-
return { meta: {}, body: raw };
|
|
11
|
-
}
|
|
12
|
-
const frontmatter = raw.slice(3, end).trim();
|
|
13
|
-
const body = raw.slice(end + 4).replace(/^\n/, "");
|
|
14
|
-
const meta = {};
|
|
15
|
-
for (const line of frontmatter.split("\n")) {
|
|
16
|
-
const colon = line.indexOf(":");
|
|
17
|
-
if (colon === -1)
|
|
18
|
-
continue;
|
|
19
|
-
const key = line.slice(0, colon).trim();
|
|
20
|
-
const value = line.slice(colon + 1).trim().replace(/^["']|["']$/g, "");
|
|
21
|
-
if (key)
|
|
22
|
-
meta[key] = value;
|
|
23
|
-
}
|
|
24
|
-
return { meta, body };
|
|
25
|
-
}
|
|
26
|
-
export function loadPosts(options) {
|
|
27
|
-
const absoluteDir = resolve(options.contentPath);
|
|
28
|
-
const files = readdirSync(absoluteDir)
|
|
29
|
-
.filter((name) => extname(name) === ".md")
|
|
30
|
-
.sort();
|
|
31
|
-
return files.map((filename) => {
|
|
32
|
-
const raw = readFileSync(join(absoluteDir, filename), "utf8");
|
|
33
|
-
const { meta, body } = parseFrontmatter(raw);
|
|
34
|
-
const slug = meta.slug ?? basename(filename, ".md");
|
|
35
|
-
const title = meta.title ?? slug;
|
|
36
|
-
const prefix = options.pathPrefix.replace(/\/+$/, "");
|
|
37
|
-
return {
|
|
38
|
-
slug,
|
|
39
|
-
title,
|
|
40
|
-
description: meta.description,
|
|
41
|
-
path: `${prefix}/${slug}`,
|
|
42
|
-
bodyHtml: marked.parse(body),
|
|
43
|
-
};
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
//# sourceMappingURL=posts.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"posts.js","sourceRoot":"","sources":["../../../src/modules/blog/posts.ts"],"names":[],"mappings":"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"}
|
package/dist/modules.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { RenderContext } from "./pages.js";
|
|
2
|
-
import type { HSHtml } from "@hyperspan/html";
|
|
3
|
-
export type ModuleRenderResult = HSHtml | Promise<HSHtml>;
|
|
4
|
-
export type Module = {
|
|
5
|
-
readonly __kind: "module";
|
|
6
|
-
readonly name: string;
|
|
7
|
-
render(ctx: RenderContext): ModuleRenderResult;
|
|
8
|
-
};
|
|
9
|
-
type ModuleDef<TOptions = undefined> = {
|
|
10
|
-
name: string;
|
|
11
|
-
render(options: TOptions, ctx?: RenderContext): ModuleRenderResult;
|
|
12
|
-
};
|
|
13
|
-
export type ModuleOptionsOf<F> = F extends (options?: infer O) => unknown ? [O] extends [undefined] ? undefined : O : never;
|
|
14
|
-
export type ModuleFactory<TOptions = undefined> = (options?: TOptions) => Module;
|
|
15
|
-
export declare function createModule<TOptions = undefined>(def: ModuleDef<TOptions>): ModuleFactory<TOptions>;
|
|
16
|
-
export declare function isModule(value: unknown): value is Module;
|
|
17
|
-
export {};
|
|
18
|
-
//# sourceMappingURL=modules.d.ts.map
|
package/dist/modules.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"modules.d.ts","sourceRoot":"","sources":["../src/modules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAE1D,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,GAAG,EAAE,aAAa,GAAG,kBAAkB,CAAC;CAChD,CAAC;AAEF,KAAK,SAAS,CAAC,QAAQ,GAAG,SAAS,IAAI;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,aAAa,GAAG,kBAAkB,CAAC;CACpE,CAAC;AAEF,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,KAAK,OAAO,GACrE,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,GACrB,SAAS,GACT,CAAC,GACH,KAAK,CAAC;AAEV,MAAM,MAAM,aAAa,CAAC,QAAQ,GAAG,SAAS,IAAI,CAChD,OAAO,CAAC,EAAE,QAAQ,KACf,MAAM,CAAC;AAQZ,wBAAgB,YAAY,CAAC,QAAQ,GAAG,SAAS,EAC/C,GAAG,EAAE,SAAS,CAAC,QAAQ,CAAC,GACvB,aAAa,CAAC,QAAQ,CAAC,CAYzB;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CASxD"}
|
package/dist/modules.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
function validateModuleName(name) {
|
|
2
|
-
if (!name || name.length === 0) {
|
|
3
|
-
throw new Error("createModule(def): name is required");
|
|
4
|
-
}
|
|
5
|
-
}
|
|
6
|
-
export function createModule(def) {
|
|
7
|
-
validateModuleName(def.name);
|
|
8
|
-
if (typeof def.render !== "function") {
|
|
9
|
-
throw new Error("createModule(def): render is required");
|
|
10
|
-
}
|
|
11
|
-
return ((options) => ({
|
|
12
|
-
__kind: "module",
|
|
13
|
-
name: def.name,
|
|
14
|
-
render: (ctx) => def.render(options, ctx),
|
|
15
|
-
}));
|
|
16
|
-
}
|
|
17
|
-
export function isModule(value) {
|
|
18
|
-
return (typeof value === "object" &&
|
|
19
|
-
value !== null &&
|
|
20
|
-
value.__kind === "module" &&
|
|
21
|
-
typeof value.name === "string" &&
|
|
22
|
-
value.name.length > 0 &&
|
|
23
|
-
typeof value.render === "function");
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=modules.js.map
|
package/dist/modules.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"modules.js","sourceRoot":"","sources":["../src/modules.ts"],"names":[],"mappings":"AA0BA,SAAS,kBAAkB,CAAC,IAAY;IACtC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,GAAwB;IAExB,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAE7B,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,CAAC,CAAC,OAAkB,EAAE,EAAE,CAAC,CAAC;QAC/B,MAAM,EAAE,QAAiB;QACzB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,MAAM,EAAE,CAAC,GAAkB,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,OAAmB,EAAE,GAAG,CAAC;KACrE,CAAC,CAA4B,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAAgB,CAAC,MAAM,KAAK,QAAQ;QACrC,OAAQ,KAAgB,CAAC,IAAI,KAAK,QAAQ;QACzC,KAAgB,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;QACjC,OAAQ,KAAgB,CAAC,MAAM,KAAK,UAAU,CAC/C,CAAC;AACJ,CAAC"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { PageMeta } from "../../pages.js";
|
|
2
|
-
import type { DefaultSlotContent } from "../../slot-content.js";
|
|
3
|
-
import { type BlogOptions } from "./posts.js";
|
|
4
|
-
export type { BlogPost, BlogOptions } from "./posts.js";
|
|
5
|
-
export type BlogPostContent = {
|
|
6
|
-
path: string;
|
|
7
|
-
title: string;
|
|
8
|
-
meta?: PageMeta;
|
|
9
|
-
content: DefaultSlotContent[];
|
|
10
|
-
};
|
|
11
|
-
export type BlogListingContent = {
|
|
12
|
-
path: string;
|
|
13
|
-
content: DefaultSlotContent[];
|
|
14
|
-
};
|
|
15
|
-
export type Blog = {
|
|
16
|
-
readonly posts: readonly BlogPostContent[];
|
|
17
|
-
readonly listings: readonly BlogListingContent[];
|
|
18
|
-
};
|
|
19
|
-
export declare function createBlog(options: BlogOptions): Blog;
|
|
20
|
-
//# sourceMappingURL=module.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../src/plugins/blog/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAa,KAAK,WAAW,EAAiB,MAAM,YAAY,CAAC;AAExE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,IAAI,GAAG;IACjB,QAAQ,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAC;CAClD,CAAC;AAwDF,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAQrD"}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { loadPosts } from "./posts.js";
|
|
2
|
-
function postToContent(post) {
|
|
3
|
-
return {
|
|
4
|
-
path: post.path,
|
|
5
|
-
title: post.title,
|
|
6
|
-
meta: post.description ? { description: post.description } : undefined,
|
|
7
|
-
content: [
|
|
8
|
-
`<article data-content-type="article" data-slug="${post.slug}">
|
|
9
|
-
<h1>${post.title}</h1>
|
|
10
|
-
${post.bodyHtml}
|
|
11
|
-
</article>`,
|
|
12
|
-
],
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
function listingPath(pathPrefix, page) {
|
|
16
|
-
return page === 1 ? pathPrefix : `${pathPrefix}/page/${page}`;
|
|
17
|
-
}
|
|
18
|
-
function buildPostListHtml(posts) {
|
|
19
|
-
return `<ul>${posts
|
|
20
|
-
.map((post) => `<li><a href="${post.path}">${post.title}</a></li>`)
|
|
21
|
-
.join("")}</ul>`;
|
|
22
|
-
}
|
|
23
|
-
function buildListingPages(posts, pathPrefix, postsPerPage) {
|
|
24
|
-
if (posts.length === 0) {
|
|
25
|
-
return [
|
|
26
|
-
{
|
|
27
|
-
path: pathPrefix,
|
|
28
|
-
content: ["<p>No blog posts found.</p>"],
|
|
29
|
-
},
|
|
30
|
-
];
|
|
31
|
-
}
|
|
32
|
-
const perPage = postsPerPage && postsPerPage > 0 ? postsPerPage : posts.length;
|
|
33
|
-
const pages = [];
|
|
34
|
-
for (let i = 0; i < posts.length; i += perPage) {
|
|
35
|
-
const chunk = posts.slice(i, i + perPage);
|
|
36
|
-
const page = i / perPage + 1;
|
|
37
|
-
pages.push({
|
|
38
|
-
path: listingPath(pathPrefix, page),
|
|
39
|
-
content: [buildPostListHtml(chunk)],
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
return pages;
|
|
43
|
-
}
|
|
44
|
-
export function createBlog(options) {
|
|
45
|
-
const loaded = loadPosts(options);
|
|
46
|
-
const pathPrefix = options.pathPrefix.replace(/\/+$/, "") || "/";
|
|
47
|
-
return {
|
|
48
|
-
posts: loaded.map(postToContent),
|
|
49
|
-
listings: buildListingPages(loaded, pathPrefix, options.postsPerPage),
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
//# sourceMappingURL=module.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"module.js","sourceRoot":"","sources":["../../../src/plugins/blog/module.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAmC,MAAM,YAAY,CAAC;AAqBxE,SAAS,aAAa,CAAC,IAAc;IACnC,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS;QACtE,OAAO,EAAE;YACP,mDAAmD,IAAI,CAAC,IAAI;QAC1D,IAAI,CAAC,KAAK;IACd,IAAI,CAAC,QAAQ;WACN;SACN;KACF,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,UAAkB,EAAE,IAAY;IACnD,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,SAAS,IAAI,EAAE,CAAC;AAChE,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAiB;IAC1C,OAAO,OAAO,KAAK;SAChB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,WAAW,CAAC;SAClE,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;AACrB,CAAC;AAED,SAAS,iBAAiB,CACxB,KAAiB,EACjB,UAAkB,EAClB,YAAqB;IAErB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;YACL;gBACE,IAAI,EAAE,UAAU;gBAChB,OAAO,EAAE,CAAC,6BAA6B,CAAC;aACzC;SACF,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GACX,YAAY,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;IACjE,MAAM,KAAK,GAAyB,EAAE,CAAC;IAEvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,OAAO,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,WAAW,CAAC,UAAU,EAAE,IAAI,CAAC;YACnC,OAAO,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;SACpC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAoB;IAC7C,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;IAClC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;IAEjE,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC;QAChC,QAAQ,EAAE,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,YAAY,CAAC;KACtE,CAAC;AACJ,CAAC"}
|