@brandon_m_behring/book-scaffold-astro 4.1.0 → 4.1.2
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/PocLayout.astro +1 -6
- package/package.json +1 -1
- package/scripts/build-labels.mjs +11 -2
- package/scripts/validate.mjs +6 -2
- package/scripts/walk-mdx.mjs +68 -2
|
@@ -11,12 +11,7 @@
|
|
|
11
11
|
* Closed `kind` union: discriminated literal type. To add a 6th kind in
|
|
12
12
|
* a future release, expand the union + add a CSS block in poc-layouts.css.
|
|
13
13
|
*/
|
|
14
|
-
export type PocLayoutKind =
|
|
15
|
-
| 'tutorial'
|
|
16
|
-
| 'how-to'
|
|
17
|
-
| 'tldr'
|
|
18
|
-
| 'part-summary'
|
|
19
|
-
| 'cheat-sheet';
|
|
14
|
+
export type PocLayoutKind = 'tutorial' | 'how-to' | 'tldr' | 'part-summary' | 'cheat-sheet';
|
|
20
15
|
|
|
21
16
|
interface Props {
|
|
22
17
|
kind: PocLayoutKind;
|
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": "4.1.
|
|
4
|
+
"version": "4.1.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Brandon Behring",
|
package/scripts/build-labels.mjs
CHANGED
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
* Designed to run in <2 s on a medium book.
|
|
35
35
|
*/
|
|
36
36
|
import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises';
|
|
37
|
-
import { resolve, join, basename, dirname } from 'node:path';
|
|
37
|
+
import { resolve, relative, join, basename, dirname } from 'node:path';
|
|
38
|
+
import { readChaptersBase } from './walk-mdx.mjs';
|
|
38
39
|
|
|
39
40
|
// --help / -h: non-mutating (closes #14).
|
|
40
41
|
const USAGE = `Usage: book-scaffold build-labels
|
|
@@ -56,7 +57,15 @@ if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
|
56
57
|
process.exit(0);
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
// v4.1.1 (closes #63): readChaptersBase honors BOOK_CHAPTERS_DIR env (when set)
|
|
61
|
+
// then parses the consumer's content.config.{ts,mjs,js} for a `chapters`
|
|
62
|
+
// collection `loader.base` override. Multi-guide consumers use
|
|
63
|
+
// `src/content/<guide-slug>/` rather than the Astro 5 default.
|
|
64
|
+
const CHAPTERS_DIR_ABS = await readChaptersBase(process.cwd());
|
|
65
|
+
// build-labels uses CHAPTERS_DIR as a path relative to cwd elsewhere in the
|
|
66
|
+
// script (joined with `walkMdx`). Convert the absolute path back to relative
|
|
67
|
+
// for compatibility with the existing call sites.
|
|
68
|
+
const CHAPTERS_DIR = relative(process.cwd(), CHAPTERS_DIR_ABS) || 'src/content/chapters';
|
|
60
69
|
const OUTPUT_PATH = process.env.BOOK_LABELS_OUT ?? 'src/data/labels.json';
|
|
61
70
|
|
|
62
71
|
/** Component names that participate in cross-referencing. */
|
package/scripts/validate.mjs
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
import { readFile, access } from 'node:fs/promises';
|
|
29
29
|
import { existsSync, readFileSync } from 'node:fs';
|
|
30
30
|
import { resolve, dirname, join } from 'node:path';
|
|
31
|
-
import { walkMdx } from './walk-mdx.mjs';
|
|
31
|
+
import { walkMdx, readChaptersBase } from './walk-mdx.mjs';
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Best-effort .env reader. Mirrors `readEnvFile` in src/types.ts; kept inline
|
|
@@ -95,7 +95,11 @@ const presetFromFlag = presetFlagIdx >= 0 ? argv[presetFlagIdx + 1] : undefined;
|
|
|
95
95
|
// Resolves issue #8 — three reference consumers reported "0 chapter(s) checked"
|
|
96
96
|
// because ROOT was the package directory inside node_modules.
|
|
97
97
|
const ROOT = process.cwd();
|
|
98
|
-
|
|
98
|
+
// v4.1.1 (closes #63): read the consumer's content.config.{ts,mjs,js} to
|
|
99
|
+
// honor `loader.base` overrides (multi-guide pattern uses
|
|
100
|
+
// `src/content/<guide-slug>/` instead of the Astro 5 default).
|
|
101
|
+
// Falls back to `src/content/chapters` when no override / no config file.
|
|
102
|
+
const CHAPTERS_DIR = await readChaptersBase(ROOT);
|
|
99
103
|
const PUBLIC_DIR = resolve(ROOT, 'public');
|
|
100
104
|
const DATA_DIR = resolve(ROOT, 'src/data');
|
|
101
105
|
|
package/scripts/walk-mdx.mjs
CHANGED
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
* Output: relative paths in POSIX form ("subdir/file.mdx"), matching what
|
|
13
13
|
* the previous `glob('**\/*.{md,mdx}', { cwd })` produced.
|
|
14
14
|
*/
|
|
15
|
-
import { readdir } from 'node:fs/promises';
|
|
16
|
-
import {
|
|
15
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
16
|
+
import { existsSync } from 'node:fs';
|
|
17
|
+
import { join, relative, resolve } from 'node:path';
|
|
17
18
|
|
|
18
19
|
export async function* walkMdx(dir, baseDir = dir) {
|
|
19
20
|
let entries;
|
|
@@ -32,3 +33,68 @@ export async function* walkMdx(dir, baseDir = dir) {
|
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Read the consumer's `content.config.ts` (or `.mjs` / `.js`) and extract
|
|
39
|
+
* the `loader.base` path for the `chapters` content collection.
|
|
40
|
+
*
|
|
41
|
+
* v4.1.1 (closes #63): consumers in the multi-guide / multi-book pattern
|
|
42
|
+
* override the chapters dir to `src/content/<guide-slug>` rather than the
|
|
43
|
+
* Astro 5 default `src/content/chapters/`. Without this helper,
|
|
44
|
+
* `book-scaffold validate` + `book-scaffold build-labels` silently report
|
|
45
|
+
* 0 chapters because they walk the default path. This helper parses the
|
|
46
|
+
* consumer's config file and returns the actual base path so both scripts
|
|
47
|
+
* discover the consumer's chapter files.
|
|
48
|
+
*
|
|
49
|
+
* Strategy: regex-parse the source file (avoid runtime import; the file
|
|
50
|
+
* imports from `astro:content` / `astro/loaders` which don't resolve in
|
|
51
|
+
* plain Node). Matches both single- and double-quoted string literals;
|
|
52
|
+
* matches paths with or without the `./` prefix.
|
|
53
|
+
*
|
|
54
|
+
* Returns the resolved absolute path. Falls back to
|
|
55
|
+
* `${projectRoot}/src/content/chapters` when:
|
|
56
|
+
* - content.config.{ts,mjs,js} doesn't exist
|
|
57
|
+
* - the file exists but no `chapters` collection or `loader.base` found
|
|
58
|
+
* - the matched base path uses dynamic forms (variables, template literals)
|
|
59
|
+
* instead of a string literal
|
|
60
|
+
*
|
|
61
|
+
* Honors env override: BOOK_CHAPTERS_DIR (when set) wins over config parse.
|
|
62
|
+
*/
|
|
63
|
+
export async function readChaptersBase(projectRoot) {
|
|
64
|
+
const envOverride = process.env.BOOK_CHAPTERS_DIR;
|
|
65
|
+
if (envOverride) {
|
|
66
|
+
return resolve(projectRoot, envOverride);
|
|
67
|
+
}
|
|
68
|
+
const DEFAULT_BASE = resolve(projectRoot, 'src/content/chapters');
|
|
69
|
+
for (const ext of ['ts', 'mjs', 'js']) {
|
|
70
|
+
const configPath = join(projectRoot, `src/content.config.${ext}`);
|
|
71
|
+
if (!existsSync(configPath)) continue;
|
|
72
|
+
let source;
|
|
73
|
+
try {
|
|
74
|
+
source = await readFile(configPath, 'utf8');
|
|
75
|
+
} catch {
|
|
76
|
+
return DEFAULT_BASE;
|
|
77
|
+
}
|
|
78
|
+
// Look for a `chapters` collection's `loader.base` string. Permissive
|
|
79
|
+
// form: match the `chapters` identifier, then within the next 500
|
|
80
|
+
// chars find `base: 'string'` or `base: "string"`. NOT template
|
|
81
|
+
// literals (which use backticks and may contain ${} interpolation —
|
|
82
|
+
// those fall back to the default since the value is dynamic).
|
|
83
|
+
//
|
|
84
|
+
// Forms matched:
|
|
85
|
+
// - `const chapters = defineCollection({ loader: glob({ base: './foo' }) })`
|
|
86
|
+
// - `export const collections = { chapters: defineCollection({ loader: glob({ base: './foo' }) }) }`
|
|
87
|
+
// - any indentation / line break style
|
|
88
|
+
const re = /\bchapters\b[\s\S]{0,500}?\bbase\s*:\s*'([^']+)'|\bchapters\b[\s\S]{0,500}?\bbase\s*:\s*"([^"]+)"/;
|
|
89
|
+
const m = source.match(re);
|
|
90
|
+
const captured = m && (m[1] || m[2]);
|
|
91
|
+
if (captured) {
|
|
92
|
+
return resolve(projectRoot, captured);
|
|
93
|
+
}
|
|
94
|
+
// File exists but no override found — assume the consumer uses the
|
|
95
|
+
// scaffold's defineBookSchemas() default.
|
|
96
|
+
return DEFAULT_BASE;
|
|
97
|
+
}
|
|
98
|
+
// No content.config.{ts,mjs,js} at all — return the Astro 5 default.
|
|
99
|
+
return DEFAULT_BASE;
|
|
100
|
+
}
|