gutterpress 0.11.23-alpha.3 → 0.11.23-alpha.4

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 (69) hide show
  1. package/README.md +4 -6
  2. package/dist/{README-8db7btxb.md → README-syg2hn83.md} +1 -1
  3. package/dist/api/index.js +5 -5
  4. package/dist/{build-m78jcpf7.js → build-c48eer6f.js} +8 -8
  5. package/dist/checks/asset/extensions.d.ts +1 -1
  6. package/dist/checks/asset/index.d.ts +0 -1
  7. package/dist/checks/policy.d.ts +5 -4
  8. package/dist/checks/source/config-file.d.ts +9 -6
  9. package/dist/checks/source/dangling-links.d.ts +1 -1
  10. package/dist/checks/types.d.ts +2 -2
  11. package/dist/{cli-xsrffqzt.js → cli-54xt7pjg.js} +8 -8
  12. package/dist/{cli-2ppw6q9v.js → cli-5crpmz42.js} +80 -1
  13. package/dist/{cli-1h17ktsg.js → cli-93mkaw6v.js} +96 -209
  14. package/dist/{cli-n2tsxqpn.js → cli-d9mm3vvh.js} +7 -2
  15. package/dist/{cli-5jzyw06s.js → cli-gnpea9dx.js} +1 -1
  16. package/dist/{cli-s6djx4ma.js → cli-pspjxnc8.js} +1 -1
  17. package/dist/{cli-t8be9yeb.js → cli-rqh295j9.js} +12 -7
  18. package/dist/{cli-h4x81x97.js → cli-y6d1arem.js} +207 -142
  19. package/dist/cli.js +12 -12
  20. package/dist/{doctor-j71jqce3.js → doctor-ptb346k8.js} +4 -3
  21. package/dist/{engine-m4y10shc.js → engine-4rzdx6af.js} +1 -1
  22. package/dist/{engine-1r6q80tq.js → engine-6txhxmmv.js} +2 -2
  23. package/dist/{ext-fq2hg9va.js → ext-gy8szg3x.js} +8 -8
  24. package/dist/{extension-restore-9dh7c4tp.js → extension-restore-dymzcasz.js} +2 -2
  25. package/dist/{extension-restore-tmxtzkm0.js → extension-restore-m8tj43e7.js} +4 -4
  26. package/dist/{index-2ppw6q9v.js → index-5crpmz42.js} +80 -1
  27. package/dist/{index-nedd07bk.js → index-5kh4c78h.js} +8 -8
  28. package/dist/{index-0wee4408.js → index-8ywkd2sv.js} +12 -7
  29. package/dist/{index-prqvz3xm.js → index-ea94zbq4.js} +132 -228
  30. package/dist/{index-7pck1q9c.js → index-kaks3mm2.js} +206 -141
  31. package/dist/{index-nvmf22rt.js → index-yc3xj266.js} +1 -1
  32. package/dist/index.js +6 -6
  33. package/dist/lib/build-preflight.d.ts +4 -10
  34. package/dist/lib/build-runner.d.ts +1 -1
  35. package/dist/lib/ghostscript.d.ts +5 -2
  36. package/dist/lib/lint-rules.d.ts +5 -1
  37. package/dist/lib/manifest-config.d.ts +3 -2
  38. package/dist/lib/manifest-old-spellings.d.ts +64 -0
  39. package/dist/lib/markdown/chapter-id.d.ts +1 -1
  40. package/dist/lib/markdown/index.d.ts +1 -1
  41. package/dist/lib/markdown/table.d.ts +5 -3
  42. package/dist/lib/page-size.d.ts +14 -9
  43. package/dist/lib/pdf-inspect.d.ts +27 -0
  44. package/dist/lib/presets.d.ts +10 -8
  45. package/dist/lib/printsafe.d.ts +1 -1
  46. package/dist/lib/project-scaffold.d.ts +1 -1
  47. package/dist/lib/remote-auth/sync-messages.d.ts +2 -2
  48. package/dist/lib/targets.d.ts +4 -4
  49. package/dist/lib/validation-exec.d.ts +4 -4
  50. package/dist/{manifest-my7mdv8j.yaml → manifest-2kc56wvw.yaml} +3 -4
  51. package/dist/{manifest-8afhrk30.yaml → manifest-41ddzm13.yaml} +3 -4
  52. package/dist/{manifest-gf13sjp8.yaml → manifest-79zsgkpn.yaml} +3 -4
  53. package/dist/{manifest-d4f9q79e.yaml → manifest-sqzm4681.yaml} +3 -4
  54. package/dist/{manifest.schema-650pe0d1.json → manifest.schema-w86c1xw1.json} +19 -309
  55. package/dist/{new-s4zcaepg.js → new-t2t28ccb.js} +9 -15
  56. package/dist/{page-rules-fj09xct5.css → page-rules-ytj9p04v.css} +1 -1
  57. package/dist/{pdf-inspect-wnw4za71.js → pdf-inspect-b3mf81ex.js} +3 -1
  58. package/dist/{plugins-80emnf31.js → plugins-fec8t2bh.js} +1 -1
  59. package/dist/{plugins-4vaf27n9.js → plugins-sqa4msdk.js} +3 -3
  60. package/dist/{preflight-c7w1xdn3.js → preflight-srq8fktp.js} +8 -8
  61. package/dist/preview/file-watcher.d.ts +1 -3
  62. package/dist/{preview-mzytcwyk.js → preview-zsp9hstd.js} +8 -8
  63. package/dist/{publish-fez14ad4.js → publish-hhy31gr9.js} +8 -8
  64. package/dist/render.js +6 -1
  65. package/dist/schema/manifest.types.d.ts +40 -107
  66. package/dist/{tokens-ye64p899.css → tokens-n82t4k9h.css} +11 -4
  67. package/dist/{validate-p2chr6hr.js → validate-r9jdz85h.js} +8 -8
  68. package/package.json +1 -1
  69. package/dist/checks/asset/approved-fonts.d.ts +0 -3
