cosmolo 0.3.0 → 0.3.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/LICENSE +21 -0
- package/README.md +583 -0
- package/package.json +8 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alcogy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
# Cosmolo
|
|
2
|
+
|
|
3
|
+
A SvelteKit-native content management package — add Markdown-based blogging to any SvelteKit project in minutes.
|
|
4
|
+
|
|
5
|
+
**Website:** https://cosmolo.alcogy.dev
|
|
6
|
+
|
|
7
|
+
> **Name origin**: Short for *cosmologist* — a deliberate nod to Astro. Cosmolo occupies
|
|
8
|
+
> a similar content-site niche but stays entirely within the SvelteKit ecosystem.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Why Cosmolo
|
|
13
|
+
|
|
14
|
+
Astro is a great product — but for SvelteKit developers it's a heavy choice.
|
|
15
|
+
Switching frameworks means leaving behind the Svelte component model, the SvelteKit
|
|
16
|
+
router, and all the ecosystem knowledge you've built up. Cosmolo gives SvelteKit
|
|
17
|
+
a canonical "just add Markdown and go" story without asking you to leave.
|
|
18
|
+
|
|
19
|
+
| | Cosmolo | Astro | Nuxt Content | SvelteKit (vanilla) |
|
|
20
|
+
|---|---|---|---|---|
|
|
21
|
+
| Framework | SvelteKit | Astro | Nuxt (Vue) | SvelteKit |
|
|
22
|
+
| Markdown | MDSveX + marked | Built-in | Built-in | Manual |
|
|
23
|
+
| Type-safe frontmatter | Zod | TS inference | Zod (optional) | Manual |
|
|
24
|
+
| Component in Markdown | Yes (.svx) | Yes (.mdx) | Yes | No |
|
|
25
|
+
| Config-driven categories | Yes | No | No | No |
|
|
26
|
+
| Headless CMS (JSON API) | Yes | Manual | Manual | Manual |
|
|
27
|
+
| Learning curve | SvelteKit only | Astro concepts | Vue + Nuxt | SvelteKit only |
|
|
28
|
+
|
|
29
|
+
**Core principles:**
|
|
30
|
+
|
|
31
|
+
1. **SvelteKit all the way down** — No framework switching. Developers who know SvelteKit already know Cosmolo.
|
|
32
|
+
2. **Config over convention** — Site identity and taxonomy are JSON files. No source code changes needed to add a category.
|
|
33
|
+
3. **Type-safe content** — Frontmatter is validated with Zod at build time. Malformed articles fail loudly during `bun build`.
|
|
34
|
+
4. **MDSveX as a first-class citizen** — `.md` and `.svx` share the same routing and Zod schema; the system auto-detects which to use.
|
|
35
|
+
5. **Headless-ready** — Cosmolo generates static JSON endpoints alongside your HTML pages, so your content can be consumed by external apps or frontends without any server.
|
|
36
|
+
6. **Non-invasive** — Cosmolo is an npm package, not a framework. It adds content management to your existing project without owning your routes or components.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# In an existing SvelteKit project:
|
|
44
|
+
bun add cosmolo
|
|
45
|
+
|
|
46
|
+
# Scaffold routes and config files interactively
|
|
47
|
+
bunx cosmolo init
|
|
48
|
+
|
|
49
|
+
# Start writing content
|
|
50
|
+
bun generate:article
|
|
51
|
+
|
|
52
|
+
# Run the dev server
|
|
53
|
+
bun dev
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`cosmolo init` asks two questions — which mode (full UI or server-only) and which adapter (SSG or serverless) — then copies the appropriate route files into your project.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Configuration
|
|
61
|
+
|
|
62
|
+
### `config/site.json`
|
|
63
|
+
|
|
64
|
+
Site-wide settings. Created by `cosmolo init`.
|
|
65
|
+
|
|
66
|
+
| Field | Description |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `url` | Production URL (used in sitemap and OGP) |
|
|
69
|
+
| `name` | Site name shown in header and `<title>` |
|
|
70
|
+
| `description` | Default meta description |
|
|
71
|
+
| `twitterHandle` | Twitter/X handle for `twitter:site` meta tag |
|
|
72
|
+
| `fallbackCategoryLabel` | Label shown for the `other` fallback category |
|
|
73
|
+
| `articlesPerPage` | Articles per page for pagination |
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"url": "https://your-site.example.com",
|
|
78
|
+
"name": "Your Site Name",
|
|
79
|
+
"description": "A content site built with Cosmolo.",
|
|
80
|
+
"twitterHandle": "@yourhandle",
|
|
81
|
+
"fallbackCategoryLabel": "Other",
|
|
82
|
+
"articlesPerPage": 10
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### `config/categories.json`
|
|
87
|
+
|
|
88
|
+
Define your content taxonomy. Each key becomes a URL slug at `/categories/<key>`.
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"tech": {
|
|
93
|
+
"label": "Technology",
|
|
94
|
+
"description": "Articles about software, tools, and the web."
|
|
95
|
+
},
|
|
96
|
+
"design": {
|
|
97
|
+
"label": "Design",
|
|
98
|
+
"description": "Articles about UI/UX and visual design."
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Articles with an unrecognized category fall back to `/categories/other` automatically.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Content
|
|
108
|
+
|
|
109
|
+
### Article Frontmatter
|
|
110
|
+
|
|
111
|
+
Every article needs these fields at the top of the file:
|
|
112
|
+
|
|
113
|
+
```yaml
|
|
114
|
+
---
|
|
115
|
+
title: "My Article Title"
|
|
116
|
+
category: "tech" # must match a key in config/categories.json
|
|
117
|
+
excerpt: "One sentence shown in article listings."
|
|
118
|
+
sort: 100 # higher number = appears earlier in listings
|
|
119
|
+
date: "2025-01-15" # ISO date string (optional)
|
|
120
|
+
tags: ["svelte", "tutorial"] # optional — tag listing pages at /tags/<tag>
|
|
121
|
+
series: "getting-started" # optional — groups articles into a series
|
|
122
|
+
seriesOrder: 1 # optional — position within the series (1-based)
|
|
123
|
+
draft: true # optional — exclude from build output and listings
|
|
124
|
+
related: ["slug-a", "slug-b"] # optional — override auto-detected related articles
|
|
125
|
+
---
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`sort` gives you manual ordering without relying on file timestamps or alphabetical order.
|
|
129
|
+
Articles with an unknown `category` value are grouped under `/categories/other`.
|
|
130
|
+
|
|
131
|
+
### Updated date
|
|
132
|
+
|
|
133
|
+
Each article page automatically shows an "Updated:" date derived from the file's
|
|
134
|
+
**last git commit timestamp**. No frontmatter change is needed — the date is resolved
|
|
135
|
+
at build time by running `git log -1` against the article file.
|
|
136
|
+
|
|
137
|
+
The updated date is displayed only when it differs from the `date` field. If the file
|
|
138
|
+
has never been committed, the updated date is omitted silently.
|
|
139
|
+
|
|
140
|
+
> **Note:** file modification times (`mtime`) are intentionally not used. They reset on
|
|
141
|
+
> `git clone`, which makes them unreliable in CI/CD environments.
|
|
142
|
+
|
|
143
|
+
### Draft mode
|
|
144
|
+
|
|
145
|
+
Add `draft: true` to any article's frontmatter to exclude it from build output and all listings.
|
|
146
|
+
Draft articles are invisible in production but accessible during `bun dev` via their direct URL.
|
|
147
|
+
|
|
148
|
+
### Tags
|
|
149
|
+
|
|
150
|
+
Articles can have multiple tags. Each tag gets a listing page at `/tags/<tag>`.
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
tags: ["svelte", "tutorial"]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Tags are case-sensitive. Unused tags produce no page.
|
|
157
|
+
|
|
158
|
+
### Series
|
|
159
|
+
|
|
160
|
+
Group related articles into an ordered sequence. Prev/next navigation is shown inside each article.
|
|
161
|
+
|
|
162
|
+
```yaml
|
|
163
|
+
series: "getting-started"
|
|
164
|
+
seriesOrder: 1
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
All articles sharing the same `series` value are linked together, sorted by `seriesOrder` ascending.
|
|
168
|
+
|
|
169
|
+
### Manual related articles
|
|
170
|
+
|
|
171
|
+
By default, the "More in this category" panel shows up to 4 articles from the same category.
|
|
172
|
+
Override it by listing slugs explicitly:
|
|
173
|
+
|
|
174
|
+
```yaml
|
|
175
|
+
related: ["slug-a", "slug-b"]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Table of contents
|
|
179
|
+
|
|
180
|
+
For `.md` articles with 2 or more `##` headings, a table of contents is automatically rendered
|
|
181
|
+
above the article body. Heading levels `##` through `######` are included. `.svx` articles do
|
|
182
|
+
not get an auto-generated TOC.
|
|
183
|
+
|
|
184
|
+
### Supported file formats
|
|
185
|
+
|
|
186
|
+
| Extension | Renderer | Svelte components in body |
|
|
187
|
+
|-----------|----------|---------------------------|
|
|
188
|
+
| `.md` | `marked` | No |
|
|
189
|
+
| `.svx` | MDSveX | Yes |
|
|
190
|
+
|
|
191
|
+
Place files in `src/content/articles/` (or your configured `articlesDir`).
|
|
192
|
+
The filename becomes the URL slug:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
src/content/articles/my-post.md → /articles/my-post
|
|
196
|
+
src/content/articles/demo.svx → /articles/demo
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Markdown extensions
|
|
200
|
+
|
|
201
|
+
**YouTube embed** — renders a responsive 16:9 iframe:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
::youtube[dQw4w9WgXcQ]
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**External links** — all `http://` and `https://` links automatically get
|
|
208
|
+
`target="_blank" rel="noopener noreferrer"`.
|
|
209
|
+
|
|
210
|
+
### Svelte components in `.svx`
|
|
211
|
+
|
|
212
|
+
`.svx` files are Markdown with embedded Svelte components. Use them when articles
|
|
213
|
+
need interactive UI.
|
|
214
|
+
|
|
215
|
+
```svx
|
|
216
|
+
<script>
|
|
217
|
+
import Callout from '$lib/components/Callout.svelte';
|
|
218
|
+
</script>
|
|
219
|
+
|
|
220
|
+
<Callout type="warning">Watch out for this edge case.</Callout>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Static Pages
|
|
224
|
+
|
|
225
|
+
Place `.md` files in `src/content/pages/`. Each file is served at `/<filename>`:
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
src/content/pages/about.md → /about
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Frontmatter:
|
|
232
|
+
|
|
233
|
+
```yaml
|
|
234
|
+
---
|
|
235
|
+
title: "About"
|
|
236
|
+
---
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Generators
|
|
242
|
+
|
|
243
|
+
Create content files without editing them by hand:
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
bunx cosmolo generate # Interactive menu (article / page / category)
|
|
247
|
+
bunx cosmolo generate article # Create an article
|
|
248
|
+
bunx cosmolo generate page # Create a static page
|
|
249
|
+
bunx cosmolo generate category # Add a category to categories.json
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
`cosmolo init` adds convenience scripts to your `package.json` automatically, so after init you can just run:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
bun generate:article
|
|
256
|
+
bun generate:page
|
|
257
|
+
bun generate:category
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Article
|
|
261
|
+
|
|
262
|
+
Prompts for title, slug, category, excerpt, tags, sort, date, draft status, and series.
|
|
263
|
+
Creates `src/content/articles/<slug>.md` with pre-filled frontmatter.
|
|
264
|
+
|
|
265
|
+
### Page
|
|
266
|
+
|
|
267
|
+
Prompts for title and slug. Creates `src/content/pages/<slug>.md`.
|
|
268
|
+
|
|
269
|
+
### Category
|
|
270
|
+
|
|
271
|
+
Prompts for key (slug), label, and description. Appends the new entry to `config/categories.json`.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Headless CMS
|
|
276
|
+
|
|
277
|
+
Cosmolo can expose your content as static JSON endpoints, making it usable as a
|
|
278
|
+
headless CMS alongside — or independently of — your rendered pages.
|
|
279
|
+
|
|
280
|
+
All endpoints are **static files** generated at build time. No server or database is required.
|
|
281
|
+
|
|
282
|
+
| Endpoint | Description |
|
|
283
|
+
|---|---|
|
|
284
|
+
| `/api/articles.json` | Slug + title for all non-draft articles |
|
|
285
|
+
| `/api/articles/<slug>.json` | Full metadata and body for a single article |
|
|
286
|
+
| `/api/categories.json` | All categories with slug, label, and description |
|
|
287
|
+
| `/rss.xml` | RSS 2.0 feed |
|
|
288
|
+
| `/sitemap.xml` | XML sitemap including all article, category, and tag URLs |
|
|
289
|
+
|
|
290
|
+
`cosmolo init` scaffolds `rss.xml` and `sitemap.xml` automatically. The JSON API routes
|
|
291
|
+
(`api/articles.json`, `api/articles/[slug].json`, `api/categories.json`) can be added
|
|
292
|
+
manually or customized to return exactly the fields your consumers need.
|
|
293
|
+
|
|
294
|
+
### Article body format
|
|
295
|
+
|
|
296
|
+
The per-article endpoint supports three body formats, configurable in `config/site.json`:
|
|
297
|
+
|
|
298
|
+
```json
|
|
299
|
+
"api": { "articleBody": "html" }
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
| Value | `contentsFormat` | `contents` |
|
|
303
|
+
|---|---|---|
|
|
304
|
+
| `"html"` | `"html"` | Rendered HTML (default) |
|
|
305
|
+
| `"markdown"` | `"markdown"` | Raw Markdown (frontmatter stripped) |
|
|
306
|
+
| `"plaintext"` | `"plaintext"` | Plain text — Markdown syntax removed |
|
|
307
|
+
|
|
308
|
+
> **Note:** All API endpoints are publicly accessible static files. Do not include
|
|
309
|
+
> sensitive or private information in article frontmatter or content.
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Package Reference
|
|
314
|
+
|
|
315
|
+
### Install
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
bun add cosmolo
|
|
319
|
+
# peer deps (if not already installed)
|
|
320
|
+
bun add -D vite @sveltejs/kit
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Setup
|
|
324
|
+
|
|
325
|
+
**1. Create `cosmolo.config.ts`** in your project root
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
import { resolveConfig } from 'cosmolo';
|
|
329
|
+
|
|
330
|
+
export default resolveConfig({
|
|
331
|
+
articlesDir: 'src/content/articles', // default
|
|
332
|
+
pagesDir: 'src/content/pages', // default
|
|
333
|
+
siteConfigPath: 'config/site.json', // default
|
|
334
|
+
categoriesConfigPath: 'config/categories.json', // default
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
All fields are optional. Omitting them uses the defaults shown above.
|
|
339
|
+
|
|
340
|
+
**2. Register the Vite plugin** in `vite.config.ts`
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
import { sveltekit } from '@sveltejs/kit/vite';
|
|
344
|
+
import { cosmoloPlugin } from 'cosmolo/plugin';
|
|
345
|
+
import config from './cosmolo.config';
|
|
346
|
+
|
|
347
|
+
export default {
|
|
348
|
+
plugins: [sveltekit(), cosmoloPlugin(config)],
|
|
349
|
+
};
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
The plugin generates a virtual module (`cosmolo:content`) containing `import.meta.glob`
|
|
353
|
+
calls for your configured paths. All content — including `categories.json` and
|
|
354
|
+
`site.json` — is bundled at build time with no `fs` access at runtime, making Cosmolo
|
|
355
|
+
compatible with Cloudflare Workers and other serverless runtimes.
|
|
356
|
+
|
|
357
|
+
### Scaffolding with `cosmolo init`
|
|
358
|
+
|
|
359
|
+
Instead of writing route files by hand, run:
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
bunx cosmolo init
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
The command asks two questions:
|
|
366
|
+
|
|
367
|
+
**Mode**
|
|
368
|
+
|
|
369
|
+
| Mode | What gets generated |
|
|
370
|
+
|---|---|
|
|
371
|
+
| **A — Full** | `+page.server.ts` + `+page.svelte` for every route, `sitemap.xml`, `rss.xml`, `Pagination.svelte`, `cosmolo.config.ts`, `vite.config.ts` |
|
|
372
|
+
| **B — Slim** | Server routes only — bring your own Svelte UI |
|
|
373
|
+
|
|
374
|
+
**Adapter**
|
|
375
|
+
|
|
376
|
+
| Adapter | Effect |
|
|
377
|
+
|---|---|
|
|
378
|
+
| **SSG** (`adapter-static`) | Also creates `src/routes/+layout.ts` with `export const prerender = true` |
|
|
379
|
+
| **Serverless / SSR** | No layout file — routes render on demand |
|
|
380
|
+
|
|
381
|
+
If any target file already exists, the command lists every conflict and exits without
|
|
382
|
+
writing anything.
|
|
383
|
+
|
|
384
|
+
**Manual prerender setup**
|
|
385
|
+
|
|
386
|
+
If you chose Serverless but later switch to SSG, add this file:
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
// src/routes/+layout.ts
|
|
390
|
+
export const prerender = true;
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Load function factories
|
|
394
|
+
|
|
395
|
+
`+page.server.ts` files can use factory functions instead of writing load boilerplate:
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
// src/routes/+page.server.ts
|
|
399
|
+
import { createArticlesLoader } from 'cosmolo';
|
|
400
|
+
import config from '../../cosmolo.config';
|
|
401
|
+
export const load = createArticlesLoader(config);
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
// src/routes/articles/[slug]/+page.server.ts
|
|
406
|
+
import { createArticleLoader, createArticleEntries } from 'cosmolo';
|
|
407
|
+
import config from '../../../../cosmolo.config';
|
|
408
|
+
export const entries = createArticleEntries(config);
|
|
409
|
+
export const load = createArticleLoader(config);
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
// src/routes/categories/[slug]/+page.server.ts
|
|
414
|
+
import { createCategoryLoader, createCategoryEntries } from 'cosmolo';
|
|
415
|
+
import config from '../../../../cosmolo.config';
|
|
416
|
+
export const entries = createCategoryEntries(config);
|
|
417
|
+
export const load = createCategoryLoader(config);
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
// src/routes/tags/[tag]/+page.server.ts
|
|
422
|
+
import { createTagLoader, createTagEntries } from 'cosmolo';
|
|
423
|
+
import config from '../../../../cosmolo.config';
|
|
424
|
+
export const entries = createTagEntries(config);
|
|
425
|
+
export const load = createTagLoader(config);
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
For git-based updated dates on the article loader:
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
import { execSync } from 'child_process';
|
|
432
|
+
|
|
433
|
+
export const load = createArticleLoader(config, {
|
|
434
|
+
getUpdatedAt(slug) {
|
|
435
|
+
try {
|
|
436
|
+
return execSync(
|
|
437
|
+
`git log -1 --format=%cI -- "src/content/articles/${slug}.md"`,
|
|
438
|
+
{ encoding: 'utf-8' }
|
|
439
|
+
).trim().split('T')[0];
|
|
440
|
+
} catch { return ''; }
|
|
441
|
+
},
|
|
442
|
+
});
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Using helpers in Svelte components
|
|
446
|
+
|
|
447
|
+
Category labels and SVX components are safe to use directly in `.svelte` files:
|
|
448
|
+
|
|
449
|
+
```svelte
|
|
450
|
+
<script lang="ts">
|
|
451
|
+
import { getCategoryLabel, getSvxComponent } from 'cosmolo';
|
|
452
|
+
import config from '../../../../cosmolo.config';
|
|
453
|
+
import type { Component } from 'svelte';
|
|
454
|
+
|
|
455
|
+
const { data } = $props();
|
|
456
|
+
|
|
457
|
+
// Category label (works client-side — no fs at runtime)
|
|
458
|
+
const label = getCategoryLabel(config, data.article.category);
|
|
459
|
+
|
|
460
|
+
// SVX component for .svx articles (undefined for .md)
|
|
461
|
+
const SvxComponent = getSvxComponent(config, data.article.slug) as Component | undefined;
|
|
462
|
+
</script>
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Package exports
|
|
466
|
+
|
|
467
|
+
| Import | Description |
|
|
468
|
+
|---|---|
|
|
469
|
+
| `cosmolo` | Types, config resolver, all content functions |
|
|
470
|
+
| `cosmolo/plugin` | `cosmoloPlugin(config)` — Vite plugin |
|
|
471
|
+
|
|
472
|
+
Key exports from `cosmolo`:
|
|
473
|
+
|
|
474
|
+
| Export | Description |
|
|
475
|
+
|---|---|
|
|
476
|
+
| `resolveConfig(config?)` | Merge user config with defaults |
|
|
477
|
+
| `getArticles(config)` | All non-draft articles, sorted |
|
|
478
|
+
| `getArticle(config, slug)` | Single article with rendered HTML + TOC |
|
|
479
|
+
| `getArticlesByTag(config, tag)` | Articles filtered by tag |
|
|
480
|
+
| `getArticlesBySeries(config, series)` | Articles in a series, sorted by `seriesOrder` |
|
|
481
|
+
| `getSlugs(config)` | All non-draft article slugs |
|
|
482
|
+
| `getTags(config)` | All tags across all articles |
|
|
483
|
+
| `getSvxComponent(config, slug)` | Svelte component for an `.svx` article (client-safe) |
|
|
484
|
+
| `getCategoryLabel(config, key)` | Category label by key (client-safe) |
|
|
485
|
+
| `getAllCategories(config)` | All category entries |
|
|
486
|
+
| `loadSiteConfig(config)` | Site configuration object |
|
|
487
|
+
| `getPage(config, slug)` | Single static page with rendered HTML |
|
|
488
|
+
| `getPageSlugs(config)` | All static page slugs |
|
|
489
|
+
| `createArticlesLoader(config)` | Load factory for article listings |
|
|
490
|
+
| `createArticleLoader(config, opts?)` | Load factory for single article |
|
|
491
|
+
| `createCategoryLoader(config)` | Load factory for category pages |
|
|
492
|
+
| `createTagLoader(config)` | Load factory for tag pages |
|
|
493
|
+
| `createPageLoader(config)` | Load factory for static pages |
|
|
494
|
+
| `createArticleEntries(config)` | `entries()` generator for article routes |
|
|
495
|
+
| `createCategoryEntries(config)` | `entries()` generator for category routes |
|
|
496
|
+
| `createTagEntries(config)` | `entries()` generator for tag routes |
|
|
497
|
+
| `createPageEntries(config)` | `entries()` generator for page routes |
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Deployment
|
|
502
|
+
|
|
503
|
+
### SSG vs Serverless
|
|
504
|
+
|
|
505
|
+
All content loading happens at build time via `import.meta.glob` and the Vite plugin,
|
|
506
|
+
so there are no `fs` calls at runtime. Cosmolo works with any SvelteKit adapter.
|
|
507
|
+
|
|
508
|
+
| Adapter | Notes |
|
|
509
|
+
|---|---|
|
|
510
|
+
| `@sveltejs/adapter-static` | Full SSG — `cosmolo init` sets `prerender = true` automatically for SSG mode |
|
|
511
|
+
| `@sveltejs/adapter-cloudflare` | Cloudflare Workers / Pages (SSR). No extra config needed. |
|
|
512
|
+
| `@sveltejs/adapter-vercel` | Vercel Edge / Node. No extra config needed. |
|
|
513
|
+
| `@sveltejs/adapter-node` | Self-hosted Node server. No extra config needed. |
|
|
514
|
+
|
|
515
|
+
### Cloudflare Pages (SSG)
|
|
516
|
+
|
|
517
|
+
Cloudflare Pages offers a free tier with global CDN, automatic HTTPS, and Git-based
|
|
518
|
+
deployments.
|
|
519
|
+
|
|
520
|
+
**1. Push your repo to GitHub.**
|
|
521
|
+
|
|
522
|
+
**2. Create a new Pages project**
|
|
523
|
+
|
|
524
|
+
1. Open the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Workers & Pages**
|
|
525
|
+
2. Click **Create** → **Pages** → **Connect to Git**
|
|
526
|
+
3. Authorize Cloudflare and select your repository
|
|
527
|
+
|
|
528
|
+
**3. Configure the build settings**
|
|
529
|
+
|
|
530
|
+
| Setting | Value |
|
|
531
|
+
|---------|-------|
|
|
532
|
+
| Framework preset | None |
|
|
533
|
+
| Build command | `npx bun run build` |
|
|
534
|
+
| Build output directory | `build` |
|
|
535
|
+
|
|
536
|
+
> Cloudflare Pages uses Node.js by default. Using `npx bun run build` ensures bun is
|
|
537
|
+
> available without requiring a custom environment. Alternatively, add `BUN_VERSION=latest`
|
|
538
|
+
> as an environment variable to enable native bun support.
|
|
539
|
+
|
|
540
|
+
**4. Deploy**
|
|
541
|
+
|
|
542
|
+
Click **Save and Deploy**. Cloudflare pulls your code, runs the build, and publishes
|
|
543
|
+
the `build/` directory to their global edge network.
|
|
544
|
+
|
|
545
|
+
**Custom domain**
|
|
546
|
+
|
|
547
|
+
Go to your Pages project → **Custom domains** → add your domain. If your domain's DNS
|
|
548
|
+
is managed on Cloudflare, the setup is automatic.
|
|
549
|
+
|
|
550
|
+
### Vercel
|
|
551
|
+
|
|
552
|
+
```bash
|
|
553
|
+
bunx vercel --prod
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
Or connect via the Vercel dashboard. Build command: `bun run build`. Output directory: `build`.
|
|
557
|
+
|
|
558
|
+
### Netlify
|
|
559
|
+
|
|
560
|
+
```bash
|
|
561
|
+
bunx netlify deploy --prod --dir build
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
Or connect via the Netlify dashboard. Build command: `bun run build`. Publish directory: `build`.
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## Development Commands
|
|
569
|
+
|
|
570
|
+
```bash
|
|
571
|
+
bun dev # Start dev server at http://localhost:5173
|
|
572
|
+
bun build # Build output
|
|
573
|
+
bun preview # Preview the production build
|
|
574
|
+
bun check # TypeScript type-check
|
|
575
|
+
bun lint # Run Prettier + ESLint checks
|
|
576
|
+
bun format # Auto-format all files
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## License
|
|
582
|
+
|
|
583
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cosmolo",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cosmolo": "./src/cli/index.ts"
|
|
@@ -15,7 +15,13 @@
|
|
|
15
15
|
"default": "./src/plugin.ts"
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
|
-
"files": [
|
|
18
|
+
"files": [
|
|
19
|
+
"src",
|
|
20
|
+
"templates",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE",
|
|
23
|
+
"!src/**/*.test.ts"
|
|
24
|
+
],
|
|
19
25
|
"dependencies": {
|
|
20
26
|
"gray-matter": "^4.0.3",
|
|
21
27
|
"marked": "^15.0.0",
|