create-astroid 0.9.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.mjs +404 -26
- package/into.mjs +203 -0
- package/package.json +5 -4
- package/template/README.md +7 -5
- package/template/_app/README.md +62 -0
- package/template/_app/_env.example +12 -0
- package/template/_app/astro.config.mjs +37 -0
- package/template/_app/src/env.d.ts +33 -0
- package/template/_app/src/layouts/App.astro +49 -0
- package/template/_app/src/pages/api/v1/index.ts +22 -0
- package/template/_app/src/pages/index.astro +18 -0
- package/template/_app/src/pages/sitemap.xml.ts +24 -0
- package/template/src/layouts/Site.astro +10 -0
- package/template/src/lib/pages.ts +118 -0
- package/template/src/pages/[...slug].astro +76 -0
- package/template/src/pages/index.astro +16 -69
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Every page an owner makes in the Pages panel, served at its slug: `about` at
|
|
3
|
+
// /about. The same render as the home page (src/lib/pages.ts): a visitor sees
|
|
4
|
+
// the live row, and an editor in edit mode sees the latest pending draft, with
|
|
5
|
+
// the same inline editing and Publish.
|
|
6
|
+
//
|
|
7
|
+
// A file route always wins over this one, so /contact and /login stay theirs.
|
|
8
|
+
// `pagesRoute` refuses those slugs (ASTROID_RESERVED_SLUGS), so a page can't be
|
|
9
|
+
// saved where it would never be reached.
|
|
10
|
+
//
|
|
11
|
+
// Anything else that matches nothing answers 404 here. That includes a hidden or
|
|
12
|
+
// never-published page, and a renamed page's old path, which the middleware's
|
|
13
|
+
// `redirectFor` then answers with a 301 to the new one.
|
|
14
|
+
import Editable from "astroidjs/components/Editable.astro";
|
|
15
|
+
import Sections from "astroidjs/components/Sections.astro";
|
|
16
|
+
import { env } from "cloudflare:workers";
|
|
17
|
+
import Site from "../layouts/Site.astro";
|
|
18
|
+
import { readPage } from "../lib/pages";
|
|
19
|
+
|
|
20
|
+
export const prerender = false;
|
|
21
|
+
|
|
22
|
+
const { editMode } = Astro.locals;
|
|
23
|
+
const slug = Astro.params.slug ?? "";
|
|
24
|
+
|
|
25
|
+
// `home` is served at "/", never at "/home".
|
|
26
|
+
if (slug === "home") return Astro.redirect("/", 301);
|
|
27
|
+
|
|
28
|
+
const page = slug ? await readPage(env.DB, slug, { editMode }) : null;
|
|
29
|
+
|
|
30
|
+
// Edge caching (ADR 0004), gated exactly as on the home page: never in edit
|
|
31
|
+
// mode, only when ASTROID_EDGE_CACHE is "true", and never for a 404, so a page
|
|
32
|
+
// published a minute later isn't hidden behind a cached miss.
|
|
33
|
+
if (!page || editMode || env.ASTROID_EDGE_CACHE !== "true") {
|
|
34
|
+
Astro.cache.set(false);
|
|
35
|
+
} else {
|
|
36
|
+
Astro.cache.set({ maxAge: 60 });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!page) Astro.response.status = 404;
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
{
|
|
43
|
+
page ? (
|
|
44
|
+
<Site
|
|
45
|
+
title={page.row.seo_title ?? page.title}
|
|
46
|
+
description={page.row.seo_description ?? undefined}
|
|
47
|
+
ogImage={page.row.og_image ?? undefined}
|
|
48
|
+
noindex={Boolean(page.row.noindex)}
|
|
49
|
+
versionedPageId={page.row.id}
|
|
50
|
+
sections={page.sections}
|
|
51
|
+
>
|
|
52
|
+
<main>
|
|
53
|
+
<Sections items={page.sections} db={env.DB} mediaBase={env.MEDIA_URL} />
|
|
54
|
+
|
|
55
|
+
<div class="mx-auto max-w-3xl px-6 py-16">
|
|
56
|
+
<Editable as="h1" collection="pages" key={page.row.id} field="title" class="text-5xl font-bold">
|
|
57
|
+
{page.title}
|
|
58
|
+
</Editable>
|
|
59
|
+
<Editable as="div" collection="pages" key={page.row.id} field="body" type="richtext" class="prose mt-6 max-w-none">
|
|
60
|
+
<Fragment set:html={page.body} />
|
|
61
|
+
</Editable>
|
|
62
|
+
</div>
|
|
63
|
+
</main>
|
|
64
|
+
</Site>
|
|
65
|
+
) : (
|
|
66
|
+
<Site title="Page not found" noindex>
|
|
67
|
+
<main class="mx-auto max-w-3xl px-6 py-24 text-center">
|
|
68
|
+
<h1 class="text-4xl font-bold">Page not found</h1>
|
|
69
|
+
<p class="mt-4">This page doesn't exist, or it has moved.</p>
|
|
70
|
+
<p class="mt-8">
|
|
71
|
+
<a class="link" href="/">Go to the home page</a>
|
|
72
|
+
</p>
|
|
73
|
+
</main>
|
|
74
|
+
</Site>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
@@ -9,6 +9,7 @@ import Sections from "astroidjs/components/Sections.astro";
|
|
|
9
9
|
import { env } from "cloudflare:workers";
|
|
10
10
|
import Hero from "../components/Hero.astro";
|
|
11
11
|
import Site from "../layouts/Site.astro";
|
|
12
|
+
import { readPage } from "../lib/pages";
|
|
12
13
|
|
|
13
14
|
export const prerender = false;
|
|
14
15
|
|
|
@@ -37,75 +38,21 @@ if (editMode || env.ASTROID_EDGE_CACHE !== "true") {
|
|
|
37
38
|
// is the on-page H1. The home page passes NO title to <Site>, so <title>
|
|
38
39
|
// is the site-wide default ("Acme Coffee", not "Acme Coffee | Acme Coffee");
|
|
39
40
|
// set `seo_title` on the row to override just the head.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
seo_description: string | null;
|
|
48
|
-
og_image: string | null;
|
|
49
|
-
noindex: number | null;
|
|
50
|
-
}
|
|
51
|
-
let page: HomeRow | null = null;
|
|
52
|
-
try {
|
|
53
|
-
page = await env.DB.prepare(
|
|
54
|
-
"SELECT id, title, body, sections, seo_title, seo_description, og_image, noindex FROM pages WHERE slug = 'home'",
|
|
55
|
-
).first<HomeRow>();
|
|
56
|
-
} catch {
|
|
57
|
-
// No DB binding yet (pre-provision)—fall through to the seed-me prompt.
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let title = page?.title ?? "__BRAND_NAME__";
|
|
61
|
-
let body = page?.body ?? "";
|
|
62
|
-
// `sections` is a JSON string in D1. Bad JSON is a render-time non-event—the
|
|
63
|
-
// page falls back to its prose body rather than 500ing on one malformed row.
|
|
64
|
-
function parseSections(raw: string | null | undefined): unknown[] {
|
|
65
|
-
if (!raw) return [];
|
|
66
|
-
try {
|
|
67
|
-
const parsed = JSON.parse(raw);
|
|
68
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
69
|
-
} catch {
|
|
70
|
-
return [];
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
let sections = parseSections(page?.sections);
|
|
74
|
-
|
|
75
|
-
// In edit mode, prefer the latest unpublished draft so edits resume across reloads.
|
|
76
|
-
if (editMode && page) {
|
|
77
|
-
try {
|
|
78
|
-
const draft = await env.DB.prepare(
|
|
79
|
-
"SELECT version_data FROM pages_versions WHERE parent_id = ? AND status = 'draft' ORDER BY created_at DESC LIMIT 1",
|
|
80
|
-
)
|
|
81
|
-
.bind(page.id)
|
|
82
|
-
.first<{ version_data: string }>();
|
|
83
|
-
if (draft?.version_data) {
|
|
84
|
-
const d = JSON.parse(draft.version_data) as {
|
|
85
|
-
title?: string;
|
|
86
|
-
body?: string;
|
|
87
|
-
sections?: unknown;
|
|
88
|
-
};
|
|
89
|
-
if (typeof d.title === "string") title = d.title;
|
|
90
|
-
if (typeof d.body === "string") body = d.body;
|
|
91
|
-
// Sections stage as drafts like any other field, so edit mode must render
|
|
92
|
-
// the draft's array—otherwise an editor's in-progress section edits
|
|
93
|
-
// vanish on reload while their title edits survive.
|
|
94
|
-
if (Array.isArray(d.sections)) sections = d.sections;
|
|
95
|
-
else if (typeof d.sections === "string") sections = parseSections(d.sections);
|
|
96
|
-
}
|
|
97
|
-
} catch {
|
|
98
|
-
// Non-fatal—fall back to the live row.
|
|
99
|
-
}
|
|
100
|
-
}
|
|
41
|
+
//
|
|
42
|
+
// `requireLive: false`: an unpublished home keeps rendering rather than turning
|
|
43
|
+
// the site's front door into a 404. Every other page is visitor-visible only
|
|
44
|
+
// while it's live (src/pages/[...slug].astro).
|
|
45
|
+
const page = await readPage(env.DB, "home", { editMode, requireLive: false });
|
|
46
|
+
const title = page?.title ?? "__BRAND_NAME__";
|
|
47
|
+
const sections = page?.sections ?? [];
|
|
101
48
|
---
|
|
102
49
|
|
|
103
50
|
<Site
|
|
104
|
-
title={page?.seo_title ?? undefined}
|
|
105
|
-
description={page?.seo_description ?? undefined}
|
|
106
|
-
ogImage={page?.og_image ?? undefined}
|
|
107
|
-
noindex={Boolean(page?.noindex)}
|
|
108
|
-
versionedPageId={page?.id}
|
|
51
|
+
title={page?.row.seo_title ?? undefined}
|
|
52
|
+
description={page?.row.seo_description ?? undefined}
|
|
53
|
+
ogImage={page?.row.og_image ?? undefined}
|
|
54
|
+
noindex={Boolean(page?.row.noindex)}
|
|
55
|
+
versionedPageId={page?.row.id}
|
|
109
56
|
sections={sections}
|
|
110
57
|
>
|
|
111
58
|
{
|
|
@@ -118,11 +65,11 @@ if (editMode && page) {
|
|
|
118
65
|
<Sections items={sections} db={env.DB} mediaBase={env.MEDIA_URL} />
|
|
119
66
|
|
|
120
67
|
<div class="mx-auto max-w-3xl px-6 py-16">
|
|
121
|
-
<Editable as="h1" collection="pages" key={page.id} field="title" class="text-5xl font-bold">
|
|
68
|
+
<Editable as="h1" collection="pages" key={page.row.id} field="title" class="text-5xl font-bold">
|
|
122
69
|
{title}
|
|
123
70
|
</Editable>
|
|
124
|
-
<Editable as="div" collection="pages" key={page.id} field="body" type="richtext" class="prose mt-6 max-w-none">
|
|
125
|
-
<Fragment set:html={body} />
|
|
71
|
+
<Editable as="div" collection="pages" key={page.row.id} field="body" type="richtext" class="prose mt-6 max-w-none">
|
|
72
|
+
<Fragment set:html={page.body} />
|
|
126
73
|
</Editable>
|
|
127
74
|
</div>
|
|
128
75
|
</main>
|