gutterpress 0.11.23-alpha.1 → 0.11.23-alpha.3

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 (82) hide show
  1. package/README.md +27 -29
  2. package/dist/{README-8ef336pp.md → README-8db7btxb.md} +13 -19
  3. package/dist/api/index.d.ts +5 -5
  4. package/dist/api/index.js +16 -22
  5. package/dist/{build-09cd1n7s.js → build-m78jcpf7.js} +9 -10
  6. package/dist/{cli-5hkmp2wy.js → cli-1h17ktsg.js} +125 -187
  7. package/dist/{cli-nr6j6300.js → cli-5jzyw06s.js} +1 -1
  8. package/dist/{cli-habkc5ng.js → cli-a35w7wzg.js} +5 -4
  9. package/dist/{cli-dd115fa8.js → cli-h4x81x97.js} +152 -124
  10. package/dist/{cli-ccp7sxz3.js → cli-n2tsxqpn.js} +1 -1
  11. package/dist/{cli-b0vyjvmt.js → cli-s6djx4ma.js} +1 -1
  12. package/dist/{cli-h17e3mtz.js → cli-t8be9yeb.js} +380 -16
  13. package/dist/{cli-dtckwda1.js → cli-xsrffqzt.js} +192 -10
  14. package/dist/{index-r7jcsk6h.js → cli-y28j019d.js} +19 -10
  15. package/dist/cli.js +12 -12
  16. package/dist/{doctor-nddspx4n.js → doctor-j71jqce3.js} +3 -3
  17. package/dist/engine/shared/gcpm-extract.d.ts +8 -0
  18. package/dist/engine/shared/print-area.d.ts +16 -4
  19. package/dist/{engine-yjzknt0g.js → engine-1r6q80tq.js} +3 -3
  20. package/dist/{engine-892swzzc.js → engine-m4y10shc.js} +1 -2
  21. package/dist/{ext-zjpen4yx.js → ext-fq2hg9va.js} +18 -33
  22. package/dist/{extension-restore-jvs60x9b.js → extension-restore-9dh7c4tp.js} +4 -4
  23. package/dist/{extension-restore-w8xt4kbq.js → extension-restore-tmxtzkm0.js} +5 -6
  24. package/dist/{gutterpress-viewer-701vf4qp.js → gutterpress-viewer-mafsby5k.js} +9 -2
  25. package/dist/{index-2jpk25en.js → index-0wee4408.js} +380 -16
  26. package/dist/index-46ycxe6r.js +18 -0
  27. package/dist/{cli-h3phdstf.js → index-5x42amh8.js} +19 -10
  28. package/dist/{index-m4hhzg3c.js → index-7pck1q9c.js} +174 -142
  29. package/dist/{index-93pqkvty.js → index-h6yk5tjx.js} +5 -4
  30. package/dist/{index-apc8w9w8.js → index-nedd07bk.js} +187 -2
  31. package/dist/{index-0w2azsk1.js → index-nvmf22rt.js} +2 -4
  32. package/dist/{index-zrhyb967.js → index-prqvz3xm.js} +510 -587
  33. package/dist/index.js +17 -23
  34. package/dist/lib/extension-manager.d.ts +24 -50
  35. package/dist/lib/extension-manifest.d.ts +1 -13
  36. package/dist/lib/extension-scaffold.d.ts +3 -3
  37. package/dist/lib/extension-specifier.d.ts +23 -6
  38. package/dist/lib/manifest.d.ts +15 -1
  39. package/dist/lib/markdown/assemble.d.ts +4 -3
  40. package/dist/lib/markdown/math.d.ts +1 -1
  41. package/dist/lib/npm-plugin-installer.d.ts +1 -1
  42. package/dist/lib/page-size.d.ts +34 -27
  43. package/dist/lib/plugin-vendor.d.ts +11 -3
  44. package/dist/lib/printsafe.d.ts +1 -0
  45. package/dist/lib/project-gitignore.d.ts +2 -2
  46. package/dist/lib/project-scaffold.d.ts +5 -7
  47. package/dist/lib/project-templates.d.ts +9 -3
  48. package/dist/lib/snippets.d.ts +2 -1
  49. package/dist/lib/style-resolver.d.ts +1 -2
  50. package/dist/manifest-8afhrk30.yaml +19 -0
  51. package/dist/manifest-d4f9q79e.yaml +23 -0
  52. package/dist/manifest-gf13sjp8.yaml +27 -0
  53. package/dist/manifest-my7mdv8j.yaml +23 -0
  54. package/dist/{manifest.schema-9aeaered.json → manifest.schema-650pe0d1.json} +17 -5
  55. package/dist/{new-xhq8wczn.js → new-s4zcaepg.js} +22 -21
  56. package/dist/{package-aenwjew3.json → package-awwkt7sa.json} +0 -1
  57. package/dist/{page-rules-nsj3aw42.css → page-rules-fj09xct5.css} +12 -40
  58. package/dist/{plugin.test.js-xgxrd8hf.tpl → plugin.test.js-gxzqqfdh.tpl} +0 -1
  59. package/dist/{plugins-9en8m9y0.js → plugins-4vaf27n9.js} +4 -4
  60. package/dist/{plugins-gf0gkjmv.js → plugins-80emnf31.js} +2 -2
  61. package/dist/{preflight-negnxg7b.js → preflight-c7w1xdn3.js} +9 -10
  62. package/dist/{preview-cep2s9cg.js → preview-mzytcwyk.js} +9 -10
  63. package/dist/{publish-xery6qc6.js → publish-fez14ad4.js} +9 -10
  64. package/dist/render.js +24 -561
  65. package/dist/schema/manifest.types.d.ts +22 -0
  66. package/dist/{source-provider-qbfknhyy.js → source-provider-98k5deda.js} +2 -2
  67. package/dist/{source-provider-2r74a7wp.js → source-provider-knmcam50.js} +2 -2
  68. package/dist/{tokens-7984461t.css → tokens-ye64p899.css} +5 -12
  69. package/dist/{validate-3d0sgf4g.js → validate-p2chr6hr.js} +9 -10
  70. package/package.json +1 -1
  71. package/dist/cli-fe43evmf.js +0 -212
  72. package/dist/index-dsvk7q6s.js +0 -229
  73. package/dist/manifest-8f10dr4f.yaml +0 -17
  74. package/dist/manifest-n1gh3qw5.yaml +0 -9
  75. package/dist/package-97gm31sg.json +0 -10
  76. package/dist/package-gag6ykwn.json +0 -10
  77. package/dist/package-nrhkwf2m.json +0 -10
  78. package/dist/package-zttkgydq.json +0 -10
  79. package/dist/theme-9c58td2d.css +0 -76
  80. package/dist/theme-h5recz6c.css +0 -61
  81. package/dist/theme-j2bagrfx.css +0 -59
  82. package/dist/theme-nn6d53zy.css +0 -71
