gutterpress 0.11.23-alpha.2 → 0.11.23-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -13
- package/dist/{README-8ef336pp.md → README-syg2hn83.md} +13 -19
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +5 -5
- package/dist/{build-encgnv0c.js → build-c48eer6f.js} +8 -8
- package/dist/checks/asset/extensions.d.ts +1 -1
- package/dist/checks/asset/index.d.ts +0 -1
- package/dist/checks/policy.d.ts +5 -4
- package/dist/checks/source/config-file.d.ts +9 -6
- package/dist/checks/source/dangling-links.d.ts +1 -1
- package/dist/checks/types.d.ts +2 -2
- package/dist/{cli-pz4bn98t.js → cli-54xt7pjg.js} +19 -13
- package/dist/{index-2ppw6q9v.js → cli-5crpmz42.js} +80 -1
- package/dist/{cli-xq6djqbj.js → cli-93mkaw6v.js} +166 -308
- package/dist/{cli-evg8sbht.js → cli-d9mm3vvh.js} +7 -2
- package/dist/{cli-kh8v4vvw.js → cli-gnpea9dx.js} +1 -1
- package/dist/{cli-aqh6brew.js → cli-pspjxnc8.js} +1 -1
- package/dist/{cli-pwwfxdgb.js → cli-rqh295j9.js} +15 -21
- package/dist/{index-x3ema2w3.js → cli-y6d1arem.js} +321 -272
- package/dist/cli.js +12 -12
- package/dist/{doctor-5gtpgpd1.js → doctor-ptb346k8.js} +4 -3
- package/dist/engine/shared/gcpm-extract.d.ts +8 -0
- package/dist/engine/shared/print-area.d.ts +16 -4
- package/dist/{engine-mc5br8g8.js → engine-4rzdx6af.js} +1 -1
- package/dist/{engine-tn13gk0b.js → engine-6txhxmmv.js} +2 -2
- package/dist/{ext-wn2a5afq.js → ext-gy8szg3x.js} +8 -8
- package/dist/{extension-restore-s3x1618g.js → extension-restore-dymzcasz.js} +2 -2
- package/dist/{extension-restore-g8jnmmzq.js → extension-restore-m8tj43e7.js} +4 -4
- package/dist/{gutterpress-viewer-701vf4qp.js → gutterpress-viewer-mafsby5k.js} +9 -2
- package/dist/{cli-2ppw6q9v.js → index-5crpmz42.js} +80 -1
- package/dist/{index-0skhjx3e.js → index-5kh4c78h.js} +19 -13
- package/dist/{index-2bjcghze.js → index-8ywkd2sv.js} +15 -21
- package/dist/{index-j5z86r4a.js → index-ea94zbq4.js} +329 -488
- package/dist/{cli-sdmv4bba.js → index-kaks3mm2.js} +448 -156
- package/dist/{index-4x6y8krm.js → index-yc3xj266.js} +1 -1
- package/dist/index.js +6 -6
- package/dist/lib/build-preflight.d.ts +4 -10
- package/dist/lib/build-runner.d.ts +1 -1
- package/dist/lib/extension-manager.d.ts +8 -7
- package/dist/lib/extension-manifest.d.ts +1 -13
- package/dist/lib/ghostscript.d.ts +5 -2
- package/dist/lib/lint-rules.d.ts +5 -1
- package/dist/lib/manifest-config.d.ts +3 -2
- package/dist/lib/manifest-old-spellings.d.ts +64 -0
- package/dist/lib/manifest.d.ts +15 -1
- package/dist/lib/markdown/assemble.d.ts +4 -3
- package/dist/lib/markdown/chapter-id.d.ts +1 -1
- package/dist/lib/markdown/index.d.ts +1 -1
- package/dist/lib/markdown/table.d.ts +5 -3
- package/dist/lib/page-size.d.ts +45 -33
- package/dist/lib/pdf-inspect.d.ts +27 -0
- package/dist/lib/presets.d.ts +10 -8
- package/dist/lib/printsafe.d.ts +2 -1
- package/dist/lib/project-scaffold.d.ts +1 -1
- package/dist/lib/project-templates.d.ts +9 -0
- package/dist/lib/remote-auth/sync-messages.d.ts +2 -2
- package/dist/lib/targets.d.ts +4 -4
- package/dist/lib/validation-exec.d.ts +4 -4
- package/dist/manifest-2kc56wvw.yaml +22 -0
- package/dist/manifest-41ddzm13.yaml +18 -0
- package/dist/manifest-79zsgkpn.yaml +26 -0
- package/dist/manifest-sqzm4681.yaml +22 -0
- package/dist/{manifest.schema-t7nx2stg.json → manifest.schema-w86c1xw1.json} +30 -308
- package/dist/{new-mhs46pw3.js → new-t2t28ccb.js} +9 -15
- package/dist/{package-aenwjew3.json → package-awwkt7sa.json} +0 -1
- package/dist/{page-rules-nsj3aw42.css → page-rules-ytj9p04v.css} +12 -40
- package/dist/{pdf-inspect-wnw4za71.js → pdf-inspect-b3mf81ex.js} +3 -1
- package/dist/{plugin.test.js-xgxrd8hf.tpl → plugin.test.js-gxzqqfdh.tpl} +0 -1
- package/dist/{plugins-2cek4mz1.js → plugins-fec8t2bh.js} +1 -1
- package/dist/{plugins-1sxe0dkz.js → plugins-sqa4msdk.js} +3 -3
- package/dist/{preflight-3k2b1x8e.js → preflight-srq8fktp.js} +8 -8
- package/dist/preview/file-watcher.d.ts +1 -3
- package/dist/{preview-6p9v6rht.js → preview-zsp9hstd.js} +8 -8
- package/dist/{publish-qgsngy8s.js → publish-hhy31gr9.js} +8 -8
- package/dist/render.js +30 -562
- package/dist/schema/manifest.types.d.ts +60 -105
- package/dist/{tokens-7984461t.css → tokens-n82t4k9h.css} +12 -12
- package/dist/{validate-0v7xw6wa.js → validate-r9jdz85h.js} +8 -8
- package/package.json +1 -1
- package/dist/checks/asset/approved-fonts.d.ts +0 -3
- package/dist/manifest-eagfhba6.yaml +0 -19
- package/dist/manifest-n1gh3qw5.yaml +0 -9
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
DEBOUNCE,
|
|
3
3
|
FriendlyHttpError,
|
|
4
4
|
MANIFEST_FILENAMES,
|
|
5
|
+
MARGIN_EDGES,
|
|
5
6
|
PRESETS,
|
|
6
7
|
PRESET_IDS,
|
|
7
8
|
TARGETS,
|
|
@@ -9,25 +10,34 @@ import {
|
|
|
9
10
|
UsageError,
|
|
10
11
|
addExtension,
|
|
11
12
|
applyDefaultPdfStrictChecks,
|
|
13
|
+
cssLength,
|
|
12
14
|
ensureProjectGitignore,
|
|
13
|
-
ensureSeq,
|
|
14
15
|
extensionConfigFor,
|
|
16
|
+
formatPageSize,
|
|
17
|
+
isNonNegativePoints,
|
|
18
|
+
isPositivePoints,
|
|
19
|
+
judgeRect,
|
|
15
20
|
listProjectExtensions,
|
|
16
21
|
loadManifest,
|
|
17
22
|
loadManifestDoc,
|
|
18
23
|
loadManifestWithPath,
|
|
19
24
|
npmRegistryUrl,
|
|
25
|
+
pageBoxes,
|
|
26
|
+
printAreaMeta,
|
|
20
27
|
publishTargetFor,
|
|
28
|
+
readEntryCss,
|
|
21
29
|
resolveConfig,
|
|
22
30
|
resolveConfigForTarget,
|
|
23
31
|
resolvePort,
|
|
24
32
|
resolveTargets,
|
|
25
33
|
restoreForCommand,
|
|
34
|
+
round3,
|
|
26
35
|
scalarString,
|
|
27
36
|
uniqueExtensionId,
|
|
37
|
+
validatePageMargin,
|
|
28
38
|
withFetchTimeout,
|
|
29
39
|
writeManifestDoc
|
|
30
|
-
} from "./index-
|
|
40
|
+
} from "./index-kaks3mm2.js";
|
|
31
41
|
import {
|
|
32
42
|
EXTENSION_MANIFEST_FILENAME,
|
|
33
43
|
GUTTERPRESS_CSS,
|
|
@@ -63,18 +73,18 @@ import {
|
|
|
63
73
|
setLogLevel,
|
|
64
74
|
slugify,
|
|
65
75
|
warn
|
|
66
|
-
} from "./index-
|
|
76
|
+
} from "./index-8ywkd2sv.js";
|
|
67
77
|
import {
|
|
68
78
|
blackTextToGray
|
|
69
79
|
} from "./index-ht1bcqdd.js";
|
|
70
80
|
import {
|
|
81
|
+
DEFAULT_PAGE_MARGIN_PT,
|
|
71
82
|
MARGIN_BOX_IGNORED_PROPERTIES,
|
|
72
83
|
extract,
|
|
73
84
|
getAssetPath,
|
|
74
85
|
lastPageMode,
|
|
75
|
-
parseSize,
|
|
76
86
|
resolvePage
|
|
77
|
-
} from "./index-
|
|
87
|
+
} from "./index-5kh4c78h.js";
|
|
78
88
|
import {
|
|
79
89
|
BuildError
|
|
80
90
|
} from "./index-46ycxe6r.js";
|
|
@@ -89,8 +99,9 @@ import {
|
|
|
89
99
|
isLoadable,
|
|
90
100
|
loadPdf,
|
|
91
101
|
pageHasMarks,
|
|
102
|
+
pagesWithVisibleShadingPaints,
|
|
92
103
|
retainPdfCache
|
|
93
|
-
} from "./index-
|
|
104
|
+
} from "./index-5crpmz42.js";
|
|
94
105
|
import {
|
|
95
106
|
INSTALL_HINTS,
|
|
96
107
|
enhancedPath,
|
|
@@ -146,14 +157,14 @@ var MSG_UP_TO_DATE = "Everything is in sync.";
|
|
|
146
157
|
var MSG_UP_TO_DATE_PULLED = "Everything is in sync. The latest online changes were downloaded to this computer.";
|
|
147
158
|
var MSG_SYNCED = "Your changes are online.";
|
|
148
159
|
var MSG_SYNCED_MERGED = "Your changes are online, combined with changes from the online copy.";
|
|
149
|
-
var MSG_OFFLINE = "Your changes are saved on this computer.
|
|
160
|
+
var MSG_OFFLINE = "Your changes are saved on this computer. Gutterpress couldn't reach the online repository — try syncing again when you're back online.";
|
|
150
161
|
var MSG_AUTH = "The online repository didn't accept the saved connection. Reconnect and try again.";
|
|
151
162
|
var MSG_INSECURE_TRANSPORT = "This book's online address isn't secure, so the saved connection wasn't sent — connections are never sent over an insecure address. Switch the address to a secure one (starting with https), or to a local loopback address for a server on this computer, to sync.";
|
|
152
163
|
var MSG_BUSY = "The online copy is changing very quickly right now. Your work is saved on this computer — try Sync again in a moment.";
|
|
153
164
|
var MSG_HISTORY_UNREADABLE = "This book's version history can't be read, so syncing can't continue. Your writing is safe — every file is still here on this computer. Use Repair online backup (in Where your work is kept) to get history and syncing working again.";
|
|
154
165
|
var MSG_UNRELATED = "This book and the online copy have no history in common, so they can't be combined. Either the online address points at a different book, or this book's own history was lost and started over. Your writing is safe — every file is still here on this computer. Check the online address; if it is correct, use Repair online backup (in Where your work is kept).";
|
|
155
166
|
var MSG_NO_REMOTE = "This book isn't connected to an online repository yet.";
|
|
156
|
-
var MSG_SSH_REMOTE = "This book's online address uses SSH (git@…), which
|
|
167
|
+
var MSG_SSH_REMOTE = "This book's online address uses SSH (git@…), which Gutterpress can't sync to. Switch it to the web (HTTPS) address to sync from here.";
|
|
157
168
|
var MSG_NO_BRANCH = "This book's version history isn't on a named branch, so it can't be synced right now.";
|
|
158
169
|
var SYNC_SNAPSHOT_MESSAGE = "Automatic backup of your work";
|
|
159
170
|
var SYNC_LATE_EDIT_MESSAGE = "Saved the edit you made while syncing";
|
|
@@ -185,95 +196,26 @@ function canonicalChapterId(p) {
|
|
|
185
196
|
return s;
|
|
186
197
|
}
|
|
187
198
|
|
|
188
|
-
// src/engine/shared/print-area.ts
|
|
189
|
-
function isPositivePoints(value) {
|
|
190
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
191
|
-
}
|
|
192
|
-
var round3 = (n) => Math.round(n * 1000) / 1000;
|
|
193
|
-
function cssLength(pt) {
|
|
194
|
-
const inches = round3(pt / 72);
|
|
195
|
-
return Math.abs(inches * 72 - pt) < 0.000001 ? `${inches}in` : `${round3(pt)}pt`;
|
|
196
|
-
}
|
|
197
|
-
function formatPageSize(page) {
|
|
198
|
-
return `${cssLength(page.width)} ${cssLength(page.height)}`;
|
|
199
|
-
}
|
|
200
|
-
function printAreaMeta(page) {
|
|
201
|
-
const area = JSON.stringify({
|
|
202
|
-
width: page.width,
|
|
203
|
-
height: page.height,
|
|
204
|
-
bleed: page.bleed ?? 0,
|
|
205
|
-
safe: page.safe ?? 0,
|
|
206
|
-
artSafe: page.artSafe,
|
|
207
|
-
gutter: page.gutter,
|
|
208
|
-
preset: page.preset,
|
|
209
|
-
spec: page.spec
|
|
210
|
-
});
|
|
211
|
-
return `<meta name="gp-print-area" content='${area.replace(/&/g, "&").replace(/'/g, "'")}'>`;
|
|
212
|
-
}
|
|
213
|
-
function pageBoxes(area, side, scale = 1) {
|
|
214
|
-
const w = area.width * scale;
|
|
215
|
-
const h = area.height * scale;
|
|
216
|
-
const b = Math.max(0, area.bleed) * scale;
|
|
217
|
-
const s = Math.max(0, area.safe) * scale;
|
|
218
|
-
const a = Math.max(0, area.artSafe) * scale;
|
|
219
|
-
const g = Math.max(0, area.gutter) * scale;
|
|
220
|
-
const [textLeft, textRight] = side === "verso" ? [s, g] : [g, s];
|
|
221
|
-
const [artLeft, artRight] = side === "verso" ? [a, g] : [g, a];
|
|
222
|
-
const trim = { x: side === "verso" ? b : 0, y: b, w: Math.max(0, w - b), h: Math.max(0, h - 2 * b) };
|
|
223
|
-
return {
|
|
224
|
-
sheet: { x: 0, y: 0, w, h },
|
|
225
|
-
trim,
|
|
226
|
-
safe: {
|
|
227
|
-
x: trim.x + textLeft,
|
|
228
|
-
y: trim.y + s,
|
|
229
|
-
w: Math.max(0, trim.w - textLeft - textRight),
|
|
230
|
-
h: Math.max(0, trim.h - 2 * s)
|
|
231
|
-
},
|
|
232
|
-
safeArt: {
|
|
233
|
-
x: trim.x + artLeft,
|
|
234
|
-
y: trim.y + a,
|
|
235
|
-
w: Math.max(0, trim.w - artLeft - artRight),
|
|
236
|
-
h: Math.max(0, trim.h - 2 * a)
|
|
237
|
-
},
|
|
238
|
-
bleed: b
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
var SEVERITY = { inside: 0, unsafe: 1, bleed: 2, "off-page": 3 };
|
|
242
|
-
function judgeRect(r, boxes, rule, tol = 0) {
|
|
243
|
-
const { sheet, trim } = boxes;
|
|
244
|
-
const safe = rule === "art" ? boxes.safeArt : boxes.safe;
|
|
245
|
-
const sides = [
|
|
246
|
-
["left", -r.x, -safe.x, -trim.x, -sheet.x],
|
|
247
|
-
["right", r.x + r.w, safe.x + safe.w, trim.x + trim.w, sheet.x + sheet.w],
|
|
248
|
-
["top", -r.y, -safe.y, -trim.y, -sheet.y],
|
|
249
|
-
["bottom", r.y + r.h, safe.y + safe.h, trim.y + trim.h, sheet.y + sheet.h]
|
|
250
|
-
];
|
|
251
|
-
let worst = { kind: "inside", gap: 0, need: 0, edge: null };
|
|
252
|
-
let beyond = 0;
|
|
253
|
-
for (const [edge, ink, limit, trimEdge, sheetEdge] of sides) {
|
|
254
|
-
const past = ink - limit;
|
|
255
|
-
if (past <= tol || rule === "art" && ink >= sheetEdge - tol)
|
|
256
|
-
continue;
|
|
257
|
-
const kind = ink > sheetEdge + tol ? "off-page" : ink > trimEdge + tol ? "bleed" : "unsafe";
|
|
258
|
-
if (SEVERITY[kind] > SEVERITY[worst.kind] || kind === worst.kind && past > beyond) {
|
|
259
|
-
worst = { kind, gap: trimEdge - ink, need: trimEdge - limit, edge };
|
|
260
|
-
beyond = past;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return worst;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
199
|
// src/lib/markdown/assemble.ts
|
|
267
|
-
function
|
|
200
|
+
function pageGeometryCss(page) {
|
|
268
201
|
if (!page || !isPositivePoints(page.width) || !isPositivePoints(page.height))
|
|
269
202
|
return null;
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
203
|
+
const size = `size: ${formatPageSize(page)};`;
|
|
204
|
+
const m = page.margin;
|
|
205
|
+
if (!m)
|
|
206
|
+
return `@page { ${size} }`;
|
|
207
|
+
const [top, bottom, inside, outside] = [m.top, m.bottom, m.inside, m.outside].map(cssLength);
|
|
208
|
+
const recto = `margin: ${top} ${outside} ${bottom} ${inside};`;
|
|
209
|
+
const verso = `margin: ${top} ${inside} ${bottom} ${outside};`;
|
|
210
|
+
return [`@page { ${size} ${recto} }`, `@page :left { ${verso} }`, `@page :right { ${recto} }`].join(`
|
|
211
|
+
`);
|
|
212
|
+
}
|
|
213
|
+
function defaultPageMarginCss(page) {
|
|
214
|
+
if (!page || page.margin)
|
|
274
215
|
return null;
|
|
275
|
-
|
|
276
|
-
|
|
216
|
+
return `@layer gp.vocab {
|
|
217
|
+
@page { margin: ${cssLength(DEFAULT_PAGE_MARGIN_PT)}; }
|
|
218
|
+
}`;
|
|
277
219
|
}
|
|
278
220
|
async function assembleBookHtml(opts) {
|
|
279
221
|
const title = opts.title ?? "Document";
|
|
@@ -313,8 +255,8 @@ async function assembleBookHtml(opts) {
|
|
|
313
255
|
imageRefs.add(ref);
|
|
314
256
|
opts.onImageRefs([...imageRefs]);
|
|
315
257
|
}
|
|
316
|
-
const
|
|
317
|
-
|
|
258
|
+
const pageGeometry = pageGeometryCss(opts.printArea);
|
|
259
|
+
const defaultMargin = defaultPageMarginCss(opts.printArea);
|
|
318
260
|
const inlineCss = [
|
|
319
261
|
"@layer gp.marker, gp.vocab;",
|
|
320
262
|
`/* gutterpress markers */
|
|
@@ -326,14 +268,16 @@ ${TWO_VERSIONS_CSS.trim()}
|
|
|
326
268
|
@layer gp.vocab {
|
|
327
269
|
${GUTTERPRESS_CSS.trim()}
|
|
328
270
|
}`,
|
|
329
|
-
|
|
330
|
-
${
|
|
271
|
+
defaultMargin ? `/* page margins (default) */
|
|
272
|
+
${defaultMargin}` : null,
|
|
331
273
|
pluginCss ? `/* extension css */
|
|
332
274
|
${pluginCss.trim()}` : null,
|
|
333
275
|
`/* column alignment */
|
|
334
276
|
${TABLE_ALIGN_CSS.trim()}`,
|
|
335
277
|
projectCss ? `/* project css */
|
|
336
|
-
${projectCss.trim()}` : null
|
|
278
|
+
${projectCss.trim()}` : null,
|
|
279
|
+
pageGeometry ? `/* page geometry (manifest) */
|
|
280
|
+
${pageGeometry}` : null
|
|
337
281
|
].filter(Boolean).join(`
|
|
338
282
|
|
|
339
283
|
`);
|
|
@@ -1012,7 +956,9 @@ async function findPostScriptShadingPages(pdfBytes) {
|
|
|
1012
956
|
if (usesPostScriptShading(asDict(page.node.Resources())))
|
|
1013
957
|
pages.push(i + 1);
|
|
1014
958
|
});
|
|
1015
|
-
|
|
959
|
+
if (pages.length === 0)
|
|
960
|
+
return pages;
|
|
961
|
+
return pagesWithVisibleShadingPaints(pdfBytes, pages).catch(() => pages);
|
|
1016
962
|
}
|
|
1017
963
|
async function stripAnnotations(pdfPath, stagingDir) {
|
|
1018
964
|
const tmp = join2(stagingDir, "annotations-stripped.pdf");
|
|
@@ -1407,7 +1353,7 @@ async function writeBuildFingerprint(input) {
|
|
|
1407
1353
|
// src/lib/validation-exec.ts
|
|
1408
1354
|
import { existsSync as existsSync7 } from "node:fs";
|
|
1409
1355
|
import { readFile as readFile20 } from "node:fs/promises";
|
|
1410
|
-
import { dirname as
|
|
1356
|
+
import { dirname as dirname3, join as join3, resolve as resolve5 } from "node:path";
|
|
1411
1357
|
|
|
1412
1358
|
// src/lib/markdownlint-fix.ts
|
|
1413
1359
|
import { readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
|
|
@@ -1416,18 +1362,20 @@ import { lint as lint2 } from "markdownlint/sync";
|
|
|
1416
1362
|
|
|
1417
1363
|
// src/checks/source/config-file.ts
|
|
1418
1364
|
import { existsSync as existsSync3 } from "node:fs";
|
|
1419
|
-
import { resolve as resolve2 } from "node:path";
|
|
1420
|
-
function findConfigFile(
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1365
|
+
import { dirname, resolve as resolve2 } from "node:path";
|
|
1366
|
+
function findConfigFile(startDir, names) {
|
|
1367
|
+
let dir = resolve2(startDir);
|
|
1368
|
+
for (;; ) {
|
|
1369
|
+
for (const name of names) {
|
|
1370
|
+
const p = resolve2(dir, name);
|
|
1371
|
+
if (existsSync3(p))
|
|
1372
|
+
return p;
|
|
1373
|
+
}
|
|
1374
|
+
const parent = dirname(dir);
|
|
1375
|
+
if (existsSync3(resolve2(dir, ".git")) || parent === dir)
|
|
1376
|
+
return null;
|
|
1377
|
+
dir = parent;
|
|
1429
1378
|
}
|
|
1430
|
-
return null;
|
|
1431
1379
|
}
|
|
1432
1380
|
|
|
1433
1381
|
// src/checks/source/markdownlint.ts
|
|
@@ -1539,17 +1487,13 @@ var check = {
|
|
|
1539
1487
|
category: "source",
|
|
1540
1488
|
phase: "pre-build",
|
|
1541
1489
|
async run(ctx) {
|
|
1542
|
-
const sourceConfig = ctx.config.validate.source;
|
|
1543
|
-
if (sourceConfig.markdownlint === false)
|
|
1544
|
-
return [];
|
|
1545
1490
|
const files = ctx.markdownFiles;
|
|
1546
1491
|
if (!files || files.length === 0)
|
|
1547
1492
|
return [];
|
|
1548
|
-
const
|
|
1549
|
-
|
|
1550
|
-
if (!resolvedConfig && !configPath)
|
|
1493
|
+
const resolvedConfig = findConfigFile(ctx.inputDir, CONFIG_NAMES);
|
|
1494
|
+
if (!resolvedConfig)
|
|
1551
1495
|
return [];
|
|
1552
|
-
const config =
|
|
1496
|
+
const config = await loadConfig(resolvedConfig);
|
|
1553
1497
|
const results = lint({ files, config });
|
|
1554
1498
|
const out = [];
|
|
1555
1499
|
for (const [file, violations] of Object.entries(results)) {
|
|
@@ -1588,8 +1532,7 @@ async function uncommittedAmong(inputDir, files) {
|
|
|
1588
1532
|
return files.filter((file) => changed.has(repoSubPath(repoRoot, file)));
|
|
1589
1533
|
}
|
|
1590
1534
|
async function applyMarkdownlintFixes(ctx) {
|
|
1591
|
-
|
|
1592
|
-
if (sourceConfig.markdownlint === false) {
|
|
1535
|
+
if (ctx.config.checks["source.markdownlint"] === "off") {
|
|
1593
1536
|
log.warn("--fix: source.markdownlint is disabled for this book — no files were fixed.");
|
|
1594
1537
|
return [];
|
|
1595
1538
|
}
|
|
@@ -1598,13 +1541,12 @@ async function applyMarkdownlintFixes(ctx) {
|
|
|
1598
1541
|
log.warn("--fix: no markdown files to fix.");
|
|
1599
1542
|
return [];
|
|
1600
1543
|
}
|
|
1601
|
-
const
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
log.warn(`--fix: no markdownlint config found in ${ctx.inputDir} (looked for ${CONFIG_NAMES.join(", ")}) — no files were fixed.`);
|
|
1544
|
+
const resolvedConfig = findConfigFile(ctx.inputDir, CONFIG_NAMES);
|
|
1545
|
+
if (!resolvedConfig) {
|
|
1546
|
+
log.warn(`--fix: no markdownlint config found in ${ctx.inputDir} or a folder above it (looked for ${CONFIG_NAMES.join(", ")}) — no files were fixed.`);
|
|
1605
1547
|
return [];
|
|
1606
1548
|
}
|
|
1607
|
-
const config =
|
|
1549
|
+
const config = await loadConfig(resolvedConfig);
|
|
1608
1550
|
const results = lint2({ files, config });
|
|
1609
1551
|
const pending = [];
|
|
1610
1552
|
for (const [file, violations] of Object.entries(results)) {
|
|
@@ -1638,10 +1580,7 @@ import { readFile as readFile7 } from "node:fs/promises";
|
|
|
1638
1580
|
// src/checks/policy.ts
|
|
1639
1581
|
var INSPECT_FAILED_CODE = "inspect-failed";
|
|
1640
1582
|
function isCheckEnabled(check2, config) {
|
|
1641
|
-
|
|
1642
|
-
if (entry === false)
|
|
1643
|
-
return false;
|
|
1644
|
-
if (typeof entry === "object" && entry.enabled === false)
|
|
1583
|
+
if (config.checks[check2.id] === "off")
|
|
1645
1584
|
return false;
|
|
1646
1585
|
if (check2.enabledWhen && !check2.enabledWhen(config))
|
|
1647
1586
|
return false;
|
|
@@ -2384,7 +2323,7 @@ var check9 = {
|
|
|
2384
2323
|
severity: "warning",
|
|
2385
2324
|
message: `Dark four-colour text at ${LARGE_TEXT_PT}pt or smaller on page(s) ${found.pages.join(", ")}.`,
|
|
2386
2325
|
file: ctx.pdfPath,
|
|
2387
|
-
detail: "Small text printed with all four inks can look fuzzy if the plates are slightly out of register, which is why print vendors such as DriveThruRPG want small black text on the black plate alone. `pdfx.blackText: k-only` already does that for near-black text; this text is either a dark colour (for example a dark brown, which keeps its colour) or painted in a way the build cannot rewrite (an image or a pattern). To print it on the black plate, make the text black (#000). If the colour is intended, set `
|
|
2326
|
+
detail: "Small text printed with all four inks can look fuzzy if the plates are slightly out of register, which is why print vendors such as DriveThruRPG want small black text on the black plate alone. `pdfx.blackText: k-only` already does that for near-black text; this text is either a dark colour (for example a dark brown, which keeps its colour) or painted in a way the build cannot rewrite (an image or a pattern). To print it on the black plate, make the text black (#000). If the colour is intended, set `checks: { pdf.print.black-text: off }` in manifest.yaml.",
|
|
2388
2327
|
code: "rich-black-text",
|
|
2389
2328
|
data: { runs: found.runs, pages: found.pages }
|
|
2390
2329
|
})
|
|
@@ -2464,8 +2403,6 @@ var check11 = {
|
|
|
2464
2403
|
async run(ctx) {
|
|
2465
2404
|
if (!ctx.pdfPath)
|
|
2466
2405
|
return [];
|
|
2467
|
-
if (!ctx.config.validate.pdf.requireBookmarks)
|
|
2468
|
-
return [];
|
|
2469
2406
|
const doc = await loadPdf(ctx.pdfPath);
|
|
2470
2407
|
if (!doc) {
|
|
2471
2408
|
return [
|
|
@@ -2498,8 +2435,6 @@ var check12 = {
|
|
|
2498
2435
|
async run(ctx) {
|
|
2499
2436
|
if (!ctx.pdfPath)
|
|
2500
2437
|
return [];
|
|
2501
|
-
if (!ctx.config.validate.pdf.requireTocLinks)
|
|
2502
|
-
return [];
|
|
2503
2438
|
const doc = await loadPdf(ctx.pdfPath);
|
|
2504
2439
|
if (!doc) {
|
|
2505
2440
|
return [
|
|
@@ -2637,8 +2572,6 @@ var check16 = {
|
|
|
2637
2572
|
async run(ctx) {
|
|
2638
2573
|
if (!ctx.pdfPath)
|
|
2639
2574
|
return [];
|
|
2640
|
-
if (!ctx.config.validate.pdf.forbidTransparency)
|
|
2641
|
-
return [];
|
|
2642
2575
|
const bytes = await readPdfBytes(ctx.pdfPath).catch(() => "");
|
|
2643
2576
|
const found = [];
|
|
2644
2577
|
if (bytes.includes("/Transparency"))
|
|
@@ -2679,8 +2612,6 @@ var check17 = {
|
|
|
2679
2612
|
async run(ctx) {
|
|
2680
2613
|
if (!ctx.pdfPath)
|
|
2681
2614
|
return [];
|
|
2682
|
-
if (!ctx.config.validate.pdf.requireBleed)
|
|
2683
|
-
return [];
|
|
2684
2615
|
const bytes = await readPdfBytes(ctx.pdfPath).catch(() => "");
|
|
2685
2616
|
if (!bytes) {
|
|
2686
2617
|
return [
|
|
@@ -2777,14 +2708,10 @@ var check19 = {
|
|
|
2777
2708
|
category: "source",
|
|
2778
2709
|
phase: "pre-build",
|
|
2779
2710
|
async run(ctx) {
|
|
2780
|
-
const sourceConfig = ctx.config.validate.source;
|
|
2781
|
-
if (sourceConfig.htmlhint === false)
|
|
2782
|
-
return [];
|
|
2783
2711
|
if (!ctx.htmlPath || !existsSync4(ctx.htmlPath))
|
|
2784
2712
|
return [];
|
|
2785
|
-
const
|
|
2786
|
-
|
|
2787
|
-
if (!resolvedConfig && !configPath)
|
|
2713
|
+
const resolvedConfig = findConfigFile(ctx.inputDir, CONFIG_NAMES2);
|
|
2714
|
+
if (!resolvedConfig)
|
|
2788
2715
|
return [];
|
|
2789
2716
|
const ruleset = resolvedConfig ? JSON.parse(await readFile10(resolvedConfig, "utf8")) : HTMLHint.defaultRuleset;
|
|
2790
2717
|
const html = await readFile10(ctx.htmlPath, "utf8");
|
|
@@ -2812,7 +2739,7 @@ function ruleLevel(rules, id) {
|
|
|
2812
2739
|
const v = rules?.[id];
|
|
2813
2740
|
if (v === "off" || v === false)
|
|
2814
2741
|
return "off";
|
|
2815
|
-
if (v === "warn" || v === "warning")
|
|
2742
|
+
if (v === "warn" || v === "warning" || v === "info")
|
|
2816
2743
|
return "warn";
|
|
2817
2744
|
if (v === "error")
|
|
2818
2745
|
return "error";
|
|
@@ -2833,6 +2760,7 @@ var ruleRiskyProps = "printsafe/no-risky-print-effects";
|
|
|
2833
2760
|
var ruleSyntax = "printsafe/syntax-error";
|
|
2834
2761
|
var rulePageContainment = "printsafe/page-containment";
|
|
2835
2762
|
var rulePreviewCannotFollow = "printsafe/preview-cannot-follow";
|
|
2763
|
+
var rulePageSizeInStylesheet = "printsafe/page-size-in-stylesheet";
|
|
2836
2764
|
function extractUrls(value) {
|
|
2837
2765
|
const urls = [];
|
|
2838
2766
|
const re = /url\(\s*(['"]?)(.*?)\1\s*\)/gi;
|
|
@@ -2881,6 +2809,12 @@ function isInPageBox(decl) {
|
|
|
2881
2809
|
const page = decl.parent;
|
|
2882
2810
|
return !!page && page.type === "atrule" && page.name.toLowerCase() === "page";
|
|
2883
2811
|
}
|
|
2812
|
+
function isInBookPageRule(decl) {
|
|
2813
|
+
if (!isInPageBox(decl))
|
|
2814
|
+
return false;
|
|
2815
|
+
const params = decl.parent.params.trim();
|
|
2816
|
+
return params === "" || /^(:[a-z-]+\s*)+$/i.test(params);
|
|
2817
|
+
}
|
|
2884
2818
|
function isDroppedPageGradient(decl) {
|
|
2885
2819
|
const prop = decl.prop.toLowerCase();
|
|
2886
2820
|
if (prop !== "background" && prop !== "background-image")
|
|
@@ -3120,6 +3054,15 @@ function checkCssRaw(css, from) {
|
|
|
3120
3054
|
root.walkAtRules((at) => reportRemoteUrls(at.params || "", at));
|
|
3121
3055
|
root.walkDecls((decl) => {
|
|
3122
3056
|
const prop = decl.prop.toLowerCase();
|
|
3057
|
+
if (prop === "size" && isInBookPageRule(decl)) {
|
|
3058
|
+
warnings.push({
|
|
3059
|
+
rule: rulePageSizeInStylesheet,
|
|
3060
|
+
severity: "warning",
|
|
3061
|
+
message: `"size" in a stylesheet's @page rule is ignored: the page size comes from the manifest (preset:, or page: width and height — Setup > Book in the app), which is written after every stylesheet. Remove this declaration; to print at another size, change the manifest.`,
|
|
3062
|
+
...nodeLoc(decl)
|
|
3063
|
+
});
|
|
3064
|
+
return;
|
|
3065
|
+
}
|
|
3123
3066
|
if (isInertValue(prop, decl.value))
|
|
3124
3067
|
return;
|
|
3125
3068
|
if (marginBoxIgnoredProperties.has(prop) && isInPageMarginBox(decl)) {
|
|
@@ -3215,10 +3158,7 @@ var check20 = {
|
|
|
3215
3158
|
description: "Checks CSS for print-safety issues (remote URLs, rasterizing effects, and page-layer containment)",
|
|
3216
3159
|
category: "source",
|
|
3217
3160
|
phase: "pre-build",
|
|
3218
|
-
enabledWhen: (config) => config.validate.source.stylelint !== false,
|
|
3219
3161
|
async run(ctx) {
|
|
3220
|
-
if (ctx.config.validate.source.stylelint === false)
|
|
3221
|
-
return [];
|
|
3222
3162
|
const files = ctx.cssFiles;
|
|
3223
3163
|
if (!files || files.length === 0)
|
|
3224
3164
|
return [];
|
|
@@ -3236,7 +3176,7 @@ var check20 = {
|
|
|
3236
3176
|
});
|
|
3237
3177
|
continue;
|
|
3238
3178
|
}
|
|
3239
|
-
for (const w of checkCss(css, file, { rules: ctx.config.
|
|
3179
|
+
for (const w of checkCss(css, file, { rules: ctx.config.checks })) {
|
|
3240
3180
|
results.push({
|
|
3241
3181
|
checkId: check20.id,
|
|
3242
3182
|
severity: w.severity,
|
|
@@ -3377,15 +3317,11 @@ var check21 = {
|
|
|
3377
3317
|
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.",
|
|
3378
3318
|
category: "source",
|
|
3379
3319
|
phase: "pre-build",
|
|
3380
|
-
enabledWhen: (config) => config.validate.source.cssOwnership !== false,
|
|
3381
3320
|
async run(ctx) {
|
|
3382
|
-
if (ctx.config.validate.source.cssOwnership === false)
|
|
3383
|
-
return [];
|
|
3384
3321
|
const files = ctx.cssFiles;
|
|
3385
3322
|
if (!files || files.length === 0)
|
|
3386
3323
|
return [];
|
|
3387
|
-
const
|
|
3388
|
-
const configPath = findConfigFile(ctx.inputDir, CONFIG_NAMES3, explicit);
|
|
3324
|
+
const configPath = findConfigFile(ctx.inputDir, CONFIG_NAMES3);
|
|
3389
3325
|
if (!configPath)
|
|
3390
3326
|
return [];
|
|
3391
3327
|
let contract;
|
|
@@ -3452,7 +3388,7 @@ registerCheck(check21);
|
|
|
3452
3388
|
|
|
3453
3389
|
// src/checks/source/local-refs.ts
|
|
3454
3390
|
import { existsSync as existsSync5 } from "node:fs";
|
|
3455
|
-
import { dirname, resolve as resolve3 } from "node:path";
|
|
3391
|
+
import { dirname as dirname2, resolve as resolve3 } from "node:path";
|
|
3456
3392
|
var check22 = {
|
|
3457
3393
|
id: "source.links.local-refs",
|
|
3458
3394
|
name: "Local Markdown References",
|
|
@@ -3492,7 +3428,7 @@ function filesystemRef(ref) {
|
|
|
3492
3428
|
}
|
|
3493
3429
|
function localRefExists(ref, kind, sourceFile, projectRoot) {
|
|
3494
3430
|
const cleaned = filesystemRef(ref);
|
|
3495
|
-
const fileRelative = resolve3(
|
|
3431
|
+
const fileRelative = resolve3(dirname2(sourceFile), cleaned);
|
|
3496
3432
|
const rootRelative = resolve3(projectRoot, cleaned);
|
|
3497
3433
|
switch (kind) {
|
|
3498
3434
|
case "image":
|
|
@@ -4568,8 +4504,6 @@ var check30 = {
|
|
|
4568
4504
|
category: "asset",
|
|
4569
4505
|
phase: "pre-build",
|
|
4570
4506
|
async run(ctx) {
|
|
4571
|
-
if (ctx.config.validate.assets.allowAlpha)
|
|
4572
|
-
return [];
|
|
4573
4507
|
if (ctx.config.pdfx.transparency === "preserve-text")
|
|
4574
4508
|
return [];
|
|
4575
4509
|
const files = shippedImages(ctx, ["png", "tiff", "tif"]);
|
|
@@ -4592,58 +4526,6 @@ var check30 = {
|
|
|
4592
4526
|
};
|
|
4593
4527
|
registerCheck(check30);
|
|
4594
4528
|
|
|
4595
|
-
// src/checks/asset/approved-fonts.ts
|
|
4596
|
-
var check31 = {
|
|
4597
|
-
id: "asset.font.approved-files",
|
|
4598
|
-
name: "Approved Font Files",
|
|
4599
|
-
description: "Checks font files against the approved file patterns",
|
|
4600
|
-
category: "asset",
|
|
4601
|
-
phase: "pre-build",
|
|
4602
|
-
async run(ctx) {
|
|
4603
|
-
const patterns = ctx.config.validate.assets.approvedFontFiles;
|
|
4604
|
-
if (!patterns || patterns.length === 0)
|
|
4605
|
-
return [];
|
|
4606
|
-
const { glob } = await import("glob");
|
|
4607
|
-
const dirs = ctx.assetDirs ?? [ctx.inputDir];
|
|
4608
|
-
const allFonts = [];
|
|
4609
|
-
for (const dir of dirs) {
|
|
4610
|
-
const matches = await glob(`**/*.{${FONT_EXTS2.join(",")}}`, {
|
|
4611
|
-
cwd: dir,
|
|
4612
|
-
absolute: true,
|
|
4613
|
-
ignore: [...ASSET_SCAN_IGNORE_GLOBS]
|
|
4614
|
-
});
|
|
4615
|
-
allFonts.push(...matches);
|
|
4616
|
-
}
|
|
4617
|
-
if (allFonts.length === 0)
|
|
4618
|
-
return [];
|
|
4619
|
-
const approvedFonts = new Set;
|
|
4620
|
-
for (const dir of dirs) {
|
|
4621
|
-
for (const pattern of patterns) {
|
|
4622
|
-
const matches = await glob(pattern, {
|
|
4623
|
-
cwd: dir,
|
|
4624
|
-
absolute: true,
|
|
4625
|
-
ignore: [...ASSET_SCAN_IGNORE_GLOBS]
|
|
4626
|
-
});
|
|
4627
|
-
for (const m of matches)
|
|
4628
|
-
approvedFonts.add(m);
|
|
4629
|
-
}
|
|
4630
|
-
}
|
|
4631
|
-
const results = [];
|
|
4632
|
-
for (const font of allFonts) {
|
|
4633
|
-
if (!approvedFonts.has(font)) {
|
|
4634
|
-
results.push({
|
|
4635
|
-
checkId: check31.id,
|
|
4636
|
-
severity: "warning",
|
|
4637
|
-
message: "Font file not in approved list",
|
|
4638
|
-
file: font
|
|
4639
|
-
});
|
|
4640
|
-
}
|
|
4641
|
-
}
|
|
4642
|
-
return results;
|
|
4643
|
-
}
|
|
4644
|
-
};
|
|
4645
|
-
registerCheck(check31);
|
|
4646
|
-
|
|
4647
4529
|
// src/checks/asset/font-license.ts
|
|
4648
4530
|
import { existsSync as existsSync6 } from "node:fs";
|
|
4649
4531
|
import { resolve as resolve4 } from "node:path";
|
|
@@ -4657,15 +4539,13 @@ var LICENSE_NAMES = [
|
|
|
4657
4539
|
"OFL-1.1.txt",
|
|
4658
4540
|
"COPYING"
|
|
4659
4541
|
];
|
|
4660
|
-
var
|
|
4542
|
+
var check31 = {
|
|
4661
4543
|
id: "asset.font.license",
|
|
4662
4544
|
name: "Font License",
|
|
4663
4545
|
description: "Checks for license files in font directories",
|
|
4664
4546
|
category: "asset",
|
|
4665
4547
|
phase: "pre-build",
|
|
4666
4548
|
async run(ctx) {
|
|
4667
|
-
if (!ctx.config.validate.assets.requireFontLicense)
|
|
4668
|
-
return [];
|
|
4669
4549
|
const { glob } = await import("glob");
|
|
4670
4550
|
const dirs = ctx.assetDirs ?? [ctx.inputDir];
|
|
4671
4551
|
const fontDirs = new Set;
|
|
@@ -4684,7 +4564,7 @@ var check32 = {
|
|
|
4684
4564
|
const hasLicense = LICENSE_NAMES.some((name) => existsSync6(resolve4(fontDir, name)));
|
|
4685
4565
|
if (!hasLicense) {
|
|
4686
4566
|
results.push({
|
|
4687
|
-
checkId:
|
|
4567
|
+
checkId: check31.id,
|
|
4688
4568
|
severity: "warning",
|
|
4689
4569
|
message: `No font license file found in directory`,
|
|
4690
4570
|
file: fontDir
|
|
@@ -4694,10 +4574,10 @@ var check32 = {
|
|
|
4694
4574
|
return results;
|
|
4695
4575
|
}
|
|
4696
4576
|
};
|
|
4697
|
-
registerCheck(
|
|
4577
|
+
registerCheck(check31);
|
|
4698
4578
|
|
|
4699
4579
|
// src/checks/heuristic/text-density.ts
|
|
4700
|
-
var
|
|
4580
|
+
var check32 = {
|
|
4701
4581
|
id: "heuristic.whitespace.text-density",
|
|
4702
4582
|
name: "Text Density",
|
|
4703
4583
|
description: "Checks characters-per-page ratio",
|
|
@@ -4726,7 +4606,7 @@ var check33 = {
|
|
|
4726
4606
|
});
|
|
4727
4607
|
if (lowPages.length > 0) {
|
|
4728
4608
|
results.push({
|
|
4729
|
-
checkId:
|
|
4609
|
+
checkId: check32.id,
|
|
4730
4610
|
severity: "info",
|
|
4731
4611
|
message: `Low text density on pages: ${lowPages.join(", ")} (below ${range.min} chars)`,
|
|
4732
4612
|
file: ctx.pdfPath
|
|
@@ -4734,7 +4614,7 @@ var check33 = {
|
|
|
4734
4614
|
}
|
|
4735
4615
|
if (highPages.length > 0) {
|
|
4736
4616
|
results.push({
|
|
4737
|
-
checkId:
|
|
4617
|
+
checkId: check32.id,
|
|
4738
4618
|
severity: "info",
|
|
4739
4619
|
message: `High text density on pages: ${highPages.join(", ")} (above ${range.max} chars)`,
|
|
4740
4620
|
file: ctx.pdfPath
|
|
@@ -4743,11 +4623,11 @@ var check33 = {
|
|
|
4743
4623
|
return results;
|
|
4744
4624
|
}
|
|
4745
4625
|
};
|
|
4746
|
-
registerCheck(
|
|
4626
|
+
registerCheck(check32);
|
|
4747
4627
|
|
|
4748
4628
|
// src/checks/heuristic/section-density.ts
|
|
4749
4629
|
import { readFile as readFile19 } from "node:fs/promises";
|
|
4750
|
-
var
|
|
4630
|
+
var check33 = {
|
|
4751
4631
|
id: "heuristic.chunking.section-density",
|
|
4752
4632
|
name: "Section Density",
|
|
4753
4633
|
description: "Checks heading/paragraph/callout density from source Markdown",
|
|
@@ -4773,7 +4653,7 @@ var check34 = {
|
|
|
4773
4653
|
const line = lines[i];
|
|
4774
4654
|
if (/^#{1,6}\s/.test(line)) {
|
|
4775
4655
|
if (paragraphCount > maxParas) {
|
|
4776
|
-
results.push(finding(
|
|
4656
|
+
results.push(finding(check33.id, {
|
|
4777
4657
|
severity: "info",
|
|
4778
4658
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
4779
4659
|
file,
|
|
@@ -4793,7 +4673,7 @@ var check34 = {
|
|
|
4793
4673
|
}
|
|
4794
4674
|
}
|
|
4795
4675
|
if (paragraphCount > maxParas) {
|
|
4796
|
-
results.push(finding(
|
|
4676
|
+
results.push(finding(check33.id, {
|
|
4797
4677
|
severity: "info",
|
|
4798
4678
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
4799
4679
|
file,
|
|
@@ -4801,7 +4681,7 @@ var check34 = {
|
|
|
4801
4681
|
}));
|
|
4802
4682
|
}
|
|
4803
4683
|
} catch {
|
|
4804
|
-
results.push(inspectionFailed(
|
|
4684
|
+
results.push(inspectionFailed(check33.id, `Could not read source file: ${file}`, {
|
|
4805
4685
|
file
|
|
4806
4686
|
}));
|
|
4807
4687
|
}
|
|
@@ -4809,10 +4689,10 @@ var check34 = {
|
|
|
4809
4689
|
return results;
|
|
4810
4690
|
}
|
|
4811
4691
|
};
|
|
4812
|
-
registerCheck(
|
|
4692
|
+
registerCheck(check33);
|
|
4813
4693
|
|
|
4814
4694
|
// src/checks/heuristic/layer-count.ts
|
|
4815
|
-
var
|
|
4695
|
+
var check34 = {
|
|
4816
4696
|
id: "heuristic.decoration.layer-count",
|
|
4817
4697
|
name: "Layer Count",
|
|
4818
4698
|
description: "Counts image objects per page",
|
|
@@ -4837,7 +4717,7 @@ var check35 = {
|
|
|
4837
4717
|
heavyPages.sort((a, b) => a - b);
|
|
4838
4718
|
return [
|
|
4839
4719
|
{
|
|
4840
|
-
checkId:
|
|
4720
|
+
checkId: check34.id,
|
|
4841
4721
|
severity: "info",
|
|
4842
4722
|
message: `Pages with many image layers (>${maxLayers}): ${heavyPages.join(", ")}`,
|
|
4843
4723
|
file: ctx.pdfPath
|
|
@@ -4847,10 +4727,10 @@ var check35 = {
|
|
|
4847
4727
|
return [];
|
|
4848
4728
|
}
|
|
4849
4729
|
};
|
|
4850
|
-
registerCheck(
|
|
4730
|
+
registerCheck(check34);
|
|
4851
4731
|
|
|
4852
4732
|
// src/checks/heuristic/placement-variance.ts
|
|
4853
|
-
var
|
|
4733
|
+
var check35 = {
|
|
4854
4734
|
id: "heuristic.layout.placement-variance",
|
|
4855
4735
|
name: "Placement Variance",
|
|
4856
4736
|
description: "Analyzes text baseline coordinates for layout consistency",
|
|
@@ -4868,7 +4748,7 @@ var check36 = {
|
|
|
4868
4748
|
const uniqueX = new Set(positions.map((p) => Math.round(p.x)));
|
|
4869
4749
|
return [
|
|
4870
4750
|
{
|
|
4871
|
-
checkId:
|
|
4751
|
+
checkId: check35.id,
|
|
4872
4752
|
severity: "info",
|
|
4873
4753
|
message: `Layout analysis: ${uniqueX.size} unique horizontal text positions across ${positions.length} text blocks.`,
|
|
4874
4754
|
file: ctx.pdfPath
|
|
@@ -4876,19 +4756,14 @@ var check36 = {
|
|
|
4876
4756
|
];
|
|
4877
4757
|
}
|
|
4878
4758
|
};
|
|
4879
|
-
registerCheck(
|
|
4759
|
+
registerCheck(check35);
|
|
4880
4760
|
|
|
4881
4761
|
// src/checks/runner.ts
|
|
4882
4762
|
function getCheckSeverityOverride(checkId, config) {
|
|
4883
|
-
const
|
|
4884
|
-
|
|
4885
|
-
return entry.severity;
|
|
4886
|
-
return;
|
|
4763
|
+
const level = config.checks[checkId];
|
|
4764
|
+
return level === "off" || level === "on" ? undefined : level;
|
|
4887
4765
|
}
|
|
4888
4766
|
async function runChecks(ctx, opts = {}) {
|
|
4889
|
-
if (ctx.config.validate.enabled === false) {
|
|
4890
|
-
return emptyReport();
|
|
4891
|
-
}
|
|
4892
4767
|
const { checks: selected, unmatched: unmatchedSelectors } = selectChecks(opts, ctx.config);
|
|
4893
4768
|
let checks2 = selected;
|
|
4894
4769
|
if (opts.skipMissingTools && opts.skipMissingTools.length > 0) {
|
|
@@ -4906,32 +4781,25 @@ async function runChecks(ctx, opts = {}) {
|
|
|
4906
4781
|
}
|
|
4907
4782
|
const release = retainPdfCache();
|
|
4908
4783
|
try {
|
|
4909
|
-
for (const
|
|
4784
|
+
for (const check36 of checks2) {
|
|
4910
4785
|
try {
|
|
4911
|
-
|
|
4912
|
-
const severityOverride = getCheckSeverityOverride(
|
|
4786
|
+
const results = await check36.run(ctx);
|
|
4787
|
+
const severityOverride = getCheckSeverityOverride(check36.id, ctx.config);
|
|
4913
4788
|
if (severityOverride) {
|
|
4914
4789
|
for (const r of results) {
|
|
4915
4790
|
r.severity = severityOverride;
|
|
4916
4791
|
}
|
|
4917
4792
|
}
|
|
4918
|
-
const lintLevel = ruleLevel(ctx.config.lint.rules, check37.id);
|
|
4919
|
-
if (lintLevel === "off")
|
|
4920
|
-
results = [];
|
|
4921
|
-
else if (lintLevel) {
|
|
4922
|
-
for (const r of results)
|
|
4923
|
-
r.severity = lintLevel === "warn" ? "warning" : "error";
|
|
4924
|
-
}
|
|
4925
4793
|
if (results.length === 0) {
|
|
4926
|
-
passed.push(
|
|
4794
|
+
passed.push(check36.id);
|
|
4927
4795
|
} else {
|
|
4928
4796
|
allResults.push(...results);
|
|
4929
4797
|
}
|
|
4930
4798
|
} catch (err) {
|
|
4931
4799
|
allResults.push({
|
|
4932
|
-
checkId:
|
|
4800
|
+
checkId: check36.id,
|
|
4933
4801
|
severity: "error",
|
|
4934
|
-
message: `Check "${
|
|
4802
|
+
message: `Check "${check36.id}" threw: ${err instanceof Error ? err.message : String(err)}`
|
|
4935
4803
|
});
|
|
4936
4804
|
}
|
|
4937
4805
|
}
|
|
@@ -4956,30 +4824,20 @@ async function runChecks(ctx, opts = {}) {
|
|
|
4956
4824
|
}
|
|
4957
4825
|
};
|
|
4958
4826
|
}
|
|
4959
|
-
function emptyReport() {
|
|
4960
|
-
return {
|
|
4961
|
-
results: [],
|
|
4962
|
-
errors: [],
|
|
4963
|
-
warnings: [],
|
|
4964
|
-
infos: [],
|
|
4965
|
-
passed: [],
|
|
4966
|
-
summary: { total: 0, errors: 0, warnings: 0, infos: 0, passed: 0 }
|
|
4967
|
-
};
|
|
4968
|
-
}
|
|
4969
4827
|
|
|
4970
4828
|
// src/checks/tool-check.ts
|
|
4971
4829
|
async function checkToolAvailability(config, opts = {}) {
|
|
4972
4830
|
const { checks: checks2 } = selectChecks(opts, config);
|
|
4973
4831
|
const toolToChecks = new Map;
|
|
4974
|
-
for (const
|
|
4975
|
-
if (!
|
|
4832
|
+
for (const check36 of checks2) {
|
|
4833
|
+
if (!check36.requiredTools?.length)
|
|
4976
4834
|
continue;
|
|
4977
|
-
for (const tool of
|
|
4835
|
+
for (const tool of check36.requiredTools) {
|
|
4978
4836
|
const existing = toolToChecks.get(tool);
|
|
4979
4837
|
if (existing) {
|
|
4980
|
-
existing.push(
|
|
4838
|
+
existing.push(check36.id);
|
|
4981
4839
|
} else {
|
|
4982
|
-
toolToChecks.set(tool, [
|
|
4840
|
+
toolToChecks.set(tool, [check36.id]);
|
|
4983
4841
|
}
|
|
4984
4842
|
}
|
|
4985
4843
|
}
|
|
@@ -4995,11 +4853,11 @@ async function checkToolAvailability(config, opts = {}) {
|
|
|
4995
4853
|
const missing = results.filter((r) => !r.found).map((r) => r.tool);
|
|
4996
4854
|
const missingSet = new Set(missing);
|
|
4997
4855
|
const skippedChecks = [];
|
|
4998
|
-
for (const
|
|
4999
|
-
if (!
|
|
4856
|
+
for (const check36 of checks2) {
|
|
4857
|
+
if (!check36.requiredTools?.length)
|
|
5000
4858
|
continue;
|
|
5001
|
-
if (
|
|
5002
|
-
skippedChecks.push(
|
|
4859
|
+
if (check36.requiredTools.some((t) => missingSet.has(t))) {
|
|
4860
|
+
skippedChecks.push(check36.id);
|
|
5003
4861
|
}
|
|
5004
4862
|
}
|
|
5005
4863
|
return { available, missing, skippedChecks, toolToChecks };
|
|
@@ -5070,7 +4928,7 @@ async function sharedAssetDirs(manifestDir, relStyles, inputDir) {
|
|
|
5070
4928
|
const ext = file.slice(file.lastIndexOf(".")).toLowerCase();
|
|
5071
4929
|
if (!SHIPPED_ASSET_EXTS.has(ext))
|
|
5072
4930
|
continue;
|
|
5073
|
-
const dir =
|
|
4931
|
+
const dir = dirname3(resolve5(file));
|
|
5074
4932
|
if (escapesProjectRoot(projectRoot, dir))
|
|
5075
4933
|
dirs.add(dir);
|
|
5076
4934
|
}
|
|
@@ -5231,7 +5089,7 @@ function planRuns(opts) {
|
|
|
5231
5089
|
async function executeValidation(args) {
|
|
5232
5090
|
const manifestPath = typeof args.manifest === "string" ? args.manifest : undefined;
|
|
5233
5091
|
const { manifest, manifestDir } = await loadManifestWithPath(manifestPath ?? args.input ?? undefined, { explicit: manifestPath !== undefined });
|
|
5234
|
-
const config = resolveConfig(args.skipStylelint ? {
|
|
5092
|
+
const config = resolveConfig(args.skipStylelint ? { checks: { "source.stylelint": "off" } } : {}, manifest);
|
|
5235
5093
|
const targetIds = resolveTargets(parseCsv(typeof args.target === "string" ? args.target : undefined), config.targets);
|
|
5236
5094
|
const pdfPath = typeof args.pdf === "string" ? resolve5(args.pdf) : undefined;
|
|
5237
5095
|
const inputDir = typeof args.input === "string" ? resolve5(args.input) : undefined;
|
|
@@ -5264,7 +5122,7 @@ async function executeValidation(args) {
|
|
|
5264
5122
|
let htmlPath;
|
|
5265
5123
|
const outDir = resolveOutputDir(manifestDir, config.title);
|
|
5266
5124
|
if (inputDir) {
|
|
5267
|
-
const relMarkdown = await resolveActiveMarkdownFiles(manifestDir, config.
|
|
5125
|
+
const relMarkdown = await resolveActiveMarkdownFiles(manifestDir, config.chapters);
|
|
5268
5126
|
markdownFiles = relMarkdown.map((f) => join3(manifestDir, canonicalChapterId(f)));
|
|
5269
5127
|
const relStyles = await resolveActiveStyles(manifestDir, config.styles);
|
|
5270
5128
|
const projectCssFiles = relStyles.map((rel) => resolve5(manifestDir, rel));
|
|
@@ -5300,7 +5158,7 @@ async function executeValidation(args) {
|
|
|
5300
5158
|
};
|
|
5301
5159
|
if (args.fix) {
|
|
5302
5160
|
const selected = selectChecks(runnerOptions, config).checks;
|
|
5303
|
-
if (selected.some((
|
|
5161
|
+
if (selected.some((check36) => check36.id === MARKDOWNLINT_CHECK_ID)) {
|
|
5304
5162
|
await applyMarkdownlintFixes(context);
|
|
5305
5163
|
} else {
|
|
5306
5164
|
log.warn(`--fix: ${MARKDOWNLINT_CHECK_ID} is not part of this run — no files were fixed.`);
|
|
@@ -5403,16 +5261,16 @@ ${list}
|
|
|
5403
5261
|
|
|
5404
5262
|
Install the missing tools and re-run, or set GHOSTSCRIPT_PATH, CHROMIUM_PATH, or system PATH so gutterpress can find them. See the User Guide Chapter 7 (System Setup) at examples/gutterpress-user-guide/07-system-setup.md for the full per-feature matrix.`, 2);
|
|
5405
5263
|
}
|
|
5406
|
-
function computeGates(format, opts
|
|
5264
|
+
function computeGates(format, opts) {
|
|
5407
5265
|
if (format === "html") {
|
|
5408
5266
|
if (opts.skipLint || opts.skipPreValidate || opts.skipPostValidate) {
|
|
5409
5267
|
log.info("Validation/lint flags ignored for --format html (no validation phases apply)");
|
|
5410
5268
|
}
|
|
5411
5269
|
return { skipStylelint: false, preValidate: false, postValidate: false };
|
|
5412
5270
|
}
|
|
5413
|
-
const skipStylelint = !!opts.skipLint
|
|
5414
|
-
const preValidate = !opts.skipPreValidate
|
|
5415
|
-
const postValidate = format === "pdfx" && !opts.skipPostValidate
|
|
5271
|
+
const skipStylelint = !!opts.skipLint;
|
|
5272
|
+
const preValidate = !opts.skipPreValidate;
|
|
5273
|
+
const postValidate = format === "pdfx" && !opts.skipPostValidate;
|
|
5416
5274
|
return { skipStylelint, preValidate, postValidate };
|
|
5417
5275
|
}
|
|
5418
5276
|
|
|
@@ -5448,7 +5306,7 @@ async function resolveBuildContext(opts) {
|
|
|
5448
5306
|
}, manifest);
|
|
5449
5307
|
const target = opts.pdfFileOverride ? { kind: "file", file: path7.resolve(opts.pdfFileOverride) } : opts.outDir ? { kind: "directory", dir: opts.outDir } : { kind: "project", dir: resolveOutputDir(manifestDir, config.title) };
|
|
5450
5308
|
const outDir = target.kind === "file" ? path7.dirname(target.file) : target.dir;
|
|
5451
|
-
const gates = computeGates(format, opts
|
|
5309
|
+
const gates = computeGates(format, opts);
|
|
5452
5310
|
const workDir = target.kind === "project" ? path7.join(path7.dirname(target.dir), `.${path7.basename(target.dir)}-build-${randomBytes(6).toString("hex")}`) : path7.join(os2.tmpdir(), `gutterpress-build-${randomBytes(6).toString("hex")}`);
|
|
5453
5311
|
return {
|
|
5454
5312
|
opts,
|
|
@@ -5504,8 +5362,8 @@ async function runQualityGates(ctx) {
|
|
|
5504
5362
|
}
|
|
5505
5363
|
async function renderBook(ctx) {
|
|
5506
5364
|
const { config, gates, renderDir, workDir, opts, format } = ctx;
|
|
5507
|
-
if (config.
|
|
5508
|
-
log.info(`Using specified files (${config.
|
|
5365
|
+
if (config.chapters && config.chapters.length > 0) {
|
|
5366
|
+
log.info(`Using specified files (${config.chapters.length} total)`);
|
|
5509
5367
|
} else {
|
|
5510
5368
|
log.info("Using all .md files in alphabetical order");
|
|
5511
5369
|
}
|
|
@@ -5518,7 +5376,7 @@ async function renderBook(ctx) {
|
|
|
5518
5376
|
print: config.print,
|
|
5519
5377
|
printArea: { ...config.page, preset: config.preset, spec: config.spec },
|
|
5520
5378
|
styles: config.styles,
|
|
5521
|
-
files: config.
|
|
5379
|
+
files: config.chapters,
|
|
5522
5380
|
plugins,
|
|
5523
5381
|
pluginStyles,
|
|
5524
5382
|
onChapterWarnings: (file, warnings) => {
|
|
@@ -5669,7 +5527,7 @@ class PdfOutput {
|
|
|
5669
5527
|
const rawPdf = pdfxMode ? path7.join(stage, "raw.pdf") : path7.resolve(pdfFile);
|
|
5670
5528
|
await fsp2.mkdir(path7.dirname(path7.resolve(pdfFile)), { recursive: true });
|
|
5671
5529
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
5672
|
-
const { buildNativePdf } = await import("./engine-
|
|
5530
|
+
const { buildNativePdf } = await import("./engine-6txhxmmv.js");
|
|
5673
5531
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
5674
5532
|
title: config.title,
|
|
5675
5533
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
@@ -5706,7 +5564,7 @@ class PdfOutput {
|
|
|
5706
5564
|
}
|
|
5707
5565
|
const flattenPages = config.pdfx.flattenPages;
|
|
5708
5566
|
if (transparent.length > 0 && config.pdfx.transparency === "preserve-text") {
|
|
5709
|
-
const { unsplittableTextReasons } = await import("./pdf-inspect-
|
|
5567
|
+
const { unsplittableTextReasons } = await import("./pdf-inspect-b3mf81ex.js");
|
|
5710
5568
|
const reasons = await unsplittableTextReasons(rawBytes, transparent).catch((err) => {
|
|
5711
5569
|
log.warn(`pdfx.transparency: preserve-text could not inspect this PDF's text (${err instanceof Error ? err.message : String(err)}), so every page that uses transparency is flattened whole, as with flatten-page.`);
|
|
5712
5570
|
return null;
|
|
@@ -5979,7 +5837,7 @@ async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets, image
|
|
|
5979
5837
|
...config,
|
|
5980
5838
|
printArea: config.page && { ...config.page, preset: config.preset, spec: config.spec }
|
|
5981
5839
|
}, {
|
|
5982
|
-
files: config.
|
|
5840
|
+
files: config.chapters ?? null,
|
|
5983
5841
|
onCssAssets: (copies) => {
|
|
5984
5842
|
for (const copy of copies)
|
|
5985
5843
|
nextAssets.set(copy.to, copy.from);
|
|
@@ -7206,24 +7064,20 @@ async function getSystemDiagnostics() {
|
|
|
7206
7064
|
};
|
|
7207
7065
|
}
|
|
7208
7066
|
// src/lib/project-scaffold.ts
|
|
7209
|
-
import { access, copyFile, cp, mkdir as
|
|
7067
|
+
import { access, copyFile, cp, mkdir as mkdir5, readdir as readdir6, readFile as readFile25, stat as stat4, writeFile as writeFile7 } from "node:fs/promises";
|
|
7210
7068
|
import { constants as FS, existsSync as existsSync9 } from "node:fs";
|
|
7211
7069
|
import path14 from "node:path";
|
|
7212
7070
|
|
|
7213
7071
|
// src/lib/page-size.ts
|
|
7214
7072
|
import { existsSync as existsSync8 } from "node:fs";
|
|
7215
|
-
import {
|
|
7073
|
+
import { readFile as readFile24 } from "node:fs/promises";
|
|
7216
7074
|
import path13 from "node:path";
|
|
7217
|
-
import
|
|
7218
|
-
import { isMap, Scalar } from "yaml";
|
|
7075
|
+
import { isMap } from "yaml";
|
|
7219
7076
|
function writePageBounds(doc, page) {
|
|
7220
|
-
|
|
7221
|
-
if (!isMap(doc.get("page", true)))
|
|
7222
|
-
doc.set("page", doc.createNode({}));
|
|
7077
|
+
ensurePageMap(doc);
|
|
7223
7078
|
doc.setIn(["page", "width"], page.width);
|
|
7224
7079
|
doc.setIn(["page", "height"], page.height);
|
|
7225
|
-
|
|
7226
|
-
doc.setIn(["page", "tolerance"], tolerance);
|
|
7080
|
+
doc.deleteIn(["page", "tolerance"]);
|
|
7227
7081
|
}
|
|
7228
7082
|
function deletePageBounds(doc) {
|
|
7229
7083
|
const block = doc.get("page", true);
|
|
@@ -7236,6 +7090,25 @@ function deletePageBounds(doc) {
|
|
|
7236
7090
|
if (block.items.length === 0)
|
|
7237
7091
|
doc.delete("page");
|
|
7238
7092
|
}
|
|
7093
|
+
function deletePageMargin(doc) {
|
|
7094
|
+
const block = doc.get("page", true);
|
|
7095
|
+
if (!isMap(block))
|
|
7096
|
+
return;
|
|
7097
|
+
block.delete("margin");
|
|
7098
|
+
if (block.items.length === 0)
|
|
7099
|
+
doc.delete("page");
|
|
7100
|
+
}
|
|
7101
|
+
function writePageMargin(doc, margin) {
|
|
7102
|
+
ensurePageMap(doc);
|
|
7103
|
+
if (!isMap(doc.getIn(["page", "margin"], true)))
|
|
7104
|
+
doc.setIn(["page", "margin"], doc.createNode({}));
|
|
7105
|
+
for (const edge of MARGIN_EDGES)
|
|
7106
|
+
doc.setIn(["page", "margin", edge], margin[edge]);
|
|
7107
|
+
}
|
|
7108
|
+
function ensurePageMap(doc) {
|
|
7109
|
+
if (!isMap(doc.get("page", true)))
|
|
7110
|
+
doc.set("page", doc.createNode({}));
|
|
7111
|
+
}
|
|
7239
7112
|
function numberAt(doc, keyPath) {
|
|
7240
7113
|
const v = doc.getIn(keyPath);
|
|
7241
7114
|
return isPositivePoints(v) ? v : undefined;
|
|
@@ -7254,132 +7127,97 @@ function pageSizeForChoice(choice) {
|
|
|
7254
7127
|
const base = PRESETS[choice.preset].page;
|
|
7255
7128
|
return { width: base.width, height: base.height };
|
|
7256
7129
|
}
|
|
7257
|
-
|
|
7258
|
-
function sameSize(a, b) {
|
|
7259
|
-
return Math.abs(a.width - b.width) < SAME_SIZE_PT && Math.abs(a.height - b.height) < SAME_SIZE_PT;
|
|
7260
|
-
}
|
|
7261
|
-
function plainPageRules(root) {
|
|
7262
|
-
const rules = [];
|
|
7263
|
-
root.walkAtRules("page", (rule) => {
|
|
7264
|
-
if (rule.params.trim() === "")
|
|
7265
|
-
rules.push(rule);
|
|
7266
|
-
});
|
|
7267
|
-
return rules;
|
|
7268
|
-
}
|
|
7269
|
-
function sizeDeclsOf(rule) {
|
|
7270
|
-
const decls = [];
|
|
7271
|
-
rule.each((node) => {
|
|
7272
|
-
if (node.type === "decl" && node.prop.toLowerCase() === "size")
|
|
7273
|
-
decls.push(node);
|
|
7274
|
-
});
|
|
7275
|
-
return decls;
|
|
7276
|
-
}
|
|
7277
|
-
function readPageSizeFromCss(css) {
|
|
7278
|
-
let root;
|
|
7279
|
-
try {
|
|
7280
|
-
root = postcss5.parse(css);
|
|
7281
|
-
} catch {
|
|
7282
|
-
return null;
|
|
7283
|
-
}
|
|
7284
|
-
const decls = plainPageRules(root).flatMap(sizeDeclsOf);
|
|
7285
|
-
const last = decls[decls.length - 1];
|
|
7286
|
-
return last ? parseSize(last.value) ?? null : null;
|
|
7287
|
-
}
|
|
7288
|
-
function setPageSizeInCss(css, page) {
|
|
7289
|
-
const root = postcss5.parse(css);
|
|
7290
|
-
const decls = plainPageRules(root).flatMap(sizeDeclsOf);
|
|
7291
|
-
const last = decls[decls.length - 1];
|
|
7292
|
-
const value = formatPageSize(page);
|
|
7293
|
-
if (last) {
|
|
7294
|
-
const current = parseSize(last.value);
|
|
7295
|
-
if (current && sameSize(current, page))
|
|
7296
|
-
return css;
|
|
7297
|
-
last.value = value;
|
|
7298
|
-
return root.toString();
|
|
7299
|
-
}
|
|
7300
|
-
const rule = `@page {
|
|
7301
|
-
size: ${value};
|
|
7302
|
-
}
|
|
7303
|
-
`;
|
|
7304
|
-
if (css.trim() === "")
|
|
7305
|
-
return css + rule;
|
|
7306
|
-
return `${css}${css.endsWith(`
|
|
7307
|
-
`) ? "" : `
|
|
7308
|
-
`}
|
|
7309
|
-
${rule}`;
|
|
7310
|
-
}
|
|
7311
|
-
async function ownStylesheets(projectDir) {
|
|
7312
|
-
const rels = await resolveActiveStyles(projectDir).catch(() => []);
|
|
7313
|
-
return rels.map((rel) => path13.resolve(projectDir, rel)).filter((abs) => {
|
|
7314
|
-
const inside = path13.relative(projectDir, abs);
|
|
7315
|
-
return !inside.startsWith("..") && !path13.isAbsolute(inside) && existsSync8(abs);
|
|
7316
|
-
});
|
|
7317
|
-
}
|
|
7318
|
-
var relDisplay2 = (projectDir, abs) => path13.relative(projectDir, abs).split(path13.sep).join("/");
|
|
7319
|
-
async function findCssSize(projectDir, sheets) {
|
|
7320
|
-
let found = null;
|
|
7321
|
-
for (const file of sheets) {
|
|
7322
|
-
const size2 = readPageSizeFromCss(await readFile24(file, "utf8"));
|
|
7323
|
-
if (size2)
|
|
7324
|
-
found = { file, size: size2 };
|
|
7325
|
-
}
|
|
7326
|
-
return found;
|
|
7327
|
-
}
|
|
7328
|
-
async function readPageSetup(projectDir) {
|
|
7329
|
-
const { doc } = await loadManifestDoc(projectDir);
|
|
7130
|
+
function manifestGeometry(doc) {
|
|
7330
7131
|
const declared = doc.get("preset");
|
|
7331
7132
|
const preset = declared === undefined || declared === null ? "dtrpg" : PRESET_IDS.includes(String(declared)) ? declared : null;
|
|
7332
7133
|
const base = preset ? PRESETS[preset].page : null;
|
|
7333
7134
|
const width = numberAt(doc, ["page", "width"]) ?? base?.width;
|
|
7334
7135
|
const height = numberAt(doc, ["page", "height"]) ?? base?.height;
|
|
7335
|
-
const tolerance =
|
|
7336
|
-
const
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7136
|
+
const tolerance = base?.tolerance ?? 0.5;
|
|
7137
|
+
const bounds = width !== undefined && height !== undefined ? { width, height, tolerance } : null;
|
|
7138
|
+
const presetSizeOverridden = base !== null && (numberAt(doc, ["page", "width"]) !== undefined || numberAt(doc, ["page", "height"]) !== undefined);
|
|
7139
|
+
const at = (edge) => doc.getIn(["page", "margin", edge]);
|
|
7140
|
+
const margins = MARGIN_EDGES.every((edge) => isNonNegativePoints(at(edge))) ? { top: at("top"), bottom: at("bottom"), inside: at("inside"), outside: at("outside") } : undefined;
|
|
7141
|
+
return { preset, bounds, presetSizeOverridden, ...margins ? { margins } : {} };
|
|
7142
|
+
}
|
|
7143
|
+
async function stylesheetMargins(projectDir, manifestStyles) {
|
|
7144
|
+
const d = DEFAULT_PAGE_MARGIN_PT;
|
|
7145
|
+
const fallback = {
|
|
7146
|
+
margins: { top: d, bottom: d, inside: d, outside: d },
|
|
7147
|
+
marginsFrom: "default"
|
|
7341
7148
|
};
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
const
|
|
7347
|
-
const
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7149
|
+
const [entries, rels] = await Promise.all([
|
|
7150
|
+
listProjectExtensions(projectDir).catch(() => []),
|
|
7151
|
+
resolveActiveStyles(projectDir, manifestStyles).catch(() => [])
|
|
7152
|
+
]);
|
|
7153
|
+
const sheets = rels.map((rel) => path13.resolve(projectDir, rel)).filter((abs) => existsSync8(abs));
|
|
7154
|
+
const parts = await Promise.all([
|
|
7155
|
+
...entries.filter((e) => e.enabled && e.carries.styles).map((e) => readEntryCss(e).catch(() => "")),
|
|
7156
|
+
...sheets.map((file) => readFile24(file, "utf8").catch(() => ""))
|
|
7157
|
+
]);
|
|
7351
7158
|
try {
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7159
|
+
const page = resolvePage(extract(parts.join(`
|
|
7160
|
+
|
|
7161
|
+
`)), { pseudos: ["right"] });
|
|
7162
|
+
const declared = Object.keys(page.decls).some((prop) => prop === "margin" || prop.startsWith("margin-"));
|
|
7163
|
+
if (!declared)
|
|
7164
|
+
return fallback;
|
|
7165
|
+
const m = page.geometry.margin;
|
|
7166
|
+
return {
|
|
7167
|
+
margins: { top: round3(m.top), bottom: round3(m.bottom), inside: round3(m.left), outside: round3(m.right) },
|
|
7168
|
+
marginsFrom: "stylesheet"
|
|
7169
|
+
};
|
|
7170
|
+
} catch {
|
|
7171
|
+
return fallback;
|
|
7355
7172
|
}
|
|
7173
|
+
}
|
|
7174
|
+
async function setupOf(projectDir, doc) {
|
|
7175
|
+
const { margins, ...geometry } = manifestGeometry(doc);
|
|
7176
|
+
const styles = doc.toJS()?.styles;
|
|
7177
|
+
const sheets = await stylesheetMargins(projectDir, Array.isArray(styles) ? styles : undefined);
|
|
7178
|
+
return margins ? { ...geometry, margins, marginsFrom: "manifest", fallbackMarginsFrom: sheets.marginsFrom } : { ...geometry, ...sheets, fallbackMarginsFrom: sheets.marginsFrom };
|
|
7179
|
+
}
|
|
7180
|
+
async function readPageSetup(projectDir) {
|
|
7181
|
+
const { doc } = await loadManifestDoc(projectDir);
|
|
7182
|
+
return setupOf(projectDir, doc);
|
|
7183
|
+
}
|
|
7184
|
+
async function setPageSetup(projectDir, change) {
|
|
7356
7185
|
const { doc, file } = await loadManifestDoc(projectDir);
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7186
|
+
const size2 = change.size ? pageSizeForChoice(change.size) : null;
|
|
7187
|
+
let margins;
|
|
7188
|
+
if (change.margins) {
|
|
7189
|
+
const against = size2 ?? manifestGeometry(doc).bounds;
|
|
7190
|
+
if (!against)
|
|
7191
|
+
throw new UsageError("Choose a page size before setting margins: the manifest records none.");
|
|
7192
|
+
margins = validatePageMargin(change.margins, against);
|
|
7193
|
+
}
|
|
7194
|
+
if (change.size && size2) {
|
|
7195
|
+
doc.set("preset", change.size.preset);
|
|
7196
|
+
if (change.size.preset === "custom")
|
|
7197
|
+
writePageBounds(doc, size2);
|
|
7198
|
+
else
|
|
7199
|
+
deletePageBounds(doc);
|
|
7364
7200
|
}
|
|
7201
|
+
if (margins)
|
|
7202
|
+
writePageMargin(doc, margins);
|
|
7203
|
+
else if (change.margins === null)
|
|
7204
|
+
deletePageMargin(doc);
|
|
7365
7205
|
await writeManifestDoc(file, doc);
|
|
7366
|
-
|
|
7367
|
-
await mkdir5(path13.dirname(target), { recursive: true });
|
|
7368
|
-
await writeFile7(target, after, "utf8");
|
|
7369
|
-
}
|
|
7370
|
-
return readPageSetup(projectDir);
|
|
7206
|
+
return setupOf(projectDir, doc);
|
|
7371
7207
|
}
|
|
7372
7208
|
|
|
7373
7209
|
// src/lib/project-scaffold.ts
|
|
7374
7210
|
function starterOverridesCss() {
|
|
7375
7211
|
return `/**
|
|
7376
|
-
* Your book's own styles.
|
|
7212
|
+
* Your book's own styles. Rules here win over your look's.
|
|
7213
|
+
*
|
|
7214
|
+
* In the app you rarely need this file: Setup > Look chooses a look and
|
|
7215
|
+
* changes its fonts and colors (those changes are saved here as :root
|
|
7216
|
+
* custom properties), Setup > Advanced lists every setting, and Setup > Book
|
|
7217
|
+
* sets the page size and margins (in the manifest).
|
|
7377
7218
|
*
|
|
7378
|
-
*
|
|
7379
|
-
*
|
|
7380
|
-
* one:
|
|
7381
|
-
* gutterpress ext add academic <book> (a built-in look)
|
|
7382
|
-
* gutterpress ext add <package> <book> (a look from npm)
|
|
7219
|
+
* To change the look from the command line: gutterpress ext remove <old>
|
|
7220
|
+
* <book>, then gutterpress ext add <look> <book>.
|
|
7383
7221
|
*/
|
|
7384
7222
|
`;
|
|
7385
7223
|
}
|
|
@@ -7415,9 +7253,6 @@ function requirePreset(preset, customPage) {
|
|
|
7415
7253
|
} else if (customPage && (!isPositivePoints(customPage.width) || !isPositivePoints(customPage.height))) {
|
|
7416
7254
|
throw new CreateProjectErrorImpl("custom-page-required", "Page overrides need a positive width and height in points (72pt = 1in).");
|
|
7417
7255
|
}
|
|
7418
|
-
if (customPage?.tolerance !== undefined && !isPositivePoints(customPage.tolerance)) {
|
|
7419
|
-
throw new CreateProjectErrorImpl("custom-page-required", "Page tolerance must be a positive number of points.");
|
|
7420
|
-
}
|
|
7421
7256
|
return preset;
|
|
7422
7257
|
}
|
|
7423
7258
|
function resolveScaffoldTargets(requested, preset) {
|
|
@@ -7462,7 +7297,7 @@ async function scaffoldProject(options) {
|
|
|
7462
7297
|
const preset = options.preset === undefined ? undefined : requirePreset(options.preset, options.customPage);
|
|
7463
7298
|
const targets = preset ? resolveScaffoldTargets(options.targets, preset) : undefined;
|
|
7464
7299
|
try {
|
|
7465
|
-
await
|
|
7300
|
+
await mkdir5(projectDir, { recursive: true });
|
|
7466
7301
|
if (customTemplateDir) {
|
|
7467
7302
|
const entries = await readdir6(customTemplateDir, { withFileTypes: true });
|
|
7468
7303
|
for (const entry of entries) {
|
|
@@ -7470,15 +7305,15 @@ async function scaffoldProject(options) {
|
|
|
7470
7305
|
continue;
|
|
7471
7306
|
await cp(path14.join(customTemplateDir, entry.name), path14.join(projectDir, entry.name), { recursive: true });
|
|
7472
7307
|
}
|
|
7473
|
-
await
|
|
7308
|
+
await mkdir5(path14.join(projectDir, "assets"), { recursive: true });
|
|
7474
7309
|
} else {
|
|
7475
7310
|
const tplManifest = await getAssetPath(`templates/${template}/manifest.yaml`);
|
|
7476
7311
|
const tplChapter = await getAssetPath(`templates/${template}/chapter-01.md`);
|
|
7477
|
-
await
|
|
7312
|
+
await mkdir5(path14.join(projectDir, "assets"), { recursive: true });
|
|
7478
7313
|
await copyFile(tplManifest, path14.join(projectDir, MANIFEST_FILENAMES[0]));
|
|
7479
7314
|
await copyFile(tplChapter, path14.join(projectDir, "chapter-01.md"));
|
|
7480
|
-
await
|
|
7481
|
-
await
|
|
7315
|
+
await mkdir5(path14.join(projectDir, "styles"), { recursive: true });
|
|
7316
|
+
await writeFile7(path14.join(projectDir, "styles", "book.css"), starterOverridesCss(), "utf8");
|
|
7482
7317
|
}
|
|
7483
7318
|
} catch (e) {
|
|
7484
7319
|
throw new CreateProjectErrorImpl("scaffold-io", `Could not create the book files: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -7502,6 +7337,8 @@ async function scaffoldProject(options) {
|
|
|
7502
7337
|
if (preset) {
|
|
7503
7338
|
doc.set("preset", preset);
|
|
7504
7339
|
doc.set("targets", doc.createNode(targets ?? []));
|
|
7340
|
+
if (preset !== "custom")
|
|
7341
|
+
deletePageBounds(doc);
|
|
7505
7342
|
}
|
|
7506
7343
|
if (options.customPage)
|
|
7507
7344
|
writePageBounds(doc, options.customPage);
|
|
@@ -7579,12 +7416,12 @@ async function adoptFolder(options) {
|
|
|
7579
7416
|
await fillTemplateFile(path14.join(dir, "chapter-01.md"), { "{{TITLE}}": title }).catch(() => {});
|
|
7580
7417
|
mdFiles = ["chapter-01.md"];
|
|
7581
7418
|
}
|
|
7582
|
-
await
|
|
7419
|
+
await mkdir5(path14.join(dir, "styles"), { recursive: true });
|
|
7583
7420
|
const bookCssPath = path14.join(dir, "styles", "book.css");
|
|
7584
7421
|
if (!existsSync9(bookCssPath)) {
|
|
7585
|
-
await
|
|
7422
|
+
await writeFile7(bookCssPath, starterOverridesCss(), "utf8");
|
|
7586
7423
|
}
|
|
7587
|
-
const filesYaml = mdFiles.map((f) => `
|
|
7424
|
+
const filesYaml = mdFiles.map((f) => ` - "${escapeYamlScalar(f)}"`).join(`
|
|
7588
7425
|
`);
|
|
7589
7426
|
const manifest = `title: "${escapeYamlScalar(title)}"
|
|
7590
7427
|
authors:
|
|
@@ -7592,14 +7429,13 @@ authors:
|
|
|
7592
7429
|
preset: dtrpg
|
|
7593
7430
|
targets:
|
|
7594
7431
|
- dtrpg
|
|
7595
|
-
|
|
7596
|
-
files:
|
|
7432
|
+
chapters:
|
|
7597
7433
|
${filesYaml}
|
|
7598
7434
|
styles:
|
|
7599
7435
|
- styles/book.css
|
|
7600
7436
|
`;
|
|
7601
|
-
await
|
|
7602
|
-
await
|
|
7437
|
+
await writeFile7(path14.join(dir, MANIFEST_FILENAMES[0]), manifest, "utf8");
|
|
7438
|
+
await mkdir5(path14.join(dir, "assets"), { recursive: true });
|
|
7603
7439
|
await ensureProjectGitignore(dir);
|
|
7604
7440
|
} catch (e) {
|
|
7605
7441
|
if (e instanceof CreateProjectErrorImpl)
|
|
@@ -7641,7 +7477,7 @@ async function firstSourceFile(manifestPath) {
|
|
|
7641
7477
|
} catch {
|
|
7642
7478
|
return;
|
|
7643
7479
|
}
|
|
7644
|
-
const filesIdx = text.search(
|
|
7480
|
+
const filesIdx = text.search(/^(?:chapters|\s+files):\s*$/m);
|
|
7645
7481
|
if (filesIdx === -1)
|
|
7646
7482
|
return;
|
|
7647
7483
|
const rest = text.slice(filesIdx);
|
|
@@ -7653,10 +7489,10 @@ async function fillTemplateFile(filePath, substitutions) {
|
|
|
7653
7489
|
for (const [token, value] of Object.entries(substitutions)) {
|
|
7654
7490
|
text = text.split(token).join(value);
|
|
7655
7491
|
}
|
|
7656
|
-
await
|
|
7492
|
+
await writeFile7(filePath, text, "utf8");
|
|
7657
7493
|
}
|
|
7658
7494
|
// src/lib/extension-scaffold.ts
|
|
7659
|
-
import { access as access2, mkdir as
|
|
7495
|
+
import { access as access2, mkdir as mkdir6, readFile as readFile26, writeFile as writeFile8 } from "node:fs/promises";
|
|
7660
7496
|
import { constants as FS2 } from "node:fs";
|
|
7661
7497
|
import path15 from "node:path";
|
|
7662
7498
|
var EXTENSION_KINDS = ["plugin", "look"];
|
|
@@ -7768,14 +7604,14 @@ async function scaffoldExtension(options) {
|
|
|
7768
7604
|
const entries = TEMPLATE_FILES[kind];
|
|
7769
7605
|
const written = [];
|
|
7770
7606
|
try {
|
|
7771
|
-
await
|
|
7607
|
+
await mkdir6(extensionDir, { recursive: true });
|
|
7772
7608
|
for (const [assetName, relDest] of entries) {
|
|
7773
7609
|
const src = await getAssetPath(`extension-templates/${kind}/${assetName}`);
|
|
7774
7610
|
const dest = path15.join(extensionDir, relDest);
|
|
7775
|
-
await
|
|
7611
|
+
await mkdir6(path15.dirname(dest), { recursive: true });
|
|
7776
7612
|
const text = await readFile26(src, "utf8");
|
|
7777
7613
|
const table = relDest.endsWith(".json") ? jsonSafe : plain;
|
|
7778
|
-
await
|
|
7614
|
+
await writeFile8(dest, substitute(text, table), "utf8");
|
|
7779
7615
|
written.push(relDest);
|
|
7780
7616
|
}
|
|
7781
7617
|
} catch (e) {
|
|
@@ -7799,7 +7635,7 @@ function substitute(text, table) {
|
|
|
7799
7635
|
return out;
|
|
7800
7636
|
}
|
|
7801
7637
|
// src/lib/project-templates.ts
|
|
7802
|
-
import { cp as cp2, mkdir as
|
|
7638
|
+
import { cp as cp2, mkdir as mkdir7, readFile as readFile27, readdir as readdir7, stat as stat5, writeFile as writeFile9 } from "node:fs/promises";
|
|
7803
7639
|
import { constants as FS3, existsSync as existsSync10 } from "node:fs";
|
|
7804
7640
|
import { access as access3 } from "node:fs/promises";
|
|
7805
7641
|
import path16 from "node:path";
|
|
@@ -7822,6 +7658,10 @@ async function readTemplateChoices(manifestPath) {
|
|
|
7822
7658
|
if (isSeq(targets)) {
|
|
7823
7659
|
out.targets = targets.items.map((i) => scalarString(i)).filter((s) => s !== null);
|
|
7824
7660
|
}
|
|
7661
|
+
const width = numberAt(doc, ["page", "width"]);
|
|
7662
|
+
const height = numberAt(doc, ["page", "height"]);
|
|
7663
|
+
if (width !== undefined && height !== undefined)
|
|
7664
|
+
out.page = { width, height };
|
|
7825
7665
|
return out;
|
|
7826
7666
|
} catch {
|
|
7827
7667
|
return {};
|
|
@@ -7830,19 +7670,19 @@ async function readTemplateChoices(manifestPath) {
|
|
|
7830
7670
|
var BUILT_IN_META = {
|
|
7831
7671
|
book: {
|
|
7832
7672
|
label: "Book",
|
|
7833
|
-
description: "A clean starting point for a novel, memoir, or any long-form book."
|
|
7673
|
+
description: "A clean starting point for a novel, memoir, or any long-form book. Uses the Clean book look."
|
|
7834
7674
|
},
|
|
7835
7675
|
zine: {
|
|
7836
7676
|
label: "Zine",
|
|
7837
|
-
description: "A short, personal zine made to be printed, folded, and shared."
|
|
7677
|
+
description: "A short, personal zine made to be printed, folded, and shared. Uses the Zine look."
|
|
7838
7678
|
},
|
|
7839
7679
|
technical: {
|
|
7840
7680
|
label: "Technical document",
|
|
7841
|
-
description: "A manual or guide with steps, reference tables, and code examples."
|
|
7681
|
+
description: "A manual or guide with steps, reference tables, and code examples. Uses the Technical doc look."
|
|
7842
7682
|
},
|
|
7843
7683
|
paper: {
|
|
7844
7684
|
label: "Academic paper",
|
|
7845
|
-
description: "A journal-style article with LaTeX math, numbered equations, figures, tables, notes and references."
|
|
7685
|
+
description: "A journal-style article with LaTeX math, numbered equations, figures, tables, notes and references. Uses the Academic look."
|
|
7846
7686
|
}
|
|
7847
7687
|
};
|
|
7848
7688
|
async function listBuiltInTemplates() {
|
|
@@ -7939,7 +7779,7 @@ async function reconcileSharedRefs(templateDir, sourceProjectDir, mode) {
|
|
|
7939
7779
|
const vendorDir = pickVendorDir(templateDir, base);
|
|
7940
7780
|
await Promise.all(externalFiles.filter((abs) => existsSync10(abs)).map(async (abs) => {
|
|
7941
7781
|
const dest = path16.join(templateDir, vendorDir, path16.relative(base, abs));
|
|
7942
|
-
await
|
|
7782
|
+
await mkdir7(path16.dirname(dest), { recursive: true });
|
|
7943
7783
|
await cp2(abs, dest, { recursive: true });
|
|
7944
7784
|
}));
|
|
7945
7785
|
const bookLocal = (value) => toPosixPath(path16.join(vendorDir, path16.relative(base, path16.resolve(sourceProjectDir, value))));
|
|
@@ -7972,7 +7812,7 @@ async function saveProjectAsTemplate(options) {
|
|
|
7972
7812
|
if (exists) {
|
|
7973
7813
|
throw new Error(`A template named "${name}" already exists. Choose a different name.`);
|
|
7974
7814
|
}
|
|
7975
|
-
await
|
|
7815
|
+
await mkdir7(dir, { recursive: true });
|
|
7976
7816
|
const entries = await readdir7(projectDir, { withFileTypes: true });
|
|
7977
7817
|
for (const entry of entries) {
|
|
7978
7818
|
if (SKIP_ENTRIES.has(entry.name))
|
|
@@ -7985,7 +7825,7 @@ async function saveProjectAsTemplate(options) {
|
|
|
7985
7825
|
const shared = manifestName ? await reconcileSharedRefs(dir, projectDir, options.sharedRefs ?? "vendor") : { vendoredRefs: [], excludedRefs: [] };
|
|
7986
7826
|
if (manifestName)
|
|
7987
7827
|
await retokeniseManifest(path16.join(dir, manifestName));
|
|
7988
|
-
await
|
|
7828
|
+
await writeFile9(path16.join(dir, TEMPLATE_META_FILE), JSON.stringify({ label: name }, null, 2), "utf8");
|
|
7989
7829
|
return {
|
|
7990
7830
|
id,
|
|
7991
7831
|
label: name,
|
|
@@ -8003,7 +7843,7 @@ async function retokeniseManifest(manifestPath) {
|
|
|
8003
7843
|
return;
|
|
8004
7844
|
}
|
|
8005
7845
|
text = text.replace(/^title:\s*.*$/m, 'title: "{{TITLE}}"').replace(/^(authors:[^\n]*\n[ \t]*-[ \t]*).*$/m, '$1"{{AUTHOR}}"');
|
|
8006
|
-
await
|
|
7846
|
+
await writeFile9(manifestPath, text, "utf8");
|
|
8007
7847
|
}
|
|
8008
7848
|
async function listCustomTemplates(templatesRoot) {
|
|
8009
7849
|
let entries;
|
|
@@ -8045,7 +7885,7 @@ async function importTemplateFromFolder(options) {
|
|
|
8045
7885
|
return saveProjectAsTemplate({ projectDir: sourceDir, name, templatesRoot });
|
|
8046
7886
|
}
|
|
8047
7887
|
// src/lib/snippets.ts
|
|
8048
|
-
import { readdir as readdir8, readFile as readFile28, writeFile as
|
|
7888
|
+
import { readdir as readdir8, readFile as readFile28, writeFile as writeFile10, mkdir as mkdir8, rm } from "node:fs/promises";
|
|
8049
7889
|
import path17 from "node:path";
|
|
8050
7890
|
var SNIPPETS_DIR = "snippets";
|
|
8051
7891
|
var LEVEL_RANK = { project: 0, extension: 1, core: 2 };
|
|
@@ -8110,8 +7950,8 @@ async function saveSnippet(projectDir, name, body) {
|
|
|
8110
7950
|
throw new Error(`Could not derive a filename from "${name}".`);
|
|
8111
7951
|
const fileName = `${stem}.md`;
|
|
8112
7952
|
const dir = path17.join(projectDir, SNIPPETS_DIR);
|
|
8113
|
-
await
|
|
8114
|
-
await
|
|
7953
|
+
await mkdir8(dir, { recursive: true });
|
|
7954
|
+
await writeFile10(resolveSnippetPath(projectDir, fileName), body, "utf8");
|
|
8115
7955
|
return { name, fileName, variables: extractVariables(body), body, source: { kind: "project" } };
|
|
8116
7956
|
}
|
|
8117
7957
|
async function deleteSnippet(projectDir, fileName) {
|
|
@@ -8244,7 +8084,7 @@ async function listLocationVariants(projectDir) {
|
|
|
8244
8084
|
}
|
|
8245
8085
|
// src/lib/extension-import.ts
|
|
8246
8086
|
import { existsSync as existsSync11 } from "node:fs";
|
|
8247
|
-
import { cp as cp3, mkdir as
|
|
8087
|
+
import { cp as cp3, mkdir as mkdir9, mkdtemp, readFile as readFile29, rm as rm2, writeFile as writeFile11 } from "node:fs/promises";
|
|
8248
8088
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
8249
8089
|
import path18 from "node:path";
|
|
8250
8090
|
import { unzipSync } from "fflate";
|
|
@@ -8365,7 +8205,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
8365
8205
|
}
|
|
8366
8206
|
const id = await uniqueExtensionId(projectDir, meta.name?.trim() || fallbackName);
|
|
8367
8207
|
const dest = path18.join(projectDir, EXTENSIONS_DIR, id);
|
|
8368
|
-
await
|
|
8208
|
+
await mkdir9(path18.dirname(dest), { recursive: true });
|
|
8369
8209
|
await cp3(sourceDir, dest, { recursive: true });
|
|
8370
8210
|
await completeLandedManifest(dest, id, meta, metaExists);
|
|
8371
8211
|
const entry = await addExtension(projectDir, `./${EXTENSIONS_DIR}/${id}`);
|
|
@@ -8385,7 +8225,7 @@ async function completeLandedManifest(dest, id, meta, metaExists) {
|
|
|
8385
8225
|
} catch {}
|
|
8386
8226
|
}
|
|
8387
8227
|
const gutterpress = pkg.gutterpress && typeof pkg.gutterpress === "object" && !Array.isArray(pkg.gutterpress) ? pkg.gutterpress : {};
|
|
8388
|
-
await
|
|
8228
|
+
await writeFile11(file, JSON.stringify({
|
|
8389
8229
|
...pkg,
|
|
8390
8230
|
name: typeof pkg.name === "string" && pkg.name.trim() ? pkg.name : id,
|
|
8391
8231
|
gutterpress: { ...gutterpress, styles: ["theme.css"] }
|
|
@@ -8449,8 +8289,8 @@ async function importExtensionFromZip(projectDir, archive) {
|
|
|
8449
8289
|
throw new Error(`The package contains an unsafe path and was rejected: ${name}`);
|
|
8450
8290
|
}
|
|
8451
8291
|
relPaths.push(rel);
|
|
8452
|
-
await
|
|
8453
|
-
await
|
|
8292
|
+
await mkdir9(path18.dirname(dest), { recursive: true });
|
|
8293
|
+
await writeFile11(dest, data);
|
|
8454
8294
|
}
|
|
8455
8295
|
return await finalizeExtensionImport(projectDir, tmp, relPaths, root || "imported-look");
|
|
8456
8296
|
} finally {
|
|
@@ -8465,8 +8305,8 @@ async function importExtensionFromCssText(projectDir, css, name) {
|
|
|
8465
8305
|
const displayName = name.trim() || "Imported theme";
|
|
8466
8306
|
const tmp = await mkdtemp(path18.join(tmpdir2(), "gutterpress-theme-"));
|
|
8467
8307
|
try {
|
|
8468
|
-
await
|
|
8469
|
-
await
|
|
8308
|
+
await writeFile11(path18.join(tmp, "theme.css"), css, "utf8");
|
|
8309
|
+
await writeFile11(path18.join(tmp, EXTENSION_MANIFEST_FILENAME), JSON.stringify({ name: slugify(displayName, "look"), gutterpress: { styles: ["theme.css"] } }, null, 2), "utf8");
|
|
8470
8310
|
const result = await finalizeExtensionImport(projectDir, tmp, ["theme.css", EXTENSION_MANIFEST_FILENAME], displayName);
|
|
8471
8311
|
result.warnings.unshift({
|
|
8472
8312
|
code: "no-theme-json",
|
|
@@ -8549,7 +8389,7 @@ async function importExtensionFromUrl(projectDir, url) {
|
|
|
8549
8389
|
assertLooksLikeCss(css, trimmed);
|
|
8550
8390
|
const tmp = await mkdtemp(path18.join(tmpdir2(), "gutterpress-ext-"));
|
|
8551
8391
|
try {
|
|
8552
|
-
await
|
|
8392
|
+
await writeFile11(path18.join(tmp, "theme.css"), css, "utf8");
|
|
8553
8393
|
const gp = meta.gutterpress && typeof meta.gutterpress === "object" && !Array.isArray(meta.gutterpress) ? meta.gutterpress : {};
|
|
8554
8394
|
const finalMeta = {
|
|
8555
8395
|
name: typeof meta.name === "string" && meta.name || slugify(baseName, "look"),
|
|
@@ -8561,7 +8401,7 @@ async function importExtensionFromUrl(projectDir, url) {
|
|
|
8561
8401
|
preview: typeof gp.preview === "string" ? gp.preview : null
|
|
8562
8402
|
}
|
|
8563
8403
|
};
|
|
8564
|
-
await
|
|
8404
|
+
await writeFile11(path18.join(tmp, EXTENSION_MANIFEST_FILENAME), JSON.stringify(finalMeta, null, 2), "utf8");
|
|
8565
8405
|
return await finalizeExtensionImport(projectDir, tmp, ["theme.css", EXTENSION_MANIFEST_FILENAME], baseName);
|
|
8566
8406
|
} finally {
|
|
8567
8407
|
await rm2(tmp, { recursive: true, force: true });
|
|
@@ -8682,7 +8522,7 @@ async function searchNpmExtensions(query, options = {}) {
|
|
|
8682
8522
|
return { matches, total: pages.reduce((sum, page) => sum + page.total, 0) };
|
|
8683
8523
|
}
|
|
8684
8524
|
// src/lib/manifest-config.ts
|
|
8685
|
-
import { isSeq as isSeq2, isMap as isMap3, YAMLSeq, Scalar
|
|
8525
|
+
import { isSeq as isSeq2, isMap as isMap3, YAMLSeq, Scalar } from "yaml";
|
|
8686
8526
|
function hasKey(obj, key) {
|
|
8687
8527
|
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
8688
8528
|
}
|
|
@@ -8717,15 +8557,16 @@ async function setManifestFields(projectDir, updates) {
|
|
|
8717
8557
|
}
|
|
8718
8558
|
if (hasKey(updates, "sourceFiles")) {
|
|
8719
8559
|
const f = updates.sourceFiles;
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
|
|
8723
|
-
if (
|
|
8560
|
+
const source = doc.get("source", true);
|
|
8561
|
+
if (isMap3(source)) {
|
|
8562
|
+
source.delete("files");
|
|
8563
|
+
if (source.items.length === 0)
|
|
8724
8564
|
doc.delete("source");
|
|
8725
|
-
}
|
|
8726
|
-
} else {
|
|
8727
|
-
doc.setIn(["source", "files"], f);
|
|
8728
8565
|
}
|
|
8566
|
+
if (f === null || Array.isArray(f) && f.length === 0)
|
|
8567
|
+
doc.delete("chapters");
|
|
8568
|
+
else
|
|
8569
|
+
doc.set("chapters", f);
|
|
8729
8570
|
}
|
|
8730
8571
|
await writeManifestDoc(file, doc);
|
|
8731
8572
|
return readManifestFields(projectDir);
|
|
@@ -8745,13 +8586,13 @@ async function readManifestFields(projectDir) {
|
|
|
8745
8586
|
if (isSeq2(targetsNode)) {
|
|
8746
8587
|
out.targets = targetsNode.items.map((i) => scalarString(i)).filter((s) => s !== null);
|
|
8747
8588
|
}
|
|
8748
|
-
const
|
|
8589
|
+
const sourceNode = doc.get("source", true);
|
|
8590
|
+
const oldSpelling = isMap3(sourceNode) && sourceNode.has("files");
|
|
8591
|
+
const filesNode = doc.has("chapters") ? doc.get("chapters", true) : doc.getIn(["source", "files"], true);
|
|
8749
8592
|
if (isSeq2(filesNode)) {
|
|
8750
8593
|
out.sourceFiles = filesNode.items.map((i) => scalarString(i) ?? "");
|
|
8751
|
-
} else {
|
|
8752
|
-
|
|
8753
|
-
if (isMap3(sourceNode) && sourceNode.has("files"))
|
|
8754
|
-
out.sourceFiles = null;
|
|
8594
|
+
} else if (doc.has("chapters") || oldSpelling) {
|
|
8595
|
+
out.sourceFiles = null;
|
|
8755
8596
|
}
|
|
8756
8597
|
return out;
|
|
8757
8598
|
} catch {
|
|
@@ -8765,7 +8606,7 @@ async function setActiveStyles(projectDir, paths) {
|
|
|
8765
8606
|
} else {
|
|
8766
8607
|
const seq = new YAMLSeq(doc.schema);
|
|
8767
8608
|
for (const p of paths)
|
|
8768
|
-
seq.add(new
|
|
8609
|
+
seq.add(new Scalar(p));
|
|
8769
8610
|
doc.set("styles", seq);
|
|
8770
8611
|
}
|
|
8771
8612
|
await writeManifestDoc(file, doc);
|
|
@@ -8773,9 +8614,9 @@ async function setActiveStyles(projectDir, paths) {
|
|
|
8773
8614
|
}
|
|
8774
8615
|
// src/lib/version-changes.ts
|
|
8775
8616
|
import path19 from "node:path";
|
|
8776
|
-
import { mkdir as
|
|
8617
|
+
import { mkdir as mkdir10, readFile as readFile30, writeFile as writeFile12 } from "node:fs/promises";
|
|
8777
8618
|
import git3 from "isomorphic-git";
|
|
8778
|
-
import
|
|
8619
|
+
import postcss5 from "postcss";
|
|
8779
8620
|
|
|
8780
8621
|
// src/lib/remote-auth/transport.ts
|
|
8781
8622
|
import git2 from "isomorphic-git";
|
|
@@ -9228,7 +9069,7 @@ function paragraphs(text) {
|
|
|
9228
9069
|
}
|
|
9229
9070
|
function cssRules(text) {
|
|
9230
9071
|
try {
|
|
9231
|
-
return
|
|
9072
|
+
return postcss5.parse(text).nodes.map((node) => node.toString().trim()).filter((rule) => rule.length > 0);
|
|
9232
9073
|
} catch {
|
|
9233
9074
|
return textLines(text);
|
|
9234
9075
|
}
|
|
@@ -9467,8 +9308,8 @@ async function exportVersionTree(projectDir, id, destDir) {
|
|
|
9467
9308
|
if (!content)
|
|
9468
9309
|
return null;
|
|
9469
9310
|
const abs = path19.join(destDir, filepath);
|
|
9470
|
-
await
|
|
9471
|
-
await
|
|
9311
|
+
await mkdir10(path19.dirname(abs), { recursive: true });
|
|
9312
|
+
await writeFile12(abs, content);
|
|
9472
9313
|
return null;
|
|
9473
9314
|
}
|
|
9474
9315
|
});
|
|
@@ -10084,7 +9925,7 @@ import git5 from "isomorphic-git";
|
|
|
10084
9925
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
10085
9926
|
|
|
10086
9927
|
// src/lib/remote-auth/converge-merge.ts
|
|
10087
|
-
import { mkdir as
|
|
9928
|
+
import { mkdir as mkdir11, unlink as unlink2, writeFile as writeFile13 } from "node:fs/promises";
|
|
10088
9929
|
import path20 from "node:path";
|
|
10089
9930
|
import git4 from "isomorphic-git";
|
|
10090
9931
|
import diff3Merge from "diff3";
|
|
@@ -10134,8 +9975,8 @@ async function readBlob(dir, cache2, oid, filepath) {
|
|
|
10134
9975
|
}
|
|
10135
9976
|
async function writeTreeFile(dir, filepath, content) {
|
|
10136
9977
|
const abs = path20.join(dir, filepath);
|
|
10137
|
-
await
|
|
10138
|
-
await
|
|
9978
|
+
await mkdir11(path20.dirname(abs), { recursive: true });
|
|
9979
|
+
await writeFile13(abs, content);
|
|
10139
9980
|
}
|
|
10140
9981
|
async function bothAddedPaths(dir, cache2, ourTip, theirTip, allowUnrelated) {
|
|
10141
9982
|
const bases = await git4.findMergeBase({ fs: gitFs, dir, cache: cache2, oids: [ourTip, theirTip] });
|
|
@@ -10791,7 +10632,7 @@ async function setUpOnlineBackup(options) {
|
|
|
10791
10632
|
}
|
|
10792
10633
|
}
|
|
10793
10634
|
// src/lib/remote-auth/clone.ts
|
|
10794
|
-
import { mkdir as
|
|
10635
|
+
import { mkdir as mkdir12, readdir as readdir9, rm as rm3 } from "node:fs/promises";
|
|
10795
10636
|
import path21 from "node:path";
|
|
10796
10637
|
import git7 from "isomorphic-git";
|
|
10797
10638
|
function sanitizeCloneFolderName(name) {
|
|
@@ -10850,7 +10691,7 @@ async function cloneRepository(options) {
|
|
|
10850
10691
|
return withRepoLock(dir, async () => {
|
|
10851
10692
|
await assertCloneTarget(dir);
|
|
10852
10693
|
const dirExistedBefore = gitFs.existsSync(dir);
|
|
10853
|
-
await
|
|
10694
|
+
await mkdir12(dir, { recursive: true });
|
|
10854
10695
|
try {
|
|
10855
10696
|
await git7.clone({
|
|
10856
10697
|
fs: gitFs,
|
|
@@ -10902,7 +10743,7 @@ var FAILURE_MESSAGES = {
|
|
|
10902
10743
|
auth: "The Git server didn't accept the saved connection. Reconnect to this server with a current access token and try again.",
|
|
10903
10744
|
"not-found": "The server answered, but no repository was found at that address. It may have been moved, renamed, or you may no longer have access.",
|
|
10904
10745
|
unreachable: "Couldn't reach the Git server. Check your internet connection (and VPN, if this is a private server), then try again.",
|
|
10905
|
-
"ssh-unsupported": "This book's online address uses SSH (git@…), which
|
|
10746
|
+
"ssh-unsupported": "This book's online address uses SSH (git@…), which Gutterpress can't check or sync with. Everything on this computer still works — sync with your usual Git tool.",
|
|
10906
10747
|
"insecure-transport": "This address isn't secure, so the saved connection wasn't sent — connections are never sent over an insecure address. Switch the address to a secure one (starting with https), or to a local loopback address for a server on this computer, to use a saved connection.",
|
|
10907
10748
|
tls: "The server's security certificate couldn't be verified. If this is a private server with its own certificate, ask its administrator about trusting it (NODE_EXTRA_CA_CERTS).",
|
|
10908
10749
|
unknown: "The connection test failed unexpectedly. The server may not be a Git server, or it may be temporarily unavailable."
|
|
@@ -11140,11 +10981,11 @@ async function diagnoseProjectRemote(projectDir, options = {}) {
|
|
|
11140
10981
|
}
|
|
11141
10982
|
// src/lib/remote-auth/repair.ts
|
|
11142
10983
|
import git9 from "isomorphic-git";
|
|
11143
|
-
import { cp as cp4, mkdir as
|
|
10984
|
+
import { cp as cp4, mkdir as mkdir13, rename as rename3, rm as rm4 } from "node:fs/promises";
|
|
11144
10985
|
import path22 from "node:path";
|
|
11145
10986
|
var REPAIR_SNAPSHOT_MESSAGE = "Repaired online backup";
|
|
11146
10987
|
async function moveDir(from, to) {
|
|
11147
|
-
await
|
|
10988
|
+
await mkdir13(path22.dirname(to), { recursive: true });
|
|
11148
10989
|
try {
|
|
11149
10990
|
await rename3(from, to);
|
|
11150
10991
|
} catch (e) {
|
|
@@ -11221,7 +11062,7 @@ async function repairOnlineBackup(options) {
|
|
|
11221
11062
|
}
|
|
11222
11063
|
// src/lib/remote-auth/scorched-earth.ts
|
|
11223
11064
|
import git10 from "isomorphic-git";
|
|
11224
|
-
import { cp as cp5, mkdir as
|
|
11065
|
+
import { cp as cp5, mkdir as mkdir14, readdir as readdir10, rm as rm5 } from "node:fs/promises";
|
|
11225
11066
|
import path23 from "node:path";
|
|
11226
11067
|
async function emptyDir(dir) {
|
|
11227
11068
|
for (const entry of await readdir10(dir)) {
|
|
@@ -11251,7 +11092,7 @@ async function scorchedEarth(options) {
|
|
|
11251
11092
|
branch2 = undefined;
|
|
11252
11093
|
}
|
|
11253
11094
|
logger.info("backup", "copying folder", { to: backupDir });
|
|
11254
|
-
await
|
|
11095
|
+
await mkdir14(backupDir, { recursive: true });
|
|
11255
11096
|
await cp5(dir, backupDir, { recursive: true });
|
|
11256
11097
|
logger.info("delete", "emptying folder", { dir });
|
|
11257
11098
|
await emptyDir(dir);
|
|
@@ -11437,11 +11278,11 @@ ${tail}` : ""}`);
|
|
|
11437
11278
|
};
|
|
11438
11279
|
|
|
11439
11280
|
// src/lib/publish/providers/guided.ts
|
|
11440
|
-
import { copyFile as copyFile2, mkdir as
|
|
11281
|
+
import { copyFile as copyFile2, mkdir as mkdir15, writeFile as writeFile14 } from "node:fs/promises";
|
|
11441
11282
|
import path24 from "node:path";
|
|
11442
11283
|
async function stageGuidedPackage(req, providerId, listingLines) {
|
|
11443
11284
|
const packageDir = path24.join(path24.dirname(req.artifact.path), "publish", providerId);
|
|
11444
|
-
await
|
|
11285
|
+
await mkdir15(packageDir, { recursive: true });
|
|
11445
11286
|
await copyFile2(req.artifact.path, path24.join(packageDir, path24.basename(req.artifact.path)));
|
|
11446
11287
|
const { title, authors } = req.project;
|
|
11447
11288
|
const sheet = [
|
|
@@ -11455,7 +11296,7 @@ async function stageGuidedPackage(req, providerId, listingLines) {
|
|
|
11455
11296
|
""
|
|
11456
11297
|
].join(`
|
|
11457
11298
|
`);
|
|
11458
|
-
await
|
|
11299
|
+
await writeFile14(path24.join(packageDir, "LISTING.md"), sheet, "utf8");
|
|
11459
11300
|
return packageDir;
|
|
11460
11301
|
}
|
|
11461
11302
|
|
|
@@ -11518,7 +11359,7 @@ var drivethrurpgProvider = {
|
|
|
11518
11359
|
};
|
|
11519
11360
|
|
|
11520
11361
|
// src/lib/publish/providers/gdrive.ts
|
|
11521
|
-
import { mkdtemp as mkdtemp2, readFile as readFile31, readdir as readdir11, rm as rm6, stat as stat7, writeFile as
|
|
11362
|
+
import { mkdtemp as mkdtemp2, readFile as readFile31, readdir as readdir11, rm as rm6, stat as stat7, writeFile as writeFile15 } from "node:fs/promises";
|
|
11522
11363
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
11523
11364
|
import path25 from "node:path";
|
|
11524
11365
|
import { Zip, ZipDeflate } from "fflate";
|
|
@@ -11926,7 +11767,7 @@ async function collectZipEntries(root, dir, out, onWarn) {
|
|
|
11926
11767
|
}
|
|
11927
11768
|
}
|
|
11928
11769
|
}
|
|
11929
|
-
async function zipHtmlExport(exportDir, title, onWarn, writeArchiveImpl =
|
|
11770
|
+
async function zipHtmlExport(exportDir, title, onWarn, writeArchiveImpl = writeFile15) {
|
|
11930
11771
|
const entries = {};
|
|
11931
11772
|
await collectZipEntries(exportDir, exportDir, entries, onWarn);
|
|
11932
11773
|
const fileName = `${bookSlug(title)}-website.zip`;
|
|
@@ -12030,7 +11871,7 @@ var gdriveProvider = {
|
|
|
12030
11871
|
};
|
|
12031
11872
|
|
|
12032
11873
|
// src/lib/publish/butler.ts
|
|
12033
|
-
import { chmod as chmod2, mkdir as
|
|
11874
|
+
import { chmod as chmod2, mkdir as mkdir16, stat as stat8, writeFile as writeFile16 } from "node:fs/promises";
|
|
12034
11875
|
import path26 from "node:path";
|
|
12035
11876
|
import { unzipSync as unzipSync2 } from "fflate";
|
|
12036
11877
|
var BUTLER_DOWNLOAD_TIMEOUT_MS = 300000;
|
|
@@ -12091,13 +11932,13 @@ async function ensureButler(deps) {
|
|
|
12091
11932
|
return new Uint8Array(await response.arrayBuffer());
|
|
12092
11933
|
});
|
|
12093
11934
|
const files = unzipSync2(archive);
|
|
12094
|
-
await
|
|
11935
|
+
await mkdir16(cacheDir, { recursive: true });
|
|
12095
11936
|
let foundBinary = false;
|
|
12096
11937
|
for (const [name, data] of Object.entries(files)) {
|
|
12097
11938
|
if (name.endsWith("/"))
|
|
12098
11939
|
continue;
|
|
12099
11940
|
const target = path26.join(cacheDir, path26.basename(name));
|
|
12100
|
-
await
|
|
11941
|
+
await writeFile16(target, data);
|
|
12101
11942
|
if (path26.basename(name) === butlerBinaryName()) {
|
|
12102
11943
|
await chmod2(target, 493).catch(() => {});
|
|
12103
11944
|
foundBinary = true;
|
|
@@ -12294,7 +12135,7 @@ var kdpProvider = {
|
|
|
12294
12135
|
};
|
|
12295
12136
|
|
|
12296
12137
|
// src/lib/publish/providers/local-folder.ts
|
|
12297
|
-
import { cp as cp6, mkdir as
|
|
12138
|
+
import { cp as cp6, mkdir as mkdir17, stat as stat9 } from "node:fs/promises";
|
|
12298
12139
|
import path27 from "node:path";
|
|
12299
12140
|
var LOCAL_PUBLISH_DEFAULT_DIR = "dist";
|
|
12300
12141
|
var info8 = {
|
|
@@ -12332,7 +12173,7 @@ var localFolderProvider = {
|
|
|
12332
12173
|
},
|
|
12333
12174
|
async upload(req) {
|
|
12334
12175
|
const dir = localPublishDir(req);
|
|
12335
|
-
await
|
|
12176
|
+
await mkdir17(dir, { recursive: true });
|
|
12336
12177
|
const dest = req.artifact.format === "html" ? dir : path27.join(dir, path27.basename(req.artifact.path));
|
|
12337
12178
|
if (path27.resolve(dest) !== path27.resolve(req.artifact.path)) {
|
|
12338
12179
|
await cp6(req.artifact.path, dest, { recursive: true });
|
|
@@ -12632,7 +12473,7 @@ async function resolvePublishProvider(id, book) {
|
|
|
12632
12473
|
const ext = await bookExtensions(book);
|
|
12633
12474
|
const npmEntry = ext.extensions.some((c) => !c.path && c.name === id);
|
|
12634
12475
|
if (npmEntry) {
|
|
12635
|
-
const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-
|
|
12476
|
+
const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-dymzcasz.js");
|
|
12636
12477
|
await restoreForCommand2(book.projectDir, { manifestPath: book.manifestPath, failFast: true });
|
|
12637
12478
|
}
|
|
12638
12479
|
const publishers = await discover(ext);
|
|
@@ -12670,7 +12511,7 @@ function infoFor(p) {
|
|
|
12670
12511
|
}
|
|
12671
12512
|
function pluginProvider(p) {
|
|
12672
12513
|
let loading;
|
|
12673
|
-
const load = () => loading ??= import("./plugins-
|
|
12514
|
+
const load = () => loading ??= import("./plugins-fec8t2bh.js").then((m) => m.loadPublishFunction(p));
|
|
12674
12515
|
return {
|
|
12675
12516
|
info: infoFor(p),
|
|
12676
12517
|
async authenticate() {
|
|
@@ -12941,7 +12782,7 @@ async function disconnectPublishCredential(key, deps, options = {}) {
|
|
|
12941
12782
|
}
|
|
12942
12783
|
}
|
|
12943
12784
|
// src/lib/publish/selections.ts
|
|
12944
|
-
import { chmod as chmod3, mkdir as
|
|
12785
|
+
import { chmod as chmod3, mkdir as mkdir18, readFile as readFile32, writeFile as writeFile17 } from "node:fs/promises";
|
|
12945
12786
|
import path30 from "node:path";
|
|
12946
12787
|
class PublishSelectionsStore {
|
|
12947
12788
|
filePath;
|
|
@@ -12964,8 +12805,8 @@ class PublishSelectionsStore {
|
|
|
12964
12805
|
return { version: 1, global: {}, projects: {} };
|
|
12965
12806
|
}
|
|
12966
12807
|
async write(data) {
|
|
12967
|
-
await
|
|
12968
|
-
await
|
|
12808
|
+
await mkdir18(path30.dirname(this.filePath), { recursive: true });
|
|
12809
|
+
await writeFile17(this.filePath, JSON.stringify(data, null, 2), {
|
|
12969
12810
|
encoding: "utf8",
|
|
12970
12811
|
mode: 384
|
|
12971
12812
|
});
|