gutterpress 0.11.18 → 0.11.19-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/index.js +2 -2
- package/dist/{build-67bj810g.js → build-wbfzx37g.js} +6 -6
- package/dist/{cdp-smdx6mr3.js → cdp-1hn3xsk2.js} +2 -2
- package/dist/{cdp-jg95s674.js → cdp-h0wa42cz.js} +1 -1
- package/dist/checks/source/character-table.d.ts +63 -0
- package/dist/checks/source/characters.d.ts +1 -0
- package/dist/checks/source/index.d.ts +1 -0
- package/dist/{cli-6q7trt7z.js → cli-55jd2t8p.js} +341 -163
- package/dist/{cli-7fkhgtfn.js → cli-asap4t1z.js} +4 -3
- package/dist/{cli-kkzka31p.js → cli-eyyggect.js} +1 -1
- package/dist/{cli-xe29z84b.js → cli-r7czdzyz.js} +74 -5
- package/dist/{cli-rg8jq04a.js → cli-xf9b7ptc.js} +1 -1
- package/dist/{cli-ptez7zcc.js → cli-xx94a1k6.js} +1 -1
- package/dist/cli.js +12 -12
- package/dist/{doctor-gcqb6xtj.js → doctor-mkhpss2t.js} +3 -3
- package/dist/{engine-wtnakejg.js → engine-fe0n7v9p.js} +3 -3
- package/dist/{engine-zy6fzs5a.js → engine-zehw6tkv.js} +2 -2
- package/dist/{ext-pkaghr3f.js → ext-vw89b9sm.js} +6 -6
- package/dist/{index-n8vs6pss.js → index-b866g2ph.js} +1 -1
- package/dist/{index-rzm1phg0.js → index-bt0qws5c.js} +4 -3
- package/dist/{index-e1hbmb5a.js → index-eqxs5118.js} +412 -167
- package/dist/index.js +3 -3
- package/dist/lib/exec.d.ts +2 -1
- package/dist/lib/ghostscript.d.ts +20 -0
- package/dist/{new-rnnavkc8.js → new-3g19kf7m.js} +6 -6
- package/dist/{preflight-4769yg8n.js → preflight-62r2cnyx.js} +6 -6
- package/dist/{preview-f9csmejj.js → preview-sjq8afwc.js} +6 -6
- package/dist/{publish-40yvnb62.js → publish-fq6gmn4e.js} +6 -6
- package/dist/{validate-pt921n11.js → validate-2zsqvbtk.js} +6 -6
- package/package.json +1 -1
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "./index-jt552wtf.js";
|
|
13
13
|
import {
|
|
14
14
|
INSTALL_HINTS,
|
|
15
|
+
enhancedPath,
|
|
15
16
|
execCapture,
|
|
16
17
|
findTool,
|
|
17
18
|
fullInstallHint,
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
resolveChromiumExecutable,
|
|
21
22
|
run,
|
|
22
23
|
spawnCapture
|
|
23
|
-
} from "./index-
|
|
24
|
+
} from "./index-bt0qws5c.js";
|
|
24
25
|
import {
|
|
25
26
|
PLUGINS_DIR,
|
|
26
27
|
VENDORED_NPM_DIR,
|
|
@@ -5774,6 +5775,58 @@ async function hasLiveTransparency(pdfBytes) {
|
|
|
5774
5775
|
}
|
|
5775
5776
|
return false;
|
|
5776
5777
|
}
|
|
5778
|
+
async function findPostScriptShadingPages(pdfBytes) {
|
|
5779
|
+
const { PDFDocument, PDFDict, PDFName, PDFNumber, PDFRawStream, PDFRef } = await import("pdf-lib");
|
|
5780
|
+
let doc;
|
|
5781
|
+
try {
|
|
5782
|
+
doc = await PDFDocument.load(pdfBytes, {
|
|
5783
|
+
updateMetadata: false,
|
|
5784
|
+
throwOnInvalidObject: false
|
|
5785
|
+
});
|
|
5786
|
+
} catch {
|
|
5787
|
+
return [];
|
|
5788
|
+
}
|
|
5789
|
+
const ctx = doc.context;
|
|
5790
|
+
const asDict = (obj) => {
|
|
5791
|
+
const o = obj instanceof PDFRef ? ctx.lookup(obj) : obj;
|
|
5792
|
+
return o instanceof PDFRawStream ? o.dict : o instanceof PDFDict ? o : undefined;
|
|
5793
|
+
};
|
|
5794
|
+
const num = (obj) => {
|
|
5795
|
+
const o = obj instanceof PDFRef ? ctx.lookup(obj) : obj;
|
|
5796
|
+
return o instanceof PDFNumber ? o.asNumber() : undefined;
|
|
5797
|
+
};
|
|
5798
|
+
const isPostScriptShading = (shading) => {
|
|
5799
|
+
const dict = asDict(shading);
|
|
5800
|
+
if (!dict || num(dict.get(PDFName.of("ShadingType"))) !== 1)
|
|
5801
|
+
return false;
|
|
5802
|
+
return num(asDict(dict.get(PDFName.of("Function")))?.get(PDFName.of("FunctionType"))) === 4;
|
|
5803
|
+
};
|
|
5804
|
+
const subDicts = (resources, key) => {
|
|
5805
|
+
const group = asDict(resources.get(PDFName.of(key)));
|
|
5806
|
+
return group ? group.entries().map(([, v]) => v) : [];
|
|
5807
|
+
};
|
|
5808
|
+
const seen = new Set;
|
|
5809
|
+
const usesPostScriptShading = (resources) => {
|
|
5810
|
+
if (!resources || seen.has(resources))
|
|
5811
|
+
return false;
|
|
5812
|
+
seen.add(resources);
|
|
5813
|
+
if (subDicts(resources, "Shading").some(isPostScriptShading))
|
|
5814
|
+
return true;
|
|
5815
|
+
for (const pattern of subDicts(resources, "Pattern")) {
|
|
5816
|
+
const dict = asDict(pattern);
|
|
5817
|
+
if (dict && isPostScriptShading(dict.get(PDFName.of("Shading"))))
|
|
5818
|
+
return true;
|
|
5819
|
+
}
|
|
5820
|
+
return subDicts(resources, "XObject").some((x) => usesPostScriptShading(asDict(asDict(x)?.get(PDFName.of("Resources")))));
|
|
5821
|
+
};
|
|
5822
|
+
const pages = [];
|
|
5823
|
+
doc.getPages().forEach((page, i) => {
|
|
5824
|
+
seen.clear();
|
|
5825
|
+
if (usesPostScriptShading(asDict(page.node.Resources())))
|
|
5826
|
+
pages.push(i + 1);
|
|
5827
|
+
});
|
|
5828
|
+
return pages;
|
|
5829
|
+
}
|
|
5777
5830
|
async function stripAnnotations(pdfPath, stagingDir) {
|
|
5778
5831
|
const tmp = join3(stagingDir, "annotations-stripped.pdf");
|
|
5779
5832
|
try {
|
|
@@ -5901,7 +5954,22 @@ async function convertToPdfxCmyk(inputPdf, outPdf, config) {
|
|
|
5901
5954
|
if (!ghostscript) {
|
|
5902
5955
|
throw new Error("Ghostscript executable not found. Install Ghostscript or set GHOSTSCRIPT_PATH to its command-line executable.");
|
|
5903
5956
|
}
|
|
5904
|
-
await
|
|
5957
|
+
const { code, signal, stdout, stderr } = await spawnCapture(ghostscript, args, {
|
|
5958
|
+
env: { ...process.env, PATH: enhancedPath }
|
|
5959
|
+
});
|
|
5960
|
+
if (signal) {
|
|
5961
|
+
const version = await execCapture(ghostscript, ["--version"]).then((r) => r.stdout.trim()).catch(() => "unknown version");
|
|
5962
|
+
throw new Error(`Ghostscript ${version} crashed (${signal}) while converting to PDF/X. This is a Ghostscript bug, not a problem with the PDF. Ghostscript 10.02.1 has been seen to crash in the PDF 1.3 transparency flattening PDF/X requires, on a page with an alpha-channel image. Flattening that image against its background avoided it there; a newer Ghostscript may too. A plain PDF (--format pdf) does not run this step.`);
|
|
5963
|
+
}
|
|
5964
|
+
if (code !== 0) {
|
|
5965
|
+
throw new Error(`${ghostscript} ${args.join(" ")} exited ${code}
|
|
5966
|
+
${stdout}${stderr}`);
|
|
5967
|
+
}
|
|
5968
|
+
const at = stderr.indexOf("The following warnings");
|
|
5969
|
+
if (at >= 0) {
|
|
5970
|
+
log.warn(`Ghostscript reported problems converting to PDF/X:
|
|
5971
|
+
${stderr.slice(at).trim()}`);
|
|
5972
|
+
}
|
|
5905
5973
|
} finally {
|
|
5906
5974
|
await unlink2(tmpDef).catch(() => {});
|
|
5907
5975
|
}
|
|
@@ -5916,7 +5984,7 @@ import git from "isomorphic-git";
|
|
|
5916
5984
|
// package.json
|
|
5917
5985
|
var package_default = {
|
|
5918
5986
|
name: "gutterpress",
|
|
5919
|
-
version: "0.11.
|
|
5987
|
+
version: "0.11.19-alpha.2",
|
|
5920
5988
|
description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
|
|
5921
5989
|
author: "itlackey",
|
|
5922
5990
|
license: "MPL-2.0",
|
|
@@ -7843,6 +7911,9 @@ function isDroppedPageGradient(decl) {
|
|
|
7843
7911
|
return false;
|
|
7844
7912
|
return isInPageBox(decl);
|
|
7845
7913
|
}
|
|
7914
|
+
function isPostScriptShadingGradient(decl) {
|
|
7915
|
+
return /(repeating-(linear|radial|conic)|conic)-gradient\(/i.test(decl.value);
|
|
7916
|
+
}
|
|
7846
7917
|
function isInPageMarginBox(decl) {
|
|
7847
7918
|
const box = decl.parent;
|
|
7848
7919
|
if (!box || box.type !== "atrule")
|
|
@@ -8091,6 +8162,13 @@ function checkCssRaw(css, from) {
|
|
|
8091
8162
|
message: `"${decl.prop}: ${decl.value}" on @page paints nothing in print — Chromium drops a gradient in the @page box's own background, with no error and a valid-looking PDF. A solid colour there paints the whole sheet; the same gradient also works on "html" (it covers the content area, not the sheet) or in a margin box.`,
|
|
8092
8163
|
...nodeLoc(decl)
|
|
8093
8164
|
});
|
|
8165
|
+
} else if (isPostScriptShadingGradient(decl)) {
|
|
8166
|
+
warnings.push({
|
|
8167
|
+
rule: ruleRiskyProps,
|
|
8168
|
+
severity: "warning",
|
|
8169
|
+
message: `"${decl.prop}: ${decl.value}" prints blank in PDF/X exports (--format pdfx): Chromium emits repeating-*-gradient() and conic-gradient() in a form Ghostscript cannot convert. Plain PDF output is unaffected. Use linear-gradient() or radial-gradient() instead; for 0deg/90deg stripes write "linear-gradient(90deg, #b33 0 6px, #c8c5bf 6px 10px)" with "background-size: 10px 100%", and for anything else a small tiled image.`,
|
|
8170
|
+
...nodeLoc(decl)
|
|
8171
|
+
});
|
|
8094
8172
|
} else if (isUnpairedFitContentWidth(decl)) {
|
|
8095
8173
|
warnings.push({
|
|
8096
8174
|
rule: ruleRiskyProps,
|
|
@@ -9184,11 +9262,174 @@ var check27 = {
|
|
|
9184
9262
|
};
|
|
9185
9263
|
registerCheck(check27);
|
|
9186
9264
|
|
|
9265
|
+
// src/checks/source/characters.ts
|
|
9266
|
+
import { readFile as readFile24 } from "node:fs/promises";
|
|
9267
|
+
|
|
9268
|
+
// src/checks/source/character-table.ts
|
|
9269
|
+
var CHARACTER_PATTERN = new RegExp("[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u00A0\\u00AD\\u00B4\\u02BC" + "\\u061C\\u2000-\\u2015\\u2018\\u2019\\u201B-\\u201D\\u201F\\u2022\\u2026" + "\\u2028-\\u202F\\u2032\\u2033\\u205F\\u2060\\u2066-\\u2069\\u2190\\u2192" + "\\u2194\\u21D2\\u2212\\u25AA\\u25E6\\u2605\\u2713\\u2714\\u2717\\u3000" + "\\uE000-\\uF8FF\\uFEFF\\uFF02\\uFF07\\uFFFD]" + "|\\p{Emoji_Presentation}|\\p{Extended_Pictographic}(?=\\uFE0F)", "gu");
|
|
9270
|
+
var NAMED = {
|
|
9271
|
+
8212: ["Em dash", "typographer", "---"],
|
|
9272
|
+
8211: ["En dash", "typographer", "--"],
|
|
9273
|
+
8220: ["Opening curly quote", "typographer", '"'],
|
|
9274
|
+
8221: ["Closing curly quote", "typographer", '"'],
|
|
9275
|
+
8216: ["Opening curly single quote", "typographer", "'"],
|
|
9276
|
+
8217: ["Curly apostrophe", "typographer", "'"],
|
|
9277
|
+
8230: ["Ellipsis character", "typographer", "..."],
|
|
9278
|
+
700: ["Modifier-letter apostrophe", "lookalike", "'"],
|
|
9279
|
+
180: ["Acute accent", "lookalike", "'"],
|
|
9280
|
+
8242: ["Prime", "lookalike", "'"],
|
|
9281
|
+
8243: ["Double prime", "lookalike", '"'],
|
|
9282
|
+
8219: ["Reversed single quote", "lookalike", "'"],
|
|
9283
|
+
8223: ["Reversed double quote", "lookalike", '"'],
|
|
9284
|
+
65282: ["Full-width quote", "lookalike", '"'],
|
|
9285
|
+
65287: ["Full-width apostrophe", "lookalike", "'"],
|
|
9286
|
+
8208: ["Unicode hyphen", "lookalike", "-"],
|
|
9287
|
+
8209: ["Non-breaking hyphen", "lookalike", "-"],
|
|
9288
|
+
8210: ["Figure dash", "lookalike", "-"],
|
|
9289
|
+
8213: ["Horizontal bar", "lookalike", "---"],
|
|
9290
|
+
8722: ["Minus sign", "lookalike", "-"],
|
|
9291
|
+
8226: ["Bullet", "symbol", "•"],
|
|
9292
|
+
9702: ["White bullet", "symbol", "◦"],
|
|
9293
|
+
9642: ["Small square", "symbol", "▪"],
|
|
9294
|
+
8594: ["Right arrow", "symbol", "→"],
|
|
9295
|
+
8592: ["Left arrow", "symbol", "←"],
|
|
9296
|
+
8596: ["Left-right arrow", "symbol", "↔"],
|
|
9297
|
+
8658: ["Double right arrow", "symbol", "⇒"],
|
|
9298
|
+
10003: ["Check mark", "symbol", "✓"],
|
|
9299
|
+
10004: ["Heavy check mark", "symbol", "✔"],
|
|
9300
|
+
10007: ["Ballot X", "symbol", "✗"],
|
|
9301
|
+
9733: ["Star", "symbol", "★"],
|
|
9302
|
+
8203: ["Zero-width space", "invisible", "​"],
|
|
9303
|
+
8288: ["Word joiner", "invisible", "⁠"],
|
|
9304
|
+
65279: ["Byte-order mark", "invisible"],
|
|
9305
|
+
173: ["Soft hyphen", "invisible", "­"],
|
|
9306
|
+
8204: ["Zero-width non-joiner", "invisible", "‌"],
|
|
9307
|
+
8205: ["Zero-width joiner", "invisible", "‍"],
|
|
9308
|
+
8206: ["Left-to-right mark", "invisible", "‎"],
|
|
9309
|
+
8207: ["Right-to-left mark", "invisible", "‏"],
|
|
9310
|
+
1564: ["Arabic letter mark", "invisible", "؜"],
|
|
9311
|
+
8234: ["Left-to-right embedding", "invisible", "‪"],
|
|
9312
|
+
8235: ["Right-to-left embedding", "invisible", "‫"],
|
|
9313
|
+
8236: ["Pop directional formatting", "invisible", "‬"],
|
|
9314
|
+
8237: ["Left-to-right override", "invisible", "‭"],
|
|
9315
|
+
8238: ["Right-to-left override", "invisible", "‮"],
|
|
9316
|
+
8294: ["Left-to-right isolate", "invisible", "⁦"],
|
|
9317
|
+
8295: ["Right-to-left isolate", "invisible", "⁧"],
|
|
9318
|
+
8296: ["First-strong isolate", "invisible", "⁨"],
|
|
9319
|
+
8297: ["Pop directional isolate", "invisible", "⁩"],
|
|
9320
|
+
8232: ["Line separator", "invisible"],
|
|
9321
|
+
8233: ["Paragraph separator", "invisible"],
|
|
9322
|
+
160: ["Non-breaking space", "whitespace", " "],
|
|
9323
|
+
8192: ["En quad", "whitespace", " "],
|
|
9324
|
+
8193: ["Em quad", "whitespace", " "],
|
|
9325
|
+
8194: ["En space", "whitespace", " "],
|
|
9326
|
+
8195: ["Em space", "whitespace", " "],
|
|
9327
|
+
8196: ["Three-per-em space", "whitespace", " "],
|
|
9328
|
+
8197: ["Four-per-em space", "whitespace", " "],
|
|
9329
|
+
8198: ["Six-per-em space", "whitespace", " "],
|
|
9330
|
+
8199: ["Figure space", "whitespace", " "],
|
|
9331
|
+
8200: ["Punctuation space", "whitespace", " "],
|
|
9332
|
+
8201: ["Thin space", "whitespace", " "],
|
|
9333
|
+
8202: ["Hair space", "whitespace", " "],
|
|
9334
|
+
8239: ["Narrow non-breaking space", "whitespace", " "],
|
|
9335
|
+
8287: ["Medium mathematical space", "whitespace", " "],
|
|
9336
|
+
12288: ["Ideographic space", "whitespace", " "],
|
|
9337
|
+
65533: ["Replacement character", "replacement"]
|
|
9338
|
+
};
|
|
9339
|
+
function fixFor(code, spelling) {
|
|
9340
|
+
switch (code) {
|
|
9341
|
+
case "typographer":
|
|
9342
|
+
return `Type ${spelling} instead; it prints the same.`;
|
|
9343
|
+
case "lookalike":
|
|
9344
|
+
return `Type ${spelling} instead.`;
|
|
9345
|
+
case "whitespace":
|
|
9346
|
+
return `Type a space, or ${spelling} if you meant it.`;
|
|
9347
|
+
case "symbol":
|
|
9348
|
+
case "invisible":
|
|
9349
|
+
return spelling ? `Delete it, or type ${spelling} if you meant it.` : "Delete it.";
|
|
9350
|
+
case "control":
|
|
9351
|
+
return "Delete it; it does not print.";
|
|
9352
|
+
case "replacement":
|
|
9353
|
+
return "Retype the character that was lost here.";
|
|
9354
|
+
case "private-use":
|
|
9355
|
+
return "Replace it with the character you meant; this one only shows in the font it came from.";
|
|
9356
|
+
}
|
|
9357
|
+
}
|
|
9358
|
+
function info2(name, code, spelling) {
|
|
9359
|
+
return { name, code, fix: fixFor(code, spelling) };
|
|
9360
|
+
}
|
|
9361
|
+
function describeCharacter(char) {
|
|
9362
|
+
const cp2 = char.codePointAt(0);
|
|
9363
|
+
const named = NAMED[cp2];
|
|
9364
|
+
if (named)
|
|
9365
|
+
return info2(...named);
|
|
9366
|
+
if (cp2 <= 159)
|
|
9367
|
+
return info2("Control character", "control");
|
|
9368
|
+
if (cp2 >= 57344 && cp2 <= 63743)
|
|
9369
|
+
return info2("Private-use character", "private-use");
|
|
9370
|
+
return info2("Emoji", "symbol", `&#x${cp2.toString(16).toUpperCase()};`);
|
|
9371
|
+
}
|
|
9372
|
+
function codePointLabel(char) {
|
|
9373
|
+
return `U+${char.codePointAt(0).toString(16).toUpperCase().padStart(4, "0")}`;
|
|
9374
|
+
}
|
|
9375
|
+
|
|
9376
|
+
// src/checks/source/characters.ts
|
|
9377
|
+
var PROBE = new RegExp(CHARACTER_PATTERN.source, "u");
|
|
9378
|
+
var check28 = {
|
|
9379
|
+
id: "source.characters",
|
|
9380
|
+
name: "Unusual Characters",
|
|
9381
|
+
description: "Finds invisible and control characters, odd spaces, and pasted typography (em dashes, curly quotes, emoji) in markdown",
|
|
9382
|
+
category: "source",
|
|
9383
|
+
phase: "pre-build",
|
|
9384
|
+
async run(ctx) {
|
|
9385
|
+
const results = [];
|
|
9386
|
+
for (const file of (ctx.markdownFiles ?? []).slice().sort()) {
|
|
9387
|
+
let content;
|
|
9388
|
+
try {
|
|
9389
|
+
content = await readFile24(file, "utf8");
|
|
9390
|
+
} catch {
|
|
9391
|
+
results.push(inspectionFailed(check28.id, `Could not read source file: ${file}`, { file }));
|
|
9392
|
+
continue;
|
|
9393
|
+
}
|
|
9394
|
+
if (!PROBE.test(content))
|
|
9395
|
+
continue;
|
|
9396
|
+
const lines = content.split(`
|
|
9397
|
+
`);
|
|
9398
|
+
let silencedLine = -1;
|
|
9399
|
+
for (let i = 0;i < lines.length; i++) {
|
|
9400
|
+
const line = lines[i];
|
|
9401
|
+
const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
|
|
9402
|
+
if (comment && parseDisableDirective(comment[1])?.includes(check28.id)) {
|
|
9403
|
+
silencedLine = i + 1;
|
|
9404
|
+
continue;
|
|
9405
|
+
}
|
|
9406
|
+
if (i === silencedLine)
|
|
9407
|
+
continue;
|
|
9408
|
+
for (const match of line.matchAll(CHARACTER_PATTERN)) {
|
|
9409
|
+
const char = match[0];
|
|
9410
|
+
const { name, code, fix } = describeCharacter(char);
|
|
9411
|
+
results.push(finding(check28.id, {
|
|
9412
|
+
severity: "warning",
|
|
9413
|
+
code,
|
|
9414
|
+
message: `${name} (${codePointLabel(char)}). ${fix}`,
|
|
9415
|
+
file,
|
|
9416
|
+
line: i + 1,
|
|
9417
|
+
column: match.index + 1,
|
|
9418
|
+
data: { codePoint: char.codePointAt(0) }
|
|
9419
|
+
}));
|
|
9420
|
+
}
|
|
9421
|
+
}
|
|
9422
|
+
}
|
|
9423
|
+
return results;
|
|
9424
|
+
}
|
|
9425
|
+
};
|
|
9426
|
+
registerCheck(check28);
|
|
9427
|
+
|
|
9187
9428
|
// src/checks/asset/image-file-size.ts
|
|
9188
9429
|
import { stat as stat4 } from "node:fs/promises";
|
|
9189
9430
|
|
|
9190
9431
|
// src/lib/image-inspect.ts
|
|
9191
|
-
import { stat as stat3, readFile as
|
|
9432
|
+
import { stat as stat3, readFile as readFile25 } from "node:fs/promises";
|
|
9192
9433
|
var DEFAULT_DPI = 72;
|
|
9193
9434
|
var cache = new Map;
|
|
9194
9435
|
async function inspectImage(path13) {
|
|
@@ -9201,15 +9442,15 @@ async function inspectImage(path13) {
|
|
|
9201
9442
|
const hit = cache.get(path13);
|
|
9202
9443
|
if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size)
|
|
9203
9444
|
return hit.info;
|
|
9204
|
-
let
|
|
9445
|
+
let info3 = null;
|
|
9205
9446
|
try {
|
|
9206
|
-
const buf = await
|
|
9207
|
-
|
|
9447
|
+
const buf = await readFile25(path13);
|
|
9448
|
+
info3 = parseImage(buf);
|
|
9208
9449
|
} catch {
|
|
9209
|
-
|
|
9450
|
+
info3 = null;
|
|
9210
9451
|
}
|
|
9211
|
-
cache.set(path13, { mtimeMs: st.mtimeMs, size: st.size, info:
|
|
9212
|
-
return
|
|
9452
|
+
cache.set(path13, { mtimeMs: st.mtimeMs, size: st.size, info: info3 });
|
|
9453
|
+
return info3;
|
|
9213
9454
|
}
|
|
9214
9455
|
function parseImage(b) {
|
|
9215
9456
|
if (b.length > 8 && b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71) {
|
|
@@ -9408,7 +9649,7 @@ async function collectImageFiles(dirs, exts, ignore2 = ASSET_SCAN_IGNORE_GLOBS)
|
|
|
9408
9649
|
}
|
|
9409
9650
|
|
|
9410
9651
|
// src/checks/asset/image-file-size.ts
|
|
9411
|
-
var
|
|
9652
|
+
var check29 = {
|
|
9412
9653
|
id: "asset.image.file-size",
|
|
9413
9654
|
name: "Image File Size",
|
|
9414
9655
|
description: "Checks that image files do not exceed the maximum size limit",
|
|
@@ -9425,18 +9666,18 @@ var check28 = {
|
|
|
9425
9666
|
const results = [];
|
|
9426
9667
|
for (const file of files) {
|
|
9427
9668
|
try {
|
|
9428
|
-
const
|
|
9429
|
-
if (
|
|
9430
|
-
const sizeMb = (
|
|
9669
|
+
const info3 = await stat4(file);
|
|
9670
|
+
if (info3.size > maxSize) {
|
|
9671
|
+
const sizeMb = (info3.size / 1e6).toFixed(1);
|
|
9431
9672
|
const maxMb = (maxSize / 1e6).toFixed(1);
|
|
9432
|
-
results.push(finding(
|
|
9673
|
+
results.push(finding(check29.id, {
|
|
9433
9674
|
severity: "warning",
|
|
9434
9675
|
message: `Image file too large: ${sizeMb}MB (max ${maxMb}MB)`,
|
|
9435
9676
|
file
|
|
9436
9677
|
}));
|
|
9437
9678
|
}
|
|
9438
9679
|
} catch {
|
|
9439
|
-
results.push(inspectionFailed(
|
|
9680
|
+
results.push(inspectionFailed(check29.id, `Could not stat image file: ${file}`, {
|
|
9440
9681
|
file
|
|
9441
9682
|
}));
|
|
9442
9683
|
}
|
|
@@ -9444,10 +9685,10 @@ var check28 = {
|
|
|
9444
9685
|
return results;
|
|
9445
9686
|
}
|
|
9446
9687
|
};
|
|
9447
|
-
registerCheck(
|
|
9688
|
+
registerCheck(check29);
|
|
9448
9689
|
|
|
9449
9690
|
// src/checks/asset/image-resolution.ts
|
|
9450
|
-
var
|
|
9691
|
+
var check30 = {
|
|
9451
9692
|
id: "asset.image.resolution",
|
|
9452
9693
|
name: "Image Resolution",
|
|
9453
9694
|
description: "Checks source image DPI from embedded density metadata",
|
|
@@ -9463,13 +9704,13 @@ var check29 = {
|
|
|
9463
9704
|
return [];
|
|
9464
9705
|
const results = [];
|
|
9465
9706
|
for (const file of files) {
|
|
9466
|
-
const
|
|
9467
|
-
if (!
|
|
9707
|
+
const info3 = await inspectImage(file);
|
|
9708
|
+
if (!info3)
|
|
9468
9709
|
continue;
|
|
9469
|
-
const { xDpi, yDpi } =
|
|
9710
|
+
const { xDpi, yDpi } = info3;
|
|
9470
9711
|
if (xDpi > 0 && yDpi > 0 && (xDpi < minDpi || yDpi < minDpi)) {
|
|
9471
9712
|
results.push({
|
|
9472
|
-
checkId:
|
|
9713
|
+
checkId: check30.id,
|
|
9473
9714
|
severity: "warning",
|
|
9474
9715
|
message: `Image resolution too low: ${xDpi}x${yDpi} DPI (minimum ${minDpi} DPI)`,
|
|
9475
9716
|
file
|
|
@@ -9479,7 +9720,7 @@ var check29 = {
|
|
|
9479
9720
|
return results;
|
|
9480
9721
|
}
|
|
9481
9722
|
};
|
|
9482
|
-
registerCheck(
|
|
9723
|
+
registerCheck(check30);
|
|
9483
9724
|
|
|
9484
9725
|
// src/checks/asset/image-color-space.ts
|
|
9485
9726
|
var LABEL = {
|
|
@@ -9487,7 +9728,7 @@ var LABEL = {
|
|
|
9487
9728
|
gray: "Gray",
|
|
9488
9729
|
cmyk: "CMYK"
|
|
9489
9730
|
};
|
|
9490
|
-
var
|
|
9731
|
+
var check31 = {
|
|
9491
9732
|
id: "asset.image.color-space",
|
|
9492
9733
|
name: "Image Color Space",
|
|
9493
9734
|
description: "Validates image color spaces against allowed list",
|
|
@@ -9504,11 +9745,11 @@ var check30 = {
|
|
|
9504
9745
|
const allowedLower = new Set(allowed.map((s) => s.toLowerCase()));
|
|
9505
9746
|
const results = [];
|
|
9506
9747
|
for (const file of files) {
|
|
9507
|
-
const
|
|
9508
|
-
const cs =
|
|
9748
|
+
const info3 = await inspectImage(file);
|
|
9749
|
+
const cs = info3?.colorSpace;
|
|
9509
9750
|
if (cs && !allowedLower.has(cs)) {
|
|
9510
9751
|
results.push({
|
|
9511
|
-
checkId:
|
|
9752
|
+
checkId: check31.id,
|
|
9512
9753
|
severity: "warning",
|
|
9513
9754
|
message: `Image uses ${LABEL[cs] ?? cs} color space (allowed: ${allowed.join(", ")})`,
|
|
9514
9755
|
file
|
|
@@ -9518,10 +9759,10 @@ var check30 = {
|
|
|
9518
9759
|
return results;
|
|
9519
9760
|
}
|
|
9520
9761
|
};
|
|
9521
|
-
registerCheck(
|
|
9762
|
+
registerCheck(check31);
|
|
9522
9763
|
|
|
9523
9764
|
// src/checks/asset/image-alpha.ts
|
|
9524
|
-
var
|
|
9765
|
+
var check32 = {
|
|
9525
9766
|
id: "asset.image.alpha-channel",
|
|
9526
9767
|
name: "Image Alpha Channel",
|
|
9527
9768
|
description: "Checks for alpha channels in PNG/TIFF images",
|
|
@@ -9536,10 +9777,10 @@ var check31 = {
|
|
|
9536
9777
|
return [];
|
|
9537
9778
|
const results = [];
|
|
9538
9779
|
for (const file of files) {
|
|
9539
|
-
const
|
|
9540
|
-
if (
|
|
9780
|
+
const info3 = await inspectImage(file);
|
|
9781
|
+
if (info3?.hasAlpha) {
|
|
9541
9782
|
results.push({
|
|
9542
|
-
checkId:
|
|
9783
|
+
checkId: check32.id,
|
|
9543
9784
|
severity: "warning",
|
|
9544
9785
|
message: "Image contains alpha channel, which may cause print issues",
|
|
9545
9786
|
file
|
|
@@ -9549,10 +9790,10 @@ var check31 = {
|
|
|
9549
9790
|
return results;
|
|
9550
9791
|
}
|
|
9551
9792
|
};
|
|
9552
|
-
registerCheck(
|
|
9793
|
+
registerCheck(check32);
|
|
9553
9794
|
|
|
9554
9795
|
// src/checks/asset/image-tac.ts
|
|
9555
|
-
var
|
|
9796
|
+
var check33 = {
|
|
9556
9797
|
id: "asset.image.tac-raster",
|
|
9557
9798
|
name: "Image TAC (Raster)",
|
|
9558
9799
|
description: "Rasterizes and checks TAC per image using Ghostscript",
|
|
@@ -9571,7 +9812,7 @@ var check32 = {
|
|
|
9571
9812
|
const ghostscript = await resolveGhostscript();
|
|
9572
9813
|
if (!ghostscript) {
|
|
9573
9814
|
return [
|
|
9574
|
-
inspectionFailed(
|
|
9815
|
+
inspectionFailed(check33.id, "Could not inspect image ink coverage: Ghostscript executable not found")
|
|
9575
9816
|
];
|
|
9576
9817
|
}
|
|
9577
9818
|
for (const file of files) {
|
|
@@ -9589,7 +9830,7 @@ var check32 = {
|
|
|
9589
9830
|
if (nums.length === 4 && nums.every((n) => Number.isFinite(n))) {
|
|
9590
9831
|
const tac = (nums[0] + nums[1] + nums[2] + nums[3]) * 100;
|
|
9591
9832
|
if (tac > maxTac) {
|
|
9592
|
-
results.push(finding(
|
|
9833
|
+
results.push(finding(check33.id, {
|
|
9593
9834
|
severity: "warning",
|
|
9594
9835
|
message: `Image TAC exceeds limit: ${tac.toFixed(1)}% (max ${maxTac}%)`,
|
|
9595
9836
|
file,
|
|
@@ -9601,16 +9842,16 @@ var check32 = {
|
|
|
9601
9842
|
}
|
|
9602
9843
|
}
|
|
9603
9844
|
} catch {
|
|
9604
|
-
results.push(inspectionFailed(
|
|
9845
|
+
results.push(inspectionFailed(check33.id, `Could not inspect image ink coverage: ${file}`, { file }));
|
|
9605
9846
|
}
|
|
9606
9847
|
}
|
|
9607
9848
|
return results;
|
|
9608
9849
|
}
|
|
9609
9850
|
};
|
|
9610
|
-
registerCheck(
|
|
9851
|
+
registerCheck(check33);
|
|
9611
9852
|
|
|
9612
9853
|
// src/checks/asset/approved-fonts.ts
|
|
9613
|
-
var
|
|
9854
|
+
var check34 = {
|
|
9614
9855
|
id: "asset.font.approved-files",
|
|
9615
9856
|
name: "Approved Font Files",
|
|
9616
9857
|
description: "Checks font files against the approved file patterns",
|
|
@@ -9649,7 +9890,7 @@ var check33 = {
|
|
|
9649
9890
|
for (const font of allFonts) {
|
|
9650
9891
|
if (!approvedFonts.has(font)) {
|
|
9651
9892
|
results.push({
|
|
9652
|
-
checkId:
|
|
9893
|
+
checkId: check34.id,
|
|
9653
9894
|
severity: "warning",
|
|
9654
9895
|
message: "Font file not in approved list",
|
|
9655
9896
|
file: font
|
|
@@ -9659,7 +9900,7 @@ var check33 = {
|
|
|
9659
9900
|
return results;
|
|
9660
9901
|
}
|
|
9661
9902
|
};
|
|
9662
|
-
registerCheck(
|
|
9903
|
+
registerCheck(check34);
|
|
9663
9904
|
|
|
9664
9905
|
// src/checks/asset/font-license.ts
|
|
9665
9906
|
import { existsSync as existsSync11 } from "node:fs";
|
|
@@ -9674,7 +9915,7 @@ var LICENSE_NAMES = [
|
|
|
9674
9915
|
"OFL-1.1.txt",
|
|
9675
9916
|
"COPYING"
|
|
9676
9917
|
];
|
|
9677
|
-
var
|
|
9918
|
+
var check35 = {
|
|
9678
9919
|
id: "asset.font.license",
|
|
9679
9920
|
name: "Font License",
|
|
9680
9921
|
description: "Checks for license files in font directories",
|
|
@@ -9701,7 +9942,7 @@ var check34 = {
|
|
|
9701
9942
|
const hasLicense = LICENSE_NAMES.some((name) => existsSync11(resolve7(fontDir, name)));
|
|
9702
9943
|
if (!hasLicense) {
|
|
9703
9944
|
results.push({
|
|
9704
|
-
checkId:
|
|
9945
|
+
checkId: check35.id,
|
|
9705
9946
|
severity: "warning",
|
|
9706
9947
|
message: `No font license file found in directory`,
|
|
9707
9948
|
file: fontDir
|
|
@@ -9711,10 +9952,10 @@ var check34 = {
|
|
|
9711
9952
|
return results;
|
|
9712
9953
|
}
|
|
9713
9954
|
};
|
|
9714
|
-
registerCheck(
|
|
9955
|
+
registerCheck(check35);
|
|
9715
9956
|
|
|
9716
9957
|
// src/checks/heuristic/text-density.ts
|
|
9717
|
-
var
|
|
9958
|
+
var check36 = {
|
|
9718
9959
|
id: "heuristic.whitespace.text-density",
|
|
9719
9960
|
name: "Text Density",
|
|
9720
9961
|
description: "Checks characters-per-page ratio",
|
|
@@ -9743,7 +9984,7 @@ var check35 = {
|
|
|
9743
9984
|
});
|
|
9744
9985
|
if (lowPages.length > 0) {
|
|
9745
9986
|
results.push({
|
|
9746
|
-
checkId:
|
|
9987
|
+
checkId: check36.id,
|
|
9747
9988
|
severity: "info",
|
|
9748
9989
|
message: `Low text density on pages: ${lowPages.join(", ")} (below ${range.min} chars)`,
|
|
9749
9990
|
file: ctx.pdfPath
|
|
@@ -9751,7 +9992,7 @@ var check35 = {
|
|
|
9751
9992
|
}
|
|
9752
9993
|
if (highPages.length > 0) {
|
|
9753
9994
|
results.push({
|
|
9754
|
-
checkId:
|
|
9995
|
+
checkId: check36.id,
|
|
9755
9996
|
severity: "info",
|
|
9756
9997
|
message: `High text density on pages: ${highPages.join(", ")} (above ${range.max} chars)`,
|
|
9757
9998
|
file: ctx.pdfPath
|
|
@@ -9760,11 +10001,11 @@ var check35 = {
|
|
|
9760
10001
|
return results;
|
|
9761
10002
|
}
|
|
9762
10003
|
};
|
|
9763
|
-
registerCheck(
|
|
10004
|
+
registerCheck(check36);
|
|
9764
10005
|
|
|
9765
10006
|
// src/checks/heuristic/section-density.ts
|
|
9766
|
-
import { readFile as
|
|
9767
|
-
var
|
|
10007
|
+
import { readFile as readFile26 } from "node:fs/promises";
|
|
10008
|
+
var check37 = {
|
|
9768
10009
|
id: "heuristic.chunking.section-density",
|
|
9769
10010
|
name: "Section Density",
|
|
9770
10011
|
description: "Checks heading/paragraph/callout density from source Markdown",
|
|
@@ -9780,7 +10021,7 @@ var check36 = {
|
|
|
9780
10021
|
const results = [];
|
|
9781
10022
|
for (const file of files) {
|
|
9782
10023
|
try {
|
|
9783
|
-
const content = await
|
|
10024
|
+
const content = await readFile26(file, "utf8");
|
|
9784
10025
|
const lines = content.split(`
|
|
9785
10026
|
`);
|
|
9786
10027
|
let currentSectionStart = 0;
|
|
@@ -9790,7 +10031,7 @@ var check36 = {
|
|
|
9790
10031
|
const line = lines[i];
|
|
9791
10032
|
if (/^#{1,6}\s/.test(line)) {
|
|
9792
10033
|
if (paragraphCount > maxParas) {
|
|
9793
|
-
results.push(finding(
|
|
10034
|
+
results.push(finding(check37.id, {
|
|
9794
10035
|
severity: "info",
|
|
9795
10036
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
9796
10037
|
file,
|
|
@@ -9810,7 +10051,7 @@ var check36 = {
|
|
|
9810
10051
|
}
|
|
9811
10052
|
}
|
|
9812
10053
|
if (paragraphCount > maxParas) {
|
|
9813
|
-
results.push(finding(
|
|
10054
|
+
results.push(finding(check37.id, {
|
|
9814
10055
|
severity: "info",
|
|
9815
10056
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
9816
10057
|
file,
|
|
@@ -9818,7 +10059,7 @@ var check36 = {
|
|
|
9818
10059
|
}));
|
|
9819
10060
|
}
|
|
9820
10061
|
} catch {
|
|
9821
|
-
results.push(inspectionFailed(
|
|
10062
|
+
results.push(inspectionFailed(check37.id, `Could not read source file: ${file}`, {
|
|
9822
10063
|
file
|
|
9823
10064
|
}));
|
|
9824
10065
|
}
|
|
@@ -9826,10 +10067,10 @@ var check36 = {
|
|
|
9826
10067
|
return results;
|
|
9827
10068
|
}
|
|
9828
10069
|
};
|
|
9829
|
-
registerCheck(
|
|
10070
|
+
registerCheck(check37);
|
|
9830
10071
|
|
|
9831
10072
|
// src/checks/heuristic/layer-count.ts
|
|
9832
|
-
var
|
|
10073
|
+
var check38 = {
|
|
9833
10074
|
id: "heuristic.decoration.layer-count",
|
|
9834
10075
|
name: "Layer Count",
|
|
9835
10076
|
description: "Counts image objects per page",
|
|
@@ -9854,7 +10095,7 @@ var check37 = {
|
|
|
9854
10095
|
heavyPages.sort((a, b) => a - b);
|
|
9855
10096
|
return [
|
|
9856
10097
|
{
|
|
9857
|
-
checkId:
|
|
10098
|
+
checkId: check38.id,
|
|
9858
10099
|
severity: "info",
|
|
9859
10100
|
message: `Pages with many image layers (>${maxLayers}): ${heavyPages.join(", ")}`,
|
|
9860
10101
|
file: ctx.pdfPath
|
|
@@ -9864,10 +10105,10 @@ var check37 = {
|
|
|
9864
10105
|
return [];
|
|
9865
10106
|
}
|
|
9866
10107
|
};
|
|
9867
|
-
registerCheck(
|
|
10108
|
+
registerCheck(check38);
|
|
9868
10109
|
|
|
9869
10110
|
// src/checks/heuristic/placement-variance.ts
|
|
9870
|
-
var
|
|
10111
|
+
var check39 = {
|
|
9871
10112
|
id: "heuristic.layout.placement-variance",
|
|
9872
10113
|
name: "Placement Variance",
|
|
9873
10114
|
description: "Analyzes text baseline coordinates for layout consistency",
|
|
@@ -9885,7 +10126,7 @@ var check38 = {
|
|
|
9885
10126
|
const uniqueX = new Set(positions.map((p) => Math.round(p.x)));
|
|
9886
10127
|
return [
|
|
9887
10128
|
{
|
|
9888
|
-
checkId:
|
|
10129
|
+
checkId: check39.id,
|
|
9889
10130
|
severity: "info",
|
|
9890
10131
|
message: `Layout analysis: ${uniqueX.size} unique horizontal text positions across ${positions.length} text blocks.`,
|
|
9891
10132
|
file: ctx.pdfPath
|
|
@@ -9893,7 +10134,7 @@ var check38 = {
|
|
|
9893
10134
|
];
|
|
9894
10135
|
}
|
|
9895
10136
|
};
|
|
9896
|
-
registerCheck(
|
|
10137
|
+
registerCheck(check39);
|
|
9897
10138
|
|
|
9898
10139
|
// src/checks/runner.ts
|
|
9899
10140
|
function getCheckSeverityOverride(checkId, config) {
|
|
@@ -9923,16 +10164,16 @@ async function runChecks(ctx, opts = {}) {
|
|
|
9923
10164
|
}
|
|
9924
10165
|
const release = retainPdfCache();
|
|
9925
10166
|
try {
|
|
9926
|
-
for (const
|
|
10167
|
+
for (const check40 of checks2) {
|
|
9927
10168
|
try {
|
|
9928
|
-
let results = await
|
|
9929
|
-
const severityOverride = getCheckSeverityOverride(
|
|
10169
|
+
let results = await check40.run(ctx);
|
|
10170
|
+
const severityOverride = getCheckSeverityOverride(check40.id, ctx.config);
|
|
9930
10171
|
if (severityOverride) {
|
|
9931
10172
|
for (const r of results) {
|
|
9932
10173
|
r.severity = severityOverride;
|
|
9933
10174
|
}
|
|
9934
10175
|
}
|
|
9935
|
-
const lintLevel = ruleLevel(ctx.config.lint.rules,
|
|
10176
|
+
const lintLevel = ruleLevel(ctx.config.lint.rules, check40.id);
|
|
9936
10177
|
if (lintLevel === "off")
|
|
9937
10178
|
results = [];
|
|
9938
10179
|
else if (lintLevel) {
|
|
@@ -9940,15 +10181,15 @@ async function runChecks(ctx, opts = {}) {
|
|
|
9940
10181
|
r.severity = lintLevel === "warn" ? "warning" : "error";
|
|
9941
10182
|
}
|
|
9942
10183
|
if (results.length === 0) {
|
|
9943
|
-
passed.push(
|
|
10184
|
+
passed.push(check40.id);
|
|
9944
10185
|
} else {
|
|
9945
10186
|
allResults.push(...results);
|
|
9946
10187
|
}
|
|
9947
10188
|
} catch (err) {
|
|
9948
10189
|
allResults.push({
|
|
9949
|
-
checkId:
|
|
10190
|
+
checkId: check40.id,
|
|
9950
10191
|
severity: "error",
|
|
9951
|
-
message: `Check "${
|
|
10192
|
+
message: `Check "${check40.id}" threw: ${err instanceof Error ? err.message : String(err)}`
|
|
9952
10193
|
});
|
|
9953
10194
|
}
|
|
9954
10195
|
}
|
|
@@ -9988,15 +10229,15 @@ function emptyReport() {
|
|
|
9988
10229
|
async function checkToolAvailability(config, opts = {}) {
|
|
9989
10230
|
const { checks: checks2 } = selectChecks(opts, config);
|
|
9990
10231
|
const toolToChecks = new Map;
|
|
9991
|
-
for (const
|
|
9992
|
-
if (!
|
|
10232
|
+
for (const check40 of checks2) {
|
|
10233
|
+
if (!check40.requiredTools?.length)
|
|
9993
10234
|
continue;
|
|
9994
|
-
for (const tool of
|
|
10235
|
+
for (const tool of check40.requiredTools) {
|
|
9995
10236
|
const existing = toolToChecks.get(tool);
|
|
9996
10237
|
if (existing) {
|
|
9997
|
-
existing.push(
|
|
10238
|
+
existing.push(check40.id);
|
|
9998
10239
|
} else {
|
|
9999
|
-
toolToChecks.set(tool, [
|
|
10240
|
+
toolToChecks.set(tool, [check40.id]);
|
|
10000
10241
|
}
|
|
10001
10242
|
}
|
|
10002
10243
|
}
|
|
@@ -10012,11 +10253,11 @@ async function checkToolAvailability(config, opts = {}) {
|
|
|
10012
10253
|
const missing = results.filter((r) => !r.found).map((r) => r.tool);
|
|
10013
10254
|
const missingSet = new Set(missing);
|
|
10014
10255
|
const skippedChecks = [];
|
|
10015
|
-
for (const
|
|
10016
|
-
if (!
|
|
10256
|
+
for (const check40 of checks2) {
|
|
10257
|
+
if (!check40.requiredTools?.length)
|
|
10017
10258
|
continue;
|
|
10018
|
-
if (
|
|
10019
|
-
skippedChecks.push(
|
|
10259
|
+
if (check40.requiredTools.some((t) => missingSet.has(t))) {
|
|
10260
|
+
skippedChecks.push(check40.id);
|
|
10020
10261
|
}
|
|
10021
10262
|
}
|
|
10022
10263
|
return { available, missing, skippedChecks, toolToChecks };
|
|
@@ -10289,7 +10530,7 @@ async function executeValidation(args) {
|
|
|
10289
10530
|
};
|
|
10290
10531
|
if (args.fix) {
|
|
10291
10532
|
const selected = selectChecks(runnerOptions, config).checks;
|
|
10292
|
-
if (selected.some((
|
|
10533
|
+
if (selected.some((check40) => check40.id === MARKDOWNLINT_CHECK_ID)) {
|
|
10293
10534
|
await applyMarkdownlintFixes(context);
|
|
10294
10535
|
} else {
|
|
10295
10536
|
log.warn(`--fix: ${MARKDOWNLINT_CHECK_ID} is not part of this run — no files were fixed.`);
|
|
@@ -10662,7 +10903,7 @@ class PdfOutput {
|
|
|
10662
10903
|
const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
|
|
10663
10904
|
await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
|
|
10664
10905
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
10665
|
-
const { buildNativePdf } = await import("./engine-
|
|
10906
|
+
const { buildNativePdf } = await import("./engine-zehw6tkv.js");
|
|
10666
10907
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
10667
10908
|
title: config.title,
|
|
10668
10909
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
@@ -10687,6 +10928,10 @@ class PdfOutput {
|
|
|
10687
10928
|
if (await hasLiveTransparency(await fsp2.readFile(rawPdf))) {
|
|
10688
10929
|
log.warn("This book uses transparency — an image with an alpha channel, or a CSS " + `rule like \`opacity\` or \`mix-blend-mode\`. PDF/${pdfxMode === "x1a" ? "X-1a" : "X-3"} has no way to represent that (both are based on PDF 1.3, which predates PDF transparency), so Ghostscript will flatten every page that uses it into a single raster ` + "image — that page loses its embedded fonts and searchable text in the " + "PDF/X output. To keep vector text, remove the `opacity`/blend rule and flatten any alpha-channel image against its intended background before including it (export it with no alpha channel), or build --format pdf instead of pdfx if this book doesn't need print-ready CMYK.");
|
|
10689
10930
|
}
|
|
10931
|
+
const blankGradientPages = await findPostScriptShadingPages(await fsp2.readFile(rawPdf));
|
|
10932
|
+
if (blankGradientPages.length > 0) {
|
|
10933
|
+
log.warn(`Page${blankGradientPages.length === 1 ? "" : "s"} ${blankGradientPages.join(", ")}: ` + "a repeating-*-gradient() or conic-gradient() will print blank — " + "Ghostscript cannot convert it to PDF/X. Replace it with a plain linear-gradient() or radial-gradient(), or a small tiled image. For 0deg/90deg stripes, `linear-gradient(90deg, #b33 0 6px, #c8c5bf 6px 10px)` with `background-size: 10px 100%` converts correctly.");
|
|
10934
|
+
}
|
|
10690
10935
|
const shouldStrip = opts.stripAnnotations ?? config.pdfx.stripAnnotations;
|
|
10691
10936
|
shouldStripAnnotations = shouldStrip;
|
|
10692
10937
|
if (shouldStrip) {
|
|
@@ -10741,7 +10986,7 @@ async function runBuild(opts) {
|
|
|
10741
10986
|
if (ctx.format !== "html") {
|
|
10742
10987
|
await preflightBuildTools(ctx.format, opts, ctx.config);
|
|
10743
10988
|
}
|
|
10744
|
-
const launch = opts.engineBrowser ?? (async () => (await import("./cdp-
|
|
10989
|
+
const launch = opts.engineBrowser ?? (async () => (await import("./cdp-h0wa42cz.js")).launchChromium());
|
|
10745
10990
|
const browserPromise = ctx.format === "html" ? null : launch();
|
|
10746
10991
|
browserPromise?.catch(() => {});
|
|
10747
10992
|
try {
|
|
@@ -10768,7 +11013,7 @@ async function runBuild(opts) {
|
|
|
10768
11013
|
import path16 from "path";
|
|
10769
11014
|
import { tmpdir } from "os";
|
|
10770
11015
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
10771
|
-
import { readdir as readdir5, readFile as
|
|
11016
|
+
import { readdir as readdir5, readFile as readFile27, writeFile as writeFile6 } from "node:fs/promises";
|
|
10772
11017
|
|
|
10773
11018
|
// src/utils/file-utils.ts
|
|
10774
11019
|
import { promises as fs3 } from "fs";
|
|
@@ -11320,7 +11565,7 @@ async function cleanupOrphanTempDirs() {
|
|
|
11320
11565
|
const dir = path16.join(TEMP_DIR_BASE, entry);
|
|
11321
11566
|
const pidFile = path16.join(dir, PID_FILE_NAME);
|
|
11322
11567
|
try {
|
|
11323
|
-
const raw = await
|
|
11568
|
+
const raw = await readFile27(pidFile, "utf8");
|
|
11324
11569
|
const pid = parseInt(raw.trim(), 10);
|
|
11325
11570
|
if (!isProcessAlive(pid)) {
|
|
11326
11571
|
debug(`Removing orphan temp dir ${dir} (pid ${pid} not alive)`);
|
|
@@ -11427,7 +11672,7 @@ function createServerState(inputPath, tempDir, config, options) {
|
|
|
11427
11672
|
import http from "node:http";
|
|
11428
11673
|
import net from "node:net";
|
|
11429
11674
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
|
|
11430
|
-
import { readFile as
|
|
11675
|
+
import { readFile as readFile28, stat as stat5 } from "node:fs/promises";
|
|
11431
11676
|
import path17 from "path";
|
|
11432
11677
|
import { WebSocket, WebSocketServer } from "ws";
|
|
11433
11678
|
|
|
@@ -11526,7 +11771,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
11526
11771
|
}
|
|
11527
11772
|
let data;
|
|
11528
11773
|
try {
|
|
11529
|
-
data = await
|
|
11774
|
+
data = await readFile28(filePath);
|
|
11530
11775
|
} catch {
|
|
11531
11776
|
res.writeHead(404);
|
|
11532
11777
|
res.end("Not Found");
|
|
@@ -11575,7 +11820,7 @@ async function serveRevalidatedStatic(req, absPath, res, validators) {
|
|
|
11575
11820
|
let data;
|
|
11576
11821
|
if (!validator || validator.stamp !== stamp) {
|
|
11577
11822
|
try {
|
|
11578
|
-
data = await
|
|
11823
|
+
data = await readFile28(absPath);
|
|
11579
11824
|
} catch {
|
|
11580
11825
|
await serveStatic(absPath, res);
|
|
11581
11826
|
return;
|
|
@@ -11887,7 +12132,7 @@ async function startPreviewServer(options) {
|
|
|
11887
12132
|
import { platform, arch, release } from "node:os";
|
|
11888
12133
|
|
|
11889
12134
|
// src/lib/remote-auth/token-store.ts
|
|
11890
|
-
import { chmod, mkdir as mkdir7, readFile as
|
|
12135
|
+
import { chmod, mkdir as mkdir7, readFile as readFile29, rename as rename4, writeFile as writeFile7 } from "node:fs/promises";
|
|
11891
12136
|
import os3 from "node:os";
|
|
11892
12137
|
import path18 from "node:path";
|
|
11893
12138
|
function credentialHostKey(hostOrUrl) {
|
|
@@ -11931,7 +12176,7 @@ class FileTokenStore {
|
|
|
11931
12176
|
async read() {
|
|
11932
12177
|
let raw;
|
|
11933
12178
|
try {
|
|
11934
|
-
raw = await
|
|
12179
|
+
raw = await readFile29(this.filePath, "utf8");
|
|
11935
12180
|
} catch (e) {
|
|
11936
12181
|
if (e?.code === "ENOENT") {
|
|
11937
12182
|
return { version: 1, credentials: {} };
|
|
@@ -12131,13 +12376,13 @@ async function getSystemDiagnostics() {
|
|
|
12131
12376
|
};
|
|
12132
12377
|
}
|
|
12133
12378
|
// src/lib/project-scaffold.ts
|
|
12134
|
-
import { access, copyFile, cp as cp2, mkdir as mkdir9, readdir as readdir6, readFile as
|
|
12379
|
+
import { access, copyFile, cp as cp2, mkdir as mkdir9, readdir as readdir6, readFile as readFile31, stat as stat6, writeFile as writeFile9 } from "node:fs/promises";
|
|
12135
12380
|
import { constants as FS, existsSync as existsSync14 } from "node:fs";
|
|
12136
12381
|
import path20 from "node:path";
|
|
12137
12382
|
|
|
12138
12383
|
// src/lib/page-size.ts
|
|
12139
12384
|
import { existsSync as existsSync13 } from "node:fs";
|
|
12140
|
-
import { mkdir as mkdir8, readFile as
|
|
12385
|
+
import { mkdir as mkdir8, readFile as readFile30, writeFile as writeFile8 } from "node:fs/promises";
|
|
12141
12386
|
import path19 from "node:path";
|
|
12142
12387
|
import postcss5 from "postcss";
|
|
12143
12388
|
import { Scalar as Scalar2 } from "yaml";
|
|
@@ -12242,7 +12487,7 @@ var relDisplay2 = (projectDir, abs) => path19.relative(projectDir, abs).split(pa
|
|
|
12242
12487
|
async function findCssSize(projectDir, sheets) {
|
|
12243
12488
|
let found = null;
|
|
12244
12489
|
for (const file of sheets) {
|
|
12245
|
-
const size = readPageSizeFromCss(await
|
|
12490
|
+
const size = readPageSizeFromCss(await readFile30(file, "utf8"));
|
|
12246
12491
|
if (size)
|
|
12247
12492
|
found = { file, size };
|
|
12248
12493
|
}
|
|
@@ -12269,7 +12514,7 @@ async function setPageSetup(projectDir, choice) {
|
|
|
12269
12514
|
const found = await findCssSize(projectDir, sheets);
|
|
12270
12515
|
const newSheet = path19.join(projectDir, "styles", "book.css");
|
|
12271
12516
|
const target = found?.file ?? sheets[sheets.length - 1] ?? newSheet;
|
|
12272
|
-
const before = existsSync13(target) ? await
|
|
12517
|
+
const before = existsSync13(target) ? await readFile30(target, "utf8") : "";
|
|
12273
12518
|
let after;
|
|
12274
12519
|
try {
|
|
12275
12520
|
after = setPageSizeInCss(before, size);
|
|
@@ -12559,7 +12804,7 @@ styles:
|
|
|
12559
12804
|
async function firstSourceFile(manifestPath) {
|
|
12560
12805
|
let text;
|
|
12561
12806
|
try {
|
|
12562
|
-
text = await
|
|
12807
|
+
text = await readFile31(manifestPath, "utf8");
|
|
12563
12808
|
} catch {
|
|
12564
12809
|
return;
|
|
12565
12810
|
}
|
|
@@ -12571,14 +12816,14 @@ async function firstSourceFile(manifestPath) {
|
|
|
12571
12816
|
return m ? m[1].trim() : undefined;
|
|
12572
12817
|
}
|
|
12573
12818
|
async function fillTemplateFile(filePath, substitutions) {
|
|
12574
|
-
let text = await
|
|
12819
|
+
let text = await readFile31(filePath, "utf8");
|
|
12575
12820
|
for (const [token, value] of Object.entries(substitutions)) {
|
|
12576
12821
|
text = text.split(token).join(value);
|
|
12577
12822
|
}
|
|
12578
12823
|
await writeFile9(filePath, text, "utf8");
|
|
12579
12824
|
}
|
|
12580
12825
|
// src/lib/extension-scaffold.ts
|
|
12581
|
-
import { access as access2, mkdir as mkdir10, readFile as
|
|
12826
|
+
import { access as access2, mkdir as mkdir10, readFile as readFile32, writeFile as writeFile10 } from "node:fs/promises";
|
|
12582
12827
|
import { constants as FS2 } from "node:fs";
|
|
12583
12828
|
import path21 from "node:path";
|
|
12584
12829
|
var EXTENSION_KINDS = ["plugin", "theme"];
|
|
@@ -12695,7 +12940,7 @@ async function scaffoldExtension(options) {
|
|
|
12695
12940
|
const src = await getAssetPath(`extension-templates/${kind}/${assetName}`);
|
|
12696
12941
|
const dest = path21.join(extensionDir, relDest);
|
|
12697
12942
|
await mkdir10(path21.dirname(dest), { recursive: true });
|
|
12698
|
-
const text = await
|
|
12943
|
+
const text = await readFile32(src, "utf8");
|
|
12699
12944
|
const table = relDest.endsWith(".json") ? jsonSafe : plain;
|
|
12700
12945
|
await writeFile10(dest, substitute(text, table), "utf8");
|
|
12701
12946
|
written.push(relDest);
|
|
@@ -12721,7 +12966,7 @@ function substitute(text, table) {
|
|
|
12721
12966
|
return out;
|
|
12722
12967
|
}
|
|
12723
12968
|
// src/lib/project-templates.ts
|
|
12724
|
-
import { cp as cp3, mkdir as mkdir11, readFile as
|
|
12969
|
+
import { cp as cp3, mkdir as mkdir11, readFile as readFile33, readdir as readdir7, stat as stat7, writeFile as writeFile11 } from "node:fs/promises";
|
|
12725
12970
|
import { constants as FS3, existsSync as existsSync15 } from "node:fs";
|
|
12726
12971
|
import { access as access3 } from "node:fs/promises";
|
|
12727
12972
|
import path22 from "node:path";
|
|
@@ -12733,7 +12978,7 @@ var BUILT_IN_TEMPLATE_IDS = [
|
|
|
12733
12978
|
];
|
|
12734
12979
|
async function readTemplateChoices(manifestPath) {
|
|
12735
12980
|
try {
|
|
12736
|
-
const text = await
|
|
12981
|
+
const text = await readFile33(manifestPath, "utf8");
|
|
12737
12982
|
const doc = parseDocument2(text);
|
|
12738
12983
|
const out = {};
|
|
12739
12984
|
const preset = doc.get("preset");
|
|
@@ -12915,7 +13160,7 @@ async function saveProjectAsTemplate(options) {
|
|
|
12915
13160
|
async function retokeniseManifest(manifestPath) {
|
|
12916
13161
|
let text;
|
|
12917
13162
|
try {
|
|
12918
|
-
text = await
|
|
13163
|
+
text = await readFile33(manifestPath, "utf8");
|
|
12919
13164
|
} catch {
|
|
12920
13165
|
return;
|
|
12921
13166
|
}
|
|
@@ -12936,7 +13181,7 @@ async function listCustomTemplates(templatesRoot) {
|
|
|
12936
13181
|
const dir = path22.join(templatesRoot, entry.name);
|
|
12937
13182
|
let label = prettify(entry.name);
|
|
12938
13183
|
try {
|
|
12939
|
-
const meta = JSON.parse(await
|
|
13184
|
+
const meta = JSON.parse(await readFile33(path22.join(dir, TEMPLATE_META_FILE), "utf8"));
|
|
12940
13185
|
if (typeof meta.label === "string" && meta.label)
|
|
12941
13186
|
label = meta.label;
|
|
12942
13187
|
} catch {}
|
|
@@ -12962,7 +13207,7 @@ async function importTemplateFromFolder(options) {
|
|
|
12962
13207
|
return saveProjectAsTemplate({ projectDir: sourceDir, name, templatesRoot });
|
|
12963
13208
|
}
|
|
12964
13209
|
// src/lib/snippets.ts
|
|
12965
|
-
import { readdir as readdir8, readFile as
|
|
13210
|
+
import { readdir as readdir8, readFile as readFile34, writeFile as writeFile12, mkdir as mkdir12, rm as rm4 } from "node:fs/promises";
|
|
12966
13211
|
import path23 from "node:path";
|
|
12967
13212
|
var SNIPPETS_DIR = "snippets";
|
|
12968
13213
|
var LEVEL_RANK = { project: 0, extension: 1, core: 2 };
|
|
@@ -13003,7 +13248,7 @@ async function scanSnippetFiles(dir) {
|
|
|
13003
13248
|
continue;
|
|
13004
13249
|
let body = "";
|
|
13005
13250
|
try {
|
|
13006
|
-
body = await
|
|
13251
|
+
body = await readFile34(path23.join(dir, fileName), "utf8");
|
|
13007
13252
|
} catch {
|
|
13008
13253
|
continue;
|
|
13009
13254
|
}
|
|
@@ -13092,7 +13337,7 @@ async function collectLibrary(projectDir) {
|
|
|
13092
13337
|
listed.component = name;
|
|
13093
13338
|
continue;
|
|
13094
13339
|
}
|
|
13095
|
-
const body = await
|
|
13340
|
+
const body = await readFile34(path23.join(entry.dir, rel), "utf8").catch(() => null);
|
|
13096
13341
|
if (body === null)
|
|
13097
13342
|
continue;
|
|
13098
13343
|
snippets2.push({
|
|
@@ -13161,7 +13406,7 @@ async function listLocationVariants(projectDir) {
|
|
|
13161
13406
|
}
|
|
13162
13407
|
// src/lib/extension-import.ts
|
|
13163
13408
|
import { existsSync as existsSync16 } from "node:fs";
|
|
13164
|
-
import { cp as cp4, mkdir as mkdir13, mkdtemp as mkdtemp2, readFile as
|
|
13409
|
+
import { cp as cp4, mkdir as mkdir13, mkdtemp as mkdtemp2, readFile as readFile35, rm as rm5, writeFile as writeFile13 } from "node:fs/promises";
|
|
13165
13410
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
13166
13411
|
import path24 from "node:path";
|
|
13167
13412
|
import { unzipSync } from "fflate";
|
|
@@ -13234,7 +13479,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
13234
13479
|
if (!existsSync16(cssPath)) {
|
|
13235
13480
|
throw new Error("A theme must contain a theme.css file.");
|
|
13236
13481
|
}
|
|
13237
|
-
const css = await
|
|
13482
|
+
const css = await readFile35(cssPath, "utf8");
|
|
13238
13483
|
const { reject, warnings: printFindings } = classifyThemeCssFindings(checkCss(css, "theme.css"));
|
|
13239
13484
|
if (reject) {
|
|
13240
13485
|
throw new Error(`theme.css could not be parsed — ${reject.message}`);
|
|
@@ -13262,7 +13507,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
13262
13507
|
for (let i = 0;i < extraSheets.length; i++) {
|
|
13263
13508
|
const rel = extraSheets[i];
|
|
13264
13509
|
const sheetPath = extraSheetPaths[i];
|
|
13265
|
-
const sheetCss = await
|
|
13510
|
+
const sheetCss = await readFile35(sheetPath, "utf8");
|
|
13266
13511
|
const { reject: sheetReject, warnings: sheetFindings } = classifyThemeCssFindings(checkCss(sheetCss, rel));
|
|
13267
13512
|
if (sheetReject) {
|
|
13268
13513
|
throw new Error(`${rel} could not be parsed — ${sheetReject.message}`);
|
|
@@ -13295,7 +13540,7 @@ async function completeLandedManifest(dest, id, meta, metaExists) {
|
|
|
13295
13540
|
let pkg = {};
|
|
13296
13541
|
if (metaExists) {
|
|
13297
13542
|
try {
|
|
13298
|
-
const parsed = JSON.parse(await
|
|
13543
|
+
const parsed = JSON.parse(await readFile35(file, "utf8"));
|
|
13299
13544
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
13300
13545
|
pkg = parsed;
|
|
13301
13546
|
}
|
|
@@ -13397,11 +13642,11 @@ async function importExtensionFromCssText(projectDir, css, name) {
|
|
|
13397
13642
|
async function importExtensionFromFile(projectDir, filePath) {
|
|
13398
13643
|
const ext = path24.extname(filePath).toLowerCase();
|
|
13399
13644
|
if (ext === ".zip") {
|
|
13400
|
-
const buf = await
|
|
13645
|
+
const buf = await readFile35(filePath);
|
|
13401
13646
|
return importExtensionFromZip(projectDir, new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
|
|
13402
13647
|
}
|
|
13403
13648
|
if (ext === ".css") {
|
|
13404
|
-
const css = await
|
|
13649
|
+
const css = await readFile35(filePath, "utf8");
|
|
13405
13650
|
return importExtensionFromCssText(projectDir, css, prettify(path24.basename(filePath, ".css")));
|
|
13406
13651
|
}
|
|
13407
13652
|
throw new Error("Choose a .zip extension package or a .css stylesheet.");
|
|
@@ -13866,8 +14111,8 @@ async function readGoogleApiError(res) {
|
|
|
13866
14111
|
}
|
|
13867
14112
|
var RATE_LIMIT_REASONS = /^(dailyLimitExceeded|userRateLimitExceeded|rateLimitExceeded|quotaExceeded)$/i;
|
|
13868
14113
|
var PERMISSION_REASONS = /^(insufficientPermissions|insufficientFilePermissions|forbidden|PERMISSION_DENIED)$/i;
|
|
13869
|
-
function hintFor(
|
|
13870
|
-
const reason =
|
|
14114
|
+
function hintFor(info3) {
|
|
14115
|
+
const reason = info3.reason ?? "";
|
|
13871
14116
|
if (/^accessNotConfigured$/i.test(reason)) {
|
|
13872
14117
|
return "Google Drive publishing isn't fully set up on this build: the Google Drive API isn't enabled for the app's Google Cloud project, so a maintainer needs to enable it (the link is in Google's message).";
|
|
13873
14118
|
}
|
|
@@ -13877,18 +14122,18 @@ function hintFor(info2) {
|
|
|
13877
14122
|
if (RATE_LIMIT_REASONS.test(reason)) {
|
|
13878
14123
|
return "Google Drive is rate-limiting requests right now. Wait a minute and try again.";
|
|
13879
14124
|
}
|
|
13880
|
-
if (PERMISSION_REASONS.test(reason) ||
|
|
14125
|
+
if (PERMISSION_REASONS.test(reason) || info3.status === 401) {
|
|
13881
14126
|
return "This Google Drive connection doesn't have permission for that — disconnect and connect Google Drive again to re-approve access.";
|
|
13882
14127
|
}
|
|
13883
14128
|
return;
|
|
13884
14129
|
}
|
|
13885
|
-
function googleApiFailure(what,
|
|
13886
|
-
const parts = [`${what} (HTTP ${
|
|
13887
|
-
const hint = hintFor(
|
|
14130
|
+
function googleApiFailure(what, info3) {
|
|
14131
|
+
const parts = [`${what} (HTTP ${info3.status}${info3.reason ? `, ${info3.reason}` : ""}).`];
|
|
14132
|
+
const hint = hintFor(info3);
|
|
13888
14133
|
if (hint)
|
|
13889
14134
|
parts.push(hint);
|
|
13890
|
-
if (
|
|
13891
|
-
parts.push(`Google said: "${
|
|
14135
|
+
if (info3.message)
|
|
14136
|
+
parts.push(`Google said: "${info3.message}"`);
|
|
13892
14137
|
let text = parts.join(" ");
|
|
13893
14138
|
if (!/\bgoogle\b/i.test(text))
|
|
13894
14139
|
text = `Google Drive: ${text}`;
|
|
@@ -14142,13 +14387,13 @@ function publishCredentialKey(host, account) {
|
|
|
14142
14387
|
const a = (account ?? "").trim();
|
|
14143
14388
|
return a ? `${host}#${a}` : host;
|
|
14144
14389
|
}
|
|
14145
|
-
async function resolvePublishCredential(
|
|
14390
|
+
async function resolvePublishCredential(info3, deps, account = deps.credentialAccount) {
|
|
14146
14391
|
const env = deps.env ?? process.env;
|
|
14147
|
-
const fromEnv =
|
|
14392
|
+
const fromEnv = info3.credential.envVar ? env[info3.credential.envVar]?.trim() : undefined;
|
|
14148
14393
|
if (fromEnv) {
|
|
14149
14394
|
return {
|
|
14150
14395
|
credential: {
|
|
14151
|
-
host:
|
|
14396
|
+
host: info3.credential.host,
|
|
14152
14397
|
kind: "token",
|
|
14153
14398
|
token: fromEnv,
|
|
14154
14399
|
createdAt: 0
|
|
@@ -14156,21 +14401,21 @@ async function resolvePublishCredential(info2, deps, account = deps.credentialAc
|
|
|
14156
14401
|
source: "env"
|
|
14157
14402
|
};
|
|
14158
14403
|
}
|
|
14159
|
-
const stored = await deps.tokenStore.get(publishCredentialKey(
|
|
14404
|
+
const stored = await deps.tokenStore.get(publishCredentialKey(info3.credential.host, account));
|
|
14160
14405
|
return stored ? { credential: stored, source: "store" } : null;
|
|
14161
14406
|
}
|
|
14162
|
-
async function publishConnectionStatus(
|
|
14163
|
-
if (!
|
|
14407
|
+
async function publishConnectionStatus(info3, deps, account = deps.credentialAccount) {
|
|
14408
|
+
if (!info3.credential.required)
|
|
14164
14409
|
return { connected: true };
|
|
14165
|
-
const resolved = await resolvePublishCredential(
|
|
14410
|
+
const resolved = await resolvePublishCredential(info3, deps, account);
|
|
14166
14411
|
return resolved ? { connected: true, source: resolved.source } : { connected: false };
|
|
14167
14412
|
}
|
|
14168
|
-
async function listPublishAccounts(
|
|
14169
|
-
const wantHost =
|
|
14413
|
+
async function listPublishAccounts(info3, deps) {
|
|
14414
|
+
const wantHost = info3.credential.host.trim().toLowerCase();
|
|
14170
14415
|
const all = await deps.tokenStore.list();
|
|
14171
14416
|
return all.filter((c) => c.host.trim().toLowerCase() === wantHost).map((c) => ({
|
|
14172
14417
|
account: (c.username ?? "").trim(),
|
|
14173
|
-
label: c.label ?? (c.username ||
|
|
14418
|
+
label: c.label ?? (c.username || info3.label),
|
|
14174
14419
|
createdAt: c.createdAt
|
|
14175
14420
|
}));
|
|
14176
14421
|
}
|
|
@@ -15842,7 +16087,7 @@ async function commandExists(cmd, runCommand = defaultCommandRunner, env) {
|
|
|
15842
16087
|
|
|
15843
16088
|
// src/lib/publish/providers/azure-swa.ts
|
|
15844
16089
|
var AZURE_SWA_HOST = "azure-swa";
|
|
15845
|
-
var
|
|
16090
|
+
var info3 = {
|
|
15846
16091
|
id: "azure-swa",
|
|
15847
16092
|
label: "Azure Static Web Apps",
|
|
15848
16093
|
kind: "api",
|
|
@@ -15867,9 +16112,9 @@ async function resolveSwaCommand(req) {
|
|
|
15867
16112
|
}
|
|
15868
16113
|
var SWA_INSTALL_HINT = "Install the Azure SWA CLI first: `npm install -g @azure/static-web-apps-cli` " + "(needs Node.js), or set SWA_CLI_PATH to the swa binary.";
|
|
15869
16114
|
var azureSwaProvider = {
|
|
15870
|
-
info:
|
|
16115
|
+
info: info3,
|
|
15871
16116
|
async authenticate(req) {
|
|
15872
|
-
const resolved = await resolvePublishCredential(
|
|
16117
|
+
const resolved = await resolvePublishCredential(info3, req.deps);
|
|
15873
16118
|
if (!resolved) {
|
|
15874
16119
|
return {
|
|
15875
16120
|
ok: false,
|
|
@@ -15900,7 +16145,7 @@ var azureSwaProvider = {
|
|
|
15900
16145
|
return issues;
|
|
15901
16146
|
},
|
|
15902
16147
|
async upload(req) {
|
|
15903
|
-
const resolved = await resolvePublishCredential(
|
|
16148
|
+
const resolved = await resolvePublishCredential(info3, req.deps);
|
|
15904
16149
|
if (!resolved) {
|
|
15905
16150
|
throw new Error("No Azure deployment token found. Connect Azure Static Web Apps (or set SWA_CLI_DEPLOYMENT_TOKEN) first.");
|
|
15906
16151
|
}
|
|
@@ -15956,7 +16201,7 @@ async function stageGuidedPackage(req, providerId, listingLines) {
|
|
|
15956
16201
|
|
|
15957
16202
|
// src/lib/publish/providers/drivethrurpg.ts
|
|
15958
16203
|
var PUBLISHER_HUB_URL = "https://www.drivethrurpg.com/publisher_hub";
|
|
15959
|
-
var
|
|
16204
|
+
var info4 = {
|
|
15960
16205
|
id: "drivethrurpg",
|
|
15961
16206
|
label: "DriveThruRPG",
|
|
15962
16207
|
kind: "guided",
|
|
@@ -15972,7 +16217,7 @@ var info3 = {
|
|
|
15972
16217
|
credential: { required: false, host: "drivethrurpg.com" }
|
|
15973
16218
|
};
|
|
15974
16219
|
var drivethrurpgProvider = {
|
|
15975
|
-
info:
|
|
16220
|
+
info: info4,
|
|
15976
16221
|
async authenticate() {
|
|
15977
16222
|
return { ok: true };
|
|
15978
16223
|
},
|
|
@@ -16013,7 +16258,7 @@ var drivethrurpgProvider = {
|
|
|
16013
16258
|
};
|
|
16014
16259
|
|
|
16015
16260
|
// src/lib/publish/providers/gdrive.ts
|
|
16016
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
16261
|
+
import { mkdtemp as mkdtemp3, readFile as readFile36, readdir as readdir11, rm as rm9, stat as stat9, writeFile as writeFile16 } from "node:fs/promises";
|
|
16017
16262
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
16018
16263
|
import path30 from "node:path";
|
|
16019
16264
|
import { Zip, ZipDeflate } from "fflate";
|
|
@@ -16281,7 +16526,7 @@ async function resumableUpload(fetchImpl, accessToken, opts) {
|
|
|
16281
16526
|
// src/lib/publish/providers/gdrive.ts
|
|
16282
16527
|
var DEFAULT_FOLDER_NAME = "Gutterpress";
|
|
16283
16528
|
var NOT_CONNECTED_MESSAGE = "Google Drive isn't connected. Run `gutterpress publish --provider gdrive --connect` (or set GDRIVE_REFRESH_TOKEN) first.";
|
|
16284
|
-
var
|
|
16529
|
+
var info5 = {
|
|
16285
16530
|
id: "gdrive",
|
|
16286
16531
|
label: "Google Drive",
|
|
16287
16532
|
kind: "api",
|
|
@@ -16320,7 +16565,7 @@ async function mintAccessToken(resolved, fetchImpl) {
|
|
|
16320
16565
|
return accessToken;
|
|
16321
16566
|
}
|
|
16322
16567
|
async function getAccessToken(req) {
|
|
16323
|
-
const resolved = await resolvePublishCredential(
|
|
16568
|
+
const resolved = await resolvePublishCredential(info5, req.deps);
|
|
16324
16569
|
if (!resolved)
|
|
16325
16570
|
throw new Error(NOT_CONNECTED_MESSAGE);
|
|
16326
16571
|
const accessToken = await mintAccessToken(resolved, req.deps.fetch ?? globalThis.fetch);
|
|
@@ -16405,12 +16650,12 @@ async function collectZipEntries(root, dir, out, onWarn) {
|
|
|
16405
16650
|
await collectZipEntries(root, full, out, onWarn);
|
|
16406
16651
|
} else if (entry.isFile()) {
|
|
16407
16652
|
const rel = path30.relative(root, full).split(path30.sep).join("/");
|
|
16408
|
-
out[rel] = await
|
|
16653
|
+
out[rel] = await readFile36(full);
|
|
16409
16654
|
} else if (entry.isSymbolicLink()) {
|
|
16410
16655
|
const resolved = await stat9(full).catch(() => null);
|
|
16411
16656
|
if (resolved?.isFile()) {
|
|
16412
16657
|
const rel = path30.relative(root, full).split(path30.sep).join("/");
|
|
16413
|
-
out[rel] = await
|
|
16658
|
+
out[rel] = await readFile36(full);
|
|
16414
16659
|
} else if (resolved?.isDirectory()) {
|
|
16415
16660
|
await collectZipEntries(root, full, out, onWarn);
|
|
16416
16661
|
} else {
|
|
@@ -16442,9 +16687,9 @@ async function zipHtmlExport(exportDir, title, onWarn, writeArchiveImpl = writeF
|
|
|
16442
16687
|
}
|
|
16443
16688
|
}
|
|
16444
16689
|
var gdriveProvider = {
|
|
16445
|
-
info:
|
|
16690
|
+
info: info5,
|
|
16446
16691
|
async authenticate(req) {
|
|
16447
|
-
const resolved = await resolvePublishCredential(
|
|
16692
|
+
const resolved = await resolvePublishCredential(info5, req.deps);
|
|
16448
16693
|
if (!resolved)
|
|
16449
16694
|
return { ok: false, message: NOT_CONNECTED_MESSAGE };
|
|
16450
16695
|
try {
|
|
@@ -16609,7 +16854,7 @@ async function ensureButler(deps) {
|
|
|
16609
16854
|
var ITCH_HOST = "itch.io";
|
|
16610
16855
|
var TARGET_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
|
16611
16856
|
var ITCH_API_TIMEOUT_MS = 15000;
|
|
16612
|
-
var
|
|
16857
|
+
var info6 = {
|
|
16613
16858
|
id: "itch",
|
|
16614
16859
|
label: "itch.io",
|
|
16615
16860
|
kind: "api",
|
|
@@ -16639,9 +16884,9 @@ function itchProjectUrl(target) {
|
|
|
16639
16884
|
return `https://${user}.itch.io/${game}`;
|
|
16640
16885
|
}
|
|
16641
16886
|
var itchProvider = {
|
|
16642
|
-
info:
|
|
16887
|
+
info: info6,
|
|
16643
16888
|
async authenticate(req) {
|
|
16644
|
-
const resolved = await resolvePublishCredential(
|
|
16889
|
+
const resolved = await resolvePublishCredential(info6, req.deps);
|
|
16645
16890
|
if (!resolved) {
|
|
16646
16891
|
return {
|
|
16647
16892
|
ok: false,
|
|
@@ -16709,7 +16954,7 @@ var itchProvider = {
|
|
|
16709
16954
|
},
|
|
16710
16955
|
async upload(req) {
|
|
16711
16956
|
const { target, channel } = readConfig2(req);
|
|
16712
|
-
const resolved = await resolvePublishCredential(
|
|
16957
|
+
const resolved = await resolvePublishCredential(info6, req.deps);
|
|
16713
16958
|
if (!resolved) {
|
|
16714
16959
|
throw new Error("No itch.io API key found. Connect itch.io (or set BUTLER_API_KEY) first.");
|
|
16715
16960
|
}
|
|
@@ -16737,7 +16982,7 @@ ${tail}` : ""}`);
|
|
|
16737
16982
|
|
|
16738
16983
|
// src/lib/publish/providers/kdp.ts
|
|
16739
16984
|
var KDP_URL = "https://kdp.amazon.com/en_US/bookshelf";
|
|
16740
|
-
var
|
|
16985
|
+
var info7 = {
|
|
16741
16986
|
id: "kdp",
|
|
16742
16987
|
label: "Amazon KDP",
|
|
16743
16988
|
kind: "guided",
|
|
@@ -16747,7 +16992,7 @@ var info6 = {
|
|
|
16747
16992
|
credential: { required: false, host: "kdp.amazon.com" }
|
|
16748
16993
|
};
|
|
16749
16994
|
var kdpProvider = {
|
|
16750
|
-
info:
|
|
16995
|
+
info: info7,
|
|
16751
16996
|
async authenticate() {
|
|
16752
16997
|
return { ok: true };
|
|
16753
16998
|
},
|
|
@@ -16792,7 +17037,7 @@ var kdpProvider = {
|
|
|
16792
17037
|
import { cp as cp7, mkdir as mkdir20, stat as stat11 } from "node:fs/promises";
|
|
16793
17038
|
import path32 from "node:path";
|
|
16794
17039
|
var LOCAL_PUBLISH_DEFAULT_DIR = "dist";
|
|
16795
|
-
var
|
|
17040
|
+
var info8 = {
|
|
16796
17041
|
id: "local",
|
|
16797
17042
|
label: "A folder on this computer",
|
|
16798
17043
|
kind: "api",
|
|
@@ -16809,7 +17054,7 @@ function localPublishDir(req) {
|
|
|
16809
17054
|
return path32.resolve(req.project.projectDir, dir);
|
|
16810
17055
|
}
|
|
16811
17056
|
var localFolderProvider = {
|
|
16812
|
-
info:
|
|
17057
|
+
info: info8,
|
|
16813
17058
|
async authenticate() {
|
|
16814
17059
|
return { ok: true };
|
|
16815
17060
|
},
|
|
@@ -16850,7 +17095,7 @@ function requireValidShop(shop) {
|
|
|
16850
17095
|
}
|
|
16851
17096
|
return shop;
|
|
16852
17097
|
}
|
|
16853
|
-
var
|
|
17098
|
+
var info9 = {
|
|
16854
17099
|
id: "shopify",
|
|
16855
17100
|
label: "Shopify",
|
|
16856
17101
|
kind: "api",
|
|
@@ -16884,7 +17129,7 @@ function readConfig3(req) {
|
|
|
16884
17129
|
async function adminGraphQL(req, query, variables = {}) {
|
|
16885
17130
|
const { shop: rawShop, apiVersion } = readConfig3(req);
|
|
16886
17131
|
const shop = requireValidShop(rawShop);
|
|
16887
|
-
const resolved = await resolvePublishCredential(
|
|
17132
|
+
const resolved = await resolvePublishCredential(info9, req.deps);
|
|
16888
17133
|
if (!resolved) {
|
|
16889
17134
|
throw new Error("No Shopify access token found. Connect Shopify (or set SHOPIFY_ADMIN_TOKEN) first.");
|
|
16890
17135
|
}
|
|
@@ -16935,9 +17180,9 @@ function userErrorsToMessage(result) {
|
|
|
16935
17180
|
return errs?.length ? errs[0].message : null;
|
|
16936
17181
|
}
|
|
16937
17182
|
var shopifyProvider = {
|
|
16938
|
-
info:
|
|
17183
|
+
info: info9,
|
|
16939
17184
|
async authenticate(req) {
|
|
16940
|
-
const resolved = await resolvePublishCredential(
|
|
17185
|
+
const resolved = await resolvePublishCredential(info9, req.deps);
|
|
16941
17186
|
if (!resolved) {
|
|
16942
17187
|
return {
|
|
16943
17188
|
ok: false,
|
|
@@ -17071,14 +17316,14 @@ function publishProviderFor(id) {
|
|
|
17071
17316
|
}
|
|
17072
17317
|
|
|
17073
17318
|
// src/lib/publish/run-publish.ts
|
|
17074
|
-
function resolvePublishFormat(
|
|
17075
|
-
if (
|
|
17319
|
+
function resolvePublishFormat(info10, providerConfig) {
|
|
17320
|
+
if (info10.formats && info10.formats.length > 0) {
|
|
17076
17321
|
const configured = typeof providerConfig.format === "string" ? providerConfig.format.trim() : "";
|
|
17077
|
-
if (configured &&
|
|
17322
|
+
if (configured && info10.formats.includes(configured)) {
|
|
17078
17323
|
return configured;
|
|
17079
17324
|
}
|
|
17080
17325
|
}
|
|
17081
|
-
return
|
|
17326
|
+
return info10.format;
|
|
17082
17327
|
}
|
|
17083
17328
|
async function resolvePublishRequest(options, deps) {
|
|
17084
17329
|
const provider = publishProviderFor(options.providerId);
|
|
@@ -17227,17 +17472,17 @@ function overlayStore(inner, key, candidate) {
|
|
|
17227
17472
|
}
|
|
17228
17473
|
async function connectPublishProvider(options, deps) {
|
|
17229
17474
|
const provider = publishProviderFor(options.providerId);
|
|
17230
|
-
const
|
|
17231
|
-
if (!
|
|
17232
|
-
throw new Error(`${
|
|
17475
|
+
const info10 = provider.info;
|
|
17476
|
+
if (!info10.credential.required) {
|
|
17477
|
+
throw new Error(`${info10.label} needs no API key — just publish when you're ready.`);
|
|
17233
17478
|
}
|
|
17234
|
-
if (
|
|
17235
|
-
throw new Error(`${
|
|
17479
|
+
if (info10.credential.connect === "oauth") {
|
|
17480
|
+
throw new Error(`${info10.label} connects through your browser, not a pasted key — run ` + `"gutterpress publish --provider ${info10.id} --connect" or use the desktop app's Connect button.`);
|
|
17236
17481
|
}
|
|
17237
17482
|
const token = options.token.trim();
|
|
17238
17483
|
if (!token)
|
|
17239
17484
|
throw new Error("Paste an API key first.");
|
|
17240
|
-
const host =
|
|
17485
|
+
const host = info10.credential.host;
|
|
17241
17486
|
const account = (options.account ?? "").trim();
|
|
17242
17487
|
const key = publishCredentialKey(host, account);
|
|
17243
17488
|
const candidate = {
|
|
@@ -17245,12 +17490,12 @@ async function connectPublishProvider(options, deps) {
|
|
|
17245
17490
|
kind: "token",
|
|
17246
17491
|
token,
|
|
17247
17492
|
...account ? { username: account } : {},
|
|
17248
|
-
label: account ||
|
|
17493
|
+
label: account || info10.label,
|
|
17249
17494
|
createdAt: Date.now()
|
|
17250
17495
|
};
|
|
17251
17496
|
const req = await resolvePublishRequest({
|
|
17252
17497
|
projectDir: options.projectDir,
|
|
17253
|
-
providerId:
|
|
17498
|
+
providerId: info10.id,
|
|
17254
17499
|
...options.manifestPath ? { manifestPath: options.manifestPath } : {}
|
|
17255
17500
|
}, deps);
|
|
17256
17501
|
const trialDeps = {
|
|
@@ -17261,10 +17506,10 @@ async function connectPublishProvider(options, deps) {
|
|
|
17261
17506
|
};
|
|
17262
17507
|
const auth = await provider.authenticate({ ...req, deps: trialDeps });
|
|
17263
17508
|
if (!auth.ok) {
|
|
17264
|
-
throw new Error(auth.message ?? `${
|
|
17509
|
+
throw new Error(auth.message ?? `${info10.label} didn't accept that key.`);
|
|
17265
17510
|
}
|
|
17266
17511
|
await deps.tokenStore.set(key, candidate);
|
|
17267
|
-
return { connected: true, providerId:
|
|
17512
|
+
return { connected: true, providerId: info10.id };
|
|
17268
17513
|
}
|
|
17269
17514
|
async function disconnectPublishCredential(key, deps, options = {}) {
|
|
17270
17515
|
const existing = await deps.tokenStore.get(key);
|
|
@@ -17276,7 +17521,7 @@ async function disconnectPublishCredential(key, deps, options = {}) {
|
|
|
17276
17521
|
}
|
|
17277
17522
|
}
|
|
17278
17523
|
// src/lib/publish/selections.ts
|
|
17279
|
-
import { chmod as chmod3, mkdir as mkdir21, readFile as
|
|
17524
|
+
import { chmod as chmod3, mkdir as mkdir21, readFile as readFile37, writeFile as writeFile18 } from "node:fs/promises";
|
|
17280
17525
|
import path34 from "node:path";
|
|
17281
17526
|
class PublishSelectionsStore {
|
|
17282
17527
|
filePath;
|
|
@@ -17286,7 +17531,7 @@ class PublishSelectionsStore {
|
|
|
17286
17531
|
}
|
|
17287
17532
|
async read() {
|
|
17288
17533
|
try {
|
|
17289
|
-
const raw = await
|
|
17534
|
+
const raw = await readFile37(this.filePath, "utf8");
|
|
17290
17535
|
const parsed = JSON.parse(raw);
|
|
17291
17536
|
if (parsed && typeof parsed === "object") {
|
|
17292
17537
|
return {
|