gutterpress 0.11.23-alpha.1 → 0.11.23-alpha.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.
Files changed (62) hide show
  1. package/README.md +21 -22
  2. package/dist/api/index.d.ts +4 -4
  3. package/dist/api/index.js +16 -22
  4. package/dist/{build-09cd1n7s.js → build-encgnv0c.js} +9 -10
  5. package/dist/{cli-habkc5ng.js → cli-a35w7wzg.js} +5 -4
  6. package/dist/{cli-b0vyjvmt.js → cli-aqh6brew.js} +1 -1
  7. package/dist/{cli-ccp7sxz3.js → cli-evg8sbht.js} +1 -1
  8. package/dist/{cli-nr6j6300.js → cli-kh8v4vvw.js} +1 -1
  9. package/dist/{cli-h17e3mtz.js → cli-pwwfxdgb.js} +385 -10
  10. package/dist/{cli-dtckwda1.js → cli-pz4bn98t.js} +185 -9
  11. package/dist/{cli-dd115fa8.js → cli-sdmv4bba.js} +47 -123
  12. package/dist/{cli-5hkmp2wy.js → cli-xq6djqbj.js} +55 -88
  13. package/dist/{index-r7jcsk6h.js → cli-y28j019d.js} +19 -10
  14. package/dist/cli.js +12 -12
  15. package/dist/{doctor-nddspx4n.js → doctor-5gtpgpd1.js} +3 -3
  16. package/dist/{engine-892swzzc.js → engine-mc5br8g8.js} +1 -2
  17. package/dist/{engine-yjzknt0g.js → engine-tn13gk0b.js} +3 -3
  18. package/dist/{ext-zjpen4yx.js → ext-wn2a5afq.js} +18 -33
  19. package/dist/{extension-restore-w8xt4kbq.js → extension-restore-g8jnmmzq.js} +5 -6
  20. package/dist/{extension-restore-jvs60x9b.js → extension-restore-s3x1618g.js} +4 -4
  21. package/dist/{index-apc8w9w8.js → index-0skhjx3e.js} +180 -1
  22. package/dist/{index-2jpk25en.js → index-2bjcghze.js} +385 -10
  23. package/dist/index-46ycxe6r.js +18 -0
  24. package/dist/{index-0w2azsk1.js → index-4x6y8krm.js} +2 -4
  25. package/dist/{cli-h3phdstf.js → index-5x42amh8.js} +19 -10
  26. package/dist/{index-93pqkvty.js → index-h6yk5tjx.js} +5 -4
  27. package/dist/{index-zrhyb967.js → index-j5z86r4a.js} +335 -349
  28. package/dist/{index-m4hhzg3c.js → index-x3ema2w3.js} +59 -134
  29. package/dist/index.js +17 -23
  30. package/dist/lib/extension-manager.d.ts +16 -43
  31. package/dist/lib/extension-scaffold.d.ts +3 -3
  32. package/dist/lib/extension-specifier.d.ts +23 -6
  33. package/dist/lib/markdown/math.d.ts +1 -1
  34. package/dist/lib/npm-plugin-installer.d.ts +1 -1
  35. package/dist/lib/plugin-vendor.d.ts +11 -3
  36. package/dist/lib/project-gitignore.d.ts +2 -2
  37. package/dist/lib/project-scaffold.d.ts +5 -7
  38. package/dist/lib/project-templates.d.ts +0 -3
  39. package/dist/lib/snippets.d.ts +2 -1
  40. package/dist/lib/style-resolver.d.ts +1 -2
  41. package/dist/{manifest-8f10dr4f.yaml → manifest-eagfhba6.yaml} +2 -0
  42. package/dist/{manifest.schema-9aeaered.json → manifest.schema-t7nx2stg.json} +5 -5
  43. package/dist/{new-xhq8wczn.js → new-mhs46pw3.js} +22 -21
  44. package/dist/{plugins-9en8m9y0.js → plugins-1sxe0dkz.js} +4 -4
  45. package/dist/{plugins-gf0gkjmv.js → plugins-2cek4mz1.js} +2 -2
  46. package/dist/{preflight-negnxg7b.js → preflight-3k2b1x8e.js} +9 -10
  47. package/dist/{preview-cep2s9cg.js → preview-6p9v6rht.js} +9 -10
  48. package/dist/{publish-xery6qc6.js → publish-qgsngy8s.js} +9 -10
  49. package/dist/{source-provider-qbfknhyy.js → source-provider-98k5deda.js} +2 -2
  50. package/dist/{source-provider-2r74a7wp.js → source-provider-knmcam50.js} +2 -2
  51. package/dist/{validate-3d0sgf4g.js → validate-0v7xw6wa.js} +9 -10
  52. package/package.json +1 -1
  53. package/dist/cli-fe43evmf.js +0 -212
  54. package/dist/index-dsvk7q6s.js +0 -229
  55. package/dist/package-97gm31sg.json +0 -10
  56. package/dist/package-gag6ykwn.json +0 -10
  57. package/dist/package-nrhkwf2m.json +0 -10
  58. package/dist/package-zttkgydq.json +0 -10
  59. package/dist/theme-9c58td2d.css +0 -76
  60. package/dist/theme-h5recz6c.css +0 -61
  61. package/dist/theme-j2bagrfx.css +0 -59
  62. package/dist/theme-nn6d53zy.css +0 -71
