gutterpress 0.11.20 → 0.11.21-alpha.1
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/README.md +1 -1
- package/dist/api/index.js +2 -2
- package/dist/{build-t49xka15.js → build-jd5ky1xh.js} +4 -4
- package/dist/checks/asset/extensions.d.ts +9 -5
- package/dist/checks/asset/index.d.ts +0 -3
- package/dist/checks/pdf/index.d.ts +0 -1
- package/dist/checks/types.d.ts +8 -1
- package/dist/{cli-8gm4p42f.js → cli-3zab895b.js} +2 -2
- package/dist/{cli-nh16svye.js → cli-a8azwpgj.js} +1 -1
- package/dist/{cli-cyej3p9v.js → cli-b57adna7.js} +5 -2
- package/dist/{cli-stt6691c.js → cli-xvtqsqy3.js} +268 -429
- package/dist/cli.js +12 -12
- package/dist/{doctor-53hq2v2n.js → doctor-0kgq1ns6.js} +2 -2
- package/dist/{engine-5fw55dfb.js → engine-0b8bt05n.js} +2 -2
- package/dist/{engine-4rkhv5ga.js → engine-x07q927h.js} +1 -1
- package/dist/{ext-bvgfwtk7.js → ext-f90yv3dv.js} +4 -4
- package/dist/{gutterpress-agent-yvd8w8my.js → gutterpress-agent-d29ry3ak.js} +5 -1
- package/dist/{index-379qafjq.js → index-5qam734s.js} +442 -605
- package/dist/{index-crys9951.js → index-mr72vt8n.js} +2 -2
- package/dist/{index-yw16cfm8.js → index-t7r8g75c.js} +4 -1
- package/dist/index.js +3 -3
- package/dist/lib/asset-inline.d.ts +2 -0
- package/dist/lib/image-inspect.d.ts +0 -1
- package/dist/lib/presets.d.ts +0 -3
- package/dist/lib/validation-exec.d.ts +1 -1
- package/dist/{manifest.schema-c4wfpg6g.json → manifest.schema-n292sb77.json} +7 -10
- package/dist/{new-jzpneexb.js → new-fc7nwxea.js} +4 -4
- package/dist/{preflight-c0f0s3y1.js → preflight-hwvj3k3d.js} +4 -4
- package/dist/{preview-x6wn3vdh.js → preview-ktqjnf3d.js} +4 -4
- package/dist/{publish-rskr6x6b.js → publish-nf3d5cbv.js} +4 -4
- package/dist/schema/manifest.types.d.ts +10 -2
- package/dist/{validate-88w89ye7.js → validate-7aywy6m4.js} +4 -4
- package/package.json +1 -1
- package/dist/checks/asset/image-color-space.d.ts +0 -3
- package/dist/checks/asset/image-resolution.d.ts +0 -3
- package/dist/checks/asset/image-tac.d.ts +0 -3
- package/dist/checks/pdf/rasterized-pages.d.ts +0 -3
|
@@ -16,12 +16,12 @@ import {
|
|
|
16
16
|
stripAnnotations,
|
|
17
17
|
transparentPages,
|
|
18
18
|
warn
|
|
19
|
-
} from "./cli-
|
|
19
|
+
} from "./cli-3zab895b.js";
|
|
20
20
|
import {
|
|
21
21
|
DEBOUNCE,
|
|
22
22
|
UsageError,
|
|
23
23
|
resolvePort
|
|
24
|
-
} from "./cli-
|
|
24
|
+
} from "./cli-a8azwpgj.js";
|
|
25
25
|
import {
|
|
26
26
|
blackTextToGray
|
|
27
27
|
} from "./cli-ht1bcqdd.js";
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
getAssetPath,
|
|
31
31
|
getAssetsDir,
|
|
32
32
|
lastPageMode
|
|
33
|
-
} from "./cli-
|
|
33
|
+
} from "./cli-b57adna7.js";
|
|
34
34
|
import {
|
|
35
35
|
BuildError,
|
|
36
36
|
EXIT_CODES
|
|
@@ -191,8 +191,6 @@ var DTRPG_PRESET = {
|
|
|
191
191
|
},
|
|
192
192
|
assets: {
|
|
193
193
|
maxImageSize: 1e7,
|
|
194
|
-
minImageDpi: 300,
|
|
195
|
-
allowedColorSpaces: ["CMYK", "Grayscale"],
|
|
196
194
|
allowAlpha: false,
|
|
197
195
|
approvedFontFiles: [],
|
|
198
196
|
requireFontLicense: false
|
|
@@ -273,8 +271,6 @@ var BOOK_PRESET = {
|
|
|
273
271
|
},
|
|
274
272
|
assets: {
|
|
275
273
|
maxImageSize: 1e7,
|
|
276
|
-
minImageDpi: 300,
|
|
277
|
-
allowedColorSpaces: ["RGB", "CMYK", "Grayscale"],
|
|
278
274
|
allowAlpha: true,
|
|
279
275
|
approvedFontFiles: [],
|
|
280
276
|
requireFontLicense: false
|
|
@@ -345,9 +341,6 @@ var DTRPG_TARGET = {
|
|
|
345
341
|
"pdf.print.embedded-fonts": { enabled: true, severity: "error" },
|
|
346
342
|
"pdf.print.safe-margins": { enabled: true, severity: "error" }
|
|
347
343
|
},
|
|
348
|
-
assets: {
|
|
349
|
-
allowedColorSpaces: [...DTRPG_PRESET.validate.assets.allowedColorSpaces]
|
|
350
|
-
},
|
|
351
344
|
pdf: {
|
|
352
345
|
forbidTransparency: DTRPG_PRESET.validate.pdf.forbidTransparency,
|
|
353
346
|
minImageResolution: DTRPG_PRESET.validate.pdf.minImageResolution
|
|
@@ -374,16 +367,16 @@ var ITCH_TARGET = {
|
|
|
374
367
|
"pdf.print.pdfx-markers": false,
|
|
375
368
|
"pdf.print.pdfx-metadata": false,
|
|
376
369
|
"pdf.print.ink-coverage": false,
|
|
377
|
-
"pdf.print.safe-margins": false
|
|
378
|
-
"asset.image.tac-raster": false
|
|
370
|
+
"pdf.print.safe-margins": false
|
|
379
371
|
},
|
|
380
372
|
assets: {
|
|
381
|
-
|
|
382
|
-
|
|
373
|
+
allowAlpha: true,
|
|
374
|
+
maxImageSize: 0
|
|
383
375
|
},
|
|
384
376
|
pdf: {
|
|
385
377
|
forbidTransparency: false,
|
|
386
|
-
requireBleed: false
|
|
378
|
+
requireBleed: false,
|
|
379
|
+
minImageResolution: 0
|
|
387
380
|
}
|
|
388
381
|
}
|
|
389
382
|
},
|
|
@@ -613,6 +606,11 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
|
|
|
613
606
|
if ((m.validate?.source?.allowedCallouts?.length ?? 0) > 0 || (c.validate?.source?.allowedCallouts?.length ?? 0) > 0) {
|
|
614
607
|
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.");
|
|
615
608
|
}
|
|
609
|
+
for (const key of ["minImageDpi", "allowedColorSpaces"]) {
|
|
610
|
+
if (m.validate?.assets?.[key] !== undefined || c.validate?.assets?.[key] !== undefined) {
|
|
611
|
+
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.");
|
|
612
|
+
}
|
|
613
|
+
}
|
|
616
614
|
const removed = m;
|
|
617
615
|
if (removed.engine !== undefined) {
|
|
618
616
|
throw new UsageError("Manifest field `engine` was removed — delete it. The Gutterpress engine " + "(native Chromium pagination) is the only engine, so there is nothing to select.");
|
|
@@ -5469,63 +5467,9 @@ var check7 = {
|
|
|
5469
5467
|
};
|
|
5470
5468
|
registerCheck(check7);
|
|
5471
5469
|
|
|
5472
|
-
// src/checks/pdf/rasterized-pages.ts
|
|
5473
|
-
var check8 = {
|
|
5474
|
-
id: "pdf.print.rasterized-pages",
|
|
5475
|
-
name: "Rasterized Pages",
|
|
5476
|
-
description: "Detects pages that appear to be fully rasterized (CSS filters, blend modes, transparency)",
|
|
5477
|
-
category: "pdf",
|
|
5478
|
-
phase: "post-build",
|
|
5479
|
-
async run(ctx) {
|
|
5480
|
-
if (!ctx.pdfPath)
|
|
5481
|
-
return [];
|
|
5482
|
-
const doc = await loadPdf(ctx.pdfPath);
|
|
5483
|
-
if (!doc)
|
|
5484
|
-
return [];
|
|
5485
|
-
const { imagesByPage } = await getOpPass(doc);
|
|
5486
|
-
const { textByPage } = await getTextPass(doc);
|
|
5487
|
-
const rasterizedPages = [];
|
|
5488
|
-
for (const [pageNum, imgs] of imagesByPage) {
|
|
5489
|
-
if (imgs.length !== 1)
|
|
5490
|
-
continue;
|
|
5491
|
-
let page;
|
|
5492
|
-
try {
|
|
5493
|
-
page = await doc.getPage(pageNum);
|
|
5494
|
-
} catch {
|
|
5495
|
-
continue;
|
|
5496
|
-
}
|
|
5497
|
-
const { w: pageW, h: pageH } = getPageSize(page);
|
|
5498
|
-
const img = imgs[0];
|
|
5499
|
-
const widthMatch = Math.abs(img.placedW - pageW) / pageW < 0.03;
|
|
5500
|
-
const heightMatch = Math.abs(img.placedH - pageH) / pageH < 0.03;
|
|
5501
|
-
if (!widthMatch || !heightMatch)
|
|
5502
|
-
continue;
|
|
5503
|
-
const text = (textByPage[pageNum - 1] ?? "").replace(/\s+/g, "");
|
|
5504
|
-
if (text.length > 20 && text.length < 200) {
|
|
5505
|
-
rasterizedPages.push(pageNum);
|
|
5506
|
-
}
|
|
5507
|
-
}
|
|
5508
|
-
if (rasterizedPages.length === 0)
|
|
5509
|
-
return [];
|
|
5510
|
-
rasterizedPages.sort((a, b) => a - b);
|
|
5511
|
-
return [
|
|
5512
|
-
{
|
|
5513
|
-
checkId: check8.id,
|
|
5514
|
-
severity: "warning",
|
|
5515
|
-
message: `Possible rasterized pages detected: ${rasterizedPages.join(", ")}`,
|
|
5516
|
-
file: ctx.pdfPath,
|
|
5517
|
-
detail: "This may indicate CSS filters, blend modes, or transparency that forced flattening. " + "Text on these pages may not be selectable and quality may be reduced.",
|
|
5518
|
-
code: "rasterized-pages-detected",
|
|
5519
|
-
data: { pages: rasterizedPages }
|
|
5520
|
-
}
|
|
5521
|
-
];
|
|
5522
|
-
}
|
|
5523
|
-
};
|
|
5524
|
-
registerCheck(check8);
|
|
5525
|
-
|
|
5526
5470
|
// src/checks/pdf/black-text.ts
|
|
5527
5471
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
5528
|
-
var
|
|
5472
|
+
var check8 = {
|
|
5529
5473
|
id: "pdf.print.black-text",
|
|
5530
5474
|
name: "Black Text",
|
|
5531
5475
|
description: "Warns when text at or below 24pt is painted in a dark four-colour mix (black, or a near-black colour such as a dark brown)",
|
|
@@ -5541,7 +5485,7 @@ var check9 = {
|
|
|
5541
5485
|
if (found.runs === 0)
|
|
5542
5486
|
return [];
|
|
5543
5487
|
return [
|
|
5544
|
-
finding(
|
|
5488
|
+
finding(check8.id, {
|
|
5545
5489
|
severity: "warning",
|
|
5546
5490
|
message: `Dark four-colour text at ${LARGE_TEXT_PT}pt or smaller on page(s) ${found.pages.join(", ")}.`,
|
|
5547
5491
|
file: ctx.pdfPath,
|
|
@@ -5552,17 +5496,17 @@ var check9 = {
|
|
|
5552
5496
|
];
|
|
5553
5497
|
} catch {
|
|
5554
5498
|
return [
|
|
5555
|
-
inspectionFailed(
|
|
5499
|
+
inspectionFailed(check8.id, "Could not inspect the PDF's text colours.", {
|
|
5556
5500
|
file: ctx.pdfPath
|
|
5557
5501
|
})
|
|
5558
5502
|
];
|
|
5559
5503
|
}
|
|
5560
5504
|
}
|
|
5561
5505
|
};
|
|
5562
|
-
registerCheck(
|
|
5506
|
+
registerCheck(check8);
|
|
5563
5507
|
|
|
5564
5508
|
// src/checks/pdf/signature.ts
|
|
5565
|
-
var
|
|
5509
|
+
var check9 = {
|
|
5566
5510
|
id: "pdf.print.signature",
|
|
5567
5511
|
name: "Signature",
|
|
5568
5512
|
description: "Fails when the interior's page count does not fit the print signature (a multiple of it ending on a blank page, or one short of a multiple)",
|
|
@@ -5574,7 +5518,7 @@ var check10 = {
|
|
|
5574
5518
|
return [];
|
|
5575
5519
|
const doc = await loadPdf(ctx.pdfPath);
|
|
5576
5520
|
if (!doc) {
|
|
5577
|
-
return [inspectionFailed(
|
|
5521
|
+
return [inspectionFailed(check9.id, "Could not parse the PDF's page count.", { file: ctx.pdfPath })];
|
|
5578
5522
|
}
|
|
5579
5523
|
const size = ctx.config.print.signature;
|
|
5580
5524
|
const pages = doc.numPages;
|
|
@@ -5583,7 +5527,7 @@ var check10 = {
|
|
|
5583
5527
|
const data = { pages, signature: size };
|
|
5584
5528
|
if (pages % size !== 0) {
|
|
5585
5529
|
return [
|
|
5586
|
-
finding(
|
|
5530
|
+
finding(check9.id, {
|
|
5587
5531
|
severity: "error",
|
|
5588
5532
|
message: `Page count ${pages} does not fit a signature of ${size}: it must be a multiple of ${size}, or one short of one.`,
|
|
5589
5533
|
file: ctx.pdfPath,
|
|
@@ -5599,10 +5543,10 @@ var check10 = {
|
|
|
5599
5543
|
if (!await pageHasMarks(doc, pages))
|
|
5600
5544
|
return [];
|
|
5601
5545
|
} catch {
|
|
5602
|
-
return [inspectionFailed(
|
|
5546
|
+
return [inspectionFailed(check9.id, "Could not read the last page.", { file: ctx.pdfPath })];
|
|
5603
5547
|
}
|
|
5604
5548
|
return [
|
|
5605
|
-
finding(
|
|
5549
|
+
finding(check9.id, {
|
|
5606
5550
|
severity: "error",
|
|
5607
5551
|
message: `The last page (${pages}) has content, but the printer needs it blank for its own information.`,
|
|
5608
5552
|
file: ctx.pdfPath,
|
|
@@ -5613,10 +5557,10 @@ var check10 = {
|
|
|
5613
5557
|
];
|
|
5614
5558
|
}
|
|
5615
5559
|
};
|
|
5616
|
-
registerCheck(
|
|
5560
|
+
registerCheck(check9);
|
|
5617
5561
|
|
|
5618
5562
|
// src/checks/pdf/bookmarks.ts
|
|
5619
|
-
var
|
|
5563
|
+
var check10 = {
|
|
5620
5564
|
id: "pdf.nav.bookmarks",
|
|
5621
5565
|
name: "Bookmarks",
|
|
5622
5566
|
description: "Checks for PDF outline (bookmarks) tree",
|
|
@@ -5630,14 +5574,14 @@ var check11 = {
|
|
|
5630
5574
|
const doc = await loadPdf(ctx.pdfPath);
|
|
5631
5575
|
if (!doc) {
|
|
5632
5576
|
return [
|
|
5633
|
-
inspectionFailed(
|
|
5577
|
+
inspectionFailed(check10.id, "Could not inspect PDF for bookmarks.", {
|
|
5634
5578
|
file: ctx.pdfPath
|
|
5635
5579
|
})
|
|
5636
5580
|
];
|
|
5637
5581
|
}
|
|
5638
5582
|
if (await getOutlineCount(doc) === 0) {
|
|
5639
5583
|
return [
|
|
5640
|
-
finding(
|
|
5584
|
+
finding(check10.id, {
|
|
5641
5585
|
severity: "warning",
|
|
5642
5586
|
message: "PDF does not contain bookmarks (outline tree).",
|
|
5643
5587
|
file: ctx.pdfPath
|
|
@@ -5647,10 +5591,10 @@ var check11 = {
|
|
|
5647
5591
|
return [];
|
|
5648
5592
|
}
|
|
5649
5593
|
};
|
|
5650
|
-
registerCheck(
|
|
5594
|
+
registerCheck(check10);
|
|
5651
5595
|
|
|
5652
5596
|
// src/checks/pdf/toc-links.ts
|
|
5653
|
-
var
|
|
5597
|
+
var check11 = {
|
|
5654
5598
|
id: "pdf.nav.toc-links",
|
|
5655
5599
|
name: "TOC Links",
|
|
5656
5600
|
description: "Detects link annotations on TOC pages",
|
|
@@ -5665,7 +5609,7 @@ var check12 = {
|
|
|
5665
5609
|
if (!doc) {
|
|
5666
5610
|
return [
|
|
5667
5611
|
{
|
|
5668
|
-
checkId:
|
|
5612
|
+
checkId: check11.id,
|
|
5669
5613
|
severity: "warning",
|
|
5670
5614
|
message: "Could not inspect PDF for TOC links.",
|
|
5671
5615
|
file: ctx.pdfPath
|
|
@@ -5676,7 +5620,7 @@ var check12 = {
|
|
|
5676
5620
|
if (!hasLinks) {
|
|
5677
5621
|
return [
|
|
5678
5622
|
{
|
|
5679
|
-
checkId:
|
|
5623
|
+
checkId: check11.id,
|
|
5680
5624
|
severity: "warning",
|
|
5681
5625
|
message: "No link annotations found in PDF (expected for TOC).",
|
|
5682
5626
|
file: ctx.pdfPath
|
|
@@ -5686,10 +5630,10 @@ var check12 = {
|
|
|
5686
5630
|
return [];
|
|
5687
5631
|
}
|
|
5688
5632
|
};
|
|
5689
|
-
registerCheck(
|
|
5633
|
+
registerCheck(check11);
|
|
5690
5634
|
|
|
5691
5635
|
// src/checks/pdf/cross-refs.ts
|
|
5692
|
-
var
|
|
5636
|
+
var check12 = {
|
|
5693
5637
|
id: "pdf.nav.cross-refs",
|
|
5694
5638
|
name: "Cross References",
|
|
5695
5639
|
description: "Counts and verifies internal link annotations",
|
|
@@ -5702,7 +5646,7 @@ var check13 = {
|
|
|
5702
5646
|
if (!doc) {
|
|
5703
5647
|
return [
|
|
5704
5648
|
{
|
|
5705
|
-
checkId:
|
|
5649
|
+
checkId: check12.id,
|
|
5706
5650
|
severity: "info",
|
|
5707
5651
|
message: "Could not inspect PDF for cross-references.",
|
|
5708
5652
|
file: ctx.pdfPath
|
|
@@ -5712,7 +5656,7 @@ var check13 = {
|
|
|
5712
5656
|
const linkCount = await countLinkAnnotations(doc);
|
|
5713
5657
|
return [
|
|
5714
5658
|
{
|
|
5715
|
-
checkId:
|
|
5659
|
+
checkId: check12.id,
|
|
5716
5660
|
severity: "info",
|
|
5717
5661
|
message: `PDF contains ${linkCount} internal link annotation${linkCount !== 1 ? "s" : ""}.`,
|
|
5718
5662
|
file: ctx.pdfPath
|
|
@@ -5720,10 +5664,10 @@ var check13 = {
|
|
|
5720
5664
|
];
|
|
5721
5665
|
}
|
|
5722
5666
|
};
|
|
5723
|
-
registerCheck(
|
|
5667
|
+
registerCheck(check12);
|
|
5724
5668
|
|
|
5725
5669
|
// src/checks/pdf/page-labels.ts
|
|
5726
|
-
var
|
|
5670
|
+
var check13 = {
|
|
5727
5671
|
id: "pdf.nav.page-labels",
|
|
5728
5672
|
name: "Page Labels",
|
|
5729
5673
|
description: "Verifies PDF page labels / folio numbering",
|
|
@@ -5738,7 +5682,7 @@ var check14 = {
|
|
|
5738
5682
|
const labels = await getPageLabels(doc);
|
|
5739
5683
|
return [
|
|
5740
5684
|
{
|
|
5741
|
-
checkId:
|
|
5685
|
+
checkId: check13.id,
|
|
5742
5686
|
severity: "info",
|
|
5743
5687
|
message: labels && labels.length > 0 ? "PDF contains page labels." : "PDF does not contain explicit page labels.",
|
|
5744
5688
|
file: ctx.pdfPath
|
|
@@ -5746,10 +5690,10 @@ var check14 = {
|
|
|
5746
5690
|
];
|
|
5747
5691
|
}
|
|
5748
5692
|
};
|
|
5749
|
-
registerCheck(
|
|
5693
|
+
registerCheck(check13);
|
|
5750
5694
|
|
|
5751
5695
|
// src/checks/pdf/image-resolution.ts
|
|
5752
|
-
var
|
|
5696
|
+
var check14 = {
|
|
5753
5697
|
id: "pdf.print.image-resolution",
|
|
5754
5698
|
name: "Image Resolution",
|
|
5755
5699
|
description: "Checks DPI of images embedded in the PDF",
|
|
@@ -5776,7 +5720,7 @@ var check15 = {
|
|
|
5776
5720
|
lowResPages.sort((a, b) => a - b);
|
|
5777
5721
|
return [
|
|
5778
5722
|
{
|
|
5779
|
-
checkId:
|
|
5723
|
+
checkId: check14.id,
|
|
5780
5724
|
severity: "warning",
|
|
5781
5725
|
message: `Low-resolution images (below ${minDpi} DPI) found on pages: ${lowResPages.join(", ")}`,
|
|
5782
5726
|
file: ctx.pdfPath
|
|
@@ -5786,10 +5730,10 @@ var check15 = {
|
|
|
5786
5730
|
return [];
|
|
5787
5731
|
}
|
|
5788
5732
|
};
|
|
5789
|
-
registerCheck(
|
|
5733
|
+
registerCheck(check14);
|
|
5790
5734
|
|
|
5791
5735
|
// src/checks/pdf/transparency.ts
|
|
5792
|
-
var
|
|
5736
|
+
var check15 = {
|
|
5793
5737
|
id: "pdf.print.transparency",
|
|
5794
5738
|
name: "Transparency",
|
|
5795
5739
|
description: "Checks for transparency markers (/Transparency, /SMask, /BM) in the PDF",
|
|
@@ -5811,7 +5755,7 @@ var check16 = {
|
|
|
5811
5755
|
if (found.length > 0) {
|
|
5812
5756
|
return [
|
|
5813
5757
|
{
|
|
5814
|
-
checkId:
|
|
5758
|
+
checkId: check15.id,
|
|
5815
5759
|
severity: "warning",
|
|
5816
5760
|
message: `Transparency detected in PDF: ${found.join(", ")}. This may cause issues with some printers.`,
|
|
5817
5761
|
file: ctx.pdfPath
|
|
@@ -5821,7 +5765,7 @@ var check16 = {
|
|
|
5821
5765
|
return [];
|
|
5822
5766
|
}
|
|
5823
5767
|
};
|
|
5824
|
-
registerCheck(
|
|
5768
|
+
registerCheck(check15);
|
|
5825
5769
|
|
|
5826
5770
|
// src/checks/pdf/bleed.ts
|
|
5827
5771
|
function findBox(bytes, box) {
|
|
@@ -5831,7 +5775,7 @@ function findBox(bytes, box) {
|
|
|
5831
5775
|
return null;
|
|
5832
5776
|
return [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])];
|
|
5833
5777
|
}
|
|
5834
|
-
var
|
|
5778
|
+
var check16 = {
|
|
5835
5779
|
id: "pdf.print.bleed",
|
|
5836
5780
|
name: "Bleed Box",
|
|
5837
5781
|
description: "Compares MediaBox vs TrimBox/BleedBox to verify bleed area",
|
|
@@ -5845,7 +5789,7 @@ var check17 = {
|
|
|
5845
5789
|
const bytes = await readPdfBytes(ctx.pdfPath).catch(() => "");
|
|
5846
5790
|
if (!bytes) {
|
|
5847
5791
|
return [
|
|
5848
|
-
inspectionFailed(
|
|
5792
|
+
inspectionFailed(check16.id, "Could not inspect PDF for bleed boxes.", {
|
|
5849
5793
|
file: ctx.pdfPath
|
|
5850
5794
|
})
|
|
5851
5795
|
];
|
|
@@ -5855,14 +5799,14 @@ var check17 = {
|
|
|
5855
5799
|
const bleed = findBox(bytes, "BleedBox");
|
|
5856
5800
|
if (!media) {
|
|
5857
5801
|
return [
|
|
5858
|
-
inspectionFailed(
|
|
5802
|
+
inspectionFailed(check16.id, "Could not parse MediaBox from PDF.", {
|
|
5859
5803
|
file: ctx.pdfPath
|
|
5860
5804
|
})
|
|
5861
5805
|
];
|
|
5862
5806
|
}
|
|
5863
5807
|
if (!trim && !bleed) {
|
|
5864
5808
|
return [
|
|
5865
|
-
finding(
|
|
5809
|
+
finding(check16.id, {
|
|
5866
5810
|
severity: "warning",
|
|
5867
5811
|
message: "No TrimBox or BleedBox found. Bleed area cannot be verified.",
|
|
5868
5812
|
file: ctx.pdfPath
|
|
@@ -5875,7 +5819,7 @@ var check17 = {
|
|
|
5875
5819
|
const bleedSize = ctx.config.validate.pdf.bleedSize;
|
|
5876
5820
|
if (mediaW - trimW < bleedSize * 2 * 0.9) {
|
|
5877
5821
|
return [
|
|
5878
|
-
finding(
|
|
5822
|
+
finding(check16.id, {
|
|
5879
5823
|
severity: "warning",
|
|
5880
5824
|
message: `Bleed area appears insufficient. Expected at least ${bleedSize}pt on each side.`,
|
|
5881
5825
|
file: ctx.pdfPath
|
|
@@ -5886,10 +5830,10 @@ var check17 = {
|
|
|
5886
5830
|
return [];
|
|
5887
5831
|
}
|
|
5888
5832
|
};
|
|
5889
|
-
registerCheck(
|
|
5833
|
+
registerCheck(check16);
|
|
5890
5834
|
|
|
5891
5835
|
// src/checks/pdf/pdfx-metadata.ts
|
|
5892
|
-
var
|
|
5836
|
+
var check17 = {
|
|
5893
5837
|
id: "pdf.print.pdfx-metadata",
|
|
5894
5838
|
name: "PDF/X Metadata",
|
|
5895
5839
|
description: "Verifies PDF/X DOCINFO metadata via qpdf JSON",
|
|
@@ -5908,22 +5852,22 @@ var check18 = {
|
|
|
5908
5852
|
const objects = parseQpdfObjectsJson(stdout);
|
|
5909
5853
|
if (!objects) {
|
|
5910
5854
|
return [
|
|
5911
|
-
inspectionFailed(
|
|
5855
|
+
inspectionFailed(check17.id, "Unable to parse qpdf JSON output for PDF/X metadata checks.", { file: ctx.pdfPath })
|
|
5912
5856
|
];
|
|
5913
5857
|
}
|
|
5914
|
-
return getPdfxMetadataIssues(objects, ctx.config.pdfx.flavor).map((message) => finding(
|
|
5858
|
+
return getPdfxMetadataIssues(objects, ctx.config.pdfx.flavor).map((message) => finding(check17.id, {
|
|
5915
5859
|
severity: "error",
|
|
5916
5860
|
message,
|
|
5917
5861
|
file: ctx.pdfPath
|
|
5918
5862
|
}));
|
|
5919
5863
|
} catch {
|
|
5920
5864
|
return [
|
|
5921
|
-
inspectionFailed(
|
|
5865
|
+
inspectionFailed(check17.id, "Failed to inspect PDF objects with qpdf for PDF/X metadata.", { file: ctx.pdfPath })
|
|
5922
5866
|
];
|
|
5923
5867
|
}
|
|
5924
5868
|
}
|
|
5925
5869
|
};
|
|
5926
|
-
registerCheck(
|
|
5870
|
+
registerCheck(check17);
|
|
5927
5871
|
|
|
5928
5872
|
// src/checks/source/markdownlint.ts
|
|
5929
5873
|
import { readFile as readFile9 } from "node:fs/promises";
|
|
@@ -5978,7 +5922,7 @@ async function loadConfig(configPath) {
|
|
|
5978
5922
|
return {};
|
|
5979
5923
|
}
|
|
5980
5924
|
var MARKDOWNLINT_CHECK_ID = "source.markdownlint";
|
|
5981
|
-
var
|
|
5925
|
+
var check18 = {
|
|
5982
5926
|
id: MARKDOWNLINT_CHECK_ID,
|
|
5983
5927
|
name: "Markdownlint",
|
|
5984
5928
|
description: "Runs markdownlint with project config to validate Markdown files",
|
|
@@ -6003,7 +5947,7 @@ var check19 = {
|
|
|
6003
5947
|
continue;
|
|
6004
5948
|
for (const v of violations) {
|
|
6005
5949
|
out.push({
|
|
6006
|
-
checkId:
|
|
5950
|
+
checkId: check18.id,
|
|
6007
5951
|
severity: "warning",
|
|
6008
5952
|
message: `${v.ruleDescription} (${v.ruleNames.join("/")})`,
|
|
6009
5953
|
file,
|
|
@@ -6016,7 +5960,7 @@ var check19 = {
|
|
|
6016
5960
|
return out;
|
|
6017
5961
|
}
|
|
6018
5962
|
};
|
|
6019
|
-
registerCheck(
|
|
5963
|
+
registerCheck(check18);
|
|
6020
5964
|
|
|
6021
5965
|
// src/checks/source/htmlhint.ts
|
|
6022
5966
|
import { existsSync as existsSync7 } from "node:fs";
|
|
@@ -6024,7 +5968,7 @@ import { readFile as readFile10 } from "node:fs/promises";
|
|
|
6024
5968
|
import htmlhint from "htmlhint";
|
|
6025
5969
|
var { HTMLHint } = htmlhint;
|
|
6026
5970
|
var CONFIG_NAMES2 = [".htmlhintrc"];
|
|
6027
|
-
var
|
|
5971
|
+
var check19 = {
|
|
6028
5972
|
id: "source.htmlhint",
|
|
6029
5973
|
name: "HTMLHint",
|
|
6030
5974
|
description: "Runs htmlhint with project config to validate generated HTML",
|
|
@@ -6044,7 +5988,7 @@ var check20 = {
|
|
|
6044
5988
|
const html = await readFile10(ctx.htmlPath, "utf8");
|
|
6045
5989
|
const messages = HTMLHint.verify(html, ruleset);
|
|
6046
5990
|
return messages.map((m) => ({
|
|
6047
|
-
checkId:
|
|
5991
|
+
checkId: check19.id,
|
|
6048
5992
|
severity: m.type === "error" ? "error" : "warning",
|
|
6049
5993
|
message: `${m.rule.id}: ${m.message}`,
|
|
6050
5994
|
file: ctx.htmlPath,
|
|
@@ -6053,7 +5997,7 @@ var check20 = {
|
|
|
6053
5997
|
}));
|
|
6054
5998
|
}
|
|
6055
5999
|
};
|
|
6056
|
-
registerCheck(
|
|
6000
|
+
registerCheck(check19);
|
|
6057
6001
|
|
|
6058
6002
|
// src/checks/source/stylelint.ts
|
|
6059
6003
|
import { readFile as readFile11 } from "node:fs/promises";
|
|
@@ -6172,10 +6116,27 @@ function isUnpairedFitContentWidth(decl) {
|
|
|
6172
6116
|
return true;
|
|
6173
6117
|
}
|
|
6174
6118
|
function targetsPageWrapper(selector) {
|
|
6175
|
-
if (!/\.(page|spread)(?![\w-])/.test(selector))
|
|
6119
|
+
if (!/\.(page|spread)(?![\w-])/.test(subjectCompound(selector)))
|
|
6176
6120
|
return false;
|
|
6177
6121
|
return !/\bgp-(bleed|pin)\b/.test(selector);
|
|
6178
6122
|
}
|
|
6123
|
+
function subjectCompound(selector) {
|
|
6124
|
+
let depth = 0;
|
|
6125
|
+
let start = 0;
|
|
6126
|
+
for (let i = 0;i < selector.length; i++) {
|
|
6127
|
+
const c = selector[i];
|
|
6128
|
+
if (c === "(" || c === "[")
|
|
6129
|
+
depth++;
|
|
6130
|
+
else if (c === ")" || c === "]")
|
|
6131
|
+
depth--;
|
|
6132
|
+
else if (depth === 0 && /[\s>+~]/.test(c))
|
|
6133
|
+
start = i + 1;
|
|
6134
|
+
}
|
|
6135
|
+
let subject = selector.slice(start);
|
|
6136
|
+
while (/\([^()]*\)/.test(subject))
|
|
6137
|
+
subject = subject.replace(/\([^()]*\)/g, "");
|
|
6138
|
+
return subject;
|
|
6139
|
+
}
|
|
6179
6140
|
var cssWideResetKeywords = new Set(["initial", "unset", "revert", "revert-layer"]);
|
|
6180
6141
|
var inertValues = {
|
|
6181
6142
|
"z-index": new Set(["auto"]),
|
|
@@ -6422,7 +6383,7 @@ function checkCssRaw(css, from) {
|
|
|
6422
6383
|
if (at === "page" || marginBoxAtRuleNames.has(at))
|
|
6423
6384
|
return;
|
|
6424
6385
|
}
|
|
6425
|
-
if (!
|
|
6386
|
+
if (!rule.selectors.some(targetsPageWrapper))
|
|
6426
6387
|
return;
|
|
6427
6388
|
rule.walkDecls((decl) => {
|
|
6428
6389
|
if (clipsDescendants(decl)) {
|
|
@@ -6446,7 +6407,7 @@ function checkCssRaw(css, from) {
|
|
|
6446
6407
|
}
|
|
6447
6408
|
|
|
6448
6409
|
// src/checks/source/stylelint.ts
|
|
6449
|
-
var
|
|
6410
|
+
var check20 = {
|
|
6450
6411
|
id: "source.stylelint",
|
|
6451
6412
|
name: "Print-safety CSS",
|
|
6452
6413
|
description: "Checks CSS for print-safety issues (remote URLs, rasterizing effects, and page-layer containment)",
|
|
@@ -6466,7 +6427,7 @@ var check21 = {
|
|
|
6466
6427
|
css = await readFile11(file, "utf8");
|
|
6467
6428
|
} catch (err) {
|
|
6468
6429
|
results.push({
|
|
6469
|
-
checkId:
|
|
6430
|
+
checkId: check20.id,
|
|
6470
6431
|
severity: "error",
|
|
6471
6432
|
message: `Cannot read stylesheet: ${err instanceof Error ? err.message : String(err)}`,
|
|
6472
6433
|
file
|
|
@@ -6475,7 +6436,7 @@ var check21 = {
|
|
|
6475
6436
|
}
|
|
6476
6437
|
for (const w of checkCss(css, file, { rules: ctx.config.lint.rules })) {
|
|
6477
6438
|
results.push({
|
|
6478
|
-
checkId:
|
|
6439
|
+
checkId: check20.id,
|
|
6479
6440
|
severity: w.severity,
|
|
6480
6441
|
message: `${w.rule}: ${w.message}`,
|
|
6481
6442
|
file,
|
|
@@ -6487,7 +6448,7 @@ var check21 = {
|
|
|
6487
6448
|
return results;
|
|
6488
6449
|
}
|
|
6489
6450
|
};
|
|
6490
|
-
registerCheck(
|
|
6451
|
+
registerCheck(check20);
|
|
6491
6452
|
|
|
6492
6453
|
// src/checks/source/css-ownership.ts
|
|
6493
6454
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
@@ -6671,7 +6632,7 @@ function checkOwnership(root, file, maps) {
|
|
|
6671
6632
|
});
|
|
6672
6633
|
return out;
|
|
6673
6634
|
}
|
|
6674
|
-
var
|
|
6635
|
+
var check21 = {
|
|
6675
6636
|
id: "source.css-ownership",
|
|
6676
6637
|
name: "CSS ownership contract",
|
|
6677
6638
|
description: "Checks an optional per-project CSS ownership contract (.gutterpress/css-contract.yaml): which file may declare which properties/at-rules, and which selectors must never appear unscoped.",
|
|
@@ -6694,7 +6655,7 @@ var check22 = {
|
|
|
6694
6655
|
} catch (err) {
|
|
6695
6656
|
return [
|
|
6696
6657
|
{
|
|
6697
|
-
checkId:
|
|
6658
|
+
checkId: check21.id,
|
|
6698
6659
|
severity: "error",
|
|
6699
6660
|
message: `Failed to parse CSS ownership contract "${configPath}": ${err instanceof Error ? err.message : String(err)}`,
|
|
6700
6661
|
file: configPath
|
|
@@ -6736,7 +6697,7 @@ var check22 = {
|
|
|
6736
6697
|
}
|
|
6737
6698
|
for (const v of violations) {
|
|
6738
6699
|
results.push({
|
|
6739
|
-
checkId:
|
|
6700
|
+
checkId: check21.id,
|
|
6740
6701
|
severity: "warning",
|
|
6741
6702
|
message: v.message,
|
|
6742
6703
|
file: absFile,
|
|
@@ -6748,7 +6709,7 @@ var check22 = {
|
|
|
6748
6709
|
return results;
|
|
6749
6710
|
}
|
|
6750
6711
|
};
|
|
6751
|
-
registerCheck(
|
|
6712
|
+
registerCheck(check21);
|
|
6752
6713
|
|
|
6753
6714
|
// src/checks/source/local-refs.ts
|
|
6754
6715
|
import { existsSync as existsSync9 } from "node:fs";
|
|
@@ -7139,7 +7100,7 @@ async function* renderedLocalRefs(ctx, checkId, results) {
|
|
|
7139
7100
|
}
|
|
7140
7101
|
|
|
7141
7102
|
// src/checks/source/local-refs.ts
|
|
7142
|
-
var
|
|
7103
|
+
var check22 = {
|
|
7143
7104
|
id: "source.links.local-refs",
|
|
7144
7105
|
name: "Local Markdown References",
|
|
7145
7106
|
description: "Checks local markdown links and image refs resolve on disk",
|
|
@@ -7147,12 +7108,12 @@ var check23 = {
|
|
|
7147
7108
|
phase: "pre-build",
|
|
7148
7109
|
async run(ctx) {
|
|
7149
7110
|
const results = [];
|
|
7150
|
-
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx,
|
|
7111
|
+
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check22.id, results)) {
|
|
7151
7112
|
if (isNonFilesystemRef(ref))
|
|
7152
7113
|
continue;
|
|
7153
7114
|
const escape = kind === "image" ? proseImageRefError(ref, ctx.inputDir) : null;
|
|
7154
7115
|
if (escape) {
|
|
7155
|
-
results.push(finding(
|
|
7116
|
+
results.push(finding(check22.id, {
|
|
7156
7117
|
severity: "error",
|
|
7157
7118
|
message: escape,
|
|
7158
7119
|
file,
|
|
@@ -7162,7 +7123,7 @@ var check23 = {
|
|
|
7162
7123
|
}
|
|
7163
7124
|
if (localRefExists(ref, kind, file, ctx.inputDir))
|
|
7164
7125
|
continue;
|
|
7165
|
-
results.push(finding(
|
|
7126
|
+
results.push(finding(check22.id, {
|
|
7166
7127
|
severity: kind === "image" ? "warning" : "error",
|
|
7167
7128
|
code: kind === "image" ? "missing-image-placeholder" : undefined,
|
|
7168
7129
|
message: kind === "image" ? `Local image not found; build will substitute a magenta placeholder: ${ref}` : `Local reference not found: ${ref}`,
|
|
@@ -7187,7 +7148,7 @@ function localRefExists(ref, kind, sourceFile, projectRoot) {
|
|
|
7187
7148
|
return existsSync9(fileRelative);
|
|
7188
7149
|
}
|
|
7189
7150
|
}
|
|
7190
|
-
registerCheck(
|
|
7151
|
+
registerCheck(check22);
|
|
7191
7152
|
|
|
7192
7153
|
// src/lib/build-staging.ts
|
|
7193
7154
|
import path8 from "node:path";
|
|
@@ -7453,7 +7414,7 @@ async function createStageRoot() {
|
|
|
7453
7414
|
}
|
|
7454
7415
|
|
|
7455
7416
|
// src/checks/source/dangling-links.ts
|
|
7456
|
-
var
|
|
7417
|
+
var check23 = {
|
|
7457
7418
|
id: "source.links.dangling",
|
|
7458
7419
|
name: "Dangling Links",
|
|
7459
7420
|
description: "Flags relative links the printed book cannot open (their href is dropped at print time)",
|
|
@@ -7461,10 +7422,10 @@ var check24 = {
|
|
|
7461
7422
|
phase: "pre-build",
|
|
7462
7423
|
async run(ctx) {
|
|
7463
7424
|
const results = [];
|
|
7464
|
-
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx,
|
|
7425
|
+
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check23.id, results)) {
|
|
7465
7426
|
if (kind !== "link" || isPrintResolvableHref(ref))
|
|
7466
7427
|
continue;
|
|
7467
|
-
results.push(finding(
|
|
7428
|
+
results.push(finding(check23.id, {
|
|
7468
7429
|
severity: "warning",
|
|
7469
7430
|
code: "dangling-link",
|
|
7470
7431
|
data: { ref },
|
|
@@ -7476,11 +7437,11 @@ var check24 = {
|
|
|
7476
7437
|
return results;
|
|
7477
7438
|
}
|
|
7478
7439
|
};
|
|
7479
|
-
registerCheck(
|
|
7440
|
+
registerCheck(check23);
|
|
7480
7441
|
|
|
7481
7442
|
// src/checks/source/accessibility-alt-text.ts
|
|
7482
7443
|
import { readFile as readFile15 } from "node:fs/promises";
|
|
7483
|
-
var
|
|
7444
|
+
var check24 = {
|
|
7484
7445
|
id: "source.accessibility.alt-text",
|
|
7485
7446
|
name: "Image Alt Text",
|
|
7486
7447
|
description: "Checks markdown images include non-empty alt text",
|
|
@@ -7509,7 +7470,7 @@ var check25 = {
|
|
|
7509
7470
|
if (alt.trim())
|
|
7510
7471
|
continue;
|
|
7511
7472
|
results.push({
|
|
7512
|
-
checkId:
|
|
7473
|
+
checkId: check24.id,
|
|
7513
7474
|
severity: "warning",
|
|
7514
7475
|
message: "Image is missing alt text",
|
|
7515
7476
|
file,
|
|
@@ -7534,11 +7495,11 @@ function extractImageAlts(line) {
|
|
|
7534
7495
|
}
|
|
7535
7496
|
return alts;
|
|
7536
7497
|
}
|
|
7537
|
-
registerCheck(
|
|
7498
|
+
registerCheck(check24);
|
|
7538
7499
|
|
|
7539
7500
|
// src/checks/source/accessibility-heading-order.ts
|
|
7540
7501
|
import { readFile as readFile16 } from "node:fs/promises";
|
|
7541
|
-
var
|
|
7502
|
+
var check25 = {
|
|
7542
7503
|
id: "source.accessibility.heading-order",
|
|
7543
7504
|
name: "Heading Order",
|
|
7544
7505
|
description: "Checks markdown heading levels do not jump by more than one",
|
|
@@ -7566,7 +7527,7 @@ var check26 = {
|
|
|
7566
7527
|
if (inFence)
|
|
7567
7528
|
continue;
|
|
7568
7529
|
const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
|
|
7569
|
-
if (comment && parseDisableDirective(comment[1])?.includes(
|
|
7530
|
+
if (comment && parseDisableDirective(comment[1])?.includes(check25.id)) {
|
|
7570
7531
|
silencedLine = i + 1;
|
|
7571
7532
|
continue;
|
|
7572
7533
|
}
|
|
@@ -7576,7 +7537,7 @@ var check26 = {
|
|
|
7576
7537
|
const level = match[1].length;
|
|
7577
7538
|
if (prevLevel != null && level > prevLevel + 1 && i !== silencedLine) {
|
|
7578
7539
|
results.push({
|
|
7579
|
-
checkId:
|
|
7540
|
+
checkId: check25.id,
|
|
7580
7541
|
severity: "warning",
|
|
7581
7542
|
message: `Heading level jump from h${prevLevel} to h${level}`,
|
|
7582
7543
|
file,
|
|
@@ -7590,7 +7551,7 @@ var check26 = {
|
|
|
7590
7551
|
return results;
|
|
7591
7552
|
}
|
|
7592
7553
|
};
|
|
7593
|
-
registerCheck(
|
|
7554
|
+
registerCheck(check25);
|
|
7594
7555
|
|
|
7595
7556
|
// src/checks/source/layout-markers.ts
|
|
7596
7557
|
import { readFile as readFile17 } from "node:fs/promises";
|
|
@@ -7701,7 +7662,7 @@ async function collectKnownClasses(ctx, plugins) {
|
|
|
7701
7662
|
}
|
|
7702
7663
|
return names;
|
|
7703
7664
|
}
|
|
7704
|
-
var
|
|
7665
|
+
var check26 = {
|
|
7705
7666
|
id: "source.markdown.layout-markers",
|
|
7706
7667
|
name: "Layout Markers",
|
|
7707
7668
|
description: "Reports @page/@section/@chapter marker arguments Gutterpress could not understand, plus any unknown gp-* class and any other class no stylesheet mentions",
|
|
@@ -7713,7 +7674,7 @@ var check27 = {
|
|
|
7713
7674
|
return [];
|
|
7714
7675
|
const results = [];
|
|
7715
7676
|
const plugins = await loadPlugins(ctx.config.extensions, ctx.inputDir, (ref, error2) => {
|
|
7716
|
-
results.push(inspectionFailed(
|
|
7677
|
+
results.push(inspectionFailed(check26.id, `Plugin "${ref}" could not be loaded, so markers it defines were not checked: ${error2.message}`));
|
|
7717
7678
|
});
|
|
7718
7679
|
const md = createMarkdownRenderer(plugins);
|
|
7719
7680
|
let known;
|
|
@@ -7732,7 +7693,7 @@ var check27 = {
|
|
|
7732
7693
|
if (names.has(cls) || warnedLines.has(use.line))
|
|
7733
7694
|
continue;
|
|
7734
7695
|
const guess = nearestKnownClass(cls, names);
|
|
7735
|
-
results.push(finding(
|
|
7696
|
+
results.push(finding(check26.id, {
|
|
7736
7697
|
severity: "warning",
|
|
7737
7698
|
message: `Class ".${cls}" is not in any stylesheet (Gutterpress's own, the plugins', or this book's), so it styles nothing.` + (guess ? ` Did you mean ".${guess}"?` : "") + (use.count > 1 ? ` Used ${use.count} times in this file; the first is reported.` : ""),
|
|
7738
7699
|
file,
|
|
@@ -7742,7 +7703,7 @@ var check27 = {
|
|
|
7742
7703
|
}
|
|
7743
7704
|
}
|
|
7744
7705
|
for (const w of env.layoutWarnings ?? []) {
|
|
7745
|
-
results.push(finding(
|
|
7706
|
+
results.push(finding(check26.id, {
|
|
7746
7707
|
severity: w.severity ?? "warning",
|
|
7747
7708
|
message: w.message,
|
|
7748
7709
|
file,
|
|
@@ -7751,13 +7712,13 @@ var check27 = {
|
|
|
7751
7712
|
}));
|
|
7752
7713
|
}
|
|
7753
7714
|
} catch (error2) {
|
|
7754
|
-
results.push(inspectionFailed(
|
|
7715
|
+
results.push(inspectionFailed(check26.id, `Could not check layout markers in ${file}: ${error2 instanceof Error ? error2.message : String(error2)}`, { file }));
|
|
7755
7716
|
}
|
|
7756
7717
|
}
|
|
7757
7718
|
return results;
|
|
7758
7719
|
}
|
|
7759
7720
|
};
|
|
7760
|
-
registerCheck(
|
|
7721
|
+
registerCheck(check26);
|
|
7761
7722
|
|
|
7762
7723
|
// src/checks/source/merge-markers.ts
|
|
7763
7724
|
import { readFile as readFile18 } from "node:fs/promises";
|
|
@@ -7777,6 +7738,9 @@ var ALL_IMAGE_EXTS = [
|
|
|
7777
7738
|
"svg",
|
|
7778
7739
|
"gif"
|
|
7779
7740
|
];
|
|
7741
|
+
function shippedImages(ctx, exts) {
|
|
7742
|
+
return (ctx.imageFiles ?? []).filter((file) => exts.includes(file.slice(file.lastIndexOf(".") + 1).toLowerCase()));
|
|
7743
|
+
}
|
|
7780
7744
|
var FONT_EXTS2 = ["woff", "woff2", "otf", "ttf", "eot"];
|
|
7781
7745
|
var ASSET_SCAN_IGNORE_GLOBS = [
|
|
7782
7746
|
"**/node_modules/**",
|
|
@@ -7790,7 +7754,7 @@ var CLOSE_SENTINEL = ">>>>>>> online version";
|
|
|
7790
7754
|
function withoutOnlineTag(relPath) {
|
|
7791
7755
|
return relPath.replace(/\.online(?=\.[^./]*$|$)/, "");
|
|
7792
7756
|
}
|
|
7793
|
-
var
|
|
7757
|
+
var check27 = {
|
|
7794
7758
|
id: "source.sync.merge-markers",
|
|
7795
7759
|
name: "Combined Versions",
|
|
7796
7760
|
description: "Finds passages and files still holding two versions (yours and the online copy) after a sync",
|
|
@@ -7804,7 +7768,7 @@ var check28 = {
|
|
|
7804
7768
|
try {
|
|
7805
7769
|
content = await readFile18(file, "utf8");
|
|
7806
7770
|
} catch {
|
|
7807
|
-
results.push(inspectionFailed(
|
|
7771
|
+
results.push(inspectionFailed(check27.id, `Could not read source file: ${file}`, { file }));
|
|
7808
7772
|
continue;
|
|
7809
7773
|
}
|
|
7810
7774
|
if (!content.includes(OPEN_SENTINEL) && !content.includes(CLOSE_SENTINEL))
|
|
@@ -7816,7 +7780,7 @@ var check28 = {
|
|
|
7816
7780
|
const line = lines[i].endsWith("\r") ? lines[i].slice(0, -1) : lines[i];
|
|
7817
7781
|
if (line === OPEN_SENTINEL) {
|
|
7818
7782
|
inBlock = true;
|
|
7819
|
-
results.push(finding(
|
|
7783
|
+
results.push(finding(check27.id, {
|
|
7820
7784
|
severity: "error",
|
|
7821
7785
|
code: "two-versions-passage",
|
|
7822
7786
|
message: "This passage has two versions (yours and the online copy) — keep what you want, then delete the marker lines.",
|
|
@@ -7825,7 +7789,7 @@ var check28 = {
|
|
|
7825
7789
|
}));
|
|
7826
7790
|
} else if (line === CLOSE_SENTINEL) {
|
|
7827
7791
|
if (!inBlock) {
|
|
7828
|
-
results.push(finding(
|
|
7792
|
+
results.push(finding(check27.id, {
|
|
7829
7793
|
severity: "error",
|
|
7830
7794
|
code: "leftover-version-marker",
|
|
7831
7795
|
message: "This marker line is left over from combining two versions — delete it.",
|
|
@@ -7846,7 +7810,7 @@ var check28 = {
|
|
|
7846
7810
|
});
|
|
7847
7811
|
for (const sibling of siblings.sort()) {
|
|
7848
7812
|
const rel = path9.relative(ctx.inputDir, sibling).split(path9.sep).join("/");
|
|
7849
|
-
results.push(finding(
|
|
7813
|
+
results.push(finding(check27.id, {
|
|
7850
7814
|
severity: "warning",
|
|
7851
7815
|
code: "kept-both-versions",
|
|
7852
7816
|
message: `Two versions of ${withoutOnlineTag(rel)} are in your book — keep the one you want, then delete the other.`,
|
|
@@ -7854,12 +7818,12 @@ var check28 = {
|
|
|
7854
7818
|
}));
|
|
7855
7819
|
}
|
|
7856
7820
|
} catch (error2) {
|
|
7857
|
-
results.push(inspectionFailed(
|
|
7821
|
+
results.push(inspectionFailed(check27.id, `Could not scan for kept-both files: ${error2 instanceof Error ? error2.message : String(error2)}`));
|
|
7858
7822
|
}
|
|
7859
7823
|
return results;
|
|
7860
7824
|
}
|
|
7861
7825
|
};
|
|
7862
|
-
registerCheck(
|
|
7826
|
+
registerCheck(check27);
|
|
7863
7827
|
|
|
7864
7828
|
// src/checks/source/characters.ts
|
|
7865
7829
|
import { readFile as readFile19 } from "node:fs/promises";
|
|
@@ -7974,7 +7938,7 @@ function codePointLabel(char) {
|
|
|
7974
7938
|
|
|
7975
7939
|
// src/checks/source/characters.ts
|
|
7976
7940
|
var PROBE = new RegExp(CHARACTER_PATTERN.source, "u");
|
|
7977
|
-
var
|
|
7941
|
+
var check28 = {
|
|
7978
7942
|
id: "source.characters",
|
|
7979
7943
|
name: "Unusual Characters",
|
|
7980
7944
|
description: "Finds invisible and control characters, odd spaces, and pasted typography (em dashes, curly quotes, emoji) in markdown",
|
|
@@ -7987,7 +7951,7 @@ var check29 = {
|
|
|
7987
7951
|
try {
|
|
7988
7952
|
content = await readFile19(file, "utf8");
|
|
7989
7953
|
} catch {
|
|
7990
|
-
results.push(inspectionFailed(
|
|
7954
|
+
results.push(inspectionFailed(check28.id, `Could not read source file: ${file}`, { file }));
|
|
7991
7955
|
continue;
|
|
7992
7956
|
}
|
|
7993
7957
|
if (!PROBE.test(content))
|
|
@@ -7998,7 +7962,7 @@ var check29 = {
|
|
|
7998
7962
|
for (let i = 0;i < lines.length; i++) {
|
|
7999
7963
|
const line = lines[i];
|
|
8000
7964
|
const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
|
|
8001
|
-
if (comment && parseDisableDirective(comment[1])?.includes(
|
|
7965
|
+
if (comment && parseDisableDirective(comment[1])?.includes(check28.id)) {
|
|
8002
7966
|
silencedLine = i + 1;
|
|
8003
7967
|
continue;
|
|
8004
7968
|
}
|
|
@@ -8007,7 +7971,7 @@ var check29 = {
|
|
|
8007
7971
|
for (const match of line.matchAll(CHARACTER_PATTERN)) {
|
|
8008
7972
|
const char = match[0];
|
|
8009
7973
|
const { name, code, fix } = describeCharacter(char);
|
|
8010
|
-
results.push(finding(
|
|
7974
|
+
results.push(finding(check28.id, {
|
|
8011
7975
|
severity: code === "typographer" ? "info" : "warning",
|
|
8012
7976
|
code,
|
|
8013
7977
|
message: `${name} (${codePointLabel(char)}). ${fix}`,
|
|
@@ -8022,19 +7986,55 @@ var check29 = {
|
|
|
8022
7986
|
return results;
|
|
8023
7987
|
}
|
|
8024
7988
|
};
|
|
8025
|
-
registerCheck(
|
|
7989
|
+
registerCheck(check28);
|
|
8026
7990
|
|
|
8027
7991
|
// src/checks/asset/image-file-size.ts
|
|
8028
|
-
import { stat as
|
|
7992
|
+
import { stat as stat2 } from "node:fs/promises";
|
|
7993
|
+
var check29 = {
|
|
7994
|
+
id: "asset.image.file-size",
|
|
7995
|
+
name: "Image File Size",
|
|
7996
|
+
description: "Checks that image files do not exceed the maximum size limit",
|
|
7997
|
+
category: "asset",
|
|
7998
|
+
phase: "pre-build",
|
|
7999
|
+
async run(ctx) {
|
|
8000
|
+
const maxSize = ctx.config.validate.assets.maxImageSize;
|
|
8001
|
+
if (!maxSize)
|
|
8002
|
+
return [];
|
|
8003
|
+
const files = shippedImages(ctx, ALL_IMAGE_EXTS);
|
|
8004
|
+
if (files.length === 0)
|
|
8005
|
+
return [];
|
|
8006
|
+
const results = [];
|
|
8007
|
+
for (const file of files) {
|
|
8008
|
+
try {
|
|
8009
|
+
const info3 = await stat2(file);
|
|
8010
|
+
if (info3.size > maxSize) {
|
|
8011
|
+
const sizeMb = (info3.size / 1e6).toFixed(1);
|
|
8012
|
+
const maxMb = (maxSize / 1e6).toFixed(1);
|
|
8013
|
+
results.push(finding(check29.id, {
|
|
8014
|
+
severity: "warning",
|
|
8015
|
+
message: `Image file too large: ${sizeMb}MB (max ${maxMb}MB)`,
|
|
8016
|
+
file
|
|
8017
|
+
}));
|
|
8018
|
+
}
|
|
8019
|
+
} catch {
|
|
8020
|
+
results.push(inspectionFailed(check29.id, `Could not stat image file: ${file}`, {
|
|
8021
|
+
file
|
|
8022
|
+
}));
|
|
8023
|
+
}
|
|
8024
|
+
}
|
|
8025
|
+
return results;
|
|
8026
|
+
}
|
|
8027
|
+
};
|
|
8028
|
+
registerCheck(check29);
|
|
8029
8029
|
|
|
8030
8030
|
// src/lib/image-inspect.ts
|
|
8031
|
-
import { stat as
|
|
8031
|
+
import { stat as stat3, readFile as readFile20 } from "node:fs/promises";
|
|
8032
8032
|
var DEFAULT_DPI = 72;
|
|
8033
8033
|
var cache = new Map;
|
|
8034
8034
|
async function inspectImage(path10) {
|
|
8035
8035
|
let st;
|
|
8036
8036
|
try {
|
|
8037
|
-
st = await
|
|
8037
|
+
st = await stat3(path10);
|
|
8038
8038
|
} catch {
|
|
8039
8039
|
return null;
|
|
8040
8040
|
}
|
|
@@ -8232,136 +8232,9 @@ function parseTiff(b) {
|
|
|
8232
8232
|
colorSpace
|
|
8233
8233
|
};
|
|
8234
8234
|
}
|
|
8235
|
-
async function collectImageFiles(dirs, exts, ignore2 = ASSET_SCAN_IGNORE_GLOBS) {
|
|
8236
|
-
const { glob } = await import("glob");
|
|
8237
|
-
const pattern = exts.length === 1 ? `**/*.${exts[0]}` : `**/*.{${exts.join(",")}}`;
|
|
8238
|
-
const files = [];
|
|
8239
|
-
for (const dir of dirs) {
|
|
8240
|
-
const matches = await glob(pattern, {
|
|
8241
|
-
cwd: dir,
|
|
8242
|
-
absolute: true,
|
|
8243
|
-
ignore: [...ignore2]
|
|
8244
|
-
});
|
|
8245
|
-
files.push(...matches);
|
|
8246
|
-
}
|
|
8247
|
-
return files;
|
|
8248
|
-
}
|
|
8249
|
-
|
|
8250
|
-
// src/checks/asset/image-file-size.ts
|
|
8251
|
-
var check30 = {
|
|
8252
|
-
id: "asset.image.file-size",
|
|
8253
|
-
name: "Image File Size",
|
|
8254
|
-
description: "Checks that image files do not exceed the maximum size limit",
|
|
8255
|
-
category: "asset",
|
|
8256
|
-
phase: "pre-build",
|
|
8257
|
-
async run(ctx) {
|
|
8258
|
-
const maxSize = ctx.config.validate.assets.maxImageSize;
|
|
8259
|
-
if (!maxSize)
|
|
8260
|
-
return [];
|
|
8261
|
-
const dirs = ctx.assetDirs ?? [ctx.inputDir];
|
|
8262
|
-
const files = await collectImageFiles(dirs, ALL_IMAGE_EXTS);
|
|
8263
|
-
if (files.length === 0)
|
|
8264
|
-
return [];
|
|
8265
|
-
const results = [];
|
|
8266
|
-
for (const file of files) {
|
|
8267
|
-
try {
|
|
8268
|
-
const info3 = await stat3(file);
|
|
8269
|
-
if (info3.size > maxSize) {
|
|
8270
|
-
const sizeMb = (info3.size / 1e6).toFixed(1);
|
|
8271
|
-
const maxMb = (maxSize / 1e6).toFixed(1);
|
|
8272
|
-
results.push(finding(check30.id, {
|
|
8273
|
-
severity: "warning",
|
|
8274
|
-
message: `Image file too large: ${sizeMb}MB (max ${maxMb}MB)`,
|
|
8275
|
-
file
|
|
8276
|
-
}));
|
|
8277
|
-
}
|
|
8278
|
-
} catch {
|
|
8279
|
-
results.push(inspectionFailed(check30.id, `Could not stat image file: ${file}`, {
|
|
8280
|
-
file
|
|
8281
|
-
}));
|
|
8282
|
-
}
|
|
8283
|
-
}
|
|
8284
|
-
return results;
|
|
8285
|
-
}
|
|
8286
|
-
};
|
|
8287
|
-
registerCheck(check30);
|
|
8288
|
-
|
|
8289
|
-
// src/checks/asset/image-resolution.ts
|
|
8290
|
-
var check31 = {
|
|
8291
|
-
id: "asset.image.resolution",
|
|
8292
|
-
name: "Image Resolution",
|
|
8293
|
-
description: "Checks source image DPI from embedded density metadata",
|
|
8294
|
-
category: "asset",
|
|
8295
|
-
phase: "pre-build",
|
|
8296
|
-
async run(ctx) {
|
|
8297
|
-
const minDpi = ctx.config.validate.assets.minImageDpi;
|
|
8298
|
-
if (!minDpi)
|
|
8299
|
-
return [];
|
|
8300
|
-
const dirs = ctx.assetDirs ?? [ctx.inputDir];
|
|
8301
|
-
const files = await collectImageFiles(dirs, RASTER_INSPECTABLE_EXTS);
|
|
8302
|
-
if (files.length === 0)
|
|
8303
|
-
return [];
|
|
8304
|
-
const results = [];
|
|
8305
|
-
for (const file of files) {
|
|
8306
|
-
const info3 = await inspectImage(file);
|
|
8307
|
-
if (!info3)
|
|
8308
|
-
continue;
|
|
8309
|
-
const { xDpi, yDpi } = info3;
|
|
8310
|
-
if (xDpi > 0 && yDpi > 0 && (xDpi < minDpi || yDpi < minDpi)) {
|
|
8311
|
-
results.push({
|
|
8312
|
-
checkId: check31.id,
|
|
8313
|
-
severity: "warning",
|
|
8314
|
-
message: `Image resolution too low: ${xDpi}x${yDpi} DPI (minimum ${minDpi} DPI)`,
|
|
8315
|
-
file
|
|
8316
|
-
});
|
|
8317
|
-
}
|
|
8318
|
-
}
|
|
8319
|
-
return results;
|
|
8320
|
-
}
|
|
8321
|
-
};
|
|
8322
|
-
registerCheck(check31);
|
|
8323
|
-
|
|
8324
|
-
// src/checks/asset/image-color-space.ts
|
|
8325
|
-
var LABEL = {
|
|
8326
|
-
srgb: "sRGB",
|
|
8327
|
-
gray: "Gray",
|
|
8328
|
-
cmyk: "CMYK"
|
|
8329
|
-
};
|
|
8330
|
-
var check32 = {
|
|
8331
|
-
id: "asset.image.color-space",
|
|
8332
|
-
name: "Image Color Space",
|
|
8333
|
-
description: "Validates image color spaces against allowed list",
|
|
8334
|
-
category: "asset",
|
|
8335
|
-
phase: "pre-build",
|
|
8336
|
-
async run(ctx) {
|
|
8337
|
-
const allowed = ctx.config.validate.assets.allowedColorSpaces;
|
|
8338
|
-
if (!allowed || allowed.length === 0)
|
|
8339
|
-
return [];
|
|
8340
|
-
const dirs = ctx.assetDirs ?? [ctx.inputDir];
|
|
8341
|
-
const files = await collectImageFiles(dirs, RASTER_INSPECTABLE_EXTS);
|
|
8342
|
-
if (files.length === 0)
|
|
8343
|
-
return [];
|
|
8344
|
-
const allowedLower = new Set(allowed.map((s) => s.toLowerCase()));
|
|
8345
|
-
const results = [];
|
|
8346
|
-
for (const file of files) {
|
|
8347
|
-
const info3 = await inspectImage(file);
|
|
8348
|
-
const cs = info3?.colorSpace;
|
|
8349
|
-
if (cs && !allowedLower.has(cs)) {
|
|
8350
|
-
results.push({
|
|
8351
|
-
checkId: check32.id,
|
|
8352
|
-
severity: "warning",
|
|
8353
|
-
message: `Image uses ${LABEL[cs] ?? cs} color space (allowed: ${allowed.join(", ")})`,
|
|
8354
|
-
file
|
|
8355
|
-
});
|
|
8356
|
-
}
|
|
8357
|
-
}
|
|
8358
|
-
return results;
|
|
8359
|
-
}
|
|
8360
|
-
};
|
|
8361
|
-
registerCheck(check32);
|
|
8362
8235
|
|
|
8363
8236
|
// src/checks/asset/image-alpha.ts
|
|
8364
|
-
var
|
|
8237
|
+
var check30 = {
|
|
8365
8238
|
id: "asset.image.alpha-channel",
|
|
8366
8239
|
name: "Image Alpha Channel",
|
|
8367
8240
|
description: "Checks for alpha channels in PNG/TIFF images",
|
|
@@ -8370,8 +8243,9 @@ var check33 = {
|
|
|
8370
8243
|
async run(ctx) {
|
|
8371
8244
|
if (ctx.config.validate.assets.allowAlpha)
|
|
8372
8245
|
return [];
|
|
8373
|
-
|
|
8374
|
-
|
|
8246
|
+
if (ctx.config.pdfx.transparency === "preserve-text")
|
|
8247
|
+
return [];
|
|
8248
|
+
const files = shippedImages(ctx, ["png", "tiff", "tif"]);
|
|
8375
8249
|
if (files.length === 0)
|
|
8376
8250
|
return [];
|
|
8377
8251
|
const results = [];
|
|
@@ -8379,7 +8253,7 @@ var check33 = {
|
|
|
8379
8253
|
const info3 = await inspectImage(file);
|
|
8380
8254
|
if (info3?.hasAlpha) {
|
|
8381
8255
|
results.push({
|
|
8382
|
-
checkId:
|
|
8256
|
+
checkId: check30.id,
|
|
8383
8257
|
severity: "warning",
|
|
8384
8258
|
message: "Image contains alpha channel, which may cause print issues",
|
|
8385
8259
|
file
|
|
@@ -8389,68 +8263,10 @@ var check33 = {
|
|
|
8389
8263
|
return results;
|
|
8390
8264
|
}
|
|
8391
8265
|
};
|
|
8392
|
-
registerCheck(
|
|
8393
|
-
|
|
8394
|
-
// src/checks/asset/image-tac.ts
|
|
8395
|
-
var check34 = {
|
|
8396
|
-
id: "asset.image.tac-raster",
|
|
8397
|
-
name: "Image TAC (Raster)",
|
|
8398
|
-
description: "Rasterizes and checks TAC per image using Ghostscript",
|
|
8399
|
-
category: "asset",
|
|
8400
|
-
phase: "pre-build",
|
|
8401
|
-
requiredTools: ["gs"],
|
|
8402
|
-
async run(ctx) {
|
|
8403
|
-
const maxTac = ctx.config.ink.maxTac;
|
|
8404
|
-
if (!maxTac)
|
|
8405
|
-
return [];
|
|
8406
|
-
const dirs = ctx.assetDirs ?? [ctx.inputDir];
|
|
8407
|
-
const files = await collectImageFiles(dirs, RASTER_INSPECTABLE_EXTS);
|
|
8408
|
-
if (files.length === 0)
|
|
8409
|
-
return [];
|
|
8410
|
-
const results = [];
|
|
8411
|
-
const ghostscript = await resolveGhostscript();
|
|
8412
|
-
if (!ghostscript) {
|
|
8413
|
-
return [
|
|
8414
|
-
inspectionFailed(check34.id, "Could not inspect image ink coverage: Ghostscript executable not found")
|
|
8415
|
-
];
|
|
8416
|
-
}
|
|
8417
|
-
for (const file of files) {
|
|
8418
|
-
try {
|
|
8419
|
-
const { stdout } = await execCapture(ghostscript, [
|
|
8420
|
-
"-q",
|
|
8421
|
-
"-dBATCH",
|
|
8422
|
-
"-dNOPAUSE",
|
|
8423
|
-
"-sDEVICE=inkcov",
|
|
8424
|
-
file
|
|
8425
|
-
]);
|
|
8426
|
-
const lines = stdout.split(/\r?\n/).filter((l) => l.includes("CMYK"));
|
|
8427
|
-
for (const line of lines) {
|
|
8428
|
-
const nums = line.trim().split(/\s+/).slice(0, 4).map(Number);
|
|
8429
|
-
if (nums.length === 4 && nums.every((n) => Number.isFinite(n))) {
|
|
8430
|
-
const tac = (nums[0] + nums[1] + nums[2] + nums[3]) * 100;
|
|
8431
|
-
if (tac > maxTac) {
|
|
8432
|
-
results.push(finding(check34.id, {
|
|
8433
|
-
severity: "warning",
|
|
8434
|
-
message: `Image TAC exceeds limit: ${tac.toFixed(1)}% (max ${maxTac}%)`,
|
|
8435
|
-
file,
|
|
8436
|
-
code: "image-tac-exceeded",
|
|
8437
|
-
data: { tac, limit: maxTac }
|
|
8438
|
-
}));
|
|
8439
|
-
break;
|
|
8440
|
-
}
|
|
8441
|
-
}
|
|
8442
|
-
}
|
|
8443
|
-
} catch {
|
|
8444
|
-
results.push(inspectionFailed(check34.id, `Could not inspect image ink coverage: ${file}`, { file }));
|
|
8445
|
-
}
|
|
8446
|
-
}
|
|
8447
|
-
return results;
|
|
8448
|
-
}
|
|
8449
|
-
};
|
|
8450
|
-
registerCheck(check34);
|
|
8266
|
+
registerCheck(check30);
|
|
8451
8267
|
|
|
8452
8268
|
// src/checks/asset/approved-fonts.ts
|
|
8453
|
-
var
|
|
8269
|
+
var check31 = {
|
|
8454
8270
|
id: "asset.font.approved-files",
|
|
8455
8271
|
name: "Approved Font Files",
|
|
8456
8272
|
description: "Checks font files against the approved file patterns",
|
|
@@ -8489,7 +8305,7 @@ var check35 = {
|
|
|
8489
8305
|
for (const font of allFonts) {
|
|
8490
8306
|
if (!approvedFonts.has(font)) {
|
|
8491
8307
|
results.push({
|
|
8492
|
-
checkId:
|
|
8308
|
+
checkId: check31.id,
|
|
8493
8309
|
severity: "warning",
|
|
8494
8310
|
message: "Font file not in approved list",
|
|
8495
8311
|
file: font
|
|
@@ -8499,7 +8315,7 @@ var check35 = {
|
|
|
8499
8315
|
return results;
|
|
8500
8316
|
}
|
|
8501
8317
|
};
|
|
8502
|
-
registerCheck(
|
|
8318
|
+
registerCheck(check31);
|
|
8503
8319
|
|
|
8504
8320
|
// src/checks/asset/font-license.ts
|
|
8505
8321
|
import { existsSync as existsSync10 } from "node:fs";
|
|
@@ -8514,7 +8330,7 @@ var LICENSE_NAMES = [
|
|
|
8514
8330
|
"OFL-1.1.txt",
|
|
8515
8331
|
"COPYING"
|
|
8516
8332
|
];
|
|
8517
|
-
var
|
|
8333
|
+
var check32 = {
|
|
8518
8334
|
id: "asset.font.license",
|
|
8519
8335
|
name: "Font License",
|
|
8520
8336
|
description: "Checks for license files in font directories",
|
|
@@ -8541,7 +8357,7 @@ var check36 = {
|
|
|
8541
8357
|
const hasLicense = LICENSE_NAMES.some((name) => existsSync10(resolve6(fontDir, name)));
|
|
8542
8358
|
if (!hasLicense) {
|
|
8543
8359
|
results.push({
|
|
8544
|
-
checkId:
|
|
8360
|
+
checkId: check32.id,
|
|
8545
8361
|
severity: "warning",
|
|
8546
8362
|
message: `No font license file found in directory`,
|
|
8547
8363
|
file: fontDir
|
|
@@ -8551,10 +8367,10 @@ var check36 = {
|
|
|
8551
8367
|
return results;
|
|
8552
8368
|
}
|
|
8553
8369
|
};
|
|
8554
|
-
registerCheck(
|
|
8370
|
+
registerCheck(check32);
|
|
8555
8371
|
|
|
8556
8372
|
// src/checks/heuristic/text-density.ts
|
|
8557
|
-
var
|
|
8373
|
+
var check33 = {
|
|
8558
8374
|
id: "heuristic.whitespace.text-density",
|
|
8559
8375
|
name: "Text Density",
|
|
8560
8376
|
description: "Checks characters-per-page ratio",
|
|
@@ -8583,7 +8399,7 @@ var check37 = {
|
|
|
8583
8399
|
});
|
|
8584
8400
|
if (lowPages.length > 0) {
|
|
8585
8401
|
results.push({
|
|
8586
|
-
checkId:
|
|
8402
|
+
checkId: check33.id,
|
|
8587
8403
|
severity: "info",
|
|
8588
8404
|
message: `Low text density on pages: ${lowPages.join(", ")} (below ${range.min} chars)`,
|
|
8589
8405
|
file: ctx.pdfPath
|
|
@@ -8591,7 +8407,7 @@ var check37 = {
|
|
|
8591
8407
|
}
|
|
8592
8408
|
if (highPages.length > 0) {
|
|
8593
8409
|
results.push({
|
|
8594
|
-
checkId:
|
|
8410
|
+
checkId: check33.id,
|
|
8595
8411
|
severity: "info",
|
|
8596
8412
|
message: `High text density on pages: ${highPages.join(", ")} (above ${range.max} chars)`,
|
|
8597
8413
|
file: ctx.pdfPath
|
|
@@ -8600,11 +8416,11 @@ var check37 = {
|
|
|
8600
8416
|
return results;
|
|
8601
8417
|
}
|
|
8602
8418
|
};
|
|
8603
|
-
registerCheck(
|
|
8419
|
+
registerCheck(check33);
|
|
8604
8420
|
|
|
8605
8421
|
// src/checks/heuristic/section-density.ts
|
|
8606
8422
|
import { readFile as readFile21 } from "node:fs/promises";
|
|
8607
|
-
var
|
|
8423
|
+
var check34 = {
|
|
8608
8424
|
id: "heuristic.chunking.section-density",
|
|
8609
8425
|
name: "Section Density",
|
|
8610
8426
|
description: "Checks heading/paragraph/callout density from source Markdown",
|
|
@@ -8630,7 +8446,7 @@ var check38 = {
|
|
|
8630
8446
|
const line = lines[i];
|
|
8631
8447
|
if (/^#{1,6}\s/.test(line)) {
|
|
8632
8448
|
if (paragraphCount > maxParas) {
|
|
8633
|
-
results.push(finding(
|
|
8449
|
+
results.push(finding(check34.id, {
|
|
8634
8450
|
severity: "info",
|
|
8635
8451
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
8636
8452
|
file,
|
|
@@ -8650,7 +8466,7 @@ var check38 = {
|
|
|
8650
8466
|
}
|
|
8651
8467
|
}
|
|
8652
8468
|
if (paragraphCount > maxParas) {
|
|
8653
|
-
results.push(finding(
|
|
8469
|
+
results.push(finding(check34.id, {
|
|
8654
8470
|
severity: "info",
|
|
8655
8471
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
8656
8472
|
file,
|
|
@@ -8658,7 +8474,7 @@ var check38 = {
|
|
|
8658
8474
|
}));
|
|
8659
8475
|
}
|
|
8660
8476
|
} catch {
|
|
8661
|
-
results.push(inspectionFailed(
|
|
8477
|
+
results.push(inspectionFailed(check34.id, `Could not read source file: ${file}`, {
|
|
8662
8478
|
file
|
|
8663
8479
|
}));
|
|
8664
8480
|
}
|
|
@@ -8666,10 +8482,10 @@ var check38 = {
|
|
|
8666
8482
|
return results;
|
|
8667
8483
|
}
|
|
8668
8484
|
};
|
|
8669
|
-
registerCheck(
|
|
8485
|
+
registerCheck(check34);
|
|
8670
8486
|
|
|
8671
8487
|
// src/checks/heuristic/layer-count.ts
|
|
8672
|
-
var
|
|
8488
|
+
var check35 = {
|
|
8673
8489
|
id: "heuristic.decoration.layer-count",
|
|
8674
8490
|
name: "Layer Count",
|
|
8675
8491
|
description: "Counts image objects per page",
|
|
@@ -8694,7 +8510,7 @@ var check39 = {
|
|
|
8694
8510
|
heavyPages.sort((a, b) => a - b);
|
|
8695
8511
|
return [
|
|
8696
8512
|
{
|
|
8697
|
-
checkId:
|
|
8513
|
+
checkId: check35.id,
|
|
8698
8514
|
severity: "info",
|
|
8699
8515
|
message: `Pages with many image layers (>${maxLayers}): ${heavyPages.join(", ")}`,
|
|
8700
8516
|
file: ctx.pdfPath
|
|
@@ -8704,10 +8520,10 @@ var check39 = {
|
|
|
8704
8520
|
return [];
|
|
8705
8521
|
}
|
|
8706
8522
|
};
|
|
8707
|
-
registerCheck(
|
|
8523
|
+
registerCheck(check35);
|
|
8708
8524
|
|
|
8709
8525
|
// src/checks/heuristic/placement-variance.ts
|
|
8710
|
-
var
|
|
8526
|
+
var check36 = {
|
|
8711
8527
|
id: "heuristic.layout.placement-variance",
|
|
8712
8528
|
name: "Placement Variance",
|
|
8713
8529
|
description: "Analyzes text baseline coordinates for layout consistency",
|
|
@@ -8725,7 +8541,7 @@ var check40 = {
|
|
|
8725
8541
|
const uniqueX = new Set(positions.map((p) => Math.round(p.x)));
|
|
8726
8542
|
return [
|
|
8727
8543
|
{
|
|
8728
|
-
checkId:
|
|
8544
|
+
checkId: check36.id,
|
|
8729
8545
|
severity: "info",
|
|
8730
8546
|
message: `Layout analysis: ${uniqueX.size} unique horizontal text positions across ${positions.length} text blocks.`,
|
|
8731
8547
|
file: ctx.pdfPath
|
|
@@ -8733,21 +8549,21 @@ var check40 = {
|
|
|
8733
8549
|
];
|
|
8734
8550
|
}
|
|
8735
8551
|
};
|
|
8736
|
-
registerCheck(
|
|
8552
|
+
registerCheck(check36);
|
|
8737
8553
|
|
|
8738
8554
|
// src/checks/tool-check.ts
|
|
8739
8555
|
async function checkToolAvailability(config, opts = {}) {
|
|
8740
8556
|
const { checks: checks2 } = selectChecks(opts, config);
|
|
8741
8557
|
const toolToChecks = new Map;
|
|
8742
|
-
for (const
|
|
8743
|
-
if (!
|
|
8558
|
+
for (const check37 of checks2) {
|
|
8559
|
+
if (!check37.requiredTools?.length)
|
|
8744
8560
|
continue;
|
|
8745
|
-
for (const tool of
|
|
8561
|
+
for (const tool of check37.requiredTools) {
|
|
8746
8562
|
const existing = toolToChecks.get(tool);
|
|
8747
8563
|
if (existing) {
|
|
8748
|
-
existing.push(
|
|
8564
|
+
existing.push(check37.id);
|
|
8749
8565
|
} else {
|
|
8750
|
-
toolToChecks.set(tool, [
|
|
8566
|
+
toolToChecks.set(tool, [check37.id]);
|
|
8751
8567
|
}
|
|
8752
8568
|
}
|
|
8753
8569
|
}
|
|
@@ -8763,11 +8579,11 @@ async function checkToolAvailability(config, opts = {}) {
|
|
|
8763
8579
|
const missing = results.filter((r) => !r.found).map((r) => r.tool);
|
|
8764
8580
|
const missingSet = new Set(missing);
|
|
8765
8581
|
const skippedChecks = [];
|
|
8766
|
-
for (const
|
|
8767
|
-
if (!
|
|
8582
|
+
for (const check37 of checks2) {
|
|
8583
|
+
if (!check37.requiredTools?.length)
|
|
8768
8584
|
continue;
|
|
8769
|
-
if (
|
|
8770
|
-
skippedChecks.push(
|
|
8585
|
+
if (check37.requiredTools.some((t) => missingSet.has(t))) {
|
|
8586
|
+
skippedChecks.push(check37.id);
|
|
8771
8587
|
}
|
|
8772
8588
|
}
|
|
8773
8589
|
return { available, missing, skippedChecks, toolToChecks };
|
|
@@ -8786,6 +8602,7 @@ function reportMissingTools(result) {
|
|
|
8786
8602
|
|
|
8787
8603
|
// src/lib/validation-exec.ts
|
|
8788
8604
|
import { existsSync as existsSync11 } from "node:fs";
|
|
8605
|
+
import { readFile as readFile24 } from "node:fs/promises";
|
|
8789
8606
|
import { dirname as dirname4, join as join3, resolve as resolve7 } from "node:path";
|
|
8790
8607
|
|
|
8791
8608
|
// src/lib/output-paths.ts
|
|
@@ -9265,16 +9082,16 @@ async function runChecks(ctx, opts = {}) {
|
|
|
9265
9082
|
}
|
|
9266
9083
|
const release = retainPdfCache();
|
|
9267
9084
|
try {
|
|
9268
|
-
for (const
|
|
9085
|
+
for (const check37 of checks2) {
|
|
9269
9086
|
try {
|
|
9270
|
-
let results = await
|
|
9271
|
-
const severityOverride = getCheckSeverityOverride(
|
|
9087
|
+
let results = await check37.run(ctx);
|
|
9088
|
+
const severityOverride = getCheckSeverityOverride(check37.id, ctx.config);
|
|
9272
9089
|
if (severityOverride) {
|
|
9273
9090
|
for (const r of results) {
|
|
9274
9091
|
r.severity = severityOverride;
|
|
9275
9092
|
}
|
|
9276
9093
|
}
|
|
9277
|
-
const lintLevel = ruleLevel(ctx.config.lint.rules,
|
|
9094
|
+
const lintLevel = ruleLevel(ctx.config.lint.rules, check37.id);
|
|
9278
9095
|
if (lintLevel === "off")
|
|
9279
9096
|
results = [];
|
|
9280
9097
|
else if (lintLevel) {
|
|
@@ -9282,15 +9099,15 @@ async function runChecks(ctx, opts = {}) {
|
|
|
9282
9099
|
r.severity = lintLevel === "warn" ? "warning" : "error";
|
|
9283
9100
|
}
|
|
9284
9101
|
if (results.length === 0) {
|
|
9285
|
-
passed.push(
|
|
9102
|
+
passed.push(check37.id);
|
|
9286
9103
|
} else {
|
|
9287
9104
|
allResults.push(...results);
|
|
9288
9105
|
}
|
|
9289
9106
|
} catch (err) {
|
|
9290
9107
|
allResults.push({
|
|
9291
|
-
checkId:
|
|
9108
|
+
checkId: check37.id,
|
|
9292
9109
|
severity: "error",
|
|
9293
|
-
message: `Check "${
|
|
9110
|
+
message: `Check "${check37.id}" threw: ${err instanceof Error ? err.message : String(err)}`
|
|
9294
9111
|
});
|
|
9295
9112
|
}
|
|
9296
9113
|
}
|
|
@@ -9342,6 +9159,31 @@ var SHIPPED_ASSET_EXTS = new Set([
|
|
|
9342
9159
|
".tif",
|
|
9343
9160
|
".tiff"
|
|
9344
9161
|
]);
|
|
9162
|
+
async function shippedImageFiles(manifestDir, markdownFiles, stylePaths, extensions) {
|
|
9163
|
+
const isImage = (file) => ALL_IMAGE_EXTS.includes(file.slice(file.lastIndexOf(".") + 1).toLowerCase());
|
|
9164
|
+
const files = new Set;
|
|
9165
|
+
try {
|
|
9166
|
+
for (const file of await collectStyleDependencies(manifestDir, stylePaths)) {
|
|
9167
|
+
if (isImage(file))
|
|
9168
|
+
files.add(file);
|
|
9169
|
+
}
|
|
9170
|
+
} catch {}
|
|
9171
|
+
const collect = createRenderedLocalRefCollector(await loadPlugins(extensions, manifestDir, () => {}));
|
|
9172
|
+
for (const file of markdownFiles) {
|
|
9173
|
+
let refs;
|
|
9174
|
+
try {
|
|
9175
|
+
refs = collect(await readFile24(file, "utf8"));
|
|
9176
|
+
} catch {
|
|
9177
|
+
continue;
|
|
9178
|
+
}
|
|
9179
|
+
for (const { ref, kind } of refs) {
|
|
9180
|
+
if (kind !== "image" || isNonFilesystemRef(ref) || proseImageRefError(ref, manifestDir))
|
|
9181
|
+
continue;
|
|
9182
|
+
files.add(resolve7(manifestDir, stripUrlSuffix(decodeRef(ref))));
|
|
9183
|
+
}
|
|
9184
|
+
}
|
|
9185
|
+
return [...files].filter((file) => existsSync11(file)).sort();
|
|
9186
|
+
}
|
|
9345
9187
|
async function sharedAssetDirs(manifestDir, relStyles, inputDir) {
|
|
9346
9188
|
let closure;
|
|
9347
9189
|
try {
|
|
@@ -9545,6 +9387,7 @@ async function executeValidation(args) {
|
|
|
9545
9387
|
let markdownFiles;
|
|
9546
9388
|
let cssFiles;
|
|
9547
9389
|
let assetDirs;
|
|
9390
|
+
let imageFiles;
|
|
9548
9391
|
let htmlPath;
|
|
9549
9392
|
const outDir = resolveOutputDir(manifestDir, config.title);
|
|
9550
9393
|
if (inputDir) {
|
|
@@ -9559,6 +9402,7 @@ async function executeValidation(args) {
|
|
|
9559
9402
|
}
|
|
9560
9403
|
cssFiles = [...projectCssFiles, ...pluginStylePaths].filter((f) => !f.endsWith(".min.css"));
|
|
9561
9404
|
assetDirs = [inputDir, ...await sharedAssetDirs(manifestDir, relStyles, inputDir)];
|
|
9405
|
+
imageFiles = await shippedImageFiles(manifestDir, markdownFiles, [...relStyles, ...pluginStylePaths], config.extensions);
|
|
9562
9406
|
const possibleHtml = join3(outDir, BOOK_HTML_FILENAME);
|
|
9563
9407
|
if (existsSync11(possibleHtml)) {
|
|
9564
9408
|
htmlPath = possibleHtml;
|
|
@@ -9572,7 +9416,8 @@ async function executeValidation(args) {
|
|
|
9572
9416
|
htmlPath,
|
|
9573
9417
|
markdownFiles,
|
|
9574
9418
|
cssFiles,
|
|
9575
|
-
assetDirs
|
|
9419
|
+
assetDirs,
|
|
9420
|
+
imageFiles
|
|
9576
9421
|
};
|
|
9577
9422
|
const runnerOptions = {
|
|
9578
9423
|
category: categories,
|
|
@@ -9582,7 +9427,7 @@ async function executeValidation(args) {
|
|
|
9582
9427
|
};
|
|
9583
9428
|
if (args.fix) {
|
|
9584
9429
|
const selected = selectChecks(runnerOptions, config).checks;
|
|
9585
|
-
if (selected.some((
|
|
9430
|
+
if (selected.some((check37) => check37.id === MARKDOWNLINT_CHECK_ID)) {
|
|
9586
9431
|
await applyMarkdownlintFixes(context);
|
|
9587
9432
|
} else {
|
|
9588
9433
|
log.warn(`--fix: ${MARKDOWNLINT_CHECK_ID} is not part of this run — no files were fixed.`);
|
|
@@ -9651,12 +9496,6 @@ function buildPdfSummaryLines(results) {
|
|
|
9651
9496
|
lines.push("Fonts: all embedded");
|
|
9652
9497
|
}
|
|
9653
9498
|
}
|
|
9654
|
-
const rasterResults = results.filter((r) => r.checkId === "pdf.print.rasterized-pages");
|
|
9655
|
-
if (rasterResults.length > 0) {
|
|
9656
|
-
const rasterFinding = rasterResults.find((r) => r.code === "rasterized-pages-detected");
|
|
9657
|
-
const pages = rasterFinding?.data?.pages;
|
|
9658
|
-
lines.push(`Rasterized pages: ${Array.isArray(pages) ? pages.join(", ") : "none"}`);
|
|
9659
|
-
}
|
|
9660
9499
|
return lines;
|
|
9661
9500
|
}
|
|
9662
9501
|
|
|
@@ -9669,7 +9508,7 @@ import { randomBytes } from "node:crypto";
|
|
|
9669
9508
|
|
|
9670
9509
|
// src/lib/build-fingerprint.ts
|
|
9671
9510
|
import * as fs from "node:fs";
|
|
9672
|
-
import { mkdir as mkdir5, readFile as
|
|
9511
|
+
import { mkdir as mkdir5, readFile as readFile25, writeFile as writeFile4 } from "node:fs/promises";
|
|
9673
9512
|
import { createHash as createHash4 } from "node:crypto";
|
|
9674
9513
|
import path12 from "node:path";
|
|
9675
9514
|
import git from "isomorphic-git";
|
|
@@ -9756,8 +9595,8 @@ async function getEngineBundleHash() {
|
|
|
9756
9595
|
getAssetPath("engine/gutterpress-agent.js")
|
|
9757
9596
|
]);
|
|
9758
9597
|
const [viewerBytes, agentBytes] = await Promise.all([
|
|
9759
|
-
|
|
9760
|
-
|
|
9598
|
+
readFile25(viewerPath),
|
|
9599
|
+
readFile25(agentPath)
|
|
9761
9600
|
]);
|
|
9762
9601
|
const hash = createHash4("sha256");
|
|
9763
9602
|
hash.update(viewerBytes);
|
|
@@ -10131,7 +9970,7 @@ class PdfOutput {
|
|
|
10131
9970
|
const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
|
|
10132
9971
|
await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
|
|
10133
9972
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
10134
|
-
const { buildNativePdf } = await import("./engine-
|
|
9973
|
+
const { buildNativePdf } = await import("./engine-x07q927h.js");
|
|
10135
9974
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
10136
9975
|
title: config.title,
|
|
10137
9976
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
@@ -10331,7 +10170,7 @@ function openPath(filePath) {
|
|
|
10331
10170
|
import path16 from "path";
|
|
10332
10171
|
import { tmpdir } from "os";
|
|
10333
10172
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
10334
|
-
import { readdir as
|
|
10173
|
+
import { readdir as readdir5, readFile as readFile26, writeFile as writeFile5 } from "node:fs/promises";
|
|
10335
10174
|
|
|
10336
10175
|
// src/utils/file-utils.ts
|
|
10337
10176
|
import { promises as fs3 } from "fs";
|
|
@@ -10877,7 +10716,7 @@ function isProcessAlive(pid) {
|
|
|
10877
10716
|
async function cleanupOrphanTempDirs() {
|
|
10878
10717
|
let entries;
|
|
10879
10718
|
try {
|
|
10880
|
-
entries = await
|
|
10719
|
+
entries = await readdir5(TEMP_DIR_BASE);
|
|
10881
10720
|
} catch {
|
|
10882
10721
|
return;
|
|
10883
10722
|
}
|
|
@@ -10885,7 +10724,7 @@ async function cleanupOrphanTempDirs() {
|
|
|
10885
10724
|
const dir = path16.join(TEMP_DIR_BASE, entry);
|
|
10886
10725
|
const pidFile = path16.join(dir, PID_FILE_NAME);
|
|
10887
10726
|
try {
|
|
10888
|
-
const raw = await
|
|
10727
|
+
const raw = await readFile26(pidFile, "utf8");
|
|
10889
10728
|
const pid = parseInt(raw.trim(), 10);
|
|
10890
10729
|
if (!isProcessAlive(pid)) {
|
|
10891
10730
|
debug(`Removing orphan temp dir ${dir} (pid ${pid} not alive)`);
|
|
@@ -10992,7 +10831,7 @@ function createServerState(inputPath, tempDir, config, options) {
|
|
|
10992
10831
|
import http from "node:http";
|
|
10993
10832
|
import net from "node:net";
|
|
10994
10833
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
|
|
10995
|
-
import { readFile as
|
|
10834
|
+
import { readFile as readFile27, stat as stat4 } from "node:fs/promises";
|
|
10996
10835
|
import path17 from "path";
|
|
10997
10836
|
import { WebSocket, WebSocketServer } from "ws";
|
|
10998
10837
|
var HMR_PATH = "/__gutterpress-hmr";
|
|
@@ -11061,7 +10900,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
11061
10900
|
}
|
|
11062
10901
|
let data;
|
|
11063
10902
|
try {
|
|
11064
|
-
data = await
|
|
10903
|
+
data = await readFile27(filePath);
|
|
11065
10904
|
} catch {
|
|
11066
10905
|
res.writeHead(404);
|
|
11067
10906
|
res.end("Not Found");
|
|
@@ -11110,7 +10949,7 @@ async function serveRevalidatedStatic(req, absPath, res, validators) {
|
|
|
11110
10949
|
let data;
|
|
11111
10950
|
if (!validator || validator.stamp !== stamp) {
|
|
11112
10951
|
try {
|
|
11113
|
-
data = await
|
|
10952
|
+
data = await readFile27(absPath);
|
|
11114
10953
|
} catch {
|
|
11115
10954
|
await serveStatic(absPath, res);
|
|
11116
10955
|
return;
|
|
@@ -11420,7 +11259,7 @@ async function startPreviewServer(options) {
|
|
|
11420
11259
|
}
|
|
11421
11260
|
|
|
11422
11261
|
// src/lib/project-scaffold.ts
|
|
11423
|
-
import { access, copyFile, cp as cp2, mkdir as mkdir7, readdir as
|
|
11262
|
+
import { access, copyFile, cp as cp2, mkdir as mkdir7, readdir as readdir6, readFile as readFile28, stat as stat5, writeFile as writeFile6 } from "node:fs/promises";
|
|
11424
11263
|
import { constants as FS, existsSync as existsSync12 } from "node:fs";
|
|
11425
11264
|
import path18 from "node:path";
|
|
11426
11265
|
|
|
@@ -11539,7 +11378,7 @@ async function scaffoldProject(options) {
|
|
|
11539
11378
|
try {
|
|
11540
11379
|
await mkdir7(projectDir, { recursive: true });
|
|
11541
11380
|
if (customTemplateDir) {
|
|
11542
|
-
const entries = await
|
|
11381
|
+
const entries = await readdir6(customTemplateDir, { withFileTypes: true });
|
|
11543
11382
|
for (const entry of entries) {
|
|
11544
11383
|
if (entry.name === ".gutterpress-template.json")
|
|
11545
11384
|
continue;
|
|
@@ -11621,7 +11460,7 @@ async function scaffoldProject(options) {
|
|
|
11621
11460
|
async function firstSourceFile(manifestPath) {
|
|
11622
11461
|
let text;
|
|
11623
11462
|
try {
|
|
11624
|
-
text = await
|
|
11463
|
+
text = await readFile28(manifestPath, "utf8");
|
|
11625
11464
|
} catch {
|
|
11626
11465
|
return;
|
|
11627
11466
|
}
|
|
@@ -11633,7 +11472,7 @@ async function firstSourceFile(manifestPath) {
|
|
|
11633
11472
|
return m ? m[1].trim() : undefined;
|
|
11634
11473
|
}
|
|
11635
11474
|
async function fillTemplateFile(filePath, substitutions) {
|
|
11636
|
-
let text = await
|
|
11475
|
+
let text = await readFile28(filePath, "utf8");
|
|
11637
11476
|
for (const [token, value] of Object.entries(substitutions)) {
|
|
11638
11477
|
text = text.split(token).join(value);
|
|
11639
11478
|
}
|
|
@@ -11641,7 +11480,7 @@ async function fillTemplateFile(filePath, substitutions) {
|
|
|
11641
11480
|
}
|
|
11642
11481
|
|
|
11643
11482
|
// src/lib/extension-scaffold.ts
|
|
11644
|
-
import { access as access2, mkdir as mkdir8, readFile as
|
|
11483
|
+
import { access as access2, mkdir as mkdir8, readFile as readFile29, writeFile as writeFile7 } from "node:fs/promises";
|
|
11645
11484
|
import { constants as FS2 } from "node:fs";
|
|
11646
11485
|
import path19 from "node:path";
|
|
11647
11486
|
var EXTENSION_KINDS = ["plugin", "theme"];
|
|
@@ -11758,7 +11597,7 @@ async function scaffoldExtension(options) {
|
|
|
11758
11597
|
const src = await getAssetPath(`extension-templates/${kind}/${assetName}`);
|
|
11759
11598
|
const dest = path19.join(extensionDir, relDest);
|
|
11760
11599
|
await mkdir8(path19.dirname(dest), { recursive: true });
|
|
11761
|
-
const text = await
|
|
11600
|
+
const text = await readFile29(src, "utf8");
|
|
11762
11601
|
const table = relDest.endsWith(".json") ? jsonSafe : plain;
|
|
11763
11602
|
await writeFile7(dest, substitute(text, table), "utf8");
|
|
11764
11603
|
written.push(relDest);
|
|
@@ -11803,7 +11642,7 @@ var SKIP_ENTRIES = new Set([
|
|
|
11803
11642
|
|
|
11804
11643
|
// src/lib/extension-import.ts
|
|
11805
11644
|
import { existsSync as existsSync13 } from "node:fs";
|
|
11806
|
-
import { cp as cp3, mkdir as mkdir9, mkdtemp as mkdtemp2, readFile as
|
|
11645
|
+
import { cp as cp3, mkdir as mkdir9, mkdtemp as mkdtemp2, readFile as readFile30, rm as rm4, writeFile as writeFile8 } from "node:fs/promises";
|
|
11807
11646
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
11808
11647
|
import path20 from "node:path";
|
|
11809
11648
|
import { unzipSync } from "fflate";
|
|
@@ -11876,7 +11715,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11876
11715
|
if (!existsSync13(cssPath)) {
|
|
11877
11716
|
throw new Error("A theme must contain a theme.css file.");
|
|
11878
11717
|
}
|
|
11879
|
-
const css = await
|
|
11718
|
+
const css = await readFile30(cssPath, "utf8");
|
|
11880
11719
|
const { reject, warnings: printFindings } = classifyThemeCssFindings(checkCss(css, "theme.css"));
|
|
11881
11720
|
if (reject) {
|
|
11882
11721
|
throw new Error(`theme.css could not be parsed — ${reject.message}`);
|
|
@@ -11904,7 +11743,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11904
11743
|
for (let i = 0;i < extraSheets.length; i++) {
|
|
11905
11744
|
const rel = extraSheets[i];
|
|
11906
11745
|
const sheetPath = extraSheetPaths[i];
|
|
11907
|
-
const sheetCss = await
|
|
11746
|
+
const sheetCss = await readFile30(sheetPath, "utf8");
|
|
11908
11747
|
const { reject: sheetReject, warnings: sheetFindings } = classifyThemeCssFindings(checkCss(sheetCss, rel));
|
|
11909
11748
|
if (sheetReject) {
|
|
11910
11749
|
throw new Error(`${rel} could not be parsed — ${sheetReject.message}`);
|
|
@@ -11937,7 +11776,7 @@ async function completeLandedManifest(dest, id, meta, metaExists) {
|
|
|
11937
11776
|
let pkg = {};
|
|
11938
11777
|
if (metaExists) {
|
|
11939
11778
|
try {
|
|
11940
|
-
const parsed = JSON.parse(await
|
|
11779
|
+
const parsed = JSON.parse(await readFile30(file, "utf8"));
|
|
11941
11780
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
11942
11781
|
pkg = parsed;
|
|
11943
11782
|
}
|
|
@@ -12039,11 +11878,11 @@ async function importExtensionFromCssText(projectDir, css, name) {
|
|
|
12039
11878
|
async function importExtensionFromFile(projectDir, filePath) {
|
|
12040
11879
|
const ext = path20.extname(filePath).toLowerCase();
|
|
12041
11880
|
if (ext === ".zip") {
|
|
12042
|
-
const buf = await
|
|
11881
|
+
const buf = await readFile30(filePath);
|
|
12043
11882
|
return importExtensionFromZip(projectDir, new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
|
|
12044
11883
|
}
|
|
12045
11884
|
if (ext === ".css") {
|
|
12046
|
-
const css = await
|
|
11885
|
+
const css = await readFile30(filePath, "utf8");
|
|
12047
11886
|
return importExtensionFromCssText(projectDir, css, prettify(path20.basename(filePath, ".css")));
|
|
12048
11887
|
}
|
|
12049
11888
|
throw new Error("Choose a .zip extension package or a .css stylesheet.");
|
|
@@ -12827,7 +12666,7 @@ var drivethrurpgProvider = {
|
|
|
12827
12666
|
};
|
|
12828
12667
|
|
|
12829
12668
|
// src/lib/publish/providers/gdrive.ts
|
|
12830
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
12669
|
+
import { mkdtemp as mkdtemp3, readFile as readFile31, readdir as readdir7, rm as rm5, stat as stat6, writeFile as writeFile10 } from "node:fs/promises";
|
|
12831
12670
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
12832
12671
|
import path22 from "node:path";
|
|
12833
12672
|
import { Zip, ZipDeflate } from "fflate";
|
|
@@ -13212,19 +13051,19 @@ function zipEntriesNonBlocking(entries, opts) {
|
|
|
13212
13051
|
});
|
|
13213
13052
|
}
|
|
13214
13053
|
async function collectZipEntries(root, dir, out, onWarn) {
|
|
13215
|
-
const entries = await
|
|
13054
|
+
const entries = await readdir7(dir, { withFileTypes: true });
|
|
13216
13055
|
for (const entry of entries) {
|
|
13217
13056
|
const full = path22.join(dir, entry.name);
|
|
13218
13057
|
if (entry.isDirectory()) {
|
|
13219
13058
|
await collectZipEntries(root, full, out, onWarn);
|
|
13220
13059
|
} else if (entry.isFile()) {
|
|
13221
13060
|
const rel = path22.relative(root, full).split(path22.sep).join("/");
|
|
13222
|
-
out[rel] = await
|
|
13061
|
+
out[rel] = await readFile31(full);
|
|
13223
13062
|
} else if (entry.isSymbolicLink()) {
|
|
13224
13063
|
const resolved = await stat6(full).catch(() => null);
|
|
13225
13064
|
if (resolved?.isFile()) {
|
|
13226
13065
|
const rel = path22.relative(root, full).split(path22.sep).join("/");
|
|
13227
|
-
out[rel] = await
|
|
13066
|
+
out[rel] = await readFile31(full);
|
|
13228
13067
|
} else if (resolved?.isDirectory()) {
|
|
13229
13068
|
await collectZipEntries(root, full, out, onWarn);
|
|
13230
13069
|
} else {
|
|
@@ -13885,7 +13724,7 @@ function publishProviderFor(id) {
|
|
|
13885
13724
|
}
|
|
13886
13725
|
|
|
13887
13726
|
// src/lib/publish/run-publish.ts
|
|
13888
|
-
import { readdir as
|
|
13727
|
+
import { readdir as readdir8, stat as stat9 } from "node:fs/promises";
|
|
13889
13728
|
import path25 from "node:path";
|
|
13890
13729
|
function resolvePublishFormat(info10, providerConfig) {
|
|
13891
13730
|
if (info10.formats && info10.formats.length > 0) {
|
|
@@ -13981,7 +13820,7 @@ async function htmlDirIssues(dir) {
|
|
|
13981
13820
|
message: `${dir} has no ${BOOK_HTML} — it isn't an HTML export. ${HTML_HINT}`
|
|
13982
13821
|
});
|
|
13983
13822
|
}
|
|
13984
|
-
const entries = await
|
|
13823
|
+
const entries = await readdir8(dir).catch(() => []);
|
|
13985
13824
|
const extras = entries.filter((name) => name.toLowerCase().endsWith(".pdf") || name === BUILD_FINGERPRINT_FILENAME);
|
|
13986
13825
|
if (extras.length > 0) {
|
|
13987
13826
|
issues.push({
|
|
@@ -14211,7 +14050,7 @@ import git8 from "isomorphic-git";
|
|
|
14211
14050
|
// src/lib/remote-auth/scorched-earth.ts
|
|
14212
14051
|
import git9 from "isomorphic-git";
|
|
14213
14052
|
// src/lib/publish/selections.ts
|
|
14214
|
-
import { chmod as chmod2, mkdir as mkdir13, readFile as
|
|
14053
|
+
import { chmod as chmod2, mkdir as mkdir13, readFile as readFile32, writeFile as writeFile12 } from "node:fs/promises";
|
|
14215
14054
|
import path26 from "node:path";
|
|
14216
14055
|
class PublishSelectionsStore {
|
|
14217
14056
|
filePath;
|
|
@@ -14221,7 +14060,7 @@ class PublishSelectionsStore {
|
|
|
14221
14060
|
}
|
|
14222
14061
|
async read() {
|
|
14223
14062
|
try {
|
|
14224
|
-
const raw = await
|
|
14063
|
+
const raw = await readFile32(this.filePath, "utf8");
|
|
14225
14064
|
const parsed = JSON.parse(raw);
|
|
14226
14065
|
if (parsed && typeof parsed === "object") {
|
|
14227
14066
|
return {
|