@@ -1,28 +1,29 @@
1
1
  import {
2
2
  EXTENSION_MANIFEST_FILENAME,
3
3
  TWO_VERSIONS_OPEN,
4
+ bundledExtension,
4
5
  extensionCarries,
5
6
  extensionStyleList,
7
+ isPathSpecifier,
6
8
  loadPlugin,
9
+ parseExtensionSpecifier,
10
+ pinnedNpmSpecifier,
7
11
  prettify,
8
12
  readExtensionMeta,
9
13
  slugify
10
- } from "./cli-h17e3mtz.js";
14
+ } from "./cli-t8be9yeb.js";
11
15
  import {
12
16
  log
13
- } from "./cli-b0vyjvmt.js";
17
+ } from "./cli-s6djx4ma.js";
14
18
  import {
15
19
  UsageError
16
20
  } from "./cli-d8xjns66.js";
17
- import {
18
- getAssetsDir
19
- } from "./cli-fe43evmf.js";
20
21
  import {
21
22
  BuildError,
22
23
  EXIT_CODES
23
24
  } from "./cli-46ycxe6r.js";
24
25
  import {
25
- PLUGINS_DIR,
26
+ EXTENSIONS_DIR,
26
27
  VENDORED_NPM_DIR,
27
28
  assertWindowsSafeRelativePath,
28
29
  isExactNpmVersion,
@@ -31,8 +32,9 @@ import {
31
32
  resolveVendoredPluginInstallRoot,
32
33
  vendoredNpmPluginPackageDir,
33
34
  vendoredNpmPluginRoot,
35
+ vendoredNpmPluginRoots,
34
36
  windowsPathKey
35
- } from "./cli-h3phdstf.js";
37
+ } from "./cli-y28j019d.js";
36
38
  import {
37
39
  __require
38
40
  } from "./cli-37x76zdn.js";
