@because-dev/astro 0.1.1 → 0.2.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/components/Head.astro +7 -1
- package/components/IdeaArticle.astro +1 -1
- package/components/IdeaShell.astro +46 -55
- package/components/IdeasIndex.astro +58 -0
- package/dist/helpers.d.ts +27 -0
- package/dist/helpers.js +25 -4
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +20 -0
- package/dist/index.js +35 -12
- package/dist/index.js.map +1 -1
- package/package.json +2 -4
- package/routes/idea.astro +7 -7
- package/routes/index-page.astro +15 -0
- package/routes/index.astro +2 -31
- package/routes/list-page.json.ts +8 -0
- package/routes/static-file.ts +10 -2
- package/virtual.d.ts +15 -0
- package/routes/ideas.json.ts +0 -8
- package/routes/posts.json.ts +0 -8
package/components/Head.astro
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Meta tags shared by Because pages: description, canonical, Open Graph, JSON-LD,
|
|
4
|
+
* plus the strategy's question colours as zero-specificity --bc-q-* tokens.
|
|
5
|
+
*/
|
|
6
|
+
import { graph } from "virtual:because/data";
|
|
7
|
+
import { DEFAULT_STRATEGY, strategyTokens } from "@because-dev/core";
|
|
3
8
|
interface Props {
|
|
4
9
|
title: string;
|
|
5
10
|
description: string;
|
|
@@ -17,3 +22,4 @@ const canonical = Astro.site ? new URL(Astro.url.pathname, Astro.site).href : un
|
|
|
17
22
|
{canonical && <meta property="og:url" content={canonical} />}
|
|
18
23
|
<meta name="twitter:card" content="summary" />
|
|
19
24
|
{jsonLd.map((data) => <script type="application/ld+json" set:html={JSON.stringify(data)} />)}
|
|
25
|
+
<style is:inline set:html={strategyTokens(graph.strategy ?? DEFAULT_STRATEGY)} />
|
|
@@ -32,7 +32,7 @@ const { idea } = page;
|
|
|
32
32
|
<div class="bc-body" set:html={page.html} />
|
|
33
33
|
|
|
34
34
|
{page.questions.map((q) => (
|
|
35
|
-
<section class="bc-question" data-question-type={q.type} id={q.type}>
|
|
35
|
+
<section class="bc-question" data-question-type={q.type} id={q.type} style={`--qc: var(--bc-q-${q.type})`}>
|
|
36
36
|
<h2>{q.label}</h2>
|
|
37
37
|
{q.answers.map((a) => (
|
|
38
38
|
<div class="bc-answer">
|
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
* Full-screen idea page: the map fills the window and the idea's text sits in
|
|
4
4
|
* a panel on the right (below the map on phones). Without JavaScript there is
|
|
5
5
|
* no map and the text panel becomes an ordinary centred article.
|
|
6
|
+
*
|
|
7
|
+
* The default shell for idea pages. Styles are low-specificity (:where) and
|
|
8
|
+
* driven by --bc-* tokens, and no fonts are loaded, so a site can restyle it
|
|
9
|
+
* with its own CSS, or replace it entirely with the integration's `shell` option.
|
|
6
10
|
*/
|
|
7
11
|
interface Props {
|
|
8
12
|
title: string;
|
|
13
|
+
description?: string;
|
|
9
14
|
/** Home link in the top bar. */
|
|
10
15
|
homeHref: string;
|
|
11
16
|
homeLabel: string;
|
|
@@ -19,91 +24,77 @@ const { title, homeHref, homeLabel, indexHref } = Astro.props;
|
|
|
19
24
|
<meta charset="utf-8" />
|
|
20
25
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
21
26
|
<title>{title}</title>
|
|
22
|
-
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
23
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
24
|
-
<link
|
|
25
|
-
rel="stylesheet"
|
|
26
|
-
href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,500;12..96,700&family=Source+Serif+4:opsz,wght@8..60,400;8..60,500&family=JetBrains+Mono:wght@400;500&display=swap"
|
|
27
|
-
/>
|
|
28
27
|
<slot name="head" />
|
|
29
28
|
<style is:global>
|
|
30
|
-
:root {
|
|
29
|
+
:where(:root) {
|
|
31
30
|
--bc-board: #eef1ef; --bc-card: #ffffff; --bc-ink: #18201d; --bc-muted: #5b6863; --bc-line: #d3dbd6;
|
|
32
31
|
--bc-accent: #1f5f4a; --bc-bar: rgba(255,255,255,.93);
|
|
33
|
-
--bc-display: "Bricolage Grotesque", "Helvetica Neue", Arial, sans-serif;
|
|
34
|
-
--bc-text: "Source Serif 4", Georgia, "Times New Roman", serif;
|
|
35
|
-
--bc-mono: "JetBrains Mono", ui-monospace, Menlo, Consolas, monospace;
|
|
36
32
|
--bc-side: 420px;
|
|
37
33
|
color-scheme: light;
|
|
38
34
|
}
|
|
39
35
|
@media (prefers-color-scheme: dark) {
|
|
40
|
-
:root:not([data-theme="light"]) {
|
|
36
|
+
:where(:root:not([data-theme="light"])) {
|
|
41
37
|
--bc-board: #101513; --bc-card: #1a211e; --bc-ink: #e6ece9; --bc-muted: #95a39d; --bc-line: #2e3934;
|
|
42
38
|
--bc-accent: #7fcfae; --bc-bar: rgba(22,28,26,.93); color-scheme: dark;
|
|
43
39
|
}
|
|
44
40
|
}
|
|
45
|
-
:root[data-theme="dark"] {
|
|
41
|
+
:where(:root[data-theme="dark"]) {
|
|
46
42
|
--bc-board: #101513; --bc-card: #1a211e; --bc-ink: #e6ece9; --bc-muted: #95a39d; --bc-line: #2e3934;
|
|
47
43
|
--bc-accent: #7fcfae; --bc-bar: rgba(22,28,26,.93); color-scheme: dark;
|
|
48
44
|
}
|
|
49
|
-
* { box-sizing: border-box; }
|
|
50
|
-
html, body { height: 100%; }
|
|
51
|
-
body { margin: 0; background: var(--bc-board); color: var(--bc-ink); font-family: var(--bc-text); display: flex; flex-direction: column; }
|
|
52
|
-
:focus-visible { outline: 2px solid var(--bc-accent); outline-offset: 3px; }
|
|
45
|
+
:where(*) { box-sizing: border-box; }
|
|
46
|
+
:where(html, body) { height: 100%; }
|
|
47
|
+
:where(body) { margin: 0; background: var(--bc-board); color: var(--bc-ink); font-family: var(--bc-text, system-ui, sans-serif); display: flex; flex-direction: column; }
|
|
48
|
+
:where(:focus-visible) { outline: 2px solid var(--bc-accent); outline-offset: 3px; }
|
|
53
49
|
|
|
54
|
-
.bc-top {
|
|
50
|
+
:where(.bc-top) {
|
|
55
51
|
display: flex; align-items: center; gap: 8px 18px; flex-wrap: wrap; z-index: 3;
|
|
56
52
|
padding: 10px max(16px, env(safe-area-inset-right)) 10px max(16px, env(safe-area-inset-left));
|
|
57
53
|
background: var(--bc-bar); border-bottom: 1px solid var(--bc-line);
|
|
58
54
|
}
|
|
59
|
-
.bc-top a { color: var(--bc-ink); text-decoration: none; }
|
|
60
|
-
.bc-top .bc-home { font-family: var(--bc-display); font-weight: 700; font-size: 19px; letter-spacing: -0.01em; }
|
|
61
|
-
.bc-top .bc-nav { font-family: var(--bc-mono); font-size: 12px; color: var(--bc-muted); }
|
|
62
|
-
.bc-top .bc-nav:hover { color: var(--bc-ink); }
|
|
63
|
-
.bc-top .bc-toggle { margin-left: auto; font: 12px var(--bc-mono); color: var(--bc-ink); background: var(--bc-card);
|
|
55
|
+
:where(.bc-top a) { color: var(--bc-ink); text-decoration: none; }
|
|
56
|
+
:where(.bc-top .bc-home) { font-family: var(--bc-display, var(--bc-text, inherit)); font-weight: 700; font-size: 19px; letter-spacing: -0.01em; }
|
|
57
|
+
:where(.bc-top .bc-nav) { font-family: var(--bc-mono, ui-monospace, Menlo, Consolas, monospace); font-size: 12px; color: var(--bc-muted); }
|
|
58
|
+
:where(.bc-top .bc-nav:hover) { color: var(--bc-ink); }
|
|
59
|
+
:where(.bc-top .bc-toggle) { margin-left: auto; font: 12px var(--bc-mono, ui-monospace, Menlo, Consolas, monospace); color: var(--bc-ink); background: var(--bc-card);
|
|
64
60
|
border: 1px solid var(--bc-line); border-radius: 6px; padding: 5px 9px; cursor: pointer; display: none; }
|
|
65
|
-
.bc-top .bc-toggle:hover { border-color: var(--bc-muted); }
|
|
61
|
+
:where(.bc-top .bc-toggle:hover) { border-color: var(--bc-muted); }
|
|
66
62
|
|
|
67
|
-
.bc-shell { flex: 1; min-height: 0; display: flex; }
|
|
68
|
-
.bc-shell .bc-map-root { display: none; }
|
|
69
|
-
.bc-side { overflow-y: auto; background: var(--bc-card); }
|
|
63
|
+
:where(.bc-shell) { flex: 1; min-height: 0; display: flex; }
|
|
64
|
+
:where(.bc-shell .bc-map-root) { display: none; }
|
|
65
|
+
:where(.bc-side) { overflow-y: auto; background: var(--bc-card); }
|
|
70
66
|
|
|
71
67
|
/* Without the map (no JavaScript, or map: false): an ordinary article. */
|
|
72
|
-
.bc-shell:not(.has-map) .bc-side { flex: 1; }
|
|
73
|
-
.bc-shell:not(.has-map) .bc-idea { max-width: 42rem; margin: 0 auto; }
|
|
68
|
+
:where(.bc-shell:not(.has-map) .bc-side) { flex: 1; }
|
|
69
|
+
:where(.bc-shell:not(.has-map) .bc-idea) { max-width: 42rem; margin: 0 auto; }
|
|
74
70
|
|
|
75
71
|
/* With the map: map fills the window, text in a panel on the right. */
|
|
76
|
-
.has-map .bc-map-root { display: block; flex: 1; min-width: 0; position: relative; }
|
|
77
|
-
.has-map .bc-side { width: var(--bc-side); flex: none; border-left: 1px solid var(--bc-line); }
|
|
78
|
-
body:has(.has-map) .bc-toggle { display: inline-block; }
|
|
79
|
-
.bc-shell.has-map.text-hidden .bc-side { display: none; }
|
|
72
|
+
:where(.has-map .bc-map-root) { display: block; flex: 1; min-width: 0; position: relative; }
|
|
73
|
+
:where(.has-map .bc-side) { width: var(--bc-side); flex: none; border-left: 1px solid var(--bc-line); }
|
|
74
|
+
:where(body:has(.has-map) .bc-toggle) { display: inline-block; }
|
|
75
|
+
:where(.bc-shell.has-map.text-hidden .bc-side) { display: none; }
|
|
80
76
|
|
|
81
77
|
@media (max-width: 760px) {
|
|
82
|
-
body { height: auto; min-height: 100%; }
|
|
83
|
-
.has-map { flex-direction: column; }
|
|
84
|
-
.has-map .bc-map-root { flex: none; height: 62vh; }
|
|
85
|
-
.has-map .bc-side { width: auto; border-left: 0; border-top: 1px solid var(--bc-line); overflow: visible; }
|
|
86
|
-
body:has(.has-map) .bc-toggle { display: none; }
|
|
78
|
+
:where(body) { height: auto; min-height: 100%; }
|
|
79
|
+
:where(.has-map) { flex-direction: column; }
|
|
80
|
+
:where(.has-map .bc-map-root) { flex: none; height: 62vh; }
|
|
81
|
+
:where(.has-map .bc-side) { width: auto; border-left: 0; border-top: 1px solid var(--bc-line); overflow: visible; }
|
|
82
|
+
:where(body:has(.has-map) .bc-toggle) { display: none; }
|
|
87
83
|
}
|
|
88
84
|
|
|
89
|
-
.bc-idea { padding: 22px 24px 48px; font-size: 16.5px; line-height: 1.6; }
|
|
90
|
-
.bc-idea h1 { font-family: var(--bc-display); font-weight: 700; font-size: 28px; line-height: 1.15; letter-spacing: -0.01em; margin: 6px 0 4px; text-wrap: balance; }
|
|
91
|
-
.bc-idea h2 { font-family: var(--bc-display); font-weight: 700; font-size: 15px; margin: 28px 0 6px; color: var(--qc, var(--bc-ink)); }
|
|
92
|
-
.bc-idea h3 { font-family: var(--bc-display); font-weight: 500; font-size: 16px; margin: 10px 0 0; }
|
|
93
|
-
.bc-idea a { color: var(--bc-accent); }
|
|
94
|
-
.bc-idea .bc-meta, .bc-idea .bc-answer p { color: var(--bc-muted); margin: 2px 0; font-size: 14.5px; }
|
|
95
|
-
.bc-idea .bc-meta { font-family: var(--bc-mono); font-size: 11.5px; }
|
|
96
|
-
.bc-idea .bc-trail ol { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 4px 6px; font-family: var(--bc-mono); font-size: 11.5px; color: var(--bc-muted); }
|
|
97
|
-
.bc-idea .bc-trail li + li::before { content: "›"; margin-right: 6px; }
|
|
98
|
-
.bc-idea .bc-trail a { color: var(--bc-muted); }
|
|
99
|
-
.bc-idea .bc-question { border-left: 3px solid var(--qc, var(--bc-line)); padding-left: 12px; margin-top: 22px; }
|
|
100
|
-
.bc-idea .bc-question h2 { margin-top: 0; }
|
|
101
|
-
.bc-idea
|
|
102
|
-
.bc-idea [data-question-type="why"] { --qc: var(--bc-q-why, #1f6fb2); }
|
|
103
|
-
.bc-idea [data-question-type="whynot"] { --qc: var(--bc-q-whynot, #b2452f); }
|
|
104
|
-
.bc-idea [data-question-type="evidence"] { --qc: var(--bc-q-evidence, #2f7d4a); }
|
|
105
|
-
.bc-idea [data-question-type="sowhat"] { --qc: var(--bc-q-sowhat, #a2680f); }
|
|
106
|
-
.bc-idea .bc-leads-here ul { padding-left: 18px; }
|
|
85
|
+
:where(.bc-idea) { padding: 22px 24px 48px; font-size: 16.5px; line-height: 1.6; }
|
|
86
|
+
:where(.bc-idea h1) { font-family: var(--bc-display, var(--bc-text, inherit)); font-weight: 700; font-size: 28px; line-height: 1.15; letter-spacing: -0.01em; margin: 6px 0 4px; text-wrap: balance; }
|
|
87
|
+
:where(.bc-idea h2) { font-family: var(--bc-display, var(--bc-text, inherit)); font-weight: 700; font-size: 15px; margin: 28px 0 6px; color: var(--qc, var(--bc-ink)); }
|
|
88
|
+
:where(.bc-idea h3) { font-family: var(--bc-display, var(--bc-text, inherit)); font-weight: 500; font-size: 16px; margin: 10px 0 0; }
|
|
89
|
+
:where(.bc-idea a) { color: var(--bc-accent); }
|
|
90
|
+
:where(.bc-idea .bc-meta, .bc-idea .bc-answer p) { color: var(--bc-muted); margin: 2px 0; font-size: 14.5px; }
|
|
91
|
+
:where(.bc-idea .bc-meta) { font-family: var(--bc-mono, ui-monospace, Menlo, Consolas, monospace); font-size: 11.5px; }
|
|
92
|
+
:where(.bc-idea .bc-trail ol) { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 4px 6px; font-family: var(--bc-mono, ui-monospace, Menlo, Consolas, monospace); font-size: 11.5px; color: var(--bc-muted); }
|
|
93
|
+
:where(.bc-idea .bc-trail li + li::before) { content: "›"; margin-right: 6px; }
|
|
94
|
+
:where(.bc-idea .bc-trail a) { color: var(--bc-muted); }
|
|
95
|
+
:where(.bc-idea .bc-question) { border-left: 3px solid var(--qc, var(--bc-line)); padding-left: 12px; margin-top: 22px; }
|
|
96
|
+
:where(.bc-idea .bc-question h2) { margin-top: 0; }
|
|
97
|
+
:where(.bc-idea .bc-leads-here ul) { padding-left: 18px; }
|
|
107
98
|
</style>
|
|
108
99
|
</head>
|
|
109
100
|
<body>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* The list page at `base`: posts newest first, then every other idea by title,
|
|
4
|
+
* split into pages of `pageSize` (/ideas/, /ideas/page/2/, …).
|
|
5
|
+
*/
|
|
6
|
+
import Layout from "virtual:because/layout";
|
|
7
|
+
import { graph } from "virtual:because/data";
|
|
8
|
+
import { config } from "virtual:because/config";
|
|
9
|
+
import { paginate, sortedIdeas, sortedPosts } from "@because-dev/core";
|
|
10
|
+
import { ideaHref, joinPath, pageTitle } from "../dist/helpers.js";
|
|
11
|
+
import Head from "./Head.astro";
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
page: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const ctx = { siteBase: import.meta.env.BASE_URL, base: config.base };
|
|
18
|
+
const posts = sortedPosts(graph);
|
|
19
|
+
const others = sortedIdeas(graph).filter((i) => i.kind !== "post");
|
|
20
|
+
const current = paginate([...posts, ...others], Astro.props.page, config.pageSize);
|
|
21
|
+
const pagePosts = current.items.filter((i) => i.kind === "post");
|
|
22
|
+
const pageIdeas = current.items.filter((i) => i.kind !== "post");
|
|
23
|
+
const pageHref = (n: number) => (n <= 1 ? joinPath(ctx.siteBase, config.base, "/") : joinPath(ctx.siteBase, config.base, "page", String(n), "/"));
|
|
24
|
+
|
|
25
|
+
const heading = "Ideas";
|
|
26
|
+
const title = pageTitle(current.page > 1 ? `${heading} (page ${current.page})` : heading, config.siteName);
|
|
27
|
+
const description = `${posts.length} posts and ${graph.ideas.length} linked ideas.`;
|
|
28
|
+
---
|
|
29
|
+
<Layout title={title} description={description}>
|
|
30
|
+
<Head slot="head" title={heading} description={description} type="website" />
|
|
31
|
+
{current.page > 1 && <link slot="head" rel="prev" href={pageHref(current.page - 1)} />}
|
|
32
|
+
{current.page < current.pageCount && <link slot="head" rel="next" href={pageHref(current.page + 1)} />}
|
|
33
|
+
<h1>{heading}</h1>
|
|
34
|
+
{pagePosts.length > 0 && (
|
|
35
|
+
<section class="bc-posts">
|
|
36
|
+
<h2>Posts</h2>
|
|
37
|
+
{pagePosts.map((p) => (
|
|
38
|
+
<div class="bc-answer">
|
|
39
|
+
<h3><a href={ideaHref(ctx, p)}>{p.title}</a></h3>
|
|
40
|
+
{p.summary && <p>{p.summary}</p>}
|
|
41
|
+
</div>
|
|
42
|
+
))}
|
|
43
|
+
</section>
|
|
44
|
+
)}
|
|
45
|
+
{pageIdeas.length > 0 && (
|
|
46
|
+
<section class="bc-all">
|
|
47
|
+
<h2>All ideas</h2>
|
|
48
|
+
<ul>{pageIdeas.map((i) => <li><a href={ideaHref(ctx, i)}>{i.title}</a></li>)}</ul>
|
|
49
|
+
</section>
|
|
50
|
+
)}
|
|
51
|
+
{current.pageCount > 1 && (
|
|
52
|
+
<nav class="bc-pagination" aria-label="Pages">
|
|
53
|
+
{current.page > 1 && <a rel="prev" href={pageHref(current.page - 1)}>Newer</a>}
|
|
54
|
+
<span>Page {current.page} of {current.pageCount}</span>
|
|
55
|
+
{current.page < current.pageCount && <a rel="next" href={pageHref(current.page + 1)}>Older</a>}
|
|
56
|
+
</nav>
|
|
57
|
+
)}
|
|
58
|
+
</Layout>
|
package/dist/helpers.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Graph, type Idea, type IdeaSummary } from "@because-dev/core";
|
|
2
|
+
export { paginate, type Page } from "@because-dev/core";
|
|
2
3
|
/** Join URL path segments with single slashes; keeps a trailing slash if the last segment has one. */
|
|
3
4
|
export declare function joinPath(...parts: string[]): string;
|
|
4
5
|
export interface PageContext {
|
|
@@ -62,3 +63,29 @@ export declare function ideaHead(page: IdeaPage, siteName: string | undefined, s
|
|
|
62
63
|
description: string;
|
|
63
64
|
jsonLd: unknown[];
|
|
64
65
|
};
|
|
66
|
+
/** A post in the ready-made list exported by `virtual:because/data`. */
|
|
67
|
+
export interface PostEntry {
|
|
68
|
+
id: string;
|
|
69
|
+
slug: string;
|
|
70
|
+
title: string;
|
|
71
|
+
summary: string;
|
|
72
|
+
/** ISO date from front matter, if any; format it however your site likes. */
|
|
73
|
+
date?: string;
|
|
74
|
+
updated?: string;
|
|
75
|
+
href: string;
|
|
76
|
+
/** Ideas reachable from the post by following its questions, the post included. */
|
|
77
|
+
ideaCount: number;
|
|
78
|
+
}
|
|
79
|
+
/** An idea that isn't a post, in the list exported by `virtual:because/data`. */
|
|
80
|
+
export interface IdeaEntry {
|
|
81
|
+
id: string;
|
|
82
|
+
slug: string;
|
|
83
|
+
title: string;
|
|
84
|
+
summary: string;
|
|
85
|
+
href: string;
|
|
86
|
+
}
|
|
87
|
+
/** Posts newest first (undated last), and every other idea by title. */
|
|
88
|
+
export declare function listings(graph: Graph, ctx: PageContext): {
|
|
89
|
+
posts: PostEntry[];
|
|
90
|
+
ideas: IdeaEntry[];
|
|
91
|
+
};
|
package/dist/helpers.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { indexGraph, neighbourhoodPath, questionLabel, summarizeIdea, trailTo, } from "@because-dev/core";
|
|
2
|
-
import { createRenderer } from "@because-dev/parser";
|
|
1
|
+
import { indexGraph, reachableFrom, neighbourhoodPath, questionLabel, summarizeIdea, trailTo, } from "@because-dev/core";
|
|
2
|
+
import { createRenderer } from "@because-dev/core/parser";
|
|
3
|
+
// For sites building their own paged lists from virtual:because/data.
|
|
4
|
+
export { paginate } from "@because-dev/core";
|
|
3
5
|
/** Join URL path segments with single slashes; keeps a trailing slash if the last segment has one. */
|
|
4
6
|
export function joinPath(...parts) {
|
|
5
7
|
const joined = parts.filter(Boolean).join("/").replace(/\/{2,}/g, "/");
|
|
@@ -46,12 +48,12 @@ function buildSite(graph, ctx) {
|
|
|
46
48
|
const questions = (index.outgoing.get(idea.id) ?? []).map((q) => ({
|
|
47
49
|
id: q.id,
|
|
48
50
|
type: q.type,
|
|
49
|
-
label: questionLabel(q),
|
|
51
|
+
label: questionLabel(q, graph.strategy),
|
|
50
52
|
answers: views(q.answers),
|
|
51
53
|
}));
|
|
52
54
|
const leadsHere = (index.incoming.get(idea.id) ?? []).flatMap((q) => {
|
|
53
55
|
const v = view(q.from);
|
|
54
|
-
return v ? [{ ...v, label: questionLabel(q) }] : [];
|
|
56
|
+
return v ? [{ ...v, label: questionLabel(q, graph.strategy) }] : [];
|
|
55
57
|
});
|
|
56
58
|
return {
|
|
57
59
|
idea,
|
|
@@ -103,4 +105,23 @@ export function ideaHead(page, siteName, site) {
|
|
|
103
105
|
}
|
|
104
106
|
return { title: pageTitle(idea.title, siteName), description, jsonLd };
|
|
105
107
|
}
|
|
108
|
+
/** Posts newest first (undated last), and every other idea by title. */
|
|
109
|
+
export function listings(graph, ctx) {
|
|
110
|
+
const index = indexGraph(graph);
|
|
111
|
+
const entry = (i) => ({ id: i.id, slug: i.slug, title: i.title, summary: i.summary, href: ideaHref(ctx, i) });
|
|
112
|
+
const posts = graph.ideas
|
|
113
|
+
.filter((i) => i.kind === "post")
|
|
114
|
+
.sort((a, b) => (b.date ?? "").localeCompare(a.date ?? ""))
|
|
115
|
+
.map((p) => ({
|
|
116
|
+
...entry(p),
|
|
117
|
+
...(p.date ? { date: p.date } : {}),
|
|
118
|
+
...(p.updated ? { updated: p.updated } : {}),
|
|
119
|
+
ideaCount: reachableFrom(index, p.id).length,
|
|
120
|
+
}));
|
|
121
|
+
const ideas = graph.ideas
|
|
122
|
+
.filter((i) => i.kind !== "post")
|
|
123
|
+
.sort((a, b) => a.title.localeCompare(b.title))
|
|
124
|
+
.map(entry);
|
|
125
|
+
return { posts, ideas };
|
|
126
|
+
}
|
|
106
127
|
//# sourceMappingURL=helpers.js.map
|
package/dist/helpers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,OAAO,GAIR,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AstroIntegration } from "astro";
|
|
2
|
+
import { type Strategy } from "@because-dev/core";
|
|
2
3
|
export interface BecauseOptions {
|
|
3
4
|
/** Folder of markdown notes, relative to the project root. Default "./ideas". */
|
|
4
5
|
content?: string;
|
|
@@ -20,6 +21,23 @@ export interface BecauseOptions {
|
|
|
20
21
|
* `/// <reference types="@because-dev/astro/virtual" />`).
|
|
21
22
|
*/
|
|
22
23
|
index?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Your own shell for idea pages (with the map on), relative to the project root,
|
|
26
|
+
* e.g. "./src/layouts/IdeaShell.astro". It receives `title`, `description`,
|
|
27
|
+
* `homeHref`, `homeLabel` and `indexHref` props and three slots: "head" (meta
|
|
28
|
+
* tags; render it in <head>), "map" (the map's root element; give it a size) and
|
|
29
|
+
* the default slot (the idea's article). Defaults to the built-in full-screen shell.
|
|
30
|
+
*/
|
|
31
|
+
shell?: string;
|
|
32
|
+
/**
|
|
33
|
+
* The question strategy: "socratic" (default), "toulmin", "five-whys",
|
|
34
|
+
* "six-hats", or your own `Strategy` object (see defineStrategy). One per site.
|
|
35
|
+
*/
|
|
36
|
+
strategy?: string | Strategy;
|
|
37
|
+
/** Items per page for the list page and the paged list files. Default 50. */
|
|
38
|
+
pageSize?: number;
|
|
39
|
+
/** Inject the map's built-in styles. Default true. Set false to style the map yourself. */
|
|
40
|
+
styles?: boolean;
|
|
23
41
|
/** Fail the build on broken links, unanswered questions or orphans. Default false. */
|
|
24
42
|
strict?: boolean;
|
|
25
43
|
/** Include `draft: true` notes. Default: only in `astro dev`. */
|
|
@@ -31,6 +49,8 @@ export interface ResolvedBecauseConfig {
|
|
|
31
49
|
base: string;
|
|
32
50
|
siteName?: string;
|
|
33
51
|
map: boolean;
|
|
52
|
+
styles: boolean;
|
|
53
|
+
pageSize: number;
|
|
34
54
|
}
|
|
35
55
|
export declare function normaliseBase(base: string): string;
|
|
36
56
|
export default function because(options?: BecauseOptions): AstroIntegration;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
import { isAbsolute, resolve } from "node:path";
|
|
3
|
-
import { checkGraph,
|
|
4
|
-
import {
|
|
3
|
+
import { checkGraph, DEFAULT_PAGE_SIZE, graphPath, indexGraph, resolveStrategy, staticFile } from "@because-dev/core";
|
|
4
|
+
import { joinPath, listings } from "./helpers.js";
|
|
5
|
+
import { loadVault } from "@because-dev/core/parser/fs";
|
|
5
6
|
const DATA = "virtual:because/data";
|
|
6
7
|
const CONFIG = "virtual:because/config";
|
|
7
8
|
const LAYOUT = "virtual:because/layout";
|
|
9
|
+
const SHELL = "virtual:because/shell";
|
|
8
10
|
const route = (file) => fileURLToPath(new URL(`../routes/${file}`, import.meta.url));
|
|
9
11
|
export function normaliseBase(base) {
|
|
10
12
|
const trimmed = base.trim().replace(/^\/+|\/+$/g, "");
|
|
@@ -20,13 +22,21 @@ export default function because(options = {}) {
|
|
|
20
22
|
const abs = (p) => (isAbsolute(p) ? p : resolve(root, p));
|
|
21
23
|
const contentDir = abs(options.content ?? "./ideas");
|
|
22
24
|
const layoutPath = options.layout ? abs(options.layout) : route("../components/DefaultLayout.astro");
|
|
25
|
+
const shellPath = options.shell ? abs(options.shell) : route("../components/IdeaShell.astro");
|
|
23
26
|
const drafts = options.drafts ?? command === "dev";
|
|
24
|
-
|
|
27
|
+
// Resolve now so a typo or bad custom strategy fails at startup with a clear message.
|
|
28
|
+
const strategy = resolveStrategy(options.strategy);
|
|
29
|
+
const resolved = { base, siteName: options.siteName, map: options.map ?? true, styles: options.styles ?? true, pageSize: options.pageSize ?? DEFAULT_PAGE_SIZE };
|
|
25
30
|
const read = async () => {
|
|
26
|
-
const { graph, issues } = await loadVault(contentDir, { drafts });
|
|
27
|
-
const
|
|
31
|
+
const { graph, issues } = await loadVault(contentDir, { drafts, strategy });
|
|
32
|
+
const checks = checkGraph(graph);
|
|
33
|
+
const all = [...issues.map((i) => `${i.path}: ${i.message}`), ...checks.filter((i) => i.level === "warning").map((i) => i.message)];
|
|
28
34
|
for (const msg of all)
|
|
29
35
|
logger.warn(msg);
|
|
36
|
+
// Questions the strategy expects but ideas haven't answered yet: prompts for the author, not errors.
|
|
37
|
+
const todos = checks.filter((i) => i.level === "todo");
|
|
38
|
+
if (todos.length)
|
|
39
|
+
logger.info(`${strategy.name}: ${todos.length} expected question(s) not yet asked:\n ${todos.map((i) => i.message).join("\n ")}`);
|
|
30
40
|
logger.info(`${graph.ideas.length} ideas, ${graph.questions.length} questions from ${contentDir}`);
|
|
31
41
|
if (options.strict && command === "build" && all.length) {
|
|
32
42
|
throw new Error(`Because found ${all.length} problem(s) and strict mode is on`);
|
|
@@ -43,16 +53,28 @@ export default function because(options = {}) {
|
|
|
43
53
|
const plugin = {
|
|
44
54
|
name: "because:virtual",
|
|
45
55
|
resolveId(id) {
|
|
46
|
-
if (id === DATA || id === CONFIG || id === LAYOUT)
|
|
56
|
+
if (id === DATA || id === CONFIG || id === LAYOUT || id === SHELL)
|
|
47
57
|
return `\0${id}`;
|
|
48
58
|
},
|
|
49
59
|
async load(id) {
|
|
50
|
-
if (id === `\0${DATA}`)
|
|
51
|
-
|
|
60
|
+
if (id === `\0${DATA}`) {
|
|
61
|
+
const graph = await load();
|
|
62
|
+
const { posts, ideas } = listings(graph, { siteBase: config.base, base });
|
|
63
|
+
return [
|
|
64
|
+
`export const graph = ${JSON.stringify(graph)};`,
|
|
65
|
+
`export const posts = ${JSON.stringify(posts)};`,
|
|
66
|
+
`export const ideas = ${JSON.stringify(ideas)};`,
|
|
67
|
+
`export const pageSize = ${JSON.stringify(resolved.pageSize)};`,
|
|
68
|
+
`const prefix = ${JSON.stringify(joinPath(config.base, base, "/"))};`,
|
|
69
|
+
"export const ideaHref = (slug) => prefix + slug + \"/\";",
|
|
70
|
+
].join("\n");
|
|
71
|
+
}
|
|
52
72
|
if (id === `\0${CONFIG}`)
|
|
53
73
|
return `export const config = ${JSON.stringify(resolved)};`;
|
|
54
74
|
if (id === `\0${LAYOUT}`)
|
|
55
75
|
return `export { default } from ${JSON.stringify(layoutPath)};`;
|
|
76
|
+
if (id === `\0${SHELL}`)
|
|
77
|
+
return `export { default } from ${JSON.stringify(shellPath)};`;
|
|
56
78
|
},
|
|
57
79
|
configureServer(server) {
|
|
58
80
|
server.watcher.add(contentDir);
|
|
@@ -77,7 +99,7 @@ export default function because(options = {}) {
|
|
|
77
99
|
if (!rest.endsWith(".json"))
|
|
78
100
|
return next();
|
|
79
101
|
try {
|
|
80
|
-
const body = staticFile(indexGraph(await load()), rest);
|
|
102
|
+
const body = staticFile(indexGraph(await load()), rest, resolved.pageSize);
|
|
81
103
|
if (body === undefined)
|
|
82
104
|
return next();
|
|
83
105
|
res.setHeader("content-type", "application/json");
|
|
@@ -90,14 +112,15 @@ export default function because(options = {}) {
|
|
|
90
112
|
},
|
|
91
113
|
};
|
|
92
114
|
updateConfig({ vite: { plugins: [plugin] } });
|
|
93
|
-
if (options.index ?? true)
|
|
115
|
+
if (options.index ?? true) {
|
|
94
116
|
injectRoute({ pattern: base || "/", entrypoint: route("index.astro") });
|
|
117
|
+
injectRoute({ pattern: `${base}/page/[page]`, entrypoint: route("index-page.astro") });
|
|
118
|
+
}
|
|
95
119
|
injectRoute({ pattern: `${base}/[slug]`, entrypoint: route(resolved.map ? "idea.astro" : "idea-plain.astro") });
|
|
96
120
|
injectRoute({ pattern: `${base}/${graphPath}`, entrypoint: route("graph.json.ts") });
|
|
97
121
|
injectRoute({ pattern: `${base}/_graph/[id].json`, entrypoint: route("neighbourhood.json.ts") });
|
|
98
122
|
injectRoute({ pattern: `${base}/_ideas/[id].json`, entrypoint: route("idea.json.ts") });
|
|
99
|
-
injectRoute({ pattern: `${base}
|
|
100
|
-
injectRoute({ pattern: `${base}/${ideasIndexPath}`, entrypoint: route("ideas.json.ts") });
|
|
123
|
+
injectRoute({ pattern: `${base}/_pages/[name].json`, entrypoint: route("list-page.json.ts") });
|
|
101
124
|
},
|
|
102
125
|
},
|
|
103
126
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGhD,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@because-dev/astro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Astro integration: one static page per Because idea, the interactive map, and per-idea neighbourhood JSON.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,9 +35,7 @@
|
|
|
35
35
|
"virtual.d.ts"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@because-dev/core": "0.
|
|
39
|
-
"@because-dev/parser": "0.1.1",
|
|
40
|
-
"@because-dev/map": "0.1.1"
|
|
38
|
+
"@because-dev/core": "0.2.0"
|
|
41
39
|
},
|
|
42
40
|
"peerDependencies": {
|
|
43
41
|
"astro": "^5.0.0 || ^6.0.0 || ^7.0.0"
|
package/routes/idea.astro
CHANGED
|
@@ -4,7 +4,7 @@ import { graph } from "virtual:because/data";
|
|
|
4
4
|
import { config } from "virtual:because/config";
|
|
5
5
|
import { createSite, graphBase, ideaHead, joinPath, neighbourhoodHref } from "../dist/helpers.js";
|
|
6
6
|
import Head from "../components/Head.astro";
|
|
7
|
-
import
|
|
7
|
+
import Shell from "virtual:because/shell";
|
|
8
8
|
import IdeaArticle from "../components/IdeaArticle.astro";
|
|
9
9
|
|
|
10
10
|
export const prerender = true;
|
|
@@ -16,16 +16,16 @@ const site = createSite(graph, ctx);
|
|
|
16
16
|
const page = site.page(Astro.params.slug!)!;
|
|
17
17
|
const { idea } = page;
|
|
18
18
|
const { title, description, jsonLd } = ideaHead(page, config.siteName, Astro.site);
|
|
19
|
-
const shellProps = { title, homeHref: ctx.siteBase, homeLabel: config.siteName ?? "Home", indexHref: joinPath(ctx.siteBase, config.base, "/") };
|
|
19
|
+
const shellProps = { title, description, homeHref: ctx.siteBase, homeLabel: config.siteName ?? "Home", indexHref: joinPath(ctx.siteBase, config.base, "/") };
|
|
20
20
|
---
|
|
21
|
-
<!-- The full-screen map, with the idea's text in a side panel. -->
|
|
22
|
-
<
|
|
21
|
+
<!-- The full-screen map, with the idea's text in a side panel (or the site's own shell). -->
|
|
22
|
+
<Shell {...shellProps}>
|
|
23
23
|
<Head slot="head" title={idea.title} description={description} jsonLd={jsonLd} />
|
|
24
|
-
<div slot="map" class="bc-map-root" data-because-map data-because-base={graphBase(ctx)} data-because-focus={idea.id}></div>
|
|
24
|
+
<div slot="map" class="bc-map-root" data-because-map data-because-base={graphBase(ctx)} data-because-focus={idea.id} data-because-styles={config.styles ? undefined : "false"}></div>
|
|
25
25
|
<IdeaArticle page={page} neighbourhood={neighbourhoodHref(ctx, idea.id)} />
|
|
26
|
-
</
|
|
26
|
+
</Shell>
|
|
27
27
|
|
|
28
28
|
<script>
|
|
29
|
-
import { mountAll } from "@because-dev/map/mount";
|
|
29
|
+
import { mountAll } from "@because-dev/core/map/mount";
|
|
30
30
|
mountAll();
|
|
31
31
|
</script>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { GetStaticPaths } from "astro";
|
|
3
|
+
import { graph } from "virtual:because/data";
|
|
4
|
+
import { config } from "virtual:because/config";
|
|
5
|
+
import IdeasIndex from "../components/IdeasIndex.astro";
|
|
6
|
+
|
|
7
|
+
export const prerender = true;
|
|
8
|
+
|
|
9
|
+
/** Pages 2 and up of the list page; page 1 lives at `base` itself. */
|
|
10
|
+
export const getStaticPaths = (() => {
|
|
11
|
+
const pages = Math.max(1, Math.ceil(graph.ideas.length / config.pageSize));
|
|
12
|
+
return Array.from({ length: pages - 1 }, (_, k) => ({ params: { page: String(k + 2) } }));
|
|
13
|
+
}) satisfies GetStaticPaths;
|
|
14
|
+
---
|
|
15
|
+
<IdeasIndex page={Number(Astro.params.page)} />
|
package/routes/index.astro
CHANGED
|
@@ -1,35 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
import
|
|
3
|
-
import { graph } from "virtual:because/data";
|
|
4
|
-
import { config } from "virtual:because/config";
|
|
5
|
-
import { createSite, ideaHref, pageTitle } from "../dist/helpers.js";
|
|
6
|
-
import Head from "../components/Head.astro";
|
|
2
|
+
import IdeasIndex from "../components/IdeasIndex.astro";
|
|
7
3
|
|
|
8
4
|
export const prerender = true;
|
|
9
|
-
|
|
10
|
-
const ctx = { siteBase: import.meta.env.BASE_URL, base: config.base };
|
|
11
|
-
const site = createSite(graph, ctx);
|
|
12
|
-
const heading = "Ideas";
|
|
13
|
-
const title = pageTitle(heading, config.siteName);
|
|
14
|
-
const description = `${site.posts.length} posts and ${graph.ideas.length} linked ideas.`;
|
|
15
|
-
const others = graph.ideas.filter((i) => i.kind !== "post");
|
|
16
5
|
---
|
|
17
|
-
<
|
|
18
|
-
<Head slot="head" title={heading} description={description} type="website" />
|
|
19
|
-
<h1>{heading}</h1>
|
|
20
|
-
<section class="bc-posts">
|
|
21
|
-
<h2>Posts</h2>
|
|
22
|
-
{site.posts.map((p) => (
|
|
23
|
-
<div class="bc-answer">
|
|
24
|
-
<h3><a href={ideaHref(ctx, p)}>{p.title}</a></h3>
|
|
25
|
-
{p.summary && <p>{p.summary}</p>}
|
|
26
|
-
</div>
|
|
27
|
-
))}
|
|
28
|
-
</section>
|
|
29
|
-
{others.length > 0 && (
|
|
30
|
-
<section class="bc-all">
|
|
31
|
-
<h2>All ideas</h2>
|
|
32
|
-
<ul>{others.map((i) => <li><a href={ideaHref(ctx, i)}>{i.title}</a></li>)}</ul>
|
|
33
|
-
</section>
|
|
34
|
-
)}
|
|
35
|
-
</Layout>
|
|
6
|
+
<IdeasIndex page={1} />
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { APIRoute } from "astro";
|
|
2
|
+
import { listPageParams, staticResponse } from "./static-file.js";
|
|
3
|
+
|
|
4
|
+
export const prerender = true;
|
|
5
|
+
export const getStaticPaths = listPageParams;
|
|
6
|
+
|
|
7
|
+
/** One page of posts or ideas; what StaticStore.listPosts and listIdeas fetch. */
|
|
8
|
+
export const GET: APIRoute = ({ params }) => staticResponse(`_pages/${params.name}.json`);
|
package/routes/static-file.ts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
|
-
import { indexGraph, staticFile } from "@because-dev/core";
|
|
1
|
+
import { indexGraph, staticFile, staticPaths } from "@because-dev/core";
|
|
2
2
|
import { graph } from "virtual:because/data";
|
|
3
|
+
import { config } from "virtual:because/config";
|
|
3
4
|
|
|
4
5
|
const index = indexGraph(graph);
|
|
5
6
|
|
|
6
7
|
/** Response for one file of the static layout (see @because-dev/core static.ts). */
|
|
7
8
|
export function staticResponse(path: string): Response {
|
|
8
|
-
const body = staticFile(index, path);
|
|
9
|
+
const body = staticFile(index, path, config.pageSize);
|
|
9
10
|
if (body === undefined) return new Response("Not found", { status: 404 });
|
|
10
11
|
return new Response(JSON.stringify(body), { headers: { "content-type": "application/json" } });
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export const ideaParams = () => graph.ideas.map((idea) => ({ params: { id: idea.id } }));
|
|
15
|
+
|
|
16
|
+
/** Every list page file, e.g. { name: "posts-1" } for _pages/posts-1.json. */
|
|
17
|
+
export const listPageParams = () =>
|
|
18
|
+
staticPaths(index, config.pageSize).flatMap((p) => {
|
|
19
|
+
const m = /^_pages\/(.+)\.json$/.exec(p);
|
|
20
|
+
return m ? [{ params: { name: m[1]! } }] : [];
|
|
21
|
+
});
|
package/virtual.d.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
declare module "virtual:because/data" {
|
|
2
2
|
import type { Graph } from "@because-dev/core";
|
|
3
|
+
import type { IdeaEntry, PostEntry } from "@because-dev/astro/helpers";
|
|
4
|
+
/** The whole published graph. */
|
|
3
5
|
export const graph: Graph;
|
|
6
|
+
/** Posts, newest first (undated last), with href and ideaCount. Dates are ISO; format them yourself. */
|
|
7
|
+
export const posts: PostEntry[];
|
|
8
|
+
/** Every idea that isn't a post, sorted by title. */
|
|
9
|
+
export const ideas: IdeaEntry[];
|
|
10
|
+
/** The integration's pageSize option; page posts or ideas with `paginate` from "@because-dev/astro/helpers". */
|
|
11
|
+
export const pageSize: number;
|
|
12
|
+
/** URL of an idea's page from its slug, including the site's base path. */
|
|
13
|
+
export function ideaHref(slug: string): string;
|
|
4
14
|
}
|
|
5
15
|
|
|
6
16
|
declare module "virtual:because/config" {
|
|
@@ -11,3 +21,8 @@ declare module "virtual:because/layout" {
|
|
|
11
21
|
const Layout: (props: Record<string, unknown>) => unknown;
|
|
12
22
|
export default Layout;
|
|
13
23
|
}
|
|
24
|
+
|
|
25
|
+
declare module "virtual:because/shell" {
|
|
26
|
+
const Shell: (props: Record<string, unknown>) => unknown;
|
|
27
|
+
export default Shell;
|
|
28
|
+
}
|
package/routes/ideas.json.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { APIRoute } from "astro";
|
|
2
|
-
import { ideasIndexPath } from "@because-dev/core";
|
|
3
|
-
import { staticResponse } from "./static-file.js";
|
|
4
|
-
|
|
5
|
-
export const prerender = true;
|
|
6
|
-
|
|
7
|
-
/** Every idea without bodies; what StaticStore.listIdeas fetches. */
|
|
8
|
-
export const GET: APIRoute = () => staticResponse(ideasIndexPath);
|
package/routes/posts.json.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { APIRoute } from "astro";
|
|
2
|
-
import { postsIndexPath } from "@because-dev/core";
|
|
3
|
-
import { staticResponse } from "./static-file.js";
|
|
4
|
-
|
|
5
|
-
export const prerender = true;
|
|
6
|
-
|
|
7
|
-
/** Posts without bodies; what StaticStore.listPosts fetches. */
|
|
8
|
-
export const GET: APIRoute = () => staticResponse(postsIndexPath);
|