@@ -2,11 +2,12 @@ import {
2
2
  isExactNpmVersion,
3
3
  isValidNpmPackageName,
4
4
  packageResolutionTargets,
5
+ parseNpmPluginSpec,
5
6
  resolvePackageEntry,
6
7
  resolveVendoredPluginInstallRoot,
7
8
  safePackageTarget,
8
9
  vendoredNpmPluginPackageDir
9
- } from "./index-r7jcsk6h.js";
10
+ } from "./index-5x42amh8.js";
10
11
 
11
12
  // src/lib/markdown/plugins.ts
12
13
  import { existsSync as existsSync2, readFileSync, statSync, unlinkSync } from "node:fs";
@@ -15,6 +16,374 @@ import { isBuiltin } from "node:module";
15
16
  import { resolve as resolve2, join, dirname, basename, extname, isAbsolute, relative, sep } from "node:path";
16
17
  import { pathToFileURL } from "node:url";
17
18
 
19
+ // src/assets/themes/clean-book/theme.css
20
+ var theme_default = `/* =============================================================================
21
+ Clean Book — gutterpress built-in theme (theme.css)
22
+ A calm, classic long-form book look. Change the :root tokens from the book's own stylesheet.
23
+ ============================================================================= */
24
+
25
+ :root {
26
+ --color-ink: #1a1a1a; /* body text */
27
+ --color-ink-muted: #555555; /* captions, secondary text */
28
+ --color-accent: #2b4c7e; /* headings, links */
29
+ --color-paper: #ffffff; /* page background */
30
+ --color-rule: #d9d9d9; /* borders, rules */
31
+
32
+ --font-body: "Georgia", "Times New Roman", serif;
33
+ --font-display: "Georgia", "Times New Roman", serif;
34
+ --font-mono: "Menlo", "Consolas", monospace;
35
+
36
+ --fs-body: 11pt;
37
+ --fs-h1: 2.1rem;
38
+ --fs-h2: 1.5rem;
39
+ --fs-h3: 1.2rem;
40
+ --leading: 1.55;
41
+ }
42
+
43
+ @page {
44
+ size: 6in 9in;
45
+ margin: 0.85in 0.75in;
46
+ }
47
+
48
+ body {
49
+ color: var(--color-ink);
50
+ background: var(--color-paper);
51
+ font-family: var(--font-body);
52
+ line-height: var(--leading);
53
+ font-size: var(--fs-body);
54
+ }
55
+
56
+ h1, h2, h3 {
57
+ font-family: var(--font-display);
58
+ color: var(--color-accent);
59
+ line-height: 1.2;
60
+ font-weight: 600;
61
+ }
62
+ h1 { font-size: var(--fs-h1); margin: 0 0 0.6em; }
63
+ h2 { font-size: var(--fs-h2); margin: 1.4em 0 0.4em; }
64
+ h3 { font-size: var(--fs-h3); margin: 1.2em 0 0.3em; }
65
+
66
+ p { margin: 0 0 0.8em; text-align: justify; hyphens: auto; }
67
+
68
+ a { color: var(--color-accent); text-decoration: none; }
69
+
70
+ blockquote {
71
+ margin: 1em 0; padding: 0.2em 0 0.2em 1em;
72
+ border-left: 3px solid var(--color-rule);
73
+ color: var(--color-ink-muted); font-style: italic;
74
+ }
75
+
76
+ code, pre { font-family: var(--font-mono); font-size: 0.9em; }
77
+
78
+ hr { border: 0; border-top: 1px solid var(--color-rule); margin: 1.5em 0; }
79
+ `;
80
+
81
+ // src/assets/themes/zine/theme.css
82
+ var theme_default2 = `/* =============================================================================
83
+ Zine — gutterpress built-in theme (theme.css)
84
+ High-contrast, punchy, photocopier-friendly. Change the :root tokens from the book's own stylesheet.
85
+ ============================================================================= */
86
+
87
+ :root {
88
+ --color-ink: #000000; /* body text */
89
+ --color-ink-muted: #333333; /* secondary text */
90
+ --color-accent: #000000; /* headings (mono, high ink) */
91
+ --color-paper: #ffffff; /* page background */
92
+ --color-rule: #000000; /* heavy borders, rules */
93
+
94
+ --font-body: "Helvetica Neue", "Arial", sans-serif;
95
+ --font-display: "Impact", "Haettenschweiler", "Arial Black", sans-serif;
96
+ --font-mono: "Courier New", monospace;
97
+
98
+ --fs-body: 10pt;
99
+ --fs-h1: 2.8rem;
100
+ --fs-h2: 1.8rem;
101
+ --fs-h3: 1.3rem;
102
+ --leading: 1.4;
103
+ }
104
+
105
+ @page {
106
+ size: 5.5in 8.5in;
107
+ margin: 0.4in;
108
+ }
109
+
110
+ body {
111
+ color: var(--color-ink);
112
+ background: var(--color-paper);
113
+ font-family: var(--font-body);
114
+ line-height: var(--leading);
115
+ font-size: var(--fs-body);
116
+ }
117
+
118
+ h1, h2, h3 {
119
+ font-family: var(--font-display);
120
+ color: var(--color-accent);
121
+ text-transform: uppercase;
122
+ letter-spacing: -0.01em;
123
+ line-height: 0.95;
124
+ font-weight: 900;
125
+ }
126
+ h1 { font-size: var(--fs-h1); margin: 0 0 0.4em; }
127
+ h2 { font-size: var(--fs-h2); margin: 1.1em 0 0.3em; }
128
+ h3 { font-size: var(--fs-h3); margin: 1em 0 0.25em; }
129
+
130
+ p { margin: 0 0 0.6em; }
131
+
132
+ a { color: var(--color-accent); text-decoration: underline; font-weight: 700; }
133
+
134
+ blockquote {
135
+ margin: 1em 0; padding: 0.5em 0.8em;
136
+ border: 2px solid var(--color-rule);
137
+ font-weight: 700;
138
+ }
139
+
140
+ hr { border: 0; border-top: 3px solid var(--color-rule); margin: 1em 0; }
141
+
142
+ code, pre { font-family: var(--font-mono); font-size: 0.95em; }
143
+ `;
144
+
145
+ // src/assets/themes/technical-doc/theme.css
146
+ var theme_default3 = `/* =============================================================================
147
+ Technical Document — gutterpress built-in theme (theme.css)
148
+ Clean sans-serif manual look with clear hierarchy. Change the :root tokens from the book's own stylesheet.
149
+ ============================================================================= */
150
+
151
+ :root {
152
+ --color-ink: #1c1e21; /* body text */
153
+ --color-ink-muted: #5a5f66; /* captions, secondary text */
154
+ --color-accent: #0b5fff; /* headings, links */
155
+ --color-paper: #ffffff; /* page background */
156
+ --color-tint: #f3f5f9; /* code / note fill */
157
+ --color-rule: #d6dae0; /* borders, rules */
158
+
159
+ --font-body: "Helvetica Neue", "Segoe UI", Arial, sans-serif;
160
+ --font-display: "Helvetica Neue", "Segoe UI", Arial, sans-serif;
161
+ --font-mono: "SFMono-Regular", "Consolas", "Menlo", monospace;
162
+
163
+ --fs-body: 10.5pt;
164
+ --fs-h1: 2.0rem;
165
+ --fs-h2: 1.45rem;
166
+ --fs-h3: 1.15rem;
167
+ --leading: 1.5;
168
+ }
169
+
170
+ @page {
171
+ size: 8.5in 11in;
172
+ margin: 0.9in 1in;
173
+ }
174
+
175
+ body {
176
+ color: var(--color-ink);
177
+ background: var(--color-paper);
178
+ font-family: var(--font-body);
179
+ line-height: var(--leading);
180
+ font-size: var(--fs-body);
181
+ }
182
+
183
+ h1, h2, h3 {
184
+ font-family: var(--font-display);
185
+ color: var(--color-ink);
186
+ font-weight: 600;
187
+ line-height: 1.25;
188
+ }
189
+ h1 { font-size: var(--fs-h1); margin: 0 0 0.5em; color: var(--color-accent); }
190
+ h2 { font-size: var(--fs-h2); margin: 1.4em 0 0.4em; border-bottom: 1px solid var(--color-rule); padding-bottom: 0.15em; }
191
+ h3 { font-size: var(--fs-h3); margin: 1.2em 0 0.3em; }
192
+
193
+ p { margin: 0 0 0.7em; }
194
+
195
+ a { color: var(--color-accent); text-decoration: none; }
196
+
197
+ code {
198
+ font-family: var(--font-mono); font-size: 0.88em;
199
+ background: var(--color-tint); padding: 0.1em 0.35em; border-radius: 3px;
200
+ }
201
+ pre {
202
+ font-family: var(--font-mono); font-size: 0.85em;
203
+ background: var(--color-tint); border: 1px solid var(--color-rule);
204
+ border-radius: 4px; padding: 0.7em 0.9em; overflow: auto;
205
+ }
206
+ pre code { background: none; padding: 0; }
207
+
208
+ table { width: 100%; border-collapse: collapse; margin: 1em 0; font-size: 0.95em; }
209
+ th, td { border: 1px solid var(--color-rule); padding: 0.4em 0.6em; text-align: left; }
210
+ th { background: var(--color-tint); font-weight: 600; }
211
+
212
+ blockquote {
213
+ margin: 1em 0; padding: 0.5em 0.9em;
214
+ background: var(--color-tint); border-left: 3px solid var(--color-accent);
215
+ color: var(--color-ink-muted);
216
+ }
217
+ `;
218
+
219
+ // src/assets/themes/academic/theme.css
220
+ var theme_default4 = `/* =============================================================================
221
+ Academic — gutterpress built-in theme (theme.css)
222
+ A journal-article look in the spirit of LaTeX's \`article\` class: a serif
223
+ text face, justified and hyphenated text, 1in margins, a centred page
224
+ number, and the parts of a paper styled by the @section they sit in.
225
+ Change the :root tokens from the book's own stylesheet. The page SIZE comes from the manifest
226
+ (\`page:\` — US Letter or A4), so it is deliberately not set here.
227
+ ============================================================================= */
228
+
229
+ :root {
230
+ --color-ink: #111111; /* body text */
231
+ --color-ink-muted: #555555; /* captions, secondary text */
232
+ --color-accent: #111111; /* headings, links */
233
+ --color-paper: #ffffff; /* page background */
234
+ --color-rule: #111111; /* table rules */
235
+
236
+ --font-body: "Latin Modern Roman", "CMU Serif", "STIX Two Text", "Times New Roman", "Liberation Serif", serif;
237
+ --font-display: "Latin Modern Roman", "CMU Serif", "STIX Two Text", "Times New Roman", "Liberation Serif", serif;
238
+ --font-mono: "Latin Modern Mono", "Menlo", "Consolas", monospace;
239
+
240
+ --fs-body: 10.5pt;
241
+ --fs-h1: 1.65em;
242
+ --fs-h2: 1.2em;
243
+ --fs-h3: 1em;
244
+ --leading: 1.38;
245
+ }
246
+
247
+ @page {
248
+ margin: 1in;
249
+ @bottom-center { content: counter(page); font-family: var(--font-body); font-size: 9pt; }
250
+ }
251
+
252
+ body {
253
+ color: var(--color-ink);
254
+ background: var(--color-paper);
255
+ font-family: var(--font-body);
256
+ line-height: var(--leading);
257
+ font-size: var(--fs-body);
258
+ text-align: justify;
259
+ hyphens: auto;
260
+ }
261
+
262
+ h1, h2, h3 {
263
+ font-family: var(--font-display);
264
+ color: var(--color-accent);
265
+ line-height: 1.2;
266
+ font-weight: bold;
267
+ }
268
+ h1 { font-size: var(--fs-h1); margin: 0 0 0.7em; }
269
+ h2 { font-size: var(--fs-h2); margin: 1.4em 0 0.5em; }
270
+ h3 { font-size: var(--fs-h3); margin: 1.2em 0 0.4em; }
271
+
272
+ a { color: var(--color-accent); text-decoration: none; }
273
+
274
+ code, pre { font-family: var(--font-mono); }
275
+ pre { font-size: 0.8em; line-height: 1.3; }
276
+
277
+ /* @section .title — the title, authors, affiliations and date */
278
+ .title { text-align: center; hyphens: manual; margin-bottom: 2em; }
279
+
280
+ /* @section .abstract — the abstract, keywords and subject classes */
281
+ .abstract { font-size: 0.9em; margin: 0 2.5em 2em; }
282
+
283
+ /* @section .figure — a figure, table or listing kept on one page with its caption */
284
+ .figure { break-inside: avoid; margin: 1.4em 0; }
285
+ .caption { font-size: 0.9em; color: var(--color-ink-muted); }
286
+
287
+ /* Tables ruled above, below and under the header row, as journals set them */
288
+ table { border-collapse: collapse; margin: 0.6em auto; border-top: 1pt solid var(--color-rule); border-bottom: 1pt solid var(--color-rule); }
289
+ thead th { border-bottom: 0.5pt solid var(--color-rule); }
290
+ th, td { padding: 0.2em 0.8em; }
291
+
292
+ /* Notes (from [^1] footnotes) and @section .references, a size smaller than the text */
293
+ .footnotes, .references { font-size: 0.85em; }
294
+ .footnotes-sep { border: 0; border-top: 0.5pt solid var(--color-rule); width: 30%; margin: 1.5em 0 0.5em; }
295
+ .footnote-backref { display: none; }
296
+ `;
297
+
298
+ // src/lib/extension-specifier.ts
299
+ var BUNDLED_EXTENSIONS = [
300
+ {
301
+ name: "markdown-it-mark",
302
+ label: "Highlight",
303
+ description: "Highlight text by wrapping it in `==double equals==`."
304
+ },
305
+ {
306
+ name: "markdown-it-sub",
307
+ label: "Subscript",
308
+ description: "Lower text below the line, like the 2 in `H~2~O`."
309
+ },
310
+ {
311
+ name: "markdown-it-sup",
312
+ label: "Superscript",
313
+ description: "Raise text above the line, like the th in `29^th^`."
314
+ },
315
+ {
316
+ name: "markdown-it-abbr",
317
+ label: "Abbreviations",
318
+ description: "Explain an abbreviation when readers hover over it. Define it once, like `*[GPS]: Global Positioning System`."
319
+ },
320
+ {
321
+ name: "gutterpress-gfm-alerts",
322
+ label: "Callouts",
323
+ description: "Turn a quote into a boxed note: start it with `> [!NOTE]` (or `TIP`, `IMPORTANT`, `WARNING`, `CAUTION`)."
324
+ },
325
+ {
326
+ name: "gutterpress-math",
327
+ label: "Math",
328
+ description: "Write LaTeX math between dollar signs: `$E = mc^2$` in a line, or `$$` … `$$` on lines of their own."
329
+ },
330
+ {
331
+ name: "clean-book",
332
+ label: "Clean book",
333
+ description: "A calm, classic book look: serif body, generous margins, restrained accents.",
334
+ css: theme_default
335
+ },
336
+ {
337
+ name: "zine",
338
+ label: "Zine",
339
+ description: "High-contrast, punchy sans-serif look for short photocopier-friendly zines.",
340
+ css: theme_default2
341
+ },
342
+ {
343
+ name: "technical-doc",
344
+ label: "Technical doc",
345
+ description: "Clean sans-serif manual look with clear hierarchy, code styling, and tidy tables.",
346
+ css: theme_default3
347
+ },
348
+ {
349
+ name: "academic",
350
+ label: "Academic",
351
+ description: "A journal-article look: serif text, justified and hyphenated, ruled tables, notes and references set smaller.",
352
+ css: theme_default4
353
+ }
354
+ ];
355
+ function bundledExtension(name) {
356
+ return BUNDLED_EXTENSIONS.find((e) => e.name === name);
357
+ }
358
+ function isBundledExtension(name) {
359
+ return bundledExtension(name) !== undefined;
360
+ }
361
+ function isPathSpecifier(use) {
362
+ return use.startsWith("./") || use.startsWith("../") || use.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(use);
363
+ }
364
+ function parseExtensionSpecifier(use) {
365
+ const spec = use.trim();
366
+ if (!spec) {
367
+ throw new Error("An extension specifier is required — an npm package name, or a ./ path.");
368
+ }
369
+ if (isPathSpecifier(spec))
370
+ return { kind: "path", path: spec };
371
+ if (/\.(m?js|cjs)$/i.test(spec) || !spec.startsWith("@") && spec.includes("/")) {
372
+ throw new Error(`"${spec}" looks like a path; write it as "./${spec}". A specifier that does not ` + "start with ./, ../ or / is an npm package name.");
373
+ }
374
+ const parsed = parseNpmPluginSpec(spec);
375
+ if (isBundledExtension(parsed.name)) {
376
+ if (parsed.selector !== undefined) {
377
+ throw new Error(`"${parsed.name}" is bundled with Gutterpress and always resolves to the bundled copy — ` + `drop "@${parsed.selector}".`);
378
+ }
379
+ return { kind: "bundled", name: parsed.name };
380
+ }
381
+ return parsed.selector === undefined ? { kind: "npm", name: parsed.name } : { kind: "npm", name: parsed.name, version: parsed.selector };
382
+ }
383
+ function pinnedNpmSpecifier(name, version) {
384
+ return `${name}@${version}`;
385
+ }
386
+
18
387
  // src/lib/style-declarations.ts
