@brandon_m_behring/book-scaffold-astro 3.0.0-alpha.4 → 3.0.0-alpha.6
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/ChapterHeader.astro +89 -34
- package/dist/index.mjs +2 -2
- package/package.json +3 -2
- package/scripts/build-labels.mjs +209 -0
- package/dist/types-Cz-pwE1N.d.ts +0 -61
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
/**
|
|
3
3
|
* ChapterHeader — renders the metadata block at the top of every chapter.
|
|
4
|
-
* Surfaces provenance signals the LaTeX book conveyed implicitly:
|
|
5
|
-
* - Part + chapter number (position in the book)
|
|
6
|
-
* - Volatility class (freshness calibration for the reader)
|
|
7
|
-
* - Tools compared (scope signal)
|
|
8
|
-
* - Last verified date (how stale are the claims?)
|
|
9
4
|
*
|
|
10
|
-
*
|
|
5
|
+
* Schema-agnostic: the package supports two profile schemas (academic and
|
|
6
|
+
* tools), so this component renders only the fields that are present on
|
|
7
|
+
* the chapter data. Tools-profile metadata (volatility, last_verified,
|
|
8
|
+
* tools_compared) appears when defined; academic-profile metadata (week,
|
|
9
|
+
* status) appears in its place.
|
|
10
|
+
*
|
|
11
|
+
* Use a chapter card's metadata to calibrate how much trust to place in
|
|
12
|
+
* its specific claims — stable principles age slowly; feature surfaces
|
|
13
|
+
* age fast.
|
|
11
14
|
*/
|
|
12
15
|
import type { CollectionEntry } from 'astro:content';
|
|
13
16
|
import { getFreshness, freshnessLabel } from '../src/lib/freshness';
|
|
@@ -17,45 +20,97 @@ interface Props {
|
|
|
17
20
|
}
|
|
18
21
|
const { data } = Astro.props;
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
// Widen for cross-schema field probing. The consumer's content.config.ts
|
|
24
|
+
// resolves `data` to exactly one of the two schemas at build time; this
|
|
25
|
+
// runtime probe handles either without crashing.
|
|
26
|
+
const d = data as Record<string, unknown>;
|
|
27
|
+
|
|
28
|
+
const hasToolsMeta =
|
|
29
|
+
typeof d.volatility === 'string' &&
|
|
30
|
+
d.last_verified instanceof Date &&
|
|
31
|
+
Array.isArray(d.tools_compared);
|
|
32
|
+
|
|
33
|
+
const hasAcademicMeta =
|
|
34
|
+
typeof d.week === 'number' && typeof d.status === 'string';
|
|
35
|
+
|
|
36
|
+
function formatDate(date: unknown): string {
|
|
37
|
+
return date instanceof Date ? date.toISOString().slice(0, 10) : '';
|
|
22
38
|
}
|
|
23
39
|
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
40
|
+
// Tools-profile freshness — only when all inputs are present + typed.
|
|
41
|
+
const freshness =
|
|
42
|
+
hasToolsMeta && d.last_verified instanceof Date && typeof d.volatility === 'string'
|
|
43
|
+
? getFreshness(d.last_verified, d.volatility as Parameters<typeof getFreshness>[1])
|
|
44
|
+
: null;
|
|
45
|
+
|
|
46
|
+
const freshnessText = freshness
|
|
47
|
+
? freshness.status === 'fresh'
|
|
27
48
|
? 'Fresh'
|
|
28
49
|
: freshness.status === 'verify-soon'
|
|
29
50
|
? 'Verify soon'
|
|
30
|
-
: 'Stale'
|
|
51
|
+
: 'Stale'
|
|
52
|
+
: null;
|
|
53
|
+
|
|
54
|
+
// Display strings, profile-tagged for clarity in markup.
|
|
55
|
+
const partLabel = (() => {
|
|
56
|
+
const p = d.part;
|
|
57
|
+
if (typeof p === 'number') return `Part ${p}`;
|
|
58
|
+
if (typeof p === 'string' && p.length > 0) return `Part: ${p}`;
|
|
59
|
+
return null;
|
|
60
|
+
})();
|
|
61
|
+
const chapterNum =
|
|
62
|
+
typeof d.chapter === 'number' ? `Chapter ${d.chapter}` : null;
|
|
63
|
+
const weekNum = typeof d.week === 'number' ? `Week ${d.week}` : null;
|
|
64
|
+
const statusBadge =
|
|
65
|
+
typeof d.status === 'string' ? d.status.replace(/_/g, ' ') : null;
|
|
66
|
+
const title = typeof d.title === 'string' ? d.title : '(untitled)';
|
|
67
|
+
const description = typeof d.description === 'string' ? d.description : null;
|
|
68
|
+
const toolsCompared = Array.isArray(d.tools_compared)
|
|
69
|
+
? (d.tools_compared as string[])
|
|
70
|
+
: [];
|
|
71
|
+
const lastVerified = d.last_verified instanceof Date ? d.last_verified : null;
|
|
72
|
+
const updated = d.updated instanceof Date ? d.updated : null;
|
|
73
|
+
const volatility = typeof d.volatility === 'string' ? d.volatility : null;
|
|
31
74
|
---
|
|
32
75
|
<header class="chapter-header">
|
|
33
76
|
<div class="chapter-meta">
|
|
34
|
-
<span>
|
|
35
|
-
<span>
|
|
36
|
-
<span>
|
|
37
|
-
|
|
38
|
-
<span
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
77
|
+
{partLabel && <span>{partLabel}</span>}
|
|
78
|
+
{chapterNum && <span>{chapterNum}</span>}
|
|
79
|
+
{weekNum && <span>{weekNum}</span>}
|
|
80
|
+
{statusBadge && (
|
|
81
|
+
<span class="status-badge" data-status={d.status as string}>
|
|
82
|
+
{statusBadge}
|
|
83
|
+
</span>
|
|
84
|
+
)}
|
|
85
|
+
{lastVerified && (
|
|
86
|
+
<span>
|
|
87
|
+
Last verified {formatDate(lastVerified)}
|
|
88
|
+
{freshness && freshnessText && (
|
|
89
|
+
<span
|
|
90
|
+
class="freshness-badge"
|
|
91
|
+
data-status={freshness.status}
|
|
92
|
+
aria-label={freshnessLabel(freshness)}
|
|
93
|
+
title={freshnessLabel(freshness)}
|
|
94
|
+
>{freshnessText}</span>
|
|
95
|
+
)}
|
|
96
|
+
</span>
|
|
97
|
+
)}
|
|
98
|
+
{updated && <span>Updated {formatDate(updated)}</span>}
|
|
54
99
|
</div>
|
|
55
|
-
{
|
|
100
|
+
<h1>{title}</h1>
|
|
101
|
+
{description && <p class="chapter-description">{description}</p>}
|
|
102
|
+
{hasToolsMeta && volatility && (
|
|
103
|
+
<div class="chapter-badge-row">
|
|
104
|
+
<span class="chapter-badge-row-label">Volatility:</span>
|
|
105
|
+
<span class={`volatility-badge volatility-${volatility}`}>
|
|
106
|
+
{volatility}
|
|
107
|
+
</span>
|
|
108
|
+
</div>
|
|
109
|
+
)}
|
|
110
|
+
{hasToolsMeta && toolsCompared.length > 0 && (
|
|
56
111
|
<div class="chapter-badge-row">
|
|
57
112
|
<span class="chapter-badge-row-label">Tools compared:</span>
|
|
58
|
-
{
|
|
113
|
+
{toolsCompared.map((t) => <span class="tool-badge">{t}</span>)}
|
|
59
114
|
</div>
|
|
60
115
|
)}
|
|
61
116
|
</header>
|
package/dist/index.mjs
CHANGED
|
@@ -153,11 +153,11 @@ var ALWAYS_ON_STYLES = [
|
|
|
153
153
|
var TOOLS_ONLY_STYLES = ["convergence.css", "tool-filter.css"];
|
|
154
154
|
var DEFAULT_ROUTES_ALL = [
|
|
155
155
|
{ pattern: "/references", file: "references.astro" },
|
|
156
|
-
{ pattern: "/search", file: "search.astro" }
|
|
156
|
+
{ pattern: "/search", file: "search.astro" },
|
|
157
|
+
{ pattern: "/print", file: "print.astro" }
|
|
157
158
|
];
|
|
158
159
|
var DEFAULT_ROUTES_TOOLS = [
|
|
159
160
|
{ pattern: "/chapters", file: "chapters.astro" },
|
|
160
|
-
{ pattern: "/print", file: "print.astro" },
|
|
161
161
|
{ pattern: "/convergence", file: "convergence.astro" }
|
|
162
162
|
];
|
|
163
163
|
function resolvePage(file) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brandon_m_behring/book-scaffold-astro",
|
|
3
3
|
"description": "Astro 6 + MDX toolkit for long-form technical books. Profile-aware (academic / tools / minimal); ships Tufte typography, KaTeX, BibTeX citations, Pagefind, Cloudflare Workers deploy. See PACKAGE_DESIGN.md for the API contract.",
|
|
4
|
-
"version": "3.0.0-alpha.
|
|
4
|
+
"version": "3.0.0-alpha.6",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Brandon Behring",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"types": "./dist/schemas.d.ts",
|
|
41
41
|
"import": "./dist/schemas.mjs"
|
|
42
42
|
},
|
|
43
|
+
"./package.json": "./package.json",
|
|
43
44
|
"./components/CaseStudy.astro": "./components/CaseStudy.astro",
|
|
44
45
|
"./components/ChapterHeader.astro": "./components/ChapterHeader.astro",
|
|
45
46
|
"./components/ChapterNav.astro": "./components/ChapterNav.astro",
|
|
@@ -109,7 +110,7 @@
|
|
|
109
110
|
"README.md"
|
|
110
111
|
],
|
|
111
112
|
"scripts": {
|
|
112
|
-
"build": "tsup",
|
|
113
|
+
"build": "tsup && rm -f dist/types-*.d.ts",
|
|
113
114
|
"prepublishOnly": "npm run build"
|
|
114
115
|
},
|
|
115
116
|
"peerDependencies": {
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* build-labels.mjs — emit src/data/labels.json for <XRef> resolution.
|
|
4
|
+
*
|
|
5
|
+
* Walks the consumer's `src/content/chapters/**\/*.mdx`, extracts each
|
|
6
|
+
* labelable component invocation (Theorem, Figure, Section, … — see
|
|
7
|
+
* `LABELABLE_TYPES` below), and assigns it a display string of the form
|
|
8
|
+
* `<Type> <chapter>.<n>` (e.g. `Theorem 4.2`), matching the LaTeX `\cref`
|
|
9
|
+
* convention. The resulting map is consumed by XRef.astro via
|
|
10
|
+
* `import.meta.glob('/src/data/labels.json', { eager: true })`.
|
|
11
|
+
*
|
|
12
|
+
* Per-chapter, per-type counter: each chapter resets the counter, so two
|
|
13
|
+
* chapters can both have `Theorem 1` without colliding. The chapter
|
|
14
|
+
* number comes from frontmatter:
|
|
15
|
+
* - tools profile: `chapter` field (number).
|
|
16
|
+
* - academic profile: `week` field (number).
|
|
17
|
+
*
|
|
18
|
+
* Slug used for the href = filename minus `.mdx`. The href shape mirrors
|
|
19
|
+
* the consumer's pages router: `/chapters/<slug>#<id>`. Academic books
|
|
20
|
+
* using `[...slug].astro` get the same shape since Astro slugifies
|
|
21
|
+
* filenames identically.
|
|
22
|
+
*
|
|
23
|
+
* Optional override:
|
|
24
|
+
* <Theorem id="…" label="Custom display" />
|
|
25
|
+
* → labels.json uses "Custom display" instead of the auto-counter.
|
|
26
|
+
*
|
|
27
|
+
* Usage:
|
|
28
|
+
* node scripts/build-labels.mjs
|
|
29
|
+
* book-scaffold build-labels
|
|
30
|
+
*
|
|
31
|
+
* Reads from cwd (the consumer's project root); writes
|
|
32
|
+
* `src/data/labels.json`. Creates `src/data/` if missing.
|
|
33
|
+
*
|
|
34
|
+
* Designed to run in <2 s on a medium book.
|
|
35
|
+
*/
|
|
36
|
+
import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises';
|
|
37
|
+
import { resolve, join, basename, dirname } from 'node:path';
|
|
38
|
+
|
|
39
|
+
const CHAPTERS_DIR = process.env.BOOK_CHAPTERS_DIR ?? 'src/content/chapters';
|
|
40
|
+
const OUTPUT_PATH = process.env.BOOK_LABELS_OUT ?? 'src/data/labels.json';
|
|
41
|
+
|
|
42
|
+
/** Component names that participate in cross-referencing. */
|
|
43
|
+
const LABELABLE_TYPES = [
|
|
44
|
+
'Theorem',
|
|
45
|
+
'Figure',
|
|
46
|
+
'ExampleBox',
|
|
47
|
+
'ResultBox',
|
|
48
|
+
'NoteBox',
|
|
49
|
+
'CaseStudy',
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
/** Display-name prefix used when no `label` override is given. */
|
|
53
|
+
const TYPE_DISPLAY = {
|
|
54
|
+
Theorem: 'Theorem',
|
|
55
|
+
Figure: 'Figure',
|
|
56
|
+
ExampleBox: 'Example',
|
|
57
|
+
ResultBox: 'Result',
|
|
58
|
+
NoteBox: 'Note',
|
|
59
|
+
CaseStudy: 'Case study',
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// ===== Frontmatter parsing =====
|
|
63
|
+
|
|
64
|
+
function parseFrontmatter(source) {
|
|
65
|
+
// Standard MDX/YAML frontmatter: `---\n…\n---`.
|
|
66
|
+
const m = source.match(/^---\n([\s\S]*?)\n---/);
|
|
67
|
+
if (!m) return {};
|
|
68
|
+
const fm = {};
|
|
69
|
+
for (const line of m[1].split('\n')) {
|
|
70
|
+
const kv = line.match(/^(\w+)\s*:\s*(.+?)\s*$/);
|
|
71
|
+
if (!kv) continue;
|
|
72
|
+
const [, key, raw] = kv;
|
|
73
|
+
// Strip quotes; coerce numeric scalars.
|
|
74
|
+
let val = raw.replace(/^["']|["']$/g, '');
|
|
75
|
+
if (/^-?\d+$/.test(val)) val = parseInt(val, 10);
|
|
76
|
+
fm[key] = val;
|
|
77
|
+
}
|
|
78
|
+
return fm;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function chapterNumberOf(frontmatter) {
|
|
82
|
+
// Tools profile uses `chapter`; academic uses `week`. Prefer chapter.
|
|
83
|
+
if (typeof frontmatter.chapter === 'number') return frontmatter.chapter;
|
|
84
|
+
if (typeof frontmatter.week === 'number') return frontmatter.week;
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ===== Component-invocation parsing =====
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Match opening tags of any labelable component, capturing the attrs blob.
|
|
92
|
+
* Conservative regex: only matches `<ComponentName ... />` or
|
|
93
|
+
* `<ComponentName ...>` (not closing tags, not self-references in prose).
|
|
94
|
+
*/
|
|
95
|
+
function buildTagRegex() {
|
|
96
|
+
const names = LABELABLE_TYPES.join('|');
|
|
97
|
+
return new RegExp(`<(${names})\\b([^>]*?)\\/?>`, 'g');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function extractAttr(attrsBlob, name) {
|
|
101
|
+
// `name="value"` or `name='value'` or `name={value}`.
|
|
102
|
+
const dq = attrsBlob.match(new RegExp(`${name}="([^"]*)"`));
|
|
103
|
+
if (dq) return dq[1];
|
|
104
|
+
const sq = attrsBlob.match(new RegExp(`${name}='([^']*)'`));
|
|
105
|
+
if (sq) return sq[1];
|
|
106
|
+
const ex = attrsBlob.match(new RegExp(`${name}=\\{([^}]*)\\}`));
|
|
107
|
+
if (ex) return ex[1].trim().replace(/^["'`]|["'`]$/g, '');
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ===== Filesystem walk =====
|
|
112
|
+
|
|
113
|
+
async function walkChapters(dir) {
|
|
114
|
+
const out = [];
|
|
115
|
+
let entries;
|
|
116
|
+
try {
|
|
117
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
118
|
+
} catch (err) {
|
|
119
|
+
if (err.code === 'ENOENT') return out;
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
for (const e of entries) {
|
|
123
|
+
const path = join(dir, e.name);
|
|
124
|
+
if (e.isDirectory()) {
|
|
125
|
+
out.push(...(await walkChapters(path)));
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (!e.isFile()) continue;
|
|
129
|
+
if (!/\.mdx?$/.test(e.name)) continue;
|
|
130
|
+
if (e.name.startsWith('_')) continue; // hidden by convention
|
|
131
|
+
out.push(path);
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ===== Main =====
|
|
137
|
+
|
|
138
|
+
async function main() {
|
|
139
|
+
const cwd = process.cwd();
|
|
140
|
+
const chaptersDir = resolve(cwd, CHAPTERS_DIR);
|
|
141
|
+
const files = await walkChapters(chaptersDir);
|
|
142
|
+
|
|
143
|
+
const labels = {};
|
|
144
|
+
const tagRegex = buildTagRegex();
|
|
145
|
+
let totalIds = 0;
|
|
146
|
+
let chaptersWithIds = 0;
|
|
147
|
+
|
|
148
|
+
for (const file of files) {
|
|
149
|
+
const source = await readFile(file, 'utf8');
|
|
150
|
+
const fm = parseFrontmatter(source);
|
|
151
|
+
const chapterNum = chapterNumberOf(fm);
|
|
152
|
+
const slug = basename(file).replace(/\.mdx?$/, '');
|
|
153
|
+
|
|
154
|
+
// Per-chapter counters reset for each file.
|
|
155
|
+
const counters = {};
|
|
156
|
+
let foundInChapter = 0;
|
|
157
|
+
|
|
158
|
+
for (const match of source.matchAll(tagRegex)) {
|
|
159
|
+
const [, type, attrs] = match;
|
|
160
|
+
const id = extractAttr(attrs, 'id');
|
|
161
|
+
if (!id) continue;
|
|
162
|
+
|
|
163
|
+
counters[type] = (counters[type] ?? 0) + 1;
|
|
164
|
+
foundInChapter += 1;
|
|
165
|
+
totalIds += 1;
|
|
166
|
+
|
|
167
|
+
const labelOverride = extractAttr(attrs, 'label');
|
|
168
|
+
const display =
|
|
169
|
+
labelOverride ??
|
|
170
|
+
(chapterNum != null
|
|
171
|
+
? `${TYPE_DISPLAY[type]} ${chapterNum}.${counters[type]}`
|
|
172
|
+
: `${TYPE_DISPLAY[type]} ${counters[type]}`);
|
|
173
|
+
|
|
174
|
+
if (labels[id]) {
|
|
175
|
+
// Duplicate id — surface but don't fail; consumer's validator
|
|
176
|
+
// catches collisions with full diagnostic context.
|
|
177
|
+
process.stderr.write(
|
|
178
|
+
`build-labels: WARN duplicate id "${id}" (first in ` +
|
|
179
|
+
`${labels[id].href.split('#')[0]}, now in ${slug})\n`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
labels[id] = {
|
|
183
|
+
href: `/chapters/${slug}#${id}`,
|
|
184
|
+
display,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (foundInChapter > 0) chaptersWithIds += 1;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Emit deterministic output: keys sorted alphabetically.
|
|
192
|
+
const sorted = {};
|
|
193
|
+
for (const k of Object.keys(labels).sort()) sorted[k] = labels[k];
|
|
194
|
+
|
|
195
|
+
const outputPath = resolve(cwd, OUTPUT_PATH);
|
|
196
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
197
|
+
await writeFile(outputPath, JSON.stringify(sorted, null, 2) + '\n', 'utf8');
|
|
198
|
+
|
|
199
|
+
process.stdout.write(
|
|
200
|
+
`build-labels: ${totalIds} id${totalIds === 1 ? '' : 's'} across ` +
|
|
201
|
+
`${chaptersWithIds} chapter${chaptersWithIds === 1 ? '' : 's'} → ` +
|
|
202
|
+
`${OUTPUT_PATH}\n`,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
main().catch((err) => {
|
|
207
|
+
process.stderr.write(`build-labels: fatal: ${err?.message ?? err}\n`);
|
|
208
|
+
process.exit(1);
|
|
209
|
+
});
|
package/dist/types-Cz-pwE1N.d.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { AstroIntegration, AstroUserConfig } from 'astro';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Shared types for @brandon_m_behring/book-scaffold-astro.
|
|
5
|
-
*
|
|
6
|
-
* Public types referenced from PACKAGE_DESIGN.md §4 / §5 / §6. Kept in
|
|
7
|
-
* one place so consumer IntelliSense surfaces a coherent API.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
type BookProfile = 'academic' | 'tools' | 'minimal';
|
|
11
|
-
declare const BOOK_PROFILES: readonly ["academic", "tools", "minimal"];
|
|
12
|
-
/**
|
|
13
|
-
* Options for `defineBookConfig`. See PACKAGE_DESIGN.md §4.
|
|
14
|
-
*
|
|
15
|
-
* Note on the index signature: `AstroUserConfig` carries generic
|
|
16
|
-
* parameters (`Locales`, `SessionDriverName`, fonts) that can't be
|
|
17
|
-
* threaded cleanly through a wrapper. Instead we type the package-
|
|
18
|
-
* specific fields strictly and allow arbitrary AstroUserConfig keys
|
|
19
|
-
* via the index signature — consumer types will lint clean but lose
|
|
20
|
-
* full IDE autocomplete on non-package fields. Acceptable trade.
|
|
21
|
-
*/
|
|
22
|
-
interface BookConfigOptions {
|
|
23
|
-
/** Required. Book's deployed origin (sitemap, canonical, Pagefind). */
|
|
24
|
-
site: string;
|
|
25
|
-
/**
|
|
26
|
-
* Optional. Falls back to `process.env.BOOK_PROFILE`, then `'minimal'`.
|
|
27
|
-
* Explicit param always wins over env.
|
|
28
|
-
*/
|
|
29
|
-
profile?: BookProfile;
|
|
30
|
-
/** Optional. Appended to the package-provided integration list. */
|
|
31
|
-
extraIntegrations?: AstroIntegration[];
|
|
32
|
-
/**
|
|
33
|
-
* Optional. CSS basenames to inject in addition to the profile-resolved
|
|
34
|
-
* set. Cross-profile escape hatch (e.g. an academic book using
|
|
35
|
-
* `<Convergence>`). Example: `['convergence.css']`.
|
|
36
|
-
*/
|
|
37
|
-
extraStyles?: string[];
|
|
38
|
-
/** Optional. Spread-merged into the package-provided markdown config. */
|
|
39
|
-
markdown?: AstroUserConfig['markdown'];
|
|
40
|
-
/** Escape hatch for any other AstroUserConfig field. */
|
|
41
|
-
[key: string]: unknown;
|
|
42
|
-
}
|
|
43
|
-
/** Options for `defineBookSchemas`. See PACKAGE_DESIGN.md §5. */
|
|
44
|
-
interface BookSchemasOptions {
|
|
45
|
-
profile?: BookProfile;
|
|
46
|
-
/** Defaults to `'./src/content/chapters'`. */
|
|
47
|
-
chaptersBase?: string;
|
|
48
|
-
}
|
|
49
|
-
/** Options for the internal `bookScaffoldIntegration`. See PACKAGE_DESIGN.md §6. */
|
|
50
|
-
interface BookScaffoldIntegrationOptions {
|
|
51
|
-
profile: BookProfile;
|
|
52
|
-
extraStyles?: string[];
|
|
53
|
-
}
|
|
54
|
-
/** Raised when the resolved profile is not one of `BOOK_PROFILES`. */
|
|
55
|
-
declare class BookConfigError extends Error {
|
|
56
|
-
constructor(message: string);
|
|
57
|
-
}
|
|
58
|
-
/** Resolve profile from explicit param → env → default. Throws on invalid. */
|
|
59
|
-
declare function resolveProfile(explicit?: BookProfile): BookProfile;
|
|
60
|
-
|
|
61
|
-
export { BOOK_PROFILES as B, BookConfigError as a, type BookConfigOptions as b, type BookProfile as c, type BookScaffoldIntegrationOptions as d, type BookSchemasOptions as e, resolveProfile as r };
|