@@ -74,7 +76,7 @@ async function withFetchTimeout(options, run) {
74
76
  }
75
77
 
76
78
  // src/lib/extension-manager.ts
77
- import { cp, mkdir as mkdir3, readdir, readFile as readFile5, rm as rm3, stat } from "node:fs/promises";
79
+ import { readdir, readFile as readFile5, rm as rm3, stat } from "node:fs/promises";
78
80
  import { existsSync as existsSync3 } from "node:fs";
79
81
  import path4 from "node:path";
80
82
  import { isSeq as isSeq2, isMap, isScalar, YAMLMap, Scalar } from "yaml";
@@ -92,42 +94,85 @@ import { existsSync, statSync } from "node:fs";
92
94
  import { resolve, dirname } from "node:path";
93
95
  import { parse as parseYaml, YAMLParseError } from "yaml";
94
96
 
95
- // src/lib/extension-specifier.ts
96
- var BUNDLED_EXTENSIONS = [
97
- "markdown-it-mark",
98
- "markdown-it-sub",
99
- "markdown-it-sup",
100
- "markdown-it-abbr",
101
- "gutterpress-gfm-alerts",
102
- "gutterpress-math"
103
- ];
104
- function isBundledExtension(name) {
105
- return BUNDLED_EXTENSIONS.includes(name);
106
- }
107
- function isPathSpecifier(use) {
108
- return use.startsWith("./") || use.startsWith("../") || use.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(use);
109
- }
110
- function parseExtensionSpecifier(use) {
111
- const spec = use.trim();
112
- if (!spec) {
113
- throw new Error("An extension specifier is required — an npm package name, or a ./ path.");
114
- }
115
- if (isPathSpecifier(spec))
116
- return { kind: "path", path: spec };
117
- if (/\.(m?js|cjs)$/i.test(spec) || !spec.startsWith("@") && spec.includes("/")) {
118
- 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.");
119
- }
120
- const parsed = parseNpmPluginSpec(spec);
121
- if (isBundledExtension(parsed.name)) {
122
- if (parsed.selector !== undefined) {
123
- throw new Error(`"${parsed.name}" is bundled with Gutterpress and always resolves to the bundled copy — ` + `drop "@${parsed.selector}".`);
97
+ // src/engine/shared/print-area.ts
98
+ function isPositivePoints(value) {
99
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
100
+ }
101
+ function isNonNegativePoints(value) {
102
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
103
+ }
104
+ var round3 = (n) => Math.round(n * 1000) / 1000;
105
+ function cssLength(pt) {
106
+ const inches = round3(pt / 72);
107
+ return Math.abs(inches * 72 - pt) < 0.000001 ? `${inches}in` : `${round3(pt)}pt`;
108
+ }
109
+ function formatPageSize(page) {
110
+ return `${cssLength(page.width)} ${cssLength(page.height)}`;
111
+ }
112
+ function printAreaMeta(page) {
113
+ const area = JSON.stringify({
114
+ width: page.width,
115
+ height: page.height,
116
+ bleed: page.bleed ?? 0,
117
+ safe: page.safe ?? 0,
118
+ artSafe: page.artSafe,
119
+ gutter: page.gutter,
120
+ preset: page.preset,
121
+ spec: page.spec
122
+ });
123
+ return `<meta name="gp-print-area" content='${area.replace(/&/g, "&amp;").replace(/'/g, "&#39;")}'>`;
124
+ }
125
+ function pageBoxes(area, side, scale = 1) {
126
+ const w = area.width * scale;
127
+ const h = area.height * scale;
128
+ const b = Math.max(0, area.bleed) * scale;
129
+ const s = Math.max(0, area.safe) * scale;
130
+ const a = Math.max(0, area.artSafe) * scale;
131
+ const g = Math.max(0, area.gutter) * scale;
132
+ const [textLeft, textRight] = side === "verso" ? [s, g] : [g, s];
133
+ const [artLeft, artRight] = side === "verso" ? [a, g] : [g, a];
134
+ const trim = { x: side === "verso" ? b : 0, y: b, w: Math.max(0, w - b), h: Math.max(0, h - 2 * b) };
135
+ return {
136
+ sheet: { x: 0, y: 0, w, h },
137
+ trim,
138
+ safe: {
139
+ x: trim.x + textLeft,
140
+ y: trim.y + s,
141
+ w: Math.max(0, trim.w - textLeft - textRight),
142
+ h: Math.max(0, trim.h - 2 * s)
143
+ },
144
+ safeArt: {
145
+ x: trim.x + artLeft,
146
+ y: trim.y + a,
147
+ w: Math.max(0, trim.w - artLeft - artRight),
148
+ h: Math.max(0, trim.h - 2 * a)
149
+ },
150
+ bleed: b
151
+ };
152
+ }
153
+ var SEVERITY = { inside: 0, unsafe: 1, bleed: 2, "off-page": 3 };
154
+ function judgeRect(r, boxes, rule, tol = 0) {
155
+ const { sheet, trim } = boxes;
156
+ const safe = rule === "art" ? boxes.safeArt : boxes.safe;
157
+ const sides = [
158
+ ["left", -r.x, -safe.x, -trim.x, -sheet.x],
159
+ ["right", r.x + r.w, safe.x + safe.w, trim.x + trim.w, sheet.x + sheet.w],
160
+ ["top", -r.y, -safe.y, -trim.y, -sheet.y],
161
+ ["bottom", r.y + r.h, safe.y + safe.h, trim.y + trim.h, sheet.y + sheet.h]
162
+ ];
163
+ let worst = { kind: "inside", gap: 0, need: 0, edge: null };
164
+ let beyond = 0;
165
+ for (const [edge, ink, limit, trimEdge, sheetEdge] of sides) {
166
+ const past = ink - limit;
167
+ if (past <= tol || rule === "art" && ink >= sheetEdge - tol)
168
+ continue;
169
+ const kind = ink > sheetEdge + tol ? "off-page" : ink > trimEdge + tol ? "bleed" : "unsafe";
170
+ if (SEVERITY[kind] > SEVERITY[worst.kind] || kind === worst.kind && past > beyond) {
171
+ worst = { kind, gap: trimEdge - ink, need: trimEdge - limit, edge };
172
+ beyond = past;
124
173
  }
125
- return { kind: "bundled", name: parsed.name };
126
174
  }
127
- return parsed.selector === undefined ? { kind: "npm", name: parsed.name } : { kind: "npm", name: parsed.name, version: parsed.selector };
128
- }
129
- function pinnedNpmSpecifier(name, version) {
130
- return `${name}@${version}`;
175
+ return worst;
131
176
  }
132
177
 
133
178
  // src/lib/presets.ts
@@ -160,7 +205,8 @@ var DTRPG_PRESET = {
160
205
  bleed: 9,
161
206
  safe: 36,
162
207
  artSafe: 18,
163
- gutter: 36
208
+ gutter: 36,
209
+ margin: undefined
164
210
  },
165
211
  ink: {
166
212
  maxTac: 240,
@@ -246,7 +292,8 @@ var BOOK_PRESET = {
246
292
  bleed: undefined,
247
293
  safe: 18,
248
294
  artSafe: undefined,
249
- gutter: undefined
295
+ gutter: undefined,
296
+ margin: undefined
250
297
  },
251
298
  ink: {
252
299
  maxTac: 400,
@@ -608,9 +655,28 @@ function resolveCustomPage(c, m) {
608
655
  bleed: c.page?.bleed ?? m.page?.bleed,
609
656
  safe: c.page?.safe ?? m.page?.safe,
610
657
  artSafe: c.page?.artSafe ?? m.page?.artSafe,
611
- gutter: c.page?.gutter ?? m.page?.gutter
658
+ gutter: c.page?.gutter ?? m.page?.gutter,
659
+ margin: c.page?.margin ?? m.page?.margin
612
660
  };
613
661
  }
662
+ var MARGIN_EDGES = ["top", "bottom", "inside", "outside"];
663
+ function validatePageMargin(margin, size) {
664
+ const edges = isPlainObject(margin) && Object.keys(margin).every((k) => MARGIN_EDGES.includes(k)) && MARGIN_EDGES.every((e) => isNonNegativePoints(margin[e]));
665
+ if (!edges) {
666
+ throw new UsageError("Manifest field `page.margin` must set all four edges in points (72pt = 1in), for example\n" + ` page:
667
+ margin:
668
+ top: 54
669
+ bottom: 54
670
+ inside: 54
671
+ outside: 54
672
+ ` + `got ${JSON.stringify(margin)}.`);
673
+ }
674
+ const m = margin;
675
+ if (m.inside + m.outside >= size.width || m.top + m.bottom >= size.height) {
676
+ throw new UsageError(`Manifest field \`page.margin\` leaves no room for text on a ${size.width} x ${size.height}pt page ` + `(inside ${m.inside} + outside ${m.outside} across, top ${m.top} + bottom ${m.bottom} down).`);
677
+ }
678
+ return m;
679
+ }
614
680
  function resolveWithPreset(cliOverrides, manifest, preset) {
615
681
  const m = manifest;
616
682
  const c = cliOverrides;
@@ -663,6 +729,9 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
663
729
  throw new UsageError("Manifest field `extensions` must be a list of specifiers.");
664
730
  }
665
731
  const extensions = rawExtensions.map((entry, index) => ({ entry, index })).filter(({ entry }) => typeof entry === "string" || entry?.enabled !== false).map(({ entry, index }) => normalizeExtensionEntry(entry, index));
732
+ const page = preset.page ? mergeShape(c.page, m.page, preset.page) : resolveCustomPage(c, m);
733
+ if (page.margin !== undefined && page.margin !== null)
734
+ validatePageMargin(page.margin, page);
666
735
  return {
667
736
  title: c.title ?? m.title ?? "Document",
668
737
  authors: c.authors ?? m.authors ?? [],
@@ -675,7 +744,7 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
675
744
  source: mergeShape(c.source, m.source, preset.source),
676
745
  pdfx: mergeShape(c.pdfx, m.pdfx, preset.pdfx),
677
746
  print: mergeShape(c.print, m.print, preset.print),
678
- page: preset.page ? mergeShape(c.page, m.page, preset.page) : resolveCustomPage(c, m),
747
+ page,
679
748
  ink: mergeShape(c.ink, m.ink, preset.ink),
680
749
  lint: {
681
750
  ...mergeShape(c.lint, m.lint, preset.lint),
@@ -1283,13 +1352,13 @@ async function removeEmptyDirs(dirs) {
1283
1352
  }
1284
1353
  }
1285
1354
  async function prepareVendorParent(projectDir, name) {
1286
- const plugins = path2.join(projectDir, PLUGINS_DIR);
1287
- const npm = path2.join(plugins, VENDORED_NPM_DIR);
1355
+ const extensions = path2.join(projectDir, EXTENSIONS_DIR);
1356
+ const npm = path2.join(extensions, VENDORED_NPM_DIR);
1288
1357
  const packageParent = path2.dirname(vendoredNpmPluginRoot(projectDir, name, "0.0.0"));
1289
1358
  await mkdir2(projectDir, { recursive: true });
1290
1359
  const createdDirs = [];
1291
1360
  try {
1292
- for (const dir of [plugins, npm, packageParent]) {
1361
+ for (const dir of [extensions, npm, packageParent]) {
1293
1362
  if (await ensureDirectory(dir))
1294
1363
  createdDirs.push(dir);
1295
1364
  }
@@ -1408,7 +1477,7 @@ function ignoreStatus(gitignore, entry) {
1408
1477
  return "ignored";
1409
1478
  return dir.unignored || file.unignored ? "negated" : "open";
1410
1479
  }
1411
- var PROJECT_GITIGNORE_ENTRIES = ["dist/", "plugins/npm/"];
1480
+ var PROJECT_GITIGNORE_ENTRIES = ["dist/", "extensions/npm/"];
1412
1481
  async function ensureProjectGitignore(projectDir) {
1413
1482
  const gitignorePath = path3.join(projectDir, ".gitignore");
1414
1483
  let existing = null;
@@ -1436,40 +1505,6 @@ async function ensureProjectGitignore(projectDir) {
1436
1505
  }
1437
1506
 
1438
1507
  // src/lib/extension-manager.ts
1439
- var EXTENSIONS_DIR = "extensions";
1440
- var RECOMMENDED_EXTENSIONS = [
1441
- {
1442
- use: "markdown-it-mark",
1443
- label: "Highlight",
1444
- description: "Highlight text by wrapping it in `==double equals==`."
1445
- },
1446
- {
1447
- use: "markdown-it-sub",
1448
- label: "Subscript",
1449
- description: "Lower text below the line, like the 2 in `H~2~O`."
1450
- },
1451
- {
1452
- use: "markdown-it-sup",
1453
- label: "Superscript",
1454
- description: "Raise text above the line, like the th in `29^th^`."
1455
- },
1456
- {
1457
- use: "markdown-it-abbr",
1458
- label: "Abbreviations",
1459
- description: "Explain an abbreviation when readers hover over it. Define it once, like `*[GPS]: Global Positioning System`."
1460
- },
1461
- {
1462
- use: "gutterpress-gfm-alerts",
1463
- label: "Callouts",
1464
- description: "Turn a quote into a boxed note: start it with `> [!NOTE]` (or `TIP`, `IMPORTANT`, `WARNING`, `CAUTION`)."
1465
- },
1466
- {
1467
- use: "gutterpress-math",
1468
- label: "Math",
1469
- description: "Write LaTeX math between dollar signs: `$E = mc^2$` in a line, or `$$` … `$$` on lines of their own."
1470
- }
1471
- ];
1472
- var BUILT_IN_STYLE_SET_IDS = ["clean-book", "zine", "technical-doc", "academic"];
1473
1508
  var mutationQueues = new Map;
1474
1509
  function projectKey(projectDir) {
1475
1510
  const resolved = path4.resolve(projectDir);
@@ -1561,7 +1596,6 @@ async function describeFolder(dir, base, fallbackLabel, labelFrom = prettify) {
1561
1596
  ...meta.description?.trim() ? { description: meta.description.trim() } : {},
1562
1597
  ...meta.author?.trim() ? { author: meta.author.trim() } : {},
1563
1598
  ...meta.preview !== undefined ? { preview: meta.preview } : {},
1564
- ...meta.tokensFile?.trim() ? { tokensFile: meta.tokensFile.trim() } : {},
1565
1599
  ...styles.length > 0 ? { styles } : {},
1566
1600
  carries: extensionCarries(meta),
1567
1601
  dir
@@ -1585,15 +1619,14 @@ async function describeEntry(projectDir, raw) {
1585
1619
  return withWarnings({ ...common, kind: "npm", name: raw.use, label: raw.use, carries: NO_CARRIES });
1586
1620
  }
1587
1621
  if (parsed.kind === "bundled") {
1588
- const bundledName = parsed.name;
1589
- const rec = RECOMMENDED_EXTENSIONS.find((r) => r.use === bundledName);
1622
+ const bundled = bundledExtension(parsed.name);
1590
1623
  return withWarnings({
1591
1624
  ...common,
1592
1625
  kind: "bundled",
1593
- name: bundledName,
1594
- label: rec?.label ?? bundledName,
1595
- ...rec ? { description: rec.description } : {},
1596
- carries: { ...NO_CARRIES, markdown: true }
1626
+ name: parsed.name,
1627
+ label: bundled.label,
1628
+ description: bundled.description,
1629
+ carries: { ...NO_CARRIES, ...bundled.css !== undefined ? { styles: true } : { markdown: true } }
1597
1630
  });
1598
1631
  }
1599
1632
  if (parsed.kind === "path") {
@@ -1715,9 +1748,10 @@ async function removeExtension(projectDir, use) {
1715
1748
  return;
1716
1749
  }
1717
1750
  if (parsed.kind === "npm" && parsed.version) {
1718
- const root = vendoredNpmPluginRoot(projectDir, parsed.name, parsed.version);
1719
- await rm3(root, { recursive: true, force: true });
1720
- await removeEmptyDirs([path4.dirname(root)]);
1751
+ for (const root of vendoredNpmPluginRoots(projectDir, parsed.name, parsed.version)) {
1752
+ await rm3(root, { recursive: true, force: true });
1753
+ await removeEmptyDirs([path4.dirname(root)]);
1754
+ }
1721
1755
  }
1722
1756
  });
1723
1757
  }
@@ -1835,8 +1869,8 @@ async function vendorNpmExtension(projectDir, input, exportName, options, commit
1835
1869
  });
1836
1870
  try {
1837
1871
  const { negated } = await ensureProjectGitignore(projectDir);
1838
- if (negated.includes("plugins/npm/")) {
1839
- warnings.push("Your .gitignore re-includes plugins/npm/, so downloaded extensions will be committed with " + "your book. They shouldn't be: the manifest pins each version and a build downloads a " + "missing copy again.");
1872
+ if (negated.includes("extensions/npm/")) {
1873
+ warnings.push("Your .gitignore re-includes extensions/npm/, so downloaded extensions will be committed with " + "your book. They shouldn't be: the manifest pins each version and a build downloads a " + "missing copy again.");
1840
1874
  }
1841
1875
  } catch (error) {
1842
1876
  warnings.push(`The extension was installed, but .gitignore could not be updated to keep it out of version history: ` + `${error instanceof Error ? error.message : String(error)}`);
@@ -1862,24 +1896,32 @@ async function pinnedVersionsOf(projectDir, name) {
1862
1896
  return versions;
1863
1897
  }
1864
1898
  async function pruneStaleVersions(projectDir, name, keep) {
1865
- const parent = path4.dirname(vendoredNpmPluginRoot(projectDir, name, "0.0.0"));
1899
+ const parents = vendoredNpmPluginRoots(projectDir, name, "0.0.0").map((root) => path4.dirname(root));
1866
1900
  try {
1867
- for (const entry of await readdir(parent, { withFileTypes: true })) {
1868
- if (!entry.isDirectory())
1869
- continue;
1870
- let version;
1901
+ for (const parent of parents) {
1902
+ let entries;
1871
1903
  try {
1872
- version = decodeURIComponent(entry.name);
1873
- } catch {
1874
- continue;
1904
+ entries = await readdir(parent, { withFileTypes: true });
1905
+ } catch (error) {
1906
+ if (error.code === "ENOENT")
1907
+ continue;
1908
+ throw error;
1909
+ }
1910
+ for (const entry of entries) {
1911
+ if (!entry.isDirectory())
1912
+ continue;
1913
+ let version;
1914
+ try {
1915
+ version = decodeURIComponent(entry.name);
1916
+ } catch {
1917
+ continue;
1918
+ }
1919
+ if (!isExactNpmVersion(version) || keep.includes(version))
1920
+ continue;
1921
+ await rm3(path4.join(parent, entry.name), { recursive: true, force: true });
1875
1922
  }
1876
- if (!isExactNpmVersion(version) || keep.includes(version))
1877
- continue;
1878
- await rm3(path4.join(parent, entry.name), { recursive: true, force: true });
1879
1923
  }
1880
1924
  } catch (error) {
1881
- if (error.code === "ENOENT")
1882
- return [];
1883
1925
  return [
1884
1926
  `The extension was installed, but a previous version's downloaded copy could not be removed: ` + `${error instanceof Error ? error.message : String(error)}`
1885
1927
  ];
@@ -1970,20 +2012,6 @@ async function uniqueExtensionId(projectDir, base) {
1970
2012
  }
1971
2013
  return candidate;
1972
2014
  }
1973
- async function addBuiltInStyleSet(projectDir, id) {
1974
- if (!BUILT_IN_STYLE_SET_IDS.includes(id)) {
1975
- throw new Error(`Unknown built-in look "${id}" (one of: ${BUILT_IN_STYLE_SET_IDS.join(", ")}).`);
1976
- }
1977
- return withMutationLock(projectDir, async () => {
1978
- const dest = path4.join(projectDir, EXTENSIONS_DIR, id);
1979
- if (!existsSync3(dest)) {
1980
- const src = path4.join(await getAssetsDir(), "themes", id);
1981
- await mkdir3(path4.dirname(dest), { recursive: true });
1982
- await cp(src, dest, { recursive: true });
1983
- }
1984
- return addExtensionUnlocked(projectDir, `./${EXTENSIONS_DIR}/${id}`, {});
1985
- });
1986
- }
1987
2015
 
1988
2016
  // src/lib/extension-restore.ts
1989
2017
  var OFFLINE_REASON = "you appear to be offline (npm can't be reached)";
@@ -2102,4 +2130,4 @@ async function restoreForCommand(dir, options) {
2102
2130
  log.warn(problem);
2103
2131
  }
2104
2132
 
2105
- export { isPathSpecifier, parseExtensionSpecifier, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, resolveTargets, applyDefaultPdfStrictChecks, MANIFEST_FILENAMES, loadManifest, loadManifestWithPath, resolveConfig, resolveConfigForTarget, FriendlyHttpError, withFetchTimeout, loadManifestDoc, writeManifestDoc, npmRegistryUrl, ensureProjectGitignore, EXTENSIONS_DIR, BUILT_IN_STYLE_SET_IDS, listProjectExtensions, setExtensionEnabled, removeExtension, addExtension, checkExtensionUpdates, updateExtensions, uniqueExtensionId, addBuiltInStyleSet, restorePinnedExtensions, restoreFailureMessage, restoreOfflineMessage, restoreForCommand };
2133
+ export { isPositivePoints, cssLength, formatPageSize, printAreaMeta, pageBoxes, judgeRect, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, resolveTargets, applyDefaultPdfStrictChecks, MANIFEST_FILENAMES, loadManifest, loadManifestWithPath, resolveConfig, resolveConfigForTarget, FriendlyHttpError, withFetchTimeout, loadManifestDoc, writeManifestDoc, npmRegistryUrl, ensureProjectGitignore, listProjectExtensions, setExtensionEnabled, removeExtension, addExtension, checkExtensionUpdates, updateExtensions, uniqueExtensionId, restorePinnedExtensions, restoreFailureMessage, restoreOfflineMessage, restoreForCommand };
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  PACKAGE_VERSION,
13
13
  log
14
- } from "./cli-b0vyjvmt.js";
14
+ } from "./cli-s6djx4ma.js";
15
15
  import {
16
16
  __require
17
17
  } from "./cli-37x76zdn.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  package_default
3
- } from "./cli-nr6j6300.js";
3
+ } from "./cli-5jzyw06s.js";
4
4
 
5
5
  // src/utils/logger.ts
6
6
  var currentLevel = "INFO";