gutterpress 0.11.17-alpha.2 → 0.11.17-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/index.d.ts +4 -0
- package/dist/api/index.js +11 -2
- package/dist/black-text-gray-7dz61456.js +15 -0
- package/dist/{build-1p999cmg.js → build-fam0tqg6.js} +5 -4
- package/dist/checks/pdf/black-text.d.ts +3 -0
- package/dist/checks/pdf/index.d.ts +2 -0
- package/dist/checks/pdf/signature.d.ts +3 -0
- package/dist/{cli-6z5zthzt.js → cli-2kc34kme.js} +470 -489
- package/dist/{cli-70xgehq2.js → cli-8apk8571.js} +1 -1
- package/dist/cli-ht1bcqdd.js +493 -0
- package/dist/{cli-gymfg04q.js → cli-jyy9pqf3.js} +15 -10
- package/dist/{cli-ppc0vz4b.js → cli-pqcb2nnb.js} +1 -1
- package/dist/cli.js +12 -12
- package/dist/{doctor-reqpnn3h.js → doctor-zre5k5fg.js} +2 -2
- package/dist/engine/compiler/build.d.ts +3 -2
- package/dist/engine/compiler/postprocess.d.ts +6 -4
- package/dist/engine/shared/synthesis.d.ts +29 -5
- package/dist/{engine-5jzhjjvv.js → engine-3xqgm82d.js} +2 -2
- package/dist/{engine-078ggczp.js → engine-zdb41ay1.js} +1 -1
- package/dist/{ext-dsbfejt3.js → ext-hfqcg6ax.js} +5 -4
- package/dist/{gutterpress-viewer-zkv2stfy.js → gutterpress-viewer-1n793gwd.js} +5 -3
- package/dist/{index-c275p061.js → index-gp503ze9.js} +1622 -1332
- package/dist/{index-5dcj1vts.js → index-hrx0gfkj.js} +23 -684
- package/dist/index-ht1bcqdd.js +493 -0
- package/dist/index-q1xqxrh9.js +949 -0
- package/dist/index.js +12 -3
- package/dist/lib/black-text-gray.d.ts +55 -8
- package/dist/lib/build-fingerprint.d.ts +2 -0
- package/dist/lib/engine.d.ts +2 -1
- package/dist/lib/lint-rules.d.ts +9 -0
- package/dist/lib/markdown/assemble.d.ts +3 -5
- package/dist/lib/markdown/index.d.ts +3 -8
- package/dist/lib/markdown/renderer.d.ts +9 -1
- package/dist/lib/page-size.d.ts +66 -0
- package/dist/lib/pdf-inspect.d.ts +6 -0
- package/dist/lib/printsafe.d.ts +8 -1
- package/dist/lib/project-scaffold.d.ts +3 -9
- package/dist/lib/remote-auth/github-repos.d.ts +7 -0
- package/dist/lib/remote-auth/online-backup.d.ts +51 -0
- package/dist/lib/snippets.d.ts +3 -0
- package/dist/{manifest.schema-40sfq49z.json → manifest.schema-gh0jjve9.json} +14 -2
- package/dist/{new-m5y2wepr.js → new-bjdf7bhg.js} +5 -4
- package/dist/{plugin.js-m4a7m3t4.tpl → plugin.js-cswq7wvd.tpl} +4 -1
- package/dist/{plugin.test.js-6n8ct5e3.tpl → plugin.test.js-xgxrd8hf.tpl} +4 -1
- package/dist/{preflight-bwpqwwny.js → preflight-gvw01661.js} +5 -4
- package/dist/preview/file-watcher.d.ts +2 -4
- package/dist/{preview-62y7qhkp.js → preview-1cschq8s.js} +5 -4
- package/dist/{publish-k2zyt3x6.js → publish-ddqrb8s9.js} +5 -4
- package/dist/render.js +30 -5
- package/dist/schema/manifest.types.d.ts +17 -0
- package/dist/{validate-fgwwxtbn.js → validate-3hjkgxrz.js} +5 -4
- package/package.json +1 -1
- package/dist/index-eyaj6nkc.js +0 -267
|
@@ -13,17 +13,21 @@ import {
|
|
|
13
13
|
stampCreator,
|
|
14
14
|
stripAnnotations,
|
|
15
15
|
warn
|
|
16
|
-
} from "./cli-
|
|
16
|
+
} from "./cli-8apk8571.js";
|
|
17
17
|
import {
|
|
18
18
|
DEBOUNCE,
|
|
19
19
|
UsageError,
|
|
20
20
|
resolvePort
|
|
21
|
-
} from "./cli-
|
|
21
|
+
} from "./cli-pqcb2nnb.js";
|
|
22
|
+
import {
|
|
23
|
+
blackTextToGray
|
|
24
|
+
} from "./cli-ht1bcqdd.js";
|
|
22
25
|
import {
|
|
23
26
|
MARGIN_BOX_IGNORED_PROPERTIES,
|
|
24
27
|
getAssetPath,
|
|
25
|
-
getAssetsDir
|
|
26
|
-
|
|
28
|
+
getAssetsDir,
|
|
29
|
+
lastPageMode
|
|
30
|
+
} from "./cli-jyy9pqf3.js";
|
|
27
31
|
import {
|
|
28
32
|
BuildError,
|
|
29
33
|
EXIT_CODES
|
|
@@ -106,7 +110,7 @@ var DTRPG_PRESET = {
|
|
|
106
110
|
source: {
|
|
107
111
|
files: null
|
|
108
112
|
},
|
|
109
|
-
print: { signature: 4, reserveLastPage: true },
|
|
113
|
+
print: { signature: 4, reserveLastPage: true, lastPage: undefined },
|
|
110
114
|
pdfx: {
|
|
111
115
|
flavor: "x1a",
|
|
112
116
|
icc: "profiles/CGATS21_CRPC1.icc",
|
|
@@ -124,7 +128,8 @@ var DTRPG_PRESET = {
|
|
|
124
128
|
},
|
|
125
129
|
lint: {
|
|
126
130
|
enabled: true,
|
|
127
|
-
configPath: null
|
|
131
|
+
configPath: null,
|
|
132
|
+
rules: {}
|
|
128
133
|
},
|
|
129
134
|
validate: {
|
|
130
135
|
enabled: true,
|
|
@@ -181,7 +186,7 @@ var BOOK_PRESET = {
|
|
|
181
186
|
source: {
|
|
182
187
|
files: null
|
|
183
188
|
},
|
|
184
|
-
print: { signature: 1, reserveLastPage: false },
|
|
189
|
+
print: { signature: 1, reserveLastPage: false, lastPage: undefined },
|
|
185
190
|
pdfx: {
|
|
186
191
|
flavor: "x1a",
|
|
187
192
|
icc: "profiles/CGATS21_CRPC1.icc",
|
|
@@ -199,7 +204,8 @@ var BOOK_PRESET = {
|
|
|
199
204
|
},
|
|
200
205
|
lint: {
|
|
201
206
|
enabled: true,
|
|
202
|
-
configPath: null
|
|
207
|
+
configPath: null,
|
|
208
|
+
rules: {}
|
|
203
209
|
},
|
|
204
210
|
validate: {
|
|
205
211
|
enabled: true,
|
|
@@ -566,6 +572,10 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
|
|
|
566
572
|
if (legacyPlugins !== undefined) {
|
|
567
573
|
throw new UsageError(pluginsRemovedMessage(legacyPlugins));
|
|
568
574
|
}
|
|
575
|
+
const lastPage = c.print?.lastPage ?? m.print?.lastPage;
|
|
576
|
+
if (lastPage !== undefined && lastPage !== "blank" && lastPage !== "short-by-one") {
|
|
577
|
+
throw new UsageError(`Manifest field \`print.lastPage\` must be "blank" or "short-by-one", got ${JSON.stringify(lastPage)}.`);
|
|
578
|
+
}
|
|
569
579
|
const rawExtensions = c.extensions ?? m.extensions ?? [];
|
|
570
580
|
if (!Array.isArray(rawExtensions)) {
|
|
571
581
|
throw new UsageError("Manifest field `extensions` must be a list of specifiers.");
|
|
@@ -582,7 +592,10 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
|
|
|
582
592
|
print: mergeShape(c.print, m.print, preset.print),
|
|
583
593
|
page: preset.page ? mergeShape(c.page, m.page, preset.page) : resolveCustomPage(c, m),
|
|
584
594
|
ink: mergeShape(c.ink, m.ink, preset.ink),
|
|
585
|
-
lint:
|
|
595
|
+
lint: {
|
|
596
|
+
...mergeShape(c.lint, m.lint, preset.lint),
|
|
597
|
+
rules: { ...preset.lint.rules, ...m.lint?.rules, ...c.lint?.rules }
|
|
598
|
+
},
|
|
586
599
|
validate: {
|
|
587
600
|
enabled: c.validate?.enabled ?? m.validate?.enabled ?? preset.validate.enabled,
|
|
588
601
|
checks: { ...preset.validate.checks, ...m.validate?.checks, ...c.validate?.checks },
|
|
@@ -1078,7 +1091,7 @@ function resolveContainerShape(name, pluginName, decl) {
|
|
|
1078
1091
|
throw new Error(`Plugin "${pluginName}"'s marker "@${name}" has a \`section\` that is not a boolean.`);
|
|
1079
1092
|
}
|
|
1080
1093
|
if (decl.section) {
|
|
1081
|
-
for (const field of ["tag", "label", "autoCloseAt"]) {
|
|
1094
|
+
for (const field of ["tag", "label", "autoCloseAt", "selfClosing"]) {
|
|
1082
1095
|
if (decl[field] !== undefined) {
|
|
1083
1096
|
throw new Error(`Plugin "${pluginName}"'s marker "@${name}" sets \`section: true\` and \`${field}\` — a section ` + `marker is a core @section, so \`${field}\` does not apply. Remove one of them.`);
|
|
1084
1097
|
}
|
|
@@ -1135,11 +1148,26 @@ function resolveContainerShape(name, pluginName, decl) {
|
|
|
1135
1148
|
}
|
|
1136
1149
|
autoCloseAtEof = decl.autoCloseAt.includes("eof");
|
|
1137
1150
|
}
|
|
1151
|
+
if (decl.selfClosing !== undefined && typeof decl.selfClosing !== "boolean") {
|
|
1152
|
+
throw new Error(`Plugin "${pluginName}"'s marker "@${name}" has a \`selfClosing\` that is not a boolean.`);
|
|
1153
|
+
}
|
|
1154
|
+
if (decl.selfClosing && decl.autoCloseAt !== undefined) {
|
|
1155
|
+
throw new Error(`Plugin "${pluginName}"'s marker "@${name}" sets \`selfClosing: true\` and \`autoCloseAt\` — a ` + `self-closing marker has no open container to auto-close. Remove one of them.`);
|
|
1156
|
+
}
|
|
1138
1157
|
if (decl.validate !== undefined && typeof decl.validate !== "function") {
|
|
1139
1158
|
throw new Error(`Plugin "${pluginName}"'s marker "@${name}" has a \`validate\` that is not a function.`);
|
|
1140
1159
|
}
|
|
1141
1160
|
const check = decl.validate ? { validate: decl.validate, validateOwner: pluginName } : {};
|
|
1142
|
-
return {
|
|
1161
|
+
return {
|
|
1162
|
+
tag,
|
|
1163
|
+
classBase,
|
|
1164
|
+
variants,
|
|
1165
|
+
label,
|
|
1166
|
+
autoCloseAtEof,
|
|
1167
|
+
...decl.section ? { section: true } : {},
|
|
1168
|
+
...decl.selfClosing ? { selfClosing: true } : {},
|
|
1169
|
+
...check
|
|
1170
|
+
};
|
|
1143
1171
|
}
|
|
1144
1172
|
function resolveMarkerDeclaration(name, rawDecl, rawRegistry, originOf) {
|
|
1145
1173
|
const pluginName = originOf.get(name);
|
|
@@ -1398,7 +1426,8 @@ function plugin(md, pluginOptions = {}) {
|
|
|
1398
1426
|
addClasses(t, baseClass, meta.attrs && meta.attrs.class ? meta.attrs.class : "");
|
|
1399
1427
|
attachDataAttrs(t, decl.baseKind, variant, meta.attrs || {});
|
|
1400
1428
|
out.push(t);
|
|
1401
|
-
|
|
1429
|
+
if (!decl.selfClosing)
|
|
1430
|
+
declaredFrames.push({ kind: decl.baseKind, tag: decl.tag, decl, line: meta.__line });
|
|
1402
1431
|
if (decl.label) {
|
|
1403
1432
|
const value = meta.attrs && meta.attrs[decl.label.attr];
|
|
1404
1433
|
if (value) {
|
|
@@ -1409,6 +1438,8 @@ function plugin(md, pluginOptions = {}) {
|
|
|
1409
1438
|
t.meta.labelled = true;
|
|
1410
1439
|
}
|
|
1411
1440
|
}
|
|
1441
|
+
if (decl.selfClosing)
|
|
1442
|
+
out.push(new state.Token("layout_component_close", decl.tag, -1));
|
|
1412
1443
|
}
|
|
1413
1444
|
const stack = {
|
|
1414
1445
|
frames: [],
|
|
@@ -1687,7 +1718,7 @@ function plugin(md, pluginOptions = {}) {
|
|
|
1687
1718
|
continue;
|
|
1688
1719
|
}
|
|
1689
1720
|
if (!closeDeclaredFrame(closeDecl.baseKind)) {
|
|
1690
|
-
warn2(state.env, line, "declared_marker_close_without_open", `@${kind} used without an open @${closeDecl.baseKind}; ignoring marker.`, meta);
|
|
1721
|
+
warn2(state.env, line, "declared_marker_close_without_open", closeDecl.selfClosing ? `@${kind} is not a marker: @${closeDecl.baseKind} is self-closing and has nothing to close; ignoring marker.` : `@${kind} used without an open @${closeDecl.baseKind}; ignoring marker.`, meta);
|
|
1691
1722
|
}
|
|
1692
1723
|
continue;
|
|
1693
1724
|
}
|
|
@@ -4987,6 +5018,32 @@ function getImagePixelSize(page, name, timeoutMs = 2000) {
|
|
|
4987
5018
|
}
|
|
4988
5019
|
});
|
|
4989
5020
|
}
|
|
5021
|
+
var PAINT_OPS = new Set([
|
|
5022
|
+
"stroke",
|
|
5023
|
+
"closeStroke",
|
|
5024
|
+
"fill",
|
|
5025
|
+
"eoFill",
|
|
5026
|
+
"fillStroke",
|
|
5027
|
+
"eoFillStroke",
|
|
5028
|
+
"closeFillStroke",
|
|
5029
|
+
"closeEOFillStroke",
|
|
5030
|
+
"shadingFill",
|
|
5031
|
+
"paintImageXObject",
|
|
5032
|
+
"paintInlineImageXObject",
|
|
5033
|
+
"paintImageXObjectRepeat",
|
|
5034
|
+
"paintImageMaskXObject",
|
|
5035
|
+
"paintImageMaskXObjectGroup",
|
|
5036
|
+
"paintImageMaskXObjectRepeat",
|
|
5037
|
+
"paintSolidColorImageMask",
|
|
5038
|
+
"showText",
|
|
5039
|
+
"showSpacedText",
|
|
5040
|
+
"nextLineShowText",
|
|
5041
|
+
"nextLineSetSpacingShowText"
|
|
5042
|
+
].map((name) => OPS[name]).filter((op) => op !== undefined));
|
|
5043
|
+
async function pageHasMarks(doc, pageNum) {
|
|
5044
|
+
const ops = await (await doc.getPage(pageNum)).getOperatorList();
|
|
5045
|
+
return ops.fnArray.some((fn, i) => fn === OPS.constructPath ? PAINT_OPS.has(ops.argsArray[i][0]) : PAINT_OPS.has(fn));
|
|
5046
|
+
}
|
|
4990
5047
|
async function getImageResolutions(doc) {
|
|
4991
5048
|
const { imagesByPage } = await getOpPass(doc);
|
|
4992
5049
|
const out = [];
|
|
@@ -5490,8 +5547,100 @@ var check7 = {
|
|
|
5490
5547
|
};
|
|
5491
5548
|
registerCheck(check7);
|
|
5492
5549
|
|
|
5493
|
-
// src/checks/pdf/
|
|
5550
|
+
// src/checks/pdf/black-text.ts
|
|
5551
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
5494
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 = {
|
|
5590
|
+
id: "pdf.print.signature",
|
|
5591
|
+
name: "Signature",
|
|
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)",
|
|
5593
|
+
category: "pdf",
|
|
5594
|
+
phase: "post-build",
|
|
5595
|
+
enabledWhen: (config) => config.print.signature > 1,
|
|
5596
|
+
async run(ctx) {
|
|
5597
|
+
if (!ctx.pdfPath)
|
|
5598
|
+
return [];
|
|
5599
|
+
const doc = await loadPdf(ctx.pdfPath);
|
|
5600
|
+
if (!doc) {
|
|
5601
|
+
return [inspectionFailed(check9.id, "Could not parse the PDF's page count.", { file: ctx.pdfPath })];
|
|
5602
|
+
}
|
|
5603
|
+
const size = ctx.config.print.signature;
|
|
5604
|
+
const pages = doc.numPages;
|
|
5605
|
+
if ((pages + 1) % size === 0)
|
|
5606
|
+
return [];
|
|
5607
|
+
const data = { pages, signature: size };
|
|
5608
|
+
if (pages % size !== 0) {
|
|
5609
|
+
return [
|
|
5610
|
+
finding(check9.id, {
|
|
5611
|
+
severity: "error",
|
|
5612
|
+
message: `Page count ${pages} does not fit a signature of ${size}: it must be a multiple of ${size}, or one short of one.`,
|
|
5613
|
+
file: ctx.pdfPath,
|
|
5614
|
+
detail: "Set `print.signature` in the manifest and rebuild; the build pads the interior for you.",
|
|
5615
|
+
code: "signature-page-count",
|
|
5616
|
+
data
|
|
5617
|
+
})
|
|
5618
|
+
];
|
|
5619
|
+
}
|
|
5620
|
+
if (!lastPageMode(ctx.config.print))
|
|
5621
|
+
return [];
|
|
5622
|
+
try {
|
|
5623
|
+
if (!await pageHasMarks(doc, pages))
|
|
5624
|
+
return [];
|
|
5625
|
+
} catch {
|
|
5626
|
+
return [inspectionFailed(check9.id, "Could not read the last page.", { file: ctx.pdfPath })];
|
|
5627
|
+
}
|
|
5628
|
+
return [
|
|
5629
|
+
finding(check9.id, {
|
|
5630
|
+
severity: "error",
|
|
5631
|
+
message: `The last page (${pages}) has content, but the printer needs it blank for its own information.`,
|
|
5632
|
+
file: ctx.pdfPath,
|
|
5633
|
+
detail: "The build adds that page for you (`print.lastPage: blank`, or `short-by-one` to stop a page short of a multiple " + `of ${size} and let the printer add it). Check that the interior was built with this manifest, and that nothing in it ` + "forces content onto the final page.",
|
|
5634
|
+
code: "signature-last-page",
|
|
5635
|
+
data
|
|
5636
|
+
})
|
|
5637
|
+
];
|
|
5638
|
+
}
|
|
5639
|
+
};
|
|
5640
|
+
registerCheck(check9);
|
|
5641
|
+
|
|
5642
|
+
// src/checks/pdf/bookmarks.ts
|
|
5643
|
+
var check10 = {
|
|
5495
5644
|
id: "pdf.nav.bookmarks",
|
|
5496
5645
|
name: "Bookmarks",
|
|
5497
5646
|
description: "Checks for PDF outline (bookmarks) tree",
|
|
@@ -5505,14 +5654,14 @@ var check8 = {
|
|
|
5505
5654
|
const doc = await loadPdf(ctx.pdfPath);
|
|
5506
5655
|
if (!doc) {
|
|
5507
5656
|
return [
|
|
5508
|
-
inspectionFailed(
|
|
5657
|
+
inspectionFailed(check10.id, "Could not inspect PDF for bookmarks.", {
|
|
5509
5658
|
file: ctx.pdfPath
|
|
5510
5659
|
})
|
|
5511
5660
|
];
|
|
5512
5661
|
}
|
|
5513
5662
|
if (await getOutlineCount(doc) === 0) {
|
|
5514
5663
|
return [
|
|
5515
|
-
finding(
|
|
5664
|
+
finding(check10.id, {
|
|
5516
5665
|
severity: "warning",
|
|
5517
5666
|
message: "PDF does not contain bookmarks (outline tree).",
|
|
5518
5667
|
file: ctx.pdfPath
|
|
@@ -5522,10 +5671,10 @@ var check8 = {
|
|
|
5522
5671
|
return [];
|
|
5523
5672
|
}
|
|
5524
5673
|
};
|
|
5525
|
-
registerCheck(
|
|
5674
|
+
registerCheck(check10);
|
|
5526
5675
|
|
|
5527
5676
|
// src/checks/pdf/toc-links.ts
|
|
5528
|
-
var
|
|
5677
|
+
var check11 = {
|
|
5529
5678
|
id: "pdf.nav.toc-links",
|
|
5530
5679
|
name: "TOC Links",
|
|
5531
5680
|
description: "Detects link annotations on TOC pages",
|
|
@@ -5540,7 +5689,7 @@ var check9 = {
|
|
|
5540
5689
|
if (!doc) {
|
|
5541
5690
|
return [
|
|
5542
5691
|
{
|
|
5543
|
-
checkId:
|
|
5692
|
+
checkId: check11.id,
|
|
5544
5693
|
severity: "warning",
|
|
5545
5694
|
message: "Could not inspect PDF for TOC links.",
|
|
5546
5695
|
file: ctx.pdfPath
|
|
@@ -5551,7 +5700,7 @@ var check9 = {
|
|
|
5551
5700
|
if (!hasLinks) {
|
|
5552
5701
|
return [
|
|
5553
5702
|
{
|
|
5554
|
-
checkId:
|
|
5703
|
+
checkId: check11.id,
|
|
5555
5704
|
severity: "warning",
|
|
5556
5705
|
message: "No link annotations found in PDF (expected for TOC).",
|
|
5557
5706
|
file: ctx.pdfPath
|
|
@@ -5561,10 +5710,10 @@ var check9 = {
|
|
|
5561
5710
|
return [];
|
|
5562
5711
|
}
|
|
5563
5712
|
};
|
|
5564
|
-
registerCheck(
|
|
5713
|
+
registerCheck(check11);
|
|
5565
5714
|
|
|
5566
5715
|
// src/checks/pdf/cross-refs.ts
|
|
5567
|
-
var
|
|
5716
|
+
var check12 = {
|
|
5568
5717
|
id: "pdf.nav.cross-refs",
|
|
5569
5718
|
name: "Cross References",
|
|
5570
5719
|
description: "Counts and verifies internal link annotations",
|
|
@@ -5577,7 +5726,7 @@ var check10 = {
|
|
|
5577
5726
|
if (!doc) {
|
|
5578
5727
|
return [
|
|
5579
5728
|
{
|
|
5580
|
-
checkId:
|
|
5729
|
+
checkId: check12.id,
|
|
5581
5730
|
severity: "info",
|
|
5582
5731
|
message: "Could not inspect PDF for cross-references.",
|
|
5583
5732
|
file: ctx.pdfPath
|
|
@@ -5587,7 +5736,7 @@ var check10 = {
|
|
|
5587
5736
|
const linkCount = await countLinkAnnotations(doc);
|
|
5588
5737
|
return [
|
|
5589
5738
|
{
|
|
5590
|
-
checkId:
|
|
5739
|
+
checkId: check12.id,
|
|
5591
5740
|
severity: "info",
|
|
5592
5741
|
message: `PDF contains ${linkCount} internal link annotation${linkCount !== 1 ? "s" : ""}.`,
|
|
5593
5742
|
file: ctx.pdfPath
|
|
@@ -5595,10 +5744,10 @@ var check10 = {
|
|
|
5595
5744
|
];
|
|
5596
5745
|
}
|
|
5597
5746
|
};
|
|
5598
|
-
registerCheck(
|
|
5747
|
+
registerCheck(check12);
|
|
5599
5748
|
|
|
5600
5749
|
// src/checks/pdf/page-labels.ts
|
|
5601
|
-
var
|
|
5750
|
+
var check13 = {
|
|
5602
5751
|
id: "pdf.nav.page-labels",
|
|
5603
5752
|
name: "Page Labels",
|
|
5604
5753
|
description: "Verifies PDF page labels / folio numbering",
|
|
@@ -5613,7 +5762,7 @@ var check11 = {
|
|
|
5613
5762
|
const labels = await getPageLabels(doc);
|
|
5614
5763
|
return [
|
|
5615
5764
|
{
|
|
5616
|
-
checkId:
|
|
5765
|
+
checkId: check13.id,
|
|
5617
5766
|
severity: "info",
|
|
5618
5767
|
message: labels && labels.length > 0 ? "PDF contains page labels." : "PDF does not contain explicit page labels.",
|
|
5619
5768
|
file: ctx.pdfPath
|
|
@@ -5621,10 +5770,10 @@ var check11 = {
|
|
|
5621
5770
|
];
|
|
5622
5771
|
}
|
|
5623
5772
|
};
|
|
5624
|
-
registerCheck(
|
|
5773
|
+
registerCheck(check13);
|
|
5625
5774
|
|
|
5626
5775
|
// src/checks/pdf/image-resolution.ts
|
|
5627
|
-
var
|
|
5776
|
+
var check14 = {
|
|
5628
5777
|
id: "pdf.print.image-resolution",
|
|
5629
5778
|
name: "Image Resolution",
|
|
5630
5779
|
description: "Checks DPI of images embedded in the PDF",
|
|
@@ -5651,7 +5800,7 @@ var check12 = {
|
|
|
5651
5800
|
lowResPages.sort((a, b) => a - b);
|
|
5652
5801
|
return [
|
|
5653
5802
|
{
|
|
5654
|
-
checkId:
|
|
5803
|
+
checkId: check14.id,
|
|
5655
5804
|
severity: "warning",
|
|
5656
5805
|
message: `Low-resolution images (below ${minDpi} DPI) found on pages: ${lowResPages.join(", ")}`,
|
|
5657
5806
|
file: ctx.pdfPath
|
|
@@ -5661,10 +5810,10 @@ var check12 = {
|
|
|
5661
5810
|
return [];
|
|
5662
5811
|
}
|
|
5663
5812
|
};
|
|
5664
|
-
registerCheck(
|
|
5813
|
+
registerCheck(check14);
|
|
5665
5814
|
|
|
5666
5815
|
// src/checks/pdf/transparency.ts
|
|
5667
|
-
var
|
|
5816
|
+
var check15 = {
|
|
5668
5817
|
id: "pdf.print.transparency",
|
|
5669
5818
|
name: "Transparency",
|
|
5670
5819
|
description: "Checks for transparency markers (/Transparency, /SMask, /BM) in the PDF",
|
|
@@ -5686,7 +5835,7 @@ var check13 = {
|
|
|
5686
5835
|
if (found.length > 0) {
|
|
5687
5836
|
return [
|
|
5688
5837
|
{
|
|
5689
|
-
checkId:
|
|
5838
|
+
checkId: check15.id,
|
|
5690
5839
|
severity: "warning",
|
|
5691
5840
|
message: `Transparency detected in PDF: ${found.join(", ")}. This may cause issues with some printers.`,
|
|
5692
5841
|
file: ctx.pdfPath
|
|
@@ -5696,7 +5845,7 @@ var check13 = {
|
|
|
5696
5845
|
return [];
|
|
5697
5846
|
}
|
|
5698
5847
|
};
|
|
5699
|
-
registerCheck(
|
|
5848
|
+
registerCheck(check15);
|
|
5700
5849
|
|
|
5701
5850
|
// src/checks/pdf/bleed.ts
|
|
5702
5851
|
function findBox(bytes, box) {
|
|
@@ -5706,7 +5855,7 @@ function findBox(bytes, box) {
|
|
|
5706
5855
|
return null;
|
|
5707
5856
|
return [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])];
|
|
5708
5857
|
}
|
|
5709
|
-
var
|
|
5858
|
+
var check16 = {
|
|
5710
5859
|
id: "pdf.print.bleed",
|
|
5711
5860
|
name: "Bleed Box",
|
|
5712
5861
|
description: "Compares MediaBox vs TrimBox/BleedBox to verify bleed area",
|
|
@@ -5720,7 +5869,7 @@ var check14 = {
|
|
|
5720
5869
|
const bytes = await readPdfBytes(ctx.pdfPath).catch(() => "");
|
|
5721
5870
|
if (!bytes) {
|
|
5722
5871
|
return [
|
|
5723
|
-
inspectionFailed(
|
|
5872
|
+
inspectionFailed(check16.id, "Could not inspect PDF for bleed boxes.", {
|
|
5724
5873
|
file: ctx.pdfPath
|
|
5725
5874
|
})
|
|
5726
5875
|
];
|
|
@@ -5730,14 +5879,14 @@ var check14 = {
|
|
|
5730
5879
|
const bleed = findBox(bytes, "BleedBox");
|
|
5731
5880
|
if (!media) {
|
|
5732
5881
|
return [
|
|
5733
|
-
inspectionFailed(
|
|
5882
|
+
inspectionFailed(check16.id, "Could not parse MediaBox from PDF.", {
|
|
5734
5883
|
file: ctx.pdfPath
|
|
5735
5884
|
})
|
|
5736
5885
|
];
|
|
5737
5886
|
}
|
|
5738
5887
|
if (!trim && !bleed) {
|
|
5739
5888
|
return [
|
|
5740
|
-
finding(
|
|
5889
|
+
finding(check16.id, {
|
|
5741
5890
|
severity: "warning",
|
|
5742
5891
|
message: "No TrimBox or BleedBox found. Bleed area cannot be verified.",
|
|
5743
5892
|
file: ctx.pdfPath
|
|
@@ -5750,7 +5899,7 @@ var check14 = {
|
|
|
5750
5899
|
const bleedSize = ctx.config.validate.pdf.bleedSize;
|
|
5751
5900
|
if (mediaW - trimW < bleedSize * 2 * 0.9) {
|
|
5752
5901
|
return [
|
|
5753
|
-
finding(
|
|
5902
|
+
finding(check16.id, {
|
|
5754
5903
|
severity: "warning",
|
|
5755
5904
|
message: `Bleed area appears insufficient. Expected at least ${bleedSize}pt on each side.`,
|
|
5756
5905
|
file: ctx.pdfPath
|
|
@@ -5761,10 +5910,10 @@ var check14 = {
|
|
|
5761
5910
|
return [];
|
|
5762
5911
|
}
|
|
5763
5912
|
};
|
|
5764
|
-
registerCheck(
|
|
5913
|
+
registerCheck(check16);
|
|
5765
5914
|
|
|
5766
5915
|
// src/checks/pdf/pdfx-metadata.ts
|
|
5767
|
-
var
|
|
5916
|
+
var check17 = {
|
|
5768
5917
|
id: "pdf.print.pdfx-metadata",
|
|
5769
5918
|
name: "PDF/X Metadata",
|
|
5770
5919
|
description: "Verifies PDF/X DOCINFO metadata via qpdf JSON",
|
|
@@ -5783,25 +5932,25 @@ var check15 = {
|
|
|
5783
5932
|
const objects = parseQpdfObjectsJson(stdout);
|
|
5784
5933
|
if (!objects) {
|
|
5785
5934
|
return [
|
|
5786
|
-
inspectionFailed(
|
|
5935
|
+
inspectionFailed(check17.id, "Unable to parse qpdf JSON output for PDF/X metadata checks.", { file: ctx.pdfPath })
|
|
5787
5936
|
];
|
|
5788
5937
|
}
|
|
5789
|
-
return getPdfxMetadataIssues(objects, ctx.config.pdfx.flavor).map((message) => finding(
|
|
5938
|
+
return getPdfxMetadataIssues(objects, ctx.config.pdfx.flavor).map((message) => finding(check17.id, {
|
|
5790
5939
|
severity: "error",
|
|
5791
5940
|
message,
|
|
5792
5941
|
file: ctx.pdfPath
|
|
5793
5942
|
}));
|
|
5794
5943
|
} catch {
|
|
5795
5944
|
return [
|
|
5796
|
-
inspectionFailed(
|
|
5945
|
+
inspectionFailed(check17.id, "Failed to inspect PDF objects with qpdf for PDF/X metadata.", { file: ctx.pdfPath })
|
|
5797
5946
|
];
|
|
5798
5947
|
}
|
|
5799
5948
|
}
|
|
5800
5949
|
};
|
|
5801
|
-
registerCheck(
|
|
5950
|
+
registerCheck(check17);
|
|
5802
5951
|
|
|
5803
5952
|
// src/checks/source/markdownlint.ts
|
|
5804
|
-
import { readFile as
|
|
5953
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
5805
5954
|
import { lint } from "markdownlint/sync";
|
|
5806
5955
|
import { parse as parseYaml2 } from "yaml";
|
|
5807
5956
|
|
|
@@ -5834,7 +5983,7 @@ function stripJsonComments(s) {
|
|
|
5834
5983
|
return s.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1");
|
|
5835
5984
|
}
|
|
5836
5985
|
async function loadConfig(configPath) {
|
|
5837
|
-
const raw = await
|
|
5986
|
+
const raw = await readFile10(configPath, "utf8");
|
|
5838
5987
|
let parsed;
|
|
5839
5988
|
if (configPath.endsWith(".json")) {
|
|
5840
5989
|
parsed = JSON.parse(raw);
|
|
@@ -5853,7 +6002,7 @@ async function loadConfig(configPath) {
|
|
|
5853
6002
|
return {};
|
|
5854
6003
|
}
|
|
5855
6004
|
var MARKDOWNLINT_CHECK_ID = "source.markdownlint";
|
|
5856
|
-
var
|
|
6005
|
+
var check18 = {
|
|
5857
6006
|
id: MARKDOWNLINT_CHECK_ID,
|
|
5858
6007
|
name: "Markdownlint",
|
|
5859
6008
|
description: "Runs markdownlint with project config to validate Markdown files",
|
|
@@ -5878,7 +6027,7 @@ var check16 = {
|
|
|
5878
6027
|
continue;
|
|
5879
6028
|
for (const v of violations) {
|
|
5880
6029
|
out.push({
|
|
5881
|
-
checkId:
|
|
6030
|
+
checkId: check18.id,
|
|
5882
6031
|
severity: "warning",
|
|
5883
6032
|
message: `${v.ruleDescription} (${v.ruleNames.join("/")})`,
|
|
5884
6033
|
file,
|
|
@@ -5891,15 +6040,15 @@ var check16 = {
|
|
|
5891
6040
|
return out;
|
|
5892
6041
|
}
|
|
5893
6042
|
};
|
|
5894
|
-
registerCheck(
|
|
6043
|
+
registerCheck(check18);
|
|
5895
6044
|
|
|
5896
6045
|
// src/checks/source/htmlhint.ts
|
|
5897
6046
|
import { existsSync as existsSync7 } from "node:fs";
|
|
5898
|
-
import { readFile as
|
|
6047
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
5899
6048
|
import htmlhint from "htmlhint";
|
|
5900
6049
|
var { HTMLHint } = htmlhint;
|
|
5901
6050
|
var CONFIG_NAMES2 = [".htmlhintrc"];
|
|
5902
|
-
var
|
|
6051
|
+
var check19 = {
|
|
5903
6052
|
id: "source.htmlhint",
|
|
5904
6053
|
name: "HTMLHint",
|
|
5905
6054
|
description: "Runs htmlhint with project config to validate generated HTML",
|
|
@@ -5915,11 +6064,11 @@ var check17 = {
|
|
|
5915
6064
|
const resolvedConfig = findConfigFile(ctx.inputDir, CONFIG_NAMES2, configPath);
|
|
5916
6065
|
if (!resolvedConfig && !configPath)
|
|
5917
6066
|
return [];
|
|
5918
|
-
const ruleset = resolvedConfig ? JSON.parse(await
|
|
5919
|
-
const html = await
|
|
6067
|
+
const ruleset = resolvedConfig ? JSON.parse(await readFile11(resolvedConfig, "utf8")) : HTMLHint.defaultRuleset;
|
|
6068
|
+
const html = await readFile11(ctx.htmlPath, "utf8");
|
|
5920
6069
|
const messages = HTMLHint.verify(html, ruleset);
|
|
5921
6070
|
return messages.map((m) => ({
|
|
5922
|
-
checkId:
|
|
6071
|
+
checkId: check19.id,
|
|
5923
6072
|
severity: m.type === "error" ? "error" : "warning",
|
|
5924
6073
|
message: `${m.rule.id}: ${m.message}`,
|
|
5925
6074
|
file: ctx.htmlPath,
|
|
@@ -5928,13 +6077,35 @@ var check17 = {
|
|
|
5928
6077
|
}));
|
|
5929
6078
|
}
|
|
5930
6079
|
};
|
|
5931
|
-
registerCheck(
|
|
6080
|
+
registerCheck(check19);
|
|
5932
6081
|
|
|
5933
6082
|
// src/checks/source/stylelint.ts
|
|
5934
|
-
import { readFile as
|
|
6083
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
5935
6084
|
|
|
5936
6085
|
// src/lib/printsafe.ts
|
|
5937
6086
|
import postcss from "postcss";
|
|
6087
|
+
|
|
6088
|
+
// src/lib/lint-rules.ts
|
|
6089
|
+
function ruleLevel(rules, id) {
|
|
6090
|
+
const v = rules?.[id];
|
|
6091
|
+
if (v === "off" || v === false)
|
|
6092
|
+
return "off";
|
|
6093
|
+
if (v === "warn" || v === "warning")
|
|
6094
|
+
return "warn";
|
|
6095
|
+
if (v === "error")
|
|
6096
|
+
return "error";
|
|
6097
|
+
return;
|
|
6098
|
+
}
|
|
6099
|
+
var DIRECTIVE = /^\s*gutterpress-disable-next-line\s+(.*?)\s*$/;
|
|
6100
|
+
function parseDisableDirective(commentText) {
|
|
6101
|
+
const m = DIRECTIVE.exec(commentText);
|
|
6102
|
+
if (!m)
|
|
6103
|
+
return null;
|
|
6104
|
+
const ids = m[1].split(/\s--(?:\s|$)/)[0].split(/[\s,]+/).filter(Boolean);
|
|
6105
|
+
return ids.length > 0 ? ids : null;
|
|
6106
|
+
}
|
|
6107
|
+
|
|
6108
|
+
// src/lib/printsafe.ts
|
|
5938
6109
|
var ruleRemoteUrls = "printsafe/no-remote-urls";
|
|
5939
6110
|
var ruleRiskyProps = "printsafe/no-risky-print-effects";
|
|
5940
6111
|
var ruleSyntax = "printsafe/syntax-error";
|
|
@@ -5959,9 +6130,11 @@ var riskyProperties = new Set([
|
|
|
5959
6130
|
"animation",
|
|
5960
6131
|
"animation-name",
|
|
5961
6132
|
"transition",
|
|
5962
|
-
"will-change"
|
|
5963
|
-
"clip-path"
|
|
6133
|
+
"will-change"
|
|
5964
6134
|
]);
|
|
6135
|
+
function isRasterizingClipPath(decl) {
|
|
6136
|
+
return decl.prop.toLowerCase() === "clip-path" && /\burl\(/i.test(decl.value);
|
|
6137
|
+
}
|
|
5965
6138
|
var marginBoxAtRuleNames = new Set([
|
|
5966
6139
|
"top-left-corner",
|
|
5967
6140
|
"top-left",
|
|
@@ -6088,7 +6261,34 @@ function nodeLoc(node) {
|
|
|
6088
6261
|
column: node.source?.start?.column ?? 1
|
|
6089
6262
|
};
|
|
6090
6263
|
}
|
|
6091
|
-
function checkCss(css, from) {
|
|
6264
|
+
function checkCss(css, from, options = {}) {
|
|
6265
|
+
return filterFindings(checkCssRaw(css, from), css, options.rules);
|
|
6266
|
+
}
|
|
6267
|
+
function filterFindings(findings, css, rules) {
|
|
6268
|
+
const silenced = new Map;
|
|
6269
|
+
for (const m of css.matchAll(/\/\*([\s\S]*?)\*\//g)) {
|
|
6270
|
+
const ids = parseDisableDirective(m[1]);
|
|
6271
|
+
if (!ids)
|
|
6272
|
+
continue;
|
|
6273
|
+
const endLine = css.slice(0, m.index + m[0].length).split(`
|
|
6274
|
+
`).length;
|
|
6275
|
+
const set = silenced.get(endLine + 1) ?? new Set;
|
|
6276
|
+
for (const id of ids)
|
|
6277
|
+
set.add(id);
|
|
6278
|
+
silenced.set(endLine + 1, set);
|
|
6279
|
+
}
|
|
6280
|
+
const out = [];
|
|
6281
|
+
for (const f of findings) {
|
|
6282
|
+
if (silenced.get(f.line)?.has(f.rule))
|
|
6283
|
+
continue;
|
|
6284
|
+
const level = ruleLevel(rules, f.rule);
|
|
6285
|
+
if (level === "off")
|
|
6286
|
+
continue;
|
|
6287
|
+
out.push(level === "warn" ? { ...f, severity: "warning" } : level === "error" ? { ...f, severity: "error" } : f);
|
|
6288
|
+
}
|
|
6289
|
+
return out;
|
|
6290
|
+
}
|
|
6291
|
+
function checkCssRaw(css, from) {
|
|
6092
6292
|
let root;
|
|
6093
6293
|
try {
|
|
6094
6294
|
root = postcss.parse(css, from ? { from } : undefined);
|
|
@@ -6137,6 +6337,13 @@ function checkCss(css, from) {
|
|
|
6137
6337
|
message: `Property is high-risk for print/PDF: 'filter' rasterizes its subtree to a 300 DPI bitmap in the printed PDF (text becomes unselectable, unsearchable, and inaccessible), and it dominates build time (~90% measured; 57.0s -> 6.2s over 60pp when scoped — see ENGINE.md §10). Scope it to the smallest possible selector. It also means the render-parity gate cannot see a text-only change inside it (docs/render-parity-gate.md, "Known blind spot").`,
|
|
6138
6338
|
...nodeLoc(decl)
|
|
6139
6339
|
});
|
|
6340
|
+
} else if (isRasterizingClipPath(decl)) {
|
|
6341
|
+
warnings.push({
|
|
6342
|
+
rule: ruleRiskyProps,
|
|
6343
|
+
severity: "warning",
|
|
6344
|
+
message: `Property is high-risk for print/PDF (can force rasterization): ${decl.prop} with url(...) references an SVG mask. Use a basic shape (polygon(), inset(), circle(), ellipse(), path()) instead — those print as vector clips.`,
|
|
6345
|
+
...nodeLoc(decl)
|
|
6346
|
+
});
|
|
6140
6347
|
} else if (riskyProperties.has(prop)) {
|
|
6141
6348
|
warnings.push({
|
|
6142
6349
|
rule: ruleRiskyProps,
|
|
@@ -6190,7 +6397,7 @@ function checkCss(css, from) {
|
|
|
6190
6397
|
}
|
|
6191
6398
|
|
|
6192
6399
|
// src/checks/source/stylelint.ts
|
|
6193
|
-
var
|
|
6400
|
+
var check20 = {
|
|
6194
6401
|
id: "source.stylelint",
|
|
6195
6402
|
name: "Print-safety CSS",
|
|
6196
6403
|
description: "Checks CSS for print-safety issues (remote URLs, rasterizing effects, and page-layer containment)",
|
|
@@ -6207,19 +6414,19 @@ var check18 = {
|
|
|
6207
6414
|
for (const file of files) {
|
|
6208
6415
|
let css;
|
|
6209
6416
|
try {
|
|
6210
|
-
css = await
|
|
6417
|
+
css = await readFile12(file, "utf8");
|
|
6211
6418
|
} catch (err) {
|
|
6212
6419
|
results.push({
|
|
6213
|
-
checkId:
|
|
6420
|
+
checkId: check20.id,
|
|
6214
6421
|
severity: "error",
|
|
6215
6422
|
message: `Cannot read stylesheet: ${err instanceof Error ? err.message : String(err)}`,
|
|
6216
6423
|
file
|
|
6217
6424
|
});
|
|
6218
6425
|
continue;
|
|
6219
6426
|
}
|
|
6220
|
-
for (const w of checkCss(css, file)) {
|
|
6427
|
+
for (const w of checkCss(css, file, { rules: ctx.config.lint.rules })) {
|
|
6221
6428
|
results.push({
|
|
6222
|
-
checkId:
|
|
6429
|
+
checkId: check20.id,
|
|
6223
6430
|
severity: w.severity,
|
|
6224
6431
|
message: `${w.rule}: ${w.message}`,
|
|
6225
6432
|
file,
|
|
@@ -6231,10 +6438,10 @@ var check18 = {
|
|
|
6231
6438
|
return results;
|
|
6232
6439
|
}
|
|
6233
6440
|
};
|
|
6234
|
-
registerCheck(
|
|
6441
|
+
registerCheck(check20);
|
|
6235
6442
|
|
|
6236
6443
|
// src/checks/source/css-ownership.ts
|
|
6237
|
-
import { readFile as
|
|
6444
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
6238
6445
|
|
|
6239
6446
|
// src/lib/style-resolver.ts
|
|
6240
6447
|
import { existsSync as existsSync8 } from "node:fs";
|
|
@@ -6302,7 +6509,7 @@ import postcss2 from "postcss";
|
|
|
6302
6509
|
import { parse as parseYaml3 } from "yaml";
|
|
6303
6510
|
var CONFIG_NAMES3 = [".gutterpress/css-contract.yaml", ".gutterpress/css-contract.yml"];
|
|
6304
6511
|
async function loadContract(configPath) {
|
|
6305
|
-
const raw = await
|
|
6512
|
+
const raw = await readFile13(configPath, "utf8");
|
|
6306
6513
|
const parsed = parseYaml3(raw);
|
|
6307
6514
|
if (parsed && typeof parsed === "object")
|
|
6308
6515
|
return parsed;
|
|
@@ -6415,7 +6622,7 @@ function checkOwnership(root, file, maps) {
|
|
|
6415
6622
|
});
|
|
6416
6623
|
return out;
|
|
6417
6624
|
}
|
|
6418
|
-
var
|
|
6625
|
+
var check21 = {
|
|
6419
6626
|
id: "source.css-ownership",
|
|
6420
6627
|
name: "CSS ownership contract",
|
|
6421
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.",
|
|
@@ -6438,7 +6645,7 @@ var check19 = {
|
|
|
6438
6645
|
} catch (err) {
|
|
6439
6646
|
return [
|
|
6440
6647
|
{
|
|
6441
|
-
checkId:
|
|
6648
|
+
checkId: check21.id,
|
|
6442
6649
|
severity: "error",
|
|
6443
6650
|
message: `Failed to parse CSS ownership contract "${configPath}": ${err instanceof Error ? err.message : String(err)}`,
|
|
6444
6651
|
file: configPath
|
|
@@ -6453,7 +6660,7 @@ var check19 = {
|
|
|
6453
6660
|
continue;
|
|
6454
6661
|
let css;
|
|
6455
6662
|
try {
|
|
6456
|
-
css = await
|
|
6663
|
+
css = await readFile13(absFile, "utf8");
|
|
6457
6664
|
} catch {
|
|
6458
6665
|
continue;
|
|
6459
6666
|
}
|
|
@@ -6480,7 +6687,7 @@ var check19 = {
|
|
|
6480
6687
|
}
|
|
6481
6688
|
for (const v of violations) {
|
|
6482
6689
|
results.push({
|
|
6483
|
-
checkId:
|
|
6690
|
+
checkId: check21.id,
|
|
6484
6691
|
severity: "warning",
|
|
6485
6692
|
message: v.message,
|
|
6486
6693
|
file: absFile,
|
|
@@ -6492,7 +6699,7 @@ var check19 = {
|
|
|
6492
6699
|
return results;
|
|
6493
6700
|
}
|
|
6494
6701
|
};
|
|
6495
|
-
registerCheck(
|
|
6702
|
+
registerCheck(check21);
|
|
6496
6703
|
|
|
6497
6704
|
// src/checks/source/local-refs.ts
|
|
6498
6705
|
import { existsSync as existsSync9 } from "node:fs";
|
|
@@ -6500,7 +6707,7 @@ import { dirname as dirname3, resolve as resolve5 } from "node:path";
|
|
|
6500
6707
|
|
|
6501
6708
|
// src/lib/asset-inline.ts
|
|
6502
6709
|
import { createHash as createHash2 } from "node:crypto";
|
|
6503
|
-
import { readFile as
|
|
6710
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
6504
6711
|
import path8 from "node:path";
|
|
6505
6712
|
import postcss3 from "postcss";
|
|
6506
6713
|
var FONT_EXTS = new Set([".woff2", ".woff", ".ttf", ".otf"]);
|
|
@@ -6565,7 +6772,7 @@ function mimeFor(ext) {
|
|
|
6565
6772
|
}
|
|
6566
6773
|
async function readOrThrow(absPath, kind, referencedFrom) {
|
|
6567
6774
|
try {
|
|
6568
|
-
return await
|
|
6775
|
+
return await readFile14(absPath);
|
|
6569
6776
|
} catch {
|
|
6570
6777
|
throw new BuildError(`Missing ${kind}: ${absPath}
|
|
6571
6778
|
referenced from ${referencedFrom}
|
|
@@ -6708,7 +6915,7 @@ async function collectStyleDependencies(projectDir, stylePaths) {
|
|
|
6708
6915
|
found.add(abs);
|
|
6709
6916
|
let source;
|
|
6710
6917
|
try {
|
|
6711
|
-
source = await
|
|
6918
|
+
source = await readFile14(abs, "utf-8");
|
|
6712
6919
|
} catch {
|
|
6713
6920
|
return;
|
|
6714
6921
|
}
|
|
@@ -6785,7 +6992,7 @@ async function inlineShapeUrls(html, baseDir) {
|
|
|
6785
6992
|
continue;
|
|
6786
6993
|
let bytes;
|
|
6787
6994
|
try {
|
|
6788
|
-
bytes = await
|
|
6995
|
+
bytes = await readFile14(abs);
|
|
6789
6996
|
} catch {
|
|
6790
6997
|
continue;
|
|
6791
6998
|
}
|
|
@@ -6803,7 +7010,7 @@ async function inlineShapeUrls(html, baseDir) {
|
|
|
6803
7010
|
}
|
|
6804
7011
|
|
|
6805
7012
|
// src/checks/source/local-ref-parser.ts
|
|
6806
|
-
import { readFile as
|
|
7013
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
6807
7014
|
function newlineOffsets(content) {
|
|
6808
7015
|
const offsets = [];
|
|
6809
7016
|
for (let i = 0;i < content.length; i++)
|
|
@@ -6872,7 +7079,7 @@ async function* renderedLocalRefs(ctx, checkId, results) {
|
|
|
6872
7079
|
for (const file of files) {
|
|
6873
7080
|
let refs;
|
|
6874
7081
|
try {
|
|
6875
|
-
refs = collectRenderedLocalRefs(await
|
|
7082
|
+
refs = collectRenderedLocalRefs(await readFile15(file, "utf8"));
|
|
6876
7083
|
} catch {
|
|
6877
7084
|
results.push(inspectionFailed(checkId, `Could not read source file: ${file}`, { file }));
|
|
6878
7085
|
continue;
|
|
@@ -6883,7 +7090,7 @@ async function* renderedLocalRefs(ctx, checkId, results) {
|
|
|
6883
7090
|
}
|
|
6884
7091
|
|
|
6885
7092
|
// src/checks/source/local-refs.ts
|
|
6886
|
-
var
|
|
7093
|
+
var check22 = {
|
|
6887
7094
|
id: "source.links.local-refs",
|
|
6888
7095
|
name: "Local Markdown References",
|
|
6889
7096
|
description: "Checks local markdown links and image refs resolve on disk",
|
|
@@ -6891,12 +7098,12 @@ var check20 = {
|
|
|
6891
7098
|
phase: "pre-build",
|
|
6892
7099
|
async run(ctx) {
|
|
6893
7100
|
const results = [];
|
|
6894
|
-
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx,
|
|
7101
|
+
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check22.id, results)) {
|
|
6895
7102
|
if (isNonFilesystemRef(ref))
|
|
6896
7103
|
continue;
|
|
6897
7104
|
const escape = kind === "image" ? proseImageRefError(ref, ctx.inputDir) : null;
|
|
6898
7105
|
if (escape) {
|
|
6899
|
-
results.push(finding(
|
|
7106
|
+
results.push(finding(check22.id, {
|
|
6900
7107
|
severity: "error",
|
|
6901
7108
|
message: escape,
|
|
6902
7109
|
file,
|
|
@@ -6906,7 +7113,7 @@ var check20 = {
|
|
|
6906
7113
|
}
|
|
6907
7114
|
if (localRefExists(ref, kind, file, ctx.inputDir))
|
|
6908
7115
|
continue;
|
|
6909
|
-
results.push(finding(
|
|
7116
|
+
results.push(finding(check22.id, {
|
|
6910
7117
|
severity: kind === "image" ? "warning" : "error",
|
|
6911
7118
|
code: kind === "image" ? "missing-image-placeholder" : undefined,
|
|
6912
7119
|
message: kind === "image" ? `Local image not found; build will substitute a magenta placeholder: ${ref}` : `Local reference not found: ${ref}`,
|
|
@@ -6931,7 +7138,7 @@ function localRefExists(ref, kind, sourceFile, projectRoot) {
|
|
|
6931
7138
|
return existsSync9(fileRelative);
|
|
6932
7139
|
}
|
|
6933
7140
|
}
|
|
6934
|
-
registerCheck(
|
|
7141
|
+
registerCheck(check22);
|
|
6935
7142
|
|
|
6936
7143
|
// src/lib/build-staging.ts
|
|
6937
7144
|
import path9 from "node:path";
|
|
@@ -7197,7 +7404,7 @@ async function createStageRoot() {
|
|
|
7197
7404
|
}
|
|
7198
7405
|
|
|
7199
7406
|
// src/checks/source/dangling-links.ts
|
|
7200
|
-
var
|
|
7407
|
+
var check23 = {
|
|
7201
7408
|
id: "source.links.dangling",
|
|
7202
7409
|
name: "Dangling Links",
|
|
7203
7410
|
description: "Flags relative links the printed book cannot open (their href is dropped at print time)",
|
|
@@ -7205,10 +7412,10 @@ var check21 = {
|
|
|
7205
7412
|
phase: "pre-build",
|
|
7206
7413
|
async run(ctx) {
|
|
7207
7414
|
const results = [];
|
|
7208
|
-
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx,
|
|
7415
|
+
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check23.id, results)) {
|
|
7209
7416
|
if (kind !== "link" || isPrintResolvableHref(ref))
|
|
7210
7417
|
continue;
|
|
7211
|
-
results.push(finding(
|
|
7418
|
+
results.push(finding(check23.id, {
|
|
7212
7419
|
severity: "warning",
|
|
7213
7420
|
code: "dangling-link",
|
|
7214
7421
|
data: { ref },
|
|
@@ -7220,11 +7427,11 @@ var check21 = {
|
|
|
7220
7427
|
return results;
|
|
7221
7428
|
}
|
|
7222
7429
|
};
|
|
7223
|
-
registerCheck(
|
|
7430
|
+
registerCheck(check23);
|
|
7224
7431
|
|
|
7225
7432
|
// src/checks/source/accessibility-alt-text.ts
|
|
7226
|
-
import { readFile as
|
|
7227
|
-
var
|
|
7433
|
+
import { readFile as readFile16 } from "node:fs/promises";
|
|
7434
|
+
var check24 = {
|
|
7228
7435
|
id: "source.accessibility.alt-text",
|
|
7229
7436
|
name: "Image Alt Text",
|
|
7230
7437
|
description: "Checks markdown images include non-empty alt text",
|
|
@@ -7237,7 +7444,7 @@ var check22 = {
|
|
|
7237
7444
|
const results = [];
|
|
7238
7445
|
for (const file of files) {
|
|
7239
7446
|
try {
|
|
7240
|
-
const content = await
|
|
7447
|
+
const content = await readFile16(file, "utf8");
|
|
7241
7448
|
const lines = content.split(`
|
|
7242
7449
|
`);
|
|
7243
7450
|
let inFence = false;
|
|
@@ -7253,7 +7460,7 @@ var check22 = {
|
|
|
7253
7460
|
if (alt.trim())
|
|
7254
7461
|
continue;
|
|
7255
7462
|
results.push({
|
|
7256
|
-
checkId:
|
|
7463
|
+
checkId: check24.id,
|
|
7257
7464
|
severity: "warning",
|
|
7258
7465
|
message: "Image is missing alt text",
|
|
7259
7466
|
file,
|
|
@@ -7278,11 +7485,11 @@ function extractImageAlts(line) {
|
|
|
7278
7485
|
}
|
|
7279
7486
|
return alts;
|
|
7280
7487
|
}
|
|
7281
|
-
registerCheck(
|
|
7488
|
+
registerCheck(check24);
|
|
7282
7489
|
|
|
7283
7490
|
// src/checks/source/accessibility-heading-order.ts
|
|
7284
|
-
import { readFile as
|
|
7285
|
-
var
|
|
7491
|
+
import { readFile as readFile17 } from "node:fs/promises";
|
|
7492
|
+
var check25 = {
|
|
7286
7493
|
id: "source.accessibility.heading-order",
|
|
7287
7494
|
name: "Heading Order",
|
|
7288
7495
|
description: "Checks markdown heading levels do not jump by more than one",
|
|
@@ -7295,11 +7502,12 @@ var check23 = {
|
|
|
7295
7502
|
const results = [];
|
|
7296
7503
|
for (const file of files) {
|
|
7297
7504
|
try {
|
|
7298
|
-
const content = await
|
|
7505
|
+
const content = await readFile17(file, "utf8");
|
|
7299
7506
|
const lines = content.split(`
|
|
7300
7507
|
`);
|
|
7301
7508
|
let inFence = false;
|
|
7302
7509
|
let prevLevel;
|
|
7510
|
+
let silencedLine = -1;
|
|
7303
7511
|
for (let i = 0;i < lines.length; i++) {
|
|
7304
7512
|
const line = lines[i] ?? "";
|
|
7305
7513
|
if (/^\s*(```|~~~)/.test(line)) {
|
|
@@ -7308,13 +7516,18 @@ var check23 = {
|
|
|
7308
7516
|
}
|
|
7309
7517
|
if (inFence)
|
|
7310
7518
|
continue;
|
|
7519
|
+
const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
|
|
7520
|
+
if (comment && parseDisableDirective(comment[1])?.includes(check25.id)) {
|
|
7521
|
+
silencedLine = i + 1;
|
|
7522
|
+
continue;
|
|
7523
|
+
}
|
|
7311
7524
|
const match = line.match(/^(#{1,6})\s+\S/);
|
|
7312
7525
|
if (!match)
|
|
7313
7526
|
continue;
|
|
7314
7527
|
const level = match[1].length;
|
|
7315
|
-
if (prevLevel != null && level > prevLevel + 1) {
|
|
7528
|
+
if (prevLevel != null && level > prevLevel + 1 && i !== silencedLine) {
|
|
7316
7529
|
results.push({
|
|
7317
|
-
checkId:
|
|
7530
|
+
checkId: check25.id,
|
|
7318
7531
|
severity: "warning",
|
|
7319
7532
|
message: `Heading level jump from h${prevLevel} to h${level}`,
|
|
7320
7533
|
file,
|
|
@@ -7328,11 +7541,11 @@ var check23 = {
|
|
|
7328
7541
|
return results;
|
|
7329
7542
|
}
|
|
7330
7543
|
};
|
|
7331
|
-
registerCheck(
|
|
7544
|
+
registerCheck(check25);
|
|
7332
7545
|
|
|
7333
7546
|
// src/checks/source/layout-markers.ts
|
|
7334
|
-
import { readFile as
|
|
7335
|
-
var
|
|
7547
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
7548
|
+
var check26 = {
|
|
7336
7549
|
id: "source.markdown.layout-markers",
|
|
7337
7550
|
name: "Layout Markers",
|
|
7338
7551
|
description: "Reports @page/@section/@chapter marker arguments Gutterpress could not understand, plus any unknown gp-* class",
|
|
@@ -7344,16 +7557,16 @@ var check24 = {
|
|
|
7344
7557
|
return [];
|
|
7345
7558
|
const results = [];
|
|
7346
7559
|
const plugins = await loadPlugins(ctx.config.extensions, ctx.inputDir, (ref, error2) => {
|
|
7347
|
-
results.push(inspectionFailed(
|
|
7560
|
+
results.push(inspectionFailed(check26.id, `Plugin "${ref}" could not be loaded, so markers it defines were not checked: ${error2.message}`));
|
|
7348
7561
|
});
|
|
7349
7562
|
const md = createMarkdownRenderer(plugins);
|
|
7350
7563
|
for (const file of files) {
|
|
7351
7564
|
try {
|
|
7352
|
-
const content = await
|
|
7565
|
+
const content = await readFile18(file, "utf8");
|
|
7353
7566
|
const env = {};
|
|
7354
7567
|
md.render(content, env);
|
|
7355
7568
|
for (const w of env.layoutWarnings ?? []) {
|
|
7356
|
-
results.push(finding(
|
|
7569
|
+
results.push(finding(check26.id, {
|
|
7357
7570
|
severity: w.severity ?? "warning",
|
|
7358
7571
|
message: w.message,
|
|
7359
7572
|
file,
|
|
@@ -7362,16 +7575,16 @@ var check24 = {
|
|
|
7362
7575
|
}));
|
|
7363
7576
|
}
|
|
7364
7577
|
} catch (error2) {
|
|
7365
|
-
results.push(inspectionFailed(
|
|
7578
|
+
results.push(inspectionFailed(check26.id, `Could not check layout markers in ${file}: ${error2 instanceof Error ? error2.message : String(error2)}`, { file }));
|
|
7366
7579
|
}
|
|
7367
7580
|
}
|
|
7368
7581
|
return results;
|
|
7369
7582
|
}
|
|
7370
7583
|
};
|
|
7371
|
-
registerCheck(
|
|
7584
|
+
registerCheck(check26);
|
|
7372
7585
|
|
|
7373
7586
|
// src/checks/source/merge-markers.ts
|
|
7374
|
-
import { readFile as
|
|
7587
|
+
import { readFile as readFile19 } from "node:fs/promises";
|
|
7375
7588
|
import path10 from "node:path";
|
|
7376
7589
|
|
|
7377
7590
|
// src/checks/asset/extensions.ts
|
|
@@ -7401,7 +7614,7 @@ var CLOSE_SENTINEL = ">>>>>>> online version";
|
|
|
7401
7614
|
function withoutOnlineTag(relPath) {
|
|
7402
7615
|
return relPath.replace(/\.online(?=\.[^./]*$|$)/, "");
|
|
7403
7616
|
}
|
|
7404
|
-
var
|
|
7617
|
+
var check27 = {
|
|
7405
7618
|
id: "source.sync.merge-markers",
|
|
7406
7619
|
name: "Combined Versions",
|
|
7407
7620
|
description: "Finds passages and files still holding two versions (yours and the online copy) after a sync",
|
|
@@ -7413,9 +7626,9 @@ var check25 = {
|
|
|
7413
7626
|
for (const file of textFiles) {
|
|
7414
7627
|
let content;
|
|
7415
7628
|
try {
|
|
7416
|
-
content = await
|
|
7629
|
+
content = await readFile19(file, "utf8");
|
|
7417
7630
|
} catch {
|
|
7418
|
-
results.push(inspectionFailed(
|
|
7631
|
+
results.push(inspectionFailed(check27.id, `Could not read source file: ${file}`, { file }));
|
|
7419
7632
|
continue;
|
|
7420
7633
|
}
|
|
7421
7634
|
if (!content.includes(OPEN_SENTINEL) && !content.includes(CLOSE_SENTINEL))
|
|
@@ -7427,7 +7640,7 @@ var check25 = {
|
|
|
7427
7640
|
const line = lines[i].endsWith("\r") ? lines[i].slice(0, -1) : lines[i];
|
|
7428
7641
|
if (line === OPEN_SENTINEL) {
|
|
7429
7642
|
inBlock = true;
|
|
7430
|
-
results.push(finding(
|
|
7643
|
+
results.push(finding(check27.id, {
|
|
7431
7644
|
severity: "error",
|
|
7432
7645
|
code: "two-versions-passage",
|
|
7433
7646
|
message: "This passage has two versions (yours and the online copy) — keep what you want, then delete the marker lines.",
|
|
@@ -7436,7 +7649,7 @@ var check25 = {
|
|
|
7436
7649
|
}));
|
|
7437
7650
|
} else if (line === CLOSE_SENTINEL) {
|
|
7438
7651
|
if (!inBlock) {
|
|
7439
|
-
results.push(finding(
|
|
7652
|
+
results.push(finding(check27.id, {
|
|
7440
7653
|
severity: "error",
|
|
7441
7654
|
code: "leftover-version-marker",
|
|
7442
7655
|
message: "This marker line is left over from combining two versions — delete it.",
|
|
@@ -7457,7 +7670,7 @@ var check25 = {
|
|
|
7457
7670
|
});
|
|
7458
7671
|
for (const sibling of siblings.sort()) {
|
|
7459
7672
|
const rel = path10.relative(ctx.inputDir, sibling).split(path10.sep).join("/");
|
|
7460
|
-
results.push(finding(
|
|
7673
|
+
results.push(finding(check27.id, {
|
|
7461
7674
|
severity: "warning",
|
|
7462
7675
|
code: "kept-both-versions",
|
|
7463
7676
|
message: `Two versions of ${withoutOnlineTag(rel)} are in your book — keep the one you want, then delete the other.`,
|
|
@@ -7465,18 +7678,18 @@ var check25 = {
|
|
|
7465
7678
|
}));
|
|
7466
7679
|
}
|
|
7467
7680
|
} catch (error2) {
|
|
7468
|
-
results.push(inspectionFailed(
|
|
7681
|
+
results.push(inspectionFailed(check27.id, `Could not scan for kept-both files: ${error2 instanceof Error ? error2.message : String(error2)}`));
|
|
7469
7682
|
}
|
|
7470
7683
|
return results;
|
|
7471
7684
|
}
|
|
7472
7685
|
};
|
|
7473
|
-
registerCheck(
|
|
7686
|
+
registerCheck(check27);
|
|
7474
7687
|
|
|
7475
7688
|
// src/checks/asset/image-file-size.ts
|
|
7476
7689
|
import { stat as stat4 } from "node:fs/promises";
|
|
7477
7690
|
|
|
7478
7691
|
// src/lib/image-inspect.ts
|
|
7479
|
-
import { stat as stat3, readFile as
|
|
7692
|
+
import { stat as stat3, readFile as readFile20 } from "node:fs/promises";
|
|
7480
7693
|
var DEFAULT_DPI = 72;
|
|
7481
7694
|
var cache = new Map;
|
|
7482
7695
|
async function inspectImage(path11) {
|
|
@@ -7491,7 +7704,7 @@ async function inspectImage(path11) {
|
|
|
7491
7704
|
return hit.info;
|
|
7492
7705
|
let info2 = null;
|
|
7493
7706
|
try {
|
|
7494
|
-
const buf = await
|
|
7707
|
+
const buf = await readFile20(path11);
|
|
7495
7708
|
info2 = parseImage(buf);
|
|
7496
7709
|
} catch {
|
|
7497
7710
|
info2 = null;
|
|
@@ -7696,7 +7909,7 @@ async function collectImageFiles(dirs, exts, ignore2 = ASSET_SCAN_IGNORE_GLOBS)
|
|
|
7696
7909
|
}
|
|
7697
7910
|
|
|
7698
7911
|
// src/checks/asset/image-file-size.ts
|
|
7699
|
-
var
|
|
7912
|
+
var check28 = {
|
|
7700
7913
|
id: "asset.image.file-size",
|
|
7701
7914
|
name: "Image File Size",
|
|
7702
7915
|
description: "Checks that image files do not exceed the maximum size limit",
|
|
@@ -7717,14 +7930,14 @@ var check26 = {
|
|
|
7717
7930
|
if (info2.size > maxSize) {
|
|
7718
7931
|
const sizeMb = (info2.size / 1e6).toFixed(1);
|
|
7719
7932
|
const maxMb = (maxSize / 1e6).toFixed(1);
|
|
7720
|
-
results.push(finding(
|
|
7933
|
+
results.push(finding(check28.id, {
|
|
7721
7934
|
severity: "warning",
|
|
7722
7935
|
message: `Image file too large: ${sizeMb}MB (max ${maxMb}MB)`,
|
|
7723
7936
|
file
|
|
7724
7937
|
}));
|
|
7725
7938
|
}
|
|
7726
7939
|
} catch {
|
|
7727
|
-
results.push(inspectionFailed(
|
|
7940
|
+
results.push(inspectionFailed(check28.id, `Could not stat image file: ${file}`, {
|
|
7728
7941
|
file
|
|
7729
7942
|
}));
|
|
7730
7943
|
}
|
|
@@ -7732,10 +7945,10 @@ var check26 = {
|
|
|
7732
7945
|
return results;
|
|
7733
7946
|
}
|
|
7734
7947
|
};
|
|
7735
|
-
registerCheck(
|
|
7948
|
+
registerCheck(check28);
|
|
7736
7949
|
|
|
7737
7950
|
// src/checks/asset/image-resolution.ts
|
|
7738
|
-
var
|
|
7951
|
+
var check29 = {
|
|
7739
7952
|
id: "asset.image.resolution",
|
|
7740
7953
|
name: "Image Resolution",
|
|
7741
7954
|
description: "Checks source image DPI from embedded density metadata",
|
|
@@ -7757,7 +7970,7 @@ var check27 = {
|
|
|
7757
7970
|
const { xDpi, yDpi } = info2;
|
|
7758
7971
|
if (xDpi > 0 && yDpi > 0 && (xDpi < minDpi || yDpi < minDpi)) {
|
|
7759
7972
|
results.push({
|
|
7760
|
-
checkId:
|
|
7973
|
+
checkId: check29.id,
|
|
7761
7974
|
severity: "warning",
|
|
7762
7975
|
message: `Image resolution too low: ${xDpi}x${yDpi} DPI (minimum ${minDpi} DPI)`,
|
|
7763
7976
|
file
|
|
@@ -7767,7 +7980,7 @@ var check27 = {
|
|
|
7767
7980
|
return results;
|
|
7768
7981
|
}
|
|
7769
7982
|
};
|
|
7770
|
-
registerCheck(
|
|
7983
|
+
registerCheck(check29);
|
|
7771
7984
|
|
|
7772
7985
|
// src/checks/asset/image-color-space.ts
|
|
7773
7986
|
var LABEL = {
|
|
@@ -7775,7 +7988,7 @@ var LABEL = {
|
|
|
7775
7988
|
gray: "Gray",
|
|
7776
7989
|
cmyk: "CMYK"
|
|
7777
7990
|
};
|
|
7778
|
-
var
|
|
7991
|
+
var check30 = {
|
|
7779
7992
|
id: "asset.image.color-space",
|
|
7780
7993
|
name: "Image Color Space",
|
|
7781
7994
|
description: "Validates image color spaces against allowed list",
|
|
@@ -7796,7 +8009,7 @@ var check28 = {
|
|
|
7796
8009
|
const cs = info2?.colorSpace;
|
|
7797
8010
|
if (cs && !allowedLower.has(cs)) {
|
|
7798
8011
|
results.push({
|
|
7799
|
-
checkId:
|
|
8012
|
+
checkId: check30.id,
|
|
7800
8013
|
severity: "warning",
|
|
7801
8014
|
message: `Image uses ${LABEL[cs] ?? cs} color space (allowed: ${allowed.join(", ")})`,
|
|
7802
8015
|
file
|
|
@@ -7806,10 +8019,10 @@ var check28 = {
|
|
|
7806
8019
|
return results;
|
|
7807
8020
|
}
|
|
7808
8021
|
};
|
|
7809
|
-
registerCheck(
|
|
8022
|
+
registerCheck(check30);
|
|
7810
8023
|
|
|
7811
8024
|
// src/checks/asset/image-alpha.ts
|
|
7812
|
-
var
|
|
8025
|
+
var check31 = {
|
|
7813
8026
|
id: "asset.image.alpha-channel",
|
|
7814
8027
|
name: "Image Alpha Channel",
|
|
7815
8028
|
description: "Checks for alpha channels in PNG/TIFF images",
|
|
@@ -7827,7 +8040,7 @@ var check29 = {
|
|
|
7827
8040
|
const info2 = await inspectImage(file);
|
|
7828
8041
|
if (info2?.hasAlpha) {
|
|
7829
8042
|
results.push({
|
|
7830
|
-
checkId:
|
|
8043
|
+
checkId: check31.id,
|
|
7831
8044
|
severity: "warning",
|
|
7832
8045
|
message: "Image contains alpha channel, which may cause print issues",
|
|
7833
8046
|
file
|
|
@@ -7837,10 +8050,10 @@ var check29 = {
|
|
|
7837
8050
|
return results;
|
|
7838
8051
|
}
|
|
7839
8052
|
};
|
|
7840
|
-
registerCheck(
|
|
8053
|
+
registerCheck(check31);
|
|
7841
8054
|
|
|
7842
8055
|
// src/checks/asset/image-tac.ts
|
|
7843
|
-
var
|
|
8056
|
+
var check32 = {
|
|
7844
8057
|
id: "asset.image.tac-raster",
|
|
7845
8058
|
name: "Image TAC (Raster)",
|
|
7846
8059
|
description: "Rasterizes and checks TAC per image using Ghostscript",
|
|
@@ -7859,7 +8072,7 @@ var check30 = {
|
|
|
7859
8072
|
const ghostscript = await resolveGhostscript();
|
|
7860
8073
|
if (!ghostscript) {
|
|
7861
8074
|
return [
|
|
7862
|
-
inspectionFailed(
|
|
8075
|
+
inspectionFailed(check32.id, "Could not inspect image ink coverage: Ghostscript executable not found")
|
|
7863
8076
|
];
|
|
7864
8077
|
}
|
|
7865
8078
|
for (const file of files) {
|
|
@@ -7877,7 +8090,7 @@ var check30 = {
|
|
|
7877
8090
|
if (nums.length === 4 && nums.every((n) => Number.isFinite(n))) {
|
|
7878
8091
|
const tac = (nums[0] + nums[1] + nums[2] + nums[3]) * 100;
|
|
7879
8092
|
if (tac > maxTac) {
|
|
7880
|
-
results.push(finding(
|
|
8093
|
+
results.push(finding(check32.id, {
|
|
7881
8094
|
severity: "warning",
|
|
7882
8095
|
message: `Image TAC exceeds limit: ${tac.toFixed(1)}% (max ${maxTac}%)`,
|
|
7883
8096
|
file,
|
|
@@ -7889,16 +8102,16 @@ var check30 = {
|
|
|
7889
8102
|
}
|
|
7890
8103
|
}
|
|
7891
8104
|
} catch {
|
|
7892
|
-
results.push(inspectionFailed(
|
|
8105
|
+
results.push(inspectionFailed(check32.id, `Could not inspect image ink coverage: ${file}`, { file }));
|
|
7893
8106
|
}
|
|
7894
8107
|
}
|
|
7895
8108
|
return results;
|
|
7896
8109
|
}
|
|
7897
8110
|
};
|
|
7898
|
-
registerCheck(
|
|
8111
|
+
registerCheck(check32);
|
|
7899
8112
|
|
|
7900
8113
|
// src/checks/asset/approved-fonts.ts
|
|
7901
|
-
var
|
|
8114
|
+
var check33 = {
|
|
7902
8115
|
id: "asset.font.approved-files",
|
|
7903
8116
|
name: "Approved Font Files",
|
|
7904
8117
|
description: "Checks font files against the approved file patterns",
|
|
@@ -7937,7 +8150,7 @@ var check31 = {
|
|
|
7937
8150
|
for (const font of allFonts) {
|
|
7938
8151
|
if (!approvedFonts.has(font)) {
|
|
7939
8152
|
results.push({
|
|
7940
|
-
checkId:
|
|
8153
|
+
checkId: check33.id,
|
|
7941
8154
|
severity: "warning",
|
|
7942
8155
|
message: "Font file not in approved list",
|
|
7943
8156
|
file: font
|
|
@@ -7947,7 +8160,7 @@ var check31 = {
|
|
|
7947
8160
|
return results;
|
|
7948
8161
|
}
|
|
7949
8162
|
};
|
|
7950
|
-
registerCheck(
|
|
8163
|
+
registerCheck(check33);
|
|
7951
8164
|
|
|
7952
8165
|
// src/checks/asset/font-license.ts
|
|
7953
8166
|
import { existsSync as existsSync10 } from "node:fs";
|
|
@@ -7962,7 +8175,7 @@ var LICENSE_NAMES = [
|
|
|
7962
8175
|
"OFL-1.1.txt",
|
|
7963
8176
|
"COPYING"
|
|
7964
8177
|
];
|
|
7965
|
-
var
|
|
8178
|
+
var check34 = {
|
|
7966
8179
|
id: "asset.font.license",
|
|
7967
8180
|
name: "Font License",
|
|
7968
8181
|
description: "Checks for license files in font directories",
|
|
@@ -7989,7 +8202,7 @@ var check32 = {
|
|
|
7989
8202
|
const hasLicense = LICENSE_NAMES.some((name) => existsSync10(resolve6(fontDir, name)));
|
|
7990
8203
|
if (!hasLicense) {
|
|
7991
8204
|
results.push({
|
|
7992
|
-
checkId:
|
|
8205
|
+
checkId: check34.id,
|
|
7993
8206
|
severity: "warning",
|
|
7994
8207
|
message: `No font license file found in directory`,
|
|
7995
8208
|
file: fontDir
|
|
@@ -7999,10 +8212,10 @@ var check32 = {
|
|
|
7999
8212
|
return results;
|
|
8000
8213
|
}
|
|
8001
8214
|
};
|
|
8002
|
-
registerCheck(
|
|
8215
|
+
registerCheck(check34);
|
|
8003
8216
|
|
|
8004
8217
|
// src/checks/heuristic/text-density.ts
|
|
8005
|
-
var
|
|
8218
|
+
var check35 = {
|
|
8006
8219
|
id: "heuristic.whitespace.text-density",
|
|
8007
8220
|
name: "Text Density",
|
|
8008
8221
|
description: "Checks characters-per-page ratio",
|
|
@@ -8031,7 +8244,7 @@ var check33 = {
|
|
|
8031
8244
|
});
|
|
8032
8245
|
if (lowPages.length > 0) {
|
|
8033
8246
|
results.push({
|
|
8034
|
-
checkId:
|
|
8247
|
+
checkId: check35.id,
|
|
8035
8248
|
severity: "info",
|
|
8036
8249
|
message: `Low text density on pages: ${lowPages.join(", ")} (below ${range.min} chars)`,
|
|
8037
8250
|
file: ctx.pdfPath
|
|
@@ -8039,7 +8252,7 @@ var check33 = {
|
|
|
8039
8252
|
}
|
|
8040
8253
|
if (highPages.length > 0) {
|
|
8041
8254
|
results.push({
|
|
8042
|
-
checkId:
|
|
8255
|
+
checkId: check35.id,
|
|
8043
8256
|
severity: "info",
|
|
8044
8257
|
message: `High text density on pages: ${highPages.join(", ")} (above ${range.max} chars)`,
|
|
8045
8258
|
file: ctx.pdfPath
|
|
@@ -8048,11 +8261,11 @@ var check33 = {
|
|
|
8048
8261
|
return results;
|
|
8049
8262
|
}
|
|
8050
8263
|
};
|
|
8051
|
-
registerCheck(
|
|
8264
|
+
registerCheck(check35);
|
|
8052
8265
|
|
|
8053
8266
|
// src/checks/heuristic/section-density.ts
|
|
8054
|
-
import { readFile as
|
|
8055
|
-
var
|
|
8267
|
+
import { readFile as readFile21 } from "node:fs/promises";
|
|
8268
|
+
var check36 = {
|
|
8056
8269
|
id: "heuristic.chunking.section-density",
|
|
8057
8270
|
name: "Section Density",
|
|
8058
8271
|
description: "Checks heading/paragraph/callout density from source Markdown",
|
|
@@ -8068,7 +8281,7 @@ var check34 = {
|
|
|
8068
8281
|
const results = [];
|
|
8069
8282
|
for (const file of files) {
|
|
8070
8283
|
try {
|
|
8071
|
-
const content = await
|
|
8284
|
+
const content = await readFile21(file, "utf8");
|
|
8072
8285
|
const lines = content.split(`
|
|
8073
8286
|
`);
|
|
8074
8287
|
let currentSectionStart = 0;
|
|
@@ -8078,7 +8291,7 @@ var check34 = {
|
|
|
8078
8291
|
const line = lines[i];
|
|
8079
8292
|
if (/^#{1,6}\s/.test(line)) {
|
|
8080
8293
|
if (paragraphCount > maxParas) {
|
|
8081
|
-
results.push(finding(
|
|
8294
|
+
results.push(finding(check36.id, {
|
|
8082
8295
|
severity: "info",
|
|
8083
8296
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
8084
8297
|
file,
|
|
@@ -8098,7 +8311,7 @@ var check34 = {
|
|
|
8098
8311
|
}
|
|
8099
8312
|
}
|
|
8100
8313
|
if (paragraphCount > maxParas) {
|
|
8101
|
-
results.push(finding(
|
|
8314
|
+
results.push(finding(check36.id, {
|
|
8102
8315
|
severity: "info",
|
|
8103
8316
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
8104
8317
|
file,
|
|
@@ -8106,7 +8319,7 @@ var check34 = {
|
|
|
8106
8319
|
}));
|
|
8107
8320
|
}
|
|
8108
8321
|
} catch {
|
|
8109
|
-
results.push(inspectionFailed(
|
|
8322
|
+
results.push(inspectionFailed(check36.id, `Could not read source file: ${file}`, {
|
|
8110
8323
|
file
|
|
8111
8324
|
}));
|
|
8112
8325
|
}
|
|
@@ -8114,10 +8327,10 @@ var check34 = {
|
|
|
8114
8327
|
return results;
|
|
8115
8328
|
}
|
|
8116
8329
|
};
|
|
8117
|
-
registerCheck(
|
|
8330
|
+
registerCheck(check36);
|
|
8118
8331
|
|
|
8119
8332
|
// src/checks/heuristic/layer-count.ts
|
|
8120
|
-
var
|
|
8333
|
+
var check37 = {
|
|
8121
8334
|
id: "heuristic.decoration.layer-count",
|
|
8122
8335
|
name: "Layer Count",
|
|
8123
8336
|
description: "Counts image objects per page",
|
|
@@ -8142,7 +8355,7 @@ var check35 = {
|
|
|
8142
8355
|
heavyPages.sort((a, b) => a - b);
|
|
8143
8356
|
return [
|
|
8144
8357
|
{
|
|
8145
|
-
checkId:
|
|
8358
|
+
checkId: check37.id,
|
|
8146
8359
|
severity: "info",
|
|
8147
8360
|
message: `Pages with many image layers (>${maxLayers}): ${heavyPages.join(", ")}`,
|
|
8148
8361
|
file: ctx.pdfPath
|
|
@@ -8152,10 +8365,10 @@ var check35 = {
|
|
|
8152
8365
|
return [];
|
|
8153
8366
|
}
|
|
8154
8367
|
};
|
|
8155
|
-
registerCheck(
|
|
8368
|
+
registerCheck(check37);
|
|
8156
8369
|
|
|
8157
8370
|
// src/checks/heuristic/placement-variance.ts
|
|
8158
|
-
var
|
|
8371
|
+
var check38 = {
|
|
8159
8372
|
id: "heuristic.layout.placement-variance",
|
|
8160
8373
|
name: "Placement Variance",
|
|
8161
8374
|
description: "Analyzes text baseline coordinates for layout consistency",
|
|
@@ -8173,7 +8386,7 @@ var check36 = {
|
|
|
8173
8386
|
const uniqueX = new Set(positions.map((p) => Math.round(p.x)));
|
|
8174
8387
|
return [
|
|
8175
8388
|
{
|
|
8176
|
-
checkId:
|
|
8389
|
+
checkId: check38.id,
|
|
8177
8390
|
severity: "info",
|
|
8178
8391
|
message: `Layout analysis: ${uniqueX.size} unique horizontal text positions across ${positions.length} text blocks.`,
|
|
8179
8392
|
file: ctx.pdfPath
|
|
@@ -8181,21 +8394,21 @@ var check36 = {
|
|
|
8181
8394
|
];
|
|
8182
8395
|
}
|
|
8183
8396
|
};
|
|
8184
|
-
registerCheck(
|
|
8397
|
+
registerCheck(check38);
|
|
8185
8398
|
|
|
8186
8399
|
// src/checks/tool-check.ts
|
|
8187
8400
|
async function checkToolAvailability(config, opts = {}) {
|
|
8188
8401
|
const { checks: checks2 } = selectChecks(opts, config);
|
|
8189
8402
|
const toolToChecks = new Map;
|
|
8190
|
-
for (const
|
|
8191
|
-
if (!
|
|
8403
|
+
for (const check39 of checks2) {
|
|
8404
|
+
if (!check39.requiredTools?.length)
|
|
8192
8405
|
continue;
|
|
8193
|
-
for (const tool of
|
|
8406
|
+
for (const tool of check39.requiredTools) {
|
|
8194
8407
|
const existing = toolToChecks.get(tool);
|
|
8195
8408
|
if (existing) {
|
|
8196
|
-
existing.push(
|
|
8409
|
+
existing.push(check39.id);
|
|
8197
8410
|
} else {
|
|
8198
|
-
toolToChecks.set(tool, [
|
|
8411
|
+
toolToChecks.set(tool, [check39.id]);
|
|
8199
8412
|
}
|
|
8200
8413
|
}
|
|
8201
8414
|
}
|
|
@@ -8211,11 +8424,11 @@ async function checkToolAvailability(config, opts = {}) {
|
|
|
8211
8424
|
const missing = results.filter((r) => !r.found).map((r) => r.tool);
|
|
8212
8425
|
const missingSet = new Set(missing);
|
|
8213
8426
|
const skippedChecks = [];
|
|
8214
|
-
for (const
|
|
8215
|
-
if (!
|
|
8427
|
+
for (const check39 of checks2) {
|
|
8428
|
+
if (!check39.requiredTools?.length)
|
|
8216
8429
|
continue;
|
|
8217
|
-
if (
|
|
8218
|
-
skippedChecks.push(
|
|
8430
|
+
if (check39.requiredTools.some((t) => missingSet.has(t))) {
|
|
8431
|
+
skippedChecks.push(check39.id);
|
|
8219
8432
|
}
|
|
8220
8433
|
}
|
|
8221
8434
|
return { available, missing, skippedChecks, toolToChecks };
|
|
@@ -8254,7 +8467,7 @@ function artifactName(title, format) {
|
|
|
8254
8467
|
var BOOK_HTML_FILENAME = "book.html";
|
|
8255
8468
|
|
|
8256
8469
|
// src/lib/markdown/index.ts
|
|
8257
|
-
import { readdir as readdir3, readFile as
|
|
8470
|
+
import { readdir as readdir3, readFile as readFile22, writeFile as writeFile2, mkdir as mkdir4 } from "node:fs/promises";
|
|
8258
8471
|
|
|
8259
8472
|
// src/lib/remote-auth/sync-messages.ts
|
|
8260
8473
|
function isOnlineSibling(filepath) {
|
|
@@ -8333,7 +8546,8 @@ ${projectCss.trim()}` : null
|
|
|
8333
8546
|
`);
|
|
8334
8547
|
const preloadTags = (opts.preloadImages ?? []).map((href) => `
|
|
8335
8548
|
<link rel="preload" as="image" href="${href}">`).join("");
|
|
8336
|
-
const
|
|
8549
|
+
const lastPage = opts.print && lastPageMode(opts.print);
|
|
8550
|
+
const printAttrs = (opts.print?.signature && opts.print.signature > 1 ? ` data-gp-signature="${opts.print.signature}"` : "") + (lastPage ? ` data-gp-last-page="${lastPage}"` : "");
|
|
8337
8551
|
return `<!DOCTYPE html>
|
|
8338
8552
|
<html lang="en"${printAttrs}>
|
|
8339
8553
|
<head>
|
|
@@ -8393,7 +8607,7 @@ ${css.trim()}
|
|
|
8393
8607
|
}
|
|
8394
8608
|
return assembleBookHtml({
|
|
8395
8609
|
files,
|
|
8396
|
-
readText: (relPath) =>
|
|
8610
|
+
readText: (relPath) => readFile22(join2(inputDir, canonicalChapterId(relPath)), "utf-8"),
|
|
8397
8611
|
projectCss: inlined.css,
|
|
8398
8612
|
preloadImages: cssAssetCopies.map((c) => c.to),
|
|
8399
8613
|
title: opts.title,
|
|
@@ -8425,7 +8639,7 @@ async function renderChaptersToFile(inputDir, outDir, opts = {}) {
|
|
|
8425
8639
|
}
|
|
8426
8640
|
|
|
8427
8641
|
// src/lib/markdownlint-fix.ts
|
|
8428
|
-
import { readFile as
|
|
8642
|
+
import { readFile as readFile23, writeFile as writeFile3 } from "node:fs/promises";
|
|
8429
8643
|
import { applyFixes } from "markdownlint";
|
|
8430
8644
|
import { lint as lint2 } from "markdownlint/sync";
|
|
8431
8645
|
async function uncommittedAmong(inputDir, files) {
|
|
@@ -8465,7 +8679,7 @@ async function applyMarkdownlintFixes(ctx) {
|
|
|
8465
8679
|
for (const [file, violations] of Object.entries(results)) {
|
|
8466
8680
|
if (!Array.isArray(violations) || violations.length === 0)
|
|
8467
8681
|
continue;
|
|
8468
|
-
const before = await
|
|
8682
|
+
const before = await readFile23(file, "utf8");
|
|
8469
8683
|
const after = applyFixes(before, violations);
|
|
8470
8684
|
if (after !== before)
|
|
8471
8685
|
pending.push({ file, content: after });
|
|
@@ -8574,25 +8788,32 @@ async function runChecks(ctx, opts = {}) {
|
|
|
8574
8788
|
}
|
|
8575
8789
|
const release = retainPdfCache();
|
|
8576
8790
|
try {
|
|
8577
|
-
for (const
|
|
8791
|
+
for (const check39 of checks2) {
|
|
8578
8792
|
try {
|
|
8579
|
-
|
|
8580
|
-
const severityOverride = getCheckSeverityOverride(
|
|
8793
|
+
let results = await check39.run(ctx);
|
|
8794
|
+
const severityOverride = getCheckSeverityOverride(check39.id, ctx.config);
|
|
8581
8795
|
if (severityOverride) {
|
|
8582
8796
|
for (const r of results) {
|
|
8583
8797
|
r.severity = severityOverride;
|
|
8584
8798
|
}
|
|
8585
8799
|
}
|
|
8800
|
+
const lintLevel = ruleLevel(ctx.config.lint.rules, check39.id);
|
|
8801
|
+
if (lintLevel === "off")
|
|
8802
|
+
results = [];
|
|
8803
|
+
else if (lintLevel) {
|
|
8804
|
+
for (const r of results)
|
|
8805
|
+
r.severity = lintLevel === "warn" ? "warning" : "error";
|
|
8806
|
+
}
|
|
8586
8807
|
if (results.length === 0) {
|
|
8587
|
-
passed.push(
|
|
8808
|
+
passed.push(check39.id);
|
|
8588
8809
|
} else {
|
|
8589
8810
|
allResults.push(...results);
|
|
8590
8811
|
}
|
|
8591
8812
|
} catch (err) {
|
|
8592
8813
|
allResults.push({
|
|
8593
|
-
checkId:
|
|
8814
|
+
checkId: check39.id,
|
|
8594
8815
|
severity: "error",
|
|
8595
|
-
message: `Check "${
|
|
8816
|
+
message: `Check "${check39.id}" threw: ${err instanceof Error ? err.message : String(err)}`
|
|
8596
8817
|
});
|
|
8597
8818
|
}
|
|
8598
8819
|
}
|
|
@@ -8884,7 +9105,7 @@ async function executeValidation(args) {
|
|
|
8884
9105
|
};
|
|
8885
9106
|
if (args.fix) {
|
|
8886
9107
|
const selected = selectChecks(runnerOptions, config).checks;
|
|
8887
|
-
if (selected.some((
|
|
9108
|
+
if (selected.some((check39) => check39.id === MARKDOWNLINT_CHECK_ID)) {
|
|
8888
9109
|
await applyMarkdownlintFixes(context);
|
|
8889
9110
|
} else {
|
|
8890
9111
|
log.warn(`--fix: ${MARKDOWNLINT_CHECK_ID} is not part of this run — no files were fixed.`);
|
|
@@ -8971,7 +9192,7 @@ import { randomBytes } from "node:crypto";
|
|
|
8971
9192
|
|
|
8972
9193
|
// src/lib/build-fingerprint.ts
|
|
8973
9194
|
import * as fs from "node:fs";
|
|
8974
|
-
import { mkdir as mkdir5, readFile as
|
|
9195
|
+
import { mkdir as mkdir5, readFile as readFile24, writeFile as writeFile4 } from "node:fs/promises";
|
|
8975
9196
|
import { createHash as createHash4 } from "node:crypto";
|
|
8976
9197
|
import path12 from "node:path";
|
|
8977
9198
|
import git from "isomorphic-git";
|
|
@@ -9058,8 +9279,8 @@ async function getEngineBundleHash() {
|
|
|
9058
9279
|
getAssetPath("engine/gutterpress-agent.js")
|
|
9059
9280
|
]);
|
|
9060
9281
|
const [viewerBytes, agentBytes] = await Promise.all([
|
|
9061
|
-
|
|
9062
|
-
|
|
9282
|
+
readFile24(viewerPath),
|
|
9283
|
+
readFile24(agentPath)
|
|
9063
9284
|
]);
|
|
9064
9285
|
const hash = createHash4("sha256");
|
|
9065
9286
|
hash.update(viewerBytes);
|
|
@@ -9121,7 +9342,8 @@ async function writeBuildFingerprint(input) {
|
|
|
9121
9342
|
requestedFlavor: input.pdfx.requestedFlavor,
|
|
9122
9343
|
resolvedFlavor: input.pdfx.resolvedFlavor,
|
|
9123
9344
|
iccPath: input.pdfx.iccPath,
|
|
9124
|
-
stripAnnotations: input.pdfx.stripAnnotations
|
|
9345
|
+
stripAnnotations: input.pdfx.stripAnnotations,
|
|
9346
|
+
blackText: input.pdfx.blackText ?? null
|
|
9125
9347
|
},
|
|
9126
9348
|
outputDir: input.recordedOutputDir ? path12.resolve(input.recordedOutputDir) : outputDir
|
|
9127
9349
|
},
|
|
@@ -9133,261 +9355,6 @@ async function writeBuildFingerprint(input) {
|
|
|
9133
9355
|
return outPath;
|
|
9134
9356
|
}
|
|
9135
9357
|
|
|
9136
|
-
// src/lib/black-text-gray.ts
|
|
9137
|
-
var MAX_CHANNEL = 0.12;
|
|
9138
|
-
var MAX_SPREAD = 0.04;
|
|
9139
|
-
function isNearBlack([r, g, b]) {
|
|
9140
|
-
const max = Math.max(r, g, b);
|
|
9141
|
-
return max <= MAX_CHANNEL && max - Math.min(r, g, b) <= MAX_SPREAD;
|
|
9142
|
-
}
|
|
9143
|
-
var WS = new Set([0, 9, 10, 12, 13, 32]);
|
|
9144
|
-
var DELIM = new Set("()<>[]{}/%".split("").map((c) => c.charCodeAt(0)));
|
|
9145
|
-
function* tokenize(s) {
|
|
9146
|
-
let i = 0;
|
|
9147
|
-
const n = s.length;
|
|
9148
|
-
while (i < n) {
|
|
9149
|
-
const c = s.charCodeAt(i);
|
|
9150
|
-
if (WS.has(c)) {
|
|
9151
|
-
i++;
|
|
9152
|
-
continue;
|
|
9153
|
-
}
|
|
9154
|
-
const start = i;
|
|
9155
|
-
if (c === 37) {
|
|
9156
|
-
while (i < n && s[i] !== `
|
|
9157
|
-
` && s[i] !== "\r")
|
|
9158
|
-
i++;
|
|
9159
|
-
continue;
|
|
9160
|
-
}
|
|
9161
|
-
if (c === 40) {
|
|
9162
|
-
let depth = 0;
|
|
9163
|
-
while (i < n) {
|
|
9164
|
-
const d = s[i++];
|
|
9165
|
-
if (d === "\\")
|
|
9166
|
-
i++;
|
|
9167
|
-
else if (d === "(")
|
|
9168
|
-
depth++;
|
|
9169
|
-
else if (d === ")" && --depth === 0)
|
|
9170
|
-
break;
|
|
9171
|
-
}
|
|
9172
|
-
yield { text: s.slice(start, i), start, end: i, op: false };
|
|
9173
|
-
continue;
|
|
9174
|
-
}
|
|
9175
|
-
if (c === 60 && s[i + 1] === "<") {
|
|
9176
|
-
i += 2;
|
|
9177
|
-
yield { text: "<<", start, end: i, op: false };
|
|
9178
|
-
continue;
|
|
9179
|
-
}
|
|
9180
|
-
if (c === 62 && s[i + 1] === ">") {
|
|
9181
|
-
i += 2;
|
|
9182
|
-
yield { text: ">>", start, end: i, op: false };
|
|
9183
|
-
continue;
|
|
9184
|
-
}
|
|
9185
|
-
if (c === 60) {
|
|
9186
|
-
while (i < n && s[i] !== ">")
|
|
9187
|
-
i++;
|
|
9188
|
-
i++;
|
|
9189
|
-
yield { text: s.slice(start, i), start, end: i, op: false };
|
|
9190
|
-
continue;
|
|
9191
|
-
}
|
|
9192
|
-
if (c === 91 || c === 93 || c === 62 || c === 123 || c === 125 || c === 41) {
|
|
9193
|
-
i++;
|
|
9194
|
-
yield { text: s[start], start, end: i, op: false };
|
|
9195
|
-
continue;
|
|
9196
|
-
}
|
|
9197
|
-
i++;
|
|
9198
|
-
while (i < n && !WS.has(s.charCodeAt(i)) && !DELIM.has(s.charCodeAt(i)))
|
|
9199
|
-
i++;
|
|
9200
|
-
const text = s.slice(start, i);
|
|
9201
|
-
const isOperand = c === 47 || /^[+-]?(\d+\.?\d*|\.\d+)$/.test(text);
|
|
9202
|
-
yield { text, start, end: i, op: !isOperand };
|
|
9203
|
-
if (text === "ID") {
|
|
9204
|
-
const m = /\sEI(?=\s|$)/g;
|
|
9205
|
-
m.lastIndex = i;
|
|
9206
|
-
const hit = m.exec(s);
|
|
9207
|
-
i = hit ? hit.index + hit[0].length : n;
|
|
9208
|
-
}
|
|
9209
|
-
}
|
|
9210
|
-
}
|
|
9211
|
-
function rewriteBlackText(src) {
|
|
9212
|
-
let fill = null;
|
|
9213
|
-
let stroke = null;
|
|
9214
|
-
const stack = [];
|
|
9215
|
-
let inText = false;
|
|
9216
|
-
let fillDirty = false;
|
|
9217
|
-
let strokeDirty = false;
|
|
9218
|
-
const edits = [];
|
|
9219
|
-
let operands = [];
|
|
9220
|
-
const rgbOf = () => {
|
|
9221
|
-
if (operands.length !== 3)
|
|
9222
|
-
return null;
|
|
9223
|
-
const v = operands.map((t) => Number(t.text));
|
|
9224
|
-
return v.some((x) => Number.isNaN(x)) ? null : { rgb: v, raw: operands.map((t) => t.text).join(" ") };
|
|
9225
|
-
};
|
|
9226
|
-
for (const t of tokenize(src)) {
|
|
9227
|
-
if (!t.op) {
|
|
9228
|
-
operands.push(t);
|
|
9229
|
-
continue;
|
|
9230
|
-
}
|
|
9231
|
-
const op = t.text;
|
|
9232
|
-
switch (op) {
|
|
9233
|
-
case "q":
|
|
9234
|
-
stack.push([fill, stroke]);
|
|
9235
|
-
break;
|
|
9236
|
-
case "Q": {
|
|
9237
|
-
const p = stack.pop();
|
|
9238
|
-
if (p)
|
|
9239
|
-
[fill, stroke] = p;
|
|
9240
|
-
break;
|
|
9241
|
-
}
|
|
9242
|
-
case "BT":
|
|
9243
|
-
inText = true;
|
|
9244
|
-
fillDirty = strokeDirty = false;
|
|
9245
|
-
{
|
|
9246
|
-
let ins = "";
|
|
9247
|
-
if (fill && isNearBlack(fill.rgb)) {
|
|
9248
|
-
ins += " 0 g";
|
|
9249
|
-
fillDirty = true;
|
|
9250
|
-
}
|
|
9251
|
-
if (stroke && isNearBlack(stroke.rgb)) {
|
|
9252
|
-
ins += " 0 G";
|
|
9253
|
-
strokeDirty = true;
|
|
9254
|
-
}
|
|
9255
|
-
if (ins)
|
|
9256
|
-
edits.push({ start: t.end, end: t.end, text: ins });
|
|
9257
|
-
}
|
|
9258
|
-
break;
|
|
9259
|
-
case "ET":
|
|
9260
|
-
if (inText) {
|
|
9261
|
-
let ins = "";
|
|
9262
|
-
if (fillDirty && fill)
|
|
9263
|
-
ins += `${fill.raw} rg `;
|
|
9264
|
-
if (strokeDirty && stroke)
|
|
9265
|
-
ins += `${stroke.raw} RG `;
|
|
9266
|
-
if (ins)
|
|
9267
|
-
edits.push({ start: t.start, end: t.start, text: ins });
|
|
9268
|
-
}
|
|
9269
|
-
inText = false;
|
|
9270
|
-
break;
|
|
9271
|
-
case "rg":
|
|
9272
|
-
case "RG": {
|
|
9273
|
-
const c = rgbOf();
|
|
9274
|
-
const isFill = op === "rg";
|
|
9275
|
-
if (isFill)
|
|
9276
|
-
fill = c;
|
|
9277
|
-
else
|
|
9278
|
-
stroke = c;
|
|
9279
|
-
if (inText && c && isNearBlack(c.rgb)) {
|
|
9280
|
-
edits.push({ start: operands[0].start, end: t.end, text: isFill ? "0 g" : "0 G" });
|
|
9281
|
-
if (isFill)
|
|
9282
|
-
fillDirty = true;
|
|
9283
|
-
else
|
|
9284
|
-
strokeDirty = true;
|
|
9285
|
-
} else if (inText) {
|
|
9286
|
-
if (isFill)
|
|
9287
|
-
fillDirty = false;
|
|
9288
|
-
else
|
|
9289
|
-
strokeDirty = false;
|
|
9290
|
-
}
|
|
9291
|
-
break;
|
|
9292
|
-
}
|
|
9293
|
-
case "g":
|
|
9294
|
-
case "k":
|
|
9295
|
-
case "sc":
|
|
9296
|
-
case "scn":
|
|
9297
|
-
case "cs":
|
|
9298
|
-
fill = null;
|
|
9299
|
-
if (inText)
|
|
9300
|
-
fillDirty = false;
|
|
9301
|
-
break;
|
|
9302
|
-
case "G":
|
|
9303
|
-
case "K":
|
|
9304
|
-
case "SC":
|
|
9305
|
-
case "SCN":
|
|
9306
|
-
case "CS":
|
|
9307
|
-
stroke = null;
|
|
9308
|
-
if (inText)
|
|
9309
|
-
strokeDirty = false;
|
|
9310
|
-
break;
|
|
9311
|
-
}
|
|
9312
|
-
operands = [];
|
|
9313
|
-
}
|
|
9314
|
-
return applyEdits(src, edits);
|
|
9315
|
-
}
|
|
9316
|
-
function applyEdits(s, edits) {
|
|
9317
|
-
if (!edits.length)
|
|
9318
|
-
return s;
|
|
9319
|
-
let out = "";
|
|
9320
|
-
let pos = 0;
|
|
9321
|
-
for (const e of edits) {
|
|
9322
|
-
out += s.slice(pos, e.start) + e.text;
|
|
9323
|
-
pos = e.end;
|
|
9324
|
-
}
|
|
9325
|
-
return out + s.slice(pos);
|
|
9326
|
-
}
|
|
9327
|
-
async function blackTextToGray(pdf) {
|
|
9328
|
-
const { PDFDocument, PDFName, PDFRawStream, PDFArray, PDFRef, decodePDFRawStream } = await import("pdf-lib");
|
|
9329
|
-
const doc = await PDFDocument.load(pdf, { updateMetadata: false });
|
|
9330
|
-
const ctx = doc.context;
|
|
9331
|
-
const SKIP = ["Filter", "DecodeParms", "Length"].map((k) => PDFName.of(k));
|
|
9332
|
-
let changed = false;
|
|
9333
|
-
const decode = (s) => {
|
|
9334
|
-
try {
|
|
9335
|
-
return Buffer.from(decodePDFRawStream(s).decode()).toString("latin1");
|
|
9336
|
-
} catch {
|
|
9337
|
-
return null;
|
|
9338
|
-
}
|
|
9339
|
-
};
|
|
9340
|
-
const replace = (ref, old, text) => {
|
|
9341
|
-
const entries = {};
|
|
9342
|
-
for (const [k, v] of old.dict.entries())
|
|
9343
|
-
if (!SKIP.includes(k))
|
|
9344
|
-
entries[k.decodeText()] = v;
|
|
9345
|
-
ctx.assign(ref, ctx.flateStream(Buffer.from(text, "latin1"), entries));
|
|
9346
|
-
changed = true;
|
|
9347
|
-
};
|
|
9348
|
-
for (const [ref, obj] of ctx.enumerateIndirectObjects()) {
|
|
9349
|
-
if (!(obj instanceof PDFRawStream))
|
|
9350
|
-
continue;
|
|
9351
|
-
if (obj.dict.get(PDFName.of("Subtype")) !== PDFName.of("Form"))
|
|
9352
|
-
continue;
|
|
9353
|
-
const text = decode(obj);
|
|
9354
|
-
if (text === null)
|
|
9355
|
-
continue;
|
|
9356
|
-
const next = rewriteBlackText(text);
|
|
9357
|
-
if (next !== text)
|
|
9358
|
-
replace(ref, obj, next);
|
|
9359
|
-
}
|
|
9360
|
-
for (const page of doc.getPages()) {
|
|
9361
|
-
const contents = page.node.Contents();
|
|
9362
|
-
const refs = [];
|
|
9363
|
-
if (contents instanceof PDFArray) {
|
|
9364
|
-
for (let i = 0;i < contents.size(); i++) {
|
|
9365
|
-
const r = contents.get(i);
|
|
9366
|
-
if (r instanceof PDFRef)
|
|
9367
|
-
refs.push(r);
|
|
9368
|
-
}
|
|
9369
|
-
} else if (contents instanceof PDFRawStream) {
|
|
9370
|
-
const r = page.node.get(PDFName.of("Contents"));
|
|
9371
|
-
if (r instanceof PDFRef)
|
|
9372
|
-
refs.push(r);
|
|
9373
|
-
}
|
|
9374
|
-
const streams = refs.map((r) => ctx.lookup(r));
|
|
9375
|
-
if (!refs.length || !streams.every((s) => s instanceof PDFRawStream))
|
|
9376
|
-
continue;
|
|
9377
|
-
const parts = streams.map((s) => decode(s));
|
|
9378
|
-
if (parts.some((p) => p === null))
|
|
9379
|
-
continue;
|
|
9380
|
-
const joined = parts.join(`
|
|
9381
|
-
`);
|
|
9382
|
-
const next = rewriteBlackText(joined);
|
|
9383
|
-
if (next === joined)
|
|
9384
|
-
continue;
|
|
9385
|
-
replace(refs[0], streams[0], next);
|
|
9386
|
-
page.node.set(PDFName.of("Contents"), refs[0]);
|
|
9387
|
-
}
|
|
9388
|
-
return changed ? doc.save({ updateFieldAppearances: false }) : pdf;
|
|
9389
|
-
}
|
|
9390
|
-
|
|
9391
9358
|
// src/lib/build-preflight.ts
|
|
9392
9359
|
var INSTALL_HINTS2 = {
|
|
9393
9360
|
gs: INSTALL_HINTS.gs.body,
|
|
@@ -9662,7 +9629,8 @@ class HtmlOutput {
|
|
|
9662
9629
|
requestedFlavor: null,
|
|
9663
9630
|
resolvedFlavor: config.pdfx.flavor,
|
|
9664
9631
|
iccPath: null,
|
|
9665
|
-
stripAnnotations: null
|
|
9632
|
+
stripAnnotations: null,
|
|
9633
|
+
blackText: null
|
|
9666
9634
|
}
|
|
9667
9635
|
}, `Wrote: ${path13.join(outDir, BOOK_HTML)}`, { htmlPath: htmlFile, pdfPath: null });
|
|
9668
9636
|
}
|
|
@@ -9682,12 +9650,12 @@ class PdfOutput {
|
|
|
9682
9650
|
const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
|
|
9683
9651
|
await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
|
|
9684
9652
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
9685
|
-
const { buildNativePdf } = await import("./engine-
|
|
9653
|
+
const { buildNativePdf } = await import("./engine-zdb41ay1.js");
|
|
9686
9654
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
9687
9655
|
title: config.title,
|
|
9688
9656
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
9689
9657
|
signature: config.print.signature,
|
|
9690
|
-
|
|
9658
|
+
lastPage: lastPageMode(config.print),
|
|
9691
9659
|
allowShrink: opts.allowShrink
|
|
9692
9660
|
}, browser);
|
|
9693
9661
|
for (const d of engineDiagnostics)
|
|
@@ -9746,7 +9714,8 @@ class PdfOutput {
|
|
|
9746
9714
|
requestedFlavor: pdfxMode ?? null,
|
|
9747
9715
|
resolvedFlavor: config.pdfx.flavor,
|
|
9748
9716
|
iccPath: effectiveIccPath,
|
|
9749
|
-
stripAnnotations: shouldStripAnnotations
|
|
9717
|
+
stripAnnotations: shouldStripAnnotations,
|
|
9718
|
+
blackText: pdfxMode ? config.pdfx.blackText : null
|
|
9750
9719
|
}
|
|
9751
9720
|
}, `Wrote: ${reportedPdf}`, { htmlPath: htmlFile, pdfPath: reportedPdf }, pdfName, engineDiagnostics);
|
|
9752
9721
|
} finally {
|
|
@@ -9816,7 +9785,7 @@ function openPath(filePath) {
|
|
|
9816
9785
|
import path16 from "path";
|
|
9817
9786
|
import { tmpdir } from "os";
|
|
9818
9787
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
9819
|
-
import { readdir as readdir4, readFile as
|
|
9788
|
+
import { readdir as readdir4, readFile as readFile25, writeFile as writeFile5 } from "node:fs/promises";
|
|
9820
9789
|
|
|
9821
9790
|
// src/utils/file-utils.ts
|
|
9822
9791
|
import { promises as fs3 } from "fs";
|
|
@@ -10365,7 +10334,7 @@ async function cleanupOrphanTempDirs() {
|
|
|
10365
10334
|
const dir = path16.join(TEMP_DIR_BASE, entry);
|
|
10366
10335
|
const pidFile = path16.join(dir, PID_FILE_NAME);
|
|
10367
10336
|
try {
|
|
10368
|
-
const raw = await
|
|
10337
|
+
const raw = await readFile25(pidFile, "utf8");
|
|
10369
10338
|
const pid = parseInt(raw.trim(), 10);
|
|
10370
10339
|
if (!isProcessAlive(pid)) {
|
|
10371
10340
|
debug(`Removing orphan temp dir ${dir} (pid ${pid} not alive)`);
|
|
@@ -10472,7 +10441,7 @@ function createServerState(inputPath, tempDir, config, options) {
|
|
|
10472
10441
|
import http from "node:http";
|
|
10473
10442
|
import net from "node:net";
|
|
10474
10443
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
|
|
10475
|
-
import { readFile as
|
|
10444
|
+
import { readFile as readFile26, stat as stat5 } from "node:fs/promises";
|
|
10476
10445
|
import path17 from "path";
|
|
10477
10446
|
import { WebSocket, WebSocketServer } from "ws";
|
|
10478
10447
|
var HMR_PATH = "/__gutterpress-hmr";
|
|
@@ -10541,7 +10510,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
10541
10510
|
}
|
|
10542
10511
|
let data;
|
|
10543
10512
|
try {
|
|
10544
|
-
data = await
|
|
10513
|
+
data = await readFile26(filePath);
|
|
10545
10514
|
} catch {
|
|
10546
10515
|
res.writeHead(404);
|
|
10547
10516
|
res.end("Not Found");
|
|
@@ -10590,7 +10559,7 @@ async function serveRevalidatedStatic(req, absPath, res, validators) {
|
|
|
10590
10559
|
let data;
|
|
10591
10560
|
if (!validator || validator.stamp !== stamp) {
|
|
10592
10561
|
try {
|
|
10593
|
-
data = await
|
|
10562
|
+
data = await readFile26(absPath);
|
|
10594
10563
|
} catch {
|
|
10595
10564
|
await serveStatic(absPath, res);
|
|
10596
10565
|
return;
|
|
@@ -10900,9 +10869,29 @@ async function startPreviewServer(options) {
|
|
|
10900
10869
|
}
|
|
10901
10870
|
|
|
10902
10871
|
// src/lib/project-scaffold.ts
|
|
10903
|
-
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";
|
|
10904
10873
|
import { constants as FS, existsSync as existsSync12 } from "node:fs";
|
|
10905
10874
|
import path18 from "node:path";
|
|
10875
|
+
|
|
10876
|
+
// src/lib/page-size.ts
|
|
10877
|
+
import postcss4 from "postcss";
|
|
10878
|
+
import { Scalar as Scalar2 } from "yaml";
|
|
10879
|
+
function isPositivePoints(value) {
|
|
10880
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
10881
|
+
}
|
|
10882
|
+
function writePageBounds(doc, page) {
|
|
10883
|
+
const out = { width: page.width, height: page.height };
|
|
10884
|
+
const tolerance = page.tolerance ?? numberAt(doc, ["page", "tolerance"]);
|
|
10885
|
+
if (tolerance !== undefined)
|
|
10886
|
+
out.tolerance = tolerance;
|
|
10887
|
+
doc.set("page", doc.createNode(out));
|
|
10888
|
+
}
|
|
10889
|
+
function numberAt(doc, keyPath) {
|
|
10890
|
+
const v = doc.getIn(keyPath);
|
|
10891
|
+
return isPositivePoints(v) ? v : undefined;
|
|
10892
|
+
}
|
|
10893
|
+
|
|
10894
|
+
// src/lib/project-scaffold.ts
|
|
10906
10895
|
function starterOverridesCss() {
|
|
10907
10896
|
return `/**
|
|
10908
10897
|
* Your book's own styles.
|
|
@@ -10931,9 +10920,6 @@ function escapeYamlScalar(value) {
|
|
|
10931
10920
|
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
10932
10921
|
}
|
|
10933
10922
|
var DEFAULT_AUTHOR = "Anonymous";
|
|
10934
|
-
function isPositivePoints(value) {
|
|
10935
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
10936
|
-
}
|
|
10937
10923
|
function requirePreset(preset, customPage) {
|
|
10938
10924
|
if (!preset || !PRESET_IDS.includes(preset)) {
|
|
10939
10925
|
const got = preset ? ` (got "${preset}")` : "";
|
|
@@ -11037,16 +11023,8 @@ async function scaffoldProject(options) {
|
|
|
11037
11023
|
const { doc, file } = await loadManifestDoc(projectDir);
|
|
11038
11024
|
doc.set("preset", preset);
|
|
11039
11025
|
doc.set("targets", doc.createNode(targets ?? []));
|
|
11040
|
-
if (options.customPage)
|
|
11041
|
-
|
|
11042
|
-
width: options.customPage.width,
|
|
11043
|
-
height: options.customPage.height
|
|
11044
|
-
};
|
|
11045
|
-
if (options.customPage.tolerance !== undefined) {
|
|
11046
|
-
page.tolerance = options.customPage.tolerance;
|
|
11047
|
-
}
|
|
11048
|
-
doc.set("page", doc.createNode(page));
|
|
11049
|
-
}
|
|
11026
|
+
if (options.customPage)
|
|
11027
|
+
writePageBounds(doc, options.customPage);
|
|
11050
11028
|
await writeManifestDoc(file, doc);
|
|
11051
11029
|
}
|
|
11052
11030
|
const firstSource = await firstSourceFile(manifestPath);
|
|
@@ -11090,7 +11068,7 @@ async function scaffoldProject(options) {
|
|
|
11090
11068
|
async function firstSourceFile(manifestPath) {
|
|
11091
11069
|
let text;
|
|
11092
11070
|
try {
|
|
11093
|
-
text = await
|
|
11071
|
+
text = await readFile27(manifestPath, "utf8");
|
|
11094
11072
|
} catch {
|
|
11095
11073
|
return;
|
|
11096
11074
|
}
|
|
@@ -11102,7 +11080,7 @@ async function firstSourceFile(manifestPath) {
|
|
|
11102
11080
|
return m ? m[1].trim() : undefined;
|
|
11103
11081
|
}
|
|
11104
11082
|
async function fillTemplateFile(filePath, substitutions) {
|
|
11105
|
-
let text = await
|
|
11083
|
+
let text = await readFile27(filePath, "utf8");
|
|
11106
11084
|
for (const [token, value] of Object.entries(substitutions)) {
|
|
11107
11085
|
text = text.split(token).join(value);
|
|
11108
11086
|
}
|
|
@@ -11110,7 +11088,7 @@ async function fillTemplateFile(filePath, substitutions) {
|
|
|
11110
11088
|
}
|
|
11111
11089
|
|
|
11112
11090
|
// src/lib/extension-scaffold.ts
|
|
11113
|
-
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";
|
|
11114
11092
|
import { constants as FS2 } from "node:fs";
|
|
11115
11093
|
import path19 from "node:path";
|
|
11116
11094
|
var EXTENSION_KINDS = ["plugin", "theme"];
|
|
@@ -11227,7 +11205,7 @@ async function scaffoldExtension(options) {
|
|
|
11227
11205
|
const src = await getAssetPath(`extension-templates/${kind}/${assetName}`);
|
|
11228
11206
|
const dest = path19.join(extensionDir, relDest);
|
|
11229
11207
|
await mkdir8(path19.dirname(dest), { recursive: true });
|
|
11230
|
-
const text = await
|
|
11208
|
+
const text = await readFile28(src, "utf8");
|
|
11231
11209
|
const table = relDest.endsWith(".json") ? jsonSafe : plain;
|
|
11232
11210
|
await writeFile7(dest, substitute(text, table), "utf8");
|
|
11233
11211
|
written.push(relDest);
|
|
@@ -11272,7 +11250,7 @@ var SKIP_ENTRIES = new Set([
|
|
|
11272
11250
|
|
|
11273
11251
|
// src/lib/extension-import.ts
|
|
11274
11252
|
import { existsSync as existsSync13 } from "node:fs";
|
|
11275
|
-
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";
|
|
11276
11254
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
11277
11255
|
import path20 from "node:path";
|
|
11278
11256
|
import { unzipSync } from "fflate";
|
|
@@ -11345,7 +11323,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11345
11323
|
if (!existsSync13(cssPath)) {
|
|
11346
11324
|
throw new Error("A theme must contain a theme.css file.");
|
|
11347
11325
|
}
|
|
11348
|
-
const css = await
|
|
11326
|
+
const css = await readFile29(cssPath, "utf8");
|
|
11349
11327
|
const { reject, warnings: printFindings } = classifyThemeCssFindings(checkCss(css, "theme.css"));
|
|
11350
11328
|
if (reject) {
|
|
11351
11329
|
throw new Error(`theme.css could not be parsed — ${reject.message}`);
|
|
@@ -11373,7 +11351,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11373
11351
|
for (let i = 0;i < extraSheets.length; i++) {
|
|
11374
11352
|
const rel = extraSheets[i];
|
|
11375
11353
|
const sheetPath = extraSheetPaths[i];
|
|
11376
|
-
const sheetCss = await
|
|
11354
|
+
const sheetCss = await readFile29(sheetPath, "utf8");
|
|
11377
11355
|
const { reject: sheetReject, warnings: sheetFindings } = classifyThemeCssFindings(checkCss(sheetCss, rel));
|
|
11378
11356
|
if (sheetReject) {
|
|
11379
11357
|
throw new Error(`${rel} could not be parsed — ${sheetReject.message}`);
|
|
@@ -11406,7 +11384,7 @@ async function completeLandedManifest(dest, id, meta, metaExists) {
|
|
|
11406
11384
|
let pkg = {};
|
|
11407
11385
|
if (metaExists) {
|
|
11408
11386
|
try {
|
|
11409
|
-
const parsed = JSON.parse(await
|
|
11387
|
+
const parsed = JSON.parse(await readFile29(file, "utf8"));
|
|
11410
11388
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
11411
11389
|
pkg = parsed;
|
|
11412
11390
|
}
|
|
@@ -11508,11 +11486,11 @@ async function importExtensionFromCssText(projectDir, css, name) {
|
|
|
11508
11486
|
async function importExtensionFromFile(projectDir, filePath) {
|
|
11509
11487
|
const ext = path20.extname(filePath).toLowerCase();
|
|
11510
11488
|
if (ext === ".zip") {
|
|
11511
|
-
const buf = await
|
|
11489
|
+
const buf = await readFile29(filePath);
|
|
11512
11490
|
return importExtensionFromZip(projectDir, new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
|
|
11513
11491
|
}
|
|
11514
11492
|
if (ext === ".css") {
|
|
11515
|
-
const css = await
|
|
11493
|
+
const css = await readFile29(filePath, "utf8");
|
|
11516
11494
|
return importExtensionFromCssText(projectDir, css, prettify(path20.basename(filePath, ".css")));
|
|
11517
11495
|
}
|
|
11518
11496
|
throw new Error("Choose a .zip extension package or a .css stylesheet.");
|
|
@@ -12296,7 +12274,7 @@ var drivethrurpgProvider = {
|
|
|
12296
12274
|
};
|
|
12297
12275
|
|
|
12298
12276
|
// src/lib/publish/providers/gdrive.ts
|
|
12299
|
-
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";
|
|
12300
12278
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
12301
12279
|
import path22 from "node:path";
|
|
12302
12280
|
import { Zip, ZipDeflate } from "fflate";
|
|
@@ -12688,12 +12666,12 @@ async function collectZipEntries(root, dir, out, onWarn) {
|
|
|
12688
12666
|
await collectZipEntries(root, full, out, onWarn);
|
|
12689
12667
|
} else if (entry.isFile()) {
|
|
12690
12668
|
const rel = path22.relative(root, full).split(path22.sep).join("/");
|
|
12691
|
-
out[rel] = await
|
|
12669
|
+
out[rel] = await readFile30(full);
|
|
12692
12670
|
} else if (entry.isSymbolicLink()) {
|
|
12693
12671
|
const resolved = await stat7(full).catch(() => null);
|
|
12694
12672
|
if (resolved?.isFile()) {
|
|
12695
12673
|
const rel = path22.relative(root, full).split(path22.sep).join("/");
|
|
12696
|
-
out[rel] = await
|
|
12674
|
+
out[rel] = await readFile30(full);
|
|
12697
12675
|
} else if (resolved?.isDirectory()) {
|
|
12698
12676
|
await collectZipEntries(root, full, out, onWarn);
|
|
12699
12677
|
} else {
|
|
@@ -13562,13 +13540,16 @@ async function disconnectPublishCredential(key, deps, options = {}) {
|
|
|
13562
13540
|
}
|
|
13563
13541
|
}
|
|
13564
13542
|
// src/lib/manifest-config.ts
|
|
13565
|
-
import { isSeq as isSeq4, isMap as isMap3, YAMLSeq as YAMLSeq3, Scalar as
|
|
13543
|
+
import { isSeq as isSeq4, isMap as isMap3, YAMLSeq as YAMLSeq3, Scalar as Scalar3 } from "yaml";
|
|
13566
13544
|
// src/lib/host-policy.ts
|
|
13567
13545
|
var AUTO_SYNC_MAX_MINUTES = 24 * 60;
|
|
13568
13546
|
// src/lib/remote-auth/github-repos.ts
|
|
13569
13547
|
var MANIFEST_NAMES = new Set(MANIFEST_FILENAMES);
|
|
13570
|
-
// src/lib/remote-auth/
|
|
13571
|
-
import
|
|
13548
|
+
// src/lib/remote-auth/online-backup.ts
|
|
13549
|
+
import git5 from "isomorphic-git";
|
|
13550
|
+
|
|
13551
|
+
// src/lib/remote-auth/sync.ts
|
|
13552
|
+
import git4 from "isomorphic-git";
|
|
13572
13553
|
|
|
13573
13554
|
// src/lib/remote-auth/git-http.ts
|
|
13574
13555
|
import httpNode from "isomorphic-git/http/node";
|
|
@@ -13661,23 +13642,23 @@ function withIdleTimeout(http3, idleMs = GIT_HTTP_IDLE_TIMEOUT_MS, uploadMs = GI
|
|
|
13661
13642
|
}
|
|
13662
13643
|
var defaultGitHttp = withIdleTimeout(httpNode);
|
|
13663
13644
|
|
|
13664
|
-
// src/lib/remote-auth/
|
|
13645
|
+
// src/lib/remote-auth/converge-merge.ts
|
|
13665
13646
|
import git2 from "isomorphic-git";
|
|
13647
|
+
import diff3Merge from "diff3";
|
|
13648
|
+
|
|
13649
|
+
// src/lib/remote-auth/transport.ts
|
|
13650
|
+
import git3 from "isomorphic-git";
|
|
13651
|
+
// src/lib/remote-auth/clone.ts
|
|
13652
|
+
import git6 from "isomorphic-git";
|
|
13666
13653
|
// src/lib/remote-auth/test-access.ts
|
|
13667
|
-
import
|
|
13654
|
+
import git7 from "isomorphic-git";
|
|
13668
13655
|
import httpNode2 from "isomorphic-git/http/node";
|
|
13669
|
-
// src/lib/remote-auth/sync.ts
|
|
13670
|
-
import git6 from "isomorphic-git";
|
|
13671
|
-
|
|
13672
|
-
// src/lib/remote-auth/converge-merge.ts
|
|
13673
|
-
import git5 from "isomorphic-git";
|
|
13674
|
-
import diff3Merge from "diff3";
|
|
13675
13656
|
// src/lib/remote-auth/repair.ts
|
|
13676
|
-
import git7 from "isomorphic-git";
|
|
13677
|
-
// src/lib/remote-auth/scorched-earth.ts
|
|
13678
13657
|
import git8 from "isomorphic-git";
|
|
13658
|
+
// src/lib/remote-auth/scorched-earth.ts
|
|
13659
|
+
import git9 from "isomorphic-git";
|
|
13679
13660
|
// src/lib/publish/selections.ts
|
|
13680
|
-
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";
|
|
13681
13662
|
import path26 from "node:path";
|
|
13682
13663
|
class PublishSelectionsStore {
|
|
13683
13664
|
filePath;
|
|
@@ -13687,7 +13668,7 @@ class PublishSelectionsStore {
|
|
|
13687
13668
|
}
|
|
13688
13669
|
async read() {
|
|
13689
13670
|
try {
|
|
13690
|
-
const raw = await
|
|
13671
|
+
const raw = await readFile31(this.filePath, "utf8");
|
|
13691
13672
|
const parsed = JSON.parse(raw);
|
|
13692
13673
|
if (parsed && typeof parsed === "object") {
|
|
13693
13674
|
return {
|