19
388
  import { existsSync } from "node:fs";
20
389
  import { resolve } from "node:path";
@@ -37,7 +406,7 @@ import { satisfies, validRange } from "semver";
37
406
  // package.json
38
407
  var package_default = {
39
408
  name: "gutterpress",
40
- version: "0.11.23-alpha.1",
409
+ version: "0.11.23-alpha.2",
41
410
  description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
42
411
  author: "itlackey",
43
412
  license: "MPL-2.0",
@@ -3354,7 +3723,7 @@ async function importVendoredEntry(installRoot, packageDir, spec) {
3354
3723
  try {
3355
3724
  entry = await resolvePackageEntry(packageDir);
3356
3725
  } catch (cause) {
3357
- throw new Error(`the downloaded copy of ${spec} under plugins/npm/ is incomplete ` + `(${cause instanceof Error ? cause.message : String(cause)}). ` + `Reinstall it with \`gutterpress ext add ${spec}\`.`, { cause });
3726
+ throw new Error(`the downloaded copy of ${spec} under extensions/npm/ is incomplete ` + `(${cause instanceof Error ? cause.message : String(cause)}). ` + `Reinstall it with \`gutterpress ext add ${spec}\`.`, { cause });
3358
3727
  }
3359
3728
  const entryPath = join(packageDir, ...entry.split("/"));
3360
3729
  vendoredRootsOpened.add(installRoot);
@@ -3574,12 +3943,18 @@ async function loadPlugin(config, baseDir) {
3574
3943
  if (!config.path && !config.name) {
3575
3944
  throw new Error("Plugin manifest entry must specify either `path` or `name`. Got an empty plugin config.");
3576
3945
  }
3577
- if (!config.path && !config.version && config.name && BUILTIN_OPTIONAL_PLUGINS[config.name]) {
3578
- return {
3579
- name: config.name,
3580
- plugin: BUILTIN_OPTIONAL_PLUGINS[config.name],
3581
- options: config.options
3582
- };
3946
+ if (!config.path && !config.version && config.name) {
3947
+ const bundled = bundledExtension(config.name);
3948
+ if (bundled?.css !== undefined) {
3949
+ return { name: config.name, plugin: noopPlugin, css: bundled.css, options: config.options };
3950
+ }
3951
+ if (BUILTIN_OPTIONAL_PLUGINS[config.name]) {
3952
+ return {
3953
+ name: config.name,
3954
+ plugin: BUILTIN_OPTIONAL_PLUGINS[config.name],
3955
+ options: config.options
3956
+ };
3957
+ }
3583
3958
  }
3584
3959
  if (config.path) {
3585
3960
  const candidatePath = resolve2(baseDir, config.path);
@@ -3700,4 +4075,4 @@ async function loadPluginsWithCss(configs, baseDir, onError) {
3700
4075
  };
3701
4076
  }
3702
4077
 
3703
- export { YOUR_VERSION_LABEL, ONLINE_VERSION_LABEL, TWO_VERSIONS_OPEN, TWO_VERSIONS_CLOSE, buildDeclaredMarkerRegistry, MARKER_CSS, GUTTERPRESS_CSS, TWO_VERSIONS_CSS, TABLE_ALIGN_CSS, slugify, prettify, setLogLevel, debug, info, warn, error, log, parseSrcsetUrlCandidates, collectHtmlImageRefs, createMarkdownRenderer, applyPlugins, collectPluginCss, collectPluginStylePaths, collectPluginStyleGroups, resolveDeclaredStyles, package_default, PACKAGE_META, PACKAGE_VERSION, EXTENSION_MANIFEST_FILENAME, readExtensionMeta, extensionStyleList, extensionEntry, extensionCarries, pathEscapesFolder, assertExtensionContained, resolveExtension, __resetPathPluginCacheForTests, loadPlugin, loadPublishFunction, loadPlugins, loadPluginsWithCss };
4078
+ export { BUNDLED_EXTENSIONS, bundledExtension, isBundledExtension, isPathSpecifier, parseExtensionSpecifier, pinnedNpmSpecifier, YOUR_VERSION_LABEL, ONLINE_VERSION_LABEL, TWO_VERSIONS_OPEN, TWO_VERSIONS_CLOSE, buildDeclaredMarkerRegistry, MARKER_CSS, GUTTERPRESS_CSS, TWO_VERSIONS_CSS, TABLE_ALIGN_CSS, slugify, prettify, setLogLevel, debug, info, warn, error, log, parseSrcsetUrlCandidates, collectHtmlImageRefs, createMarkdownRenderer, applyPlugins, collectPluginCss, collectPluginStylePaths, collectPluginStyleGroups, resolveDeclaredStyles, package_default, PACKAGE_META, PACKAGE_VERSION, EXTENSION_MANIFEST_FILENAME, readExtensionMeta, extensionStyleList, extensionEntry, extensionCarries, pathEscapesFolder, assertExtensionContained, resolveExtension, __resetPathPluginCacheForTests, loadPlugin, loadPublishFunction, loadPlugins, loadPluginsWithCss };
@@ -0,0 +1,18 @@
1
+ // src/lib/build-error.ts
2
+ var EXIT_CODES = {
3
+ OK: 0,
4
+ FINDINGS: 1,
5
+ USAGE: 2,
6
+ PIPELINE: 3
7
+ };
8
+
9
+ class BuildError extends Error {
10
+ exitCode;
11
+ constructor(message, exitCode = EXIT_CODES.PIPELINE, options) {
12
+ super(message, options);
13
+ this.name = "BuildError";
14
+ this.exitCode = exitCode;
15
+ }
16
+ }
17
+
18
+ export { EXIT_CODES, BuildError };
@@ -3,6 +3,7 @@ import {
3
3
  cssQuote,
4
4
  extract,
5
5
  generatedContentCss,
6
+ getAssetPath,
6
7
  isIgnoredMarginBoxProperty,
7
8
  isRectoVersoBreak,
8
9
  leaderMarker,
@@ -18,10 +19,7 @@ import {
18
19
  stringValueAt,
19
20
  toFolioPage,
20
21
  wantsRecto
21
- } from "./index-apc8w9w8.js";
22
- import {
23
- getAssetPath
24
- } from "./index-dsvk7q6s.js";
22
+ } from "./index-0skhjx3e.js";
25
23
  import {
26
24
  launchChromium
27
25
  } from "./index-bt0qws5c.js";
@@ -2,7 +2,8 @@
2
2
  import { lstat, readFile, realpath } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { exports as resolveExports } from "resolve.exports";
5
- var PLUGINS_DIR = "plugins";
5
+ var EXTENSIONS_DIR = "extensions";
6
+ var LEGACY_PLUGINS_DIR = "plugins";
6
7
  var VENDORED_NPM_DIR = "npm";
7
8
  var NPM_SEGMENT = /^[a-z0-9][a-z0-9._~-]*$/;
8
9
  var EXACT_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
@@ -51,7 +52,10 @@ function encodePathPart(value) {
51
52
  return encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
52
53
  }
53
54
  function vendoredNpmPluginRoot(projectDir, name, version) {
54
- return path.join(projectDir, PLUGINS_DIR, VENDORED_NPM_DIR, encodePathPart(name), encodePathPart(version));
55
+ return path.join(projectDir, EXTENSIONS_DIR, VENDORED_NPM_DIR, encodePathPart(name), encodePathPart(version));
56
+ }
57
+ function vendoredNpmPluginRoots(projectDir, name, version) {
58
+ return [EXTENSIONS_DIR, LEGACY_PLUGINS_DIR].map((parent) => path.join(projectDir, parent, VENDORED_NPM_DIR, encodePathPart(name), encodePathPart(version)));
55
59
  }
56
60
  function vendoredNpmPluginPackageDir(installRoot, name) {
57
61
  return path.join(installRoot, "node_modules", ...name.split("/"));
@@ -175,15 +179,20 @@ async function resolvePackageEntry(packageDir, packageJson) {
175
179
  }
176
180
  async function resolveVendoredPluginInstallRoot(projectDir, expectedName, expectedVersion) {
177
181
  const requestedProjectRoot = path.resolve(projectDir);
178
- const requestedInstallRoot = vendoredNpmPluginRoot(requestedProjectRoot, expectedName, expectedVersion);
182
+ let requestedInstallRoot = "";
179
183
  let installInfo;
180
- try {
181
- installInfo = await lstat(requestedInstallRoot);
182
- } catch (error) {
183
- if (error.code === "ENOENT")
184
- return null;
185
- throw error;
184
+ for (const candidate of vendoredNpmPluginRoots(requestedProjectRoot, expectedName, expectedVersion)) {
185
+ requestedInstallRoot = candidate;
186
+ try {
187
+ installInfo = await lstat(requestedInstallRoot);
188
+ break;
189
+ } catch (error) {
190
+ if (error.code !== "ENOENT")
191
+ throw error;
192
+ }
186
193
  }
194
+ if (!installInfo)
195
+ return null;
187
196
  if (!installInfo.isDirectory() || installInfo.isSymbolicLink()) {
188
197
  throw new Error("Vendor install root is not a normal directory.");
189
198
  }
@@ -197,4 +206,4 @@ async function resolveVendoredPluginInstallRoot(projectDir, expectedName, expect
197
206
  return installRoot;
198
207
  }
199
208
 
200
- export { PLUGINS_DIR, VENDORED_NPM_DIR, isValidNpmPackageName, parseNpmPluginSpec, isExactNpmVersion, vendoredNpmPluginRoot, vendoredNpmPluginPackageDir, assertWindowsSafeRelativePath, windowsPathKey, packageResolutionTargets, safePackageTarget, resolvePackageEntry, resolveVendoredPluginInstallRoot };
209
+ export { EXTENSIONS_DIR, LEGACY_PLUGINS_DIR, VENDORED_NPM_DIR, isValidNpmPackageName, parseNpmPluginSpec, isExactNpmVersion, vendoredNpmPluginRoot, vendoredNpmPluginRoots, vendoredNpmPluginPackageDir, toPosixPath, assertWindowsSafeRelativePath, windowsPathKey, packageResolutionTargets, safePackageTarget, resolvePackageEntry, resolveVendoredPluginInstallRoot };
@@ -1,7 +1,8 @@
1
1
  import {
2
- PLUGINS_DIR,
2
+ EXTENSIONS_DIR,
3
+ LEGACY_PLUGINS_DIR,
3
4
  VENDORED_NPM_DIR
4
- } from "./index-r7jcsk6h.js";
5
+ } from "./index-5x42amh8.js";
5
6
  import {
6
7
  capabilitiesFor,
7
8
  detectProjectSource,
@@ -562,10 +563,10 @@ async function countUnversionedChanges(projectDir) {
562
563
  return null;
563
564
  const dir = gitScopeFor(source);
564
565
  const bookPrefix = source.subPath ? `${source.subPath.replace(/\/+$/, "")}/` : "";
565
- const appWritten = `${bookPrefix}${PLUGINS_DIR}/${VENDORED_NPM_DIR}/`;
566
+ const appWritten = [EXTENSIONS_DIR, LEGACY_PLUGINS_DIR].map((d) => `${bookPrefix}${d}/${VENDORED_NPM_DIR}/`);
566
567
  return withRepoLock(dir, async () => {
567
568
  const { adds, removes } = await listWorkdirChanges(dir);
568
- const mine = new Set([...adds, ...removes].filter((f) => f.startsWith(bookPrefix) && !f.startsWith(appWritten)));
569
+ const mine = new Set([...adds, ...removes].filter((f) => f.startsWith(bookPrefix) && !appWritten.some((p) => f.startsWith(p))));
569
570
  return { changedFiles: mine.size, stale: gitFs.existsSync(snapshotStagingMarkerPath(dir)) };
570
571
  });
571
572
  }