gutterpress 0.11.17-alpha.3 → 0.11.17-alpha.5
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/dist/api/index.js +3 -2
- package/dist/black-text-gray-7dz61456.js +15 -0
- package/dist/{build-394c1ee1.js → build-jr9mqwgr.js} +5 -4
- package/dist/checks/pdf/black-text.d.ts +3 -0
- package/dist/checks/pdf/index.d.ts +1 -0
- package/dist/{cli-d57bsfpb.js → cli-2xpwjanr.js} +1 -1
- package/dist/{cli-3p1e95zc.js → cli-5sg6hcqa.js} +1 -1
- package/dist/{cli-9nba5ve6.js → cli-82tvm0sg.js} +237 -607
- package/dist/{cli-jyy9pqf3.js → cli-c3enmb58.js} +1 -1
- package/dist/cli-ht1bcqdd.js +493 -0
- package/dist/cli.js +12 -12
- package/dist/{doctor-c459qv5j.js → doctor-zxk3pamr.js} +2 -2
- package/dist/{engine-zdb41ay1.js → engine-63r7pxsy.js} +1 -1
- package/dist/{engine-3xqgm82d.js → engine-eactk9nn.js} +2 -2
- package/dist/{ext-ha7gg8h9.js → ext-4vapkh0p.js} +5 -4
- package/dist/favicon-272ga6y0.ico +0 -0
- package/dist/{index-bscfmg5r.js → index-5ez30g3a.js} +232 -602
- package/dist/{index-q1xqxrh9.js → index-5gp6mkmf.js} +1 -1
- package/dist/index-ht1bcqdd.js +493 -0
- package/dist/{index-hrx0gfkj.js → index-mb8mzwye.js} +1 -1
- package/dist/index.js +4 -3
- package/dist/lib/black-text-gray.d.ts +17 -0
- package/dist/{new-kky3z7kv.js → new-hzs75cmd.js} +5 -4
- package/dist/{preflight-t8v6szq6.js → preflight-4k8dtsfs.js} +5 -4
- package/dist/{preview-cxab4y5x.js → preview-whg4rwg6.js} +5 -4
- package/dist/{publish-s2mtenb5.js → publish-4vtd7qve.js} +5 -4
- package/dist/{validate-xaajdw4x.js → validate-c32mm2bh.js} +5 -4
- package/package.json +1 -1
- package/dist/favicon-wkbm9cjn.ico +0 -0
|
@@ -13,18 +13,21 @@ import {
|
|
|
13
13
|
stampCreator,
|
|
14
14
|
stripAnnotations,
|
|
15
15
|
warn
|
|
16
|
-
} from "./cli-
|
|
16
|
+
} from "./cli-5sg6hcqa.js";
|
|
17
17
|
import {
|
|
18
18
|
DEBOUNCE,
|
|
19
19
|
UsageError,
|
|
20
20
|
resolvePort
|
|
21
|
-
} from "./cli-
|
|
21
|
+
} from "./cli-2xpwjanr.js";
|
|
22
|
+
import {
|
|
23
|
+
blackTextToGray
|
|
24
|
+
} from "./cli-ht1bcqdd.js";
|
|
22
25
|
import {
|
|
23
26
|
MARGIN_BOX_IGNORED_PROPERTIES,
|
|
24
27
|
getAssetPath,
|
|
25
28
|
getAssetsDir,
|
|
26
29
|
lastPageMode
|
|
27
|
-
} from "./cli-
|
|
30
|
+
} from "./cli-c3enmb58.js";
|
|
28
31
|
import {
|
|
29
32
|
BuildError,
|
|
30
33
|
EXIT_CODES
|
|
@@ -5544,8 +5547,46 @@ var check7 = {
|
|
|
5544
5547
|
};
|
|
5545
5548
|
registerCheck(check7);
|
|
5546
5549
|
|
|
5547
|
-
// src/checks/pdf/
|
|
5550
|
+
// src/checks/pdf/black-text.ts
|
|
5551
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
5548
5552
|
var check8 = {
|
|
5553
|
+
id: "pdf.print.black-text",
|
|
5554
|
+
name: "Black Text",
|
|
5555
|
+
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)",
|
|
5556
|
+
category: "pdf",
|
|
5557
|
+
phase: "post-build",
|
|
5558
|
+
enabledWhen: (config) => config.pdfx.blackText === "k-only",
|
|
5559
|
+
async run(ctx) {
|
|
5560
|
+
if (!ctx.pdfPath)
|
|
5561
|
+
return [];
|
|
5562
|
+
try {
|
|
5563
|
+
const { findRichBlackText, LARGE_TEXT_PT } = await import("./black-text-gray-7dz61456.js");
|
|
5564
|
+
const found = await findRichBlackText(await readFile9(ctx.pdfPath));
|
|
5565
|
+
if (found.runs === 0)
|
|
5566
|
+
return [];
|
|
5567
|
+
return [
|
|
5568
|
+
finding(check8.id, {
|
|
5569
|
+
severity: "warning",
|
|
5570
|
+
message: `Dark four-colour text at ${LARGE_TEXT_PT}pt or smaller on page(s) ${found.pages.join(", ")}.`,
|
|
5571
|
+
file: ctx.pdfPath,
|
|
5572
|
+
detail: "Small text printed with all four inks can look fuzzy if the plates are slightly out of register, which is why print vendors such as DriveThruRPG want small black text on the black plate alone. `pdfx.blackText: k-only` already does that for near-black text; this text is either a dark colour (for example a dark brown, which keeps its colour) or painted in a way the build cannot rewrite (an image or a pattern). To print it on the black plate, make the text black (#000). If the colour is intended, set `lint.rules: { pdf.print.black-text: off }` in manifest.yaml.",
|
|
5573
|
+
code: "rich-black-text",
|
|
5574
|
+
data: { runs: found.runs, pages: found.pages }
|
|
5575
|
+
})
|
|
5576
|
+
];
|
|
5577
|
+
} catch {
|
|
5578
|
+
return [
|
|
5579
|
+
inspectionFailed(check8.id, "Could not inspect the PDF's text colours.", {
|
|
5580
|
+
file: ctx.pdfPath
|
|
5581
|
+
})
|
|
5582
|
+
];
|
|
5583
|
+
}
|
|
5584
|
+
}
|
|
5585
|
+
};
|
|
5586
|
+
registerCheck(check8);
|
|
5587
|
+
|
|
5588
|
+
// src/checks/pdf/signature.ts
|
|
5589
|
+
var check9 = {
|
|
5549
5590
|
id: "pdf.print.signature",
|
|
5550
5591
|
name: "Signature",
|
|
5551
5592
|
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)",
|
|
@@ -5557,7 +5598,7 @@ var check8 = {
|
|
|
5557
5598
|
return [];
|
|
5558
5599
|
const doc = await loadPdf(ctx.pdfPath);
|
|
5559
5600
|
if (!doc) {
|
|
5560
|
-
return [inspectionFailed(
|
|
5601
|
+
return [inspectionFailed(check9.id, "Could not parse the PDF's page count.", { file: ctx.pdfPath })];
|
|
5561
5602
|
}
|
|
5562
5603
|
const size = ctx.config.print.signature;
|
|
5563
5604
|
const pages = doc.numPages;
|
|
@@ -5566,7 +5607,7 @@ var check8 = {
|
|
|
5566
5607
|
const data = { pages, signature: size };
|
|
5567
5608
|
if (pages % size !== 0) {
|
|
5568
5609
|
return [
|
|
5569
|
-
finding(
|
|
5610
|
+
finding(check9.id, {
|
|
5570
5611
|
severity: "error",
|
|
5571
5612
|
message: `Page count ${pages} does not fit a signature of ${size}: it must be a multiple of ${size}, or one short of one.`,
|
|
5572
5613
|
file: ctx.pdfPath,
|
|
@@ -5582,10 +5623,10 @@ var check8 = {
|
|
|
5582
5623
|
if (!await pageHasMarks(doc, pages))
|
|
5583
5624
|
return [];
|
|
5584
5625
|
} catch {
|
|
5585
|
-
return [inspectionFailed(
|
|
5626
|
+
return [inspectionFailed(check9.id, "Could not read the last page.", { file: ctx.pdfPath })];
|
|
5586
5627
|
}
|
|
5587
5628
|
return [
|
|
5588
|
-
finding(
|
|
5629
|
+
finding(check9.id, {
|
|
5589
5630
|
severity: "error",
|
|
5590
5631
|
message: `The last page (${pages}) has content, but the printer needs it blank for its own information.`,
|
|
5591
5632
|
file: ctx.pdfPath,
|
|
@@ -5596,10 +5637,10 @@ var check8 = {
|
|
|
5596
5637
|
];
|
|
5597
5638
|
}
|
|
5598
5639
|
};
|
|
5599
|
-
registerCheck(
|
|
5640
|
+
registerCheck(check9);
|
|
5600
5641
|
|
|
5601
5642
|
// src/checks/pdf/bookmarks.ts
|
|
5602
|
-
var
|
|
5643
|
+
var check10 = {
|
|
5603
5644
|
id: "pdf.nav.bookmarks",
|
|
5604
5645
|
name: "Bookmarks",
|
|
5605
5646
|
description: "Checks for PDF outline (bookmarks) tree",
|
|
@@ -5613,14 +5654,14 @@ var check9 = {
|
|
|
5613
5654
|
const doc = await loadPdf(ctx.pdfPath);
|
|
5614
5655
|
if (!doc) {
|
|
5615
5656
|
return [
|
|
5616
|
-
inspectionFailed(
|
|
5657
|
+
inspectionFailed(check10.id, "Could not inspect PDF for bookmarks.", {
|
|
5617
5658
|
file: ctx.pdfPath
|
|
5618
5659
|
})
|
|
5619
5660
|
];
|
|
5620
5661
|
}
|
|
5621
5662
|
if (await getOutlineCount(doc) === 0) {
|
|
5622
5663
|
return [
|
|
5623
|
-
finding(
|
|
5664
|
+
finding(check10.id, {
|
|
5624
5665
|
severity: "warning",
|
|
5625
5666
|
message: "PDF does not contain bookmarks (outline tree).",
|
|
5626
5667
|
file: ctx.pdfPath
|
|
@@ -5630,10 +5671,10 @@ var check9 = {
|
|
|
5630
5671
|
return [];
|
|
5631
5672
|
}
|
|
5632
5673
|
};
|
|
5633
|
-
registerCheck(
|
|
5674
|
+
registerCheck(check10);
|
|
5634
5675
|
|
|
5635
5676
|
// src/checks/pdf/toc-links.ts
|
|
5636
|
-
var
|
|
5677
|
+
var check11 = {
|
|
5637
5678
|
id: "pdf.nav.toc-links",
|
|
5638
5679
|
name: "TOC Links",
|
|
5639
5680
|
description: "Detects link annotations on TOC pages",
|
|
@@ -5648,7 +5689,7 @@ var check10 = {
|
|
|
5648
5689
|
if (!doc) {
|
|
5649
5690
|
return [
|
|
5650
5691
|
{
|
|
5651
|
-
checkId:
|
|
5692
|
+
checkId: check11.id,
|
|
5652
5693
|
severity: "warning",
|
|
5653
5694
|
message: "Could not inspect PDF for TOC links.",
|
|
5654
5695
|
file: ctx.pdfPath
|
|
@@ -5659,7 +5700,7 @@ var check10 = {
|
|
|
5659
5700
|
if (!hasLinks) {
|
|
5660
5701
|
return [
|
|
5661
5702
|
{
|
|
5662
|
-
checkId:
|
|
5703
|
+
checkId: check11.id,
|
|
5663
5704
|
severity: "warning",
|
|
5664
5705
|
message: "No link annotations found in PDF (expected for TOC).",
|
|
5665
5706
|
file: ctx.pdfPath
|
|
@@ -5669,10 +5710,10 @@ var check10 = {
|
|
|
5669
5710
|
return [];
|
|
5670
5711
|
}
|
|
5671
5712
|
};
|
|
5672
|
-
registerCheck(
|
|
5713
|
+
registerCheck(check11);
|
|
5673
5714
|
|
|
5674
5715
|
// src/checks/pdf/cross-refs.ts
|
|
5675
|
-
var
|
|
5716
|
+
var check12 = {
|
|
5676
5717
|
id: "pdf.nav.cross-refs",
|
|
5677
5718
|
name: "Cross References",
|
|
5678
5719
|
description: "Counts and verifies internal link annotations",
|
|
@@ -5685,7 +5726,7 @@ var check11 = {
|
|
|
5685
5726
|
if (!doc) {
|
|
5686
5727
|
return [
|
|
5687
5728
|
{
|
|
5688
|
-
checkId:
|
|
5729
|
+
checkId: check12.id,
|
|
5689
5730
|
severity: "info",
|
|
5690
5731
|
message: "Could not inspect PDF for cross-references.",
|
|
5691
5732
|
file: ctx.pdfPath
|
|
@@ -5695,7 +5736,7 @@ var check11 = {
|
|
|
5695
5736
|
const linkCount = await countLinkAnnotations(doc);
|
|
5696
5737
|
return [
|
|
5697
5738
|
{
|
|
5698
|
-
checkId:
|
|
5739
|
+
checkId: check12.id,
|
|
5699
5740
|
severity: "info",
|
|
5700
5741
|
message: `PDF contains ${linkCount} internal link annotation${linkCount !== 1 ? "s" : ""}.`,
|
|
5701
5742
|
file: ctx.pdfPath
|
|
@@ -5703,10 +5744,10 @@ var check11 = {
|
|
|
5703
5744
|
];
|
|
5704
5745
|
}
|
|
5705
5746
|
};
|
|
5706
|
-
registerCheck(
|
|
5747
|
+
registerCheck(check12);
|
|
5707
5748
|
|
|
5708
5749
|
// src/checks/pdf/page-labels.ts
|
|
5709
|
-
var
|
|
5750
|
+
var check13 = {
|
|
5710
5751
|
id: "pdf.nav.page-labels",
|
|
5711
5752
|
name: "Page Labels",
|
|
5712
5753
|
description: "Verifies PDF page labels / folio numbering",
|
|
@@ -5721,7 +5762,7 @@ var check12 = {
|
|
|
5721
5762
|
const labels = await getPageLabels(doc);
|
|
5722
5763
|
return [
|
|
5723
5764
|
{
|
|
5724
|
-
checkId:
|
|
5765
|
+
checkId: check13.id,
|
|
5725
5766
|
severity: "info",
|
|
5726
5767
|
message: labels && labels.length > 0 ? "PDF contains page labels." : "PDF does not contain explicit page labels.",
|
|
5727
5768
|
file: ctx.pdfPath
|
|
@@ -5729,10 +5770,10 @@ var check12 = {
|
|
|
5729
5770
|
];
|
|
5730
5771
|
}
|
|
5731
5772
|
};
|
|
5732
|
-
registerCheck(
|
|
5773
|
+
registerCheck(check13);
|
|
5733
5774
|
|
|
5734
5775
|
// src/checks/pdf/image-resolution.ts
|
|
5735
|
-
var
|
|
5776
|
+
var check14 = {
|
|
5736
5777
|
id: "pdf.print.image-resolution",
|
|
5737
5778
|
name: "Image Resolution",
|
|
5738
5779
|
description: "Checks DPI of images embedded in the PDF",
|
|
@@ -5759,7 +5800,7 @@ var check13 = {
|
|
|
5759
5800
|
lowResPages.sort((a, b) => a - b);
|
|
5760
5801
|
return [
|
|
5761
5802
|
{
|
|
5762
|
-
checkId:
|
|
5803
|
+
checkId: check14.id,
|
|
5763
5804
|
severity: "warning",
|
|
5764
5805
|
message: `Low-resolution images (below ${minDpi} DPI) found on pages: ${lowResPages.join(", ")}`,
|
|
5765
5806
|
file: ctx.pdfPath
|
|
@@ -5769,10 +5810,10 @@ var check13 = {
|
|
|
5769
5810
|
return [];
|
|
5770
5811
|
}
|
|
5771
5812
|
};
|
|
5772
|
-
registerCheck(
|
|
5813
|
+
registerCheck(check14);
|
|
5773
5814
|
|
|
5774
5815
|
// src/checks/pdf/transparency.ts
|
|
5775
|
-
var
|
|
5816
|
+
var check15 = {
|
|
5776
5817
|
id: "pdf.print.transparency",
|
|
5777
5818
|
name: "Transparency",
|
|
5778
5819
|
description: "Checks for transparency markers (/Transparency, /SMask, /BM) in the PDF",
|
|
@@ -5794,7 +5835,7 @@ var check14 = {
|
|
|
5794
5835
|
if (found.length > 0) {
|
|
5795
5836
|
return [
|
|
5796
5837
|
{
|
|
5797
|
-
checkId:
|
|
5838
|
+
checkId: check15.id,
|
|
5798
5839
|
severity: "warning",
|
|
5799
5840
|
message: `Transparency detected in PDF: ${found.join(", ")}. This may cause issues with some printers.`,
|
|
5800
5841
|
file: ctx.pdfPath
|
|
@@ -5804,7 +5845,7 @@ var check14 = {
|
|
|
5804
5845
|
return [];
|
|
5805
5846
|
}
|
|
5806
5847
|
};
|
|
5807
|
-
registerCheck(
|
|
5848
|
+
registerCheck(check15);
|
|
5808
5849
|
|
|
5809
5850
|
// src/checks/pdf/bleed.ts
|
|
5810
5851
|
function findBox(bytes, box) {
|
|
@@ -5814,7 +5855,7 @@ function findBox(bytes, box) {
|
|
|
5814
5855
|
return null;
|
|
5815
5856
|
return [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])];
|
|
5816
5857
|
}
|
|
5817
|
-
var
|
|
5858
|
+
var check16 = {
|
|
5818
5859
|
id: "pdf.print.bleed",
|
|
5819
5860
|
name: "Bleed Box",
|
|
5820
5861
|
description: "Compares MediaBox vs TrimBox/BleedBox to verify bleed area",
|
|
@@ -5828,7 +5869,7 @@ var check15 = {
|
|
|
5828
5869
|
const bytes = await readPdfBytes(ctx.pdfPath).catch(() => "");
|
|
5829
5870
|
if (!bytes) {
|
|
5830
5871
|
return [
|
|
5831
|
-
inspectionFailed(
|
|
5872
|
+
inspectionFailed(check16.id, "Could not inspect PDF for bleed boxes.", {
|
|
5832
5873
|
file: ctx.pdfPath
|
|
5833
5874
|
})
|
|
5834
5875
|
];
|
|
@@ -5838,14 +5879,14 @@ var check15 = {
|
|
|
5838
5879
|
const bleed = findBox(bytes, "BleedBox");
|
|
5839
5880
|
if (!media) {
|
|
5840
5881
|
return [
|
|
5841
|
-
inspectionFailed(
|
|
5882
|
+
inspectionFailed(check16.id, "Could not parse MediaBox from PDF.", {
|
|
5842
5883
|
file: ctx.pdfPath
|
|
5843
5884
|
})
|
|
5844
5885
|
];
|
|
5845
5886
|
}
|
|
5846
5887
|
if (!trim && !bleed) {
|
|
5847
5888
|
return [
|
|
5848
|
-
finding(
|
|
5889
|
+
finding(check16.id, {
|
|
5849
5890
|
severity: "warning",
|
|
5850
5891
|
message: "No TrimBox or BleedBox found. Bleed area cannot be verified.",
|
|
5851
5892
|
file: ctx.pdfPath
|
|
@@ -5858,7 +5899,7 @@ var check15 = {
|
|
|
5858
5899
|
const bleedSize = ctx.config.validate.pdf.bleedSize;
|
|
5859
5900
|
if (mediaW - trimW < bleedSize * 2 * 0.9) {
|
|
5860
5901
|
return [
|
|
5861
|
-
finding(
|
|
5902
|
+
finding(check16.id, {
|
|
5862
5903
|
severity: "warning",
|
|
5863
5904
|
message: `Bleed area appears insufficient. Expected at least ${bleedSize}pt on each side.`,
|
|
5864
5905
|
file: ctx.pdfPath
|
|
@@ -5869,10 +5910,10 @@ var check15 = {
|
|
|
5869
5910
|
return [];
|
|
5870
5911
|
}
|
|
5871
5912
|
};
|
|
5872
|
-
registerCheck(
|
|
5913
|
+
registerCheck(check16);
|
|
5873
5914
|
|
|
5874
5915
|
// src/checks/pdf/pdfx-metadata.ts
|
|
5875
|
-
var
|
|
5916
|
+
var check17 = {
|
|
5876
5917
|
id: "pdf.print.pdfx-metadata",
|
|
5877
5918
|
name: "PDF/X Metadata",
|
|
5878
5919
|
description: "Verifies PDF/X DOCINFO metadata via qpdf JSON",
|
|
@@ -5891,25 +5932,25 @@ var check16 = {
|
|
|
5891
5932
|
const objects = parseQpdfObjectsJson(stdout);
|
|
5892
5933
|
if (!objects) {
|
|
5893
5934
|
return [
|
|
5894
|
-
inspectionFailed(
|
|
5935
|
+
inspectionFailed(check17.id, "Unable to parse qpdf JSON output for PDF/X metadata checks.", { file: ctx.pdfPath })
|
|
5895
5936
|
];
|
|
5896
5937
|
}
|
|
5897
|
-
return getPdfxMetadataIssues(objects, ctx.config.pdfx.flavor).map((message) => finding(
|
|
5938
|
+
return getPdfxMetadataIssues(objects, ctx.config.pdfx.flavor).map((message) => finding(check17.id, {
|
|
5898
5939
|
severity: "error",
|
|
5899
5940
|
message,
|
|
5900
5941
|
file: ctx.pdfPath
|
|
5901
5942
|
}));
|
|
5902
5943
|
} catch {
|
|
5903
5944
|
return [
|
|
5904
|
-
inspectionFailed(
|
|
5945
|
+
inspectionFailed(check17.id, "Failed to inspect PDF objects with qpdf for PDF/X metadata.", { file: ctx.pdfPath })
|
|
5905
5946
|
];
|
|
5906
5947
|
}
|
|
5907
5948
|
}
|
|
5908
5949
|
};
|
|
5909
|
-
registerCheck(
|
|
5950
|
+
registerCheck(check17);
|
|
5910
5951
|
|
|
5911
5952
|
// src/checks/source/markdownlint.ts
|
|
5912
|
-
import { readFile as
|
|
5953
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
5913
5954
|
import { lint } from "markdownlint/sync";
|
|
5914
5955
|
import { parse as parseYaml2 } from "yaml";
|
|
5915
5956
|
|
|
@@ -5942,7 +5983,7 @@ function stripJsonComments(s) {
|
|
|
5942
5983
|
return s.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1");
|
|
5943
5984
|
}
|
|
5944
5985
|
async function loadConfig(configPath) {
|
|
5945
|
-
const raw = await
|
|
5986
|
+
const raw = await readFile10(configPath, "utf8");
|
|
5946
5987
|
let parsed;
|
|
5947
5988
|
if (configPath.endsWith(".json")) {
|
|
5948
5989
|
parsed = JSON.parse(raw);
|
|
@@ -5961,7 +6002,7 @@ async function loadConfig(configPath) {
|
|
|
5961
6002
|
return {};
|
|
5962
6003
|
}
|
|
5963
6004
|
var MARKDOWNLINT_CHECK_ID = "source.markdownlint";
|
|
5964
|
-
var
|
|
6005
|
+
var check18 = {
|
|
5965
6006
|
id: MARKDOWNLINT_CHECK_ID,
|
|
5966
6007
|
name: "Markdownlint",
|
|
5967
6008
|
description: "Runs markdownlint with project config to validate Markdown files",
|
|
@@ -5986,7 +6027,7 @@ var check17 = {
|
|
|
5986
6027
|
continue;
|
|
5987
6028
|
for (const v of violations) {
|
|
5988
6029
|
out.push({
|
|
5989
|
-
checkId:
|
|
6030
|
+
checkId: check18.id,
|
|
5990
6031
|
severity: "warning",
|
|
5991
6032
|
message: `${v.ruleDescription} (${v.ruleNames.join("/")})`,
|
|
5992
6033
|
file,
|
|
@@ -5999,15 +6040,15 @@ var check17 = {
|
|
|
5999
6040
|
return out;
|
|
6000
6041
|
}
|
|
6001
6042
|
};
|
|
6002
|
-
registerCheck(
|
|
6043
|
+
registerCheck(check18);
|
|
6003
6044
|
|
|
6004
6045
|
// src/checks/source/htmlhint.ts
|
|
6005
6046
|
import { existsSync as existsSync7 } from "node:fs";
|
|
6006
|
-
import { readFile as
|
|
6047
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
6007
6048
|
import htmlhint from "htmlhint";
|
|
6008
6049
|
var { HTMLHint } = htmlhint;
|
|
6009
6050
|
var CONFIG_NAMES2 = [".htmlhintrc"];
|
|
6010
|
-
var
|
|
6051
|
+
var check19 = {
|
|
6011
6052
|
id: "source.htmlhint",
|
|
6012
6053
|
name: "HTMLHint",
|
|
6013
6054
|
description: "Runs htmlhint with project config to validate generated HTML",
|
|
@@ -6023,11 +6064,11 @@ var check18 = {
|
|
|
6023
6064
|
const resolvedConfig = findConfigFile(ctx.inputDir, CONFIG_NAMES2, configPath);
|
|
6024
6065
|
if (!resolvedConfig && !configPath)
|
|
6025
6066
|
return [];
|
|
6026
|
-
const ruleset = resolvedConfig ? JSON.parse(await
|
|
6027
|
-
const html = await
|
|
6067
|
+
const ruleset = resolvedConfig ? JSON.parse(await readFile11(resolvedConfig, "utf8")) : HTMLHint.defaultRuleset;
|
|
6068
|
+
const html = await readFile11(ctx.htmlPath, "utf8");
|
|
6028
6069
|
const messages = HTMLHint.verify(html, ruleset);
|
|
6029
6070
|
return messages.map((m) => ({
|
|
6030
|
-
checkId:
|
|
6071
|
+
checkId: check19.id,
|
|
6031
6072
|
severity: m.type === "error" ? "error" : "warning",
|
|
6032
6073
|
message: `${m.rule.id}: ${m.message}`,
|
|
6033
6074
|
file: ctx.htmlPath,
|
|
@@ -6036,10 +6077,10 @@ var check18 = {
|
|
|
6036
6077
|
}));
|
|
6037
6078
|
}
|
|
6038
6079
|
};
|
|
6039
|
-
registerCheck(
|
|
6080
|
+
registerCheck(check19);
|
|
6040
6081
|
|
|
6041
6082
|
// src/checks/source/stylelint.ts
|
|
6042
|
-
import { readFile as
|
|
6083
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
6043
6084
|
|
|
6044
6085
|
// src/lib/printsafe.ts
|
|
6045
6086
|
import postcss from "postcss";
|
|
@@ -6356,7 +6397,7 @@ function checkCssRaw(css, from) {
|
|
|
6356
6397
|
}
|
|
6357
6398
|
|
|
6358
6399
|
// src/checks/source/stylelint.ts
|
|
6359
|
-
var
|
|
6400
|
+
var check20 = {
|
|
6360
6401
|
id: "source.stylelint",
|
|
6361
6402
|
name: "Print-safety CSS",
|
|
6362
6403
|
description: "Checks CSS for print-safety issues (remote URLs, rasterizing effects, and page-layer containment)",
|
|
@@ -6373,10 +6414,10 @@ var check19 = {
|
|
|
6373
6414
|
for (const file of files) {
|
|
6374
6415
|
let css;
|
|
6375
6416
|
try {
|
|
6376
|
-
css = await
|
|
6417
|
+
css = await readFile12(file, "utf8");
|
|
6377
6418
|
} catch (err) {
|
|
6378
6419
|
results.push({
|
|
6379
|
-
checkId:
|
|
6420
|
+
checkId: check20.id,
|
|
6380
6421
|
severity: "error",
|
|
6381
6422
|
message: `Cannot read stylesheet: ${err instanceof Error ? err.message : String(err)}`,
|
|
6382
6423
|
file
|
|
@@ -6385,7 +6426,7 @@ var check19 = {
|
|
|
6385
6426
|
}
|
|
6386
6427
|
for (const w of checkCss(css, file, { rules: ctx.config.lint.rules })) {
|
|
6387
6428
|
results.push({
|
|
6388
|
-
checkId:
|
|
6429
|
+
checkId: check20.id,
|
|
6389
6430
|
severity: w.severity,
|
|
6390
6431
|
message: `${w.rule}: ${w.message}`,
|
|
6391
6432
|
file,
|
|
@@ -6397,10 +6438,10 @@ var check19 = {
|
|
|
6397
6438
|
return results;
|
|
6398
6439
|
}
|
|
6399
6440
|
};
|
|
6400
|
-
registerCheck(
|
|
6441
|
+
registerCheck(check20);
|
|
6401
6442
|
|
|
6402
6443
|
// src/checks/source/css-ownership.ts
|
|
6403
|
-
import { readFile as
|
|
6444
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
6404
6445
|
|
|
6405
6446
|
// src/lib/style-resolver.ts
|
|
6406
6447
|
import { existsSync as existsSync8 } from "node:fs";
|
|
@@ -6468,7 +6509,7 @@ import postcss2 from "postcss";
|
|
|
6468
6509
|
import { parse as parseYaml3 } from "yaml";
|
|
6469
6510
|
var CONFIG_NAMES3 = [".gutterpress/css-contract.yaml", ".gutterpress/css-contract.yml"];
|
|
6470
6511
|
async function loadContract(configPath) {
|
|
6471
|
-
const raw = await
|
|
6512
|
+
const raw = await readFile13(configPath, "utf8");
|
|
6472
6513
|
const parsed = parseYaml3(raw);
|
|
6473
6514
|
if (parsed && typeof parsed === "object")
|
|
6474
6515
|
return parsed;
|
|
@@ -6581,7 +6622,7 @@ function checkOwnership(root, file, maps) {
|
|
|
6581
6622
|
});
|
|
6582
6623
|
return out;
|
|
6583
6624
|
}
|
|
6584
|
-
var
|
|
6625
|
+
var check21 = {
|
|
6585
6626
|
id: "source.css-ownership",
|
|
6586
6627
|
name: "CSS ownership contract",
|
|
6587
6628
|
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.",
|
|
@@ -6604,7 +6645,7 @@ var check20 = {
|
|
|
6604
6645
|
} catch (err) {
|
|
6605
6646
|
return [
|
|
6606
6647
|
{
|
|
6607
|
-
checkId:
|
|
6648
|
+
checkId: check21.id,
|
|
6608
6649
|
severity: "error",
|
|
6609
6650
|
message: `Failed to parse CSS ownership contract "${configPath}": ${err instanceof Error ? err.message : String(err)}`,
|
|
6610
6651
|
file: configPath
|
|
@@ -6619,7 +6660,7 @@ var check20 = {
|
|
|
6619
6660
|
continue;
|
|
6620
6661
|
let css;
|
|
6621
6662
|
try {
|
|
6622
|
-
css = await
|
|
6663
|
+
css = await readFile13(absFile, "utf8");
|
|
6623
6664
|
} catch {
|
|
6624
6665
|
continue;
|
|
6625
6666
|
}
|
|
@@ -6646,7 +6687,7 @@ var check20 = {
|
|
|
6646
6687
|
}
|
|
6647
6688
|
for (const v of violations) {
|
|
6648
6689
|
results.push({
|
|
6649
|
-
checkId:
|
|
6690
|
+
checkId: check21.id,
|
|
6650
6691
|
severity: "warning",
|
|
6651
6692
|
message: v.message,
|
|
6652
6693
|
file: absFile,
|
|
@@ -6658,7 +6699,7 @@ var check20 = {
|
|
|
6658
6699
|
return results;
|
|
6659
6700
|
}
|
|
6660
6701
|
};
|
|
6661
|
-
registerCheck(
|
|
6702
|
+
registerCheck(check21);
|
|
6662
6703
|
|
|
6663
6704
|
// src/checks/source/local-refs.ts
|
|
6664
6705
|
import { existsSync as existsSync9 } from "node:fs";
|
|
@@ -6666,7 +6707,7 @@ import { dirname as dirname3, resolve as resolve5 } from "node:path";
|
|
|
6666
6707
|
|
|
6667
6708
|
// src/lib/asset-inline.ts
|
|
6668
6709
|
import { createHash as createHash2 } from "node:crypto";
|
|
6669
|
-
import { readFile as
|
|
6710
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
6670
6711
|
import path8 from "node:path";
|
|
6671
6712
|
import postcss3 from "postcss";
|
|
6672
6713
|
var FONT_EXTS = new Set([".woff2", ".woff", ".ttf", ".otf"]);
|
|
@@ -6731,7 +6772,7 @@ function mimeFor(ext) {
|
|
|
6731
6772
|
}
|
|
6732
6773
|
async function readOrThrow(absPath, kind, referencedFrom) {
|
|
6733
6774
|
try {
|
|
6734
|
-
return await
|
|
6775
|
+
return await readFile14(absPath);
|
|
6735
6776
|
} catch {
|
|
6736
6777
|
throw new BuildError(`Missing ${kind}: ${absPath}
|
|
6737
6778
|
referenced from ${referencedFrom}
|
|
@@ -6874,7 +6915,7 @@ async function collectStyleDependencies(projectDir, stylePaths) {
|
|
|
6874
6915
|
found.add(abs);
|
|
6875
6916
|
let source;
|
|
6876
6917
|
try {
|
|
6877
|
-
source = await
|
|
6918
|
+
source = await readFile14(abs, "utf-8");
|
|
6878
6919
|
} catch {
|
|
6879
6920
|
return;
|
|
6880
6921
|
}
|
|
@@ -6951,7 +6992,7 @@ async function inlineShapeUrls(html, baseDir) {
|
|
|
6951
6992
|
continue;
|
|
6952
6993
|
let bytes;
|
|
6953
6994
|
try {
|
|
6954
|
-
bytes = await
|
|
6995
|
+
bytes = await readFile14(abs);
|
|
6955
6996
|
} catch {
|
|
6956
6997
|
continue;
|
|
6957
6998
|
}
|
|
@@ -6969,7 +7010,7 @@ async function inlineShapeUrls(html, baseDir) {
|
|
|
6969
7010
|
}
|
|
6970
7011
|
|
|
6971
7012
|
// src/checks/source/local-ref-parser.ts
|
|
6972
|
-
import { readFile as
|
|
7013
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
6973
7014
|
function newlineOffsets(content) {
|
|
6974
7015
|
const offsets = [];
|
|
6975
7016
|
for (let i = 0;i < content.length; i++)
|
|
@@ -7038,7 +7079,7 @@ async function* renderedLocalRefs(ctx, checkId, results) {
|
|
|
7038
7079
|
for (const file of files) {
|
|
7039
7080
|
let refs;
|
|
7040
7081
|
try {
|
|
7041
|
-
refs = collectRenderedLocalRefs(await
|
|
7082
|
+
refs = collectRenderedLocalRefs(await readFile15(file, "utf8"));
|
|
7042
7083
|
} catch {
|
|
7043
7084
|
results.push(inspectionFailed(checkId, `Could not read source file: ${file}`, { file }));
|
|
7044
7085
|
continue;
|
|
@@ -7049,7 +7090,7 @@ async function* renderedLocalRefs(ctx, checkId, results) {
|
|
|
7049
7090
|
}
|
|
7050
7091
|
|
|
7051
7092
|
// src/checks/source/local-refs.ts
|
|
7052
|
-
var
|
|
7093
|
+
var check22 = {
|
|
7053
7094
|
id: "source.links.local-refs",
|
|
7054
7095
|
name: "Local Markdown References",
|
|
7055
7096
|
description: "Checks local markdown links and image refs resolve on disk",
|
|
@@ -7057,12 +7098,12 @@ var check21 = {
|
|
|
7057
7098
|
phase: "pre-build",
|
|
7058
7099
|
async run(ctx) {
|
|
7059
7100
|
const results = [];
|
|
7060
|
-
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx,
|
|
7101
|
+
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check22.id, results)) {
|
|
7061
7102
|
if (isNonFilesystemRef(ref))
|
|
7062
7103
|
continue;
|
|
7063
7104
|
const escape = kind === "image" ? proseImageRefError(ref, ctx.inputDir) : null;
|
|
7064
7105
|
if (escape) {
|
|
7065
|
-
results.push(finding(
|
|
7106
|
+
results.push(finding(check22.id, {
|
|
7066
7107
|
severity: "error",
|
|
7067
7108
|
message: escape,
|
|
7068
7109
|
file,
|
|
@@ -7072,7 +7113,7 @@ var check21 = {
|
|
|
7072
7113
|
}
|
|
7073
7114
|
if (localRefExists(ref, kind, file, ctx.inputDir))
|
|
7074
7115
|
continue;
|
|
7075
|
-
results.push(finding(
|
|
7116
|
+
results.push(finding(check22.id, {
|
|
7076
7117
|
severity: kind === "image" ? "warning" : "error",
|
|
7077
7118
|
code: kind === "image" ? "missing-image-placeholder" : undefined,
|
|
7078
7119
|
message: kind === "image" ? `Local image not found; build will substitute a magenta placeholder: ${ref}` : `Local reference not found: ${ref}`,
|
|
@@ -7097,7 +7138,7 @@ function localRefExists(ref, kind, sourceFile, projectRoot) {
|
|
|
7097
7138
|
return existsSync9(fileRelative);
|
|
7098
7139
|
}
|
|
7099
7140
|
}
|
|
7100
|
-
registerCheck(
|
|
7141
|
+
registerCheck(check22);
|
|
7101
7142
|
|
|
7102
7143
|
// src/lib/build-staging.ts
|
|
7103
7144
|
import path9 from "node:path";
|
|
@@ -7363,7 +7404,7 @@ async function createStageRoot() {
|
|
|
7363
7404
|
}
|
|
7364
7405
|
|
|
7365
7406
|
// src/checks/source/dangling-links.ts
|
|
7366
|
-
var
|
|
7407
|
+
var check23 = {
|
|
7367
7408
|
id: "source.links.dangling",
|
|
7368
7409
|
name: "Dangling Links",
|
|
7369
7410
|
description: "Flags relative links the printed book cannot open (their href is dropped at print time)",
|
|
@@ -7371,10 +7412,10 @@ var check22 = {
|
|
|
7371
7412
|
phase: "pre-build",
|
|
7372
7413
|
async run(ctx) {
|
|
7373
7414
|
const results = [];
|
|
7374
|
-
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx,
|
|
7415
|
+
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check23.id, results)) {
|
|
7375
7416
|
if (kind !== "link" || isPrintResolvableHref(ref))
|
|
7376
7417
|
continue;
|
|
7377
|
-
results.push(finding(
|
|
7418
|
+
results.push(finding(check23.id, {
|
|
7378
7419
|
severity: "warning",
|
|
7379
7420
|
code: "dangling-link",
|
|
7380
7421
|
data: { ref },
|
|
@@ -7386,11 +7427,11 @@ var check22 = {
|
|
|
7386
7427
|
return results;
|
|
7387
7428
|
}
|
|
7388
7429
|
};
|
|
7389
|
-
registerCheck(
|
|
7430
|
+
registerCheck(check23);
|
|
7390
7431
|
|
|
7391
7432
|
// src/checks/source/accessibility-alt-text.ts
|
|
7392
|
-
import { readFile as
|
|
7393
|
-
var
|
|
7433
|
+
import { readFile as readFile16 } from "node:fs/promises";
|
|
7434
|
+
var check24 = {
|
|
7394
7435
|
id: "source.accessibility.alt-text",
|
|
7395
7436
|
name: "Image Alt Text",
|
|
7396
7437
|
description: "Checks markdown images include non-empty alt text",
|
|
@@ -7403,7 +7444,7 @@ var check23 = {
|
|
|
7403
7444
|
const results = [];
|
|
7404
7445
|
for (const file of files) {
|
|
7405
7446
|
try {
|
|
7406
|
-
const content = await
|
|
7447
|
+
const content = await readFile16(file, "utf8");
|
|
7407
7448
|
const lines = content.split(`
|
|
7408
7449
|
`);
|
|
7409
7450
|
let inFence = false;
|
|
@@ -7419,7 +7460,7 @@ var check23 = {
|
|
|
7419
7460
|
if (alt.trim())
|
|
7420
7461
|
continue;
|
|
7421
7462
|
results.push({
|
|
7422
|
-
checkId:
|
|
7463
|
+
checkId: check24.id,
|
|
7423
7464
|
severity: "warning",
|
|
7424
7465
|
message: "Image is missing alt text",
|
|
7425
7466
|
file,
|
|
@@ -7444,11 +7485,11 @@ function extractImageAlts(line) {
|
|
|
7444
7485
|
}
|
|
7445
7486
|
return alts;
|
|
7446
7487
|
}
|
|
7447
|
-
registerCheck(
|
|
7488
|
+
registerCheck(check24);
|
|
7448
7489
|
|
|
7449
7490
|
// src/checks/source/accessibility-heading-order.ts
|
|
7450
|
-
import { readFile as
|
|
7451
|
-
var
|
|
7491
|
+
import { readFile as readFile17 } from "node:fs/promises";
|
|
7492
|
+
var check25 = {
|
|
7452
7493
|
id: "source.accessibility.heading-order",
|
|
7453
7494
|
name: "Heading Order",
|
|
7454
7495
|
description: "Checks markdown heading levels do not jump by more than one",
|
|
@@ -7461,7 +7502,7 @@ var check24 = {
|
|
|
7461
7502
|
const results = [];
|
|
7462
7503
|
for (const file of files) {
|
|
7463
7504
|
try {
|
|
7464
|
-
const content = await
|
|
7505
|
+
const content = await readFile17(file, "utf8");
|
|
7465
7506
|
const lines = content.split(`
|
|
7466
7507
|
`);
|
|
7467
7508
|
let inFence = false;
|
|
@@ -7476,7 +7517,7 @@ var check24 = {
|
|
|
7476
7517
|
if (inFence)
|
|
7477
7518
|
continue;
|
|
7478
7519
|
const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
|
|
7479
|
-
if (comment && parseDisableDirective(comment[1])?.includes(
|
|
7520
|
+
if (comment && parseDisableDirective(comment[1])?.includes(check25.id)) {
|
|
7480
7521
|
silencedLine = i + 1;
|
|
7481
7522
|
continue;
|
|
7482
7523
|
}
|
|
@@ -7486,7 +7527,7 @@ var check24 = {
|
|
|
7486
7527
|
const level = match[1].length;
|
|
7487
7528
|
if (prevLevel != null && level > prevLevel + 1 && i !== silencedLine) {
|
|
7488
7529
|
results.push({
|
|
7489
|
-
checkId:
|
|
7530
|
+
checkId: check25.id,
|
|
7490
7531
|
severity: "warning",
|
|
7491
7532
|
message: `Heading level jump from h${prevLevel} to h${level}`,
|
|
7492
7533
|
file,
|
|
@@ -7500,11 +7541,11 @@ var check24 = {
|
|
|
7500
7541
|
return results;
|
|
7501
7542
|
}
|
|
7502
7543
|
};
|
|
7503
|
-
registerCheck(
|
|
7544
|
+
registerCheck(check25);
|
|
7504
7545
|
|
|
7505
7546
|
// src/checks/source/layout-markers.ts
|
|
7506
|
-
import { readFile as
|
|
7507
|
-
var
|
|
7547
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
7548
|
+
var check26 = {
|
|
7508
7549
|
id: "source.markdown.layout-markers",
|
|
7509
7550
|
name: "Layout Markers",
|
|
7510
7551
|
description: "Reports @page/@section/@chapter marker arguments Gutterpress could not understand, plus any unknown gp-* class",
|
|
@@ -7516,16 +7557,16 @@ var check25 = {
|
|
|
7516
7557
|
return [];
|
|
7517
7558
|
const results = [];
|
|
7518
7559
|
const plugins = await loadPlugins(ctx.config.extensions, ctx.inputDir, (ref, error2) => {
|
|
7519
|
-
results.push(inspectionFailed(
|
|
7560
|
+
results.push(inspectionFailed(check26.id, `Plugin "${ref}" could not be loaded, so markers it defines were not checked: ${error2.message}`));
|
|
7520
7561
|
});
|
|
7521
7562
|
const md = createMarkdownRenderer(plugins);
|
|
7522
7563
|
for (const file of files) {
|
|
7523
7564
|
try {
|
|
7524
|
-
const content = await
|
|
7565
|
+
const content = await readFile18(file, "utf8");
|
|
7525
7566
|
const env = {};
|
|
7526
7567
|
md.render(content, env);
|
|
7527
7568
|
for (const w of env.layoutWarnings ?? []) {
|
|
7528
|
-
results.push(finding(
|
|
7569
|
+
results.push(finding(check26.id, {
|
|
7529
7570
|
severity: w.severity ?? "warning",
|
|
7530
7571
|
message: w.message,
|
|
7531
7572
|
file,
|
|
@@ -7534,16 +7575,16 @@ var check25 = {
|
|
|
7534
7575
|
}));
|
|
7535
7576
|
}
|
|
7536
7577
|
} catch (error2) {
|
|
7537
|
-
results.push(inspectionFailed(
|
|
7578
|
+
results.push(inspectionFailed(check26.id, `Could not check layout markers in ${file}: ${error2 instanceof Error ? error2.message : String(error2)}`, { file }));
|
|
7538
7579
|
}
|
|
7539
7580
|
}
|
|
7540
7581
|
return results;
|
|
7541
7582
|
}
|
|
7542
7583
|
};
|
|
7543
|
-
registerCheck(
|
|
7584
|
+
registerCheck(check26);
|
|
7544
7585
|
|
|
7545
7586
|
// src/checks/source/merge-markers.ts
|
|
7546
|
-
import { readFile as
|
|
7587
|
+
import { readFile as readFile19 } from "node:fs/promises";
|
|
7547
7588
|
import path10 from "node:path";
|
|
7548
7589
|
|
|
7549
7590
|
// src/checks/asset/extensions.ts
|
|
@@ -7573,7 +7614,7 @@ var CLOSE_SENTINEL = ">>>>>>> online version";
|
|
|
7573
7614
|
function withoutOnlineTag(relPath) {
|
|
7574
7615
|
return relPath.replace(/\.online(?=\.[^./]*$|$)/, "");
|
|
7575
7616
|
}
|
|
7576
|
-
var
|
|
7617
|
+
var check27 = {
|
|
7577
7618
|
id: "source.sync.merge-markers",
|
|
7578
7619
|
name: "Combined Versions",
|
|
7579
7620
|
description: "Finds passages and files still holding two versions (yours and the online copy) after a sync",
|
|
@@ -7585,9 +7626,9 @@ var check26 = {
|
|
|
7585
7626
|
for (const file of textFiles) {
|
|
7586
7627
|
let content;
|
|
7587
7628
|
try {
|
|
7588
|
-
content = await
|
|
7629
|
+
content = await readFile19(file, "utf8");
|
|
7589
7630
|
} catch {
|
|
7590
|
-
results.push(inspectionFailed(
|
|
7631
|
+
results.push(inspectionFailed(check27.id, `Could not read source file: ${file}`, { file }));
|
|
7591
7632
|
continue;
|
|
7592
7633
|
}
|
|
7593
7634
|
if (!content.includes(OPEN_SENTINEL) && !content.includes(CLOSE_SENTINEL))
|
|
@@ -7599,7 +7640,7 @@ var check26 = {
|
|
|
7599
7640
|
const line = lines[i].endsWith("\r") ? lines[i].slice(0, -1) : lines[i];
|
|
7600
7641
|
if (line === OPEN_SENTINEL) {
|
|
7601
7642
|
inBlock = true;
|
|
7602
|
-
results.push(finding(
|
|
7643
|
+
results.push(finding(check27.id, {
|
|
7603
7644
|
severity: "error",
|
|
7604
7645
|
code: "two-versions-passage",
|
|
7605
7646
|
message: "This passage has two versions (yours and the online copy) — keep what you want, then delete the marker lines.",
|
|
@@ -7608,7 +7649,7 @@ var check26 = {
|
|
|
7608
7649
|
}));
|
|
7609
7650
|
} else if (line === CLOSE_SENTINEL) {
|
|
7610
7651
|
if (!inBlock) {
|
|
7611
|
-
results.push(finding(
|
|
7652
|
+
results.push(finding(check27.id, {
|
|
7612
7653
|
severity: "error",
|
|
7613
7654
|
code: "leftover-version-marker",
|
|
7614
7655
|
message: "This marker line is left over from combining two versions — delete it.",
|
|
@@ -7629,7 +7670,7 @@ var check26 = {
|
|
|
7629
7670
|
});
|
|
7630
7671
|
for (const sibling of siblings.sort()) {
|
|
7631
7672
|
const rel = path10.relative(ctx.inputDir, sibling).split(path10.sep).join("/");
|
|
7632
|
-
results.push(finding(
|
|
7673
|
+
results.push(finding(check27.id, {
|
|
7633
7674
|
severity: "warning",
|
|
7634
7675
|
code: "kept-both-versions",
|
|
7635
7676
|
message: `Two versions of ${withoutOnlineTag(rel)} are in your book — keep the one you want, then delete the other.`,
|
|
@@ -7637,18 +7678,18 @@ var check26 = {
|
|
|
7637
7678
|
}));
|
|
7638
7679
|
}
|
|
7639
7680
|
} catch (error2) {
|
|
7640
|
-
results.push(inspectionFailed(
|
|
7681
|
+
results.push(inspectionFailed(check27.id, `Could not scan for kept-both files: ${error2 instanceof Error ? error2.message : String(error2)}`));
|
|
7641
7682
|
}
|
|
7642
7683
|
return results;
|
|
7643
7684
|
}
|
|
7644
7685
|
};
|
|
7645
|
-
registerCheck(
|
|
7686
|
+
registerCheck(check27);
|
|
7646
7687
|
|
|
7647
7688
|
// src/checks/asset/image-file-size.ts
|
|
7648
7689
|
import { stat as stat4 } from "node:fs/promises";
|
|
7649
7690
|
|
|
7650
7691
|
// src/lib/image-inspect.ts
|
|
7651
|
-
import { stat as stat3, readFile as
|
|
7692
|
+
import { stat as stat3, readFile as readFile20 } from "node:fs/promises";
|
|
7652
7693
|
var DEFAULT_DPI = 72;
|
|
7653
7694
|
var cache = new Map;
|
|
7654
7695
|
async function inspectImage(path11) {
|
|
@@ -7663,7 +7704,7 @@ async function inspectImage(path11) {
|
|
|
7663
7704
|
return hit.info;
|
|
7664
7705
|
let info2 = null;
|
|
7665
7706
|
try {
|
|
7666
|
-
const buf = await
|
|
7707
|
+
const buf = await readFile20(path11);
|
|
7667
7708
|
info2 = parseImage(buf);
|
|
7668
7709
|
} catch {
|
|
7669
7710
|
info2 = null;
|
|
@@ -7868,7 +7909,7 @@ async function collectImageFiles(dirs, exts, ignore2 = ASSET_SCAN_IGNORE_GLOBS)
|
|
|
7868
7909
|
}
|
|
7869
7910
|
|
|
7870
7911
|
// src/checks/asset/image-file-size.ts
|
|
7871
|
-
var
|
|
7912
|
+
var check28 = {
|
|
7872
7913
|
id: "asset.image.file-size",
|
|
7873
7914
|
name: "Image File Size",
|
|
7874
7915
|
description: "Checks that image files do not exceed the maximum size limit",
|
|
@@ -7889,14 +7930,14 @@ var check27 = {
|
|
|
7889
7930
|
if (info2.size > maxSize) {
|
|
7890
7931
|
const sizeMb = (info2.size / 1e6).toFixed(1);
|
|
7891
7932
|
const maxMb = (maxSize / 1e6).toFixed(1);
|
|
7892
|
-
results.push(finding(
|
|
7933
|
+
results.push(finding(check28.id, {
|
|
7893
7934
|
severity: "warning",
|
|
7894
7935
|
message: `Image file too large: ${sizeMb}MB (max ${maxMb}MB)`,
|
|
7895
7936
|
file
|
|
7896
7937
|
}));
|
|
7897
7938
|
}
|
|
7898
7939
|
} catch {
|
|
7899
|
-
results.push(inspectionFailed(
|
|
7940
|
+
results.push(inspectionFailed(check28.id, `Could not stat image file: ${file}`, {
|
|
7900
7941
|
file
|
|
7901
7942
|
}));
|
|
7902
7943
|
}
|
|
@@ -7904,10 +7945,10 @@ var check27 = {
|
|
|
7904
7945
|
return results;
|
|
7905
7946
|
}
|
|
7906
7947
|
};
|
|
7907
|
-
registerCheck(
|
|
7948
|
+
registerCheck(check28);
|
|
7908
7949
|
|
|
7909
7950
|
// src/checks/asset/image-resolution.ts
|
|
7910
|
-
var
|
|
7951
|
+
var check29 = {
|
|
7911
7952
|
id: "asset.image.resolution",
|
|
7912
7953
|
name: "Image Resolution",
|
|
7913
7954
|
description: "Checks source image DPI from embedded density metadata",
|
|
@@ -7929,7 +7970,7 @@ var check28 = {
|
|
|
7929
7970
|
const { xDpi, yDpi } = info2;
|
|
7930
7971
|
if (xDpi > 0 && yDpi > 0 && (xDpi < minDpi || yDpi < minDpi)) {
|
|
7931
7972
|
results.push({
|
|
7932
|
-
checkId:
|
|
7973
|
+
checkId: check29.id,
|
|
7933
7974
|
severity: "warning",
|
|
7934
7975
|
message: `Image resolution too low: ${xDpi}x${yDpi} DPI (minimum ${minDpi} DPI)`,
|
|
7935
7976
|
file
|
|
@@ -7939,7 +7980,7 @@ var check28 = {
|
|
|
7939
7980
|
return results;
|
|
7940
7981
|
}
|
|
7941
7982
|
};
|
|
7942
|
-
registerCheck(
|
|
7983
|
+
registerCheck(check29);
|
|
7943
7984
|
|
|
7944
7985
|
// src/checks/asset/image-color-space.ts
|
|
7945
7986
|
var LABEL = {
|
|
@@ -7947,7 +7988,7 @@ var LABEL = {
|
|
|
7947
7988
|
gray: "Gray",
|
|
7948
7989
|
cmyk: "CMYK"
|
|
7949
7990
|
};
|
|
7950
|
-
var
|
|
7991
|
+
var check30 = {
|
|
7951
7992
|
id: "asset.image.color-space",
|
|
7952
7993
|
name: "Image Color Space",
|
|
7953
7994
|
description: "Validates image color spaces against allowed list",
|
|
@@ -7968,7 +8009,7 @@ var check29 = {
|
|
|
7968
8009
|
const cs = info2?.colorSpace;
|
|
7969
8010
|
if (cs && !allowedLower.has(cs)) {
|
|
7970
8011
|
results.push({
|
|
7971
|
-
checkId:
|
|
8012
|
+
checkId: check30.id,
|
|
7972
8013
|
severity: "warning",
|
|
7973
8014
|
message: `Image uses ${LABEL[cs] ?? cs} color space (allowed: ${allowed.join(", ")})`,
|
|
7974
8015
|
file
|
|
@@ -7978,10 +8019,10 @@ var check29 = {
|
|
|
7978
8019
|
return results;
|
|
7979
8020
|
}
|
|
7980
8021
|
};
|
|
7981
|
-
registerCheck(
|
|
8022
|
+
registerCheck(check30);
|
|
7982
8023
|
|
|
7983
8024
|
// src/checks/asset/image-alpha.ts
|
|
7984
|
-
var
|
|
8025
|
+
var check31 = {
|
|
7985
8026
|
id: "asset.image.alpha-channel",
|
|
7986
8027
|
name: "Image Alpha Channel",
|
|
7987
8028
|
description: "Checks for alpha channels in PNG/TIFF images",
|
|
@@ -7999,7 +8040,7 @@ var check30 = {
|
|
|
7999
8040
|
const info2 = await inspectImage(file);
|
|
8000
8041
|
if (info2?.hasAlpha) {
|
|
8001
8042
|
results.push({
|
|
8002
|
-
checkId:
|
|
8043
|
+
checkId: check31.id,
|
|
8003
8044
|
severity: "warning",
|
|
8004
8045
|
message: "Image contains alpha channel, which may cause print issues",
|
|
8005
8046
|
file
|
|
@@ -8009,10 +8050,10 @@ var check30 = {
|
|
|
8009
8050
|
return results;
|
|
8010
8051
|
}
|
|
8011
8052
|
};
|
|
8012
|
-
registerCheck(
|
|
8053
|
+
registerCheck(check31);
|
|
8013
8054
|
|
|
8014
8055
|
// src/checks/asset/image-tac.ts
|
|
8015
|
-
var
|
|
8056
|
+
var check32 = {
|
|
8016
8057
|
id: "asset.image.tac-raster",
|
|
8017
8058
|
name: "Image TAC (Raster)",
|
|
8018
8059
|
description: "Rasterizes and checks TAC per image using Ghostscript",
|
|
@@ -8031,7 +8072,7 @@ var check31 = {
|
|
|
8031
8072
|
const ghostscript = await resolveGhostscript();
|
|
8032
8073
|
if (!ghostscript) {
|
|
8033
8074
|
return [
|
|
8034
|
-
inspectionFailed(
|
|
8075
|
+
inspectionFailed(check32.id, "Could not inspect image ink coverage: Ghostscript executable not found")
|
|
8035
8076
|
];
|
|
8036
8077
|
}
|
|
8037
8078
|
for (const file of files) {
|
|
@@ -8049,7 +8090,7 @@ var check31 = {
|
|
|
8049
8090
|
if (nums.length === 4 && nums.every((n) => Number.isFinite(n))) {
|
|
8050
8091
|
const tac = (nums[0] + nums[1] + nums[2] + nums[3]) * 100;
|
|
8051
8092
|
if (tac > maxTac) {
|
|
8052
|
-
results.push(finding(
|
|
8093
|
+
results.push(finding(check32.id, {
|
|
8053
8094
|
severity: "warning",
|
|
8054
8095
|
message: `Image TAC exceeds limit: ${tac.toFixed(1)}% (max ${maxTac}%)`,
|
|
8055
8096
|
file,
|
|
@@ -8061,16 +8102,16 @@ var check31 = {
|
|
|
8061
8102
|
}
|
|
8062
8103
|
}
|
|
8063
8104
|
} catch {
|
|
8064
|
-
results.push(inspectionFailed(
|
|
8105
|
+
results.push(inspectionFailed(check32.id, `Could not inspect image ink coverage: ${file}`, { file }));
|
|
8065
8106
|
}
|
|
8066
8107
|
}
|
|
8067
8108
|
return results;
|
|
8068
8109
|
}
|
|
8069
8110
|
};
|
|
8070
|
-
registerCheck(
|
|
8111
|
+
registerCheck(check32);
|
|
8071
8112
|
|
|
8072
8113
|
// src/checks/asset/approved-fonts.ts
|
|
8073
|
-
var
|
|
8114
|
+
var check33 = {
|
|
8074
8115
|
id: "asset.font.approved-files",
|
|
8075
8116
|
name: "Approved Font Files",
|
|
8076
8117
|
description: "Checks font files against the approved file patterns",
|
|
@@ -8109,7 +8150,7 @@ var check32 = {
|
|
|
8109
8150
|
for (const font of allFonts) {
|
|
8110
8151
|
if (!approvedFonts.has(font)) {
|
|
8111
8152
|
results.push({
|
|
8112
|
-
checkId:
|
|
8153
|
+
checkId: check33.id,
|
|
8113
8154
|
severity: "warning",
|
|
8114
8155
|
message: "Font file not in approved list",
|
|
8115
8156
|
file: font
|
|
@@ -8119,7 +8160,7 @@ var check32 = {
|
|
|
8119
8160
|
return results;
|
|
8120
8161
|
}
|
|
8121
8162
|
};
|
|
8122
|
-
registerCheck(
|
|
8163
|
+
registerCheck(check33);
|
|
8123
8164
|
|
|
8124
8165
|
// src/checks/asset/font-license.ts
|
|
8125
8166
|
import { existsSync as existsSync10 } from "node:fs";
|
|
@@ -8134,7 +8175,7 @@ var LICENSE_NAMES = [
|
|
|
8134
8175
|
"OFL-1.1.txt",
|
|
8135
8176
|
"COPYING"
|
|
8136
8177
|
];
|
|
8137
|
-
var
|
|
8178
|
+
var check34 = {
|
|
8138
8179
|
id: "asset.font.license",
|
|
8139
8180
|
name: "Font License",
|
|
8140
8181
|
description: "Checks for license files in font directories",
|
|
@@ -8161,7 +8202,7 @@ var check33 = {
|
|
|
8161
8202
|
const hasLicense = LICENSE_NAMES.some((name) => existsSync10(resolve6(fontDir, name)));
|
|
8162
8203
|
if (!hasLicense) {
|
|
8163
8204
|
results.push({
|
|
8164
|
-
checkId:
|
|
8205
|
+
checkId: check34.id,
|
|
8165
8206
|
severity: "warning",
|
|
8166
8207
|
message: `No font license file found in directory`,
|
|
8167
8208
|
file: fontDir
|
|
@@ -8171,10 +8212,10 @@ var check33 = {
|
|
|
8171
8212
|
return results;
|
|
8172
8213
|
}
|
|
8173
8214
|
};
|
|
8174
|
-
registerCheck(
|
|
8215
|
+
registerCheck(check34);
|
|
8175
8216
|
|
|
8176
8217
|
// src/checks/heuristic/text-density.ts
|
|
8177
|
-
var
|
|
8218
|
+
var check35 = {
|
|
8178
8219
|
id: "heuristic.whitespace.text-density",
|
|
8179
8220
|
name: "Text Density",
|
|
8180
8221
|
description: "Checks characters-per-page ratio",
|
|
@@ -8203,7 +8244,7 @@ var check34 = {
|
|
|
8203
8244
|
});
|
|
8204
8245
|
if (lowPages.length > 0) {
|
|
8205
8246
|
results.push({
|
|
8206
|
-
checkId:
|
|
8247
|
+
checkId: check35.id,
|
|
8207
8248
|
severity: "info",
|
|
8208
8249
|
message: `Low text density on pages: ${lowPages.join(", ")} (below ${range.min} chars)`,
|
|
8209
8250
|
file: ctx.pdfPath
|
|
@@ -8211,7 +8252,7 @@ var check34 = {
|
|
|
8211
8252
|
}
|
|
8212
8253
|
if (highPages.length > 0) {
|
|
8213
8254
|
results.push({
|
|
8214
|
-
checkId:
|
|
8255
|
+
checkId: check35.id,
|
|
8215
8256
|
severity: "info",
|
|
8216
8257
|
message: `High text density on pages: ${highPages.join(", ")} (above ${range.max} chars)`,
|
|
8217
8258
|
file: ctx.pdfPath
|
|
@@ -8220,11 +8261,11 @@ var check34 = {
|
|
|
8220
8261
|
return results;
|
|
8221
8262
|
}
|
|
8222
8263
|
};
|
|
8223
|
-
registerCheck(
|
|
8264
|
+
registerCheck(check35);
|
|
8224
8265
|
|
|
8225
8266
|
// src/checks/heuristic/section-density.ts
|
|
8226
|
-
import { readFile as
|
|
8227
|
-
var
|
|
8267
|
+
import { readFile as readFile21 } from "node:fs/promises";
|
|
8268
|
+
var check36 = {
|
|
8228
8269
|
id: "heuristic.chunking.section-density",
|
|
8229
8270
|
name: "Section Density",
|
|
8230
8271
|
description: "Checks heading/paragraph/callout density from source Markdown",
|
|
@@ -8240,7 +8281,7 @@ var check35 = {
|
|
|
8240
8281
|
const results = [];
|
|
8241
8282
|
for (const file of files) {
|
|
8242
8283
|
try {
|
|
8243
|
-
const content = await
|
|
8284
|
+
const content = await readFile21(file, "utf8");
|
|
8244
8285
|
const lines = content.split(`
|
|
8245
8286
|
`);
|
|
8246
8287
|
let currentSectionStart = 0;
|
|
@@ -8250,7 +8291,7 @@ var check35 = {
|
|
|
8250
8291
|
const line = lines[i];
|
|
8251
8292
|
if (/^#{1,6}\s/.test(line)) {
|
|
8252
8293
|
if (paragraphCount > maxParas) {
|
|
8253
|
-
results.push(finding(
|
|
8294
|
+
results.push(finding(check36.id, {
|
|
8254
8295
|
severity: "info",
|
|
8255
8296
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
8256
8297
|
file,
|
|
@@ -8270,7 +8311,7 @@ var check35 = {
|
|
|
8270
8311
|
}
|
|
8271
8312
|
}
|
|
8272
8313
|
if (paragraphCount > maxParas) {
|
|
8273
|
-
results.push(finding(
|
|
8314
|
+
results.push(finding(check36.id, {
|
|
8274
8315
|
severity: "info",
|
|
8275
8316
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
8276
8317
|
file,
|
|
@@ -8278,7 +8319,7 @@ var check35 = {
|
|
|
8278
8319
|
}));
|
|
8279
8320
|
}
|
|
8280
8321
|
} catch {
|
|
8281
|
-
results.push(inspectionFailed(
|
|
8322
|
+
results.push(inspectionFailed(check36.id, `Could not read source file: ${file}`, {
|
|
8282
8323
|
file
|
|
8283
8324
|
}));
|
|
8284
8325
|
}
|
|
@@ -8286,10 +8327,10 @@ var check35 = {
|
|
|
8286
8327
|
return results;
|
|
8287
8328
|
}
|
|
8288
8329
|
};
|
|
8289
|
-
registerCheck(
|
|
8330
|
+
registerCheck(check36);
|
|
8290
8331
|
|
|
8291
8332
|
// src/checks/heuristic/layer-count.ts
|
|
8292
|
-
var
|
|
8333
|
+
var check37 = {
|
|
8293
8334
|
id: "heuristic.decoration.layer-count",
|
|
8294
8335
|
name: "Layer Count",
|
|
8295
8336
|
description: "Counts image objects per page",
|
|
@@ -8314,7 +8355,7 @@ var check36 = {
|
|
|
8314
8355
|
heavyPages.sort((a, b) => a - b);
|
|
8315
8356
|
return [
|
|
8316
8357
|
{
|
|
8317
|
-
checkId:
|
|
8358
|
+
checkId: check37.id,
|
|
8318
8359
|
severity: "info",
|
|
8319
8360
|
message: `Pages with many image layers (>${maxLayers}): ${heavyPages.join(", ")}`,
|
|
8320
8361
|
file: ctx.pdfPath
|
|
@@ -8324,10 +8365,10 @@ var check36 = {
|
|
|
8324
8365
|
return [];
|
|
8325
8366
|
}
|
|
8326
8367
|
};
|
|
8327
|
-
registerCheck(
|
|
8368
|
+
registerCheck(check37);
|
|
8328
8369
|
|
|
8329
8370
|
// src/checks/heuristic/placement-variance.ts
|
|
8330
|
-
var
|
|
8371
|
+
var check38 = {
|
|
8331
8372
|
id: "heuristic.layout.placement-variance",
|
|
8332
8373
|
name: "Placement Variance",
|
|
8333
8374
|
description: "Analyzes text baseline coordinates for layout consistency",
|
|
@@ -8345,7 +8386,7 @@ var check37 = {
|
|
|
8345
8386
|
const uniqueX = new Set(positions.map((p) => Math.round(p.x)));
|
|
8346
8387
|
return [
|
|
8347
8388
|
{
|
|
8348
|
-
checkId:
|
|
8389
|
+
checkId: check38.id,
|
|
8349
8390
|
severity: "info",
|
|
8350
8391
|
message: `Layout analysis: ${uniqueX.size} unique horizontal text positions across ${positions.length} text blocks.`,
|
|
8351
8392
|
file: ctx.pdfPath
|
|
@@ -8353,21 +8394,21 @@ var check37 = {
|
|
|
8353
8394
|
];
|
|
8354
8395
|
}
|
|
8355
8396
|
};
|
|
8356
|
-
registerCheck(
|
|
8397
|
+
registerCheck(check38);
|
|
8357
8398
|
|
|
8358
8399
|
// src/checks/tool-check.ts
|
|
8359
8400
|
async function checkToolAvailability(config, opts = {}) {
|
|
8360
8401
|
const { checks: checks2 } = selectChecks(opts, config);
|
|
8361
8402
|
const toolToChecks = new Map;
|
|
8362
|
-
for (const
|
|
8363
|
-
if (!
|
|
8403
|
+
for (const check39 of checks2) {
|
|
8404
|
+
if (!check39.requiredTools?.length)
|
|
8364
8405
|
continue;
|
|
8365
|
-
for (const tool of
|
|
8406
|
+
for (const tool of check39.requiredTools) {
|
|
8366
8407
|
const existing = toolToChecks.get(tool);
|
|
8367
8408
|
if (existing) {
|
|
8368
|
-
existing.push(
|
|
8409
|
+
existing.push(check39.id);
|
|
8369
8410
|
} else {
|
|
8370
|
-
toolToChecks.set(tool, [
|
|
8411
|
+
toolToChecks.set(tool, [check39.id]);
|
|
8371
8412
|
}
|
|
8372
8413
|
}
|
|
8373
8414
|
}
|
|
@@ -8383,11 +8424,11 @@ async function checkToolAvailability(config, opts = {}) {
|
|
|
8383
8424
|
const missing = results.filter((r) => !r.found).map((r) => r.tool);
|
|
8384
8425
|
const missingSet = new Set(missing);
|
|
8385
8426
|
const skippedChecks = [];
|
|
8386
|
-
for (const
|
|
8387
|
-
if (!
|
|
8427
|
+
for (const check39 of checks2) {
|
|
8428
|
+
if (!check39.requiredTools?.length)
|
|
8388
8429
|
continue;
|
|
8389
|
-
if (
|
|
8390
|
-
skippedChecks.push(
|
|
8430
|
+
if (check39.requiredTools.some((t) => missingSet.has(t))) {
|
|
8431
|
+
skippedChecks.push(check39.id);
|
|
8391
8432
|
}
|
|
8392
8433
|
}
|
|
8393
8434
|
return { available, missing, skippedChecks, toolToChecks };
|
|
@@ -8426,7 +8467,7 @@ function artifactName(title, format) {
|
|
|
8426
8467
|
var BOOK_HTML_FILENAME = "book.html";
|
|
8427
8468
|
|
|
8428
8469
|
// src/lib/markdown/index.ts
|
|
8429
|
-
import { readdir as readdir3, readFile as
|
|
8470
|
+
import { readdir as readdir3, readFile as readFile22, writeFile as writeFile2, mkdir as mkdir4 } from "node:fs/promises";
|
|
8430
8471
|
|
|
8431
8472
|
// src/lib/remote-auth/sync-messages.ts
|
|
8432
8473
|
function isOnlineSibling(filepath) {
|
|
@@ -8566,7 +8607,7 @@ ${css.trim()}
|
|
|
8566
8607
|
}
|
|
8567
8608
|
return assembleBookHtml({
|
|
8568
8609
|
files,
|
|
8569
|
-
readText: (relPath) =>
|
|
8610
|
+
readText: (relPath) => readFile22(join2(inputDir, canonicalChapterId(relPath)), "utf-8"),
|
|
8570
8611
|
projectCss: inlined.css,
|
|
8571
8612
|
preloadImages: cssAssetCopies.map((c) => c.to),
|
|
8572
8613
|
title: opts.title,
|
|
@@ -8598,7 +8639,7 @@ async function renderChaptersToFile(inputDir, outDir, opts = {}) {
|
|
|
8598
8639
|
}
|
|
8599
8640
|
|
|
8600
8641
|
// src/lib/markdownlint-fix.ts
|
|
8601
|
-
import { readFile as
|
|
8642
|
+
import { readFile as readFile23, writeFile as writeFile3 } from "node:fs/promises";
|
|
8602
8643
|
import { applyFixes } from "markdownlint";
|
|
8603
8644
|
import { lint as lint2 } from "markdownlint/sync";
|
|
8604
8645
|
async function uncommittedAmong(inputDir, files) {
|
|
@@ -8638,7 +8679,7 @@ async function applyMarkdownlintFixes(ctx) {
|
|
|
8638
8679
|
for (const [file, violations] of Object.entries(results)) {
|
|
8639
8680
|
if (!Array.isArray(violations) || violations.length === 0)
|
|
8640
8681
|
continue;
|
|
8641
|
-
const before = await
|
|
8682
|
+
const before = await readFile23(file, "utf8");
|
|
8642
8683
|
const after = applyFixes(before, violations);
|
|
8643
8684
|
if (after !== before)
|
|
8644
8685
|
pending.push({ file, content: after });
|
|
@@ -8747,16 +8788,16 @@ async function runChecks(ctx, opts = {}) {
|
|
|
8747
8788
|
}
|
|
8748
8789
|
const release = retainPdfCache();
|
|
8749
8790
|
try {
|
|
8750
|
-
for (const
|
|
8791
|
+
for (const check39 of checks2) {
|
|
8751
8792
|
try {
|
|
8752
|
-
let results = await
|
|
8753
|
-
const severityOverride = getCheckSeverityOverride(
|
|
8793
|
+
let results = await check39.run(ctx);
|
|
8794
|
+
const severityOverride = getCheckSeverityOverride(check39.id, ctx.config);
|
|
8754
8795
|
if (severityOverride) {
|
|
8755
8796
|
for (const r of results) {
|
|
8756
8797
|
r.severity = severityOverride;
|
|
8757
8798
|
}
|
|
8758
8799
|
}
|
|
8759
|
-
const lintLevel = ruleLevel(ctx.config.lint.rules,
|
|
8800
|
+
const lintLevel = ruleLevel(ctx.config.lint.rules, check39.id);
|
|
8760
8801
|
if (lintLevel === "off")
|
|
8761
8802
|
results = [];
|
|
8762
8803
|
else if (lintLevel) {
|
|
@@ -8764,15 +8805,15 @@ async function runChecks(ctx, opts = {}) {
|
|
|
8764
8805
|
r.severity = lintLevel === "warn" ? "warning" : "error";
|
|
8765
8806
|
}
|
|
8766
8807
|
if (results.length === 0) {
|
|
8767
|
-
passed.push(
|
|
8808
|
+
passed.push(check39.id);
|
|
8768
8809
|
} else {
|
|
8769
8810
|
allResults.push(...results);
|
|
8770
8811
|
}
|
|
8771
8812
|
} catch (err) {
|
|
8772
8813
|
allResults.push({
|
|
8773
|
-
checkId:
|
|
8814
|
+
checkId: check39.id,
|
|
8774
8815
|
severity: "error",
|
|
8775
|
-
message: `Check "${
|
|
8816
|
+
message: `Check "${check39.id}" threw: ${err instanceof Error ? err.message : String(err)}`
|
|
8776
8817
|
});
|
|
8777
8818
|
}
|
|
8778
8819
|
}
|
|
@@ -9064,7 +9105,7 @@ async function executeValidation(args) {
|
|
|
9064
9105
|
};
|
|
9065
9106
|
if (args.fix) {
|
|
9066
9107
|
const selected = selectChecks(runnerOptions, config).checks;
|
|
9067
|
-
if (selected.some((
|
|
9108
|
+
if (selected.some((check39) => check39.id === MARKDOWNLINT_CHECK_ID)) {
|
|
9068
9109
|
await applyMarkdownlintFixes(context);
|
|
9069
9110
|
} else {
|
|
9070
9111
|
log.warn(`--fix: ${MARKDOWNLINT_CHECK_ID} is not part of this run — no files were fixed.`);
|
|
@@ -9151,7 +9192,7 @@ import { randomBytes } from "node:crypto";
|
|
|
9151
9192
|
|
|
9152
9193
|
// src/lib/build-fingerprint.ts
|
|
9153
9194
|
import * as fs from "node:fs";
|
|
9154
|
-
import { mkdir as mkdir5, readFile as
|
|
9195
|
+
import { mkdir as mkdir5, readFile as readFile24, writeFile as writeFile4 } from "node:fs/promises";
|
|
9155
9196
|
import { createHash as createHash4 } from "node:crypto";
|
|
9156
9197
|
import path12 from "node:path";
|
|
9157
9198
|
import git from "isomorphic-git";
|
|
@@ -9238,8 +9279,8 @@ async function getEngineBundleHash() {
|
|
|
9238
9279
|
getAssetPath("engine/gutterpress-agent.js")
|
|
9239
9280
|
]);
|
|
9240
9281
|
const [viewerBytes, agentBytes] = await Promise.all([
|
|
9241
|
-
|
|
9242
|
-
|
|
9282
|
+
readFile24(viewerPath),
|
|
9283
|
+
readFile24(agentPath)
|
|
9243
9284
|
]);
|
|
9244
9285
|
const hash = createHash4("sha256");
|
|
9245
9286
|
hash.update(viewerBytes);
|
|
@@ -9314,417 +9355,6 @@ async function writeBuildFingerprint(input) {
|
|
|
9314
9355
|
return outPath;
|
|
9315
9356
|
}
|
|
9316
9357
|
|
|
9317
|
-
// src/lib/black-text-gray.ts
|
|
9318
|
-
var MAX_CHANNEL = 0.12;
|
|
9319
|
-
var MAX_SPREAD = 0.04;
|
|
9320
|
-
function isNearBlack([r, g, b]) {
|
|
9321
|
-
const max = Math.max(r, g, b);
|
|
9322
|
-
return max <= MAX_CHANNEL && max - Math.min(r, g, b) <= MAX_SPREAD;
|
|
9323
|
-
}
|
|
9324
|
-
var WS = new Set([0, 9, 10, 12, 13, 32]);
|
|
9325
|
-
var DELIM = new Set("()<>[]{}/%".split("").map((c) => c.charCodeAt(0)));
|
|
9326
|
-
function* tokenize(s) {
|
|
9327
|
-
let i = 0;
|
|
9328
|
-
const n = s.length;
|
|
9329
|
-
while (i < n) {
|
|
9330
|
-
const c = s.charCodeAt(i);
|
|
9331
|
-
if (WS.has(c)) {
|
|
9332
|
-
i++;
|
|
9333
|
-
continue;
|
|
9334
|
-
}
|
|
9335
|
-
const start = i;
|
|
9336
|
-
if (c === 37) {
|
|
9337
|
-
while (i < n && s[i] !== `
|
|
9338
|
-
` && s[i] !== "\r")
|
|
9339
|
-
i++;
|
|
9340
|
-
continue;
|
|
9341
|
-
}
|
|
9342
|
-
if (c === 40) {
|
|
9343
|
-
let depth = 0;
|
|
9344
|
-
while (i < n) {
|
|
9345
|
-
const d = s[i++];
|
|
9346
|
-
if (d === "\\")
|
|
9347
|
-
i++;
|
|
9348
|
-
else if (d === "(")
|
|
9349
|
-
depth++;
|
|
9350
|
-
else if (d === ")" && --depth === 0)
|
|
9351
|
-
break;
|
|
9352
|
-
}
|
|
9353
|
-
yield { text: s.slice(start, i), start, end: i, op: false };
|
|
9354
|
-
continue;
|
|
9355
|
-
}
|
|
9356
|
-
if (c === 60 && s[i + 1] === "<") {
|
|
9357
|
-
i += 2;
|
|
9358
|
-
yield { text: "<<", start, end: i, op: false };
|
|
9359
|
-
continue;
|
|
9360
|
-
}
|
|
9361
|
-
if (c === 62 && s[i + 1] === ">") {
|
|
9362
|
-
i += 2;
|
|
9363
|
-
yield { text: ">>", start, end: i, op: false };
|
|
9364
|
-
continue;
|
|
9365
|
-
}
|
|
9366
|
-
if (c === 60) {
|
|
9367
|
-
while (i < n && s[i] !== ">")
|
|
9368
|
-
i++;
|
|
9369
|
-
i++;
|
|
9370
|
-
yield { text: s.slice(start, i), start, end: i, op: false };
|
|
9371
|
-
continue;
|
|
9372
|
-
}
|
|
9373
|
-
if (c === 91 || c === 93 || c === 62 || c === 123 || c === 125 || c === 41) {
|
|
9374
|
-
i++;
|
|
9375
|
-
yield { text: s[start], start, end: i, op: false };
|
|
9376
|
-
continue;
|
|
9377
|
-
}
|
|
9378
|
-
i++;
|
|
9379
|
-
while (i < n && !WS.has(s.charCodeAt(i)) && !DELIM.has(s.charCodeAt(i)))
|
|
9380
|
-
i++;
|
|
9381
|
-
const text = s.slice(start, i);
|
|
9382
|
-
const isOperand = c === 47 || /^[+-]?(\d+\.?\d*|\.\d+)$/.test(text);
|
|
9383
|
-
yield { text, start, end: i, op: !isOperand };
|
|
9384
|
-
if (text === "ID") {
|
|
9385
|
-
const m = /\sEI(?=\s|$)/g;
|
|
9386
|
-
m.lastIndex = i;
|
|
9387
|
-
const hit = m.exec(s);
|
|
9388
|
-
i = hit ? hit.index + hit[0].length : n;
|
|
9389
|
-
}
|
|
9390
|
-
}
|
|
9391
|
-
}
|
|
9392
|
-
var LARGE_TEXT_PT = 24;
|
|
9393
|
-
var SIZE_EPS = 0.05;
|
|
9394
|
-
var area = (m) => Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2]));
|
|
9395
|
-
|
|
9396
|
-
class TextScale {
|
|
9397
|
-
ctm;
|
|
9398
|
-
font = 0;
|
|
9399
|
-
tm = 1;
|
|
9400
|
-
stack = [];
|
|
9401
|
-
constructor(ctm) {
|
|
9402
|
-
this.ctm = ctm;
|
|
9403
|
-
}
|
|
9404
|
-
get unit() {
|
|
9405
|
-
return this.ctm;
|
|
9406
|
-
}
|
|
9407
|
-
get size() {
|
|
9408
|
-
return this.font * this.tm * this.ctm;
|
|
9409
|
-
}
|
|
9410
|
-
op(op, nums) {
|
|
9411
|
-
switch (op) {
|
|
9412
|
-
case "q":
|
|
9413
|
-
this.stack.push([this.ctm, this.font]);
|
|
9414
|
-
return false;
|
|
9415
|
-
case "Q": {
|
|
9416
|
-
const p = this.stack.pop();
|
|
9417
|
-
if (p)
|
|
9418
|
-
[this.ctm, this.font] = p;
|
|
9419
|
-
return true;
|
|
9420
|
-
}
|
|
9421
|
-
case "cm":
|
|
9422
|
-
if (nums.length === 6)
|
|
9423
|
-
this.ctm *= area(nums);
|
|
9424
|
-
return true;
|
|
9425
|
-
case "BT":
|
|
9426
|
-
this.tm = 1;
|
|
9427
|
-
return true;
|
|
9428
|
-
case "Tm":
|
|
9429
|
-
if (nums.length === 6)
|
|
9430
|
-
this.tm = area(nums);
|
|
9431
|
-
return true;
|
|
9432
|
-
case "Tf":
|
|
9433
|
-
this.font = Math.abs(nums[nums.length - 1] ?? 0) || 0;
|
|
9434
|
-
return true;
|
|
9435
|
-
}
|
|
9436
|
-
return false;
|
|
9437
|
-
}
|
|
9438
|
-
}
|
|
9439
|
-
function rewriteBlackText(src, ctx = {}) {
|
|
9440
|
-
let fill = null;
|
|
9441
|
-
let stroke = null;
|
|
9442
|
-
const stack = [];
|
|
9443
|
-
const scale = new TextScale(ctx.baseScale ?? 1);
|
|
9444
|
-
let inText = false;
|
|
9445
|
-
let fillDirty = false;
|
|
9446
|
-
let strokeDirty = false;
|
|
9447
|
-
const spans = [];
|
|
9448
|
-
let lastOp = "";
|
|
9449
|
-
let rect = null;
|
|
9450
|
-
const edits = [];
|
|
9451
|
-
let operands = [];
|
|
9452
|
-
const rgbOf = () => {
|
|
9453
|
-
if (operands.length !== 3)
|
|
9454
|
-
return null;
|
|
9455
|
-
const v = operands.map((t) => Number(t.text));
|
|
9456
|
-
return v.some((x) => Number.isNaN(x)) ? null : { rgb: v, raw: operands.map((t) => t.text).join(" ") };
|
|
9457
|
-
};
|
|
9458
|
-
const isSmall = () => scale.size <= LARGE_TEXT_PT + SIZE_EPS;
|
|
9459
|
-
let fillIns = null;
|
|
9460
|
-
let strokeIns = null;
|
|
9461
|
-
const syncOne = (colour, dirty, ins, at, gray, op) => {
|
|
9462
|
-
if (!colour || !isNearBlack(colour.rgb))
|
|
9463
|
-
return [dirty, ins];
|
|
9464
|
-
const small = isSmall();
|
|
9465
|
-
if (small && !dirty) {
|
|
9466
|
-
const e = { start: at, end: at, text: ` ${gray}` };
|
|
9467
|
-
edits.push(e);
|
|
9468
|
-
return [true, e];
|
|
9469
|
-
}
|
|
9470
|
-
if (!small && dirty) {
|
|
9471
|
-
if (ins)
|
|
9472
|
-
ins.text = "";
|
|
9473
|
-
else
|
|
9474
|
-
edits.push({ start: at, end: at, text: ` ${colour.raw} ${op}` });
|
|
9475
|
-
return [false, null];
|
|
9476
|
-
}
|
|
9477
|
-
return [dirty, ins];
|
|
9478
|
-
};
|
|
9479
|
-
const sync = (at) => {
|
|
9480
|
-
[fillDirty, fillIns] = syncOne(fill, fillDirty, fillIns, at, "0 g", "rg");
|
|
9481
|
-
[strokeDirty, strokeIns] = syncOne(stroke, strokeDirty, strokeIns, at, "0 G", "RG");
|
|
9482
|
-
};
|
|
9483
|
-
const flush = (s) => {
|
|
9484
|
-
for (const r of s.rects) {
|
|
9485
|
-
if (r.thickness > 0.2 * s.maxSmall)
|
|
9486
|
-
continue;
|
|
9487
|
-
edits.push({ start: r.start, end: r.start, text: "q 0 g " }, { start: r.end, end: r.end, text: " Q" });
|
|
9488
|
-
}
|
|
9489
|
-
};
|
|
9490
|
-
for (const t of tokenize(src)) {
|
|
9491
|
-
if (!t.op) {
|
|
9492
|
-
operands.push(t);
|
|
9493
|
-
continue;
|
|
9494
|
-
}
|
|
9495
|
-
const op = t.text;
|
|
9496
|
-
const nums = operands.map((o) => Number(o.text));
|
|
9497
|
-
const sizeMayHaveChanged = scale.op(op, nums);
|
|
9498
|
-
switch (op) {
|
|
9499
|
-
case "q":
|
|
9500
|
-
stack.push([fill, stroke]);
|
|
9501
|
-
break;
|
|
9502
|
-
case "Q": {
|
|
9503
|
-
const p = stack.pop();
|
|
9504
|
-
if (p)
|
|
9505
|
-
[fill, stroke] = p;
|
|
9506
|
-
break;
|
|
9507
|
-
}
|
|
9508
|
-
case "BT":
|
|
9509
|
-
inText = true;
|
|
9510
|
-
fillDirty = strokeDirty = false;
|
|
9511
|
-
fillIns = strokeIns = null;
|
|
9512
|
-
sync(t.end);
|
|
9513
|
-
break;
|
|
9514
|
-
case "Tf":
|
|
9515
|
-
case "Tm":
|
|
9516
|
-
if (inText && sizeMayHaveChanged)
|
|
9517
|
-
sync(t.end);
|
|
9518
|
-
break;
|
|
9519
|
-
case "ET":
|
|
9520
|
-
if (inText) {
|
|
9521
|
-
let ins = "";
|
|
9522
|
-
if (fillDirty && fill)
|
|
9523
|
-
ins += `${fill.raw} rg `;
|
|
9524
|
-
if (strokeDirty && stroke)
|
|
9525
|
-
ins += `${stroke.raw} RG `;
|
|
9526
|
-
if (ins)
|
|
9527
|
-
edits.push({ start: t.start, end: t.start, text: ins });
|
|
9528
|
-
}
|
|
9529
|
-
inText = false;
|
|
9530
|
-
fillIns = strokeIns = null;
|
|
9531
|
-
break;
|
|
9532
|
-
case "rg":
|
|
9533
|
-
case "RG": {
|
|
9534
|
-
const c = rgbOf();
|
|
9535
|
-
const isFill = op === "rg";
|
|
9536
|
-
if (isFill)
|
|
9537
|
-
fill = c;
|
|
9538
|
-
else
|
|
9539
|
-
stroke = c;
|
|
9540
|
-
if (inText && c && isNearBlack(c.rgb) && isSmall()) {
|
|
9541
|
-
edits.push({ start: operands[0].start, end: t.end, text: isFill ? "0 g" : "0 G" });
|
|
9542
|
-
if (isFill)
|
|
9543
|
-
fillDirty = true;
|
|
9544
|
-
else
|
|
9545
|
-
strokeDirty = true;
|
|
9546
|
-
} else if (inText) {
|
|
9547
|
-
if (isFill)
|
|
9548
|
-
fillDirty = false;
|
|
9549
|
-
else
|
|
9550
|
-
strokeDirty = false;
|
|
9551
|
-
}
|
|
9552
|
-
break;
|
|
9553
|
-
}
|
|
9554
|
-
case "g":
|
|
9555
|
-
case "k":
|
|
9556
|
-
case "sc":
|
|
9557
|
-
case "scn":
|
|
9558
|
-
case "cs":
|
|
9559
|
-
fill = null;
|
|
9560
|
-
if (inText)
|
|
9561
|
-
fillDirty = false;
|
|
9562
|
-
break;
|
|
9563
|
-
case "G":
|
|
9564
|
-
case "K":
|
|
9565
|
-
case "SC":
|
|
9566
|
-
case "SCN":
|
|
9567
|
-
case "CS":
|
|
9568
|
-
stroke = null;
|
|
9569
|
-
if (inText)
|
|
9570
|
-
strokeDirty = false;
|
|
9571
|
-
break;
|
|
9572
|
-
case "Do": {
|
|
9573
|
-
const name = operands[operands.length - 1]?.text;
|
|
9574
|
-
if (name?.startsWith("/"))
|
|
9575
|
-
ctx.onDo?.(name.slice(1), scale.unit);
|
|
9576
|
-
break;
|
|
9577
|
-
}
|
|
9578
|
-
case "BDC":
|
|
9579
|
-
case "BMC":
|
|
9580
|
-
spans.push({ hasSmall: false, maxSmall: 0, rects: [] });
|
|
9581
|
-
break;
|
|
9582
|
-
case "EMC": {
|
|
9583
|
-
const s = spans.pop();
|
|
9584
|
-
if (!s)
|
|
9585
|
-
break;
|
|
9586
|
-
const parent = spans[spans.length - 1];
|
|
9587
|
-
if (s.hasSmall)
|
|
9588
|
-
flush(s);
|
|
9589
|
-
else
|
|
9590
|
-
parent?.rects.push(...s.rects);
|
|
9591
|
-
if (parent && s.hasSmall) {
|
|
9592
|
-
parent.hasSmall = true;
|
|
9593
|
-
parent.maxSmall = Math.max(parent.maxSmall, s.maxSmall);
|
|
9594
|
-
}
|
|
9595
|
-
break;
|
|
9596
|
-
}
|
|
9597
|
-
case "Tj":
|
|
9598
|
-
case "TJ":
|
|
9599
|
-
case "'":
|
|
9600
|
-
case '"': {
|
|
9601
|
-
fillIns = strokeIns = null;
|
|
9602
|
-
const span = spans[spans.length - 1];
|
|
9603
|
-
if (span && inText && isSmall()) {
|
|
9604
|
-
span.hasSmall = true;
|
|
9605
|
-
span.maxSmall = Math.max(span.maxSmall, scale.size);
|
|
9606
|
-
}
|
|
9607
|
-
break;
|
|
9608
|
-
}
|
|
9609
|
-
case "re":
|
|
9610
|
-
rect = !inText && fill && isNearBlack(fill.rgb) && operands.length === 4 && nums.every((x) => !Number.isNaN(x)) ? { start: operands[0].start, thickness: Math.min(Math.abs(nums[2]), Math.abs(nums[3])) * scale.unit } : null;
|
|
9611
|
-
break;
|
|
9612
|
-
case "f":
|
|
9613
|
-
case "F":
|
|
9614
|
-
case "f*":
|
|
9615
|
-
if (rect && lastOp === "re")
|
|
9616
|
-
spans[spans.length - 1]?.rects.push({ ...rect, end: t.end });
|
|
9617
|
-
break;
|
|
9618
|
-
}
|
|
9619
|
-
lastOp = op;
|
|
9620
|
-
operands = [];
|
|
9621
|
-
}
|
|
9622
|
-
return applyEdits(src, edits);
|
|
9623
|
-
}
|
|
9624
|
-
function applyEdits(s, edits) {
|
|
9625
|
-
if (!edits.length)
|
|
9626
|
-
return s;
|
|
9627
|
-
edits.sort((a, b) => a.start - b.start);
|
|
9628
|
-
let out = "";
|
|
9629
|
-
let pos = 0;
|
|
9630
|
-
for (const e of edits) {
|
|
9631
|
-
out += s.slice(pos, e.start) + e.text;
|
|
9632
|
-
pos = e.end;
|
|
9633
|
-
}
|
|
9634
|
-
return out + s.slice(pos);
|
|
9635
|
-
}
|
|
9636
|
-
async function visitContent(doc, visit) {
|
|
9637
|
-
const { PDFName, PDFRawStream, PDFArray, PDFRef, PDFDict, decodePDFRawStream } = await import("pdf-lib");
|
|
9638
|
-
const ctx = doc.context;
|
|
9639
|
-
const SKIP = ["Filter", "DecodeParms", "Length"].map((k) => PDFName.of(k));
|
|
9640
|
-
let changed = false;
|
|
9641
|
-
const decode = (s) => {
|
|
9642
|
-
try {
|
|
9643
|
-
return Buffer.from(decodePDFRawStream(s).decode()).toString("latin1");
|
|
9644
|
-
} catch {
|
|
9645
|
-
return null;
|
|
9646
|
-
}
|
|
9647
|
-
};
|
|
9648
|
-
const replace = (ref, old, text) => {
|
|
9649
|
-
const entries = {};
|
|
9650
|
-
for (const [k, v] of old.dict.entries())
|
|
9651
|
-
if (!SKIP.includes(k))
|
|
9652
|
-
entries[k.decodeText()] = v;
|
|
9653
|
-
ctx.assign(ref, ctx.flateStream(Buffer.from(text, "latin1"), entries));
|
|
9654
|
-
changed = true;
|
|
9655
|
-
};
|
|
9656
|
-
const formScale = new Map;
|
|
9657
|
-
const formMatrixScale = (form) => {
|
|
9658
|
-
const m = form.dict.lookupMaybe(PDFName.of("Matrix"), PDFArray);
|
|
9659
|
-
const nums = m?.asArray().map((n) => Number(n.toString()));
|
|
9660
|
-
return nums?.length === 6 && !nums.some(Number.isNaN) ? area(nums) : 1;
|
|
9661
|
-
};
|
|
9662
|
-
const pages = doc.getPages();
|
|
9663
|
-
for (let p = 0;p < pages.length; p++) {
|
|
9664
|
-
const page = pages[p];
|
|
9665
|
-
const contents = page.node.Contents();
|
|
9666
|
-
const refs = [];
|
|
9667
|
-
if (contents instanceof PDFArray) {
|
|
9668
|
-
for (let i = 0;i < contents.size(); i++) {
|
|
9669
|
-
const r = contents.get(i);
|
|
9670
|
-
if (r instanceof PDFRef)
|
|
9671
|
-
refs.push(r);
|
|
9672
|
-
}
|
|
9673
|
-
} else if (contents instanceof PDFRawStream) {
|
|
9674
|
-
const r = page.node.get(PDFName.of("Contents"));
|
|
9675
|
-
if (r instanceof PDFRef)
|
|
9676
|
-
refs.push(r);
|
|
9677
|
-
}
|
|
9678
|
-
const streams = refs.map((r) => ctx.lookup(r));
|
|
9679
|
-
if (!refs.length || !streams.every((s) => s instanceof PDFRawStream))
|
|
9680
|
-
continue;
|
|
9681
|
-
const parts = streams.map((s) => decode(s));
|
|
9682
|
-
if (parts.some((q) => q === null))
|
|
9683
|
-
continue;
|
|
9684
|
-
const joined = parts.join(`
|
|
9685
|
-
`);
|
|
9686
|
-
const xobjects = page.node.Resources()?.lookupMaybe(PDFName.of("XObject"), PDFDict);
|
|
9687
|
-
const next = visit(joined, {
|
|
9688
|
-
baseScale: 1,
|
|
9689
|
-
onDo: (name, scale) => {
|
|
9690
|
-
const ref = xobjects?.get(PDFName.of(name));
|
|
9691
|
-
const form = ref instanceof PDFRef ? ctx.lookup(ref) : undefined;
|
|
9692
|
-
if (!(ref instanceof PDFRef) || !(form instanceof PDFRawStream))
|
|
9693
|
-
return;
|
|
9694
|
-
if (form.dict.get(PDFName.of("Subtype")) !== PDFName.of("Form"))
|
|
9695
|
-
return;
|
|
9696
|
-
const at = scale * formMatrixScale(form);
|
|
9697
|
-
formScale.set(ref.tag, Math.min(formScale.get(ref.tag) ?? Infinity, at));
|
|
9698
|
-
}
|
|
9699
|
-
});
|
|
9700
|
-
if (next === joined)
|
|
9701
|
-
continue;
|
|
9702
|
-
replace(refs[0], streams[0], next);
|
|
9703
|
-
page.node.set(PDFName.of("Contents"), refs[0]);
|
|
9704
|
-
}
|
|
9705
|
-
for (const [ref, obj] of ctx.enumerateIndirectObjects()) {
|
|
9706
|
-
if (!(obj instanceof PDFRawStream))
|
|
9707
|
-
continue;
|
|
9708
|
-
if (obj.dict.get(PDFName.of("Subtype")) !== PDFName.of("Form"))
|
|
9709
|
-
continue;
|
|
9710
|
-
const text = decode(obj);
|
|
9711
|
-
if (text === null)
|
|
9712
|
-
continue;
|
|
9713
|
-
const next = visit(text, {
|
|
9714
|
-
baseScale: formScale.get(ref.tag) ?? 0
|
|
9715
|
-
});
|
|
9716
|
-
if (next !== text)
|
|
9717
|
-
replace(ref, obj, next);
|
|
9718
|
-
}
|
|
9719
|
-
return changed;
|
|
9720
|
-
}
|
|
9721
|
-
async function blackTextToGray(pdf) {
|
|
9722
|
-
const { PDFDocument } = await import("pdf-lib");
|
|
9723
|
-
const doc = await PDFDocument.load(pdf, { updateMetadata: false });
|
|
9724
|
-
const changed = await visitContent(doc, (src, info2) => rewriteBlackText(src, info2));
|
|
9725
|
-
return changed ? doc.save({ updateFieldAppearances: false }) : pdf;
|
|
9726
|
-
}
|
|
9727
|
-
|
|
9728
9358
|
// src/lib/build-preflight.ts
|
|
9729
9359
|
var INSTALL_HINTS2 = {
|
|
9730
9360
|
gs: INSTALL_HINTS.gs.body,
|
|
@@ -10020,7 +9650,7 @@ class PdfOutput {
|
|
|
10020
9650
|
const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
|
|
10021
9651
|
await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
|
|
10022
9652
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
10023
|
-
const { buildNativePdf } = await import("./engine-
|
|
9653
|
+
const { buildNativePdf } = await import("./engine-63r7pxsy.js");
|
|
10024
9654
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
10025
9655
|
title: config.title,
|
|
10026
9656
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
@@ -10155,7 +9785,7 @@ function openPath(filePath) {
|
|
|
10155
9785
|
import path16 from "path";
|
|
10156
9786
|
import { tmpdir } from "os";
|
|
10157
9787
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
10158
|
-
import { readdir as readdir4, readFile as
|
|
9788
|
+
import { readdir as readdir4, readFile as readFile25, writeFile as writeFile5 } from "node:fs/promises";
|
|
10159
9789
|
|
|
10160
9790
|
// src/utils/file-utils.ts
|
|
10161
9791
|
import { promises as fs3 } from "fs";
|
|
@@ -10704,7 +10334,7 @@ async function cleanupOrphanTempDirs() {
|
|
|
10704
10334
|
const dir = path16.join(TEMP_DIR_BASE, entry);
|
|
10705
10335
|
const pidFile = path16.join(dir, PID_FILE_NAME);
|
|
10706
10336
|
try {
|
|
10707
|
-
const raw = await
|
|
10337
|
+
const raw = await readFile25(pidFile, "utf8");
|
|
10708
10338
|
const pid = parseInt(raw.trim(), 10);
|
|
10709
10339
|
if (!isProcessAlive(pid)) {
|
|
10710
10340
|
debug(`Removing orphan temp dir ${dir} (pid ${pid} not alive)`);
|
|
@@ -10811,7 +10441,7 @@ function createServerState(inputPath, tempDir, config, options) {
|
|
|
10811
10441
|
import http from "node:http";
|
|
10812
10442
|
import net from "node:net";
|
|
10813
10443
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
|
|
10814
|
-
import { readFile as
|
|
10444
|
+
import { readFile as readFile26, stat as stat5 } from "node:fs/promises";
|
|
10815
10445
|
import path17 from "path";
|
|
10816
10446
|
import { WebSocket, WebSocketServer } from "ws";
|
|
10817
10447
|
var HMR_PATH = "/__gutterpress-hmr";
|
|
@@ -10880,7 +10510,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
10880
10510
|
}
|
|
10881
10511
|
let data;
|
|
10882
10512
|
try {
|
|
10883
|
-
data = await
|
|
10513
|
+
data = await readFile26(filePath);
|
|
10884
10514
|
} catch {
|
|
10885
10515
|
res.writeHead(404);
|
|
10886
10516
|
res.end("Not Found");
|
|
@@ -10929,7 +10559,7 @@ async function serveRevalidatedStatic(req, absPath, res, validators) {
|
|
|
10929
10559
|
let data;
|
|
10930
10560
|
if (!validator || validator.stamp !== stamp) {
|
|
10931
10561
|
try {
|
|
10932
|
-
data = await
|
|
10562
|
+
data = await readFile26(absPath);
|
|
10933
10563
|
} catch {
|
|
10934
10564
|
await serveStatic(absPath, res);
|
|
10935
10565
|
return;
|
|
@@ -11239,7 +10869,7 @@ async function startPreviewServer(options) {
|
|
|
11239
10869
|
}
|
|
11240
10870
|
|
|
11241
10871
|
// src/lib/project-scaffold.ts
|
|
11242
|
-
import { access, copyFile, cp as cp2, mkdir as mkdir7, readdir as readdir5, readFile as
|
|
10872
|
+
import { access, copyFile, cp as cp2, mkdir as mkdir7, readdir as readdir5, readFile as readFile27, stat as stat6, writeFile as writeFile6 } from "node:fs/promises";
|
|
11243
10873
|
import { constants as FS, existsSync as existsSync12 } from "node:fs";
|
|
11244
10874
|
import path18 from "node:path";
|
|
11245
10875
|
|
|
@@ -11438,7 +11068,7 @@ async function scaffoldProject(options) {
|
|
|
11438
11068
|
async function firstSourceFile(manifestPath) {
|
|
11439
11069
|
let text;
|
|
11440
11070
|
try {
|
|
11441
|
-
text = await
|
|
11071
|
+
text = await readFile27(manifestPath, "utf8");
|
|
11442
11072
|
} catch {
|
|
11443
11073
|
return;
|
|
11444
11074
|
}
|
|
@@ -11450,7 +11080,7 @@ async function firstSourceFile(manifestPath) {
|
|
|
11450
11080
|
return m ? m[1].trim() : undefined;
|
|
11451
11081
|
}
|
|
11452
11082
|
async function fillTemplateFile(filePath, substitutions) {
|
|
11453
|
-
let text = await
|
|
11083
|
+
let text = await readFile27(filePath, "utf8");
|
|
11454
11084
|
for (const [token, value] of Object.entries(substitutions)) {
|
|
11455
11085
|
text = text.split(token).join(value);
|
|
11456
11086
|
}
|
|
@@ -11458,7 +11088,7 @@ async function fillTemplateFile(filePath, substitutions) {
|
|
|
11458
11088
|
}
|
|
11459
11089
|
|
|
11460
11090
|
// src/lib/extension-scaffold.ts
|
|
11461
|
-
import { access as access2, mkdir as mkdir8, readFile as
|
|
11091
|
+
import { access as access2, mkdir as mkdir8, readFile as readFile28, writeFile as writeFile7 } from "node:fs/promises";
|
|
11462
11092
|
import { constants as FS2 } from "node:fs";
|
|
11463
11093
|
import path19 from "node:path";
|
|
11464
11094
|
var EXTENSION_KINDS = ["plugin", "theme"];
|
|
@@ -11575,7 +11205,7 @@ async function scaffoldExtension(options) {
|
|
|
11575
11205
|
const src = await getAssetPath(`extension-templates/${kind}/${assetName}`);
|
|
11576
11206
|
const dest = path19.join(extensionDir, relDest);
|
|
11577
11207
|
await mkdir8(path19.dirname(dest), { recursive: true });
|
|
11578
|
-
const text = await
|
|
11208
|
+
const text = await readFile28(src, "utf8");
|
|
11579
11209
|
const table = relDest.endsWith(".json") ? jsonSafe : plain;
|
|
11580
11210
|
await writeFile7(dest, substitute(text, table), "utf8");
|
|
11581
11211
|
written.push(relDest);
|
|
@@ -11620,7 +11250,7 @@ var SKIP_ENTRIES = new Set([
|
|
|
11620
11250
|
|
|
11621
11251
|
// src/lib/extension-import.ts
|
|
11622
11252
|
import { existsSync as existsSync13 } from "node:fs";
|
|
11623
|
-
import { cp as cp3, mkdir as mkdir9, mkdtemp as mkdtemp2, readFile as
|
|
11253
|
+
import { cp as cp3, mkdir as mkdir9, mkdtemp as mkdtemp2, readFile as readFile29, rm as rm4, writeFile as writeFile8 } from "node:fs/promises";
|
|
11624
11254
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
11625
11255
|
import path20 from "node:path";
|
|
11626
11256
|
import { unzipSync } from "fflate";
|
|
@@ -11693,7 +11323,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11693
11323
|
if (!existsSync13(cssPath)) {
|
|
11694
11324
|
throw new Error("A theme must contain a theme.css file.");
|
|
11695
11325
|
}
|
|
11696
|
-
const css = await
|
|
11326
|
+
const css = await readFile29(cssPath, "utf8");
|
|
11697
11327
|
const { reject, warnings: printFindings } = classifyThemeCssFindings(checkCss(css, "theme.css"));
|
|
11698
11328
|
if (reject) {
|
|
11699
11329
|
throw new Error(`theme.css could not be parsed — ${reject.message}`);
|
|
@@ -11721,7 +11351,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11721
11351
|
for (let i = 0;i < extraSheets.length; i++) {
|
|
11722
11352
|
const rel = extraSheets[i];
|
|
11723
11353
|
const sheetPath = extraSheetPaths[i];
|
|
11724
|
-
const sheetCss = await
|
|
11354
|
+
const sheetCss = await readFile29(sheetPath, "utf8");
|
|
11725
11355
|
const { reject: sheetReject, warnings: sheetFindings } = classifyThemeCssFindings(checkCss(sheetCss, rel));
|
|
11726
11356
|
if (sheetReject) {
|
|
11727
11357
|
throw new Error(`${rel} could not be parsed — ${sheetReject.message}`);
|
|
@@ -11754,7 +11384,7 @@ async function completeLandedManifest(dest, id, meta, metaExists) {
|
|
|
11754
11384
|
let pkg = {};
|
|
11755
11385
|
if (metaExists) {
|
|
11756
11386
|
try {
|
|
11757
|
-
const parsed = JSON.parse(await
|
|
11387
|
+
const parsed = JSON.parse(await readFile29(file, "utf8"));
|
|
11758
11388
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
11759
11389
|
pkg = parsed;
|
|
11760
11390
|
}
|
|
@@ -11856,11 +11486,11 @@ async function importExtensionFromCssText(projectDir, css, name) {
|
|
|
11856
11486
|
async function importExtensionFromFile(projectDir, filePath) {
|
|
11857
11487
|
const ext = path20.extname(filePath).toLowerCase();
|
|
11858
11488
|
if (ext === ".zip") {
|
|
11859
|
-
const buf = await
|
|
11489
|
+
const buf = await readFile29(filePath);
|
|
11860
11490
|
return importExtensionFromZip(projectDir, new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
|
|
11861
11491
|
}
|
|
11862
11492
|
if (ext === ".css") {
|
|
11863
|
-
const css = await
|
|
11493
|
+
const css = await readFile29(filePath, "utf8");
|
|
11864
11494
|
return importExtensionFromCssText(projectDir, css, prettify(path20.basename(filePath, ".css")));
|
|
11865
11495
|
}
|
|
11866
11496
|
throw new Error("Choose a .zip extension package or a .css stylesheet.");
|
|
@@ -12644,7 +12274,7 @@ var drivethrurpgProvider = {
|
|
|
12644
12274
|
};
|
|
12645
12275
|
|
|
12646
12276
|
// src/lib/publish/providers/gdrive.ts
|
|
12647
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
12277
|
+
import { mkdtemp as mkdtemp3, readFile as readFile30, readdir as readdir6, rm as rm5, stat as stat7, writeFile as writeFile10 } from "node:fs/promises";
|
|
12648
12278
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
12649
12279
|
import path22 from "node:path";
|
|
12650
12280
|
import { Zip, ZipDeflate } from "fflate";
|
|
@@ -13036,12 +12666,12 @@ async function collectZipEntries(root, dir, out, onWarn) {
|
|
|
13036
12666
|
await collectZipEntries(root, full, out, onWarn);
|
|
13037
12667
|
} else if (entry.isFile()) {
|
|
13038
12668
|
const rel = path22.relative(root, full).split(path22.sep).join("/");
|
|
13039
|
-
out[rel] = await
|
|
12669
|
+
out[rel] = await readFile30(full);
|
|
13040
12670
|
} else if (entry.isSymbolicLink()) {
|
|
13041
12671
|
const resolved = await stat7(full).catch(() => null);
|
|
13042
12672
|
if (resolved?.isFile()) {
|
|
13043
12673
|
const rel = path22.relative(root, full).split(path22.sep).join("/");
|
|
13044
|
-
out[rel] = await
|
|
12674
|
+
out[rel] = await readFile30(full);
|
|
13045
12675
|
} else if (resolved?.isDirectory()) {
|
|
13046
12676
|
await collectZipEntries(root, full, out, onWarn);
|
|
13047
12677
|
} else {
|
|
@@ -14028,7 +13658,7 @@ import git8 from "isomorphic-git";
|
|
|
14028
13658
|
// src/lib/remote-auth/scorched-earth.ts
|
|
14029
13659
|
import git9 from "isomorphic-git";
|
|
14030
13660
|
// src/lib/publish/selections.ts
|
|
14031
|
-
import { chmod as chmod2, mkdir as mkdir13, readFile as
|
|
13661
|
+
import { chmod as chmod2, mkdir as mkdir13, readFile as readFile31, writeFile as writeFile12 } from "node:fs/promises";
|
|
14032
13662
|
import path26 from "node:path";
|
|
14033
13663
|
class PublishSelectionsStore {
|
|
14034
13664
|
filePath;
|
|
@@ -14038,7 +13668,7 @@ class PublishSelectionsStore {
|
|
|
14038
13668
|
}
|
|
14039
13669
|
async read() {
|
|
14040
13670
|
try {
|
|
14041
|
-
const raw = await
|
|
13671
|
+
const raw = await readFile31(this.filePath, "utf8");
|
|
14042
13672
|
const parsed = JSON.parse(raw);
|
|
14043
13673
|
if (parsed && typeof parsed === "object") {
|
|
14044
13674
|
return {
|