@@ -14,7 +14,7 @@ import {
14
14
  readExtensionMeta,
15
15
  resolveExtension,
16
16
  slugify
17
- } from "./index-0wee4408.js";
17
+ } from "./index-8ywkd2sv.js";
18
18
  import {
19
19
  BuildError,
20
20
  EXIT_CODES
@@ -212,9 +212,7 @@ var DTRPG_PRESET = {
212
212
  },
213
213
  defaultTargets: ["dtrpg"],
214
214
  extensions: [],
215
- source: {
216
- files: null
217
- },
215
+ chapters: null,
218
216
  print: { signature: 4, reserveLastPage: true, lastPage: undefined },
219
217
  pdfx: {
220
218
  flavor: "x1a",
@@ -239,53 +237,23 @@ var DTRPG_PRESET = {
239
237
  maxTac: 240,
240
238
  tacTolerance: 0.5
241
239
  },
242
- lint: {
243
- enabled: true,
244
- configPath: null,
245
- rules: {}
240
+ checks: {
241
+ "pdf.structure.qpdf": "error",
242
+ "pdf.print.pdfx-markers": "error",
243
+ "pdf.print.pdfx-metadata": "error",
244
+ "pdf.print.embedded-fonts": "error",
245
+ "pdf.print.safe-margins": "error",
246
+ "pdf.print.bleed": "off",
247
+ "pdf.nav.bookmarks": "off",
248
+ "pdf.nav.toc-links": "off",
249
+ "asset.font.license": "off"
246
250
  },
247
251
  validate: {
248
- enabled: true,
249
- checks: {
250
- "pdf.structure.qpdf": {
251
- enabled: true,
252
- severity: "error"
253
- },
254
- "pdf.print.pdfx-markers": {
255
- enabled: true,
256
- severity: "error"
257
- },
258
- "pdf.print.pdfx-metadata": {
259
- enabled: true,
260
- severity: "error"
261
- },
262
- "pdf.print.embedded-fonts": {
263
- enabled: true,
264
- severity: "error"
265
- },
266
- "pdf.print.safe-margins": {
267
- enabled: true,
268
- severity: "error"
269
- }
270
- },
271
- source: {
272
- markdownlint: null,
273
- htmlhint: null,
274
- stylelint: null,
275
- cssOwnership: null
276
- },
277
252
  assets: {
278
- maxImageSize: 1e7,
279
- allowAlpha: false,
280
- approvedFontFiles: [],
281
- requireFontLicense: false
253
+ maxImageSize: 1e7
282
254
  },
283
255
  pdf: {
284
- requireBookmarks: false,
285
- requireTocLinks: false,
286
256
  minImageResolution: 300,
287
- forbidTransparency: true,
288
- requireBleed: false,
289
257
  bleedSize: 9
290
258
  },
291
259
  heuristics: {
@@ -299,9 +267,7 @@ var BOOK_PRESET = {
299
267
  id: "book",
300
268
  defaultTargets: [],
301
269
  extensions: [],
302
- source: {
303
- files: null
304
- },
270
+ chapters: null,
305
271
  print: { signature: 1, reserveLastPage: false, lastPage: undefined },
306
272
  pdfx: {
307
273
  flavor: "x1a",
@@ -326,47 +292,25 @@ var BOOK_PRESET = {
326
292
  maxTac: 400,
327
293
  tacTolerance: 0.5
328
294
  },
329
- lint: {
330
- enabled: true,
331
- configPath: null,
332
- rules: {}
295
+ checks: {
296
+ "pdf.structure.qpdf": "error",
297
+ "pdf.print.embedded-fonts": "error",
298
+ "pdf.print.safe-margins": "warning",
299
+ "pdf.print.pdfx-markers": "off",
300
+ "pdf.print.pdfx-metadata": "off",
301
+ "pdf.print.transparency": "off",
302
+ "asset.image.alpha-channel": "off",
303
+ "pdf.print.bleed": "off",
304
+ "pdf.nav.bookmarks": "off",
305
+ "pdf.nav.toc-links": "off",
306
+ "asset.font.license": "off"
333
307
  },
334
308
  validate: {
335
- enabled: true,
336
- checks: {
337
- "pdf.structure.qpdf": {
338
- enabled: true,
339
- severity: "error"
340
- },
341
- "pdf.print.embedded-fonts": {
342
- enabled: true,
343
- severity: "error"
344
- },
345
- "pdf.print.safe-margins": {
346
- enabled: true,
347
- severity: "warning"
348
- },
349
- "pdf.print.pdfx-markers": false,
350
- "pdf.print.pdfx-metadata": false
351
- },
352
- source: {
353
- markdownlint: null,
354
- htmlhint: null,
355
- stylelint: null,
356
- cssOwnership: null
357
- },
358
309
  assets: {
359
- maxImageSize: 1e7,
360
- allowAlpha: true,
361
- approvedFontFiles: [],
362
- requireFontLicense: false
310
+ maxImageSize: 1e7
363
311
  },
364
312
  pdf: {
365
- requireBookmarks: false,
366
- requireTocLinks: false,
367
313
  minImageResolution: 300,
368
- forbidTransparency: false,
369
- requireBleed: false,
370
314
  bleedSize: 9
371
315
  },
372
316
  heuristics: {
@@ -419,16 +363,16 @@ var DTRPG_TARGET = {
419
363
  maxTac: DTRPG_PRESET.ink.maxTac,
420
364
  tacTolerance: DTRPG_PRESET.ink.tacTolerance
421
365
  },
366
+ checks: {
367
+ "pdf.structure.qpdf": "error",
368
+ "pdf.print.pdfx-markers": "error",
369
+ "pdf.print.pdfx-metadata": "error",
370
+ "pdf.print.embedded-fonts": "error",
371
+ "pdf.print.safe-margins": "error",
372
+ "pdf.print.transparency": "on"
373
+ },
422
374
  validate: {
423
- checks: {
424
- "pdf.structure.qpdf": { enabled: true, severity: "error" },
425
- "pdf.print.pdfx-markers": { enabled: true, severity: "error" },
426
- "pdf.print.pdfx-metadata": { enabled: true, severity: "error" },
427
- "pdf.print.embedded-fonts": { enabled: true, severity: "error" },
428
- "pdf.print.safe-margins": { enabled: true, severity: "error" }
429
- },
430
375
  pdf: {
431
- forbidTransparency: DTRPG_PRESET.validate.pdf.forbidTransparency,
432
376
  minImageResolution: DTRPG_PRESET.validate.pdf.minImageResolution
433
377
  }
434
378
  }
@@ -446,22 +390,22 @@ var ITCH_TARGET = {
446
390
  id: "itch",
447
391
  label: "itch.io",
448
392
  overlay: {
393
+ checks: {
394
+ "pdf.structure.qpdf": "error",
395
+ "pdf.print.embedded-fonts": "error",
396
+ "pdf.print.pdfx-markers": "off",
397
+ "pdf.print.pdfx-metadata": "off",
398
+ "pdf.print.ink-coverage": "off",
399
+ "pdf.print.safe-margins": "off",
400
+ "pdf.print.transparency": "off",
401
+ "pdf.print.bleed": "off",
402
+ "asset.image.alpha-channel": "off"
403
+ },
449
404
  validate: {
450
- checks: {
451
- "pdf.structure.qpdf": { enabled: true, severity: "error" },
452
- "pdf.print.embedded-fonts": { enabled: true, severity: "error" },
453
- "pdf.print.pdfx-markers": false,
454
- "pdf.print.pdfx-metadata": false,
455
- "pdf.print.ink-coverage": false,
456
- "pdf.print.safe-margins": false
457
- },
458
405
  assets: {
459
- allowAlpha: true,
460
406
  maxImageSize: 0
461
407
  },
462
408
  pdf: {
463
- forbidTransparency: false,
464
- requireBleed: false,
465
409
  minImageResolution: 0
466
410
  }
467
411
  }
@@ -500,9 +444,9 @@ function overlayPreset(preset, target) {
500
444
  ...preset,
501
445
  pdfx: { ...preset.pdfx, ...o.pdfx },
502
446
  ink: { ...preset.ink, ...o.ink },
447
+ checks: { ...preset.checks, ...o.checks },
503
448
  validate: {
504
449
  ...preset.validate,
505
- checks: { ...preset.validate.checks, ...o.validate?.checks },
506
450
  assets: { ...preset.validate.assets, ...o.validate?.assets },
507
451
  pdf: { ...preset.validate.pdf, ...o.validate?.pdf }
508
452
  }
@@ -517,13 +461,120 @@ var DEFAULT_STRICT_PDF_CHECKS = [
517
461
  function applyDefaultPdfStrictChecks(config) {
518
462
  const next = structuredClone(config);
519
463
  for (const checkId of DEFAULT_STRICT_PDF_CHECKS) {
520
- if (next.validate.checks[checkId] === undefined) {
521
- next.validate.checks[checkId] = { enabled: true, severity: "error" };
522
- }
464
+ if (next.checks[checkId] === undefined)
465
+ next.checks[checkId] = "error";
523
466
  }
524
467
  return next;
525
468
  }
526
469
 
470
+ // src/lib/manifest-old-spellings.ts
471
+ var SOURCE_TOOL_CHECKS = {
472
+ markdownlint: "source.markdownlint",
473
+ htmlhint: "source.htmlhint",
474
+ stylelint: "source.stylelint",
475
+ cssOwnership: "source.css-ownership"
476
+ };
477
+ var FLAG_CHECKS = {
478
+ "assets.allowAlpha": { check: "asset.image.alpha-channel", trueMeansOn: false },
479
+ "assets.requireFontLicense": { check: "asset.font.license", trueMeansOn: true },
480
+ "pdf.requireBookmarks": { check: "pdf.nav.bookmarks", trueMeansOn: true },
481
+ "pdf.requireTocLinks": { check: "pdf.nav.toc-links", trueMeansOn: true },
482
+ "pdf.requireBleed": { check: "pdf.print.bleed", trueMeansOn: true },
483
+ "pdf.forbidTransparency": { check: "pdf.print.transparency", trueMeansOn: true }
484
+ };
485
+ function isObject(v) {
486
+ return typeof v === "object" && v !== null && !Array.isArray(v);
487
+ }
488
+ function readOldSpellings(raw) {
489
+ const { source, lint, validate, ink, ...manifest } = raw;
490
+ const tolerance = isObject(raw.page) && "tolerance" in raw.page;
491
+ if (source === undefined && lint === undefined && validate === undefined && ink === undefined && !tolerance) {
492
+ return raw;
493
+ }
494
+ if (isObject(source) && source.assets !== undefined) {
495
+ throw new UsageError("Manifest field `source.assets` is no longer supported — remove it.\n" + " Assets are discovered from what your book actually references, so no list is needed.");
496
+ }
497
+ const renamed = [];
498
+ const dropped = [];
499
+ const checks = {};
500
+ if (isObject(source) && "files" in source) {
501
+ renamed.push("source.files -> chapters");
502
+ if (manifest.chapters === undefined)
503
+ manifest.chapters = source.files ?? null;
504
+ }
505
+ if (isObject(validate)) {
506
+ for (const [id, entry] of Object.entries(validate.checks ?? {})) {
507
+ if (entry === false || isObject(entry) && entry.enabled === false)
508
+ checks[id] = "off";
509
+ else if (isObject(entry) && typeof entry.severity === "string")
510
+ checks[id] = entry.severity;
511
+ else
512
+ checks[id] = "on";
513
+ }
514
+ if (validate.checks !== undefined)
515
+ renamed.push("validate.checks -> checks");
516
+ for (const [tool, value] of Object.entries(validate.source ?? {})) {
517
+ const id = SOURCE_TOOL_CHECKS[tool];
518
+ if (id && value === false) {
519
+ checks[id] = "off";
520
+ renamed.push(`validate.source.${tool}: false -> checks: { ${id}: off }`);
521
+ } else if (id && tool !== "stylelint" && typeof value === "string") {
522
+ dropped.push(`validate.source.${tool} (the config file is found by its usual name in the book's folder or a folder above it)`);
523
+ } else {
524
+ dropped.push(`validate.source.${tool}`);
525
+ }
526
+ }
527
+ for (const block of ["assets", "pdf", "heuristics"]) {
528
+ for (const [key, value] of Object.entries(validate[block] ?? {})) {
529
+ const flag = FLAG_CHECKS[`${block}.${key}`];
530
+ if (flag && typeof value === "boolean") {
531
+ const level = value === flag.trueMeansOn ? "on" : "off";
532
+ checks[flag.check] = level;
533
+ renamed.push(`validate.${block}.${key}: ${value} -> checks: { ${flag.check}: ${level} }`);
534
+ } else {
535
+ dropped.push(`validate.${block}.${key} (the preset and the publish target set it)`);
536
+ }
537
+ }
538
+ }
539
+ if (validate.enabled !== undefined) {
540
+ dropped.push("validate.enabled (`gutterpress build --skip-pre-validate --skip-post-validate` skips validation for one build)");
541
+ }
542
+ }
543
+ if (isObject(lint)) {
544
+ for (const [id, level] of Object.entries(lint.rules ?? {}))
545
+ checks[id] = level;
546
+ if (lint.rules !== undefined)
547
+ renamed.push("lint.rules -> checks");
548
+ if (lint.enabled === false) {
549
+ checks["source.stylelint"] = "off";
550
+ renamed.push("lint.enabled: false -> checks: { source.stylelint: off }");
551
+ } else if (lint.enabled !== undefined) {
552
+ dropped.push("lint.enabled");
553
+ }
554
+ if (lint.configPath !== undefined)
555
+ dropped.push("lint.configPath");
556
+ }
557
+ if (ink !== undefined)
558
+ dropped.push("ink (the preset and the publish target set it)");
559
+ if (tolerance) {
560
+ const { tolerance: _, ...page } = raw.page;
561
+ manifest.page = page;
562
+ dropped.push("page.tolerance (the preset sets it)");
563
+ }
564
+ if (Object.keys(checks).length > 0)
565
+ manifest.checks = { ...checks, ...manifest.checks };
566
+ const lines = [
567
+ ...renamed.map((r) => ` ${r}`),
568
+ ...dropped.map((d) => ` ${d} — no longer a setting, ignored`)
569
+ ];
570
+ if (lines.length > 0) {
571
+ warnOnce(`old-manifest-spellings:${lines.join("|")}`, "[gutterpress] manifest.yaml uses spellings 0.11.23 replaced. They still work in this " + `release; rewrite them before the next one:
572
+ ` + lines.join(`
573
+ `));
574
+ }
575
+ return manifest;
576
+ }
577
+
527
578
  // src/lib/manifest.ts
528
579
  var MANIFEST_FILENAMES = [
529
580
  "manifest.yaml"
@@ -551,8 +602,8 @@ async function loadManifestWithPath(pathOrDir, opts) {
551
602
  throw new UsageError(`"${p}" has two versions of a setting — yours and the online version — left by a sync. Open it, keep the one you want and delete the marker lines.`);
552
603
  }
553
604
  try {
554
- const manifest = parseYaml(raw) ?? {};
555
- return { manifest, manifestDir: dirname(p), manifestPath: p };
605
+ const parsed = parseYaml(raw) ?? {};
606
+ return { manifest: readOldSpellings(parsed), manifestDir: dirname(p), manifestPath: p };
556
607
  } catch (error) {
557
608
  if (!(error instanceof YAMLParseError))
558
609
  throw error;
@@ -688,7 +739,7 @@ function resolveCustomPage(c, m) {
688
739
  return {
689
740
  width,
690
741
  height,
691
- tolerance: c.page?.tolerance ?? m.page?.tolerance ?? 0.5,
742
+ tolerance: 0.5,
692
743
  bleed: c.page?.bleed ?? m.page?.bleed,
693
744
  safe: c.page?.safe ?? m.page?.safe,
694
745
  artSafe: c.page?.artSafe ?? m.page?.artSafe,
@@ -717,22 +768,8 @@ function validatePageMargin(margin, size) {
717
768
  function resolveWithPreset(cliOverrides, manifest, preset) {
718
769
  const m = manifest;
719
770
  const c = cliOverrides;
720
- const legacy = m;
721
- if (legacy.output !== undefined || legacy.source?.assets !== undefined) {
722
- const stale = [
723
- legacy.output !== undefined ? "`output`" : null,
724
- legacy.source?.assets !== undefined ? "`source.assets`" : null
725
- ].filter(Boolean);
726
- throw new UsageError(`Manifest field(s) ${stale.join(" and ")} are no longer supported — remove them.
727
- ` + " Output goes to `dist/<title-slug>/` automatically (use `--out` for a one-off location).\n" + " Assets are discovered from what your book actually references, so no list is needed.");
728
- }
729
- if ((m.validate?.source?.allowedCallouts?.length ?? 0) > 0 || (c.validate?.source?.allowedCallouts?.length ?? 0) > 0) {
730
- warnOnce("allowed-callouts-deprecated", "[gutterpress] manifest field `validate.source.allowedCallouts` is " + "deprecated and ignored. The `:::` container syntax it gated was " + "removed 2026-05-17. See docs/migrations/2026-05-removing-container-syntax.md.");
731
- }
732
- for (const key of ["minImageDpi", "allowedColorSpaces"]) {
733
- if (m.validate?.assets?.[key] !== undefined || c.validate?.assets?.[key] !== undefined) {
734
- warnOnce(`assets-${key}-deprecated`, `[gutterpress] manifest field \`validate.assets.${key}\` is deprecated and ` + "ignored. The source-image check it configured was removed; the build " + "and the PDF checks measure what actually prints.");
735
- }
771
+ if (m.output !== undefined) {
772
+ throw new UsageError("Manifest field `output` is no longer supported — remove it.\n" + " Output goes to `dist/<title-slug>/` automatically (use `--out` for a one-off location).");
736
773
  }
737
774
  const removed = m;
738
775
  if (removed.engine !== undefined) {
@@ -778,25 +815,37 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
778
815
  styles: c.styles ?? m.styles,
779
816
  extensions,
780
817
  targets: resolveTargets(c.targets ?? m.targets, preset.defaultTargets),
781
- source: mergeShape(c.source, m.source, preset.source),
818
+ chapters: c.chapters ?? m.chapters ?? preset.chapters,
819
+ checks: { ...preset.checks, ...resolveCheckLevels(m.checks), ...resolveCheckLevels(c.checks) },
782
820
  pdfx: mergeShape(c.pdfx, m.pdfx, preset.pdfx),
783
821
  print: mergeShape(c.print, m.print, preset.print),
784
822
  page,
785
- ink: mergeShape(c.ink, m.ink, preset.ink),
786
- lint: {
787
- ...mergeShape(c.lint, m.lint, preset.lint),
788
- rules: { ...preset.lint.rules, ...m.lint?.rules, ...c.lint?.rules }
789
- },
790
- validate: {
791
- enabled: c.validate?.enabled ?? m.validate?.enabled ?? preset.validate.enabled,
792
- checks: { ...preset.validate.checks, ...m.validate?.checks, ...c.validate?.checks },
793
- source: mergeShape(c.validate?.source, m.validate?.source, preset.validate.source),
794
- assets: mergeShape(c.validate?.assets, m.validate?.assets, preset.validate.assets),
795
- pdf: mergeShape(c.validate?.pdf, m.validate?.pdf, preset.validate.pdf),
796
- heuristics: mergeShape(c.validate?.heuristics, m.validate?.heuristics, preset.validate.heuristics)
797
- }
823
+ ink: structuredClone(preset.ink),
824
+ validate: structuredClone(preset.validate)
798
825
  };
799
826
  }
827
+ function resolveCheckLevels(input) {
828
+ if (input === undefined || input === null)
829
+ return {};
830
+ if (!isPlainObject(input)) {
831
+ throw new UsageError("Manifest field `checks` must map check ids to off, on, info, warn or error.");
832
+ }
833
+ const out = {};
834
+ for (const [id, value] of Object.entries(input)) {
835
+ if (value === false || value === "off")
836
+ out[id] = "off";
837
+ else if (value === true || value === "on")
838
+ out[id] = "on";
839
+ else if (value === "warn" || value === "warning")
840
+ out[id] = "warning";
841
+ else if (value === "info" || value === "error")
842
+ out[id] = value;
843
+ else {
844
+ throw new UsageError(`Manifest field \`checks.${id}\` must be off, on, info, warn or error, got ${JSON.stringify(value)}.`);
845
+ }
846
+ }
847
+ return out;
848
+ }
800
849
 
801
850
  // src/lib/manifest-doc.ts
802
851
  function resolveManifestPath(projectDir) {
@@ -1522,6 +1571,7 @@ function ignoreStatus(gitignore, entry) {
1522
1571
  return dir.unignored || file.unignored ? "negated" : "open";
1523
1572
  }
1524
1573
  var PROJECT_GITIGNORE_ENTRIES = ["dist/", "extensions/npm/"];
1574
+ var LEGACY_NPM_LINE = /^\/?plugins\/npm\/?[ \t]*\r?\n?/gm;
1525
1575
  async function ensureProjectGitignore(projectDir) {
1526
1576
  const gitignorePath = path3.join(projectDir, ".gitignore");
1527
1577
  let existing = null;
@@ -1538,10 +1588,11 @@ async function ensureProjectGitignore(projectDir) {
1538
1588
  negated.push(entry);
1539
1589
  }
1540
1590
  if (missing.length > 0) {
1541
- const lead = existing !== null && existing.length > 0 && !existing.endsWith(`
1591
+ const kept = missing.includes("extensions/npm/") ? (existing ?? "").replace(LEGACY_NPM_LINE, "") : existing ?? "";
1592
+ const lead = kept.length > 0 && !kept.endsWith(`
1542
1593
  `) ? `
1543
1594
  ` : "";
1544
- await writeFile(gitignorePath, `${existing ?? ""}${lead}${missing.join(`
1595
+ await writeFile(gitignorePath, `${kept}${lead}${missing.join(`
1545
1596
  `)}
1546
1597
  `, "utf8");
1547
1598
  }
@@ -1934,6 +1985,7 @@ async function vendorNpmExtension(projectDir, input, exportName, options, commit
1934
1985
  await finalizeNpmPluginInstall(installed).catch((error) => {
1935
1986
  warnings.push(`The extension was installed, but an old backup could not be removed: ` + `${error instanceof Error ? error.message : String(error)}`);
1936
1987
  });
1988
+ warnings.push(...await retireLegacyCopies(projectDir, installed.name));
1937
1989
  try {
1938
1990
  const { negated } = await ensureProjectGitignore(projectDir);
1939
1991
  if (negated.includes("extensions/npm/")) {
@@ -1944,6 +1996,19 @@ async function vendorNpmExtension(projectDir, input, exportName, options, commit
1944
1996
  }
1945
1997
  return { name: installed.name, version: installed.version, use, warnings };
1946
1998
  }
1999
+ async function retireLegacyCopies(projectDir, name) {
2000
+ const legacyPackageDir = path4.dirname(vendoredNpmPluginRoots(projectDir, name, "0.0.0")[1]);
2001
+ const legacyNpmDir = path4.dirname(legacyPackageDir);
2002
+ try {
2003
+ await rm3(legacyPackageDir, { recursive: true, force: true });
2004
+ await removeEmptyDirs([path4.dirname(legacyNpmDir), legacyNpmDir]);
2005
+ } catch (error) {
2006
+ return [
2007
+ `The extension was installed, but the copy an older version of Gutterpress kept under plugins/npm/ ` + `could not be removed: ${error instanceof Error ? error.message : String(error)}`
2008
+ ];
2009
+ }
2010
+ return [];
2011
+ }
1947
2012
  async function pinnedVersionsOf(projectDir, name) {
1948
2013
  const { doc } = await loadManifestDoc(projectDir);
1949
2014
  const seq = doc.get("extensions", true);
@@ -19,7 +19,7 @@ import {
19
19
  stringValueAt,
20
20
  toFolioPage,
21
21
  wantsRecto
22
- } from "./index-nedd07bk.js";
22
+ } from "./index-5kh4c78h.js";
23
23
  import {
24
24
  launchChromium
25
25
  } from "./index-bt0qws5c.js";
package/dist/index.js CHANGED
@@ -116,7 +116,7 @@ import {
116
116
  substituteVariables,
117
117
  syncProject,
118
118
  testRemoteAccess
119
- } from "./index-prqvz3xm.js";
119
+ } from "./index-ea94zbq4.js";
120
120
  import {
121
121
  DEFAULT_NPM_REGISTRY,
122
122
  MANIFEST_FILENAMES,
@@ -146,7 +146,7 @@ import {
146
146
  setExtensionEnabled,
147
147
  updateExtensions,
148
148
  validateProjectExtensions
149
- } from "./index-7pck1q9c.js";
149
+ } from "./index-kaks3mm2.js";
150
150
  import {
151
151
  BUNDLED_EXTENSIONS,
152
152
  EXTENSION_MANIFEST_FILENAME,
@@ -162,16 +162,16 @@ import {
162
162
  pathEscapesFolder,
163
163
  readExtensionMeta,
164
164
  resolveExtension
165
- } from "./index-0wee4408.js";
165
+ } from "./index-8ywkd2sv.js";
166
166
  import"./index-ht1bcqdd.js";
167
167
  import {
168
168
  BUILD_DIAGNOSTIC_CODES
169
- } from "./index-nvmf22rt.js";
170
- import"./index-nedd07bk.js";
169
+ } from "./index-yc3xj266.js";
170
+ import"./index-5kh4c78h.js";
171
171
  import {
172
172
  BuildError
173
173
  } from "./index-46ycxe6r.js";
174
- import"./index-2ppw6q9v.js";
174
+ import"./index-5crpmz42.js";
175
175
  import {
176
176
  DEFAULT_PRINT_OPTS,
177
177
  assertMilestone,
@@ -28,9 +28,10 @@ export interface Gates {
28
28
  * Disable the `source.stylelint` check (CSS print-safety: remote urls,
29
29
  * risky print effects, page-containment) for this run, without touching
30
30
  * `preValidate` itself (#272 — one CSS gate, not two). `true` when
31
- * `--skip-lint` was passed or `config.lint.enabled === false`; consulted
32
- * only when `preValidate` is also true — with `preValidate` off, no
33
- * pre-build check (CSS included) runs at all, `--skip-lint` or not.
31
+ * `--skip-lint` was passed (a book switches it off for good with
32
+ * `checks: { source.stylelint: off }`); consulted only when `preValidate`
33
+ * is also true — with `preValidate` off, no pre-build check (CSS
34
+ * included) runs at all, `--skip-lint` or not.
34
35
  */
35
36
  skipStylelint: boolean;
36
37
  preValidate: boolean;
@@ -40,11 +41,4 @@ export declare function computeGates(format: BuildFormat, opts: {
40
41
  skipLint?: boolean;
41
42
  skipPreValidate?: boolean;
42
43
  skipPostValidate?: boolean;
43
- }, config: {
44
- lint: {
45
- enabled: boolean;
46
- };
47
- validate: {
48
- enabled: boolean;
49
- };
50
44
  }): Gates;
@@ -131,7 +131,7 @@ export interface BuildContext {
131
131
  inputDir: string;
132
132
  /**
133
133
  * THE anchor every manifest-relative path resolves against: `styles:`,
134
- * `source.files`, authored plugin `path:` entries, and the CSS print-safety
134
+ * `chapters`, authored plugin `path:` entries, and the CSS print-safety
135
135
  * check's stylesheet resolution. Equal to {@link BuildContext.manifestDir}.
136
136
  *
137
137
  * It is NOT `inputDir`. The two are identical in the normative layout (the
@@ -1,4 +1,4 @@
1
- import type { UnsplittableReason } from "./pdf-inspect";
1
+ import { type UnsplittableReason } from "./pdf-inspect";
2
2
  /**
3
3
  * Resolve the Ghostscript command on every supported platform.
4
4
  *
@@ -57,7 +57,10 @@ export declare function transparentPages(pdfBytes: Uint8Array): Promise<number[]
57
57
  * these shadings.
58
58
  *
59
59
  * Walks page resources (and form XObjects they draw) rather than every
60
- * object, so only shadings a page can actually paint are reported. Like
60
+ * object, so only shadings a page can actually paint are reported; of those
61
+ * pages, only the ones that paint a shading with a visible area (#438: a box
62
+ * that opens the NEXT page is also in this page's content as a sliver —
63
+ * `pagesWithVisibleShadingPaints`, pdf-inspect.ts). Like
61
64
  * {@link transparentPages}, it never fails a build: an unparseable PDF
62
65
  * reports no pages.
63
66
  */
@@ -1,5 +1,9 @@
1
1
  export type LintLevel = "off" | "warn" | "error";
2
- /** Read one rule's configured level; unrecognised values are ignored. */
2
+ /**
3
+ * Read one rule's configured level; unrecognised values (and `on`) leave the
4
+ * rule at its own severity. A lint rule reports at warning or error, so
5
+ * `info` reads as warning.
6
+ */
3
7
  export declare function ruleLevel(rules: Readonly<Record<string, unknown>> | undefined, id: string): LintLevel | undefined;
4
8
  /**
5
9
  * The rule ids named by a `gutterpress-disable-next-line <id>[, <id>...]
@@ -2,7 +2,7 @@
2
2
  export interface ProjectConfigFields {
3
3
  title?: string;
4
4
  authors?: string[];
5
- /** `source.files` — the markdown inputs (null means "all chapter files"). */
5
+ /** `chapters` — the markdown inputs (null means "all chapter files"). */
6
6
  sourceFiles?: string[] | null;
7
7
  /**
8
8
  * `targets:` — the publish destinations this book is validated against
@@ -16,7 +16,8 @@ export interface ProjectConfigFields {
16
16
  * Apply the author-facing manifest field updates in a single yaml round-trip.
17
17
  * Only the keys present in `updates` are written; everything else is left
18
18
  * untouched (so unrelated comments + sections survive). `sourceFiles: null`
19
- * deletes the `source.files` entry (back to "all chapter files").
19
+ * deletes the `chapters` entry (back to "all chapter files"). Either way an
20
+ * older book's `source.files` is taken out, so the list has one spelling.
20
21
  */
21
22
  export declare function setManifestFields(projectDir: string, updates: ProjectConfigFields): Promise<ProjectConfigFields>;
22
23
  /**