gutterpress 0.11.20 → 0.11.21-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/api/index.js +2 -2
- package/dist/{build-t49xka15.js → build-jd5ky1xh.js} +4 -4
- package/dist/checks/asset/extensions.d.ts +9 -5
- package/dist/checks/asset/index.d.ts +0 -3
- package/dist/checks/pdf/index.d.ts +0 -1
- package/dist/checks/types.d.ts +8 -1
- package/dist/{cli-8gm4p42f.js → cli-3zab895b.js} +2 -2
- package/dist/{cli-nh16svye.js → cli-a8azwpgj.js} +1 -1
- package/dist/{cli-cyej3p9v.js → cli-b57adna7.js} +5 -2
- package/dist/{cli-stt6691c.js → cli-xvtqsqy3.js} +268 -429
- package/dist/cli.js +12 -12
- package/dist/{doctor-53hq2v2n.js → doctor-0kgq1ns6.js} +2 -2
- package/dist/{engine-5fw55dfb.js → engine-0b8bt05n.js} +2 -2
- package/dist/{engine-4rkhv5ga.js → engine-x07q927h.js} +1 -1
- package/dist/{ext-bvgfwtk7.js → ext-f90yv3dv.js} +4 -4
- package/dist/{gutterpress-agent-yvd8w8my.js → gutterpress-agent-d29ry3ak.js} +5 -1
- package/dist/{index-379qafjq.js → index-5qam734s.js} +442 -605
- package/dist/{index-crys9951.js → index-mr72vt8n.js} +2 -2
- package/dist/{index-yw16cfm8.js → index-t7r8g75c.js} +4 -1
- package/dist/index.js +3 -3
- package/dist/lib/asset-inline.d.ts +2 -0
- package/dist/lib/image-inspect.d.ts +0 -1
- package/dist/lib/presets.d.ts +0 -3
- package/dist/lib/validation-exec.d.ts +1 -1
- package/dist/{manifest.schema-c4wfpg6g.json → manifest.schema-n292sb77.json} +7 -10
- package/dist/{new-jzpneexb.js → new-fc7nwxea.js} +4 -4
- package/dist/{preflight-c0f0s3y1.js → preflight-hwvj3k3d.js} +4 -4
- package/dist/{preview-x6wn3vdh.js → preview-ktqjnf3d.js} +4 -4
- package/dist/{publish-rskr6x6b.js → publish-nf3d5cbv.js} +4 -4
- package/dist/schema/manifest.types.d.ts +10 -2
- package/dist/{validate-88w89ye7.js → validate-7aywy6m4.js} +4 -4
- package/package.json +1 -1
- package/dist/checks/asset/image-color-space.d.ts +0 -3
- package/dist/checks/asset/image-resolution.d.ts +0 -3
- package/dist/checks/asset/image-tac.d.ts +0 -3
- package/dist/checks/pdf/rasterized-pages.d.ts +0 -3
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
getAssetsDir,
|
|
10
10
|
lastPageMode,
|
|
11
11
|
parseSize
|
|
12
|
-
} from "./index-
|
|
12
|
+
} from "./index-mr72vt8n.js";
|
|
13
13
|
import {
|
|
14
14
|
countLinkAnnotations,
|
|
15
15
|
getImageResolutions,
|
|
@@ -224,8 +224,6 @@ var DTRPG_PRESET = {
|
|
|
224
224
|
},
|
|
225
225
|
assets: {
|
|
226
226
|
maxImageSize: 1e7,
|
|
227
|
-
minImageDpi: 300,
|
|
228
|
-
allowedColorSpaces: ["CMYK", "Grayscale"],
|
|
229
227
|
allowAlpha: false,
|
|
230
228
|
approvedFontFiles: [],
|
|
231
229
|
requireFontLicense: false
|
|
@@ -306,8 +304,6 @@ var BOOK_PRESET = {
|
|
|
306
304
|
},
|
|
307
305
|
assets: {
|
|
308
306
|
maxImageSize: 1e7,
|
|
309
|
-
minImageDpi: 300,
|
|
310
|
-
allowedColorSpaces: ["RGB", "CMYK", "Grayscale"],
|
|
311
307
|
allowAlpha: true,
|
|
312
308
|
approvedFontFiles: [],
|
|
313
309
|
requireFontLicense: false
|
|
@@ -378,9 +374,6 @@ var DTRPG_TARGET = {
|
|
|
378
374
|
"pdf.print.embedded-fonts": { enabled: true, severity: "error" },
|
|
379
375
|
"pdf.print.safe-margins": { enabled: true, severity: "error" }
|
|
380
376
|
},
|
|
381
|
-
assets: {
|
|
382
|
-
allowedColorSpaces: [...DTRPG_PRESET.validate.assets.allowedColorSpaces]
|
|
383
|
-
},
|
|
384
377
|
pdf: {
|
|
385
378
|
forbidTransparency: DTRPG_PRESET.validate.pdf.forbidTransparency,
|
|
386
379
|
minImageResolution: DTRPG_PRESET.validate.pdf.minImageResolution
|
|
@@ -407,16 +400,16 @@ var ITCH_TARGET = {
|
|
|
407
400
|
"pdf.print.pdfx-markers": false,
|
|
408
401
|
"pdf.print.pdfx-metadata": false,
|
|
409
402
|
"pdf.print.ink-coverage": false,
|
|
410
|
-
"pdf.print.safe-margins": false
|
|
411
|
-
"asset.image.tac-raster": false
|
|
403
|
+
"pdf.print.safe-margins": false
|
|
412
404
|
},
|
|
413
405
|
assets: {
|
|
414
|
-
|
|
415
|
-
|
|
406
|
+
allowAlpha: true,
|
|
407
|
+
maxImageSize: 0
|
|
416
408
|
},
|
|
417
409
|
pdf: {
|
|
418
410
|
forbidTransparency: false,
|
|
419
|
-
requireBleed: false
|
|
411
|
+
requireBleed: false,
|
|
412
|
+
minImageResolution: 0
|
|
420
413
|
}
|
|
421
414
|
}
|
|
422
415
|
},
|
|
@@ -652,6 +645,11 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
|
|
|
652
645
|
if ((m.validate?.source?.allowedCallouts?.length ?? 0) > 0 || (c.validate?.source?.allowedCallouts?.length ?? 0) > 0) {
|
|
653
646
|
warnOnce("allowed-callouts-deprecated", "[gutterpress] manifest field `validate.source.allowedCallouts` is " + "deprecated and ignored. The `:::` container syntax it gated was " + "removed 2026-05-17. See docs/migrations/2026-05-removing-container-syntax.md.");
|
|
654
647
|
}
|
|
648
|
+
for (const key of ["minImageDpi", "allowedColorSpaces"]) {
|
|
649
|
+
if (m.validate?.assets?.[key] !== undefined || c.validate?.assets?.[key] !== undefined) {
|
|
650
|
+
warnOnce(`assets-${key}-deprecated`, `[gutterpress] manifest field \`validate.assets.${key}\` is deprecated and ` + "ignored. The source-image check it configured was removed; the build " + "and the PDF checks measure what actually prints.");
|
|
651
|
+
}
|
|
652
|
+
}
|
|
655
653
|
const removed = m;
|
|
656
654
|
if (removed.engine !== undefined) {
|
|
657
655
|
throw new UsageError("Manifest field `engine` was removed — delete it. The Gutterpress engine " + "(native Chromium pagination) is the only engine, so there is nothing to select.");
|
|
@@ -3825,7 +3823,7 @@ import { satisfies, validRange } from "semver";
|
|
|
3825
3823
|
// package.json
|
|
3826
3824
|
var package_default = {
|
|
3827
3825
|
name: "gutterpress",
|
|
3828
|
-
version: "0.11.
|
|
3826
|
+
version: "0.11.21-alpha.1",
|
|
3829
3827
|
description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
|
|
3830
3828
|
author: "itlackey",
|
|
3831
3829
|
license: "MPL-2.0",
|
|
@@ -6495,6 +6493,7 @@ async function writeBuildFingerprint(input) {
|
|
|
6495
6493
|
|
|
6496
6494
|
// src/lib/validation-exec.ts
|
|
6497
6495
|
import { existsSync as existsSync12 } from "node:fs";
|
|
6496
|
+
import { readFile as readFile26 } from "node:fs/promises";
|
|
6498
6497
|
import { dirname as dirname4, join as join4, resolve as resolve8 } from "node:path";
|
|
6499
6498
|
|
|
6500
6499
|
// src/lib/markdownlint-fix.ts
|
|
@@ -6720,6 +6719,156 @@ async function applyMarkdownlintFixes(ctx) {
|
|
|
6720
6719
|
return pending.map((p) => p.file);
|
|
6721
6720
|
}
|
|
6722
6721
|
|
|
6722
|
+
// src/checks/source/local-ref-parser.ts
|
|
6723
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
6724
|
+
|
|
6725
|
+
// src/checks/policy.ts
|
|
6726
|
+
var INSPECT_FAILED_CODE = "inspect-failed";
|
|
6727
|
+
function isCheckEnabled(check2, config) {
|
|
6728
|
+
const entry = config.validate.checks[check2.id];
|
|
6729
|
+
if (entry === false)
|
|
6730
|
+
return false;
|
|
6731
|
+
if (typeof entry === "object" && entry.enabled === false)
|
|
6732
|
+
return false;
|
|
6733
|
+
if (check2.enabledWhen && !check2.enabledWhen(config))
|
|
6734
|
+
return false;
|
|
6735
|
+
return true;
|
|
6736
|
+
}
|
|
6737
|
+
function selectChecks(opts, config) {
|
|
6738
|
+
const unmatched = [];
|
|
6739
|
+
let checks2;
|
|
6740
|
+
if (opts.only && opts.only.length > 0) {
|
|
6741
|
+
const resolved = resolveCheckSelectors(opts.only);
|
|
6742
|
+
checks2 = getChecks({ ids: resolved.resolved });
|
|
6743
|
+
unmatched.push(...resolved.unmatched);
|
|
6744
|
+
} else {
|
|
6745
|
+
checks2 = getChecks({ category: opts.category, phase: opts.phase });
|
|
6746
|
+
}
|
|
6747
|
+
if (opts.skip && opts.skip.length > 0) {
|
|
6748
|
+
const resolved = resolveCheckSelectors(opts.skip);
|
|
6749
|
+
const skipSet = new Set(resolved.resolved);
|
|
6750
|
+
checks2 = checks2.filter((c) => !skipSet.has(c.id));
|
|
6751
|
+
unmatched.push(...resolved.unmatched);
|
|
6752
|
+
}
|
|
6753
|
+
checks2 = checks2.filter((c) => isCheckEnabled(c, config));
|
|
6754
|
+
return { checks: checks2, unmatched };
|
|
6755
|
+
}
|
|
6756
|
+
function finding(checkId, init) {
|
|
6757
|
+
return { checkId, ...init };
|
|
6758
|
+
}
|
|
6759
|
+
function inspectionFailed(checkId, message, extra = {}) {
|
|
6760
|
+
return {
|
|
6761
|
+
checkId,
|
|
6762
|
+
severity: "warning",
|
|
6763
|
+
code: INSPECT_FAILED_CODE,
|
|
6764
|
+
message,
|
|
6765
|
+
...extra
|
|
6766
|
+
};
|
|
6767
|
+
}
|
|
6768
|
+
|
|
6769
|
+
// src/checks/source/local-ref-parser.ts
|
|
6770
|
+
function newlineOffsets(content) {
|
|
6771
|
+
const offsets = [];
|
|
6772
|
+
for (let i = 0;i < content.length; i++)
|
|
6773
|
+
if (content.charCodeAt(i) === 10)
|
|
6774
|
+
offsets.push(i);
|
|
6775
|
+
return offsets;
|
|
6776
|
+
}
|
|
6777
|
+
function lineWithinInline(newlines, offset) {
|
|
6778
|
+
let low = 0;
|
|
6779
|
+
let high = newlines.length;
|
|
6780
|
+
while (low < high) {
|
|
6781
|
+
const middle = low + high >>> 1;
|
|
6782
|
+
if (newlines[middle] < offset)
|
|
6783
|
+
low = middle + 1;
|
|
6784
|
+
else
|
|
6785
|
+
high = middle;
|
|
6786
|
+
}
|
|
6787
|
+
return low;
|
|
6788
|
+
}
|
|
6789
|
+
function createRenderedLocalRefCollector(customPlugins) {
|
|
6790
|
+
const tokenOffsets = new WeakMap;
|
|
6791
|
+
const parser = createMarkdownRenderer(customPlugins);
|
|
6792
|
+
const BaseInlineState = parser.inline.State;
|
|
6793
|
+
|
|
6794
|
+
class PositionedInlineState extends BaseInlineState {
|
|
6795
|
+
push(...args) {
|
|
6796
|
+
const token = super.push(...args);
|
|
6797
|
+
tokenOffsets.set(token, this.pos);
|
|
6798
|
+
return token;
|
|
6799
|
+
}
|
|
6800
|
+
}
|
|
6801
|
+
parser.inline.State = PositionedInlineState;
|
|
6802
|
+
return (content) => {
|
|
6803
|
+
const tokens = parser.parse(content, {});
|
|
6804
|
+
const refs = [];
|
|
6805
|
+
for (const block of tokens) {
|
|
6806
|
+
if (block.type !== "inline" || !block.children || !block.map)
|
|
6807
|
+
continue;
|
|
6808
|
+
const newlines = newlineOffsets(block.content);
|
|
6809
|
+
for (const child of block.children) {
|
|
6810
|
+
const kind = child.type === "image" ? "image" : child.type === "link_open" ? "link" : undefined;
|
|
6811
|
+
if (!kind)
|
|
6812
|
+
continue;
|
|
6813
|
+
const ref = child.attrGet(kind === "image" ? "src" : "href");
|
|
6814
|
+
if (ref === null)
|
|
6815
|
+
continue;
|
|
6816
|
+
const offset = tokenOffsets.get(child);
|
|
6817
|
+
refs.push({
|
|
6818
|
+
ref,
|
|
6819
|
+
kind,
|
|
6820
|
+
line: block.map[0] + (offset === undefined ? 0 : lineWithinInline(newlines, offset)) + 1
|
|
6821
|
+
});
|
|
6822
|
+
}
|
|
6823
|
+
}
|
|
6824
|
+
return refs;
|
|
6825
|
+
};
|
|
6826
|
+
}
|
|
6827
|
+
async function* renderedLocalRefs(ctx, checkId, results) {
|
|
6828
|
+
const files = (ctx.markdownFiles ?? []).slice().sort();
|
|
6829
|
+
if (files.length === 0)
|
|
6830
|
+
return;
|
|
6831
|
+
const plugins = await loadPlugins(ctx.config.extensions, ctx.inputDir, (ref, error2) => {
|
|
6832
|
+
results.push(inspectionFailed(checkId, `Plugin "${ref}" could not be loaded, so local references it defines were not checked: ${error2.message}`));
|
|
6833
|
+
});
|
|
6834
|
+
const collectRenderedLocalRefs = createRenderedLocalRefCollector(plugins);
|
|
6835
|
+
for (const file of files) {
|
|
6836
|
+
let refs;
|
|
6837
|
+
try {
|
|
6838
|
+
refs = collectRenderedLocalRefs(await readFile13(file, "utf8"));
|
|
6839
|
+
} catch {
|
|
6840
|
+
results.push(inspectionFailed(checkId, `Could not read source file: ${file}`, { file }));
|
|
6841
|
+
continue;
|
|
6842
|
+
}
|
|
6843
|
+
for (const ref of refs)
|
|
6844
|
+
yield { ...ref, file };
|
|
6845
|
+
}
|
|
6846
|
+
}
|
|
6847
|
+
|
|
6848
|
+
// src/checks/asset/extensions.ts
|
|
6849
|
+
var RASTER_INSPECTABLE_EXTS = [
|
|
6850
|
+
"png",
|
|
6851
|
+
"jpg",
|
|
6852
|
+
"jpeg",
|
|
6853
|
+
"tiff",
|
|
6854
|
+
"tif"
|
|
6855
|
+
];
|
|
6856
|
+
var ALL_IMAGE_EXTS = [
|
|
6857
|
+
...RASTER_INSPECTABLE_EXTS,
|
|
6858
|
+
"webp",
|
|
6859
|
+
"svg",
|
|
6860
|
+
"gif"
|
|
6861
|
+
];
|
|
6862
|
+
function shippedImages(ctx, exts) {
|
|
6863
|
+
return (ctx.imageFiles ?? []).filter((file) => exts.includes(file.slice(file.lastIndexOf(".") + 1).toLowerCase()));
|
|
6864
|
+
}
|
|
6865
|
+
var FONT_EXTS2 = ["woff", "woff2", "otf", "ttf", "eot"];
|
|
6866
|
+
var ASSET_SCAN_IGNORE_GLOBS = [
|
|
6867
|
+
"**/node_modules/**",
|
|
6868
|
+
"**/.git/**",
|
|
6869
|
+
"**/dist/**"
|
|
6870
|
+
];
|
|
6871
|
+
|
|
6723
6872
|
// src/checks/formatter.ts
|
|
6724
6873
|
function formatReport(report, format = "text") {
|
|
6725
6874
|
if (format === "json") {
|
|
@@ -6779,50 +6928,6 @@ function formatLocation(file, line, column) {
|
|
|
6779
6928
|
return `${loc}: `;
|
|
6780
6929
|
}
|
|
6781
6930
|
|
|
6782
|
-
// src/checks/policy.ts
|
|
6783
|
-
var INSPECT_FAILED_CODE = "inspect-failed";
|
|
6784
|
-
function isCheckEnabled(check2, config) {
|
|
6785
|
-
const entry = config.validate.checks[check2.id];
|
|
6786
|
-
if (entry === false)
|
|
6787
|
-
return false;
|
|
6788
|
-
if (typeof entry === "object" && entry.enabled === false)
|
|
6789
|
-
return false;
|
|
6790
|
-
if (check2.enabledWhen && !check2.enabledWhen(config))
|
|
6791
|
-
return false;
|
|
6792
|
-
return true;
|
|
6793
|
-
}
|
|
6794
|
-
function selectChecks(opts, config) {
|
|
6795
|
-
const unmatched = [];
|
|
6796
|
-
let checks2;
|
|
6797
|
-
if (opts.only && opts.only.length > 0) {
|
|
6798
|
-
const resolved = resolveCheckSelectors(opts.only);
|
|
6799
|
-
checks2 = getChecks({ ids: resolved.resolved });
|
|
6800
|
-
unmatched.push(...resolved.unmatched);
|
|
6801
|
-
} else {
|
|
6802
|
-
checks2 = getChecks({ category: opts.category, phase: opts.phase });
|
|
6803
|
-
}
|
|
6804
|
-
if (opts.skip && opts.skip.length > 0) {
|
|
6805
|
-
const resolved = resolveCheckSelectors(opts.skip);
|
|
6806
|
-
const skipSet = new Set(resolved.resolved);
|
|
6807
|
-
checks2 = checks2.filter((c) => !skipSet.has(c.id));
|
|
6808
|
-
unmatched.push(...resolved.unmatched);
|
|
6809
|
-
}
|
|
6810
|
-
checks2 = checks2.filter((c) => isCheckEnabled(c, config));
|
|
6811
|
-
return { checks: checks2, unmatched };
|
|
6812
|
-
}
|
|
6813
|
-
function finding(checkId, init) {
|
|
6814
|
-
return { checkId, ...init };
|
|
6815
|
-
}
|
|
6816
|
-
function inspectionFailed(checkId, message, extra = {}) {
|
|
6817
|
-
return {
|
|
6818
|
-
checkId,
|
|
6819
|
-
severity: "warning",
|
|
6820
|
-
code: INSPECT_FAILED_CODE,
|
|
6821
|
-
message,
|
|
6822
|
-
...extra
|
|
6823
|
-
};
|
|
6824
|
-
}
|
|
6825
|
-
|
|
6826
6931
|
// src/checks/pdf/qpdf-structure.ts
|
|
6827
6932
|
var check2 = {
|
|
6828
6933
|
id: "pdf.structure.qpdf",
|
|
@@ -7096,9 +7201,9 @@ var check5 = {
|
|
|
7096
7201
|
registerCheck(check5);
|
|
7097
7202
|
|
|
7098
7203
|
// src/lib/pdf-parse.ts
|
|
7099
|
-
import { readFile as
|
|
7204
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
7100
7205
|
async function readPdfBytes(pdfPath) {
|
|
7101
|
-
return
|
|
7206
|
+
return readFile14(pdfPath, "latin1");
|
|
7102
7207
|
}
|
|
7103
7208
|
function parseInkCov(out) {
|
|
7104
7209
|
const lines = out.split(/\r?\n/).filter((l) => l.includes("CMYK"));
|
|
@@ -7304,63 +7409,9 @@ var check8 = {
|
|
|
7304
7409
|
};
|
|
7305
7410
|
registerCheck(check8);
|
|
7306
7411
|
|
|
7307
|
-
// src/checks/pdf/rasterized-pages.ts
|
|
7308
|
-
var check9 = {
|
|
7309
|
-
id: "pdf.print.rasterized-pages",
|
|
7310
|
-
name: "Rasterized Pages",
|
|
7311
|
-
description: "Detects pages that appear to be fully rasterized (CSS filters, blend modes, transparency)",
|
|
7312
|
-
category: "pdf",
|
|
7313
|
-
phase: "post-build",
|
|
7314
|
-
async run(ctx) {
|
|
7315
|
-
if (!ctx.pdfPath)
|
|
7316
|
-
return [];
|
|
7317
|
-
const doc = await loadPdf(ctx.pdfPath);
|
|
7318
|
-
if (!doc)
|
|
7319
|
-
return [];
|
|
7320
|
-
const { imagesByPage } = await getOpPass(doc);
|
|
7321
|
-
const { textByPage } = await getTextPass(doc);
|
|
7322
|
-
const rasterizedPages = [];
|
|
7323
|
-
for (const [pageNum, imgs] of imagesByPage) {
|
|
7324
|
-
if (imgs.length !== 1)
|
|
7325
|
-
continue;
|
|
7326
|
-
let page;
|
|
7327
|
-
try {
|
|
7328
|
-
page = await doc.getPage(pageNum);
|
|
7329
|
-
} catch {
|
|
7330
|
-
continue;
|
|
7331
|
-
}
|
|
7332
|
-
const { w: pageW, h: pageH } = getPageSize(page);
|
|
7333
|
-
const img = imgs[0];
|
|
7334
|
-
const widthMatch = Math.abs(img.placedW - pageW) / pageW < 0.03;
|
|
7335
|
-
const heightMatch = Math.abs(img.placedH - pageH) / pageH < 0.03;
|
|
7336
|
-
if (!widthMatch || !heightMatch)
|
|
7337
|
-
continue;
|
|
7338
|
-
const text = (textByPage[pageNum - 1] ?? "").replace(/\s+/g, "");
|
|
7339
|
-
if (text.length > 20 && text.length < 200) {
|
|
7340
|
-
rasterizedPages.push(pageNum);
|
|
7341
|
-
}
|
|
7342
|
-
}
|
|
7343
|
-
if (rasterizedPages.length === 0)
|
|
7344
|
-
return [];
|
|
7345
|
-
rasterizedPages.sort((a, b) => a - b);
|
|
7346
|
-
return [
|
|
7347
|
-
{
|
|
7348
|
-
checkId: check9.id,
|
|
7349
|
-
severity: "warning",
|
|
7350
|
-
message: `Possible rasterized pages detected: ${rasterizedPages.join(", ")}`,
|
|
7351
|
-
file: ctx.pdfPath,
|
|
7352
|
-
detail: "This may indicate CSS filters, blend modes, or transparency that forced flattening. " + "Text on these pages may not be selectable and quality may be reduced.",
|
|
7353
|
-
code: "rasterized-pages-detected",
|
|
7354
|
-
data: { pages: rasterizedPages }
|
|
7355
|
-
}
|
|
7356
|
-
];
|
|
7357
|
-
}
|
|
7358
|
-
};
|
|
7359
|
-
registerCheck(check9);
|
|
7360
|
-
|
|
7361
7412
|
// src/checks/pdf/black-text.ts
|
|
7362
|
-
import { readFile as
|
|
7363
|
-
var
|
|
7413
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
7414
|
+
var check9 = {
|
|
7364
7415
|
id: "pdf.print.black-text",
|
|
7365
7416
|
name: "Black Text",
|
|
7366
7417
|
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)",
|
|
@@ -7372,11 +7423,11 @@ var check10 = {
|
|
|
7372
7423
|
return [];
|
|
7373
7424
|
try {
|
|
7374
7425
|
const { findRichBlackText, LARGE_TEXT_PT } = await import("./black-text-gray-7dz61456.js");
|
|
7375
|
-
const found = await findRichBlackText(await
|
|
7426
|
+
const found = await findRichBlackText(await readFile15(ctx.pdfPath));
|
|
7376
7427
|
if (found.runs === 0)
|
|
7377
7428
|
return [];
|
|
7378
7429
|
return [
|
|
7379
|
-
finding(
|
|
7430
|
+
finding(check9.id, {
|
|
7380
7431
|
severity: "warning",
|
|
7381
7432
|
message: `Dark four-colour text at ${LARGE_TEXT_PT}pt or smaller on page(s) ${found.pages.join(", ")}.`,
|
|
7382
7433
|
file: ctx.pdfPath,
|
|
@@ -7387,17 +7438,17 @@ var check10 = {
|
|
|
7387
7438
|
];
|
|
7388
7439
|
} catch {
|
|
7389
7440
|
return [
|
|
7390
|
-
inspectionFailed(
|
|
7441
|
+
inspectionFailed(check9.id, "Could not inspect the PDF's text colours.", {
|
|
7391
7442
|
file: ctx.pdfPath
|
|
7392
7443
|
})
|
|
7393
7444
|
];
|
|
7394
7445
|
}
|
|
7395
7446
|
}
|
|
7396
7447
|
};
|
|
7397
|
-
registerCheck(
|
|
7448
|
+
registerCheck(check9);
|
|
7398
7449
|
|
|
7399
7450
|
// src/checks/pdf/signature.ts
|
|
7400
|
-
var
|
|
7451
|
+
var check10 = {
|
|
7401
7452
|
id: "pdf.print.signature",
|
|
7402
7453
|
name: "Signature",
|
|
7403
7454
|
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)",
|
|
@@ -7409,7 +7460,7 @@ var check11 = {
|
|
|
7409
7460
|
return [];
|
|
7410
7461
|
const doc = await loadPdf(ctx.pdfPath);
|
|
7411
7462
|
if (!doc) {
|
|
7412
|
-
return [inspectionFailed(
|
|
7463
|
+
return [inspectionFailed(check10.id, "Could not parse the PDF's page count.", { file: ctx.pdfPath })];
|
|
7413
7464
|
}
|
|
7414
7465
|
const size = ctx.config.print.signature;
|
|
7415
7466
|
const pages = doc.numPages;
|
|
@@ -7418,7 +7469,7 @@ var check11 = {
|
|
|
7418
7469
|
const data = { pages, signature: size };
|
|
7419
7470
|
if (pages % size !== 0) {
|
|
7420
7471
|
return [
|
|
7421
|
-
finding(
|
|
7472
|
+
finding(check10.id, {
|
|
7422
7473
|
severity: "error",
|
|
7423
7474
|
message: `Page count ${pages} does not fit a signature of ${size}: it must be a multiple of ${size}, or one short of one.`,
|
|
7424
7475
|
file: ctx.pdfPath,
|
|
@@ -7434,10 +7485,10 @@ var check11 = {
|
|
|
7434
7485
|
if (!await pageHasMarks(doc, pages))
|
|
7435
7486
|
return [];
|
|
7436
7487
|
} catch {
|
|
7437
|
-
return [inspectionFailed(
|
|
7488
|
+
return [inspectionFailed(check10.id, "Could not read the last page.", { file: ctx.pdfPath })];
|
|
7438
7489
|
}
|
|
7439
7490
|
return [
|
|
7440
|
-
finding(
|
|
7491
|
+
finding(check10.id, {
|
|
7441
7492
|
severity: "error",
|
|
7442
7493
|
message: `The last page (${pages}) has content, but the printer needs it blank for its own information.`,
|
|
7443
7494
|
file: ctx.pdfPath,
|
|
@@ -7448,10 +7499,10 @@ var check11 = {
|
|
|
7448
7499
|
];
|
|
7449
7500
|
}
|
|
7450
7501
|
};
|
|
7451
|
-
registerCheck(
|
|
7502
|
+
registerCheck(check10);
|
|
7452
7503
|
|
|
7453
7504
|
// src/checks/pdf/bookmarks.ts
|
|
7454
|
-
var
|
|
7505
|
+
var check11 = {
|
|
7455
7506
|
id: "pdf.nav.bookmarks",
|
|
7456
7507
|
name: "Bookmarks",
|
|
7457
7508
|
description: "Checks for PDF outline (bookmarks) tree",
|
|
@@ -7465,14 +7516,14 @@ var check12 = {
|
|
|
7465
7516
|
const doc = await loadPdf(ctx.pdfPath);
|
|
7466
7517
|
if (!doc) {
|
|
7467
7518
|
return [
|
|
7468
|
-
inspectionFailed(
|
|
7519
|
+
inspectionFailed(check11.id, "Could not inspect PDF for bookmarks.", {
|
|
7469
7520
|
file: ctx.pdfPath
|
|
7470
7521
|
})
|
|
7471
7522
|
];
|
|
7472
7523
|
}
|
|
7473
7524
|
if (await getOutlineCount(doc) === 0) {
|
|
7474
7525
|
return [
|
|
7475
|
-
finding(
|
|
7526
|
+
finding(check11.id, {
|
|
7476
7527
|
severity: "warning",
|
|
7477
7528
|
message: "PDF does not contain bookmarks (outline tree).",
|
|
7478
7529
|
file: ctx.pdfPath
|
|
@@ -7482,10 +7533,10 @@ var check12 = {
|
|
|
7482
7533
|
return [];
|
|
7483
7534
|
}
|
|
7484
7535
|
};
|
|
7485
|
-
registerCheck(
|
|
7536
|
+
registerCheck(check11);
|
|
7486
7537
|
|
|
7487
7538
|
// src/checks/pdf/toc-links.ts
|
|
7488
|
-
var
|
|
7539
|
+
var check12 = {
|
|
7489
7540
|
id: "pdf.nav.toc-links",
|
|
7490
7541
|
name: "TOC Links",
|
|
7491
7542
|
description: "Detects link annotations on TOC pages",
|
|
@@ -7500,7 +7551,7 @@ var check13 = {
|
|
|
7500
7551
|
if (!doc) {
|
|
7501
7552
|
return [
|
|
7502
7553
|
{
|
|
7503
|
-
checkId:
|
|
7554
|
+
checkId: check12.id,
|
|
7504
7555
|
severity: "warning",
|
|
7505
7556
|
message: "Could not inspect PDF for TOC links.",
|
|
7506
7557
|
file: ctx.pdfPath
|
|
@@ -7511,7 +7562,7 @@ var check13 = {
|
|
|
7511
7562
|
if (!hasLinks) {
|
|
7512
7563
|
return [
|
|
7513
7564
|
{
|
|
7514
|
-
checkId:
|
|
7565
|
+
checkId: check12.id,
|
|
7515
7566
|
severity: "warning",
|
|
7516
7567
|
message: "No link annotations found in PDF (expected for TOC).",
|
|
7517
7568
|
file: ctx.pdfPath
|
|
@@ -7521,10 +7572,10 @@ var check13 = {
|
|
|
7521
7572
|
return [];
|
|
7522
7573
|
}
|
|
7523
7574
|
};
|
|
7524
|
-
registerCheck(
|
|
7575
|
+
registerCheck(check12);
|
|
7525
7576
|
|
|
7526
7577
|
// src/checks/pdf/cross-refs.ts
|
|
7527
|
-
var
|
|
7578
|
+
var check13 = {
|
|
7528
7579
|
id: "pdf.nav.cross-refs",
|
|
7529
7580
|
name: "Cross References",
|
|
7530
7581
|
description: "Counts and verifies internal link annotations",
|
|
@@ -7537,7 +7588,7 @@ var check14 = {
|
|
|
7537
7588
|
if (!doc) {
|
|
7538
7589
|
return [
|
|
7539
7590
|
{
|
|
7540
|
-
checkId:
|
|
7591
|
+
checkId: check13.id,
|
|
7541
7592
|
severity: "info",
|
|
7542
7593
|
message: "Could not inspect PDF for cross-references.",
|
|
7543
7594
|
file: ctx.pdfPath
|
|
@@ -7547,7 +7598,7 @@ var check14 = {
|
|
|
7547
7598
|
const linkCount = await countLinkAnnotations(doc);
|
|
7548
7599
|
return [
|
|
7549
7600
|
{
|
|
7550
|
-
checkId:
|
|
7601
|
+
checkId: check13.id,
|
|
7551
7602
|
severity: "info",
|
|
7552
7603
|
message: `PDF contains ${linkCount} internal link annotation${linkCount !== 1 ? "s" : ""}.`,
|
|
7553
7604
|
file: ctx.pdfPath
|
|
@@ -7555,10 +7606,10 @@ var check14 = {
|
|
|
7555
7606
|
];
|
|
7556
7607
|
}
|
|
7557
7608
|
};
|
|
7558
|
-
registerCheck(
|
|
7609
|
+
registerCheck(check13);
|
|
7559
7610
|
|
|
7560
7611
|
// src/checks/pdf/page-labels.ts
|
|
7561
|
-
var
|
|
7612
|
+
var check14 = {
|
|
7562
7613
|
id: "pdf.nav.page-labels",
|
|
7563
7614
|
name: "Page Labels",
|
|
7564
7615
|
description: "Verifies PDF page labels / folio numbering",
|
|
@@ -7573,7 +7624,7 @@ var check15 = {
|
|
|
7573
7624
|
const labels = await getPageLabels(doc);
|
|
7574
7625
|
return [
|
|
7575
7626
|
{
|
|
7576
|
-
checkId:
|
|
7627
|
+
checkId: check14.id,
|
|
7577
7628
|
severity: "info",
|
|
7578
7629
|
message: labels && labels.length > 0 ? "PDF contains page labels." : "PDF does not contain explicit page labels.",
|
|
7579
7630
|
file: ctx.pdfPath
|
|
@@ -7581,10 +7632,10 @@ var check15 = {
|
|
|
7581
7632
|
];
|
|
7582
7633
|
}
|
|
7583
7634
|
};
|
|
7584
|
-
registerCheck(
|
|
7635
|
+
registerCheck(check14);
|
|
7585
7636
|
|
|
7586
7637
|
// src/checks/pdf/image-resolution.ts
|
|
7587
|
-
var
|
|
7638
|
+
var check15 = {
|
|
7588
7639
|
id: "pdf.print.image-resolution",
|
|
7589
7640
|
name: "Image Resolution",
|
|
7590
7641
|
description: "Checks DPI of images embedded in the PDF",
|
|
@@ -7611,7 +7662,7 @@ var check16 = {
|
|
|
7611
7662
|
lowResPages.sort((a, b) => a - b);
|
|
7612
7663
|
return [
|
|
7613
7664
|
{
|
|
7614
|
-
checkId:
|
|
7665
|
+
checkId: check15.id,
|
|
7615
7666
|
severity: "warning",
|
|
7616
7667
|
message: `Low-resolution images (below ${minDpi} DPI) found on pages: ${lowResPages.join(", ")}`,
|
|
7617
7668
|
file: ctx.pdfPath
|
|
@@ -7621,10 +7672,10 @@ var check16 = {
|
|
|
7621
7672
|
return [];
|
|
7622
7673
|
}
|
|
7623
7674
|
};
|
|
7624
|
-
registerCheck(
|
|
7675
|
+
registerCheck(check15);
|
|
7625
7676
|
|
|
7626
7677
|
// src/checks/pdf/transparency.ts
|
|
7627
|
-
var
|
|
7678
|
+
var check16 = {
|
|
7628
7679
|
id: "pdf.print.transparency",
|
|
7629
7680
|
name: "Transparency",
|
|
7630
7681
|
description: "Checks for transparency markers (/Transparency, /SMask, /BM) in the PDF",
|
|
@@ -7646,7 +7697,7 @@ var check17 = {
|
|
|
7646
7697
|
if (found.length > 0) {
|
|
7647
7698
|
return [
|
|
7648
7699
|
{
|
|
7649
|
-
checkId:
|
|
7700
|
+
checkId: check16.id,
|
|
7650
7701
|
severity: "warning",
|
|
7651
7702
|
message: `Transparency detected in PDF: ${found.join(", ")}. This may cause issues with some printers.`,
|
|
7652
7703
|
file: ctx.pdfPath
|
|
@@ -7656,7 +7707,7 @@ var check17 = {
|
|
|
7656
7707
|
return [];
|
|
7657
7708
|
}
|
|
7658
7709
|
};
|
|
7659
|
-
registerCheck(
|
|
7710
|
+
registerCheck(check16);
|
|
7660
7711
|
|
|
7661
7712
|
// src/checks/pdf/bleed.ts
|
|
7662
7713
|
function findBox(bytes, box) {
|
|
@@ -7666,7 +7717,7 @@ function findBox(bytes, box) {
|
|
|
7666
7717
|
return null;
|
|
7667
7718
|
return [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])];
|
|
7668
7719
|
}
|
|
7669
|
-
var
|
|
7720
|
+
var check17 = {
|
|
7670
7721
|
id: "pdf.print.bleed",
|
|
7671
7722
|
name: "Bleed Box",
|
|
7672
7723
|
description: "Compares MediaBox vs TrimBox/BleedBox to verify bleed area",
|
|
@@ -7680,7 +7731,7 @@ var check18 = {
|
|
|
7680
7731
|
const bytes = await readPdfBytes(ctx.pdfPath).catch(() => "");
|
|
7681
7732
|
if (!bytes) {
|
|
7682
7733
|
return [
|
|
7683
|
-
inspectionFailed(
|
|
7734
|
+
inspectionFailed(check17.id, "Could not inspect PDF for bleed boxes.", {
|
|
7684
7735
|
file: ctx.pdfPath
|
|
7685
7736
|
})
|
|
7686
7737
|
];
|
|
@@ -7690,14 +7741,14 @@ var check18 = {
|
|
|
7690
7741
|
const bleed = findBox(bytes, "BleedBox");
|
|
7691
7742
|
if (!media) {
|
|
7692
7743
|
return [
|
|
7693
|
-
inspectionFailed(
|
|
7744
|
+
inspectionFailed(check17.id, "Could not parse MediaBox from PDF.", {
|
|
7694
7745
|
file: ctx.pdfPath
|
|
7695
7746
|
})
|
|
7696
7747
|
];
|
|
7697
7748
|
}
|
|
7698
7749
|
if (!trim && !bleed) {
|
|
7699
7750
|
return [
|
|
7700
|
-
finding(
|
|
7751
|
+
finding(check17.id, {
|
|
7701
7752
|
severity: "warning",
|
|
7702
7753
|
message: "No TrimBox or BleedBox found. Bleed area cannot be verified.",
|
|
7703
7754
|
file: ctx.pdfPath
|
|
@@ -7710,7 +7761,7 @@ var check18 = {
|
|
|
7710
7761
|
const bleedSize = ctx.config.validate.pdf.bleedSize;
|
|
7711
7762
|
if (mediaW - trimW < bleedSize * 2 * 0.9) {
|
|
7712
7763
|
return [
|
|
7713
|
-
finding(
|
|
7764
|
+
finding(check17.id, {
|
|
7714
7765
|
severity: "warning",
|
|
7715
7766
|
message: `Bleed area appears insufficient. Expected at least ${bleedSize}pt on each side.`,
|
|
7716
7767
|
file: ctx.pdfPath
|
|
@@ -7721,10 +7772,10 @@ var check18 = {
|
|
|
7721
7772
|
return [];
|
|
7722
7773
|
}
|
|
7723
7774
|
};
|
|
7724
|
-
registerCheck(
|
|
7775
|
+
registerCheck(check17);
|
|
7725
7776
|
|
|
7726
7777
|
// src/checks/pdf/pdfx-metadata.ts
|
|
7727
|
-
var
|
|
7778
|
+
var check18 = {
|
|
7728
7779
|
id: "pdf.print.pdfx-metadata",
|
|
7729
7780
|
name: "PDF/X Metadata",
|
|
7730
7781
|
description: "Verifies PDF/X DOCINFO metadata via qpdf JSON",
|
|
@@ -7743,30 +7794,30 @@ var check19 = {
|
|
|
7743
7794
|
const objects = parseQpdfObjectsJson(stdout);
|
|
7744
7795
|
if (!objects) {
|
|
7745
7796
|
return [
|
|
7746
|
-
inspectionFailed(
|
|
7797
|
+
inspectionFailed(check18.id, "Unable to parse qpdf JSON output for PDF/X metadata checks.", { file: ctx.pdfPath })
|
|
7747
7798
|
];
|
|
7748
7799
|
}
|
|
7749
|
-
return getPdfxMetadataIssues(objects, ctx.config.pdfx.flavor).map((message) => finding(
|
|
7800
|
+
return getPdfxMetadataIssues(objects, ctx.config.pdfx.flavor).map((message) => finding(check18.id, {
|
|
7750
7801
|
severity: "error",
|
|
7751
7802
|
message,
|
|
7752
7803
|
file: ctx.pdfPath
|
|
7753
7804
|
}));
|
|
7754
7805
|
} catch {
|
|
7755
7806
|
return [
|
|
7756
|
-
inspectionFailed(
|
|
7807
|
+
inspectionFailed(check18.id, "Failed to inspect PDF objects with qpdf for PDF/X metadata.", { file: ctx.pdfPath })
|
|
7757
7808
|
];
|
|
7758
7809
|
}
|
|
7759
7810
|
}
|
|
7760
7811
|
};
|
|
7761
|
-
registerCheck(
|
|
7812
|
+
registerCheck(check18);
|
|
7762
7813
|
|
|
7763
7814
|
// src/checks/source/htmlhint.ts
|
|
7764
7815
|
import { existsSync as existsSync9 } from "node:fs";
|
|
7765
|
-
import { readFile as
|
|
7816
|
+
import { readFile as readFile16 } from "node:fs/promises";
|
|
7766
7817
|
import htmlhint from "htmlhint";
|
|
7767
7818
|
var { HTMLHint } = htmlhint;
|
|
7768
7819
|
var CONFIG_NAMES2 = [".htmlhintrc"];
|
|
7769
|
-
var
|
|
7820
|
+
var check19 = {
|
|
7770
7821
|
id: "source.htmlhint",
|
|
7771
7822
|
name: "HTMLHint",
|
|
7772
7823
|
description: "Runs htmlhint with project config to validate generated HTML",
|
|
@@ -7782,11 +7833,11 @@ var check20 = {
|
|
|
7782
7833
|
const resolvedConfig = findConfigFile(ctx.inputDir, CONFIG_NAMES2, configPath);
|
|
7783
7834
|
if (!resolvedConfig && !configPath)
|
|
7784
7835
|
return [];
|
|
7785
|
-
const ruleset = resolvedConfig ? JSON.parse(await
|
|
7786
|
-
const html = await
|
|
7836
|
+
const ruleset = resolvedConfig ? JSON.parse(await readFile16(resolvedConfig, "utf8")) : HTMLHint.defaultRuleset;
|
|
7837
|
+
const html = await readFile16(ctx.htmlPath, "utf8");
|
|
7787
7838
|
const messages = HTMLHint.verify(html, ruleset);
|
|
7788
7839
|
return messages.map((m) => ({
|
|
7789
|
-
checkId:
|
|
7840
|
+
checkId: check19.id,
|
|
7790
7841
|
severity: m.type === "error" ? "error" : "warning",
|
|
7791
7842
|
message: `${m.rule.id}: ${m.message}`,
|
|
7792
7843
|
file: ctx.htmlPath,
|
|
@@ -7795,10 +7846,10 @@ var check20 = {
|
|
|
7795
7846
|
}));
|
|
7796
7847
|
}
|
|
7797
7848
|
};
|
|
7798
|
-
registerCheck(
|
|
7849
|
+
registerCheck(check19);
|
|
7799
7850
|
|
|
7800
7851
|
// src/checks/source/stylelint.ts
|
|
7801
|
-
import { readFile as
|
|
7852
|
+
import { readFile as readFile17 } from "node:fs/promises";
|
|
7802
7853
|
|
|
7803
7854
|
// src/lib/printsafe.ts
|
|
7804
7855
|
import postcss2 from "postcss";
|
|
@@ -7914,10 +7965,27 @@ function isUnpairedFitContentWidth(decl) {
|
|
|
7914
7965
|
return true;
|
|
7915
7966
|
}
|
|
7916
7967
|
function targetsPageWrapper(selector) {
|
|
7917
|
-
if (!/\.(page|spread)(?![\w-])/.test(selector))
|
|
7968
|
+
if (!/\.(page|spread)(?![\w-])/.test(subjectCompound(selector)))
|
|
7918
7969
|
return false;
|
|
7919
7970
|
return !/\bgp-(bleed|pin)\b/.test(selector);
|
|
7920
7971
|
}
|
|
7972
|
+
function subjectCompound(selector) {
|
|
7973
|
+
let depth = 0;
|
|
7974
|
+
let start = 0;
|
|
7975
|
+
for (let i = 0;i < selector.length; i++) {
|
|
7976
|
+
const c = selector[i];
|
|
7977
|
+
if (c === "(" || c === "[")
|
|
7978
|
+
depth++;
|
|
7979
|
+
else if (c === ")" || c === "]")
|
|
7980
|
+
depth--;
|
|
7981
|
+
else if (depth === 0 && /[\s>+~]/.test(c))
|
|
7982
|
+
start = i + 1;
|
|
7983
|
+
}
|
|
7984
|
+
let subject = selector.slice(start);
|
|
7985
|
+
while (/\([^()]*\)/.test(subject))
|
|
7986
|
+
subject = subject.replace(/\([^()]*\)/g, "");
|
|
7987
|
+
return subject;
|
|
7988
|
+
}
|
|
7921
7989
|
var cssWideResetKeywords = new Set(["initial", "unset", "revert", "revert-layer"]);
|
|
7922
7990
|
var inertValues = {
|
|
7923
7991
|
"z-index": new Set(["auto"]),
|
|
@@ -8164,7 +8232,7 @@ function checkCssRaw(css, from) {
|
|
|
8164
8232
|
if (at === "page" || marginBoxAtRuleNames.has(at))
|
|
8165
8233
|
return;
|
|
8166
8234
|
}
|
|
8167
|
-
if (!
|
|
8235
|
+
if (!rule.selectors.some(targetsPageWrapper))
|
|
8168
8236
|
return;
|
|
8169
8237
|
rule.walkDecls((decl) => {
|
|
8170
8238
|
if (clipsDescendants(decl)) {
|
|
@@ -8188,7 +8256,7 @@ function checkCssRaw(css, from) {
|
|
|
8188
8256
|
}
|
|
8189
8257
|
|
|
8190
8258
|
// src/checks/source/stylelint.ts
|
|
8191
|
-
var
|
|
8259
|
+
var check20 = {
|
|
8192
8260
|
id: "source.stylelint",
|
|
8193
8261
|
name: "Print-safety CSS",
|
|
8194
8262
|
description: "Checks CSS for print-safety issues (remote URLs, rasterizing effects, and page-layer containment)",
|
|
@@ -8205,10 +8273,10 @@ var check21 = {
|
|
|
8205
8273
|
for (const file of files) {
|
|
8206
8274
|
let css;
|
|
8207
8275
|
try {
|
|
8208
|
-
css = await
|
|
8276
|
+
css = await readFile17(file, "utf8");
|
|
8209
8277
|
} catch (err) {
|
|
8210
8278
|
results.push({
|
|
8211
|
-
checkId:
|
|
8279
|
+
checkId: check20.id,
|
|
8212
8280
|
severity: "error",
|
|
8213
8281
|
message: `Cannot read stylesheet: ${err instanceof Error ? err.message : String(err)}`,
|
|
8214
8282
|
file
|
|
@@ -8217,7 +8285,7 @@ var check21 = {
|
|
|
8217
8285
|
}
|
|
8218
8286
|
for (const w of checkCss(css, file, { rules: ctx.config.lint.rules })) {
|
|
8219
8287
|
results.push({
|
|
8220
|
-
checkId:
|
|
8288
|
+
checkId: check20.id,
|
|
8221
8289
|
severity: w.severity,
|
|
8222
8290
|
message: `${w.rule}: ${w.message}`,
|
|
8223
8291
|
file,
|
|
@@ -8229,15 +8297,15 @@ var check21 = {
|
|
|
8229
8297
|
return results;
|
|
8230
8298
|
}
|
|
8231
8299
|
};
|
|
8232
|
-
registerCheck(
|
|
8300
|
+
registerCheck(check20);
|
|
8233
8301
|
|
|
8234
8302
|
// src/checks/source/css-ownership.ts
|
|
8235
|
-
import { readFile as
|
|
8303
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
8236
8304
|
import postcss3 from "postcss";
|
|
8237
8305
|
import { parse as parseYaml3 } from "yaml";
|
|
8238
8306
|
var CONFIG_NAMES3 = [".gutterpress/css-contract.yaml", ".gutterpress/css-contract.yml"];
|
|
8239
8307
|
async function loadContract(configPath) {
|
|
8240
|
-
const raw = await
|
|
8308
|
+
const raw = await readFile18(configPath, "utf8");
|
|
8241
8309
|
const parsed = parseYaml3(raw);
|
|
8242
8310
|
if (parsed && typeof parsed === "object")
|
|
8243
8311
|
return parsed;
|
|
@@ -8350,7 +8418,7 @@ function checkOwnership(root, file, maps) {
|
|
|
8350
8418
|
});
|
|
8351
8419
|
return out;
|
|
8352
8420
|
}
|
|
8353
|
-
var
|
|
8421
|
+
var check21 = {
|
|
8354
8422
|
id: "source.css-ownership",
|
|
8355
8423
|
name: "CSS ownership contract",
|
|
8356
8424
|
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.",
|
|
@@ -8373,7 +8441,7 @@ var check22 = {
|
|
|
8373
8441
|
} catch (err) {
|
|
8374
8442
|
return [
|
|
8375
8443
|
{
|
|
8376
|
-
checkId:
|
|
8444
|
+
checkId: check21.id,
|
|
8377
8445
|
severity: "error",
|
|
8378
8446
|
message: `Failed to parse CSS ownership contract "${configPath}": ${err instanceof Error ? err.message : String(err)}`,
|
|
8379
8447
|
file: configPath
|
|
@@ -8388,7 +8456,7 @@ var check22 = {
|
|
|
8388
8456
|
continue;
|
|
8389
8457
|
let css;
|
|
8390
8458
|
try {
|
|
8391
|
-
css = await
|
|
8459
|
+
css = await readFile18(absFile, "utf8");
|
|
8392
8460
|
} catch {
|
|
8393
8461
|
continue;
|
|
8394
8462
|
}
|
|
@@ -8415,7 +8483,7 @@ var check22 = {
|
|
|
8415
8483
|
}
|
|
8416
8484
|
for (const v of violations) {
|
|
8417
8485
|
results.push({
|
|
8418
|
-
checkId:
|
|
8486
|
+
checkId: check21.id,
|
|
8419
8487
|
severity: "warning",
|
|
8420
8488
|
message: v.message,
|
|
8421
8489
|
file: absFile,
|
|
@@ -8427,94 +8495,12 @@ var check22 = {
|
|
|
8427
8495
|
return results;
|
|
8428
8496
|
}
|
|
8429
8497
|
};
|
|
8430
|
-
registerCheck(
|
|
8498
|
+
registerCheck(check21);
|
|
8431
8499
|
|
|
8432
8500
|
// src/checks/source/local-refs.ts
|
|
8433
8501
|
import { existsSync as existsSync10 } from "node:fs";
|
|
8434
8502
|
import { dirname as dirname3, resolve as resolve6 } from "node:path";
|
|
8435
|
-
|
|
8436
|
-
// src/checks/source/local-ref-parser.ts
|
|
8437
|
-
import { readFile as readFile18 } from "node:fs/promises";
|
|
8438
|
-
function newlineOffsets(content) {
|
|
8439
|
-
const offsets = [];
|
|
8440
|
-
for (let i = 0;i < content.length; i++)
|
|
8441
|
-
if (content.charCodeAt(i) === 10)
|
|
8442
|
-
offsets.push(i);
|
|
8443
|
-
return offsets;
|
|
8444
|
-
}
|
|
8445
|
-
function lineWithinInline(newlines, offset) {
|
|
8446
|
-
let low = 0;
|
|
8447
|
-
let high = newlines.length;
|
|
8448
|
-
while (low < high) {
|
|
8449
|
-
const middle = low + high >>> 1;
|
|
8450
|
-
if (newlines[middle] < offset)
|
|
8451
|
-
low = middle + 1;
|
|
8452
|
-
else
|
|
8453
|
-
high = middle;
|
|
8454
|
-
}
|
|
8455
|
-
return low;
|
|
8456
|
-
}
|
|
8457
|
-
function createRenderedLocalRefCollector(customPlugins) {
|
|
8458
|
-
const tokenOffsets = new WeakMap;
|
|
8459
|
-
const parser = createMarkdownRenderer(customPlugins);
|
|
8460
|
-
const BaseInlineState = parser.inline.State;
|
|
8461
|
-
|
|
8462
|
-
class PositionedInlineState extends BaseInlineState {
|
|
8463
|
-
push(...args) {
|
|
8464
|
-
const token = super.push(...args);
|
|
8465
|
-
tokenOffsets.set(token, this.pos);
|
|
8466
|
-
return token;
|
|
8467
|
-
}
|
|
8468
|
-
}
|
|
8469
|
-
parser.inline.State = PositionedInlineState;
|
|
8470
|
-
return (content) => {
|
|
8471
|
-
const tokens = parser.parse(content, {});
|
|
8472
|
-
const refs = [];
|
|
8473
|
-
for (const block of tokens) {
|
|
8474
|
-
if (block.type !== "inline" || !block.children || !block.map)
|
|
8475
|
-
continue;
|
|
8476
|
-
const newlines = newlineOffsets(block.content);
|
|
8477
|
-
for (const child of block.children) {
|
|
8478
|
-
const kind = child.type === "image" ? "image" : child.type === "link_open" ? "link" : undefined;
|
|
8479
|
-
if (!kind)
|
|
8480
|
-
continue;
|
|
8481
|
-
const ref = child.attrGet(kind === "image" ? "src" : "href");
|
|
8482
|
-
if (ref === null)
|
|
8483
|
-
continue;
|
|
8484
|
-
const offset = tokenOffsets.get(child);
|
|
8485
|
-
refs.push({
|
|
8486
|
-
ref,
|
|
8487
|
-
kind,
|
|
8488
|
-
line: block.map[0] + (offset === undefined ? 0 : lineWithinInline(newlines, offset)) + 1
|
|
8489
|
-
});
|
|
8490
|
-
}
|
|
8491
|
-
}
|
|
8492
|
-
return refs;
|
|
8493
|
-
};
|
|
8494
|
-
}
|
|
8495
|
-
async function* renderedLocalRefs(ctx, checkId, results) {
|
|
8496
|
-
const files = (ctx.markdownFiles ?? []).slice().sort();
|
|
8497
|
-
if (files.length === 0)
|
|
8498
|
-
return;
|
|
8499
|
-
const plugins = await loadPlugins(ctx.config.extensions, ctx.inputDir, (ref, error2) => {
|
|
8500
|
-
results.push(inspectionFailed(checkId, `Plugin "${ref}" could not be loaded, so local references it defines were not checked: ${error2.message}`));
|
|
8501
|
-
});
|
|
8502
|
-
const collectRenderedLocalRefs = createRenderedLocalRefCollector(plugins);
|
|
8503
|
-
for (const file of files) {
|
|
8504
|
-
let refs;
|
|
8505
|
-
try {
|
|
8506
|
-
refs = collectRenderedLocalRefs(await readFile18(file, "utf8"));
|
|
8507
|
-
} catch {
|
|
8508
|
-
results.push(inspectionFailed(checkId, `Could not read source file: ${file}`, { file }));
|
|
8509
|
-
continue;
|
|
8510
|
-
}
|
|
8511
|
-
for (const ref of refs)
|
|
8512
|
-
yield { ...ref, file };
|
|
8513
|
-
}
|
|
8514
|
-
}
|
|
8515
|
-
|
|
8516
|
-
// src/checks/source/local-refs.ts
|
|
8517
|
-
var check23 = {
|
|
8503
|
+
var check22 = {
|
|
8518
8504
|
id: "source.links.local-refs",
|
|
8519
8505
|
name: "Local Markdown References",
|
|
8520
8506
|
description: "Checks local markdown links and image refs resolve on disk",
|
|
@@ -8522,12 +8508,12 @@ var check23 = {
|
|
|
8522
8508
|
phase: "pre-build",
|
|
8523
8509
|
async run(ctx) {
|
|
8524
8510
|
const results = [];
|
|
8525
|
-
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx,
|
|
8511
|
+
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check22.id, results)) {
|
|
8526
8512
|
if (isNonFilesystemRef(ref))
|
|
8527
8513
|
continue;
|
|
8528
8514
|
const escape = kind === "image" ? proseImageRefError(ref, ctx.inputDir) : null;
|
|
8529
8515
|
if (escape) {
|
|
8530
|
-
results.push(finding(
|
|
8516
|
+
results.push(finding(check22.id, {
|
|
8531
8517
|
severity: "error",
|
|
8532
8518
|
message: escape,
|
|
8533
8519
|
file,
|
|
@@ -8537,7 +8523,7 @@ var check23 = {
|
|
|
8537
8523
|
}
|
|
8538
8524
|
if (localRefExists(ref, kind, file, ctx.inputDir))
|
|
8539
8525
|
continue;
|
|
8540
|
-
results.push(finding(
|
|
8526
|
+
results.push(finding(check22.id, {
|
|
8541
8527
|
severity: kind === "image" ? "warning" : "error",
|
|
8542
8528
|
code: kind === "image" ? "missing-image-placeholder" : undefined,
|
|
8543
8529
|
message: kind === "image" ? `Local image not found; build will substitute a magenta placeholder: ${ref}` : `Local reference not found: ${ref}`,
|
|
@@ -8562,7 +8548,7 @@ function localRefExists(ref, kind, sourceFile, projectRoot) {
|
|
|
8562
8548
|
return existsSync10(fileRelative);
|
|
8563
8549
|
}
|
|
8564
8550
|
}
|
|
8565
|
-
registerCheck(
|
|
8551
|
+
registerCheck(check22);
|
|
8566
8552
|
|
|
8567
8553
|
// src/lib/build-staging.ts
|
|
8568
8554
|
import path11 from "node:path";
|
|
@@ -8828,7 +8814,7 @@ async function createStageRoot() {
|
|
|
8828
8814
|
}
|
|
8829
8815
|
|
|
8830
8816
|
// src/checks/source/dangling-links.ts
|
|
8831
|
-
var
|
|
8817
|
+
var check23 = {
|
|
8832
8818
|
id: "source.links.dangling",
|
|
8833
8819
|
name: "Dangling Links",
|
|
8834
8820
|
description: "Flags relative links the printed book cannot open (their href is dropped at print time)",
|
|
@@ -8836,10 +8822,10 @@ var check24 = {
|
|
|
8836
8822
|
phase: "pre-build",
|
|
8837
8823
|
async run(ctx) {
|
|
8838
8824
|
const results = [];
|
|
8839
|
-
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx,
|
|
8825
|
+
for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check23.id, results)) {
|
|
8840
8826
|
if (kind !== "link" || isPrintResolvableHref(ref))
|
|
8841
8827
|
continue;
|
|
8842
|
-
results.push(finding(
|
|
8828
|
+
results.push(finding(check23.id, {
|
|
8843
8829
|
severity: "warning",
|
|
8844
8830
|
code: "dangling-link",
|
|
8845
8831
|
data: { ref },
|
|
@@ -8851,11 +8837,11 @@ var check24 = {
|
|
|
8851
8837
|
return results;
|
|
8852
8838
|
}
|
|
8853
8839
|
};
|
|
8854
|
-
registerCheck(
|
|
8840
|
+
registerCheck(check23);
|
|
8855
8841
|
|
|
8856
8842
|
// src/checks/source/accessibility-alt-text.ts
|
|
8857
8843
|
import { readFile as readFile19 } from "node:fs/promises";
|
|
8858
|
-
var
|
|
8844
|
+
var check24 = {
|
|
8859
8845
|
id: "source.accessibility.alt-text",
|
|
8860
8846
|
name: "Image Alt Text",
|
|
8861
8847
|
description: "Checks markdown images include non-empty alt text",
|
|
@@ -8884,7 +8870,7 @@ var check25 = {
|
|
|
8884
8870
|
if (alt.trim())
|
|
8885
8871
|
continue;
|
|
8886
8872
|
results.push({
|
|
8887
|
-
checkId:
|
|
8873
|
+
checkId: check24.id,
|
|
8888
8874
|
severity: "warning",
|
|
8889
8875
|
message: "Image is missing alt text",
|
|
8890
8876
|
file,
|
|
@@ -8909,11 +8895,11 @@ function extractImageAlts(line) {
|
|
|
8909
8895
|
}
|
|
8910
8896
|
return alts;
|
|
8911
8897
|
}
|
|
8912
|
-
registerCheck(
|
|
8898
|
+
registerCheck(check24);
|
|
8913
8899
|
|
|
8914
8900
|
// src/checks/source/accessibility-heading-order.ts
|
|
8915
8901
|
import { readFile as readFile20 } from "node:fs/promises";
|
|
8916
|
-
var
|
|
8902
|
+
var check25 = {
|
|
8917
8903
|
id: "source.accessibility.heading-order",
|
|
8918
8904
|
name: "Heading Order",
|
|
8919
8905
|
description: "Checks markdown heading levels do not jump by more than one",
|
|
@@ -8941,7 +8927,7 @@ var check26 = {
|
|
|
8941
8927
|
if (inFence)
|
|
8942
8928
|
continue;
|
|
8943
8929
|
const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
|
|
8944
|
-
if (comment && parseDisableDirective(comment[1])?.includes(
|
|
8930
|
+
if (comment && parseDisableDirective(comment[1])?.includes(check25.id)) {
|
|
8945
8931
|
silencedLine = i + 1;
|
|
8946
8932
|
continue;
|
|
8947
8933
|
}
|
|
@@ -8951,7 +8937,7 @@ var check26 = {
|
|
|
8951
8937
|
const level = match[1].length;
|
|
8952
8938
|
if (prevLevel != null && level > prevLevel + 1 && i !== silencedLine) {
|
|
8953
8939
|
results.push({
|
|
8954
|
-
checkId:
|
|
8940
|
+
checkId: check25.id,
|
|
8955
8941
|
severity: "warning",
|
|
8956
8942
|
message: `Heading level jump from h${prevLevel} to h${level}`,
|
|
8957
8943
|
file,
|
|
@@ -8965,7 +8951,7 @@ var check26 = {
|
|
|
8965
8951
|
return results;
|
|
8966
8952
|
}
|
|
8967
8953
|
};
|
|
8968
|
-
registerCheck(
|
|
8954
|
+
registerCheck(check25);
|
|
8969
8955
|
|
|
8970
8956
|
// src/checks/source/layout-markers.ts
|
|
8971
8957
|
import { readFile as readFile21 } from "node:fs/promises";
|
|
@@ -9076,7 +9062,7 @@ async function collectKnownClasses(ctx, plugins) {
|
|
|
9076
9062
|
}
|
|
9077
9063
|
return names;
|
|
9078
9064
|
}
|
|
9079
|
-
var
|
|
9065
|
+
var check26 = {
|
|
9080
9066
|
id: "source.markdown.layout-markers",
|
|
9081
9067
|
name: "Layout Markers",
|
|
9082
9068
|
description: "Reports @page/@section/@chapter marker arguments Gutterpress could not understand, plus any unknown gp-* class and any other class no stylesheet mentions",
|
|
@@ -9088,7 +9074,7 @@ var check27 = {
|
|
|
9088
9074
|
return [];
|
|
9089
9075
|
const results = [];
|
|
9090
9076
|
const plugins = await loadPlugins(ctx.config.extensions, ctx.inputDir, (ref, error2) => {
|
|
9091
|
-
results.push(inspectionFailed(
|
|
9077
|
+
results.push(inspectionFailed(check26.id, `Plugin "${ref}" could not be loaded, so markers it defines were not checked: ${error2.message}`));
|
|
9092
9078
|
});
|
|
9093
9079
|
const md = createMarkdownRenderer(plugins);
|
|
9094
9080
|
let known;
|
|
@@ -9107,7 +9093,7 @@ var check27 = {
|
|
|
9107
9093
|
if (names.has(cls) || warnedLines.has(use.line))
|
|
9108
9094
|
continue;
|
|
9109
9095
|
const guess = nearestKnownClass(cls, names);
|
|
9110
|
-
results.push(finding(
|
|
9096
|
+
results.push(finding(check26.id, {
|
|
9111
9097
|
severity: "warning",
|
|
9112
9098
|
message: `Class ".${cls}" is not in any stylesheet (Gutterpress's own, the plugins', or this book's), so it styles nothing.` + (guess ? ` Did you mean ".${guess}"?` : "") + (use.count > 1 ? ` Used ${use.count} times in this file; the first is reported.` : ""),
|
|
9113
9099
|
file,
|
|
@@ -9117,7 +9103,7 @@ var check27 = {
|
|
|
9117
9103
|
}
|
|
9118
9104
|
}
|
|
9119
9105
|
for (const w of env.layoutWarnings ?? []) {
|
|
9120
|
-
results.push(finding(
|
|
9106
|
+
results.push(finding(check26.id, {
|
|
9121
9107
|
severity: w.severity ?? "warning",
|
|
9122
9108
|
message: w.message,
|
|
9123
9109
|
file,
|
|
@@ -9126,46 +9112,23 @@ var check27 = {
|
|
|
9126
9112
|
}));
|
|
9127
9113
|
}
|
|
9128
9114
|
} catch (error2) {
|
|
9129
|
-
results.push(inspectionFailed(
|
|
9115
|
+
results.push(inspectionFailed(check26.id, `Could not check layout markers in ${file}: ${error2 instanceof Error ? error2.message : String(error2)}`, { file }));
|
|
9130
9116
|
}
|
|
9131
9117
|
}
|
|
9132
9118
|
return results;
|
|
9133
9119
|
}
|
|
9134
9120
|
};
|
|
9135
|
-
registerCheck(
|
|
9121
|
+
registerCheck(check26);
|
|
9136
9122
|
|
|
9137
9123
|
// src/checks/source/merge-markers.ts
|
|
9138
9124
|
import { readFile as readFile22 } from "node:fs/promises";
|
|
9139
9125
|
import path12 from "node:path";
|
|
9140
|
-
|
|
9141
|
-
// src/checks/asset/extensions.ts
|
|
9142
|
-
var RASTER_INSPECTABLE_EXTS = [
|
|
9143
|
-
"png",
|
|
9144
|
-
"jpg",
|
|
9145
|
-
"jpeg",
|
|
9146
|
-
"tiff",
|
|
9147
|
-
"tif"
|
|
9148
|
-
];
|
|
9149
|
-
var ALL_IMAGE_EXTS = [
|
|
9150
|
-
...RASTER_INSPECTABLE_EXTS,
|
|
9151
|
-
"webp",
|
|
9152
|
-
"svg",
|
|
9153
|
-
"gif"
|
|
9154
|
-
];
|
|
9155
|
-
var FONT_EXTS2 = ["woff", "woff2", "otf", "ttf", "eot"];
|
|
9156
|
-
var ASSET_SCAN_IGNORE_GLOBS = [
|
|
9157
|
-
"**/node_modules/**",
|
|
9158
|
-
"**/.git/**",
|
|
9159
|
-
"**/dist/**"
|
|
9160
|
-
];
|
|
9161
|
-
|
|
9162
|
-
// src/checks/source/merge-markers.ts
|
|
9163
9126
|
var OPEN_SENTINEL = "<<<<<<< your version";
|
|
9164
9127
|
var CLOSE_SENTINEL = ">>>>>>> online version";
|
|
9165
9128
|
function withoutOnlineTag(relPath) {
|
|
9166
9129
|
return relPath.replace(/\.online(?=\.[^./]*$|$)/, "");
|
|
9167
9130
|
}
|
|
9168
|
-
var
|
|
9131
|
+
var check27 = {
|
|
9169
9132
|
id: "source.sync.merge-markers",
|
|
9170
9133
|
name: "Combined Versions",
|
|
9171
9134
|
description: "Finds passages and files still holding two versions (yours and the online copy) after a sync",
|
|
@@ -9179,7 +9142,7 @@ var check28 = {
|
|
|
9179
9142
|
try {
|
|
9180
9143
|
content = await readFile22(file, "utf8");
|
|
9181
9144
|
} catch {
|
|
9182
|
-
results.push(inspectionFailed(
|
|
9145
|
+
results.push(inspectionFailed(check27.id, `Could not read source file: ${file}`, { file }));
|
|
9183
9146
|
continue;
|
|
9184
9147
|
}
|
|
9185
9148
|
if (!content.includes(OPEN_SENTINEL) && !content.includes(CLOSE_SENTINEL))
|
|
@@ -9191,7 +9154,7 @@ var check28 = {
|
|
|
9191
9154
|
const line = lines[i].endsWith("\r") ? lines[i].slice(0, -1) : lines[i];
|
|
9192
9155
|
if (line === OPEN_SENTINEL) {
|
|
9193
9156
|
inBlock = true;
|
|
9194
|
-
results.push(finding(
|
|
9157
|
+
results.push(finding(check27.id, {
|
|
9195
9158
|
severity: "error",
|
|
9196
9159
|
code: "two-versions-passage",
|
|
9197
9160
|
message: "This passage has two versions (yours and the online copy) — keep what you want, then delete the marker lines.",
|
|
@@ -9200,7 +9163,7 @@ var check28 = {
|
|
|
9200
9163
|
}));
|
|
9201
9164
|
} else if (line === CLOSE_SENTINEL) {
|
|
9202
9165
|
if (!inBlock) {
|
|
9203
|
-
results.push(finding(
|
|
9166
|
+
results.push(finding(check27.id, {
|
|
9204
9167
|
severity: "error",
|
|
9205
9168
|
code: "leftover-version-marker",
|
|
9206
9169
|
message: "This marker line is left over from combining two versions — delete it.",
|
|
@@ -9221,7 +9184,7 @@ var check28 = {
|
|
|
9221
9184
|
});
|
|
9222
9185
|
for (const sibling of siblings.sort()) {
|
|
9223
9186
|
const rel = path12.relative(ctx.inputDir, sibling).split(path12.sep).join("/");
|
|
9224
|
-
results.push(finding(
|
|
9187
|
+
results.push(finding(check27.id, {
|
|
9225
9188
|
severity: "warning",
|
|
9226
9189
|
code: "kept-both-versions",
|
|
9227
9190
|
message: `Two versions of ${withoutOnlineTag(rel)} are in your book — keep the one you want, then delete the other.`,
|
|
@@ -9229,12 +9192,12 @@ var check28 = {
|
|
|
9229
9192
|
}));
|
|
9230
9193
|
}
|
|
9231
9194
|
} catch (error2) {
|
|
9232
|
-
results.push(inspectionFailed(
|
|
9195
|
+
results.push(inspectionFailed(check27.id, `Could not scan for kept-both files: ${error2 instanceof Error ? error2.message : String(error2)}`));
|
|
9233
9196
|
}
|
|
9234
9197
|
return results;
|
|
9235
9198
|
}
|
|
9236
9199
|
};
|
|
9237
|
-
registerCheck(
|
|
9200
|
+
registerCheck(check27);
|
|
9238
9201
|
|
|
9239
9202
|
// src/checks/source/characters.ts
|
|
9240
9203
|
import { readFile as readFile23 } from "node:fs/promises";
|
|
@@ -9349,7 +9312,7 @@ function codePointLabel(char) {
|
|
|
9349
9312
|
|
|
9350
9313
|
// src/checks/source/characters.ts
|
|
9351
9314
|
var PROBE = new RegExp(CHARACTER_PATTERN.source, "u");
|
|
9352
|
-
var
|
|
9315
|
+
var check28 = {
|
|
9353
9316
|
id: "source.characters",
|
|
9354
9317
|
name: "Unusual Characters",
|
|
9355
9318
|
description: "Finds invisible and control characters, odd spaces, and pasted typography (em dashes, curly quotes, emoji) in markdown",
|
|
@@ -9362,7 +9325,7 @@ var check29 = {
|
|
|
9362
9325
|
try {
|
|
9363
9326
|
content = await readFile23(file, "utf8");
|
|
9364
9327
|
} catch {
|
|
9365
|
-
results.push(inspectionFailed(
|
|
9328
|
+
results.push(inspectionFailed(check28.id, `Could not read source file: ${file}`, { file }));
|
|
9366
9329
|
continue;
|
|
9367
9330
|
}
|
|
9368
9331
|
if (!PROBE.test(content))
|
|
@@ -9373,7 +9336,7 @@ var check29 = {
|
|
|
9373
9336
|
for (let i = 0;i < lines.length; i++) {
|
|
9374
9337
|
const line = lines[i];
|
|
9375
9338
|
const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
|
|
9376
|
-
if (comment && parseDisableDirective(comment[1])?.includes(
|
|
9339
|
+
if (comment && parseDisableDirective(comment[1])?.includes(check28.id)) {
|
|
9377
9340
|
silencedLine = i + 1;
|
|
9378
9341
|
continue;
|
|
9379
9342
|
}
|
|
@@ -9382,7 +9345,7 @@ var check29 = {
|
|
|
9382
9345
|
for (const match of line.matchAll(CHARACTER_PATTERN)) {
|
|
9383
9346
|
const char = match[0];
|
|
9384
9347
|
const { name, code, fix } = describeCharacter(char);
|
|
9385
|
-
results.push(finding(
|
|
9348
|
+
results.push(finding(check28.id, {
|
|
9386
9349
|
severity: code === "typographer" ? "info" : "warning",
|
|
9387
9350
|
code,
|
|
9388
9351
|
message: `${name} (${codePointLabel(char)}). ${fix}`,
|
|
@@ -9397,19 +9360,55 @@ var check29 = {
|
|
|
9397
9360
|
return results;
|
|
9398
9361
|
}
|
|
9399
9362
|
};
|
|
9400
|
-
registerCheck(
|
|
9363
|
+
registerCheck(check28);
|
|
9401
9364
|
|
|
9402
9365
|
// src/checks/asset/image-file-size.ts
|
|
9403
|
-
import { stat as
|
|
9366
|
+
import { stat as stat2 } from "node:fs/promises";
|
|
9367
|
+
var check29 = {
|
|
9368
|
+
id: "asset.image.file-size",
|
|
9369
|
+
name: "Image File Size",
|
|
9370
|
+
description: "Checks that image files do not exceed the maximum size limit",
|
|
9371
|
+
category: "asset",
|
|
9372
|
+
phase: "pre-build",
|
|
9373
|
+
async run(ctx) {
|
|
9374
|
+
const maxSize = ctx.config.validate.assets.maxImageSize;
|
|
9375
|
+
if (!maxSize)
|
|
9376
|
+
return [];
|
|
9377
|
+
const files = shippedImages(ctx, ALL_IMAGE_EXTS);
|
|
9378
|
+
if (files.length === 0)
|
|
9379
|
+
return [];
|
|
9380
|
+
const results = [];
|
|
9381
|
+
for (const file of files) {
|
|
9382
|
+
try {
|
|
9383
|
+
const info3 = await stat2(file);
|
|
9384
|
+
if (info3.size > maxSize) {
|
|
9385
|
+
const sizeMb = (info3.size / 1e6).toFixed(1);
|
|
9386
|
+
const maxMb = (maxSize / 1e6).toFixed(1);
|
|
9387
|
+
results.push(finding(check29.id, {
|
|
9388
|
+
severity: "warning",
|
|
9389
|
+
message: `Image file too large: ${sizeMb}MB (max ${maxMb}MB)`,
|
|
9390
|
+
file
|
|
9391
|
+
}));
|
|
9392
|
+
}
|
|
9393
|
+
} catch {
|
|
9394
|
+
results.push(inspectionFailed(check29.id, `Could not stat image file: ${file}`, {
|
|
9395
|
+
file
|
|
9396
|
+
}));
|
|
9397
|
+
}
|
|
9398
|
+
}
|
|
9399
|
+
return results;
|
|
9400
|
+
}
|
|
9401
|
+
};
|
|
9402
|
+
registerCheck(check29);
|
|
9404
9403
|
|
|
9405
9404
|
// src/lib/image-inspect.ts
|
|
9406
|
-
import { stat as
|
|
9405
|
+
import { stat as stat3, readFile as readFile24 } from "node:fs/promises";
|
|
9407
9406
|
var DEFAULT_DPI = 72;
|
|
9408
9407
|
var cache = new Map;
|
|
9409
9408
|
async function inspectImage(path13) {
|
|
9410
9409
|
let st;
|
|
9411
9410
|
try {
|
|
9412
|
-
st = await
|
|
9411
|
+
st = await stat3(path13);
|
|
9413
9412
|
} catch {
|
|
9414
9413
|
return null;
|
|
9415
9414
|
}
|
|
@@ -9607,136 +9606,9 @@ function parseTiff(b) {
|
|
|
9607
9606
|
colorSpace
|
|
9608
9607
|
};
|
|
9609
9608
|
}
|
|
9610
|
-
async function collectImageFiles(dirs, exts, ignore2 = ASSET_SCAN_IGNORE_GLOBS) {
|
|
9611
|
-
const { glob } = await import("glob");
|
|
9612
|
-
const pattern = exts.length === 1 ? `**/*.${exts[0]}` : `**/*.{${exts.join(",")}}`;
|
|
9613
|
-
const files = [];
|
|
9614
|
-
for (const dir of dirs) {
|
|
9615
|
-
const matches = await glob(pattern, {
|
|
9616
|
-
cwd: dir,
|
|
9617
|
-
absolute: true,
|
|
9618
|
-
ignore: [...ignore2]
|
|
9619
|
-
});
|
|
9620
|
-
files.push(...matches);
|
|
9621
|
-
}
|
|
9622
|
-
return files;
|
|
9623
|
-
}
|
|
9624
|
-
|
|
9625
|
-
// src/checks/asset/image-file-size.ts
|
|
9626
|
-
var check30 = {
|
|
9627
|
-
id: "asset.image.file-size",
|
|
9628
|
-
name: "Image File Size",
|
|
9629
|
-
description: "Checks that image files do not exceed the maximum size limit",
|
|
9630
|
-
category: "asset",
|
|
9631
|
-
phase: "pre-build",
|
|
9632
|
-
async run(ctx) {
|
|
9633
|
-
const maxSize = ctx.config.validate.assets.maxImageSize;
|
|
9634
|
-
if (!maxSize)
|
|
9635
|
-
return [];
|
|
9636
|
-
const dirs = ctx.assetDirs ?? [ctx.inputDir];
|
|
9637
|
-
const files = await collectImageFiles(dirs, ALL_IMAGE_EXTS);
|
|
9638
|
-
if (files.length === 0)
|
|
9639
|
-
return [];
|
|
9640
|
-
const results = [];
|
|
9641
|
-
for (const file of files) {
|
|
9642
|
-
try {
|
|
9643
|
-
const info3 = await stat3(file);
|
|
9644
|
-
if (info3.size > maxSize) {
|
|
9645
|
-
const sizeMb = (info3.size / 1e6).toFixed(1);
|
|
9646
|
-
const maxMb = (maxSize / 1e6).toFixed(1);
|
|
9647
|
-
results.push(finding(check30.id, {
|
|
9648
|
-
severity: "warning",
|
|
9649
|
-
message: `Image file too large: ${sizeMb}MB (max ${maxMb}MB)`,
|
|
9650
|
-
file
|
|
9651
|
-
}));
|
|
9652
|
-
}
|
|
9653
|
-
} catch {
|
|
9654
|
-
results.push(inspectionFailed(check30.id, `Could not stat image file: ${file}`, {
|
|
9655
|
-
file
|
|
9656
|
-
}));
|
|
9657
|
-
}
|
|
9658
|
-
}
|
|
9659
|
-
return results;
|
|
9660
|
-
}
|
|
9661
|
-
};
|
|
9662
|
-
registerCheck(check30);
|
|
9663
|
-
|
|
9664
|
-
// src/checks/asset/image-resolution.ts
|
|
9665
|
-
var check31 = {
|
|
9666
|
-
id: "asset.image.resolution",
|
|
9667
|
-
name: "Image Resolution",
|
|
9668
|
-
description: "Checks source image DPI from embedded density metadata",
|
|
9669
|
-
category: "asset",
|
|
9670
|
-
phase: "pre-build",
|
|
9671
|
-
async run(ctx) {
|
|
9672
|
-
const minDpi = ctx.config.validate.assets.minImageDpi;
|
|
9673
|
-
if (!minDpi)
|
|
9674
|
-
return [];
|
|
9675
|
-
const dirs = ctx.assetDirs ?? [ctx.inputDir];
|
|
9676
|
-
const files = await collectImageFiles(dirs, RASTER_INSPECTABLE_EXTS);
|
|
9677
|
-
if (files.length === 0)
|
|
9678
|
-
return [];
|
|
9679
|
-
const results = [];
|
|
9680
|
-
for (const file of files) {
|
|
9681
|
-
const info3 = await inspectImage(file);
|
|
9682
|
-
if (!info3)
|
|
9683
|
-
continue;
|
|
9684
|
-
const { xDpi, yDpi } = info3;
|
|
9685
|
-
if (xDpi > 0 && yDpi > 0 && (xDpi < minDpi || yDpi < minDpi)) {
|
|
9686
|
-
results.push({
|
|
9687
|
-
checkId: check31.id,
|
|
9688
|
-
severity: "warning",
|
|
9689
|
-
message: `Image resolution too low: ${xDpi}x${yDpi} DPI (minimum ${minDpi} DPI)`,
|
|
9690
|
-
file
|
|
9691
|
-
});
|
|
9692
|
-
}
|
|
9693
|
-
}
|
|
9694
|
-
return results;
|
|
9695
|
-
}
|
|
9696
|
-
};
|
|
9697
|
-
registerCheck(check31);
|
|
9698
|
-
|
|
9699
|
-
// src/checks/asset/image-color-space.ts
|
|
9700
|
-
var LABEL = {
|
|
9701
|
-
srgb: "sRGB",
|
|
9702
|
-
gray: "Gray",
|
|
9703
|
-
cmyk: "CMYK"
|
|
9704
|
-
};
|
|
9705
|
-
var check32 = {
|
|
9706
|
-
id: "asset.image.color-space",
|
|
9707
|
-
name: "Image Color Space",
|
|
9708
|
-
description: "Validates image color spaces against allowed list",
|
|
9709
|
-
category: "asset",
|
|
9710
|
-
phase: "pre-build",
|
|
9711
|
-
async run(ctx) {
|
|
9712
|
-
const allowed = ctx.config.validate.assets.allowedColorSpaces;
|
|
9713
|
-
if (!allowed || allowed.length === 0)
|
|
9714
|
-
return [];
|
|
9715
|
-
const dirs = ctx.assetDirs ?? [ctx.inputDir];
|
|
9716
|
-
const files = await collectImageFiles(dirs, RASTER_INSPECTABLE_EXTS);
|
|
9717
|
-
if (files.length === 0)
|
|
9718
|
-
return [];
|
|
9719
|
-
const allowedLower = new Set(allowed.map((s) => s.toLowerCase()));
|
|
9720
|
-
const results = [];
|
|
9721
|
-
for (const file of files) {
|
|
9722
|
-
const info3 = await inspectImage(file);
|
|
9723
|
-
const cs = info3?.colorSpace;
|
|
9724
|
-
if (cs && !allowedLower.has(cs)) {
|
|
9725
|
-
results.push({
|
|
9726
|
-
checkId: check32.id,
|
|
9727
|
-
severity: "warning",
|
|
9728
|
-
message: `Image uses ${LABEL[cs] ?? cs} color space (allowed: ${allowed.join(", ")})`,
|
|
9729
|
-
file
|
|
9730
|
-
});
|
|
9731
|
-
}
|
|
9732
|
-
}
|
|
9733
|
-
return results;
|
|
9734
|
-
}
|
|
9735
|
-
};
|
|
9736
|
-
registerCheck(check32);
|
|
9737
9609
|
|
|
9738
9610
|
// src/checks/asset/image-alpha.ts
|
|
9739
|
-
var
|
|
9611
|
+
var check30 = {
|
|
9740
9612
|
id: "asset.image.alpha-channel",
|
|
9741
9613
|
name: "Image Alpha Channel",
|
|
9742
9614
|
description: "Checks for alpha channels in PNG/TIFF images",
|
|
@@ -9745,8 +9617,9 @@ var check33 = {
|
|
|
9745
9617
|
async run(ctx) {
|
|
9746
9618
|
if (ctx.config.validate.assets.allowAlpha)
|
|
9747
9619
|
return [];
|
|
9748
|
-
|
|
9749
|
-
|
|
9620
|
+
if (ctx.config.pdfx.transparency === "preserve-text")
|
|
9621
|
+
return [];
|
|
9622
|
+
const files = shippedImages(ctx, ["png", "tiff", "tif"]);
|
|
9750
9623
|
if (files.length === 0)
|
|
9751
9624
|
return [];
|
|
9752
9625
|
const results = [];
|
|
@@ -9754,7 +9627,7 @@ var check33 = {
|
|
|
9754
9627
|
const info3 = await inspectImage(file);
|
|
9755
9628
|
if (info3?.hasAlpha) {
|
|
9756
9629
|
results.push({
|
|
9757
|
-
checkId:
|
|
9630
|
+
checkId: check30.id,
|
|
9758
9631
|
severity: "warning",
|
|
9759
9632
|
message: "Image contains alpha channel, which may cause print issues",
|
|
9760
9633
|
file
|
|
@@ -9764,68 +9637,10 @@ var check33 = {
|
|
|
9764
9637
|
return results;
|
|
9765
9638
|
}
|
|
9766
9639
|
};
|
|
9767
|
-
registerCheck(
|
|
9768
|
-
|
|
9769
|
-
// src/checks/asset/image-tac.ts
|
|
9770
|
-
var check34 = {
|
|
9771
|
-
id: "asset.image.tac-raster",
|
|
9772
|
-
name: "Image TAC (Raster)",
|
|
9773
|
-
description: "Rasterizes and checks TAC per image using Ghostscript",
|
|
9774
|
-
category: "asset",
|
|
9775
|
-
phase: "pre-build",
|
|
9776
|
-
requiredTools: ["gs"],
|
|
9777
|
-
async run(ctx) {
|
|
9778
|
-
const maxTac = ctx.config.ink.maxTac;
|
|
9779
|
-
if (!maxTac)
|
|
9780
|
-
return [];
|
|
9781
|
-
const dirs = ctx.assetDirs ?? [ctx.inputDir];
|
|
9782
|
-
const files = await collectImageFiles(dirs, RASTER_INSPECTABLE_EXTS);
|
|
9783
|
-
if (files.length === 0)
|
|
9784
|
-
return [];
|
|
9785
|
-
const results = [];
|
|
9786
|
-
const ghostscript = await resolveGhostscript();
|
|
9787
|
-
if (!ghostscript) {
|
|
9788
|
-
return [
|
|
9789
|
-
inspectionFailed(check34.id, "Could not inspect image ink coverage: Ghostscript executable not found")
|
|
9790
|
-
];
|
|
9791
|
-
}
|
|
9792
|
-
for (const file of files) {
|
|
9793
|
-
try {
|
|
9794
|
-
const { stdout } = await execCapture(ghostscript, [
|
|
9795
|
-
"-q",
|
|
9796
|
-
"-dBATCH",
|
|
9797
|
-
"-dNOPAUSE",
|
|
9798
|
-
"-sDEVICE=inkcov",
|
|
9799
|
-
file
|
|
9800
|
-
]);
|
|
9801
|
-
const lines = stdout.split(/\r?\n/).filter((l) => l.includes("CMYK"));
|
|
9802
|
-
for (const line of lines) {
|
|
9803
|
-
const nums = line.trim().split(/\s+/).slice(0, 4).map(Number);
|
|
9804
|
-
if (nums.length === 4 && nums.every((n) => Number.isFinite(n))) {
|
|
9805
|
-
const tac = (nums[0] + nums[1] + nums[2] + nums[3]) * 100;
|
|
9806
|
-
if (tac > maxTac) {
|
|
9807
|
-
results.push(finding(check34.id, {
|
|
9808
|
-
severity: "warning",
|
|
9809
|
-
message: `Image TAC exceeds limit: ${tac.toFixed(1)}% (max ${maxTac}%)`,
|
|
9810
|
-
file,
|
|
9811
|
-
code: "image-tac-exceeded",
|
|
9812
|
-
data: { tac, limit: maxTac }
|
|
9813
|
-
}));
|
|
9814
|
-
break;
|
|
9815
|
-
}
|
|
9816
|
-
}
|
|
9817
|
-
}
|
|
9818
|
-
} catch {
|
|
9819
|
-
results.push(inspectionFailed(check34.id, `Could not inspect image ink coverage: ${file}`, { file }));
|
|
9820
|
-
}
|
|
9821
|
-
}
|
|
9822
|
-
return results;
|
|
9823
|
-
}
|
|
9824
|
-
};
|
|
9825
|
-
registerCheck(check34);
|
|
9640
|
+
registerCheck(check30);
|
|
9826
9641
|
|
|
9827
9642
|
// src/checks/asset/approved-fonts.ts
|
|
9828
|
-
var
|
|
9643
|
+
var check31 = {
|
|
9829
9644
|
id: "asset.font.approved-files",
|
|
9830
9645
|
name: "Approved Font Files",
|
|
9831
9646
|
description: "Checks font files against the approved file patterns",
|
|
@@ -9864,7 +9679,7 @@ var check35 = {
|
|
|
9864
9679
|
for (const font of allFonts) {
|
|
9865
9680
|
if (!approvedFonts.has(font)) {
|
|
9866
9681
|
results.push({
|
|
9867
|
-
checkId:
|
|
9682
|
+
checkId: check31.id,
|
|
9868
9683
|
severity: "warning",
|
|
9869
9684
|
message: "Font file not in approved list",
|
|
9870
9685
|
file: font
|
|
@@ -9874,7 +9689,7 @@ var check35 = {
|
|
|
9874
9689
|
return results;
|
|
9875
9690
|
}
|
|
9876
9691
|
};
|
|
9877
|
-
registerCheck(
|
|
9692
|
+
registerCheck(check31);
|
|
9878
9693
|
|
|
9879
9694
|
// src/checks/asset/font-license.ts
|
|
9880
9695
|
import { existsSync as existsSync11 } from "node:fs";
|
|
@@ -9889,7 +9704,7 @@ var LICENSE_NAMES = [
|
|
|
9889
9704
|
"OFL-1.1.txt",
|
|
9890
9705
|
"COPYING"
|
|
9891
9706
|
];
|
|
9892
|
-
var
|
|
9707
|
+
var check32 = {
|
|
9893
9708
|
id: "asset.font.license",
|
|
9894
9709
|
name: "Font License",
|
|
9895
9710
|
description: "Checks for license files in font directories",
|
|
@@ -9916,7 +9731,7 @@ var check36 = {
|
|
|
9916
9731
|
const hasLicense = LICENSE_NAMES.some((name) => existsSync11(resolve7(fontDir, name)));
|
|
9917
9732
|
if (!hasLicense) {
|
|
9918
9733
|
results.push({
|
|
9919
|
-
checkId:
|
|
9734
|
+
checkId: check32.id,
|
|
9920
9735
|
severity: "warning",
|
|
9921
9736
|
message: `No font license file found in directory`,
|
|
9922
9737
|
file: fontDir
|
|
@@ -9926,10 +9741,10 @@ var check36 = {
|
|
|
9926
9741
|
return results;
|
|
9927
9742
|
}
|
|
9928
9743
|
};
|
|
9929
|
-
registerCheck(
|
|
9744
|
+
registerCheck(check32);
|
|
9930
9745
|
|
|
9931
9746
|
// src/checks/heuristic/text-density.ts
|
|
9932
|
-
var
|
|
9747
|
+
var check33 = {
|
|
9933
9748
|
id: "heuristic.whitespace.text-density",
|
|
9934
9749
|
name: "Text Density",
|
|
9935
9750
|
description: "Checks characters-per-page ratio",
|
|
@@ -9958,7 +9773,7 @@ var check37 = {
|
|
|
9958
9773
|
});
|
|
9959
9774
|
if (lowPages.length > 0) {
|
|
9960
9775
|
results.push({
|
|
9961
|
-
checkId:
|
|
9776
|
+
checkId: check33.id,
|
|
9962
9777
|
severity: "info",
|
|
9963
9778
|
message: `Low text density on pages: ${lowPages.join(", ")} (below ${range.min} chars)`,
|
|
9964
9779
|
file: ctx.pdfPath
|
|
@@ -9966,7 +9781,7 @@ var check37 = {
|
|
|
9966
9781
|
}
|
|
9967
9782
|
if (highPages.length > 0) {
|
|
9968
9783
|
results.push({
|
|
9969
|
-
checkId:
|
|
9784
|
+
checkId: check33.id,
|
|
9970
9785
|
severity: "info",
|
|
9971
9786
|
message: `High text density on pages: ${highPages.join(", ")} (above ${range.max} chars)`,
|
|
9972
9787
|
file: ctx.pdfPath
|
|
@@ -9975,11 +9790,11 @@ var check37 = {
|
|
|
9975
9790
|
return results;
|
|
9976
9791
|
}
|
|
9977
9792
|
};
|
|
9978
|
-
registerCheck(
|
|
9793
|
+
registerCheck(check33);
|
|
9979
9794
|
|
|
9980
9795
|
// src/checks/heuristic/section-density.ts
|
|
9981
9796
|
import { readFile as readFile25 } from "node:fs/promises";
|
|
9982
|
-
var
|
|
9797
|
+
var check34 = {
|
|
9983
9798
|
id: "heuristic.chunking.section-density",
|
|
9984
9799
|
name: "Section Density",
|
|
9985
9800
|
description: "Checks heading/paragraph/callout density from source Markdown",
|
|
@@ -10005,7 +9820,7 @@ var check38 = {
|
|
|
10005
9820
|
const line = lines[i];
|
|
10006
9821
|
if (/^#{1,6}\s/.test(line)) {
|
|
10007
9822
|
if (paragraphCount > maxParas) {
|
|
10008
|
-
results.push(finding(
|
|
9823
|
+
results.push(finding(check34.id, {
|
|
10009
9824
|
severity: "info",
|
|
10010
9825
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
10011
9826
|
file,
|
|
@@ -10025,7 +9840,7 @@ var check38 = {
|
|
|
10025
9840
|
}
|
|
10026
9841
|
}
|
|
10027
9842
|
if (paragraphCount > maxParas) {
|
|
10028
|
-
results.push(finding(
|
|
9843
|
+
results.push(finding(check34.id, {
|
|
10029
9844
|
severity: "info",
|
|
10030
9845
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
10031
9846
|
file,
|
|
@@ -10033,7 +9848,7 @@ var check38 = {
|
|
|
10033
9848
|
}));
|
|
10034
9849
|
}
|
|
10035
9850
|
} catch {
|
|
10036
|
-
results.push(inspectionFailed(
|
|
9851
|
+
results.push(inspectionFailed(check34.id, `Could not read source file: ${file}`, {
|
|
10037
9852
|
file
|
|
10038
9853
|
}));
|
|
10039
9854
|
}
|
|
@@ -10041,10 +9856,10 @@ var check38 = {
|
|
|
10041
9856
|
return results;
|
|
10042
9857
|
}
|
|
10043
9858
|
};
|
|
10044
|
-
registerCheck(
|
|
9859
|
+
registerCheck(check34);
|
|
10045
9860
|
|
|
10046
9861
|
// src/checks/heuristic/layer-count.ts
|
|
10047
|
-
var
|
|
9862
|
+
var check35 = {
|
|
10048
9863
|
id: "heuristic.decoration.layer-count",
|
|
10049
9864
|
name: "Layer Count",
|
|
10050
9865
|
description: "Counts image objects per page",
|
|
@@ -10069,7 +9884,7 @@ var check39 = {
|
|
|
10069
9884
|
heavyPages.sort((a, b) => a - b);
|
|
10070
9885
|
return [
|
|
10071
9886
|
{
|
|
10072
|
-
checkId:
|
|
9887
|
+
checkId: check35.id,
|
|
10073
9888
|
severity: "info",
|
|
10074
9889
|
message: `Pages with many image layers (>${maxLayers}): ${heavyPages.join(", ")}`,
|
|
10075
9890
|
file: ctx.pdfPath
|
|
@@ -10079,10 +9894,10 @@ var check39 = {
|
|
|
10079
9894
|
return [];
|
|
10080
9895
|
}
|
|
10081
9896
|
};
|
|
10082
|
-
registerCheck(
|
|
9897
|
+
registerCheck(check35);
|
|
10083
9898
|
|
|
10084
9899
|
// src/checks/heuristic/placement-variance.ts
|
|
10085
|
-
var
|
|
9900
|
+
var check36 = {
|
|
10086
9901
|
id: "heuristic.layout.placement-variance",
|
|
10087
9902
|
name: "Placement Variance",
|
|
10088
9903
|
description: "Analyzes text baseline coordinates for layout consistency",
|
|
@@ -10100,7 +9915,7 @@ var check40 = {
|
|
|
10100
9915
|
const uniqueX = new Set(positions.map((p) => Math.round(p.x)));
|
|
10101
9916
|
return [
|
|
10102
9917
|
{
|
|
10103
|
-
checkId:
|
|
9918
|
+
checkId: check36.id,
|
|
10104
9919
|
severity: "info",
|
|
10105
9920
|
message: `Layout analysis: ${uniqueX.size} unique horizontal text positions across ${positions.length} text blocks.`,
|
|
10106
9921
|
file: ctx.pdfPath
|
|
@@ -10108,7 +9923,7 @@ var check40 = {
|
|
|
10108
9923
|
];
|
|
10109
9924
|
}
|
|
10110
9925
|
};
|
|
10111
|
-
registerCheck(
|
|
9926
|
+
registerCheck(check36);
|
|
10112
9927
|
|
|
10113
9928
|
// src/checks/runner.ts
|
|
10114
9929
|
function getCheckSeverityOverride(checkId, config) {
|
|
@@ -10138,16 +9953,16 @@ async function runChecks(ctx, opts = {}) {
|
|
|
10138
9953
|
}
|
|
10139
9954
|
const release = retainPdfCache();
|
|
10140
9955
|
try {
|
|
10141
|
-
for (const
|
|
9956
|
+
for (const check37 of checks2) {
|
|
10142
9957
|
try {
|
|
10143
|
-
let results = await
|
|
10144
|
-
const severityOverride = getCheckSeverityOverride(
|
|
9958
|
+
let results = await check37.run(ctx);
|
|
9959
|
+
const severityOverride = getCheckSeverityOverride(check37.id, ctx.config);
|
|
10145
9960
|
if (severityOverride) {
|
|
10146
9961
|
for (const r of results) {
|
|
10147
9962
|
r.severity = severityOverride;
|
|
10148
9963
|
}
|
|
10149
9964
|
}
|
|
10150
|
-
const lintLevel = ruleLevel(ctx.config.lint.rules,
|
|
9965
|
+
const lintLevel = ruleLevel(ctx.config.lint.rules, check37.id);
|
|
10151
9966
|
if (lintLevel === "off")
|
|
10152
9967
|
results = [];
|
|
10153
9968
|
else if (lintLevel) {
|
|
@@ -10155,15 +9970,15 @@ async function runChecks(ctx, opts = {}) {
|
|
|
10155
9970
|
r.severity = lintLevel === "warn" ? "warning" : "error";
|
|
10156
9971
|
}
|
|
10157
9972
|
if (results.length === 0) {
|
|
10158
|
-
passed.push(
|
|
9973
|
+
passed.push(check37.id);
|
|
10159
9974
|
} else {
|
|
10160
9975
|
allResults.push(...results);
|
|
10161
9976
|
}
|
|
10162
9977
|
} catch (err) {
|
|
10163
9978
|
allResults.push({
|
|
10164
|
-
checkId:
|
|
9979
|
+
checkId: check37.id,
|
|
10165
9980
|
severity: "error",
|
|
10166
|
-
message: `Check "${
|
|
9981
|
+
message: `Check "${check37.id}" threw: ${err instanceof Error ? err.message : String(err)}`
|
|
10167
9982
|
});
|
|
10168
9983
|
}
|
|
10169
9984
|
}
|
|
@@ -10203,15 +10018,15 @@ function emptyReport() {
|
|
|
10203
10018
|
async function checkToolAvailability(config, opts = {}) {
|
|
10204
10019
|
const { checks: checks2 } = selectChecks(opts, config);
|
|
10205
10020
|
const toolToChecks = new Map;
|
|
10206
|
-
for (const
|
|
10207
|
-
if (!
|
|
10021
|
+
for (const check37 of checks2) {
|
|
10022
|
+
if (!check37.requiredTools?.length)
|
|
10208
10023
|
continue;
|
|
10209
|
-
for (const tool of
|
|
10024
|
+
for (const tool of check37.requiredTools) {
|
|
10210
10025
|
const existing = toolToChecks.get(tool);
|
|
10211
10026
|
if (existing) {
|
|
10212
|
-
existing.push(
|
|
10027
|
+
existing.push(check37.id);
|
|
10213
10028
|
} else {
|
|
10214
|
-
toolToChecks.set(tool, [
|
|
10029
|
+
toolToChecks.set(tool, [check37.id]);
|
|
10215
10030
|
}
|
|
10216
10031
|
}
|
|
10217
10032
|
}
|
|
@@ -10227,11 +10042,11 @@ async function checkToolAvailability(config, opts = {}) {
|
|
|
10227
10042
|
const missing = results.filter((r) => !r.found).map((r) => r.tool);
|
|
10228
10043
|
const missingSet = new Set(missing);
|
|
10229
10044
|
const skippedChecks = [];
|
|
10230
|
-
for (const
|
|
10231
|
-
if (!
|
|
10045
|
+
for (const check37 of checks2) {
|
|
10046
|
+
if (!check37.requiredTools?.length)
|
|
10232
10047
|
continue;
|
|
10233
|
-
if (
|
|
10234
|
-
skippedChecks.push(
|
|
10048
|
+
if (check37.requiredTools.some((t) => missingSet.has(t))) {
|
|
10049
|
+
skippedChecks.push(check37.id);
|
|
10235
10050
|
}
|
|
10236
10051
|
}
|
|
10237
10052
|
return { available, missing, skippedChecks, toolToChecks };
|
|
@@ -10264,6 +10079,31 @@ var SHIPPED_ASSET_EXTS = new Set([
|
|
|
10264
10079
|
".tif",
|
|
10265
10080
|
".tiff"
|
|
10266
10081
|
]);
|
|
10082
|
+
async function shippedImageFiles(manifestDir, markdownFiles, stylePaths, extensions) {
|
|
10083
|
+
const isImage = (file) => ALL_IMAGE_EXTS.includes(file.slice(file.lastIndexOf(".") + 1).toLowerCase());
|
|
10084
|
+
const files = new Set;
|
|
10085
|
+
try {
|
|
10086
|
+
for (const file of await collectStyleDependencies(manifestDir, stylePaths)) {
|
|
10087
|
+
if (isImage(file))
|
|
10088
|
+
files.add(file);
|
|
10089
|
+
}
|
|
10090
|
+
} catch {}
|
|
10091
|
+
const collect = createRenderedLocalRefCollector(await loadPlugins(extensions, manifestDir, () => {}));
|
|
10092
|
+
for (const file of markdownFiles) {
|
|
10093
|
+
let refs;
|
|
10094
|
+
try {
|
|
10095
|
+
refs = collect(await readFile26(file, "utf8"));
|
|
10096
|
+
} catch {
|
|
10097
|
+
continue;
|
|
10098
|
+
}
|
|
10099
|
+
for (const { ref, kind } of refs) {
|
|
10100
|
+
if (kind !== "image" || isNonFilesystemRef(ref) || proseImageRefError(ref, manifestDir))
|
|
10101
|
+
continue;
|
|
10102
|
+
files.add(resolve8(manifestDir, stripUrlSuffix(decodeRef(ref))));
|
|
10103
|
+
}
|
|
10104
|
+
}
|
|
10105
|
+
return [...files].filter((file) => existsSync12(file)).sort();
|
|
10106
|
+
}
|
|
10267
10107
|
async function sharedAssetDirs(manifestDir, relStyles, inputDir) {
|
|
10268
10108
|
let closure;
|
|
10269
10109
|
try {
|
|
@@ -10467,6 +10307,7 @@ async function executeValidation(args) {
|
|
|
10467
10307
|
let markdownFiles;
|
|
10468
10308
|
let cssFiles;
|
|
10469
10309
|
let assetDirs;
|
|
10310
|
+
let imageFiles;
|
|
10470
10311
|
let htmlPath;
|
|
10471
10312
|
const outDir = resolveOutputDir(manifestDir, config.title);
|
|
10472
10313
|
if (inputDir) {
|
|
@@ -10481,6 +10322,7 @@ async function executeValidation(args) {
|
|
|
10481
10322
|
}
|
|
10482
10323
|
cssFiles = [...projectCssFiles, ...pluginStylePaths].filter((f) => !f.endsWith(".min.css"));
|
|
10483
10324
|
assetDirs = [inputDir, ...await sharedAssetDirs(manifestDir, relStyles, inputDir)];
|
|
10325
|
+
imageFiles = await shippedImageFiles(manifestDir, markdownFiles, [...relStyles, ...pluginStylePaths], config.extensions);
|
|
10484
10326
|
const possibleHtml = join4(outDir, BOOK_HTML_FILENAME);
|
|
10485
10327
|
if (existsSync12(possibleHtml)) {
|
|
10486
10328
|
htmlPath = possibleHtml;
|
|
@@ -10494,7 +10336,8 @@ async function executeValidation(args) {
|
|
|
10494
10336
|
htmlPath,
|
|
10495
10337
|
markdownFiles,
|
|
10496
10338
|
cssFiles,
|
|
10497
|
-
assetDirs
|
|
10339
|
+
assetDirs,
|
|
10340
|
+
imageFiles
|
|
10498
10341
|
};
|
|
10499
10342
|
const runnerOptions = {
|
|
10500
10343
|
category: categories,
|
|
@@ -10504,7 +10347,7 @@ async function executeValidation(args) {
|
|
|
10504
10347
|
};
|
|
10505
10348
|
if (args.fix) {
|
|
10506
10349
|
const selected = selectChecks(runnerOptions, config).checks;
|
|
10507
|
-
if (selected.some((
|
|
10350
|
+
if (selected.some((check37) => check37.id === MARKDOWNLINT_CHECK_ID)) {
|
|
10508
10351
|
await applyMarkdownlintFixes(context);
|
|
10509
10352
|
} else {
|
|
10510
10353
|
log.warn(`--fix: ${MARKDOWNLINT_CHECK_ID} is not part of this run — no files were fixed.`);
|
|
@@ -10573,12 +10416,6 @@ function buildPdfSummaryLines(results) {
|
|
|
10573
10416
|
lines.push("Fonts: all embedded");
|
|
10574
10417
|
}
|
|
10575
10418
|
}
|
|
10576
|
-
const rasterResults = results.filter((r) => r.checkId === "pdf.print.rasterized-pages");
|
|
10577
|
-
if (rasterResults.length > 0) {
|
|
10578
|
-
const rasterFinding = rasterResults.find((r) => r.code === "rasterized-pages-detected");
|
|
10579
|
-
const pages = rasterFinding?.data?.pages;
|
|
10580
|
-
lines.push(`Rasterized pages: ${Array.isArray(pages) ? pages.join(", ") : "none"}`);
|
|
10581
|
-
}
|
|
10582
10419
|
return lines;
|
|
10583
10420
|
}
|
|
10584
10421
|
|
|
@@ -10878,7 +10715,7 @@ class PdfOutput {
|
|
|
10878
10715
|
const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
|
|
10879
10716
|
await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
|
|
10880
10717
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
10881
|
-
const { buildNativePdf } = await import("./engine-
|
|
10718
|
+
const { buildNativePdf } = await import("./engine-0b8bt05n.js");
|
|
10882
10719
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
10883
10720
|
title: config.title,
|
|
10884
10721
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
@@ -11049,7 +10886,7 @@ function reportTransparentPlan({ split, artOnly, kept }, pdfxMode, dpi, pageList
|
|
|
11049
10886
|
import path16 from "path";
|
|
11050
10887
|
import { tmpdir } from "os";
|
|
11051
10888
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
11052
|
-
import { readdir as
|
|
10889
|
+
import { readdir as readdir6, readFile as readFile27, writeFile as writeFile6 } from "node:fs/promises";
|
|
11053
10890
|
|
|
11054
10891
|
// src/utils/file-utils.ts
|
|
11055
10892
|
import { promises as fs3 } from "fs";
|
|
@@ -11595,7 +11432,7 @@ function isProcessAlive(pid) {
|
|
|
11595
11432
|
async function cleanupOrphanTempDirs() {
|
|
11596
11433
|
let entries;
|
|
11597
11434
|
try {
|
|
11598
|
-
entries = await
|
|
11435
|
+
entries = await readdir6(TEMP_DIR_BASE);
|
|
11599
11436
|
} catch {
|
|
11600
11437
|
return;
|
|
11601
11438
|
}
|
|
@@ -11603,7 +11440,7 @@ async function cleanupOrphanTempDirs() {
|
|
|
11603
11440
|
const dir = path16.join(TEMP_DIR_BASE, entry);
|
|
11604
11441
|
const pidFile = path16.join(dir, PID_FILE_NAME);
|
|
11605
11442
|
try {
|
|
11606
|
-
const raw = await
|
|
11443
|
+
const raw = await readFile27(pidFile, "utf8");
|
|
11607
11444
|
const pid = parseInt(raw.trim(), 10);
|
|
11608
11445
|
if (!isProcessAlive(pid)) {
|
|
11609
11446
|
debug(`Removing orphan temp dir ${dir} (pid ${pid} not alive)`);
|
|
@@ -11710,7 +11547,7 @@ function createServerState(inputPath, tempDir, config, options) {
|
|
|
11710
11547
|
import http from "node:http";
|
|
11711
11548
|
import net from "node:net";
|
|
11712
11549
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
|
|
11713
|
-
import { readFile as
|
|
11550
|
+
import { readFile as readFile28, stat as stat4 } from "node:fs/promises";
|
|
11714
11551
|
import path17 from "path";
|
|
11715
11552
|
import { WebSocket, WebSocketServer } from "ws";
|
|
11716
11553
|
|
|
@@ -11809,7 +11646,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
11809
11646
|
}
|
|
11810
11647
|
let data;
|
|
11811
11648
|
try {
|
|
11812
|
-
data = await
|
|
11649
|
+
data = await readFile28(filePath);
|
|
11813
11650
|
} catch {
|
|
11814
11651
|
res.writeHead(404);
|
|
11815
11652
|
res.end("Not Found");
|
|
@@ -11858,7 +11695,7 @@ async function serveRevalidatedStatic(req, absPath, res, validators) {
|
|
|
11858
11695
|
let data;
|
|
11859
11696
|
if (!validator || validator.stamp !== stamp) {
|
|
11860
11697
|
try {
|
|
11861
|
-
data = await
|
|
11698
|
+
data = await readFile28(absPath);
|
|
11862
11699
|
} catch {
|
|
11863
11700
|
await serveStatic(absPath, res);
|
|
11864
11701
|
return;
|
|
@@ -12170,7 +12007,7 @@ async function startPreviewServer(options) {
|
|
|
12170
12007
|
import { platform, arch, release } from "node:os";
|
|
12171
12008
|
|
|
12172
12009
|
// src/lib/remote-auth/token-store.ts
|
|
12173
|
-
import { chmod, mkdir as mkdir7, readFile as
|
|
12010
|
+
import { chmod, mkdir as mkdir7, readFile as readFile29, rename as rename4, writeFile as writeFile7 } from "node:fs/promises";
|
|
12174
12011
|
import os3 from "node:os";
|
|
12175
12012
|
import path18 from "node:path";
|
|
12176
12013
|
function credentialHostKey(hostOrUrl) {
|
|
@@ -12214,7 +12051,7 @@ class FileTokenStore {
|
|
|
12214
12051
|
async read() {
|
|
12215
12052
|
let raw;
|
|
12216
12053
|
try {
|
|
12217
|
-
raw = await
|
|
12054
|
+
raw = await readFile29(this.filePath, "utf8");
|
|
12218
12055
|
} catch (e) {
|
|
12219
12056
|
if (e?.code === "ENOENT") {
|
|
12220
12057
|
return { version: 1, credentials: {} };
|
|
@@ -12329,7 +12166,7 @@ var TOOLS_TO_PROBE = [
|
|
|
12329
12166
|
hintKey: "gs",
|
|
12330
12167
|
usedBy: [
|
|
12331
12168
|
{ feature: "PDF/X CMYK conversion (build --format pdfx)", severity: "required" },
|
|
12332
|
-
{ feature: "validate: pdf.print.ink-coverage
|
|
12169
|
+
{ feature: "validate: pdf.print.ink-coverage", severity: "optional" }
|
|
12333
12170
|
]
|
|
12334
12171
|
},
|
|
12335
12172
|
{
|
|
@@ -12414,13 +12251,13 @@ async function getSystemDiagnostics() {
|
|
|
12414
12251
|
};
|
|
12415
12252
|
}
|
|
12416
12253
|
// src/lib/project-scaffold.ts
|
|
12417
|
-
import { access, copyFile, cp as cp2, mkdir as mkdir9, readdir as
|
|
12254
|
+
import { access, copyFile, cp as cp2, mkdir as mkdir9, readdir as readdir7, readFile as readFile31, stat as stat5, writeFile as writeFile9 } from "node:fs/promises";
|
|
12418
12255
|
import { constants as FS, existsSync as existsSync14 } from "node:fs";
|
|
12419
12256
|
import path20 from "node:path";
|
|
12420
12257
|
|
|
12421
12258
|
// src/lib/page-size.ts
|
|
12422
12259
|
import { existsSync as existsSync13 } from "node:fs";
|
|
12423
|
-
import { mkdir as mkdir8, readFile as
|
|
12260
|
+
import { mkdir as mkdir8, readFile as readFile30, writeFile as writeFile8 } from "node:fs/promises";
|
|
12424
12261
|
import path19 from "node:path";
|
|
12425
12262
|
import postcss5 from "postcss";
|
|
12426
12263
|
import { isMap as isMap2, Scalar as Scalar2 } from "yaml";
|
|
@@ -12538,7 +12375,7 @@ var relDisplay2 = (projectDir, abs) => path19.relative(projectDir, abs).split(pa
|
|
|
12538
12375
|
async function findCssSize(projectDir, sheets) {
|
|
12539
12376
|
let found = null;
|
|
12540
12377
|
for (const file of sheets) {
|
|
12541
|
-
const size = readPageSizeFromCss(await
|
|
12378
|
+
const size = readPageSizeFromCss(await readFile30(file, "utf8"));
|
|
12542
12379
|
if (size)
|
|
12543
12380
|
found = { file, size };
|
|
12544
12381
|
}
|
|
@@ -12565,7 +12402,7 @@ async function setPageSetup(projectDir, choice) {
|
|
|
12565
12402
|
const found = await findCssSize(projectDir, sheets);
|
|
12566
12403
|
const newSheet = path19.join(projectDir, "styles", "book.css");
|
|
12567
12404
|
const target = found?.file ?? sheets[sheets.length - 1] ?? newSheet;
|
|
12568
|
-
const before = existsSync13(target) ? await
|
|
12405
|
+
const before = existsSync13(target) ? await readFile30(target, "utf8") : "";
|
|
12569
12406
|
let after;
|
|
12570
12407
|
try {
|
|
12571
12408
|
after = setPageSizeInCss(before, size);
|
|
@@ -12684,7 +12521,7 @@ async function scaffoldProject(options) {
|
|
|
12684
12521
|
try {
|
|
12685
12522
|
await mkdir9(projectDir, { recursive: true });
|
|
12686
12523
|
if (customTemplateDir) {
|
|
12687
|
-
const entries = await
|
|
12524
|
+
const entries = await readdir7(customTemplateDir, { withFileTypes: true });
|
|
12688
12525
|
for (const entry of entries) {
|
|
12689
12526
|
if (entry.name === ".gutterpress-template.json")
|
|
12690
12527
|
continue;
|
|
@@ -12789,7 +12626,7 @@ async function adoptFolder(options) {
|
|
|
12789
12626
|
const author = (options.author ?? "").trim() || DEFAULT_AUTHOR;
|
|
12790
12627
|
let mdFiles;
|
|
12791
12628
|
try {
|
|
12792
|
-
const entries = await
|
|
12629
|
+
const entries = await readdir7(dir, { withFileTypes: true });
|
|
12793
12630
|
mdFiles = entries.filter((e) => e.isFile() && /\.(md|markdown)$/i.test(e.name)).map((e) => e.name).sort((a, b) => a.localeCompare(b));
|
|
12794
12631
|
if (mdFiles.length === 0) {
|
|
12795
12632
|
const tplChapter = await getAssetPath(`templates/${template}/chapter-01.md`);
|
|
@@ -12855,7 +12692,7 @@ styles:
|
|
|
12855
12692
|
async function firstSourceFile(manifestPath) {
|
|
12856
12693
|
let text;
|
|
12857
12694
|
try {
|
|
12858
|
-
text = await
|
|
12695
|
+
text = await readFile31(manifestPath, "utf8");
|
|
12859
12696
|
} catch {
|
|
12860
12697
|
return;
|
|
12861
12698
|
}
|
|
@@ -12867,14 +12704,14 @@ async function firstSourceFile(manifestPath) {
|
|
|
12867
12704
|
return m ? m[1].trim() : undefined;
|
|
12868
12705
|
}
|
|
12869
12706
|
async function fillTemplateFile(filePath, substitutions) {
|
|
12870
|
-
let text = await
|
|
12707
|
+
let text = await readFile31(filePath, "utf8");
|
|
12871
12708
|
for (const [token, value] of Object.entries(substitutions)) {
|
|
12872
12709
|
text = text.split(token).join(value);
|
|
12873
12710
|
}
|
|
12874
12711
|
await writeFile9(filePath, text, "utf8");
|
|
12875
12712
|
}
|
|
12876
12713
|
// src/lib/extension-scaffold.ts
|
|
12877
|
-
import { access as access2, mkdir as mkdir10, readFile as
|
|
12714
|
+
import { access as access2, mkdir as mkdir10, readFile as readFile32, writeFile as writeFile10 } from "node:fs/promises";
|
|
12878
12715
|
import { constants as FS2 } from "node:fs";
|
|
12879
12716
|
import path21 from "node:path";
|
|
12880
12717
|
var EXTENSION_KINDS = ["plugin", "theme"];
|
|
@@ -12991,7 +12828,7 @@ async function scaffoldExtension(options) {
|
|
|
12991
12828
|
const src = await getAssetPath(`extension-templates/${kind}/${assetName}`);
|
|
12992
12829
|
const dest = path21.join(extensionDir, relDest);
|
|
12993
12830
|
await mkdir10(path21.dirname(dest), { recursive: true });
|
|
12994
|
-
const text = await
|
|
12831
|
+
const text = await readFile32(src, "utf8");
|
|
12995
12832
|
const table = relDest.endsWith(".json") ? jsonSafe : plain;
|
|
12996
12833
|
await writeFile10(dest, substitute(text, table), "utf8");
|
|
12997
12834
|
written.push(relDest);
|
|
@@ -13017,7 +12854,7 @@ function substitute(text, table) {
|
|
|
13017
12854
|
return out;
|
|
13018
12855
|
}
|
|
13019
12856
|
// src/lib/project-templates.ts
|
|
13020
|
-
import { cp as cp3, mkdir as mkdir11, readFile as
|
|
12857
|
+
import { cp as cp3, mkdir as mkdir11, readFile as readFile33, readdir as readdir8, stat as stat6, writeFile as writeFile11 } from "node:fs/promises";
|
|
13021
12858
|
import { constants as FS3, existsSync as existsSync15 } from "node:fs";
|
|
13022
12859
|
import { access as access3 } from "node:fs/promises";
|
|
13023
12860
|
import path22 from "node:path";
|
|
@@ -13029,7 +12866,7 @@ var BUILT_IN_TEMPLATE_IDS = [
|
|
|
13029
12866
|
];
|
|
13030
12867
|
async function readTemplateChoices(manifestPath) {
|
|
13031
12868
|
try {
|
|
13032
|
-
const text = await
|
|
12869
|
+
const text = await readFile33(manifestPath, "utf8");
|
|
13033
12870
|
const doc = parseDocument2(text);
|
|
13034
12871
|
const out = {};
|
|
13035
12872
|
const preset = doc.get("preset");
|
|
@@ -13186,7 +13023,7 @@ async function saveProjectAsTemplate(options) {
|
|
|
13186
13023
|
throw new Error(`A template named "${name}" already exists. Choose a different name.`);
|
|
13187
13024
|
}
|
|
13188
13025
|
await mkdir11(dir, { recursive: true });
|
|
13189
|
-
const entries = await
|
|
13026
|
+
const entries = await readdir8(projectDir, { withFileTypes: true });
|
|
13190
13027
|
for (const entry of entries) {
|
|
13191
13028
|
if (SKIP_ENTRIES.has(entry.name))
|
|
13192
13029
|
continue;
|
|
@@ -13211,7 +13048,7 @@ async function saveProjectAsTemplate(options) {
|
|
|
13211
13048
|
async function retokeniseManifest(manifestPath) {
|
|
13212
13049
|
let text;
|
|
13213
13050
|
try {
|
|
13214
|
-
text = await
|
|
13051
|
+
text = await readFile33(manifestPath, "utf8");
|
|
13215
13052
|
} catch {
|
|
13216
13053
|
return;
|
|
13217
13054
|
}
|
|
@@ -13221,7 +13058,7 @@ async function retokeniseManifest(manifestPath) {
|
|
|
13221
13058
|
async function listCustomTemplates(templatesRoot) {
|
|
13222
13059
|
let entries;
|
|
13223
13060
|
try {
|
|
13224
|
-
entries = await
|
|
13061
|
+
entries = await readdir8(templatesRoot, { withFileTypes: true });
|
|
13225
13062
|
} catch {
|
|
13226
13063
|
return [];
|
|
13227
13064
|
}
|
|
@@ -13232,7 +13069,7 @@ async function listCustomTemplates(templatesRoot) {
|
|
|
13232
13069
|
const dir = path22.join(templatesRoot, entry.name);
|
|
13233
13070
|
let label = prettify(entry.name);
|
|
13234
13071
|
try {
|
|
13235
|
-
const meta = JSON.parse(await
|
|
13072
|
+
const meta = JSON.parse(await readFile33(path22.join(dir, TEMPLATE_META_FILE), "utf8"));
|
|
13236
13073
|
if (typeof meta.label === "string" && meta.label)
|
|
13237
13074
|
label = meta.label;
|
|
13238
13075
|
} catch {}
|
|
@@ -13258,7 +13095,7 @@ async function importTemplateFromFolder(options) {
|
|
|
13258
13095
|
return saveProjectAsTemplate({ projectDir: sourceDir, name, templatesRoot });
|
|
13259
13096
|
}
|
|
13260
13097
|
// src/lib/snippets.ts
|
|
13261
|
-
import { readdir as
|
|
13098
|
+
import { readdir as readdir9, readFile as readFile34, writeFile as writeFile12, mkdir as mkdir12, rm as rm4 } from "node:fs/promises";
|
|
13262
13099
|
import path23 from "node:path";
|
|
13263
13100
|
var SNIPPETS_DIR = "snippets";
|
|
13264
13101
|
var LEVEL_RANK = { project: 0, extension: 1, core: 2 };
|
|
@@ -13289,7 +13126,7 @@ function resolveSnippetPath(projectDir, fileName) {
|
|
|
13289
13126
|
async function scanSnippetFiles(dir) {
|
|
13290
13127
|
let names;
|
|
13291
13128
|
try {
|
|
13292
|
-
names = await
|
|
13129
|
+
names = await readdir9(dir);
|
|
13293
13130
|
} catch {
|
|
13294
13131
|
return [];
|
|
13295
13132
|
}
|
|
@@ -13299,7 +13136,7 @@ async function scanSnippetFiles(dir) {
|
|
|
13299
13136
|
continue;
|
|
13300
13137
|
let body = "";
|
|
13301
13138
|
try {
|
|
13302
|
-
body = await
|
|
13139
|
+
body = await readFile34(path23.join(dir, fileName), "utf8");
|
|
13303
13140
|
} catch {
|
|
13304
13141
|
continue;
|
|
13305
13142
|
}
|
|
@@ -13388,7 +13225,7 @@ async function collectLibrary(projectDir) {
|
|
|
13388
13225
|
listed.component = name;
|
|
13389
13226
|
continue;
|
|
13390
13227
|
}
|
|
13391
|
-
const body = await
|
|
13228
|
+
const body = await readFile34(path23.join(entry.dir, rel), "utf8").catch(() => null);
|
|
13392
13229
|
if (body === null)
|
|
13393
13230
|
continue;
|
|
13394
13231
|
snippets2.push({
|
|
@@ -13457,7 +13294,7 @@ async function listLocationVariants(projectDir) {
|
|
|
13457
13294
|
}
|
|
13458
13295
|
// src/lib/extension-import.ts
|
|
13459
13296
|
import { existsSync as existsSync16 } from "node:fs";
|
|
13460
|
-
import { cp as cp4, mkdir as mkdir13, mkdtemp as mkdtemp2, readFile as
|
|
13297
|
+
import { cp as cp4, mkdir as mkdir13, mkdtemp as mkdtemp2, readFile as readFile35, rm as rm5, writeFile as writeFile13 } from "node:fs/promises";
|
|
13461
13298
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
13462
13299
|
import path24 from "node:path";
|
|
13463
13300
|
import { unzipSync } from "fflate";
|
|
@@ -13530,7 +13367,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
13530
13367
|
if (!existsSync16(cssPath)) {
|
|
13531
13368
|
throw new Error("A theme must contain a theme.css file.");
|
|
13532
13369
|
}
|
|
13533
|
-
const css = await
|
|
13370
|
+
const css = await readFile35(cssPath, "utf8");
|
|
13534
13371
|
const { reject, warnings: printFindings } = classifyThemeCssFindings(checkCss(css, "theme.css"));
|
|
13535
13372
|
if (reject) {
|
|
13536
13373
|
throw new Error(`theme.css could not be parsed — ${reject.message}`);
|
|
@@ -13558,7 +13395,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
13558
13395
|
for (let i = 0;i < extraSheets.length; i++) {
|
|
13559
13396
|
const rel = extraSheets[i];
|
|
13560
13397
|
const sheetPath = extraSheetPaths[i];
|
|
13561
|
-
const sheetCss = await
|
|
13398
|
+
const sheetCss = await readFile35(sheetPath, "utf8");
|
|
13562
13399
|
const { reject: sheetReject, warnings: sheetFindings } = classifyThemeCssFindings(checkCss(sheetCss, rel));
|
|
13563
13400
|
if (sheetReject) {
|
|
13564
13401
|
throw new Error(`${rel} could not be parsed — ${sheetReject.message}`);
|
|
@@ -13591,7 +13428,7 @@ async function completeLandedManifest(dest, id, meta, metaExists) {
|
|
|
13591
13428
|
let pkg = {};
|
|
13592
13429
|
if (metaExists) {
|
|
13593
13430
|
try {
|
|
13594
|
-
const parsed = JSON.parse(await
|
|
13431
|
+
const parsed = JSON.parse(await readFile35(file, "utf8"));
|
|
13595
13432
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
13596
13433
|
pkg = parsed;
|
|
13597
13434
|
}
|
|
@@ -13693,11 +13530,11 @@ async function importExtensionFromCssText(projectDir, css, name) {
|
|
|
13693
13530
|
async function importExtensionFromFile(projectDir, filePath) {
|
|
13694
13531
|
const ext = path24.extname(filePath).toLowerCase();
|
|
13695
13532
|
if (ext === ".zip") {
|
|
13696
|
-
const buf = await
|
|
13533
|
+
const buf = await readFile35(filePath);
|
|
13697
13534
|
return importExtensionFromZip(projectDir, new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
|
|
13698
13535
|
}
|
|
13699
13536
|
if (ext === ".css") {
|
|
13700
|
-
const css = await
|
|
13537
|
+
const css = await readFile35(filePath, "utf8");
|
|
13701
13538
|
return importExtensionFromCssText(projectDir, css, prettify(path24.basename(filePath, ".css")));
|
|
13702
13539
|
}
|
|
13703
13540
|
throw new Error("Choose a .zip extension package or a .css stylesheet.");
|
|
@@ -15586,7 +15423,7 @@ async function setUpOnlineBackup(options) {
|
|
|
15586
15423
|
}
|
|
15587
15424
|
}
|
|
15588
15425
|
// src/lib/remote-auth/clone.ts
|
|
15589
|
-
import { mkdir as mkdir15, readdir as
|
|
15426
|
+
import { mkdir as mkdir15, readdir as readdir10, rm as rm6 } from "node:fs/promises";
|
|
15590
15427
|
import path26 from "node:path";
|
|
15591
15428
|
import git6 from "isomorphic-git";
|
|
15592
15429
|
function sanitizeCloneFolderName(name) {
|
|
@@ -15594,7 +15431,7 @@ function sanitizeCloneFolderName(name) {
|
|
|
15594
15431
|
}
|
|
15595
15432
|
async function assertCloneTarget(dir) {
|
|
15596
15433
|
try {
|
|
15597
|
-
const entries = await
|
|
15434
|
+
const entries = await readdir10(dir);
|
|
15598
15435
|
if (entries.length > 0) {
|
|
15599
15436
|
throw new Error("That folder already has files in it. Choose an empty folder for the book.");
|
|
15600
15437
|
}
|
|
@@ -16016,16 +15853,16 @@ async function repairOnlineBackup(options) {
|
|
|
16016
15853
|
}
|
|
16017
15854
|
// src/lib/remote-auth/scorched-earth.ts
|
|
16018
15855
|
import git9 from "isomorphic-git";
|
|
16019
|
-
import { cp as cp6, mkdir as mkdir17, readdir as
|
|
15856
|
+
import { cp as cp6, mkdir as mkdir17, readdir as readdir11, rm as rm8 } from "node:fs/promises";
|
|
16020
15857
|
import path28 from "node:path";
|
|
16021
15858
|
async function emptyDir(dir) {
|
|
16022
|
-
for (const entry of await
|
|
15859
|
+
for (const entry of await readdir11(dir)) {
|
|
16023
15860
|
await rm8(path28.join(dir, entry), { recursive: true, force: true });
|
|
16024
15861
|
}
|
|
16025
15862
|
}
|
|
16026
15863
|
async function copyFilesBack(from, to) {
|
|
16027
15864
|
const skip = path28.join(from, ".git");
|
|
16028
|
-
for (const entry of await
|
|
15865
|
+
for (const entry of await readdir11(from)) {
|
|
16029
15866
|
const src = path28.join(from, entry);
|
|
16030
15867
|
if (src === skip)
|
|
16031
15868
|
continue;
|
|
@@ -16078,7 +15915,7 @@ async function scorchedEarth(options) {
|
|
|
16078
15915
|
}
|
|
16079
15916
|
}
|
|
16080
15917
|
// src/lib/publish/run-publish.ts
|
|
16081
|
-
import { readdir as
|
|
15918
|
+
import { readdir as readdir13, stat as stat11 } from "node:fs/promises";
|
|
16082
15919
|
import path33 from "node:path";
|
|
16083
15920
|
|
|
16084
15921
|
// src/lib/publish/command-runner.ts
|
|
@@ -16309,7 +16146,7 @@ var drivethrurpgProvider = {
|
|
|
16309
16146
|
};
|
|
16310
16147
|
|
|
16311
16148
|
// src/lib/publish/providers/gdrive.ts
|
|
16312
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
16149
|
+
import { mkdtemp as mkdtemp3, readFile as readFile36, readdir as readdir12, rm as rm9, stat as stat8, writeFile as writeFile16 } from "node:fs/promises";
|
|
16313
16150
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
16314
16151
|
import path30 from "node:path";
|
|
16315
16152
|
import { Zip, ZipDeflate } from "fflate";
|
|
@@ -16694,19 +16531,19 @@ function zipEntriesNonBlocking(entries, opts) {
|
|
|
16694
16531
|
});
|
|
16695
16532
|
}
|
|
16696
16533
|
async function collectZipEntries(root, dir, out, onWarn) {
|
|
16697
|
-
const entries = await
|
|
16534
|
+
const entries = await readdir12(dir, { withFileTypes: true });
|
|
16698
16535
|
for (const entry of entries) {
|
|
16699
16536
|
const full = path30.join(dir, entry.name);
|
|
16700
16537
|
if (entry.isDirectory()) {
|
|
16701
16538
|
await collectZipEntries(root, full, out, onWarn);
|
|
16702
16539
|
} else if (entry.isFile()) {
|
|
16703
16540
|
const rel = path30.relative(root, full).split(path30.sep).join("/");
|
|
16704
|
-
out[rel] = await
|
|
16541
|
+
out[rel] = await readFile36(full);
|
|
16705
16542
|
} else if (entry.isSymbolicLink()) {
|
|
16706
16543
|
const resolved = await stat8(full).catch(() => null);
|
|
16707
16544
|
if (resolved?.isFile()) {
|
|
16708
16545
|
const rel = path30.relative(root, full).split(path30.sep).join("/");
|
|
16709
|
-
out[rel] = await
|
|
16546
|
+
out[rel] = await readFile36(full);
|
|
16710
16547
|
} else if (resolved?.isDirectory()) {
|
|
16711
16548
|
await collectZipEntries(root, full, out, onWarn);
|
|
16712
16549
|
} else {
|
|
@@ -17461,7 +17298,7 @@ async function htmlDirIssues(dir) {
|
|
|
17461
17298
|
message: `${dir} has no ${BOOK_HTML} — it isn't an HTML export. ${HTML_HINT}`
|
|
17462
17299
|
});
|
|
17463
17300
|
}
|
|
17464
|
-
const entries = await
|
|
17301
|
+
const entries = await readdir13(dir).catch(() => []);
|
|
17465
17302
|
const extras = entries.filter((name) => name.toLowerCase().endsWith(".pdf") || name === BUILD_FINGERPRINT_FILENAME);
|
|
17466
17303
|
if (extras.length > 0) {
|
|
17467
17304
|
issues.push({
|
|
@@ -17572,7 +17409,7 @@ async function disconnectPublishCredential(key, deps, options = {}) {
|
|
|
17572
17409
|
}
|
|
17573
17410
|
}
|
|
17574
17411
|
// src/lib/publish/selections.ts
|
|
17575
|
-
import { chmod as chmod3, mkdir as mkdir21, readFile as
|
|
17412
|
+
import { chmod as chmod3, mkdir as mkdir21, readFile as readFile37, writeFile as writeFile18 } from "node:fs/promises";
|
|
17576
17413
|
import path34 from "node:path";
|
|
17577
17414
|
class PublishSelectionsStore {
|
|
17578
17415
|
filePath;
|
|
@@ -17582,7 +17419,7 @@ class PublishSelectionsStore {
|
|
|
17582
17419
|
}
|
|
17583
17420
|
async read() {
|
|
17584
17421
|
try {
|
|
17585
|
-
const raw = await
|
|
17422
|
+
const raw = await readFile37(this.filePath, "utf8");
|
|
17586
17423
|
const parsed = JSON.parse(raw);
|
|
17587
17424
|
if (parsed && typeof parsed === "object") {
|
|
17588
17425
|
return {
|