@beatzball/create-litro 0.1.4 → 0.2.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/CHANGELOG.md +21 -0
- package/dist/recipes/11ty-blog/template/e2e/index.spec.ts +19 -0
- package/dist/recipes/11ty-blog/template/package.json +4 -2
- package/dist/recipes/11ty-blog/template/playwright.config.ts +20 -0
- package/dist/recipes/fullstack/template/e2e/index.spec.ts +19 -0
- package/dist/recipes/fullstack/template/package.json +4 -2
- package/dist/recipes/fullstack/template/playwright.config.ts +20 -0
- package/dist/recipes/starlight/recipe.config.d.ts +4 -0
- package/dist/recipes/starlight/recipe.config.d.ts.map +1 -0
- package/dist/recipes/starlight/recipe.config.js +9 -0
- package/dist/recipes/starlight/recipe.config.js.map +1 -0
- package/dist/recipes/starlight/recipe.config.ts +11 -0
- package/dist/recipes/starlight/template/_data/metadata.js +10 -0
- package/dist/recipes/starlight/template/app.ts +18 -0
- package/dist/recipes/starlight/template/content/blog/.11tydata.json +1 -0
- package/dist/recipes/starlight/template/content/blog/release-notes.md +44 -0
- package/dist/recipes/starlight/template/content/blog/welcome.md +44 -0
- package/dist/recipes/starlight/template/content/docs/.11tydata.json +1 -0
- package/dist/recipes/starlight/template/content/docs/configuration.md +77 -0
- package/dist/recipes/starlight/template/content/docs/getting-started.md +53 -0
- package/dist/recipes/starlight/template/content/docs/guides-deploying.md +79 -0
- package/dist/recipes/starlight/template/content/docs/guides-first-page.md +64 -0
- package/dist/recipes/starlight/template/content/docs/installation.md +54 -0
- package/dist/recipes/starlight/template/e2e/index.spec.ts +32 -0
- package/dist/recipes/starlight/template/litro.recipe.json +7 -0
- package/dist/recipes/starlight/template/nitro.config.ts +57 -0
- package/dist/recipes/starlight/template/package.json +28 -0
- package/dist/recipes/starlight/template/pages/blog/[slug].ts +125 -0
- package/dist/recipes/starlight/template/pages/blog/index.ts +114 -0
- package/dist/recipes/starlight/template/pages/blog/tags/[tag].ts +110 -0
- package/dist/recipes/starlight/template/pages/docs/[slug].ts +147 -0
- package/dist/recipes/starlight/template/pages/index.ts +135 -0
- package/dist/recipes/starlight/template/playwright.config.ts +20 -0
- package/dist/recipes/starlight/template/public/styles/starlight.css +215 -0
- package/dist/recipes/starlight/template/server/middleware/vite-dev.ts +29 -0
- package/dist/recipes/starlight/template/server/routes/[...].ts +57 -0
- package/dist/recipes/starlight/template/server/starlight.config.js +29 -0
- package/dist/recipes/starlight/template/src/components/sl-aside.ts +91 -0
- package/dist/recipes/starlight/template/src/components/sl-badge.ts +76 -0
- package/dist/recipes/starlight/template/src/components/sl-card-grid.ts +34 -0
- package/dist/recipes/starlight/template/src/components/sl-card.ts +91 -0
- package/dist/recipes/starlight/template/src/components/sl-tab-item.ts +35 -0
- package/dist/recipes/starlight/template/src/components/sl-tabs.ts +108 -0
- package/dist/recipes/starlight/template/src/components/starlight-header.ts +152 -0
- package/dist/recipes/starlight/template/src/components/starlight-page.ts +168 -0
- package/dist/recipes/starlight/template/src/components/starlight-sidebar.ts +125 -0
- package/dist/recipes/starlight/template/src/components/starlight-toc.ts +133 -0
- package/dist/recipes/starlight/template/src/date-utils.ts +20 -0
- package/dist/recipes/starlight/template/src/extract-headings.ts +68 -0
- package/dist/recipes/starlight/template/src/route-meta.ts +16 -0
- package/dist/recipes/starlight/template/tsconfig.json +14 -0
- package/dist/recipes/starlight/template/vite.config.ts +19 -0
- package/dist/src/scaffold.test.js +134 -0
- package/dist/src/scaffold.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/recipes/11ty-blog/template/e2e/index.spec.ts +19 -0
- package/recipes/11ty-blog/template/package.json +4 -2
- package/recipes/11ty-blog/template/playwright.config.ts +20 -0
- package/recipes/fullstack/template/e2e/index.spec.ts +19 -0
- package/recipes/fullstack/template/package.json +4 -2
- package/recipes/fullstack/template/playwright.config.ts +20 -0
- package/recipes/starlight/recipe.config.ts +11 -0
- package/recipes/starlight/template/_data/metadata.js +10 -0
- package/recipes/starlight/template/app.ts +18 -0
- package/recipes/starlight/template/content/blog/.11tydata.json +1 -0
- package/recipes/starlight/template/content/blog/release-notes.md +44 -0
- package/recipes/starlight/template/content/blog/welcome.md +44 -0
- package/recipes/starlight/template/content/docs/.11tydata.json +1 -0
- package/recipes/starlight/template/content/docs/configuration.md +77 -0
- package/recipes/starlight/template/content/docs/getting-started.md +53 -0
- package/recipes/starlight/template/content/docs/guides-deploying.md +79 -0
- package/recipes/starlight/template/content/docs/guides-first-page.md +64 -0
- package/recipes/starlight/template/content/docs/installation.md +54 -0
- package/recipes/starlight/template/e2e/index.spec.ts +32 -0
- package/recipes/starlight/template/litro.recipe.json +7 -0
- package/recipes/starlight/template/nitro.config.ts +57 -0
- package/recipes/starlight/template/package.json +28 -0
- package/recipes/starlight/template/pages/blog/[slug].ts +125 -0
- package/recipes/starlight/template/pages/blog/index.ts +114 -0
- package/recipes/starlight/template/pages/blog/tags/[tag].ts +110 -0
- package/recipes/starlight/template/pages/docs/[slug].ts +147 -0
- package/recipes/starlight/template/pages/index.ts +135 -0
- package/recipes/starlight/template/playwright.config.ts +20 -0
- package/recipes/starlight/template/public/styles/starlight.css +215 -0
- package/recipes/starlight/template/server/middleware/vite-dev.ts +29 -0
- package/recipes/starlight/template/server/routes/[...].ts +57 -0
- package/recipes/starlight/template/server/starlight.config.js +29 -0
- package/recipes/starlight/template/src/components/sl-aside.ts +91 -0
- package/recipes/starlight/template/src/components/sl-badge.ts +76 -0
- package/recipes/starlight/template/src/components/sl-card-grid.ts +34 -0
- package/recipes/starlight/template/src/components/sl-card.ts +91 -0
- package/recipes/starlight/template/src/components/sl-tab-item.ts +35 -0
- package/recipes/starlight/template/src/components/sl-tabs.ts +108 -0
- package/recipes/starlight/template/src/components/starlight-header.ts +152 -0
- package/recipes/starlight/template/src/components/starlight-page.ts +168 -0
- package/recipes/starlight/template/src/components/starlight-sidebar.ts +125 -0
- package/recipes/starlight/template/src/components/starlight-toc.ts +133 -0
- package/recipes/starlight/template/src/date-utils.ts +20 -0
- package/recipes/starlight/template/src/extract-headings.ts +68 -0
- package/recipes/starlight/template/src/route-meta.ts +16 -0
- package/recipes/starlight/template/tsconfig.json +14 -0
- package/recipes/starlight/template/vite.config.ts +19 -0
- package/src/scaffold.test.ts +148 -0
- package/vitest.config.ts +10 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Welcome to {{projectName}}
|
|
3
|
+
date: 2026-01-15
|
|
4
|
+
description: Introducing the {{projectName}} docs and blog site, built with Litro and Starlight-inspired components.
|
|
5
|
+
tags:
|
|
6
|
+
- posts
|
|
7
|
+
- welcome
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Hello, World!
|
|
11
|
+
|
|
12
|
+
Welcome to **{{projectName}}** — a docs and blog site scaffolded with Litro's Starlight recipe.
|
|
13
|
+
|
|
14
|
+
This blog is powered by Litro's content layer. Each `.md` file in `content/blog/` becomes a post, available at `/blog/<slug>`. Write in plain Markdown; the framework handles rendering, tag indexing, and static generation.
|
|
15
|
+
|
|
16
|
+
## What's Included
|
|
17
|
+
|
|
18
|
+
- **Docs site** at `/docs/` — structured documentation with a sidebar, table of contents, and prev/next navigation
|
|
19
|
+
- **Blog** at `/blog/` — a chronological listing of posts with tag filtering
|
|
20
|
+
- **Dark mode** — a theme toggle in the header that persists to `localStorage`
|
|
21
|
+
- **CSS design tokens** — all colors, fonts, and spacing via `--sl-*` custom properties
|
|
22
|
+
|
|
23
|
+
## Writing Posts
|
|
24
|
+
|
|
25
|
+
Create a new `.md` file in `content/blog/`:
|
|
26
|
+
|
|
27
|
+
```markdown
|
|
28
|
+
---
|
|
29
|
+
title: My Post Title
|
|
30
|
+
date: 2026-02-01
|
|
31
|
+
description: A short summary.
|
|
32
|
+
tags:
|
|
33
|
+
- posts
|
|
34
|
+
- my-tag
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
Post body here.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Every post in `content/blog/` inherits the `posts` tag via `blog.11tydata.json`. List any additional tags in the post frontmatter — tag pages are generated automatically at `/blog/tags/<tag>`.
|
|
41
|
+
|
|
42
|
+
## Building
|
|
43
|
+
|
|
44
|
+
Run `pnpm build` to generate the static HTML for every route, then `pnpm preview` to serve it locally.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "section": "docs" }
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Configuration
|
|
3
|
+
description: Configure your Starlight site — title, navigation, sidebar, and theme.
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 3
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Site Config
|
|
9
|
+
|
|
10
|
+
All site-wide settings live in `server/starlight.config.js`:
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
export const siteConfig = {
|
|
14
|
+
title: 'My Docs',
|
|
15
|
+
description: 'Documentation and blog powered by Litro',
|
|
16
|
+
logo: null,
|
|
17
|
+
editUrlBase: null,
|
|
18
|
+
nav: [...],
|
|
19
|
+
sidebar: [...],
|
|
20
|
+
};
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### `title`
|
|
24
|
+
|
|
25
|
+
The site title shown in the header and used in `<title>` tags.
|
|
26
|
+
|
|
27
|
+
### `description`
|
|
28
|
+
|
|
29
|
+
A short description used in meta tags.
|
|
30
|
+
|
|
31
|
+
### `editUrlBase`
|
|
32
|
+
|
|
33
|
+
If set (e.g. `'https://github.com/you/repo/edit/main'`), each doc page shows an "Edit this page" link pointing to the source file on GitHub.
|
|
34
|
+
|
|
35
|
+
### `nav`
|
|
36
|
+
|
|
37
|
+
Top-level navigation links shown in the header:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
nav: [
|
|
41
|
+
{ label: 'Docs', href: '/docs/getting-started' },
|
|
42
|
+
{ label: 'Blog', href: '/blog' },
|
|
43
|
+
],
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### `sidebar`
|
|
47
|
+
|
|
48
|
+
Sidebar groups, each with a label and array of items. Each item has a `label` and a `slug` (the filename without `.md` under `content/docs/`):
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
sidebar: [
|
|
52
|
+
{
|
|
53
|
+
label: 'Start Here',
|
|
54
|
+
items: [
|
|
55
|
+
{ label: 'Getting Started', slug: 'getting-started' },
|
|
56
|
+
{ label: 'Installation', slug: 'installation' },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## CSS Design Tokens
|
|
63
|
+
|
|
64
|
+
The visual theme is controlled by CSS custom properties in `public/styles/starlight.css`. Override any `--sl-*` variable to customize colors, fonts, and spacing:
|
|
65
|
+
|
|
66
|
+
```css
|
|
67
|
+
:root {
|
|
68
|
+
--sl-color-accent: #7c3aed; /* primary accent color */
|
|
69
|
+
--sl-font-sans: ui-sans-serif, system-ui, sans-serif;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Dark mode tokens are under `[data-theme='dark'] { ... }`.
|
|
74
|
+
|
|
75
|
+
## Dark / Light Mode
|
|
76
|
+
|
|
77
|
+
The theme toggle button in the header reads and writes `localStorage.getItem('sl-theme')`. A FOUC-prevention inline script (injected via `routeMeta.head`) sets `data-theme` on `<html>` before first paint.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Getting Started
|
|
3
|
+
description: Learn how to set up and run your Litro Starlight docs site.
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 1
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Welcome
|
|
9
|
+
|
|
10
|
+
You've just scaffolded a docs and blog site powered by **Litro** — a fullstack web framework that combines Lit web components with Nitro's server engine.
|
|
11
|
+
|
|
12
|
+
This site is a **static site** (SSG mode). Run `pnpm build` to pre-render every page to HTML, then `pnpm preview` to serve the output locally.
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
You'll need **Node.js 20+** and a package manager (`pnpm`, `npm`, or `yarn`).
|
|
17
|
+
|
|
18
|
+
## Install Dependencies
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Start the Dev Server
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pnpm dev
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The dev server starts on `http://localhost:3030`. Changes to Lit components and Markdown content are reflected immediately.
|
|
31
|
+
|
|
32
|
+
## Project Structure
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
my-docs/
|
|
36
|
+
pages/ Lit page components (filename = route)
|
|
37
|
+
src/
|
|
38
|
+
components/ Starlight layout and UI components
|
|
39
|
+
content/
|
|
40
|
+
docs/ Documentation Markdown files
|
|
41
|
+
blog/ Blog post Markdown files
|
|
42
|
+
server/
|
|
43
|
+
starlight.config.js Site title, nav, and sidebar config
|
|
44
|
+
public/
|
|
45
|
+
styles/ CSS design tokens (--sl-* variables)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Next Steps
|
|
49
|
+
|
|
50
|
+
- Edit `server/starlight.config.js` to update the site title, nav links, and sidebar groups.
|
|
51
|
+
- Add new docs pages to `content/docs/` — each `.md` file becomes a route under `/docs/`.
|
|
52
|
+
- Write blog posts in `content/blog/`.
|
|
53
|
+
- Run `pnpm build` to generate the static site.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Deploying
|
|
3
|
+
description: Deploy your Litro Starlight docs site to any CDN or static hosting platform.
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 5
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Build
|
|
9
|
+
|
|
10
|
+
Generate the static HTML output:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm build
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
This runs `litro build` under the hood, which:
|
|
17
|
+
|
|
18
|
+
1. Scans `pages/` to discover all routes
|
|
19
|
+
2. Calls `generateRoutes()` on each dynamic page to enumerate all paths
|
|
20
|
+
3. Pre-renders every path to a `.html` file in `.output/public/`
|
|
21
|
+
|
|
22
|
+
## Output
|
|
23
|
+
|
|
24
|
+
After a successful build, the static site is in `.output/public/`. You can serve this directory from any web server or CDN.
|
|
25
|
+
|
|
26
|
+
## Platforms
|
|
27
|
+
|
|
28
|
+
### Vercel
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# vercel.json
|
|
32
|
+
{
|
|
33
|
+
"outputDirectory": ".output/public"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or connect the repository in the Vercel dashboard — Vercel detects the static output automatically.
|
|
38
|
+
|
|
39
|
+
### Netlify
|
|
40
|
+
|
|
41
|
+
```toml
|
|
42
|
+
# netlify.toml
|
|
43
|
+
[build]
|
|
44
|
+
command = "pnpm build"
|
|
45
|
+
publish = ".output/public"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Cloudflare Pages
|
|
49
|
+
|
|
50
|
+
Set the build output directory to `.output/public` in the Cloudflare Pages dashboard.
|
|
51
|
+
|
|
52
|
+
### GitHub Pages
|
|
53
|
+
|
|
54
|
+
Add a GitHub Actions workflow:
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
- name: Build
|
|
58
|
+
run: pnpm build
|
|
59
|
+
|
|
60
|
+
- name: Deploy
|
|
61
|
+
uses: peaceiris/actions-gh-pages@v3
|
|
62
|
+
with:
|
|
63
|
+
github_token: GITHUB_TOKEN # set via repository secrets
|
|
64
|
+
publish_dir: .output/public
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Custom Domain
|
|
68
|
+
|
|
69
|
+
Configure the custom domain in your hosting provider's dashboard. No Litro-specific changes are needed — the static output is plain HTML, CSS, and JS.
|
|
70
|
+
|
|
71
|
+
## Preview Locally
|
|
72
|
+
|
|
73
|
+
Before deploying, preview the production build locally:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pnpm preview
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This serves the `.output/` directory using Nitro's static preset server on `http://localhost:3030`.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Your First Page
|
|
3
|
+
description: Create a new docs page and add it to the sidebar navigation.
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 4
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Create a Markdown File
|
|
9
|
+
|
|
10
|
+
Add a new `.md` file to `content/docs/`. The filename (without extension) becomes the URL slug.
|
|
11
|
+
|
|
12
|
+
For example, create `content/docs/my-topic.md`:
|
|
13
|
+
|
|
14
|
+
```markdown
|
|
15
|
+
---
|
|
16
|
+
title: My Topic
|
|
17
|
+
description: A brief description for SEO and the sidebar.
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Introduction
|
|
21
|
+
|
|
22
|
+
Write your documentation here using standard Markdown.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Add It to the Sidebar
|
|
26
|
+
|
|
27
|
+
Open `server/starlight.config.js` and add an entry to the appropriate sidebar group:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
sidebar: [
|
|
31
|
+
{
|
|
32
|
+
label: 'My Section',
|
|
33
|
+
items: [
|
|
34
|
+
{ label: 'My Topic', slug: 'my-topic' },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The `slug` must match the filename (without `.md`). The page will be available at `/docs/my-topic`.
|
|
41
|
+
|
|
42
|
+
## Frontmatter Fields
|
|
43
|
+
|
|
44
|
+
| Field | Type | Required | Description |
|
|
45
|
+
|---|---|---|---|
|
|
46
|
+
| `title` | `string` | Yes | Page title (shown in sidebar and `<title>`) |
|
|
47
|
+
| `description` | `string` | No | Short summary for SEO |
|
|
48
|
+
| `sidebar.order` | `number` | No | Controls sort order within the sidebar group |
|
|
49
|
+
| `sidebar.label` | `string` | No | Override the label shown in the sidebar |
|
|
50
|
+
|
|
51
|
+
## Markdown Features
|
|
52
|
+
|
|
53
|
+
This site supports **GitHub Flavored Markdown (GFM)**, including:
|
|
54
|
+
|
|
55
|
+
- Tables (like the one above)
|
|
56
|
+
- Fenced code blocks with syntax highlighting
|
|
57
|
+
- Task lists: `- [ ] Todo`
|
|
58
|
+
- Strikethrough: `~~text~~`
|
|
59
|
+
|
|
60
|
+
Headings (`##`, `###`, `####`) are automatically extracted to build the table of contents shown on the right side of each docs page.
|
|
61
|
+
|
|
62
|
+
## After Adding a Page
|
|
63
|
+
|
|
64
|
+
Run `pnpm build` to regenerate the static HTML for all routes, then `pnpm preview` to verify the new page appears in the sidebar and TOC.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Installation
|
|
3
|
+
description: Install dependencies and configure your Litro Starlight project.
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 2
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Package Manager
|
|
9
|
+
|
|
10
|
+
This project uses **pnpm** by default. You can also use `npm` or `yarn` — just replace `pnpm` in the commands below.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
From the project root, run:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This installs all dependencies listed in `package.json`, including:
|
|
21
|
+
|
|
22
|
+
- **`@beatzball/litro`** — the Litro framework (pages plugin, SSR/SSG, content layer)
|
|
23
|
+
- **`lit`** — Lit web components library
|
|
24
|
+
- **`@lit-labs/ssr`** — server-side rendering via Declarative Shadow DOM
|
|
25
|
+
- **`@lit-labs/ssr-client`** — hydration support script
|
|
26
|
+
|
|
27
|
+
## TypeScript
|
|
28
|
+
|
|
29
|
+
The project ships with `tsconfig.json` preconfigured for Lit decorators:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"compilerOptions": {
|
|
34
|
+
"experimentalDecorators": true,
|
|
35
|
+
"useDefineForClassFields": false
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
These two settings are required for Lit's `@customElement`, `@state`, and `@property` decorators to work correctly with TypeScript's decorator transform.
|
|
41
|
+
|
|
42
|
+
## Environment
|
|
43
|
+
|
|
44
|
+
No environment variables are required for SSG mode. If you add API routes later, create a `.env` file at the project root.
|
|
45
|
+
|
|
46
|
+
## Verify
|
|
47
|
+
|
|
48
|
+
After installing, start the dev server:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pnpm dev
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Navigate to `http://localhost:3030`. You should see the splash page with the sidebar navigation.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
const PRERENDERED_ROUTES = [
|
|
4
|
+
'/',
|
|
5
|
+
'/docs/getting-started',
|
|
6
|
+
'/docs/installation',
|
|
7
|
+
'/docs/configuration',
|
|
8
|
+
'/docs/guides-first-page',
|
|
9
|
+
'/docs/guides-deploying',
|
|
10
|
+
'/blog',
|
|
11
|
+
'/blog/welcome',
|
|
12
|
+
'/blog/release-notes',
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
test('home renders page-home component', async ({ page }) => {
|
|
16
|
+
await page.goto('/');
|
|
17
|
+
await page.waitForSelector('page-home');
|
|
18
|
+
await expect(page.locator('page-home')).toBeVisible();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('/docs/getting-started renders', async ({ page }) => {
|
|
22
|
+
await page.goto('/docs/getting-started');
|
|
23
|
+
await page.waitForSelector('page-docs-slug');
|
|
24
|
+
await expect(page.locator('page-docs-slug')).toBeVisible();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('all prerendered routes return 200', async ({ request }) => {
|
|
28
|
+
for (const route of PRERENDERED_ROUTES) {
|
|
29
|
+
const response = await request.get(route);
|
|
30
|
+
expect(response.status(), `Expected 200 for ${route}`).toBe(200);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { defineNitroConfig } from 'nitropack/config';
|
|
2
|
+
import type { Nitro } from 'nitropack';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { ssgPreset } from '@beatzball/litro/config';
|
|
5
|
+
import pagesPlugin from '@beatzball/litro/plugins';
|
|
6
|
+
import ssgPlugin from '@beatzball/litro/plugins/ssg';
|
|
7
|
+
import contentPlugin from '@beatzball/litro/content/plugin';
|
|
8
|
+
|
|
9
|
+
export default defineNitroConfig({
|
|
10
|
+
...ssgPreset(),
|
|
11
|
+
|
|
12
|
+
srcDir: 'server',
|
|
13
|
+
|
|
14
|
+
publicAssets: [
|
|
15
|
+
{ dir: '../dist/client', baseURL: '/_litro/', maxAge: 31536000 },
|
|
16
|
+
{ dir: '../public', baseURL: '/', maxAge: 0 },
|
|
17
|
+
{ dir: '../content', baseURL: '/content/', maxAge: 86400 },
|
|
18
|
+
],
|
|
19
|
+
|
|
20
|
+
externals: { inline: ['@lit-labs/ssr', '@lit-labs/ssr-client'] },
|
|
21
|
+
|
|
22
|
+
esbuild: {
|
|
23
|
+
options: {
|
|
24
|
+
tsconfigRaw: {
|
|
25
|
+
compilerOptions: {
|
|
26
|
+
experimentalDecorators: true,
|
|
27
|
+
useDefineForClassFields: false,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
ignore: ['**/middleware/vite-dev.ts'],
|
|
34
|
+
handlers: [
|
|
35
|
+
{
|
|
36
|
+
middleware: true,
|
|
37
|
+
handler: resolve('./server/middleware/vite-dev.ts'),
|
|
38
|
+
env: 'dev',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
|
|
42
|
+
hooks: {
|
|
43
|
+
'build:before': async (nitro: Nitro) => {
|
|
44
|
+
await contentPlugin(nitro);
|
|
45
|
+
await pagesPlugin(nitro);
|
|
46
|
+
await ssgPlugin(nitro);
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
compatibilityDate: '2025-01-01',
|
|
51
|
+
|
|
52
|
+
routeRules: {
|
|
53
|
+
'/_litro/**': {
|
|
54
|
+
headers: { 'cache-control': 'public, max-age=31536000, immutable' },
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"imports": {
|
|
6
|
+
"#litro/page-manifest": "./server/stubs/page-manifest.ts"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "litro dev",
|
|
10
|
+
"build": "litro build",
|
|
11
|
+
"preview": "litro preview",
|
|
12
|
+
"generate": "litro generate",
|
|
13
|
+
"test:e2e": "playwright test"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@beatzball/litro": "latest",
|
|
17
|
+
"lit": "^3.2.1",
|
|
18
|
+
"@lit-labs/ssr": "^3.3.0",
|
|
19
|
+
"@lit-labs/ssr-client": "^1.1.7",
|
|
20
|
+
"h3": "^1.13.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"nitropack": "^2.13.1",
|
|
24
|
+
"vite": "^5.4.11",
|
|
25
|
+
"typescript": "^5.7.3",
|
|
26
|
+
"@playwright/test": "^1.48.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
|
3
|
+
import { customElement } from 'lit/decorators.js';
|
|
4
|
+
import { LitroPage } from '@beatzball/litro/runtime';
|
|
5
|
+
import { definePageData } from '@beatzball/litro';
|
|
6
|
+
import { createError } from 'h3';
|
|
7
|
+
import type { Post } from 'litro:content';
|
|
8
|
+
import { getPosts } from 'litro:content';
|
|
9
|
+
import { siteConfig } from '../../server/starlight.config.js';
|
|
10
|
+
import { extractHeadings, addHeadingIds } from '../../src/extract-headings.js';
|
|
11
|
+
import { starlightHead } from '../../src/route-meta.js';
|
|
12
|
+
import { formatDate, isoDate } from '../../src/date-utils.js';
|
|
13
|
+
|
|
14
|
+
// Register components used in render()
|
|
15
|
+
import '../../src/components/starlight-header.js';
|
|
16
|
+
|
|
17
|
+
export interface BlogPostData {
|
|
18
|
+
post: Post;
|
|
19
|
+
body: string;
|
|
20
|
+
toc: Array<{ depth: number; text: string; slug: string }>;
|
|
21
|
+
siteTitle: string;
|
|
22
|
+
nav: typeof siteConfig.nav;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const pageData = definePageData(async (event) => {
|
|
26
|
+
const slug = event.context.params?.slug ?? '';
|
|
27
|
+
|
|
28
|
+
// Content URLs are /content/blog/<slug> (contentDir = 'content')
|
|
29
|
+
const posts = await getPosts();
|
|
30
|
+
const post = posts.find(p => p.url === `/content/blog/${slug}`);
|
|
31
|
+
|
|
32
|
+
if (!post) {
|
|
33
|
+
throw createError({ statusCode: 404, message: `Post not found: ${slug}` });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const toc = extractHeadings(post.rawBody);
|
|
37
|
+
const body = addHeadingIds(post.body);
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
post,
|
|
41
|
+
body,
|
|
42
|
+
toc,
|
|
43
|
+
siteTitle: siteConfig.title,
|
|
44
|
+
nav: siteConfig.nav,
|
|
45
|
+
} satisfies BlogPostData;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export async function generateRoutes(): Promise<string[]> {
|
|
49
|
+
const posts = await getPosts();
|
|
50
|
+
return posts
|
|
51
|
+
.filter(p => p.url.startsWith('/content/blog/'))
|
|
52
|
+
.map(p => '/blog' + p.url.slice('/content/blog'.length));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const routeMeta = {
|
|
56
|
+
head: starlightHead,
|
|
57
|
+
title: 'Blog — {{projectName}}',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
@customElement('page-blog-slug')
|
|
61
|
+
export class BlogPostPage extends LitroPage {
|
|
62
|
+
override render() {
|
|
63
|
+
const data = this.serverData as BlogPostData | null;
|
|
64
|
+
if (!data?.post) return html`<p>Loading…</p>`;
|
|
65
|
+
|
|
66
|
+
const { post, body, siteTitle, nav } = data;
|
|
67
|
+
const blogSlug = post.url.slice('/content/blog/'.length);
|
|
68
|
+
|
|
69
|
+
return html`
|
|
70
|
+
<div style="min-height:100vh;display:flex;flex-direction:column;">
|
|
71
|
+
<starlight-header
|
|
72
|
+
siteTitle="${siteTitle}"
|
|
73
|
+
.nav="${nav}"
|
|
74
|
+
currentPath="/blog/${blogSlug}"
|
|
75
|
+
></starlight-header>
|
|
76
|
+
<main style="
|
|
77
|
+
flex:1;
|
|
78
|
+
max-width:52rem;
|
|
79
|
+
margin:0 auto;
|
|
80
|
+
padding:var(--sl-content-pad-y,2rem) var(--sl-content-pad-x,1.5rem);
|
|
81
|
+
width:100%;
|
|
82
|
+
">
|
|
83
|
+
<article>
|
|
84
|
+
<header style="margin-bottom:2rem;">
|
|
85
|
+
<h1 style="font-size:var(--sl-text-4xl);font-weight:700;margin:0 0 0.75rem;line-height:1.15;">
|
|
86
|
+
${post.title}
|
|
87
|
+
</h1>
|
|
88
|
+
<time
|
|
89
|
+
datetime="${isoDate(post.date)}"
|
|
90
|
+
style="font-size:var(--sl-text-sm);color:var(--sl-color-gray-4);"
|
|
91
|
+
>${formatDate(post.date)}</time>
|
|
92
|
+
${post.tags.filter(t => t !== 'posts').length > 0 ? html`
|
|
93
|
+
<div style="display:flex;gap:0.4rem;flex-wrap:wrap;margin-top:0.75rem;">
|
|
94
|
+
${post.tags.filter(t => t !== 'posts').map(tag => html`
|
|
95
|
+
<a href="/blog/tags/${tag}" style="
|
|
96
|
+
display:inline-block;
|
|
97
|
+
padding:0.15em 0.55em;
|
|
98
|
+
font-size:var(--sl-text-xs);
|
|
99
|
+
border-radius:9999px;
|
|
100
|
+
background:var(--sl-color-accent-low);
|
|
101
|
+
color:var(--sl-color-accent-high,#5b21b6);
|
|
102
|
+
text-decoration:none;
|
|
103
|
+
font-weight:600;
|
|
104
|
+
">#${tag}</a>
|
|
105
|
+
`)}
|
|
106
|
+
</div>
|
|
107
|
+
` : ''}
|
|
108
|
+
</header>
|
|
109
|
+
<!-- unsafeHTML renders the Markdown-generated HTML directly.
|
|
110
|
+
The content directory is trusted-author-only; do not place
|
|
111
|
+
user-submitted or untrusted content here without sanitizing. -->
|
|
112
|
+
${unsafeHTML(body)}
|
|
113
|
+
</article>
|
|
114
|
+
<footer style="margin-top:3rem;padding-top:1.5rem;border-top:1px solid var(--sl-color-border);">
|
|
115
|
+
<a href="/blog" style="font-size:var(--sl-text-sm);color:var(--sl-color-accent);text-decoration:none;">
|
|
116
|
+
← Back to Blog
|
|
117
|
+
</a>
|
|
118
|
+
</footer>
|
|
119
|
+
</main>
|
|
120
|
+
</div>
|
|
121
|
+
`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default BlogPostPage;
|