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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
BUNDLED_EXTENSIONS,
|
|
2
3
|
EXTENSION_MANIFEST_FILENAME,
|
|
3
4
|
TWO_VERSIONS_OPEN,
|
|
4
5
|
bundledExtension,
|
|
@@ -6,22 +7,18 @@ import {
|
|
|
6
7
|
extensionStyleList,
|
|
7
8
|
isPathSpecifier,
|
|
8
9
|
loadPlugin,
|
|
10
|
+
log,
|
|
9
11
|
parseExtensionSpecifier,
|
|
10
12
|
pinnedNpmSpecifier,
|
|
11
13
|
prettify,
|
|
12
14
|
readExtensionMeta,
|
|
15
|
+
resolveExtension,
|
|
13
16
|
slugify
|
|
14
|
-
} from "./
|
|
15
|
-
import {
|
|
16
|
-
log
|
|
17
|
-
} from "./cli-aqh6brew.js";
|
|
18
|
-
import {
|
|
19
|
-
UsageError
|
|
20
|
-
} from "./cli-d8xjns66.js";
|
|
17
|
+
} from "./index-8ywkd2sv.js";
|
|
21
18
|
import {
|
|
22
19
|
BuildError,
|
|
23
20
|
EXIT_CODES
|
|
24
|
-
} from "./
|
|
21
|
+
} from "./index-46ycxe6r.js";
|
|
25
22
|
import {
|
|
26
23
|
EXTENSIONS_DIR,
|
|
27
24
|
VENDORED_NPM_DIR,
|
|
@@ -34,10 +31,10 @@ import {
|
|
|
34
31
|
vendoredNpmPluginRoot,
|
|
35
32
|
vendoredNpmPluginRoots,
|
|
36
33
|
windowsPathKey
|
|
37
|
-
} from "./
|
|
34
|
+
} from "./index-5x42amh8.js";
|
|
38
35
|
import {
|
|
39
36
|
__require
|
|
40
|
-
} from "./
|
|
37
|
+
} from "./index-37x76zdn.js";
|
|
41
38
|
|
|
42
39
|
// src/lib/extension-restore.ts
|
|
43
40
|
import path5 from "node:path";
|
|
@@ -94,6 +91,117 @@ import { existsSync, statSync } from "node:fs";
|
|
|
94
91
|
import { resolve, dirname } from "node:path";
|
|
95
92
|
import { parse as parseYaml, YAMLParseError } from "yaml";
|
|
96
93
|
|
|
94
|
+
// src/engine/shared/print-area.ts
|
|
95
|
+
function isPositivePoints(value) {
|
|
96
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
97
|
+
}
|
|
98
|
+
function isNonNegativePoints(value) {
|
|
99
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
100
|
+
}
|
|
101
|
+
var round3 = (n) => Math.round(n * 1000) / 1000;
|
|
102
|
+
function cssLength(pt) {
|
|
103
|
+
const inches = round3(pt / 72);
|
|
104
|
+
return Math.abs(inches * 72 - pt) < 0.000001 ? `${inches}in` : `${round3(pt)}pt`;
|
|
105
|
+
}
|
|
106
|
+
function formatPageSize(page) {
|
|
107
|
+
return `${cssLength(page.width)} ${cssLength(page.height)}`;
|
|
108
|
+
}
|
|
109
|
+
function printAreaMeta(page) {
|
|
110
|
+
const area = JSON.stringify({
|
|
111
|
+
width: page.width,
|
|
112
|
+
height: page.height,
|
|
113
|
+
bleed: page.bleed ?? 0,
|
|
114
|
+
safe: page.safe ?? 0,
|
|
115
|
+
artSafe: page.artSafe,
|
|
116
|
+
gutter: page.gutter,
|
|
117
|
+
preset: page.preset,
|
|
118
|
+
spec: page.spec
|
|
119
|
+
});
|
|
120
|
+
return `<meta name="gp-print-area" content='${area.replace(/&/g, "&").replace(/'/g, "'")}'>`;
|
|
121
|
+
}
|
|
122
|
+
function pageBoxes(area, side, scale = 1) {
|
|
123
|
+
const w = area.width * scale;
|
|
124
|
+
const h = area.height * scale;
|
|
125
|
+
const b = Math.max(0, area.bleed) * scale;
|
|
126
|
+
const s = Math.max(0, area.safe) * scale;
|
|
127
|
+
const a = Math.max(0, area.artSafe) * scale;
|
|
128
|
+
const g = Math.max(0, area.gutter) * scale;
|
|
129
|
+
const [textLeft, textRight] = side === "verso" ? [s, g] : [g, s];
|
|
130
|
+
const [artLeft, artRight] = side === "verso" ? [a, g] : [g, a];
|
|
131
|
+
const trim = { x: side === "verso" ? b : 0, y: b, w: Math.max(0, w - b), h: Math.max(0, h - 2 * b) };
|
|
132
|
+
return {
|
|
133
|
+
sheet: { x: 0, y: 0, w, h },
|
|
134
|
+
trim,
|
|
135
|
+
safe: {
|
|
136
|
+
x: trim.x + textLeft,
|
|
137
|
+
y: trim.y + s,
|
|
138
|
+
w: Math.max(0, trim.w - textLeft - textRight),
|
|
139
|
+
h: Math.max(0, trim.h - 2 * s)
|
|
140
|
+
},
|
|
141
|
+
safeArt: {
|
|
142
|
+
x: trim.x + artLeft,
|
|
143
|
+
y: trim.y + a,
|
|
144
|
+
w: Math.max(0, trim.w - artLeft - artRight),
|
|
145
|
+
h: Math.max(0, trim.h - 2 * a)
|
|
146
|
+
},
|
|
147
|
+
bleed: b
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
var SEVERITY = { inside: 0, unsafe: 1, bleed: 2, "off-page": 3 };
|
|
151
|
+
function judgeRect(r, boxes, rule, tol = 0) {
|
|
152
|
+
const { sheet, trim } = boxes;
|
|
153
|
+
const safe = rule === "art" ? boxes.safeArt : boxes.safe;
|
|
154
|
+
const sides = [
|
|
155
|
+
["left", -r.x, -safe.x, -trim.x, -sheet.x],
|
|
156
|
+
["right", r.x + r.w, safe.x + safe.w, trim.x + trim.w, sheet.x + sheet.w],
|
|
157
|
+
["top", -r.y, -safe.y, -trim.y, -sheet.y],
|
|
158
|
+
["bottom", r.y + r.h, safe.y + safe.h, trim.y + trim.h, sheet.y + sheet.h]
|
|
159
|
+
];
|
|
160
|
+
let worst = { kind: "inside", gap: 0, need: 0, edge: null };
|
|
161
|
+
let beyond = 0;
|
|
162
|
+
for (const [edge, ink, limit, trimEdge, sheetEdge] of sides) {
|
|
163
|
+
const past = ink - limit;
|
|
164
|
+
if (past <= tol || rule === "art" && ink >= sheetEdge - tol)
|
|
165
|
+
continue;
|
|
166
|
+
const kind = ink > sheetEdge + tol ? "off-page" : ink > trimEdge + tol ? "bleed" : "unsafe";
|
|
167
|
+
if (SEVERITY[kind] > SEVERITY[worst.kind] || kind === worst.kind && past > beyond) {
|
|
168
|
+
worst = { kind, gap: trimEdge - ink, need: trimEdge - limit, edge };
|
|
169
|
+
beyond = past;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return worst;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// src/constants.ts
|
|
176
|
+
var NETWORK = {
|
|
177
|
+
DEFAULT_PORT: 3579
|
|
178
|
+
};
|
|
179
|
+
var DEBOUNCE = {
|
|
180
|
+
FILE_WATCH: 100
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// src/lib/cli-args.ts
|
|
184
|
+
class UsageError extends Error {
|
|
185
|
+
exitCode;
|
|
186
|
+
constructor(message, exitCode = EXIT_CODES.USAGE) {
|
|
187
|
+
super(message);
|
|
188
|
+
this.name = "UsageError";
|
|
189
|
+
this.exitCode = exitCode;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function resolvePort(raw) {
|
|
193
|
+
if (raw === undefined)
|
|
194
|
+
return NETWORK.DEFAULT_PORT;
|
|
195
|
+
if (raw === "" || typeof raw === "string" && raw.trim() === "") {
|
|
196
|
+
throw new UsageError("--port requires a value.");
|
|
197
|
+
}
|
|
198
|
+
const n = typeof raw === "number" || typeof raw === "string" ? Number(raw) : NaN;
|
|
199
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0 || n > 65535) {
|
|
200
|
+
throw new UsageError(`Invalid --port value: "${raw}". Expected an integer from 0 to 65535 (0 = OS-assigned).`);
|
|
201
|
+
}
|
|
202
|
+
return n;
|
|
203
|
+
}
|
|
204
|
+
|
|
97
205
|
// src/lib/presets.ts
|
|
98
206
|
var PRESET_IDS = ["dtrpg", "book", "custom"];
|
|
99
207
|
var DTRPG_PRESET = {
|
|
@@ -104,9 +212,7 @@ var DTRPG_PRESET = {
|
|
|
104
212
|
},
|
|
105
213
|
defaultTargets: ["dtrpg"],
|
|
106
214
|
extensions: [],
|
|
107
|
-
|
|
108
|
-
files: null
|
|
109
|
-
},
|
|
215
|
+
chapters: null,
|
|
110
216
|
print: { signature: 4, reserveLastPage: true, lastPage: undefined },
|
|
111
217
|
pdfx: {
|
|
112
218
|
flavor: "x1a",
|
|
@@ -124,59 +230,30 @@ var DTRPG_PRESET = {
|
|
|
124
230
|
bleed: 9,
|
|
125
231
|
safe: 36,
|
|
126
232
|
artSafe: 18,
|
|
127
|
-
gutter: 36
|
|
233
|
+
gutter: 36,
|
|
234
|
+
margin: undefined
|
|
128
235
|
},
|
|
129
236
|
ink: {
|
|
130
237
|
maxTac: 240,
|
|
131
238
|
tacTolerance: 0.5
|
|
132
239
|
},
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
240
|
+
checks: {
|
|
241
|
+
"pdf.structure.qpdf": "error",
|
|
242
|
+
"pdf.print.pdfx-markers": "error",
|
|
243
|
+
"pdf.print.pdfx-metadata": "error",
|
|
244
|
+
"pdf.print.embedded-fonts": "error",
|
|
245
|
+
"pdf.print.safe-margins": "error",
|
|
246
|
+
"pdf.print.bleed": "off",
|
|
247
|
+
"pdf.nav.bookmarks": "off",
|
|
248
|
+
"pdf.nav.toc-links": "off",
|
|
249
|
+
"asset.font.license": "off"
|
|
137
250
|
},
|
|
138
251
|
validate: {
|
|
139
|
-
enabled: true,
|
|
140
|
-
checks: {
|
|
141
|
-
"pdf.structure.qpdf": {
|
|
142
|
-
enabled: true,
|
|
143
|
-
severity: "error"
|
|
144
|
-
},
|
|
145
|
-
"pdf.print.pdfx-markers": {
|
|
146
|
-
enabled: true,
|
|
147
|
-
severity: "error"
|
|
148
|
-
},
|
|
149
|
-
"pdf.print.pdfx-metadata": {
|
|
150
|
-
enabled: true,
|
|
151
|
-
severity: "error"
|
|
152
|
-
},
|
|
153
|
-
"pdf.print.embedded-fonts": {
|
|
154
|
-
enabled: true,
|
|
155
|
-
severity: "error"
|
|
156
|
-
},
|
|
157
|
-
"pdf.print.safe-margins": {
|
|
158
|
-
enabled: true,
|
|
159
|
-
severity: "error"
|
|
160
|
-
}
|
|
161
|
-
},
|
|
162
|
-
source: {
|
|
163
|
-
markdownlint: null,
|
|
164
|
-
htmlhint: null,
|
|
165
|
-
stylelint: null,
|
|
166
|
-
cssOwnership: null
|
|
167
|
-
},
|
|
168
252
|
assets: {
|
|
169
|
-
maxImageSize: 1e7
|
|
170
|
-
allowAlpha: false,
|
|
171
|
-
approvedFontFiles: [],
|
|
172
|
-
requireFontLicense: false
|
|
253
|
+
maxImageSize: 1e7
|
|
173
254
|
},
|
|
174
255
|
pdf: {
|
|
175
|
-
requireBookmarks: false,
|
|
176
|
-
requireTocLinks: false,
|
|
177
256
|
minImageResolution: 300,
|
|
178
|
-
forbidTransparency: true,
|
|
179
|
-
requireBleed: false,
|
|
180
257
|
bleedSize: 9
|
|
181
258
|
},
|
|
182
259
|
heuristics: {
|
|
@@ -190,9 +267,7 @@ var BOOK_PRESET = {
|
|
|
190
267
|
id: "book",
|
|
191
268
|
defaultTargets: [],
|
|
192
269
|
extensions: [],
|
|
193
|
-
|
|
194
|
-
files: null
|
|
195
|
-
},
|
|
270
|
+
chapters: null,
|
|
196
271
|
print: { signature: 1, reserveLastPage: false, lastPage: undefined },
|
|
197
272
|
pdfx: {
|
|
198
273
|
flavor: "x1a",
|
|
@@ -210,53 +285,32 @@ var BOOK_PRESET = {
|
|
|
210
285
|
bleed: undefined,
|
|
211
286
|
safe: 18,
|
|
212
287
|
artSafe: undefined,
|
|
213
|
-
gutter: undefined
|
|
288
|
+
gutter: undefined,
|
|
289
|
+
margin: undefined
|
|
214
290
|
},
|
|
215
291
|
ink: {
|
|
216
292
|
maxTac: 400,
|
|
217
293
|
tacTolerance: 0.5
|
|
218
294
|
},
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
295
|
+
checks: {
|
|
296
|
+
"pdf.structure.qpdf": "error",
|
|
297
|
+
"pdf.print.embedded-fonts": "error",
|
|
298
|
+
"pdf.print.safe-margins": "warning",
|
|
299
|
+
"pdf.print.pdfx-markers": "off",
|
|
300
|
+
"pdf.print.pdfx-metadata": "off",
|
|
301
|
+
"pdf.print.transparency": "off",
|
|
302
|
+
"asset.image.alpha-channel": "off",
|
|
303
|
+
"pdf.print.bleed": "off",
|
|
304
|
+
"pdf.nav.bookmarks": "off",
|
|
305
|
+
"pdf.nav.toc-links": "off",
|
|
306
|
+
"asset.font.license": "off"
|
|
223
307
|
},
|
|
224
308
|
validate: {
|
|
225
|
-
enabled: true,
|
|
226
|
-
checks: {
|
|
227
|
-
"pdf.structure.qpdf": {
|
|
228
|
-
enabled: true,
|
|
229
|
-
severity: "error"
|
|
230
|
-
},
|
|
231
|
-
"pdf.print.embedded-fonts": {
|
|
232
|
-
enabled: true,
|
|
233
|
-
severity: "error"
|
|
234
|
-
},
|
|
235
|
-
"pdf.print.safe-margins": {
|
|
236
|
-
enabled: true,
|
|
237
|
-
severity: "warning"
|
|
238
|
-
},
|
|
239
|
-
"pdf.print.pdfx-markers": false,
|
|
240
|
-
"pdf.print.pdfx-metadata": false
|
|
241
|
-
},
|
|
242
|
-
source: {
|
|
243
|
-
markdownlint: null,
|
|
244
|
-
htmlhint: null,
|
|
245
|
-
stylelint: null,
|
|
246
|
-
cssOwnership: null
|
|
247
|
-
},
|
|
248
309
|
assets: {
|
|
249
|
-
maxImageSize: 1e7
|
|
250
|
-
allowAlpha: true,
|
|
251
|
-
approvedFontFiles: [],
|
|
252
|
-
requireFontLicense: false
|
|
310
|
+
maxImageSize: 1e7
|
|
253
311
|
},
|
|
254
312
|
pdf: {
|
|
255
|
-
requireBookmarks: false,
|
|
256
|
-
requireTocLinks: false,
|
|
257
313
|
minImageResolution: 300,
|
|
258
|
-
forbidTransparency: false,
|
|
259
|
-
requireBleed: false,
|
|
260
314
|
bleedSize: 9
|
|
261
315
|
},
|
|
262
316
|
heuristics: {
|
|
@@ -309,16 +363,16 @@ var DTRPG_TARGET = {
|
|
|
309
363
|
maxTac: DTRPG_PRESET.ink.maxTac,
|
|
310
364
|
tacTolerance: DTRPG_PRESET.ink.tacTolerance
|
|
311
365
|
},
|
|
366
|
+
checks: {
|
|
367
|
+
"pdf.structure.qpdf": "error",
|
|
368
|
+
"pdf.print.pdfx-markers": "error",
|
|
369
|
+
"pdf.print.pdfx-metadata": "error",
|
|
370
|
+
"pdf.print.embedded-fonts": "error",
|
|
371
|
+
"pdf.print.safe-margins": "error",
|
|
372
|
+
"pdf.print.transparency": "on"
|
|
373
|
+
},
|
|
312
374
|
validate: {
|
|
313
|
-
checks: {
|
|
314
|
-
"pdf.structure.qpdf": { enabled: true, severity: "error" },
|
|
315
|
-
"pdf.print.pdfx-markers": { enabled: true, severity: "error" },
|
|
316
|
-
"pdf.print.pdfx-metadata": { enabled: true, severity: "error" },
|
|
317
|
-
"pdf.print.embedded-fonts": { enabled: true, severity: "error" },
|
|
318
|
-
"pdf.print.safe-margins": { enabled: true, severity: "error" }
|
|
319
|
-
},
|
|
320
375
|
pdf: {
|
|
321
|
-
forbidTransparency: DTRPG_PRESET.validate.pdf.forbidTransparency,
|
|
322
376
|
minImageResolution: DTRPG_PRESET.validate.pdf.minImageResolution
|
|
323
377
|
}
|
|
324
378
|
}
|
|
@@ -336,22 +390,22 @@ var ITCH_TARGET = {
|
|
|
336
390
|
id: "itch",
|
|
337
391
|
label: "itch.io",
|
|
338
392
|
overlay: {
|
|
393
|
+
checks: {
|
|
394
|
+
"pdf.structure.qpdf": "error",
|
|
395
|
+
"pdf.print.embedded-fonts": "error",
|
|
396
|
+
"pdf.print.pdfx-markers": "off",
|
|
397
|
+
"pdf.print.pdfx-metadata": "off",
|
|
398
|
+
"pdf.print.ink-coverage": "off",
|
|
399
|
+
"pdf.print.safe-margins": "off",
|
|
400
|
+
"pdf.print.transparency": "off",
|
|
401
|
+
"pdf.print.bleed": "off",
|
|
402
|
+
"asset.image.alpha-channel": "off"
|
|
403
|
+
},
|
|
339
404
|
validate: {
|
|
340
|
-
checks: {
|
|
341
|
-
"pdf.structure.qpdf": { enabled: true, severity: "error" },
|
|
342
|
-
"pdf.print.embedded-fonts": { enabled: true, severity: "error" },
|
|
343
|
-
"pdf.print.pdfx-markers": false,
|
|
344
|
-
"pdf.print.pdfx-metadata": false,
|
|
345
|
-
"pdf.print.ink-coverage": false,
|
|
346
|
-
"pdf.print.safe-margins": false
|
|
347
|
-
},
|
|
348
405
|
assets: {
|
|
349
|
-
allowAlpha: true,
|
|
350
406
|
maxImageSize: 0
|
|
351
407
|
},
|
|
352
408
|
pdf: {
|
|
353
|
-
forbidTransparency: false,
|
|
354
|
-
requireBleed: false,
|
|
355
409
|
minImageResolution: 0
|
|
356
410
|
}
|
|
357
411
|
}
|
|
@@ -390,9 +444,9 @@ function overlayPreset(preset, target) {
|
|
|
390
444
|
...preset,
|
|
391
445
|
pdfx: { ...preset.pdfx, ...o.pdfx },
|
|
392
446
|
ink: { ...preset.ink, ...o.ink },
|
|
447
|
+
checks: { ...preset.checks, ...o.checks },
|
|
393
448
|
validate: {
|
|
394
449
|
...preset.validate,
|
|
395
|
-
checks: { ...preset.validate.checks, ...o.validate?.checks },
|
|
396
450
|
assets: { ...preset.validate.assets, ...o.validate?.assets },
|
|
397
451
|
pdf: { ...preset.validate.pdf, ...o.validate?.pdf }
|
|
398
452
|
}
|
|
@@ -407,17 +461,134 @@ var DEFAULT_STRICT_PDF_CHECKS = [
|
|
|
407
461
|
function applyDefaultPdfStrictChecks(config) {
|
|
408
462
|
const next = structuredClone(config);
|
|
409
463
|
for (const checkId of DEFAULT_STRICT_PDF_CHECKS) {
|
|
410
|
-
if (next.
|
|
411
|
-
next.
|
|
412
|
-
}
|
|
464
|
+
if (next.checks[checkId] === undefined)
|
|
465
|
+
next.checks[checkId] = "error";
|
|
413
466
|
}
|
|
414
467
|
return next;
|
|
415
468
|
}
|
|
416
469
|
|
|
470
|
+
// src/lib/manifest-old-spellings.ts
|
|
471
|
+
var SOURCE_TOOL_CHECKS = {
|
|
472
|
+
markdownlint: "source.markdownlint",
|
|
473
|
+
htmlhint: "source.htmlhint",
|
|
474
|
+
stylelint: "source.stylelint",
|
|
475
|
+
cssOwnership: "source.css-ownership"
|
|
476
|
+
};
|
|
477
|
+
var FLAG_CHECKS = {
|
|
478
|
+
"assets.allowAlpha": { check: "asset.image.alpha-channel", trueMeansOn: false },
|
|
479
|
+
"assets.requireFontLicense": { check: "asset.font.license", trueMeansOn: true },
|
|
480
|
+
"pdf.requireBookmarks": { check: "pdf.nav.bookmarks", trueMeansOn: true },
|
|
481
|
+
"pdf.requireTocLinks": { check: "pdf.nav.toc-links", trueMeansOn: true },
|
|
482
|
+
"pdf.requireBleed": { check: "pdf.print.bleed", trueMeansOn: true },
|
|
483
|
+
"pdf.forbidTransparency": { check: "pdf.print.transparency", trueMeansOn: true }
|
|
484
|
+
};
|
|
485
|
+
function isObject(v) {
|
|
486
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
487
|
+
}
|
|
488
|
+
function readOldSpellings(raw) {
|
|
489
|
+
const { source, lint, validate, ink, ...manifest } = raw;
|
|
490
|
+
const tolerance = isObject(raw.page) && "tolerance" in raw.page;
|
|
491
|
+
if (source === undefined && lint === undefined && validate === undefined && ink === undefined && !tolerance) {
|
|
492
|
+
return raw;
|
|
493
|
+
}
|
|
494
|
+
if (isObject(source) && source.assets !== undefined) {
|
|
495
|
+
throw new UsageError("Manifest field `source.assets` is no longer supported — remove it.\n" + " Assets are discovered from what your book actually references, so no list is needed.");
|
|
496
|
+
}
|
|
497
|
+
const renamed = [];
|
|
498
|
+
const dropped = [];
|
|
499
|
+
const checks = {};
|
|
500
|
+
if (isObject(source) && "files" in source) {
|
|
501
|
+
renamed.push("source.files -> chapters");
|
|
502
|
+
if (manifest.chapters === undefined)
|
|
503
|
+
manifest.chapters = source.files ?? null;
|
|
504
|
+
}
|
|
505
|
+
if (isObject(validate)) {
|
|
506
|
+
for (const [id, entry] of Object.entries(validate.checks ?? {})) {
|
|
507
|
+
if (entry === false || isObject(entry) && entry.enabled === false)
|
|
508
|
+
checks[id] = "off";
|
|
509
|
+
else if (isObject(entry) && typeof entry.severity === "string")
|
|
510
|
+
checks[id] = entry.severity;
|
|
511
|
+
else
|
|
512
|
+
checks[id] = "on";
|
|
513
|
+
}
|
|
514
|
+
if (validate.checks !== undefined)
|
|
515
|
+
renamed.push("validate.checks -> checks");
|
|
516
|
+
for (const [tool, value] of Object.entries(validate.source ?? {})) {
|
|
517
|
+
const id = SOURCE_TOOL_CHECKS[tool];
|
|
518
|
+
if (id && value === false) {
|
|
519
|
+
checks[id] = "off";
|
|
520
|
+
renamed.push(`validate.source.${tool}: false -> checks: { ${id}: off }`);
|
|
521
|
+
} else if (id && tool !== "stylelint" && typeof value === "string") {
|
|
522
|
+
dropped.push(`validate.source.${tool} (the config file is found by its usual name in the book's folder or a folder above it)`);
|
|
523
|
+
} else {
|
|
524
|
+
dropped.push(`validate.source.${tool}`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
for (const block of ["assets", "pdf", "heuristics"]) {
|
|
528
|
+
for (const [key, value] of Object.entries(validate[block] ?? {})) {
|
|
529
|
+
const flag = FLAG_CHECKS[`${block}.${key}`];
|
|
530
|
+
if (flag && typeof value === "boolean") {
|
|
531
|
+
const level = value === flag.trueMeansOn ? "on" : "off";
|
|
532
|
+
checks[flag.check] = level;
|
|
533
|
+
renamed.push(`validate.${block}.${key}: ${value} -> checks: { ${flag.check}: ${level} }`);
|
|
534
|
+
} else {
|
|
535
|
+
dropped.push(`validate.${block}.${key} (the preset and the publish target set it)`);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (validate.enabled !== undefined) {
|
|
540
|
+
dropped.push("validate.enabled (`gutterpress build --skip-pre-validate --skip-post-validate` skips validation for one build)");
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
if (isObject(lint)) {
|
|
544
|
+
for (const [id, level] of Object.entries(lint.rules ?? {}))
|
|
545
|
+
checks[id] = level;
|
|
546
|
+
if (lint.rules !== undefined)
|
|
547
|
+
renamed.push("lint.rules -> checks");
|
|
548
|
+
if (lint.enabled === false) {
|
|
549
|
+
checks["source.stylelint"] = "off";
|
|
550
|
+
renamed.push("lint.enabled: false -> checks: { source.stylelint: off }");
|
|
551
|
+
} else if (lint.enabled !== undefined) {
|
|
552
|
+
dropped.push("lint.enabled");
|
|
553
|
+
}
|
|
554
|
+
if (lint.configPath !== undefined)
|
|
555
|
+
dropped.push("lint.configPath");
|
|
556
|
+
}
|
|
557
|
+
if (ink !== undefined)
|
|
558
|
+
dropped.push("ink (the preset and the publish target set it)");
|
|
559
|
+
if (tolerance) {
|
|
560
|
+
const { tolerance: _, ...page } = raw.page;
|
|
561
|
+
manifest.page = page;
|
|
562
|
+
dropped.push("page.tolerance (the preset sets it)");
|
|
563
|
+
}
|
|
564
|
+
if (Object.keys(checks).length > 0)
|
|
565
|
+
manifest.checks = { ...checks, ...manifest.checks };
|
|
566
|
+
const lines = [
|
|
567
|
+
...renamed.map((r) => ` ${r}`),
|
|
568
|
+
...dropped.map((d) => ` ${d} — no longer a setting, ignored`)
|
|
569
|
+
];
|
|
570
|
+
if (lines.length > 0) {
|
|
571
|
+
warnOnce(`old-manifest-spellings:${lines.join("|")}`, "[gutterpress] manifest.yaml uses spellings 0.11.23 replaced. They still work in this " + `release; rewrite them before the next one:
|
|
572
|
+
` + lines.join(`
|
|
573
|
+
`));
|
|
574
|
+
}
|
|
575
|
+
return manifest;
|
|
576
|
+
}
|
|
577
|
+
|
|
417
578
|
// src/lib/manifest.ts
|
|
418
579
|
var MANIFEST_FILENAMES = [
|
|
419
580
|
"manifest.yaml"
|
|
420
581
|
];
|
|
582
|
+
function hasProjectManifest(dir) {
|
|
583
|
+
return MANIFEST_FILENAMES.some((name) => {
|
|
584
|
+
const candidate = resolve(dir, name);
|
|
585
|
+
try {
|
|
586
|
+
return statSync(candidate).isFile();
|
|
587
|
+
} catch {
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
}
|
|
421
592
|
async function loadManifest(pathOrDir) {
|
|
422
593
|
const { manifest } = await loadManifestWithPath(pathOrDir);
|
|
423
594
|
return manifest;
|
|
@@ -431,8 +602,8 @@ async function loadManifestWithPath(pathOrDir, opts) {
|
|
|
431
602
|
throw new UsageError(`"${p}" has two versions of a setting — yours and the online version — left by a sync. Open it, keep the one you want and delete the marker lines.`);
|
|
432
603
|
}
|
|
433
604
|
try {
|
|
434
|
-
const
|
|
435
|
-
return { manifest, manifestDir: dirname(p), manifestPath: p };
|
|
605
|
+
const parsed = parseYaml(raw) ?? {};
|
|
606
|
+
return { manifest: readOldSpellings(parsed), manifestDir: dirname(p), manifestPath: p };
|
|
436
607
|
} catch (error) {
|
|
437
608
|
if (!(error instanceof YAMLParseError))
|
|
438
609
|
throw error;
|
|
@@ -568,32 +739,37 @@ function resolveCustomPage(c, m) {
|
|
|
568
739
|
return {
|
|
569
740
|
width,
|
|
570
741
|
height,
|
|
571
|
-
tolerance:
|
|
742
|
+
tolerance: 0.5,
|
|
572
743
|
bleed: c.page?.bleed ?? m.page?.bleed,
|
|
573
744
|
safe: c.page?.safe ?? m.page?.safe,
|
|
574
745
|
artSafe: c.page?.artSafe ?? m.page?.artSafe,
|
|
575
|
-
gutter: c.page?.gutter ?? m.page?.gutter
|
|
746
|
+
gutter: c.page?.gutter ?? m.page?.gutter,
|
|
747
|
+
margin: c.page?.margin ?? m.page?.margin
|
|
576
748
|
};
|
|
577
749
|
}
|
|
750
|
+
var MARGIN_EDGES = ["top", "bottom", "inside", "outside"];
|
|
751
|
+
function validatePageMargin(margin, size) {
|
|
752
|
+
const edges = isPlainObject(margin) && Object.keys(margin).every((k) => MARGIN_EDGES.includes(k)) && MARGIN_EDGES.every((e) => isNonNegativePoints(margin[e]));
|
|
753
|
+
if (!edges) {
|
|
754
|
+
throw new UsageError("Manifest field `page.margin` must set all four edges in points (72pt = 1in), for example\n" + ` page:
|
|
755
|
+
margin:
|
|
756
|
+
top: 54
|
|
757
|
+
bottom: 54
|
|
758
|
+
inside: 54
|
|
759
|
+
outside: 54
|
|
760
|
+
` + `got ${JSON.stringify(margin)}.`);
|
|
761
|
+
}
|
|
762
|
+
const m = margin;
|
|
763
|
+
if (m.inside + m.outside >= size.width || m.top + m.bottom >= size.height) {
|
|
764
|
+
throw new UsageError(`Manifest field \`page.margin\` leaves no room for text on a ${size.width} x ${size.height}pt page ` + `(inside ${m.inside} + outside ${m.outside} across, top ${m.top} + bottom ${m.bottom} down).`);
|
|
765
|
+
}
|
|
766
|
+
return m;
|
|
767
|
+
}
|
|
578
768
|
function resolveWithPreset(cliOverrides, manifest, preset) {
|
|
579
769
|
const m = manifest;
|
|
580
770
|
const c = cliOverrides;
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
const stale = [
|
|
584
|
-
legacy.output !== undefined ? "`output`" : null,
|
|
585
|
-
legacy.source?.assets !== undefined ? "`source.assets`" : null
|
|
586
|
-
].filter(Boolean);
|
|
587
|
-
throw new UsageError(`Manifest field(s) ${stale.join(" and ")} are no longer supported — remove them.
|
|
588
|
-
` + " Output goes to `dist/<title-slug>/` automatically (use `--out` for a one-off location).\n" + " Assets are discovered from what your book actually references, so no list is needed.");
|
|
589
|
-
}
|
|
590
|
-
if ((m.validate?.source?.allowedCallouts?.length ?? 0) > 0 || (c.validate?.source?.allowedCallouts?.length ?? 0) > 0) {
|
|
591
|
-
warnOnce("allowed-callouts-deprecated", "[gutterpress] manifest field `validate.source.allowedCallouts` is " + "deprecated and ignored. The `:::` container syntax it gated was " + "removed 2026-05-17. See docs/migrations/2026-05-removing-container-syntax.md.");
|
|
592
|
-
}
|
|
593
|
-
for (const key of ["minImageDpi", "allowedColorSpaces"]) {
|
|
594
|
-
if (m.validate?.assets?.[key] !== undefined || c.validate?.assets?.[key] !== undefined) {
|
|
595
|
-
warnOnce(`assets-${key}-deprecated`, `[gutterpress] manifest field \`validate.assets.${key}\` is deprecated and ` + "ignored. The source-image check it configured was removed; the build " + "and the PDF checks measure what actually prints.");
|
|
596
|
-
}
|
|
771
|
+
if (m.output !== undefined) {
|
|
772
|
+
throw new UsageError("Manifest field `output` is no longer supported — remove it.\n" + " Output goes to `dist/<title-slug>/` automatically (use `--out` for a one-off location).");
|
|
597
773
|
}
|
|
598
774
|
const removed = m;
|
|
599
775
|
if (removed.engine !== undefined) {
|
|
@@ -627,6 +803,9 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
|
|
|
627
803
|
throw new UsageError("Manifest field `extensions` must be a list of specifiers.");
|
|
628
804
|
}
|
|
629
805
|
const extensions = rawExtensions.map((entry, index) => ({ entry, index })).filter(({ entry }) => typeof entry === "string" || entry?.enabled !== false).map(({ entry, index }) => normalizeExtensionEntry(entry, index));
|
|
806
|
+
const page = preset.page ? mergeShape(c.page, m.page, preset.page) : resolveCustomPage(c, m);
|
|
807
|
+
if (page.margin !== undefined && page.margin !== null)
|
|
808
|
+
validatePageMargin(page.margin, page);
|
|
630
809
|
return {
|
|
631
810
|
title: c.title ?? m.title ?? "Document",
|
|
632
811
|
authors: c.authors ?? m.authors ?? [],
|
|
@@ -636,25 +815,37 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
|
|
|
636
815
|
styles: c.styles ?? m.styles,
|
|
637
816
|
extensions,
|
|
638
817
|
targets: resolveTargets(c.targets ?? m.targets, preset.defaultTargets),
|
|
639
|
-
|
|
818
|
+
chapters: c.chapters ?? m.chapters ?? preset.chapters,
|
|
819
|
+
checks: { ...preset.checks, ...resolveCheckLevels(m.checks), ...resolveCheckLevels(c.checks) },
|
|
640
820
|
pdfx: mergeShape(c.pdfx, m.pdfx, preset.pdfx),
|
|
641
821
|
print: mergeShape(c.print, m.print, preset.print),
|
|
642
|
-
page
|
|
643
|
-
ink:
|
|
644
|
-
|
|
645
|
-
...mergeShape(c.lint, m.lint, preset.lint),
|
|
646
|
-
rules: { ...preset.lint.rules, ...m.lint?.rules, ...c.lint?.rules }
|
|
647
|
-
},
|
|
648
|
-
validate: {
|
|
649
|
-
enabled: c.validate?.enabled ?? m.validate?.enabled ?? preset.validate.enabled,
|
|
650
|
-
checks: { ...preset.validate.checks, ...m.validate?.checks, ...c.validate?.checks },
|
|
651
|
-
source: mergeShape(c.validate?.source, m.validate?.source, preset.validate.source),
|
|
652
|
-
assets: mergeShape(c.validate?.assets, m.validate?.assets, preset.validate.assets),
|
|
653
|
-
pdf: mergeShape(c.validate?.pdf, m.validate?.pdf, preset.validate.pdf),
|
|
654
|
-
heuristics: mergeShape(c.validate?.heuristics, m.validate?.heuristics, preset.validate.heuristics)
|
|
655
|
-
}
|
|
822
|
+
page,
|
|
823
|
+
ink: structuredClone(preset.ink),
|
|
824
|
+
validate: structuredClone(preset.validate)
|
|
656
825
|
};
|
|
657
826
|
}
|
|
827
|
+
function resolveCheckLevels(input) {
|
|
828
|
+
if (input === undefined || input === null)
|
|
829
|
+
return {};
|
|
830
|
+
if (!isPlainObject(input)) {
|
|
831
|
+
throw new UsageError("Manifest field `checks` must map check ids to off, on, info, warn or error.");
|
|
832
|
+
}
|
|
833
|
+
const out = {};
|
|
834
|
+
for (const [id, value] of Object.entries(input)) {
|
|
835
|
+
if (value === false || value === "off")
|
|
836
|
+
out[id] = "off";
|
|
837
|
+
else if (value === true || value === "on")
|
|
838
|
+
out[id] = "on";
|
|
839
|
+
else if (value === "warn" || value === "warning")
|
|
840
|
+
out[id] = "warning";
|
|
841
|
+
else if (value === "info" || value === "error")
|
|
842
|
+
out[id] = value;
|
|
843
|
+
else {
|
|
844
|
+
throw new UsageError(`Manifest field \`checks.${id}\` must be off, on, info, warn or error, got ${JSON.stringify(value)}.`);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
return out;
|
|
848
|
+
}
|
|
658
849
|
|
|
659
850
|
// src/lib/manifest-doc.ts
|
|
660
851
|
function resolveManifestPath(projectDir) {
|
|
@@ -703,6 +894,13 @@ function ensureSeq(doc, key) {
|
|
|
703
894
|
}
|
|
704
895
|
return seq;
|
|
705
896
|
}
|
|
897
|
+
function scalarString(item) {
|
|
898
|
+
if (item && typeof item === "object" && "value" in item) {
|
|
899
|
+
const v = item.value;
|
|
900
|
+
return typeof v === "string" ? v : null;
|
|
901
|
+
}
|
|
902
|
+
return typeof item === "string" ? item : null;
|
|
903
|
+
}
|
|
706
904
|
|
|
707
905
|
// src/lib/npm-plugin-installer.ts
|
|
708
906
|
import { createHash, randomUUID as randomUUID2, timingSafeEqual } from "node:crypto";
|
|
@@ -1373,6 +1571,7 @@ function ignoreStatus(gitignore, entry) {
|
|
|
1373
1571
|
return dir.unignored || file.unignored ? "negated" : "open";
|
|
1374
1572
|
}
|
|
1375
1573
|
var PROJECT_GITIGNORE_ENTRIES = ["dist/", "extensions/npm/"];
|
|
1574
|
+
var LEGACY_NPM_LINE = /^\/?plugins\/npm\/?[ \t]*\r?\n?/gm;
|
|
1376
1575
|
async function ensureProjectGitignore(projectDir) {
|
|
1377
1576
|
const gitignorePath = path3.join(projectDir, ".gitignore");
|
|
1378
1577
|
let existing = null;
|
|
@@ -1389,10 +1588,11 @@ async function ensureProjectGitignore(projectDir) {
|
|
|
1389
1588
|
negated.push(entry);
|
|
1390
1589
|
}
|
|
1391
1590
|
if (missing.length > 0) {
|
|
1392
|
-
const
|
|
1591
|
+
const kept = missing.includes("extensions/npm/") ? (existing ?? "").replace(LEGACY_NPM_LINE, "") : existing ?? "";
|
|
1592
|
+
const lead = kept.length > 0 && !kept.endsWith(`
|
|
1393
1593
|
`) ? `
|
|
1394
1594
|
` : "";
|
|
1395
|
-
await writeFile(gitignorePath, `${
|
|
1595
|
+
await writeFile(gitignorePath, `${kept}${lead}${missing.join(`
|
|
1396
1596
|
`)}
|
|
1397
1597
|
`, "utf8");
|
|
1398
1598
|
}
|
|
@@ -1491,7 +1691,6 @@ async function describeFolder(dir, base, fallbackLabel, labelFrom = prettify) {
|
|
|
1491
1691
|
...meta.description?.trim() ? { description: meta.description.trim() } : {},
|
|
1492
1692
|
...meta.author?.trim() ? { author: meta.author.trim() } : {},
|
|
1493
1693
|
...meta.preview !== undefined ? { preview: meta.preview } : {},
|
|
1494
|
-
...meta.tokensFile?.trim() ? { tokensFile: meta.tokensFile.trim() } : {},
|
|
1495
1694
|
...styles.length > 0 ? { styles } : {},
|
|
1496
1695
|
carries: extensionCarries(meta),
|
|
1497
1696
|
dir
|
|
@@ -1625,6 +1824,29 @@ async function setExtensionEnabled(projectDir, use, enabled) {
|
|
|
1625
1824
|
await writeManifestDoc(file, doc);
|
|
1626
1825
|
});
|
|
1627
1826
|
}
|
|
1827
|
+
async function reorderExtensions(projectDir, order) {
|
|
1828
|
+
return withMutationLock(projectDir, async () => {
|
|
1829
|
+
const { doc, file } = await loadManifestDoc(projectDir);
|
|
1830
|
+
const seq = doc.get("extensions", true);
|
|
1831
|
+
if (!isSeq2(seq))
|
|
1832
|
+
throw new Error(`No extensions are configured in ${path4.basename(file)}.`);
|
|
1833
|
+
const items = seq.items;
|
|
1834
|
+
const current = items.map((item) => entryUse(item));
|
|
1835
|
+
const wanted = order.map((u) => u.trim());
|
|
1836
|
+
const sameSet = current.length === wanted.length && [...current].sort().join(`
|
|
1837
|
+
`) === [...wanted].sort().join(`
|
|
1838
|
+
`);
|
|
1839
|
+
if (!sameSet) {
|
|
1840
|
+
throw new Error("The new order must list every configured extension exactly once — reload the list and try again.");
|
|
1841
|
+
}
|
|
1842
|
+
const remaining = [...items];
|
|
1843
|
+
seq.items = wanted.map((u) => {
|
|
1844
|
+
const i = remaining.findIndex((item) => entryUse(item) === u);
|
|
1845
|
+
return remaining.splice(i, 1)[0];
|
|
1846
|
+
});
|
|
1847
|
+
await writeManifestDoc(file, doc);
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1628
1850
|
async function removeExtension(projectDir, use) {
|
|
1629
1851
|
return withMutationLock(projectDir, async () => {
|
|
1630
1852
|
const { doc, file } = await loadManifestDoc(projectDir);
|
|
@@ -1763,6 +1985,7 @@ async function vendorNpmExtension(projectDir, input, exportName, options, commit
|
|
|
1763
1985
|
await finalizeNpmPluginInstall(installed).catch((error) => {
|
|
1764
1986
|
warnings.push(`The extension was installed, but an old backup could not be removed: ` + `${error instanceof Error ? error.message : String(error)}`);
|
|
1765
1987
|
});
|
|
1988
|
+
warnings.push(...await retireLegacyCopies(projectDir, installed.name));
|
|
1766
1989
|
try {
|
|
1767
1990
|
const { negated } = await ensureProjectGitignore(projectDir);
|
|
1768
1991
|
if (negated.includes("extensions/npm/")) {
|
|
@@ -1773,6 +1996,19 @@ async function vendorNpmExtension(projectDir, input, exportName, options, commit
|
|
|
1773
1996
|
}
|
|
1774
1997
|
return { name: installed.name, version: installed.version, use, warnings };
|
|
1775
1998
|
}
|
|
1999
|
+
async function retireLegacyCopies(projectDir, name) {
|
|
2000
|
+
const legacyPackageDir = path4.dirname(vendoredNpmPluginRoots(projectDir, name, "0.0.0")[1]);
|
|
2001
|
+
const legacyNpmDir = path4.dirname(legacyPackageDir);
|
|
2002
|
+
try {
|
|
2003
|
+
await rm3(legacyPackageDir, { recursive: true, force: true });
|
|
2004
|
+
await removeEmptyDirs([path4.dirname(legacyNpmDir), legacyNpmDir]);
|
|
2005
|
+
} catch (error) {
|
|
2006
|
+
return [
|
|
2007
|
+
`The extension was installed, but the copy an older version of Gutterpress kept under plugins/npm/ ` + `could not be removed: ${error instanceof Error ? error.message : String(error)}`
|
|
2008
|
+
];
|
|
2009
|
+
}
|
|
2010
|
+
return [];
|
|
2011
|
+
}
|
|
1776
2012
|
async function pinnedVersionsOf(projectDir, name) {
|
|
1777
2013
|
const { doc } = await loadManifestDoc(projectDir);
|
|
1778
2014
|
const seq = doc.get("extensions", true);
|
|
@@ -1870,6 +2106,9 @@ async function latestNpmVersion(name, options) {
|
|
|
1870
2106
|
}
|
|
1871
2107
|
return latest;
|
|
1872
2108
|
}
|
|
2109
|
+
async function listNpmVersions(name, options = {}) {
|
|
2110
|
+
return (await fetchNpmVersions(name, options)).versions.sort(semverRcompare);
|
|
2111
|
+
}
|
|
1873
2112
|
async function checkExtensionUpdates(projectDir, options = {}) {
|
|
1874
2113
|
const entries = await listProjectExtensions(projectDir);
|
|
1875
2114
|
const pinned = entries.filter((e) => e.kind === "npm" && e.version);
|
|
@@ -1900,6 +2139,14 @@ async function updateExtensions(projectDir, options = {}) {
|
|
|
1900
2139
|
}
|
|
1901
2140
|
return updated;
|
|
1902
2141
|
}
|
|
2142
|
+
function listBundledExtensions() {
|
|
2143
|
+
return BUNDLED_EXTENSIONS.map((b) => ({
|
|
2144
|
+
use: b.name,
|
|
2145
|
+
label: b.label,
|
|
2146
|
+
description: b.description,
|
|
2147
|
+
look: b.css !== undefined
|
|
2148
|
+
}));
|
|
2149
|
+
}
|
|
1903
2150
|
async function uniqueExtensionId(projectDir, base) {
|
|
1904
2151
|
const slug = slugify(base) || "extension";
|
|
1905
2152
|
let candidate = slug;
|
|
@@ -1908,6 +2155,51 @@ async function uniqueExtensionId(projectDir, base) {
|
|
|
1908
2155
|
}
|
|
1909
2156
|
return candidate;
|
|
1910
2157
|
}
|
|
2158
|
+
async function validateProjectExtensions(projectDir) {
|
|
2159
|
+
const out = [];
|
|
2160
|
+
for (const entry of await listProjectExtensions(projectDir)) {
|
|
2161
|
+
if (!entry.enabled) {
|
|
2162
|
+
out.push({ use: entry.use, kind: entry.kind, enabled: false, ok: true });
|
|
2163
|
+
continue;
|
|
2164
|
+
}
|
|
2165
|
+
try {
|
|
2166
|
+
await loadPlugin(extensionConfigFor(entry), projectDir);
|
|
2167
|
+
out.push({ use: entry.use, kind: entry.kind, enabled: true, ok: true });
|
|
2168
|
+
} catch (e) {
|
|
2169
|
+
out.push({
|
|
2170
|
+
use: entry.use,
|
|
2171
|
+
kind: entry.kind,
|
|
2172
|
+
enabled: true,
|
|
2173
|
+
ok: false,
|
|
2174
|
+
error: e instanceof Error ? e.message : String(e)
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
return out;
|
|
2179
|
+
}
|
|
2180
|
+
async function readExtensionCss(projectDir, use) {
|
|
2181
|
+
const direct = bundledExtension(use)?.css;
|
|
2182
|
+
if (direct !== undefined)
|
|
2183
|
+
return direct;
|
|
2184
|
+
const entry = await describeExtension(projectDir, use);
|
|
2185
|
+
if (!entry)
|
|
2186
|
+
throw new Error(`Extension "${use}" is not in the manifest.`);
|
|
2187
|
+
return readEntryCss(entry);
|
|
2188
|
+
}
|
|
2189
|
+
async function readEntryCss(entry) {
|
|
2190
|
+
const direct = bundledExtension(entry.use)?.css;
|
|
2191
|
+
if (direct !== undefined)
|
|
2192
|
+
return direct;
|
|
2193
|
+
if (!entry.dir)
|
|
2194
|
+
throw new Error(`Extension "${entry.use}" has no stylesheets to preview.`);
|
|
2195
|
+
const meta = await readExtensionMeta(entry.dir);
|
|
2196
|
+
const sheets = resolveExtension(entry.dir, meta, `Extension "${entry.use}"`).styles ?? [];
|
|
2197
|
+
if (sheets.length === 0)
|
|
2198
|
+
throw new Error(`Extension "${entry.use}" declares no stylesheets.`);
|
|
2199
|
+
return (await Promise.all(sheets.map((sheet) => readFile5(sheet, "utf8")))).join(`
|
|
2200
|
+
|
|
2201
|
+
`);
|
|
2202
|
+
}
|
|
1911
2203
|
|
|
1912
2204
|
// src/lib/extension-restore.ts
|
|
1913
2205
|
var OFFLINE_REASON = "you appear to be offline (npm can't be reached)";
|
|
@@ -2026,4 +2318,4 @@ async function restoreForCommand(dir, options) {
|
|
|
2026
2318
|
log.warn(problem);
|
|
2027
2319
|
}
|
|
2028
2320
|
|
|
2029
|
-
export { PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, resolveTargets, applyDefaultPdfStrictChecks, MANIFEST_FILENAMES, loadManifest, loadManifestWithPath, resolveConfig, resolveConfigForTarget, FriendlyHttpError, withFetchTimeout, loadManifestDoc, writeManifestDoc, npmRegistryUrl, ensureProjectGitignore, listProjectExtensions, setExtensionEnabled, removeExtension, addExtension, checkExtensionUpdates, updateExtensions, uniqueExtensionId, restorePinnedExtensions, restoreFailureMessage, restoreOfflineMessage, restoreForCommand };
|
|
2321
|
+
export { isPositivePoints, isNonNegativePoints, round3, cssLength, formatPageSize, printAreaMeta, pageBoxes, judgeRect, DEBOUNCE, UsageError, resolvePort, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, resolveTargets, applyDefaultPdfStrictChecks, MANIFEST_FILENAMES, hasProjectManifest, loadManifest, loadManifestWithPath, resolveConfig, resolveConfigForTarget, MARGIN_EDGES, validatePageMargin, FriendlyHttpError, withFetchTimeout, loadManifestDoc, writeManifestDoc, scalarString, DEFAULT_NPM_REGISTRY, npmRegistryUrl, ensureProjectGitignore, sameExtension, listProjectExtensions, describeExtension, extensionConfigFor, setExtensionEnabled, reorderExtensions, removeExtension, addExtension, listNpmVersions, checkExtensionUpdates, updateExtensions, listBundledExtensions, uniqueExtensionId, validateProjectExtensions, readExtensionCss, readEntryCss, restorePinnedExtensions, restoreFailureMessage, restoreOfflineMessage, restoreForCommand };
|