gutterpress 0.11.6-alpha.3 → 0.11.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/index.d.ts +3 -3
- package/dist/api/index.js +9 -5
- package/dist/{audit-t4r954ff.js → audit-vbyr2yaa.js} +4 -4
- package/dist/{build-8gx90w22.js → build-j0e2hrza.js} +4 -4
- package/dist/{cli-g6vshy3f.js → cli-02sh3r8x.js} +393 -968
- package/dist/{cli-5c2xfsmz.js → cli-1bccnym3.js} +1 -1
- package/dist/cli-4tx0szk7.js +1251 -0
- package/dist/{cli-rra50cp6.js → cli-emp8wz36.js} +1 -1
- package/dist/cli.js +14 -14
- package/dist/{doctor-b1awtn7m.js → doctor-radk1tcg.js} +2 -2
- package/dist/{ext-mxxz3p8t.js → ext-vtnqbct0.js} +4 -4
- package/dist/index-7mqst9tg.js +1302 -0
- package/dist/{index-0h00ve4y.js → index-yzcd0ezr.js} +468 -1042
- package/dist/index.js +9 -5
- package/dist/lib/project-scaffold.d.ts +20 -0
- package/dist/lib/source-provider.d.ts +24 -0
- package/dist/{lint-vyxf5zmw.js → lint-692hba2b.js} +4 -4
- package/dist/{new-r3s74yfv.js → new-431dez0g.js} +4 -4
- package/dist/{preflight-j9e9m4ea.js → preflight-9e0xm760.js} +4 -4
- package/dist/{preview-aprv0c49.js → preview-j429jbx1.js} +4 -4
- package/dist/{publish-4y1kztbd.js → publish-hr60t4j2.js} +4 -4
- package/dist/{source-provider-exy1wakv.js → source-provider-gy3cn7kw.js} +3 -1
- package/dist/{source-provider-y9ktprbx.js → source-provider-m5wes47h.js} +3 -1
- package/dist/{validate-sf6hga9e.js → validate-g1ckwmnz.js} +4 -4
- package/package.json +1 -1
- package/dist/cli-c5360prx.js +0 -645
- package/dist/index-8xkw284t.js +0 -696
|
@@ -13,12 +13,12 @@ import {
|
|
|
13
13
|
stampCreator,
|
|
14
14
|
stripAnnotations,
|
|
15
15
|
warn
|
|
16
|
-
} from "./cli-
|
|
16
|
+
} from "./cli-emp8wz36.js";
|
|
17
17
|
import {
|
|
18
18
|
DEBOUNCE,
|
|
19
19
|
UsageError,
|
|
20
20
|
resolvePort
|
|
21
|
-
} from "./cli-
|
|
21
|
+
} from "./cli-1bccnym3.js";
|
|
22
22
|
import {
|
|
23
23
|
MARGIN_BOX_IGNORED_PROPERTIES,
|
|
24
24
|
RENDER_TIMEOUT_MS,
|
|
@@ -41,9 +41,27 @@ import {
|
|
|
41
41
|
BuildError
|
|
42
42
|
} from "./cli-46ycxe6r.js";
|
|
43
43
|
import {
|
|
44
|
+
PLUGINS_DIR,
|
|
45
|
+
VENDORED_NPM_DIR,
|
|
46
|
+
VENDOR_RECEIPT_FILE,
|
|
47
|
+
VENDOR_RECEIPT_VERSION,
|
|
48
|
+
assertWindowsSafeRelativePath,
|
|
49
|
+
computeVendorTreeDigest,
|
|
44
50
|
hasUncommittedChanges,
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
isExactNpmVersion,
|
|
52
|
+
isValidNpmPackageName,
|
|
53
|
+
listWorkdirChanges,
|
|
54
|
+
npmRegistryUrl,
|
|
55
|
+
packageEntryFormat,
|
|
56
|
+
packageResolutionTargets,
|
|
57
|
+
parseNpmPluginSpec,
|
|
58
|
+
resolvePackageEntry,
|
|
59
|
+
resolveVendoredPluginInstallRoot,
|
|
60
|
+
vendoredNpmPluginPackageDir,
|
|
61
|
+
vendoredNpmPluginRoot,
|
|
62
|
+
verifyVendoredPlugin,
|
|
63
|
+
windowsPathKey
|
|
64
|
+
} from "./cli-4tx0szk7.js";
|
|
47
65
|
import {
|
|
48
66
|
detectProjectSource,
|
|
49
67
|
repoSubPath
|
|
@@ -52,599 +70,6 @@ import {
|
|
|
52
70
|
__require
|
|
53
71
|
} from "./cli-37x76zdn.js";
|
|
54
72
|
|
|
55
|
-
// src/lib/plugin-vendor.ts
|
|
56
|
-
import { createHash } from "node:crypto";
|
|
57
|
-
import { createReadStream } from "node:fs";
|
|
58
|
-
import { lstat, readFile, readdir, realpath } from "node:fs/promises";
|
|
59
|
-
import path from "node:path";
|
|
60
|
-
import { exports as resolveExports } from "resolve.exports";
|
|
61
|
-
import { satisfies, validRange } from "semver";
|
|
62
|
-
|
|
63
|
-
// src/lib/npm-registry.ts
|
|
64
|
-
var DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
|
|
65
|
-
var ENV_OVERRIDE = "GUTTERPRESS_NPM_REGISTRY";
|
|
66
|
-
function npmRegistryUrl() {
|
|
67
|
-
const configured = process.env[ENV_OVERRIDE]?.trim();
|
|
68
|
-
if (!configured)
|
|
69
|
-
return DEFAULT_NPM_REGISTRY;
|
|
70
|
-
let scheme;
|
|
71
|
-
try {
|
|
72
|
-
scheme = new URL(configured).protocol;
|
|
73
|
-
} catch {
|
|
74
|
-
throw new Error(`Invalid ${ENV_OVERRIDE} URL "${configured}".`);
|
|
75
|
-
}
|
|
76
|
-
if (scheme !== "http:" && scheme !== "https:") {
|
|
77
|
-
throw new Error(`${ENV_OVERRIDE} must be an http(s) URL — got "${scheme}".`);
|
|
78
|
-
}
|
|
79
|
-
return configured.replace(/\/+$/, "");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// src/lib/plugin-vendor.ts
|
|
83
|
-
var PLUGINS_DIR = "plugins";
|
|
84
|
-
var VENDORED_NPM_DIR = "npm";
|
|
85
|
-
var VENDOR_RECEIPT_FILE = ".gutterpress-install.json";
|
|
86
|
-
var VENDOR_RECEIPT_VERSION = 2;
|
|
87
|
-
var NPM_SEGMENT = /^[a-z0-9][a-z0-9._~-]*$/;
|
|
88
|
-
var EXACT_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
89
|
-
var WINDOWS_RESERVED = /^(con|prn|aux|nul|clock\$|conin\$|conout\$|com[1-9¹²³]|lpt[1-9¹²³])(?:\..*)?$/i;
|
|
90
|
-
var MAX_VERIFIED_FILES = 50000;
|
|
91
|
-
var MAX_VERIFIED_BYTES = 512 * 1024 * 1024;
|
|
92
|
-
function isValidNpmPackageName(name) {
|
|
93
|
-
if (!name || name.length > 214 || name !== name.toLowerCase())
|
|
94
|
-
return false;
|
|
95
|
-
if (name.startsWith("@")) {
|
|
96
|
-
const parts = name.slice(1).split("/");
|
|
97
|
-
return parts.length === 2 && parts.every((part) => NPM_SEGMENT.test(part));
|
|
98
|
-
}
|
|
99
|
-
return !name.includes("/") && NPM_SEGMENT.test(name);
|
|
100
|
-
}
|
|
101
|
-
function parseNpmPluginSpec(input) {
|
|
102
|
-
const spec = input.trim();
|
|
103
|
-
if (!spec)
|
|
104
|
-
throw new Error("An npm package name is required.");
|
|
105
|
-
let name = spec;
|
|
106
|
-
let selector;
|
|
107
|
-
if (spec.startsWith("@")) {
|
|
108
|
-
const slash = spec.indexOf("/");
|
|
109
|
-
const versionAt = spec.lastIndexOf("@");
|
|
110
|
-
if (slash > 1 && versionAt > slash) {
|
|
111
|
-
name = spec.slice(0, versionAt);
|
|
112
|
-
selector = spec.slice(versionAt + 1);
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
const versionAt = spec.lastIndexOf("@");
|
|
116
|
-
if (versionAt > 0) {
|
|
117
|
-
name = spec.slice(0, versionAt);
|
|
118
|
-
selector = spec.slice(versionAt + 1);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if (!isValidNpmPackageName(name)) {
|
|
122
|
-
throw new Error(`"${name}" is not a valid npm package name. Use a name like ` + "markdown-it-highlightjs or @scope/markdown-it-plugin.");
|
|
123
|
-
}
|
|
124
|
-
if (selector !== undefined && !selector) {
|
|
125
|
-
throw new Error(`The package spec "${spec}" is missing a selector after @.`);
|
|
126
|
-
}
|
|
127
|
-
return selector === undefined ? { name } : { name, selector };
|
|
128
|
-
}
|
|
129
|
-
function isExactNpmVersion(version) {
|
|
130
|
-
return EXACT_VERSION.test(version);
|
|
131
|
-
}
|
|
132
|
-
function encodePathPart(value) {
|
|
133
|
-
return encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
134
|
-
}
|
|
135
|
-
function vendoredNpmPluginRoot(projectDir, name, version) {
|
|
136
|
-
return path.join(projectDir, PLUGINS_DIR, VENDORED_NPM_DIR, encodePathPart(name), encodePathPart(version));
|
|
137
|
-
}
|
|
138
|
-
function vendoredNpmPluginPackageDir(installRoot, name) {
|
|
139
|
-
return path.join(installRoot, "node_modules", ...name.split("/"));
|
|
140
|
-
}
|
|
141
|
-
function toPosixPath(value) {
|
|
142
|
-
return value.split(path.sep).join("/");
|
|
143
|
-
}
|
|
144
|
-
function packageEntryFormat(manifest, relativeEntry) {
|
|
145
|
-
const extension = path.posix.extname(relativeEntry);
|
|
146
|
-
return extension === ".cjs" || extension === ".json" || extension !== ".mjs" && manifest.type !== "module" ? "commonjs" : "module";
|
|
147
|
-
}
|
|
148
|
-
function assertWindowsSafeRelativePath(relative) {
|
|
149
|
-
if (!relative || relative.includes("\\") || relative.startsWith("/") || /^[a-zA-Z]:/.test(relative)) {
|
|
150
|
-
throw new Error(`Unsafe package path: ${relative || "(empty)"}`);
|
|
151
|
-
}
|
|
152
|
-
const normalized = relative.normalize("NFKC");
|
|
153
|
-
const parts = normalized.split("/");
|
|
154
|
-
if (parts.some((part) => !part || part === "." || part === "..")) {
|
|
155
|
-
throw new Error(`Unsafe package path: ${relative}`);
|
|
156
|
-
}
|
|
157
|
-
for (const part of parts) {
|
|
158
|
-
if (part.length > 255 || /[\u0000-\u001f<>:"|?*]/.test(part) || part.endsWith(".") || part.endsWith(" ") || WINDOWS_RESERVED.test(part)) {
|
|
159
|
-
throw new Error(`Package path is invalid on Windows: ${relative}`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
function windowsPathKey(relative) {
|
|
164
|
-
return relative.normalize("NFKC").toLowerCase();
|
|
165
|
-
}
|
|
166
|
-
function safeReceiptPath(relative, label) {
|
|
167
|
-
if (typeof relative !== "string")
|
|
168
|
-
throw new Error(`${label} must be a string.`);
|
|
169
|
-
assertWindowsSafeRelativePath(relative);
|
|
170
|
-
return relative;
|
|
171
|
-
}
|
|
172
|
-
function isContained(root, candidate) {
|
|
173
|
-
const relative = path.relative(root, candidate);
|
|
174
|
-
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
175
|
-
}
|
|
176
|
-
function object(value) {
|
|
177
|
-
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
178
|
-
}
|
|
179
|
-
function stringMap(value) {
|
|
180
|
-
const map = object(value);
|
|
181
|
-
if (!map)
|
|
182
|
-
return {};
|
|
183
|
-
const result = {};
|
|
184
|
-
for (const [name, selector] of Object.entries(map)) {
|
|
185
|
-
if (typeof selector === "string" && selector.trim())
|
|
186
|
-
result[name] = selector.trim();
|
|
187
|
-
}
|
|
188
|
-
return result;
|
|
189
|
-
}
|
|
190
|
-
function dependencyDeclarations(manifest) {
|
|
191
|
-
const required = stringMap(manifest.dependencies);
|
|
192
|
-
const optional = stringMap(manifest.optionalDependencies);
|
|
193
|
-
for (const name of Object.keys(optional))
|
|
194
|
-
delete required[name];
|
|
195
|
-
const peers = stringMap(manifest.peerDependencies);
|
|
196
|
-
const peerMeta = object(manifest.peerDependenciesMeta) ?? {};
|
|
197
|
-
const optionalPeers = {};
|
|
198
|
-
for (const [name, selector] of Object.entries(peers)) {
|
|
199
|
-
if (name in required || name in optional)
|
|
200
|
-
continue;
|
|
201
|
-
if (object(peerMeta[name])?.optional === true)
|
|
202
|
-
optionalPeers[name] = selector;
|
|
203
|
-
else
|
|
204
|
-
required[name] = selector;
|
|
205
|
-
}
|
|
206
|
-
return { required, optional, optionalPeers };
|
|
207
|
-
}
|
|
208
|
-
function unsupportedRegistrySelector(selector) {
|
|
209
|
-
return /^(?:file:|git\+|https?:|ssh:|github:|workspace:|link:|npm:)/i.test(selector);
|
|
210
|
-
}
|
|
211
|
-
async function readPackageJson(packageDir) {
|
|
212
|
-
const packageJsonPath = path.join(packageDir, "package.json");
|
|
213
|
-
const info2 = await lstat(packageJsonPath);
|
|
214
|
-
if (!info2.isFile() || info2.isSymbolicLink()) {
|
|
215
|
-
throw new Error(`Package metadata is not a regular file: ${packageJsonPath}`);
|
|
216
|
-
}
|
|
217
|
-
const parsed = object(JSON.parse(await readFile(packageJsonPath, "utf8")));
|
|
218
|
-
if (!parsed)
|
|
219
|
-
throw new Error(`Package metadata is not an object: ${packageJsonPath}`);
|
|
220
|
-
return parsed;
|
|
221
|
-
}
|
|
222
|
-
async function verifyReceiptEntry(installRoot, packagePath, entry, label) {
|
|
223
|
-
if (entry === undefined)
|
|
224
|
-
return;
|
|
225
|
-
const relative = safeReceiptPath(entry, label);
|
|
226
|
-
if (!relative.startsWith(`${packagePath}/`)) {
|
|
227
|
-
throw new Error(`${label} is outside its package.`);
|
|
228
|
-
}
|
|
229
|
-
const packageDir = path.resolve(installRoot, ...packagePath.split("/"));
|
|
230
|
-
const entryPath = path.resolve(installRoot, ...relative.split("/"));
|
|
231
|
-
const [realPackageDir, realEntry] = await Promise.all([
|
|
232
|
-
realpath(packageDir),
|
|
233
|
-
realpath(entryPath)
|
|
234
|
-
]);
|
|
235
|
-
if (!isContained(realPackageDir, realEntry))
|
|
236
|
-
throw new Error(`${label} escapes its package.`);
|
|
237
|
-
const info2 = await lstat(realEntry);
|
|
238
|
-
if (!info2.isFile() || info2.isSymbolicLink())
|
|
239
|
-
throw new Error(`${label} is not a regular file.`);
|
|
240
|
-
return realEntry;
|
|
241
|
-
}
|
|
242
|
-
function packageResolutionTargets(manifest, subpath, condition) {
|
|
243
|
-
if (manifest.exports !== undefined) {
|
|
244
|
-
try {
|
|
245
|
-
const targets2 = resolveExports(manifest, subpath ? `./${subpath}` : ".", { require: condition === "require" }) ?? [];
|
|
246
|
-
return targets2.map((target) => ({ target, exact: true }));
|
|
247
|
-
} catch {
|
|
248
|
-
return [];
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
if (subpath)
|
|
252
|
-
return [{ target: subpath, exact: false }];
|
|
253
|
-
const targets = [
|
|
254
|
-
...condition === "import" && typeof manifest.module === "string" ? [manifest.module] : [],
|
|
255
|
-
...typeof manifest.main === "string" ? [manifest.main] : [],
|
|
256
|
-
"index.mjs",
|
|
257
|
-
"index.js",
|
|
258
|
-
"index.cjs"
|
|
259
|
-
];
|
|
260
|
-
return [...new Set(targets)].map((target) => ({ target, exact: false }));
|
|
261
|
-
}
|
|
262
|
-
function safePackageTarget(target) {
|
|
263
|
-
if (!target || target.includes("\\") || target.includes("*") || target.includes("?") || target.includes("#")) {
|
|
264
|
-
return null;
|
|
265
|
-
}
|
|
266
|
-
const relative = target.startsWith("./") ? target.slice(2) : target;
|
|
267
|
-
if (!relative || path.isAbsolute(relative))
|
|
268
|
-
return null;
|
|
269
|
-
assertWindowsSafeRelativePath(relative);
|
|
270
|
-
if (relative.normalize("NFKC").split("/").some((part) => part.toLowerCase() === "node_modules")) {
|
|
271
|
-
return null;
|
|
272
|
-
}
|
|
273
|
-
return relative;
|
|
274
|
-
}
|
|
275
|
-
async function resolveEntryCandidate(packageDir, resolution) {
|
|
276
|
-
const relative = safePackageTarget(resolution.target);
|
|
277
|
-
if (!relative)
|
|
278
|
-
return null;
|
|
279
|
-
const base = path.resolve(packageDir, ...relative.split("/"));
|
|
280
|
-
if (!isContained(packageDir, base))
|
|
281
|
-
return null;
|
|
282
|
-
const candidates = resolution.exact ? [base] : [
|
|
283
|
-
base,
|
|
284
|
-
`${base}.mjs`,
|
|
285
|
-
`${base}.js`,
|
|
286
|
-
`${base}.cjs`,
|
|
287
|
-
`${base}.json`,
|
|
288
|
-
path.join(base, "index.mjs"),
|
|
289
|
-
path.join(base, "index.js"),
|
|
290
|
-
path.join(base, "index.cjs"),
|
|
291
|
-
path.join(base, "index.json")
|
|
292
|
-
];
|
|
293
|
-
for (const candidate of candidates) {
|
|
294
|
-
try {
|
|
295
|
-
const info2 = await lstat(candidate);
|
|
296
|
-
if (!info2.isFile() || info2.isSymbolicLink())
|
|
297
|
-
continue;
|
|
298
|
-
const extension = path.extname(candidate);
|
|
299
|
-
if (extension && ![".js", ".mjs", ".cjs", ".json"].includes(extension))
|
|
300
|
-
continue;
|
|
301
|
-
const [realRoot, realCandidate] = await Promise.all([realpath(packageDir), realpath(candidate)]);
|
|
302
|
-
if (!isContained(realRoot, realCandidate))
|
|
303
|
-
continue;
|
|
304
|
-
return toPosixPath(path.relative(packageDir, realCandidate));
|
|
305
|
-
} catch {}
|
|
306
|
-
}
|
|
307
|
-
return null;
|
|
308
|
-
}
|
|
309
|
-
async function resolvePackageEntry(packageDir, packageJson, condition = "import") {
|
|
310
|
-
const manifest = packageJson ?? await readPackageJson(packageDir);
|
|
311
|
-
for (const target of packageResolutionTargets(manifest, "", condition)) {
|
|
312
|
-
const resolved = await resolveEntryCandidate(packageDir, target);
|
|
313
|
-
if (resolved)
|
|
314
|
-
return resolved;
|
|
315
|
-
}
|
|
316
|
-
throw new Error("Package has no contained JavaScript entry point for ESM import.");
|
|
317
|
-
}
|
|
318
|
-
async function listTreeEntries(root) {
|
|
319
|
-
const out = [];
|
|
320
|
-
async function visit(dir) {
|
|
321
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
322
|
-
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
323
|
-
for (const entry of entries) {
|
|
324
|
-
const absolute = path.join(dir, entry.name);
|
|
325
|
-
const relative = toPosixPath(path.relative(root, absolute));
|
|
326
|
-
if (relative === VENDOR_RECEIPT_FILE)
|
|
327
|
-
continue;
|
|
328
|
-
if (entry.isSymbolicLink())
|
|
329
|
-
throw new Error(`Vendor tree contains a symbolic link: ${relative}`);
|
|
330
|
-
if (entry.isDirectory()) {
|
|
331
|
-
out.push({ relative, directory: true });
|
|
332
|
-
await visit(absolute);
|
|
333
|
-
} else if (entry.isFile())
|
|
334
|
-
out.push({ relative, directory: false });
|
|
335
|
-
else
|
|
336
|
-
throw new Error(`Vendor tree contains an unsupported filesystem entry: ${relative}`);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
await visit(root);
|
|
340
|
-
return out;
|
|
341
|
-
}
|
|
342
|
-
async function computeVendorTreeDigest(root) {
|
|
343
|
-
const entries = await listTreeEntries(root);
|
|
344
|
-
const files = entries.filter((entry) => !entry.directory);
|
|
345
|
-
if (files.length > MAX_VERIFIED_FILES) {
|
|
346
|
-
throw new Error(`Vendor tree contains more than ${MAX_VERIFIED_FILES} files.`);
|
|
347
|
-
}
|
|
348
|
-
const seen = new Set;
|
|
349
|
-
const hash = createHash("sha256");
|
|
350
|
-
let bytes = 0;
|
|
351
|
-
for (const entry of entries) {
|
|
352
|
-
const { relative } = entry;
|
|
353
|
-
assertWindowsSafeRelativePath(relative);
|
|
354
|
-
const key = windowsPathKey(relative);
|
|
355
|
-
if (seen.has(key))
|
|
356
|
-
throw new Error(`Vendor tree has a Windows-colliding path: ${relative}`);
|
|
357
|
-
seen.add(key);
|
|
358
|
-
if (entry.directory)
|
|
359
|
-
continue;
|
|
360
|
-
const absolute = path.join(root, ...relative.split("/"));
|
|
361
|
-
const info2 = await lstat(absolute);
|
|
362
|
-
if (!info2.isFile() || info2.isSymbolicLink() || info2.nlink !== 1) {
|
|
363
|
-
throw new Error(`Vendor tree file is not an independent regular file: ${relative}`);
|
|
364
|
-
}
|
|
365
|
-
bytes += info2.size;
|
|
366
|
-
if (bytes > MAX_VERIFIED_BYTES) {
|
|
367
|
-
throw new Error(`Vendor tree exceeds ${Math.round(MAX_VERIFIED_BYTES / 1024 / 1024)}MB.`);
|
|
368
|
-
}
|
|
369
|
-
hash.update(`${Buffer.byteLength(relative)}:${relative}:${info2.size}\x00`);
|
|
370
|
-
for await (const chunk of createReadStream(absolute))
|
|
371
|
-
hash.update(chunk);
|
|
372
|
-
}
|
|
373
|
-
return { digest: hash.digest("hex"), files: files.length, bytes };
|
|
374
|
-
}
|
|
375
|
-
async function assertVendorPackageOwnership(root, packagePaths) {
|
|
376
|
-
const packages = packagePaths.map((relative) => ({
|
|
377
|
-
relative,
|
|
378
|
-
key: windowsPathKey(relative),
|
|
379
|
-
parts: relative.split("/").length
|
|
380
|
-
})).sort((a, b) => b.key.length - a.key.length);
|
|
381
|
-
for (const entry of await listTreeEntries(root)) {
|
|
382
|
-
const actualParts = entry.relative.split("/");
|
|
383
|
-
for (const part of actualParts) {
|
|
384
|
-
if (part.normalize("NFKC").toLowerCase() === "node_modules" && part !== "node_modules") {
|
|
385
|
-
throw new Error(`Vendor tree contains a non-canonical node_modules path: ${entry.relative}`);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
const key = windowsPathKey(entry.relative);
|
|
389
|
-
const structural = packages.some((pkg) => pkg.key === key || pkg.key.startsWith(`${key}/`));
|
|
390
|
-
if (entry.directory && structural)
|
|
391
|
-
continue;
|
|
392
|
-
const owner = packages.find((pkg) => key.startsWith(`${pkg.key}/`));
|
|
393
|
-
if (!owner)
|
|
394
|
-
throw new Error(`Vendor tree path is not owned by a receipt package: ${entry.relative}`);
|
|
395
|
-
const packageRelative = actualParts.slice(owner.parts);
|
|
396
|
-
const nodeModulesAt = packageRelative.findIndex((part) => part.normalize("NFKC").toLowerCase() === "node_modules");
|
|
397
|
-
if (nodeModulesAt < 0)
|
|
398
|
-
continue;
|
|
399
|
-
const generatedEmptyDirectory = entry.directory && nodeModulesAt === 0 && (packageRelative.length === 1 || packageRelative.length === 2 && packageRelative[1].startsWith("@"));
|
|
400
|
-
if (!generatedEmptyDirectory) {
|
|
401
|
-
throw new Error(`Package contains bundled node_modules content: ${entry.relative}`);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
async function recomputeReceiptEntry(packageDir, packagePath, manifest, condition) {
|
|
406
|
-
try {
|
|
407
|
-
const entry = await resolvePackageEntry(packageDir, manifest, condition);
|
|
408
|
-
return `${packagePath}/${entry}`;
|
|
409
|
-
} catch (error2) {
|
|
410
|
-
if (error2 instanceof Error && error2.message.includes("no contained JavaScript entry point")) {
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
throw error2;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
async function resolveVendoredPluginInstallRoot(projectDir, expectedName, expectedVersion) {
|
|
417
|
-
const requestedProjectRoot = path.resolve(projectDir);
|
|
418
|
-
const requestedInstallRoot = vendoredNpmPluginRoot(requestedProjectRoot, expectedName, expectedVersion);
|
|
419
|
-
let installInfo;
|
|
420
|
-
try {
|
|
421
|
-
installInfo = await lstat(requestedInstallRoot);
|
|
422
|
-
} catch (error2) {
|
|
423
|
-
if (error2.code === "ENOENT")
|
|
424
|
-
return null;
|
|
425
|
-
throw error2;
|
|
426
|
-
}
|
|
427
|
-
if (!installInfo.isDirectory() || installInfo.isSymbolicLink()) {
|
|
428
|
-
throw new Error("Vendor install root is not a normal directory.");
|
|
429
|
-
}
|
|
430
|
-
const [realProjectRoot, installRoot] = await Promise.all([
|
|
431
|
-
realpath(requestedProjectRoot),
|
|
432
|
-
realpath(requestedInstallRoot)
|
|
433
|
-
]);
|
|
434
|
-
if (!isContained(realProjectRoot, installRoot)) {
|
|
435
|
-
throw new Error("Vendor install root resolves outside the book folder.");
|
|
436
|
-
}
|
|
437
|
-
return installRoot;
|
|
438
|
-
}
|
|
439
|
-
function reconcileReceiptGraph(receipt, packages, manifests) {
|
|
440
|
-
if (!Array.isArray(receipt.skipped))
|
|
441
|
-
throw new Error("Vendor receipt skipped list is invalid.");
|
|
442
|
-
const declarations = new Map;
|
|
443
|
-
for (const [packagePath, manifest] of manifests) {
|
|
444
|
-
declarations.set(packagePath, dependencyDeclarations(manifest));
|
|
445
|
-
}
|
|
446
|
-
const skippedByEdge = new Map;
|
|
447
|
-
for (const skipped of receipt.skipped) {
|
|
448
|
-
if (!packages.has(skipped.from) || !isValidNpmPackageName(skipped.name) || typeof skipped.selector !== "string" || !["optional", "optional-peer"].includes(skipped.kind) || typeof skipped.reason !== "string") {
|
|
449
|
-
throw new Error("Vendor receipt contains an invalid skipped dependency.");
|
|
450
|
-
}
|
|
451
|
-
const declared = declarations.get(skipped.from);
|
|
452
|
-
const expectedSelector = skipped.kind === "optional" ? declared.optional[skipped.name] : declared.optionalPeers[skipped.name];
|
|
453
|
-
if (expectedSelector !== skipped.selector) {
|
|
454
|
-
throw new Error(`Skipped dependency is not declared as ${skipped.kind}: ${skipped.name}`);
|
|
455
|
-
}
|
|
456
|
-
const key = `${skipped.from}\x00${skipped.name}`;
|
|
457
|
-
if (skippedByEdge.has(key))
|
|
458
|
-
throw new Error(`Duplicate skipped dependency: ${skipped.name}`);
|
|
459
|
-
skippedByEdge.set(key, skipped);
|
|
460
|
-
}
|
|
461
|
-
for (const pkg of receipt.packages) {
|
|
462
|
-
if (!object(pkg.dependencies))
|
|
463
|
-
throw new Error(`Invalid dependency map for ${pkg.name}.`);
|
|
464
|
-
const declared = declarations.get(pkg.path);
|
|
465
|
-
for (const [dependency, target] of Object.entries(pkg.dependencies)) {
|
|
466
|
-
const targetPackage = packages.get(target);
|
|
467
|
-
if (!isValidNpmPackageName(dependency) || !targetPackage || targetPackage.name !== dependency) {
|
|
468
|
-
throw new Error(`Broken dependency edge ${pkg.name} -> ${dependency}.`);
|
|
469
|
-
}
|
|
470
|
-
const selector = declared.required[dependency] ?? declared.optional[dependency];
|
|
471
|
-
if (!selector)
|
|
472
|
-
throw new Error(`Undeclared dependency edge ${pkg.name} -> ${dependency}.`);
|
|
473
|
-
if (unsupportedRegistrySelector(selector)) {
|
|
474
|
-
throw new Error(`Unsupported selector on dependency edge ${pkg.name} -> ${dependency}.`);
|
|
475
|
-
}
|
|
476
|
-
const range = validRange(selector);
|
|
477
|
-
if (range && !satisfies(targetPackage.version, range)) {
|
|
478
|
-
throw new Error(`Dependency edge ${pkg.name} -> ${dependency}@${targetPackage.version} does not satisfy ${selector}.`);
|
|
479
|
-
}
|
|
480
|
-
if (skippedByEdge.has(`${pkg.path}\x00${dependency}`)) {
|
|
481
|
-
throw new Error(`Dependency is both installed and skipped: ${pkg.name} -> ${dependency}.`);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
for (const dependency of Object.keys(declared.required)) {
|
|
485
|
-
if (!(dependency in pkg.dependencies)) {
|
|
486
|
-
throw new Error(`Missing required dependency edge ${pkg.name} -> ${dependency}.`);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
for (const dependency of Object.keys(declared.optional)) {
|
|
490
|
-
if (!(dependency in pkg.dependencies) && !skippedByEdge.has(`${pkg.path}\x00${dependency}`)) {
|
|
491
|
-
throw new Error(`Optional dependency is neither installed nor skipped: ${pkg.name} -> ${dependency}.`);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
for (const dependency of Object.keys(declared.optionalPeers)) {
|
|
495
|
-
if (!skippedByEdge.has(`${pkg.path}\x00${dependency}`)) {
|
|
496
|
-
throw new Error(`Optional peer dependency is not recorded as skipped: ${pkg.name} -> ${dependency}.`);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
async function verifyVendoredPlugin(projectDir, expectedName, expectedVersion) {
|
|
502
|
-
try {
|
|
503
|
-
const installRoot = await resolveVendoredPluginInstallRoot(projectDir, expectedName, expectedVersion);
|
|
504
|
-
if (!installRoot)
|
|
505
|
-
return null;
|
|
506
|
-
const receiptPath = path.join(installRoot, VENDOR_RECEIPT_FILE);
|
|
507
|
-
let raw;
|
|
508
|
-
try {
|
|
509
|
-
const info2 = await lstat(receiptPath);
|
|
510
|
-
if (!info2.isFile() || info2.isSymbolicLink() || info2.nlink !== 1 || info2.size > 1024 * 1024) {
|
|
511
|
-
throw new Error("Vendor receipt is not a small regular file.");
|
|
512
|
-
}
|
|
513
|
-
raw = await readFile(receiptPath, "utf8");
|
|
514
|
-
} catch (error2) {
|
|
515
|
-
if (error2.code === "ENOENT")
|
|
516
|
-
return null;
|
|
517
|
-
throw error2;
|
|
518
|
-
}
|
|
519
|
-
const receipt = JSON.parse(raw);
|
|
520
|
-
if (receipt.schemaVersion !== VENDOR_RECEIPT_VERSION) {
|
|
521
|
-
throw new Error(`Unsupported vendor receipt schema ${String(receipt.schemaVersion)}.`);
|
|
522
|
-
}
|
|
523
|
-
if (receipt.root?.name !== expectedName || receipt.root?.version !== expectedVersion) {
|
|
524
|
-
throw new Error("Vendor receipt identity does not match the manifest entry.");
|
|
525
|
-
}
|
|
526
|
-
if (!Array.isArray(receipt.packages) || receipt.packages.length === 0) {
|
|
527
|
-
throw new Error("Vendor receipt has no package graph.");
|
|
528
|
-
}
|
|
529
|
-
const tree = await computeVendorTreeDigest(installRoot);
|
|
530
|
-
if (receipt.tree?.algorithm !== "sha256" || receipt.tree.digest !== tree.digest || receipt.tree.files !== tree.files || receipt.tree.bytes !== tree.bytes) {
|
|
531
|
-
throw new Error("Vendor tree hash does not match its receipt.");
|
|
532
|
-
}
|
|
533
|
-
const packages = new Map;
|
|
534
|
-
const manifests = new Map;
|
|
535
|
-
const verifiedPackages = [];
|
|
536
|
-
const pathKeys = new Set;
|
|
537
|
-
for (const pkg of receipt.packages) {
|
|
538
|
-
const relative = safeReceiptPath(pkg.path, "Package receipt path");
|
|
539
|
-
const key = windowsPathKey(relative);
|
|
540
|
-
if (pathKeys.has(key))
|
|
541
|
-
throw new Error(`Duplicate package receipt path: ${relative}`);
|
|
542
|
-
pathKeys.add(key);
|
|
543
|
-
if (!isValidNpmPackageName(pkg.name) || !isExactNpmVersion(pkg.version)) {
|
|
544
|
-
throw new Error(`Invalid package identity in receipt: ${pkg.name}@${pkg.version}`);
|
|
545
|
-
}
|
|
546
|
-
const expectedSuffix = `node_modules/${pkg.name}`;
|
|
547
|
-
if (relative !== expectedSuffix && !relative.endsWith(`/${expectedSuffix}`)) {
|
|
548
|
-
throw new Error(`Package receipt path does not match ${pkg.name}: ${relative}`);
|
|
549
|
-
}
|
|
550
|
-
if (typeof pkg.integrity !== "string" || !/^(?:sha512|sha384|sha256|sha1)-(?:hex-)?[A-Za-z0-9+/=]+$/.test(pkg.integrity) || typeof pkg.legacySha1 !== "boolean" || pkg.legacySha1 !== pkg.integrity.startsWith("sha1-")) {
|
|
551
|
-
throw new Error(`Package receipt is missing provenance: ${pkg.name}@${pkg.version}`);
|
|
552
|
-
}
|
|
553
|
-
const tarball = new URL(pkg.tarball);
|
|
554
|
-
if (tarball.origin !== new URL(npmRegistryUrl()).origin || tarball.username || tarball.password) {
|
|
555
|
-
throw new Error(`Package receipt has an invalid tarball URL: ${pkg.name}@${pkg.version}`);
|
|
556
|
-
}
|
|
557
|
-
const packageDir = path.resolve(installRoot, ...relative.split("/"));
|
|
558
|
-
if (!isContained(installRoot, packageDir))
|
|
559
|
-
throw new Error(`Package escapes vendor root: ${relative}`);
|
|
560
|
-
const realPackageDir2 = await realpath(packageDir);
|
|
561
|
-
if (!isContained(installRoot, realPackageDir2)) {
|
|
562
|
-
throw new Error(`Package resolves outside the vendor root: ${relative}`);
|
|
563
|
-
}
|
|
564
|
-
const manifest = await readPackageJson(packageDir);
|
|
565
|
-
if (manifest.name !== pkg.name || manifest.version !== pkg.version) {
|
|
566
|
-
throw new Error(`Installed package identity changed: ${pkg.name}@${pkg.version}`);
|
|
567
|
-
}
|
|
568
|
-
const [expectedEntry, expectedRequireEntry] = await Promise.all([
|
|
569
|
-
recomputeReceiptEntry(packageDir, relative, manifest, "import"),
|
|
570
|
-
recomputeReceiptEntry(packageDir, relative, manifest, "require")
|
|
571
|
-
]);
|
|
572
|
-
if (pkg.entry !== expectedEntry || pkg.requireEntry !== expectedRequireEntry) {
|
|
573
|
-
throw new Error(`Recorded package entries do not match package.json: ${pkg.name}@${pkg.version}`);
|
|
574
|
-
}
|
|
575
|
-
packages.set(relative, pkg);
|
|
576
|
-
manifests.set(relative, manifest);
|
|
577
|
-
verifiedPackages.push({
|
|
578
|
-
path: relative,
|
|
579
|
-
name: pkg.name,
|
|
580
|
-
packageDir: realPackageDir2,
|
|
581
|
-
manifest,
|
|
582
|
-
entryPath: await verifyReceiptEntry(installRoot, relative, pkg.entry, "Package import entry"),
|
|
583
|
-
requireEntryPath: await verifyReceiptEntry(installRoot, relative, pkg.requireEntry, "Package require entry"),
|
|
584
|
-
dependencies: pkg.dependencies
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
await assertVendorPackageOwnership(installRoot, receipt.packages.map((pkg) => pkg.path));
|
|
588
|
-
reconcileReceiptGraph(receipt, packages, manifests);
|
|
589
|
-
const rootPackagePath = safeReceiptPath(receipt.root.packagePath, "Root package path");
|
|
590
|
-
const rootPackage = packages.get(rootPackagePath);
|
|
591
|
-
if (!rootPackage || rootPackage.name !== expectedName || rootPackage.version !== expectedVersion) {
|
|
592
|
-
throw new Error("Vendor receipt root package is missing.");
|
|
593
|
-
}
|
|
594
|
-
const reachablePackages = new Set([rootPackagePath]);
|
|
595
|
-
const pendingPackages = [rootPackagePath];
|
|
596
|
-
while (pendingPackages.length > 0) {
|
|
597
|
-
const current = packages.get(pendingPackages.pop());
|
|
598
|
-
for (const target of Object.values(current.dependencies)) {
|
|
599
|
-
if (reachablePackages.has(target))
|
|
600
|
-
continue;
|
|
601
|
-
reachablePackages.add(target);
|
|
602
|
-
pendingPackages.push(target);
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
if (reachablePackages.size !== receipt.packages.length) {
|
|
606
|
-
throw new Error("Vendor receipt contains a package that is not reachable from its root.");
|
|
607
|
-
}
|
|
608
|
-
const rootManifest = manifests.get(rootPackagePath);
|
|
609
|
-
if (receipt.root.format !== "module" && receipt.root.format !== "commonjs") {
|
|
610
|
-
throw new Error("Vendor receipt root format is invalid.");
|
|
611
|
-
}
|
|
612
|
-
const entryRelative = safeReceiptPath(receipt.root.entry, "Plugin entry path");
|
|
613
|
-
if (!entryRelative.startsWith(`${rootPackagePath}/`)) {
|
|
614
|
-
throw new Error("Plugin entry is outside its package.");
|
|
615
|
-
}
|
|
616
|
-
const expectedRootEntry = receipt.root.format === "commonjs" ? rootPackage.requireEntry ?? rootPackage.entry : rootPackage.entry;
|
|
617
|
-
if (entryRelative !== expectedRootEntry) {
|
|
618
|
-
throw new Error("Plugin entry does not match the package graph entry.");
|
|
619
|
-
}
|
|
620
|
-
if (packageEntryFormat(rootManifest, entryRelative) !== receipt.root.format) {
|
|
621
|
-
throw new Error("Plugin entry format does not match its package metadata.");
|
|
622
|
-
}
|
|
623
|
-
const entryPath = path.resolve(installRoot, ...entryRelative.split("/"));
|
|
624
|
-
const rootPackageDir = path.resolve(installRoot, ...rootPackagePath.split("/"));
|
|
625
|
-
const [realPackageDir, realEntry] = await Promise.all([
|
|
626
|
-
realpath(rootPackageDir),
|
|
627
|
-
realpath(entryPath)
|
|
628
|
-
]);
|
|
629
|
-
if (!isContained(installRoot, realEntry) || !isContained(realPackageDir, realEntry)) {
|
|
630
|
-
throw new Error("Plugin entry resolves outside the verified vendor tree.");
|
|
631
|
-
}
|
|
632
|
-
const entryInfo = await lstat(realEntry);
|
|
633
|
-
if (!entryInfo.isFile() || entryInfo.isSymbolicLink()) {
|
|
634
|
-
throw new Error("Plugin entry is not a regular file.");
|
|
635
|
-
}
|
|
636
|
-
return {
|
|
637
|
-
installRoot,
|
|
638
|
-
entryPath: realEntry,
|
|
639
|
-
format: receipt.root.format,
|
|
640
|
-
packages: verifiedPackages,
|
|
641
|
-
receipt
|
|
642
|
-
};
|
|
643
|
-
} catch (error2) {
|
|
644
|
-
throw new Error(`Vendored plugin "${expectedName}@${expectedVersion}" failed verification: ` + `${error2 instanceof Error ? error2.message : String(error2)} Reinstall it before building.`, { cause: error2 });
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
|
|
648
73
|
// src/lib/extension-specifier.ts
|
|
649
74
|
var BUNDLED_EXTENSIONS = [
|
|
650
75
|
"markdown-it-mark",
|
|
@@ -974,7 +399,7 @@ function applyDefaultPdfStrictChecks(config) {
|
|
|
974
399
|
}
|
|
975
400
|
|
|
976
401
|
// src/lib/manifest.ts
|
|
977
|
-
import { readFile
|
|
402
|
+
import { readFile } from "node:fs/promises";
|
|
978
403
|
import { existsSync, statSync } from "node:fs";
|
|
979
404
|
import { resolve, dirname } from "node:path";
|
|
980
405
|
import { parse as parseYaml, YAMLParseError } from "yaml";
|
|
@@ -989,7 +414,7 @@ async function loadManifestWithPath(pathOrDir, opts) {
|
|
|
989
414
|
const candidates = pathOrDir ? [resolve(pathOrDir), ...MANIFEST_FILENAMES.map((name) => resolve(pathOrDir, name))] : MANIFEST_FILENAMES.map((name) => resolve(name));
|
|
990
415
|
for (const p of candidates) {
|
|
991
416
|
if (existsSync(p) && statSync(p).isFile()) {
|
|
992
|
-
const raw = await
|
|
417
|
+
const raw = await readFile(p, "utf8");
|
|
993
418
|
try {
|
|
994
419
|
const manifest = parseYaml(raw) ?? {};
|
|
995
420
|
return { manifest, manifestDir: dirname(p), manifestPath: p };
|
|
@@ -1283,7 +708,7 @@ function inspectionFailed(checkId, message, extra = {}) {
|
|
|
1283
708
|
}
|
|
1284
709
|
|
|
1285
710
|
// src/lib/pdf-inspect.ts
|
|
1286
|
-
import { stat, readFile as
|
|
711
|
+
import { stat, readFile as readFile2 } from "node:fs/promises";
|
|
1287
712
|
import { getDocumentProxy } from "unpdf";
|
|
1288
713
|
import * as pdfjs from "unpdf/pdfjs";
|
|
1289
714
|
function getOps(mod) {
|
|
@@ -1319,17 +744,17 @@ function evictLru() {
|
|
|
1319
744
|
destroyEntryAfterGrace(evicted);
|
|
1320
745
|
}
|
|
1321
746
|
}
|
|
1322
|
-
async function loadPdf(
|
|
747
|
+
async function loadPdf(path) {
|
|
1323
748
|
let st;
|
|
1324
749
|
try {
|
|
1325
|
-
st = await stat(
|
|
750
|
+
st = await stat(path);
|
|
1326
751
|
} catch {
|
|
1327
752
|
return null;
|
|
1328
753
|
}
|
|
1329
|
-
const hit = docCache.get(
|
|
754
|
+
const hit = docCache.get(path);
|
|
1330
755
|
if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size) {
|
|
1331
|
-
docCache.delete(
|
|
1332
|
-
docCache.set(
|
|
756
|
+
docCache.delete(path);
|
|
757
|
+
docCache.set(path, hit);
|
|
1333
758
|
try {
|
|
1334
759
|
return await hit.doc;
|
|
1335
760
|
} catch {
|
|
@@ -1338,15 +763,15 @@ async function loadPdf(path2) {
|
|
|
1338
763
|
}
|
|
1339
764
|
if (hit)
|
|
1340
765
|
destroyEntry(hit);
|
|
1341
|
-
const docPromise =
|
|
1342
|
-
docCache.delete(
|
|
1343
|
-
docCache.set(
|
|
766
|
+
const docPromise = readFile2(path).then((buf) => getDocumentProxy(new Uint8Array(buf)));
|
|
767
|
+
docCache.delete(path);
|
|
768
|
+
docCache.set(path, { mtimeMs: st.mtimeMs, size: st.size, doc: docPromise });
|
|
1344
769
|
evictLru();
|
|
1345
770
|
try {
|
|
1346
771
|
return await docPromise;
|
|
1347
772
|
} catch {
|
|
1348
|
-
if (docCache.get(
|
|
1349
|
-
docCache.delete(
|
|
773
|
+
if (docCache.get(path)?.doc === docPromise)
|
|
774
|
+
docCache.delete(path);
|
|
1350
775
|
return null;
|
|
1351
776
|
}
|
|
1352
777
|
}
|
|
@@ -1797,9 +1222,9 @@ var check3 = {
|
|
|
1797
1222
|
registerCheck(check3);
|
|
1798
1223
|
|
|
1799
1224
|
// src/lib/pdf-parse.ts
|
|
1800
|
-
import { readFile as
|
|
1225
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
1801
1226
|
async function readPdfBytes(pdfPath) {
|
|
1802
|
-
return
|
|
1227
|
+
return readFile3(pdfPath, "latin1");
|
|
1803
1228
|
}
|
|
1804
1229
|
function parseInkCov(out) {
|
|
1805
1230
|
const lines = out.split(/\r?\n/).filter((l) => l.includes("CMYK"));
|
|
@@ -2370,7 +1795,7 @@ var check15 = {
|
|
|
2370
1795
|
registerCheck(check15);
|
|
2371
1796
|
|
|
2372
1797
|
// src/checks/source/markdownlint.ts
|
|
2373
|
-
import { readFile as
|
|
1798
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
2374
1799
|
import { lint } from "markdownlint/sync";
|
|
2375
1800
|
import { parse as parseYaml2 } from "yaml";
|
|
2376
1801
|
|
|
@@ -2403,7 +1828,7 @@ function stripJsonComments(s) {
|
|
|
2403
1828
|
return s.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1");
|
|
2404
1829
|
}
|
|
2405
1830
|
async function loadConfig(configPath) {
|
|
2406
|
-
const raw = await
|
|
1831
|
+
const raw = await readFile4(configPath, "utf8");
|
|
2407
1832
|
let parsed;
|
|
2408
1833
|
if (configPath.endsWith(".json")) {
|
|
2409
1834
|
parsed = JSON.parse(raw);
|
|
@@ -2464,7 +1889,7 @@ registerCheck(check16);
|
|
|
2464
1889
|
|
|
2465
1890
|
// src/checks/source/htmlhint.ts
|
|
2466
1891
|
import { existsSync as existsSync3 } from "node:fs";
|
|
2467
|
-
import { readFile as
|
|
1892
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
2468
1893
|
import htmlhint from "htmlhint";
|
|
2469
1894
|
var { HTMLHint } = htmlhint;
|
|
2470
1895
|
var CONFIG_NAMES2 = [".htmlhintrc"];
|
|
@@ -2484,8 +1909,8 @@ var check17 = {
|
|
|
2484
1909
|
const resolvedConfig = findConfigFile(ctx.inputDir, CONFIG_NAMES2, configPath);
|
|
2485
1910
|
if (!resolvedConfig && !configPath)
|
|
2486
1911
|
return [];
|
|
2487
|
-
const ruleset = resolvedConfig ? JSON.parse(await
|
|
2488
|
-
const html = await
|
|
1912
|
+
const ruleset = resolvedConfig ? JSON.parse(await readFile5(resolvedConfig, "utf8")) : HTMLHint.defaultRuleset;
|
|
1913
|
+
const html = await readFile5(ctx.htmlPath, "utf8");
|
|
2489
1914
|
const messages = HTMLHint.verify(html, ruleset);
|
|
2490
1915
|
return messages.map((m) => ({
|
|
2491
1916
|
checkId: check17.id,
|
|
@@ -2500,7 +1925,7 @@ var check17 = {
|
|
|
2500
1925
|
registerCheck(check17);
|
|
2501
1926
|
|
|
2502
1927
|
// src/checks/source/stylelint.ts
|
|
2503
|
-
import { readFile as
|
|
1928
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
2504
1929
|
|
|
2505
1930
|
// src/lib/printsafe.ts
|
|
2506
1931
|
import postcss from "postcss";
|
|
@@ -2776,7 +2201,7 @@ var check18 = {
|
|
|
2776
2201
|
for (const file of files) {
|
|
2777
2202
|
let css;
|
|
2778
2203
|
try {
|
|
2779
|
-
css = await
|
|
2204
|
+
css = await readFile6(file, "utf8");
|
|
2780
2205
|
} catch (err) {
|
|
2781
2206
|
results.push({
|
|
2782
2207
|
checkId: check18.id,
|
|
@@ -2803,12 +2228,12 @@ var check18 = {
|
|
|
2803
2228
|
registerCheck(check18);
|
|
2804
2229
|
|
|
2805
2230
|
// src/checks/source/css-ownership.ts
|
|
2806
|
-
import { readFile as
|
|
2231
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
2807
2232
|
|
|
2808
2233
|
// src/lib/style-resolver.ts
|
|
2809
2234
|
import { existsSync as existsSync4 } from "node:fs";
|
|
2810
|
-
import { readdir
|
|
2811
|
-
import
|
|
2235
|
+
import { readdir } from "node:fs/promises";
|
|
2236
|
+
import path from "node:path";
|
|
2812
2237
|
var STYLES_SUBDIR = "styles";
|
|
2813
2238
|
var FALLBACK_PRIORITY = [
|
|
2814
2239
|
"styles/book.css",
|
|
@@ -2818,21 +2243,21 @@ var FALLBACK_PRIORITY = [
|
|
|
2818
2243
|
"css/main.css"
|
|
2819
2244
|
];
|
|
2820
2245
|
function relDisplay(projectDir, absPath) {
|
|
2821
|
-
return
|
|
2246
|
+
return path.relative(projectDir, absPath).split(path.sep).join("/");
|
|
2822
2247
|
}
|
|
2823
2248
|
async function dirEntries(dir) {
|
|
2824
2249
|
try {
|
|
2825
|
-
return await
|
|
2250
|
+
return await readdir(dir, { withFileTypes: true });
|
|
2826
2251
|
} catch {
|
|
2827
2252
|
return [];
|
|
2828
2253
|
}
|
|
2829
2254
|
}
|
|
2830
2255
|
function cssFilesFrom(dir, entries) {
|
|
2831
|
-
return entries.filter((e) => e.isFile() && e.name.toLowerCase().endsWith(".css")).map((e) =>
|
|
2256
|
+
return entries.filter((e) => e.isFile() && e.name.toLowerCase().endsWith(".css")).map((e) => path.join(dir, e.name));
|
|
2832
2257
|
}
|
|
2833
2258
|
async function discoverCssFiles(projectDir) {
|
|
2834
|
-
const stylesDir =
|
|
2835
|
-
const cssDir =
|
|
2259
|
+
const stylesDir = path.join(projectDir, STYLES_SUBDIR);
|
|
2260
|
+
const cssDir = path.join(projectDir, "css");
|
|
2836
2261
|
const [rootEntries, stylesEntries, cssEntries] = await Promise.all([
|
|
2837
2262
|
dirEntries(projectDir),
|
|
2838
2263
|
dirEntries(stylesDir),
|
|
@@ -2855,7 +2280,7 @@ async function resolveActiveStyles(projectDir, manifestStyles) {
|
|
|
2855
2280
|
if (manifest.length > 0)
|
|
2856
2281
|
return manifest;
|
|
2857
2282
|
for (const rel of FALLBACK_PRIORITY) {
|
|
2858
|
-
if (existsSync4(
|
|
2283
|
+
if (existsSync4(path.join(projectDir, rel)))
|
|
2859
2284
|
return [rel];
|
|
2860
2285
|
}
|
|
2861
2286
|
const discovered = await discoverCssFiles(projectDir);
|
|
@@ -2871,7 +2296,7 @@ import postcss2 from "postcss";
|
|
|
2871
2296
|
import { parse as parseYaml3 } from "yaml";
|
|
2872
2297
|
var CONFIG_NAMES3 = [".gutterpress/css-contract.yaml", ".gutterpress/css-contract.yml"];
|
|
2873
2298
|
async function loadContract(configPath) {
|
|
2874
|
-
const raw = await
|
|
2299
|
+
const raw = await readFile7(configPath, "utf8");
|
|
2875
2300
|
const parsed = parseYaml3(raw);
|
|
2876
2301
|
if (parsed && typeof parsed === "object")
|
|
2877
2302
|
return parsed;
|
|
@@ -3022,7 +2447,7 @@ var check19 = {
|
|
|
3022
2447
|
continue;
|
|
3023
2448
|
let css;
|
|
3024
2449
|
try {
|
|
3025
|
-
css = await
|
|
2450
|
+
css = await readFile7(absFile, "utf8");
|
|
3026
2451
|
} catch {
|
|
3027
2452
|
continue;
|
|
3028
2453
|
}
|
|
@@ -3068,9 +2493,9 @@ import { existsSync as existsSync7 } from "node:fs";
|
|
|
3068
2493
|
import { dirname as dirname3, resolve as resolve5 } from "node:path";
|
|
3069
2494
|
|
|
3070
2495
|
// src/lib/asset-inline.ts
|
|
3071
|
-
import { createHash
|
|
3072
|
-
import { readFile as
|
|
3073
|
-
import
|
|
2496
|
+
import { createHash } from "node:crypto";
|
|
2497
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
2498
|
+
import path2 from "node:path";
|
|
3074
2499
|
import postcss3 from "postcss";
|
|
3075
2500
|
var FONT_EXTS = new Set([".woff2", ".woff", ".ttf", ".otf"]);
|
|
3076
2501
|
var HASHED_ASSET_DIR = "assets";
|
|
@@ -3111,19 +2536,19 @@ function decodeRef(ref) {
|
|
|
3111
2536
|
}
|
|
3112
2537
|
}
|
|
3113
2538
|
function toPosix(p) {
|
|
3114
|
-
return p.split(
|
|
2539
|
+
return p.split(path2.sep).join("/");
|
|
3115
2540
|
}
|
|
3116
2541
|
function escapesProjectRoot(projectDir, absPath) {
|
|
3117
|
-
const rel =
|
|
3118
|
-
return rel === ".." || rel.startsWith(`..${
|
|
2542
|
+
const rel = path2.relative(projectDir, absPath);
|
|
2543
|
+
return rel === ".." || rel.startsWith(`..${path2.sep}`) || path2.isAbsolute(rel);
|
|
3119
2544
|
}
|
|
3120
2545
|
function proseImageRefError(ref, projectDir) {
|
|
3121
2546
|
const cleaned = stripUrlSuffix(decodeRef(ref));
|
|
3122
|
-
if (/^file:/i.test(cleaned) ||
|
|
2547
|
+
if (/^file:/i.test(cleaned) || path2.isAbsolute(cleaned)) {
|
|
3123
2548
|
return `Image reference must be relative to the book folder: ${ref}
|
|
3124
2549
|
` + ` Copy the file into your book folder and reference it from there.`;
|
|
3125
2550
|
}
|
|
3126
|
-
if (escapesProjectRoot(projectDir,
|
|
2551
|
+
if (escapesProjectRoot(projectDir, path2.resolve(projectDir, cleaned))) {
|
|
3127
2552
|
return `Image reference points outside the book folder: ${ref}
|
|
3128
2553
|
` + ` Copy the file into your book folder and reference it from there.`;
|
|
3129
2554
|
}
|
|
@@ -3134,7 +2559,7 @@ function mimeFor(ext) {
|
|
|
3134
2559
|
}
|
|
3135
2560
|
async function readOrThrow(absPath, kind, referencedFrom) {
|
|
3136
2561
|
try {
|
|
3137
|
-
return await
|
|
2562
|
+
return await readFile8(absPath);
|
|
3138
2563
|
} catch {
|
|
3139
2564
|
throw new BuildError(`Missing ${kind}: ${absPath}
|
|
3140
2565
|
referenced from ${referencedFrom}
|
|
@@ -3145,7 +2570,7 @@ function dataUri(bytes, ext) {
|
|
|
3145
2570
|
return `data:${mimeFor(ext)};base64,${bytes.toString("base64")}`;
|
|
3146
2571
|
}
|
|
3147
2572
|
function contentHash(bytes) {
|
|
3148
|
-
return
|
|
2573
|
+
return createHash("sha256").update(bytes).digest("hex").slice(0, 16);
|
|
3149
2574
|
}
|
|
3150
2575
|
async function rewriteUrls(value, rewrite) {
|
|
3151
2576
|
const matches = [...value.matchAll(URL_TOKEN_RE)];
|
|
@@ -3193,12 +2618,12 @@ function importTarget(atRule) {
|
|
|
3193
2618
|
return target;
|
|
3194
2619
|
}
|
|
3195
2620
|
async function inlineOne(cssPath, copies, warnings, seen) {
|
|
3196
|
-
const abs =
|
|
2621
|
+
const abs = path2.resolve(cssPath);
|
|
3197
2622
|
if (seen.has(abs))
|
|
3198
2623
|
return "";
|
|
3199
2624
|
seen.add(abs);
|
|
3200
2625
|
const source = await readOrThrow(abs, "stylesheet", "the book manifest");
|
|
3201
|
-
const cssDir =
|
|
2626
|
+
const cssDir = path2.dirname(abs);
|
|
3202
2627
|
let root;
|
|
3203
2628
|
try {
|
|
3204
2629
|
root = postcss3.parse(source.toString("utf-8"), { from: abs });
|
|
@@ -3220,8 +2645,8 @@ async function inlineOne(cssPath, copies, warnings, seen) {
|
|
|
3220
2645
|
return null;
|
|
3221
2646
|
}
|
|
3222
2647
|
const relPath = stripUrlSuffix(decodeRef(raw));
|
|
3223
|
-
const absAsset =
|
|
3224
|
-
const ext =
|
|
2648
|
+
const absAsset = path2.resolve(cssDir, relPath);
|
|
2649
|
+
const ext = path2.extname(absAsset).toLowerCase();
|
|
3225
2650
|
if (FONT_EXTS.has(ext)) {
|
|
3226
2651
|
const bytes2 = await readOrThrow(absAsset, "font file", abs);
|
|
3227
2652
|
return dataUri(bytes2, ext);
|
|
@@ -3239,7 +2664,7 @@ async function inlineOne(cssPath, copies, warnings, seen) {
|
|
|
3239
2664
|
imports.push({ node, target });
|
|
3240
2665
|
});
|
|
3241
2666
|
for (const { node, target } of imports) {
|
|
3242
|
-
const importedAbs =
|
|
2667
|
+
const importedAbs = path2.resolve(cssDir, stripUrlSuffix(decodeRef(target)));
|
|
3243
2668
|
const inlined = await inlineOne(importedAbs, copies, warnings, seen);
|
|
3244
2669
|
node.replaceWith(wrapImportConditions(node, postcss3.parse(inlined, { from: importedAbs })));
|
|
3245
2670
|
}
|
|
@@ -3251,10 +2676,10 @@ async function inlineStyles(projectDir, stylePaths) {
|
|
|
3251
2676
|
const seen = new Set;
|
|
3252
2677
|
const parts = [];
|
|
3253
2678
|
for (const rel of stylePaths) {
|
|
3254
|
-
const abs =
|
|
2679
|
+
const abs = path2.resolve(projectDir, rel);
|
|
3255
2680
|
const css = await inlineOne(abs, copies, warnings, seen);
|
|
3256
2681
|
if (css.trim().length > 0) {
|
|
3257
|
-
parts.push(`/* ${toPosix(
|
|
2682
|
+
parts.push(`/* ${toPosix(path2.relative(projectDir, abs))} */
|
|
3258
2683
|
${css.trim()}`);
|
|
3259
2684
|
}
|
|
3260
2685
|
}
|
|
@@ -3270,14 +2695,14 @@ async function collectStyleDependencies(projectDir, stylePaths) {
|
|
|
3270
2695
|
const found = new Set;
|
|
3271
2696
|
const visited = new Set;
|
|
3272
2697
|
async function walk(cssPath) {
|
|
3273
|
-
const abs =
|
|
2698
|
+
const abs = path2.resolve(cssPath);
|
|
3274
2699
|
if (visited.has(abs))
|
|
3275
2700
|
return;
|
|
3276
2701
|
visited.add(abs);
|
|
3277
2702
|
found.add(abs);
|
|
3278
2703
|
let source;
|
|
3279
2704
|
try {
|
|
3280
|
-
source = await
|
|
2705
|
+
source = await readFile8(abs, "utf-8");
|
|
3281
2706
|
} catch {
|
|
3282
2707
|
return;
|
|
3283
2708
|
}
|
|
@@ -3287,14 +2712,14 @@ async function collectStyleDependencies(projectDir, stylePaths) {
|
|
|
3287
2712
|
} catch {
|
|
3288
2713
|
return;
|
|
3289
2714
|
}
|
|
3290
|
-
const cssDir =
|
|
2715
|
+
const cssDir = path2.dirname(abs);
|
|
3291
2716
|
root.walkDecls((decl) => {
|
|
3292
2717
|
if (!decl.value.includes("url("))
|
|
3293
2718
|
return;
|
|
3294
2719
|
for (const raw of extractCssUrls(decl.value)) {
|
|
3295
2720
|
if (!raw || isNonFilesystemRef(raw))
|
|
3296
2721
|
continue;
|
|
3297
|
-
found.add(
|
|
2722
|
+
found.add(path2.resolve(cssDir, stripUrlSuffix(decodeRef(raw))));
|
|
3298
2723
|
}
|
|
3299
2724
|
});
|
|
3300
2725
|
const imports = [];
|
|
@@ -3304,11 +2729,11 @@ async function collectStyleDependencies(projectDir, stylePaths) {
|
|
|
3304
2729
|
imports.push(target);
|
|
3305
2730
|
});
|
|
3306
2731
|
for (const target of imports) {
|
|
3307
|
-
await walk(
|
|
2732
|
+
await walk(path2.resolve(cssDir, stripUrlSuffix(decodeRef(target))));
|
|
3308
2733
|
}
|
|
3309
2734
|
}
|
|
3310
2735
|
for (const rel of stylePaths) {
|
|
3311
|
-
await walk(
|
|
2736
|
+
await walk(path2.resolve(projectDir, rel));
|
|
3312
2737
|
}
|
|
3313
2738
|
return [...found];
|
|
3314
2739
|
}
|
|
@@ -3325,8 +2750,8 @@ async function planImageCopies(projectDir, refs) {
|
|
|
3325
2750
|
continue;
|
|
3326
2751
|
}
|
|
3327
2752
|
const cleaned = stripUrlSuffix(decodeRef(ref));
|
|
3328
|
-
const abs =
|
|
3329
|
-
const rel =
|
|
2753
|
+
const abs = path2.resolve(projectDir, cleaned);
|
|
2754
|
+
const rel = path2.relative(projectDir, abs);
|
|
3330
2755
|
const dest = toPosix(rel);
|
|
3331
2756
|
copies.set(dest, { from: abs, to: dest });
|
|
3332
2757
|
destinations.set(ref, dest);
|
|
@@ -3347,18 +2772,18 @@ async function inlineShapeUrls(html, baseDir) {
|
|
|
3347
2772
|
if (!raw || isNonFilesystemRef(raw))
|
|
3348
2773
|
continue;
|
|
3349
2774
|
const cleaned = stripUrlSuffix(decodeRef(raw));
|
|
3350
|
-
if (
|
|
2775
|
+
if (path2.isAbsolute(cleaned))
|
|
3351
2776
|
continue;
|
|
3352
|
-
const abs =
|
|
2777
|
+
const abs = path2.resolve(baseDir, cleaned);
|
|
3353
2778
|
if (escapesProjectRoot(baseDir, abs))
|
|
3354
2779
|
continue;
|
|
3355
2780
|
let bytes;
|
|
3356
2781
|
try {
|
|
3357
|
-
bytes = await
|
|
2782
|
+
bytes = await readFile8(abs);
|
|
3358
2783
|
} catch {
|
|
3359
2784
|
continue;
|
|
3360
2785
|
}
|
|
3361
|
-
const mime = mimeFor(
|
|
2786
|
+
const mime = mimeFor(path2.extname(abs).toLowerCase());
|
|
3362
2787
|
const dataUri2 = `data:${mime};base64,${bytes.toString("base64")}`;
|
|
3363
2788
|
replacements.set(m[0], `--gp-shape:url("${dataUri2}")`);
|
|
3364
2789
|
}
|
|
@@ -3372,7 +2797,7 @@ async function inlineShapeUrls(html, baseDir) {
|
|
|
3372
2797
|
}
|
|
3373
2798
|
|
|
3374
2799
|
// src/checks/source/local-ref-parser.ts
|
|
3375
|
-
import { readFile as
|
|
2800
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
3376
2801
|
|
|
3377
2802
|
// src/lib/markdown/renderer.ts
|
|
3378
2803
|
import MarkdownIt from "markdown-it";
|
|
@@ -5191,9 +4616,9 @@ function layerSlug(name) {
|
|
|
5191
4616
|
}
|
|
5192
4617
|
|
|
5193
4618
|
// src/lib/markdown/plugins.ts
|
|
5194
|
-
import { createHash as
|
|
4619
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
5195
4620
|
import { existsSync as existsSync6, lstatSync, realpathSync, rmSync, statSync as statSync2, unlinkSync } from "node:fs";
|
|
5196
|
-
import { cp, link, mkdir, mkdtemp, readFile as
|
|
4621
|
+
import { cp, link, mkdir, mkdtemp, readFile as readFile10, rm, unlink, writeFile } from "node:fs/promises";
|
|
5197
4622
|
import { tmpdir } from "node:os";
|
|
5198
4623
|
import { resolve as resolve4, join, dirname as dirname2, basename, extname, relative, isAbsolute } from "node:path";
|
|
5199
4624
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -5218,24 +4643,24 @@ function resolveDeclaredStyles(rawStyles, baseDir, subject) {
|
|
|
5218
4643
|
}
|
|
5219
4644
|
|
|
5220
4645
|
// src/lib/extension-manifest.ts
|
|
5221
|
-
import { readFile as
|
|
5222
|
-
import
|
|
4646
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
4647
|
+
import path3 from "node:path";
|
|
5223
4648
|
var EXTENSION_MANIFEST_FILENAME = "package.json";
|
|
5224
4649
|
var GUTTERPRESS_KEY = "gutterpress";
|
|
5225
|
-
function
|
|
4650
|
+
function object(value) {
|
|
5226
4651
|
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
5227
4652
|
}
|
|
5228
4653
|
function str(value) {
|
|
5229
4654
|
return typeof value === "string" ? value : undefined;
|
|
5230
4655
|
}
|
|
5231
4656
|
function authorName(value) {
|
|
5232
|
-
return str(value) ?? str(
|
|
4657
|
+
return str(value) ?? str(object(value)?.name);
|
|
5233
4658
|
}
|
|
5234
4659
|
function metaFromPackageJson(raw) {
|
|
5235
|
-
const pkg =
|
|
4660
|
+
const pkg = object(raw);
|
|
5236
4661
|
if (!pkg)
|
|
5237
4662
|
return {};
|
|
5238
|
-
const gp =
|
|
4663
|
+
const gp = object(pkg[GUTTERPRESS_KEY]) ?? {};
|
|
5239
4664
|
const keywords = Array.isArray(pkg.keywords) ? pkg.keywords.filter((k) => typeof k === "string") : undefined;
|
|
5240
4665
|
return {
|
|
5241
4666
|
...str(pkg.name) ? { name: str(pkg.name) } : {},
|
|
@@ -5254,7 +4679,7 @@ function metaFromPackageJson(raw) {
|
|
|
5254
4679
|
}
|
|
5255
4680
|
async function readExtensionMeta(dir) {
|
|
5256
4681
|
try {
|
|
5257
|
-
return metaFromPackageJson(JSON.parse(await
|
|
4682
|
+
return metaFromPackageJson(JSON.parse(await readFile9(path3.join(dir, EXTENSION_MANIFEST_FILENAME), "utf8")));
|
|
5258
4683
|
} catch {
|
|
5259
4684
|
return {};
|
|
5260
4685
|
}
|
|
@@ -5277,7 +4702,7 @@ function extensionCarries(meta) {
|
|
|
5277
4702
|
};
|
|
5278
4703
|
}
|
|
5279
4704
|
function pathEscapesFolder(rel) {
|
|
5280
|
-
return
|
|
4705
|
+
return path3.isAbsolute(rel) || rel.split(/[\\/]/).includes("..");
|
|
5281
4706
|
}
|
|
5282
4707
|
function assertExtensionContained(meta) {
|
|
5283
4708
|
if (meta.engineStyles !== undefined) {
|
|
@@ -5548,9 +4973,9 @@ function isReceiptBoundRequireCall(node) {
|
|
|
5548
4973
|
return true;
|
|
5549
4974
|
if (callee.type !== "MemberExpression")
|
|
5550
4975
|
return false;
|
|
5551
|
-
const
|
|
4976
|
+
const object2 = callee.object;
|
|
5552
4977
|
const property = memberName(callee);
|
|
5553
|
-
return Boolean(
|
|
4978
|
+
return Boolean(object2 && (object2.type === "Identifier" && object2.name === "module" && property === "require" || object2.type === "Identifier" && object2.name === "require" && property === "resolve"));
|
|
5554
4979
|
}
|
|
5555
4980
|
function rewriteCommonJsRequires(source, absolute, pkg, tree) {
|
|
5556
4981
|
const ast = parseJavaScript(source, {
|
|
@@ -5601,7 +5026,7 @@ async function rewriteReachableModuleImports(entryPath, tree) {
|
|
|
5601
5026
|
const pkg = packageOwningFile(tree, absolute);
|
|
5602
5027
|
if (!pkg)
|
|
5603
5028
|
throw new Error(`Reachable plugin module is outside the verified package tree: ${absolute}`);
|
|
5604
|
-
const source = await
|
|
5029
|
+
const source = await readFile10(absolute, "utf8");
|
|
5605
5030
|
const [imports] = parseModuleImports(source, absolute);
|
|
5606
5031
|
const replacements = [];
|
|
5607
5032
|
const targets = new Set;
|
|
@@ -5708,7 +5133,7 @@ async function createIsolatedVendorTree(verified, address) {
|
|
|
5708
5133
|
}
|
|
5709
5134
|
}
|
|
5710
5135
|
async function isolateVendoredPlugin(verified) {
|
|
5711
|
-
const address =
|
|
5136
|
+
const address = createHash2("sha256").update(verified.receipt.tree.digest).update("\x00").update(JSON.stringify(verified.receipt)).digest("hex");
|
|
5712
5137
|
let pending = isolatedVendorTrees.get(address);
|
|
5713
5138
|
if (!pending) {
|
|
5714
5139
|
pending = createIsolatedVendorTree(verified, address).catch((error2) => {
|
|
@@ -6180,7 +5605,7 @@ async function* renderedLocalRefs(ctx, checkId, results) {
|
|
|
6180
5605
|
for (const file of files) {
|
|
6181
5606
|
let refs;
|
|
6182
5607
|
try {
|
|
6183
|
-
refs = collectRenderedLocalRefs(await
|
|
5608
|
+
refs = collectRenderedLocalRefs(await readFile11(file, "utf8"));
|
|
6184
5609
|
} catch {
|
|
6185
5610
|
results.push(inspectionFailed(checkId, `Could not read source file: ${file}`, { file }));
|
|
6186
5611
|
continue;
|
|
@@ -6242,15 +5667,15 @@ function localRefExists(ref, kind, sourceFile, projectRoot) {
|
|
|
6242
5667
|
registerCheck(check20);
|
|
6243
5668
|
|
|
6244
5669
|
// src/lib/build-staging.ts
|
|
6245
|
-
import
|
|
5670
|
+
import path4 from "node:path";
|
|
6246
5671
|
import os from "node:os";
|
|
6247
5672
|
import fsp from "node:fs/promises";
|
|
6248
5673
|
|
|
6249
5674
|
// src/lib/missing-asset-placeholder.ts
|
|
6250
|
-
import { createHash as
|
|
5675
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
6251
5676
|
import { deflateSync } from "node:zlib";
|
|
6252
5677
|
function placeholderOutputPath(missingOutputPath) {
|
|
6253
|
-
const hash =
|
|
5678
|
+
const hash = createHash3("sha256").update(missingOutputPath).digest("hex").slice(0, 16);
|
|
6254
5679
|
return `assets/gutterpress-missing/${hash}.png`;
|
|
6255
5680
|
}
|
|
6256
5681
|
function decodeHtmlAttribute(value) {
|
|
@@ -6427,8 +5852,8 @@ function placeholderPng(width = 640, height = 480, cell = 32) {
|
|
|
6427
5852
|
|
|
6428
5853
|
// src/lib/build-staging.ts
|
|
6429
5854
|
async function shipViewerHtml(htmlFile, outDir) {
|
|
6430
|
-
await fsp.mkdir(
|
|
6431
|
-
await fsp.copyFile(await getAssetPath("engine/gutterpress-viewer.js"),
|
|
5855
|
+
await fsp.mkdir(path4.join(outDir, "engine"), { recursive: true });
|
|
5856
|
+
await fsp.copyFile(await getAssetPath("engine/gutterpress-viewer.js"), path4.join(outDir, "engine/gutterpress-viewer.js"));
|
|
6432
5857
|
const tag = ` <script src="engine/gutterpress-viewer.js"></script>
|
|
6433
5858
|
`;
|
|
6434
5859
|
const html = await fsp.readFile(htmlFile, "utf-8");
|
|
@@ -6460,18 +5885,18 @@ async function stageBookAssets(options) {
|
|
|
6460
5885
|
return { missing: [...missingPlaceholders.keys()].sort() };
|
|
6461
5886
|
}
|
|
6462
5887
|
async function copyReferencedAssets(copies, outDir) {
|
|
6463
|
-
const dirs = new Set(copies.map((c) =>
|
|
5888
|
+
const dirs = new Set(copies.map((c) => path4.dirname(path4.resolve(outDir, c.to))));
|
|
6464
5889
|
await Promise.all([...dirs].map((d) => fsp.mkdir(d, { recursive: true })));
|
|
6465
5890
|
const missing = new Map;
|
|
6466
5891
|
await Promise.all(copies.map(async (c) => {
|
|
6467
|
-
const dest =
|
|
5892
|
+
const dest = path4.resolve(outDir, c.to);
|
|
6468
5893
|
try {
|
|
6469
5894
|
await fsp.copyFile(c.from, dest);
|
|
6470
5895
|
} catch (err) {
|
|
6471
5896
|
if (err?.code === "ENOENT") {
|
|
6472
5897
|
const placeholder = placeholderOutputPath(c.to);
|
|
6473
|
-
const placeholderDest =
|
|
6474
|
-
await fsp.mkdir(
|
|
5898
|
+
const placeholderDest = path4.resolve(outDir, placeholder);
|
|
5899
|
+
await fsp.mkdir(path4.dirname(placeholderDest), { recursive: true });
|
|
6475
5900
|
await fsp.writeFile(placeholderDest, placeholderPng());
|
|
6476
5901
|
missing.set(c.to, placeholder);
|
|
6477
5902
|
return;
|
|
@@ -6501,7 +5926,7 @@ function dropRelativeLinkHrefs(html) {
|
|
|
6501
5926
|
});
|
|
6502
5927
|
}
|
|
6503
5928
|
async function createStageRoot() {
|
|
6504
|
-
return fsp.mkdtemp(
|
|
5929
|
+
return fsp.mkdtemp(path4.join(os.tmpdir(), "gutterpress-stage-"));
|
|
6505
5930
|
}
|
|
6506
5931
|
|
|
6507
5932
|
// src/checks/source/dangling-links.ts
|
|
@@ -6531,7 +5956,7 @@ var check21 = {
|
|
|
6531
5956
|
registerCheck(check21);
|
|
6532
5957
|
|
|
6533
5958
|
// src/checks/source/accessibility-alt-text.ts
|
|
6534
|
-
import { readFile as
|
|
5959
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
6535
5960
|
var check22 = {
|
|
6536
5961
|
id: "source.accessibility.alt-text",
|
|
6537
5962
|
name: "Image Alt Text",
|
|
@@ -6545,7 +5970,7 @@ var check22 = {
|
|
|
6545
5970
|
const results = [];
|
|
6546
5971
|
for (const file of files) {
|
|
6547
5972
|
try {
|
|
6548
|
-
const content = await
|
|
5973
|
+
const content = await readFile12(file, "utf8");
|
|
6549
5974
|
const lines = content.split(`
|
|
6550
5975
|
`);
|
|
6551
5976
|
let inFence = false;
|
|
@@ -6589,7 +6014,7 @@ function extractImageAlts(line) {
|
|
|
6589
6014
|
registerCheck(check22);
|
|
6590
6015
|
|
|
6591
6016
|
// src/checks/source/accessibility-heading-order.ts
|
|
6592
|
-
import { readFile as
|
|
6017
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
6593
6018
|
var check23 = {
|
|
6594
6019
|
id: "source.accessibility.heading-order",
|
|
6595
6020
|
name: "Heading Order",
|
|
@@ -6603,7 +6028,7 @@ var check23 = {
|
|
|
6603
6028
|
const results = [];
|
|
6604
6029
|
for (const file of files) {
|
|
6605
6030
|
try {
|
|
6606
|
-
const content = await
|
|
6031
|
+
const content = await readFile13(file, "utf8");
|
|
6607
6032
|
const lines = content.split(`
|
|
6608
6033
|
`);
|
|
6609
6034
|
let inFence = false;
|
|
@@ -6639,7 +6064,7 @@ var check23 = {
|
|
|
6639
6064
|
registerCheck(check23);
|
|
6640
6065
|
|
|
6641
6066
|
// src/checks/source/layout-markers.ts
|
|
6642
|
-
import { readFile as
|
|
6067
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
6643
6068
|
var check24 = {
|
|
6644
6069
|
id: "source.markdown.layout-markers",
|
|
6645
6070
|
name: "Layout Markers",
|
|
@@ -6657,7 +6082,7 @@ var check24 = {
|
|
|
6657
6082
|
const md = createMarkdownRenderer(plugins);
|
|
6658
6083
|
for (const file of files) {
|
|
6659
6084
|
try {
|
|
6660
|
-
const content = await
|
|
6085
|
+
const content = await readFile14(file, "utf8");
|
|
6661
6086
|
const env = {};
|
|
6662
6087
|
md.render(content, env);
|
|
6663
6088
|
for (const w of env.layoutWarnings ?? []) {
|
|
@@ -6679,8 +6104,8 @@ var check24 = {
|
|
|
6679
6104
|
registerCheck(check24);
|
|
6680
6105
|
|
|
6681
6106
|
// src/checks/source/merge-markers.ts
|
|
6682
|
-
import { readFile as
|
|
6683
|
-
import
|
|
6107
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
6108
|
+
import path5 from "node:path";
|
|
6684
6109
|
|
|
6685
6110
|
// src/checks/asset/extensions.ts
|
|
6686
6111
|
var RASTER_INSPECTABLE_EXTS = [
|
|
@@ -6721,7 +6146,7 @@ var check25 = {
|
|
|
6721
6146
|
for (const file of textFiles) {
|
|
6722
6147
|
let content;
|
|
6723
6148
|
try {
|
|
6724
|
-
content = await
|
|
6149
|
+
content = await readFile15(file, "utf8");
|
|
6725
6150
|
} catch {
|
|
6726
6151
|
results.push(inspectionFailed(check25.id, `Could not read source file: ${file}`, { file }));
|
|
6727
6152
|
continue;
|
|
@@ -6764,7 +6189,7 @@ var check25 = {
|
|
|
6764
6189
|
ignore: [...ASSET_SCAN_IGNORE_GLOBS]
|
|
6765
6190
|
});
|
|
6766
6191
|
for (const sibling of siblings.sort()) {
|
|
6767
|
-
const rel =
|
|
6192
|
+
const rel = path5.relative(ctx.inputDir, sibling).split(path5.sep).join("/");
|
|
6768
6193
|
results.push(finding(check25.id, {
|
|
6769
6194
|
severity: "warning",
|
|
6770
6195
|
code: "kept-both-versions",
|
|
@@ -6784,27 +6209,27 @@ registerCheck(check25);
|
|
|
6784
6209
|
import { stat as stat3 } from "node:fs/promises";
|
|
6785
6210
|
|
|
6786
6211
|
// src/lib/image-inspect.ts
|
|
6787
|
-
import { stat as stat2, readFile as
|
|
6212
|
+
import { stat as stat2, readFile as readFile16 } from "node:fs/promises";
|
|
6788
6213
|
var DEFAULT_DPI = 72;
|
|
6789
6214
|
var cache = new Map;
|
|
6790
|
-
async function inspectImage(
|
|
6215
|
+
async function inspectImage(path6) {
|
|
6791
6216
|
let st;
|
|
6792
6217
|
try {
|
|
6793
|
-
st = await stat2(
|
|
6218
|
+
st = await stat2(path6);
|
|
6794
6219
|
} catch {
|
|
6795
6220
|
return null;
|
|
6796
6221
|
}
|
|
6797
|
-
const hit = cache.get(
|
|
6222
|
+
const hit = cache.get(path6);
|
|
6798
6223
|
if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size)
|
|
6799
6224
|
return hit.info;
|
|
6800
6225
|
let info2 = null;
|
|
6801
6226
|
try {
|
|
6802
|
-
const buf = await
|
|
6227
|
+
const buf = await readFile16(path6);
|
|
6803
6228
|
info2 = parseImage(buf);
|
|
6804
6229
|
} catch {
|
|
6805
6230
|
info2 = null;
|
|
6806
6231
|
}
|
|
6807
|
-
cache.set(
|
|
6232
|
+
cache.set(path6, { mtimeMs: st.mtimeMs, size: st.size, info: info2 });
|
|
6808
6233
|
return info2;
|
|
6809
6234
|
}
|
|
6810
6235
|
function parseImage(b) {
|
|
@@ -7359,7 +6784,7 @@ var check33 = {
|
|
|
7359
6784
|
registerCheck(check33);
|
|
7360
6785
|
|
|
7361
6786
|
// src/checks/heuristic/section-density.ts
|
|
7362
|
-
import { readFile as
|
|
6787
|
+
import { readFile as readFile17 } from "node:fs/promises";
|
|
7363
6788
|
var check34 = {
|
|
7364
6789
|
id: "heuristic.chunking.section-density",
|
|
7365
6790
|
name: "Section Density",
|
|
@@ -7376,7 +6801,7 @@ var check34 = {
|
|
|
7376
6801
|
const results = [];
|
|
7377
6802
|
for (const file of files) {
|
|
7378
6803
|
try {
|
|
7379
|
-
const content = await
|
|
6804
|
+
const content = await readFile17(file, "utf8");
|
|
7380
6805
|
const lines = content.split(`
|
|
7381
6806
|
`);
|
|
7382
6807
|
let currentSectionStart = 0;
|
|
@@ -7545,7 +6970,7 @@ import { existsSync as existsSync9 } from "node:fs";
|
|
|
7545
6970
|
import { dirname as dirname4, join as join3, resolve as resolve7 } from "node:path";
|
|
7546
6971
|
|
|
7547
6972
|
// src/lib/output-paths.ts
|
|
7548
|
-
import
|
|
6973
|
+
import path6 from "node:path";
|
|
7549
6974
|
|
|
7550
6975
|
// src/lib/slug.ts
|
|
7551
6976
|
function slugify(name, fallback = "") {
|
|
@@ -7563,7 +6988,7 @@ function bookSlug(title) {
|
|
|
7563
6988
|
return slugify(title ?? "", "book");
|
|
7564
6989
|
}
|
|
7565
6990
|
function resolveOutputDir(manifestDir, title) {
|
|
7566
|
-
return
|
|
6991
|
+
return path6.resolve(manifestDir, DIST_DIRNAME, bookSlug(title));
|
|
7567
6992
|
}
|
|
7568
6993
|
function artifactName(title, format) {
|
|
7569
6994
|
return `${bookSlug(title)}-${format}.pdf`;
|
|
@@ -7573,7 +6998,7 @@ function artifactName(title, format) {
|
|
|
7573
6998
|
var BOOK_HTML_FILENAME = "book.html";
|
|
7574
6999
|
|
|
7575
7000
|
// src/lib/markdown/index.ts
|
|
7576
|
-
import { readdir as
|
|
7001
|
+
import { readdir as readdir2, readFile as readFile18, writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
|
|
7577
7002
|
|
|
7578
7003
|
// src/lib/remote-auth/sync-messages.ts
|
|
7579
7004
|
function isOnlineSibling(filepath) {
|
|
@@ -7680,7 +7105,7 @@ ${bodyContent}
|
|
|
7680
7105
|
async function resolveActiveMarkdownFiles(inputDir, configuredFiles) {
|
|
7681
7106
|
if (configuredFiles && configuredFiles.length > 0)
|
|
7682
7107
|
return configuredFiles;
|
|
7683
|
-
return (await
|
|
7108
|
+
return (await readdir2(inputDir)).filter((f) => f.endsWith(".md") && !isOnlineSibling(f)).sort();
|
|
7684
7109
|
}
|
|
7685
7110
|
async function renderChapters(inputDir, opts = {}) {
|
|
7686
7111
|
const styles = await resolveActiveStyles(inputDir, opts.styles);
|
|
@@ -7719,7 +7144,7 @@ ${css.trim()}
|
|
|
7719
7144
|
}
|
|
7720
7145
|
return assembleBookHtml({
|
|
7721
7146
|
files,
|
|
7722
|
-
readText: (relPath) =>
|
|
7147
|
+
readText: (relPath) => readFile18(join2(inputDir, canonicalChapterId(relPath)), "utf-8"),
|
|
7723
7148
|
projectCss: inlined.css,
|
|
7724
7149
|
preloadImages: cssAssetCopies.map((c) => c.to),
|
|
7725
7150
|
title: opts.title,
|
|
@@ -7750,7 +7175,7 @@ async function renderChaptersToFile(inputDir, outDir, opts = {}) {
|
|
|
7750
7175
|
}
|
|
7751
7176
|
|
|
7752
7177
|
// src/lib/markdownlint-fix.ts
|
|
7753
|
-
import { readFile as
|
|
7178
|
+
import { readFile as readFile19, writeFile as writeFile3 } from "node:fs/promises";
|
|
7754
7179
|
import { applyFixes } from "markdownlint";
|
|
7755
7180
|
import { lint as lint2 } from "markdownlint/sync";
|
|
7756
7181
|
async function uncommittedAmong(inputDir, files) {
|
|
@@ -7790,7 +7215,7 @@ async function applyMarkdownlintFixes(ctx) {
|
|
|
7790
7215
|
for (const [file, violations] of Object.entries(results)) {
|
|
7791
7216
|
if (!Array.isArray(violations) || violations.length === 0)
|
|
7792
7217
|
continue;
|
|
7793
|
-
const before = await
|
|
7218
|
+
const before = await readFile19(file, "utf8");
|
|
7794
7219
|
const after = applyFixes(before, violations);
|
|
7795
7220
|
if (after !== before)
|
|
7796
7221
|
pending.push({ file, content: after });
|
|
@@ -8286,7 +7711,7 @@ function buildPdfSummaryLines(results) {
|
|
|
8286
7711
|
}
|
|
8287
7712
|
|
|
8288
7713
|
// src/lib/build-runner.ts
|
|
8289
|
-
import
|
|
7714
|
+
import path8 from "node:path";
|
|
8290
7715
|
import fs2 from "node:fs";
|
|
8291
7716
|
import fsp2 from "node:fs/promises";
|
|
8292
7717
|
import os2 from "node:os";
|
|
@@ -8294,9 +7719,9 @@ import { randomBytes } from "node:crypto";
|
|
|
8294
7719
|
|
|
8295
7720
|
// src/lib/build-fingerprint.ts
|
|
8296
7721
|
import * as fs from "node:fs";
|
|
8297
|
-
import { mkdir as mkdir3, readFile as
|
|
8298
|
-
import { createHash as
|
|
8299
|
-
import
|
|
7722
|
+
import { mkdir as mkdir3, readFile as readFile20, writeFile as writeFile4 } from "node:fs/promises";
|
|
7723
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
7724
|
+
import path7 from "node:path";
|
|
8300
7725
|
import git from "isomorphic-git";
|
|
8301
7726
|
var FINGERPRINT_FILENAME = "build-fingerprint.json";
|
|
8302
7727
|
var VERSION_TIMEOUT_MS = 4000;
|
|
@@ -8345,7 +7770,7 @@ async function getGitRevision(sourceDir) {
|
|
|
8345
7770
|
const candidateDirs = [sourceDir, process.cwd()].filter((v) => Boolean(v));
|
|
8346
7771
|
const seen = new Set;
|
|
8347
7772
|
for (const dir of candidateDirs) {
|
|
8348
|
-
const abs =
|
|
7773
|
+
const abs = path7.resolve(dir);
|
|
8349
7774
|
if (seen.has(abs))
|
|
8350
7775
|
continue;
|
|
8351
7776
|
seen.add(abs);
|
|
@@ -8381,10 +7806,10 @@ async function getEngineBundleHash() {
|
|
|
8381
7806
|
getAssetPath("engine/gutterpress-agent.js")
|
|
8382
7807
|
]);
|
|
8383
7808
|
const [viewerBytes, agentBytes] = await Promise.all([
|
|
8384
|
-
|
|
8385
|
-
|
|
7809
|
+
readFile20(viewerPath),
|
|
7810
|
+
readFile20(agentPath)
|
|
8386
7811
|
]);
|
|
8387
|
-
const hash =
|
|
7812
|
+
const hash = createHash4("sha256");
|
|
8388
7813
|
hash.update(viewerBytes);
|
|
8389
7814
|
hash.update(agentBytes);
|
|
8390
7815
|
return hash.digest("hex").slice(0, 12);
|
|
@@ -8430,8 +7855,8 @@ function sanitizeArgs(args) {
|
|
|
8430
7855
|
return out;
|
|
8431
7856
|
}
|
|
8432
7857
|
async function writeBuildFingerprint(input) {
|
|
8433
|
-
const outputDir =
|
|
8434
|
-
const outPath =
|
|
7858
|
+
const outputDir = path7.resolve(input.outputDir);
|
|
7859
|
+
const outPath = path7.join(outputDir, FINGERPRINT_FILENAME);
|
|
8435
7860
|
const [tools, sourceRevision] = await Promise.all([
|
|
8436
7861
|
getToolVersions(),
|
|
8437
7862
|
getGitRevision(input.sourceDir)
|
|
@@ -8447,7 +7872,7 @@ async function writeBuildFingerprint(input) {
|
|
|
8447
7872
|
iccPath: input.pdfx.iccPath,
|
|
8448
7873
|
stripAnnotations: input.pdfx.stripAnnotations
|
|
8449
7874
|
},
|
|
8450
|
-
outputDir: input.recordedOutputDir ?
|
|
7875
|
+
outputDir: input.recordedOutputDir ? path7.resolve(input.recordedOutputDir) : outputDir
|
|
8451
7876
|
},
|
|
8452
7877
|
sourceRevision,
|
|
8453
7878
|
tools
|
|
@@ -8527,18 +7952,18 @@ function splitOutPath(outArg, format) {
|
|
|
8527
7952
|
if (typeof outArg !== "string" || outArg.length === 0) {
|
|
8528
7953
|
return { outDir: undefined, pdfFileOverride: null };
|
|
8529
7954
|
}
|
|
8530
|
-
const resolved =
|
|
7955
|
+
const resolved = path8.resolve(outArg);
|
|
8531
7956
|
if ((format === "pdf" || format === "pdfx") && resolved.toLowerCase().endsWith(".pdf")) {
|
|
8532
|
-
return { outDir:
|
|
7957
|
+
return { outDir: path8.dirname(resolved), pdfFileOverride: resolved };
|
|
8533
7958
|
}
|
|
8534
7959
|
return { outDir: resolved, pdfFileOverride: null };
|
|
8535
7960
|
}
|
|
8536
7961
|
function layoutWarningKey(file, line, message) {
|
|
8537
|
-
return `${
|
|
7962
|
+
return `${path8.resolve(file)}\x00${line ?? 0}\x00${message}`;
|
|
8538
7963
|
}
|
|
8539
7964
|
async function resolveBuildContext(opts) {
|
|
8540
7965
|
const { format } = opts;
|
|
8541
|
-
const inputDir =
|
|
7966
|
+
const inputDir = path8.resolve(opts.inputDir);
|
|
8542
7967
|
const { manifest, manifestDir, manifestPath } = await loadManifestWithPath(opts.manifestPath ?? inputDir, { explicit: opts.manifestPath !== undefined });
|
|
8543
7968
|
if (manifestPath === null) {
|
|
8544
7969
|
throw new UsageError(`No book manifest found in ${inputDir}. Looked for ${MANIFEST_FILENAMES.join(" or ")}. ` + "Run from your book folder or pass that folder with `gutterpress build <book-dir>`. " + "For a custom manifest filename, pass `--manifest <path>`.");
|
|
@@ -8552,10 +7977,10 @@ async function resolveBuildContext(opts) {
|
|
|
8552
7977
|
title: opts.title,
|
|
8553
7978
|
pdfx: pdfxConfigOverride
|
|
8554
7979
|
}, manifest);
|
|
8555
|
-
const target = opts.pdfFileOverride ? { kind: "file", file:
|
|
8556
|
-
const outDir = target.kind === "file" ?
|
|
7980
|
+
const target = opts.pdfFileOverride ? { kind: "file", file: path8.resolve(opts.pdfFileOverride) } : opts.outDir ? { kind: "directory", dir: opts.outDir } : { kind: "project", dir: resolveOutputDir(manifestDir, config.title) };
|
|
7981
|
+
const outDir = target.kind === "file" ? path8.dirname(target.file) : target.dir;
|
|
8557
7982
|
const gates = computeGates(format, opts, config);
|
|
8558
|
-
const workDir = target.kind === "project" ?
|
|
7983
|
+
const workDir = target.kind === "project" ? path8.join(path8.dirname(target.dir), `.${path8.basename(target.dir)}-build-${randomBytes(6).toString("hex")}`) : path8.join(os2.tmpdir(), `gutterpress-build-${randomBytes(6).toString("hex")}`);
|
|
8559
7984
|
return {
|
|
8560
7985
|
opts,
|
|
8561
7986
|
format,
|
|
@@ -8625,7 +8050,7 @@ async function renderBook(ctx) {
|
|
|
8625
8050
|
pluginStyles,
|
|
8626
8051
|
onChapterWarnings: (file, warnings) => {
|
|
8627
8052
|
for (const w of warnings) {
|
|
8628
|
-
const key = layoutWarningKey(
|
|
8053
|
+
const key = layoutWarningKey(path8.resolve(renderDir, file), w.line, w.message);
|
|
8629
8054
|
if (!ctx.prevalidatedLayoutWarningKeys.has(key)) {
|
|
8630
8055
|
log.warn(` ${file}, line ${w.line}: ${w.message}`);
|
|
8631
8056
|
}
|
|
@@ -8664,9 +8089,9 @@ async function renderBook(ctx) {
|
|
|
8664
8089
|
return htmlFile;
|
|
8665
8090
|
}
|
|
8666
8091
|
async function resolveIccProfile(icc, manifestDir, explicitIccPath) {
|
|
8667
|
-
const iccCandidates =
|
|
8092
|
+
const iccCandidates = path8.isAbsolute(icc) ? [icc] : [path8.resolve(manifestDir, icc), path8.resolve(icc)];
|
|
8668
8093
|
let effectiveIccPath = iccCandidates.find((candidate) => fs2.existsSync(candidate)) ?? null;
|
|
8669
|
-
if (!effectiveIccPath && !explicitIccPath &&
|
|
8094
|
+
if (!effectiveIccPath && !explicitIccPath && path8.basename(icc) === "CGATS21_CRPC1.icc") {
|
|
8670
8095
|
effectiveIccPath = await getAssetPath("profiles/CGATS21_CRPC1.icc");
|
|
8671
8096
|
}
|
|
8672
8097
|
if (!effectiveIccPath) {
|
|
@@ -8682,13 +8107,13 @@ async function finalizeBuild(ctx, fingerprint, wroteMessage, paths, artifactName
|
|
|
8682
8107
|
});
|
|
8683
8108
|
await publishBuild(ctx, artifactName2);
|
|
8684
8109
|
const delivered = ctx.target.kind === "project" || ctx.target.kind === "directory" && ctx.format === "html";
|
|
8685
|
-
const fingerprintPath = delivered ?
|
|
8110
|
+
const fingerprintPath = delivered ? path8.join(ctx.outDir, path8.basename(workFingerprint)) : null;
|
|
8686
8111
|
log.success(wroteMessage);
|
|
8687
8112
|
if (fingerprintPath)
|
|
8688
8113
|
log.info(`Fingerprint: ${fingerprintPath}`);
|
|
8689
8114
|
return {
|
|
8690
8115
|
outDir: ctx.outDir,
|
|
8691
|
-
htmlPath: delivered ?
|
|
8116
|
+
htmlPath: delivered ? path8.join(ctx.outDir, path8.relative(ctx.workDir, paths.htmlPath)) : null,
|
|
8692
8117
|
pdfPath: paths.pdfPath,
|
|
8693
8118
|
fingerprintPath,
|
|
8694
8119
|
diagnostics
|
|
@@ -8699,7 +8124,7 @@ async function publishToDirectory(workDir, targetDir, format, artifactName2) {
|
|
|
8699
8124
|
if (format !== "html") {
|
|
8700
8125
|
if (!artifactName2)
|
|
8701
8126
|
throw new BuildError("No artifact to write for this format", 1);
|
|
8702
|
-
await fsp2.copyFile(
|
|
8127
|
+
await fsp2.copyFile(path8.join(workDir, artifactName2), path8.join(targetDir, artifactName2));
|
|
8703
8128
|
return;
|
|
8704
8129
|
}
|
|
8705
8130
|
await fsp2.cp(workDir, targetDir, { recursive: true, force: true });
|
|
@@ -8709,8 +8134,8 @@ async function publishBuild(ctx, artifactName2) {
|
|
|
8709
8134
|
if (target.kind === "file") {
|
|
8710
8135
|
if (!artifactName2)
|
|
8711
8136
|
throw new BuildError("No artifact to write for this format", 1);
|
|
8712
|
-
await fsp2.mkdir(
|
|
8713
|
-
await fsp2.copyFile(
|
|
8137
|
+
await fsp2.mkdir(path8.dirname(target.file), { recursive: true });
|
|
8138
|
+
await fsp2.copyFile(path8.join(workDir, artifactName2), target.file);
|
|
8714
8139
|
return;
|
|
8715
8140
|
}
|
|
8716
8141
|
if (target.kind === "directory") {
|
|
@@ -8718,12 +8143,12 @@ async function publishBuild(ctx, artifactName2) {
|
|
|
8718
8143
|
return;
|
|
8719
8144
|
}
|
|
8720
8145
|
for (const name of await listPdfs(target.dir)) {
|
|
8721
|
-
if (!fs2.existsSync(
|
|
8722
|
-
await fsp2.rename(
|
|
8146
|
+
if (!fs2.existsSync(path8.join(workDir, name))) {
|
|
8147
|
+
await fsp2.rename(path8.join(target.dir, name), path8.join(workDir, name));
|
|
8723
8148
|
}
|
|
8724
8149
|
}
|
|
8725
8150
|
await fsp2.rm(target.dir, { recursive: true, force: true });
|
|
8726
|
-
await fsp2.mkdir(
|
|
8151
|
+
await fsp2.mkdir(path8.dirname(target.dir), { recursive: true });
|
|
8727
8152
|
await fsp2.rename(workDir, target.dir);
|
|
8728
8153
|
}
|
|
8729
8154
|
async function listPdfs(dir) {
|
|
@@ -8739,7 +8164,7 @@ class HtmlOutput {
|
|
|
8739
8164
|
const { workDir, outDir, inputDir, config, opts } = ctx;
|
|
8740
8165
|
log.info("Shipping self-contained HTML + viewer bundle (native engine)");
|
|
8741
8166
|
await shipViewerHtml(htmlFile, workDir);
|
|
8742
|
-
await fsp2.writeFile(
|
|
8167
|
+
await fsp2.writeFile(path8.join(workDir, "index.html"), `<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="refresh" content="0;url=book.html"><title>Gutterpress</title></head><body></body></html>
|
|
8743
8168
|
`, "utf-8");
|
|
8744
8169
|
return finalizeBuild(ctx, {
|
|
8745
8170
|
command: "build",
|
|
@@ -8752,7 +8177,7 @@ class HtmlOutput {
|
|
|
8752
8177
|
iccPath: null,
|
|
8753
8178
|
stripAnnotations: null
|
|
8754
8179
|
}
|
|
8755
|
-
}, `Wrote: ${
|
|
8180
|
+
}, `Wrote: ${path8.join(outDir, BOOK_HTML)}`, { htmlPath: htmlFile, pdfPath: null });
|
|
8756
8181
|
}
|
|
8757
8182
|
}
|
|
8758
8183
|
|
|
@@ -8761,12 +8186,12 @@ class PdfOutput {
|
|
|
8761
8186
|
const { workDir, outDir, inputDir, config, opts, format, manifestDir, gates } = ctx;
|
|
8762
8187
|
const pdfxMode = format === "pdfx" ? opts.pdfxFlavor ?? config.pdfx.flavor : undefined;
|
|
8763
8188
|
const pdfName = artifactName(config.title, pdfxMode ? "pdfx" : "pdf");
|
|
8764
|
-
const pdfFile =
|
|
8765
|
-
const reportedPdf = ctx.target.kind === "file" ? ctx.target.file :
|
|
8189
|
+
const pdfFile = path8.join(workDir, pdfName);
|
|
8190
|
+
const reportedPdf = ctx.target.kind === "file" ? ctx.target.file : path8.join(outDir, pdfName);
|
|
8766
8191
|
const stage = await createStageRoot();
|
|
8767
8192
|
try {
|
|
8768
|
-
const rawPdf = pdfxMode ?
|
|
8769
|
-
await fsp2.mkdir(
|
|
8193
|
+
const rawPdf = pdfxMode ? path8.join(stage, "raw.pdf") : path8.resolve(pdfFile);
|
|
8194
|
+
await fsp2.mkdir(path8.dirname(path8.resolve(pdfFile)), { recursive: true });
|
|
8770
8195
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
8771
8196
|
const { buildNativePdf } = await import("./engine-amxqsfka.js");
|
|
8772
8197
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
@@ -8799,7 +8224,7 @@ class PdfOutput {
|
|
|
8799
8224
|
await stripAnnotations(rawPdf, stage);
|
|
8800
8225
|
}
|
|
8801
8226
|
log.info(`Converting to CMYK PDF/X (${pdfxMode})`);
|
|
8802
|
-
await convertToPdfxCmyk(rawPdf,
|
|
8227
|
+
await convertToPdfxCmyk(rawPdf, path8.resolve(pdfFile), {
|
|
8803
8228
|
iccPath: effectiveIccPath,
|
|
8804
8229
|
pdfx: pdfxMode,
|
|
8805
8230
|
title: config.title,
|
|
@@ -8888,10 +8313,10 @@ function openPath(filePath) {
|
|
|
8888
8313
|
}
|
|
8889
8314
|
|
|
8890
8315
|
// src/preview/lifecycle.ts
|
|
8891
|
-
import
|
|
8316
|
+
import path10 from "path";
|
|
8892
8317
|
import { tmpdir as tmpdir2 } from "os";
|
|
8893
8318
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
8894
|
-
import { readdir as
|
|
8319
|
+
import { readdir as readdir3, readFile as readFile21, writeFile as writeFile5 } from "node:fs/promises";
|
|
8895
8320
|
|
|
8896
8321
|
// src/utils/file-utils.ts
|
|
8897
8322
|
import { promises as fs3 } from "fs";
|
|
@@ -8913,7 +8338,7 @@ async function mkdir4(dirPath) {
|
|
|
8913
8338
|
// src/preview/file-watcher.ts
|
|
8914
8339
|
import { watch } from "chokidar";
|
|
8915
8340
|
import fsp3 from "node:fs/promises";
|
|
8916
|
-
import
|
|
8341
|
+
import path9 from "path";
|
|
8917
8342
|
var EMPTY_BOOK_HTML = `<!doctype html>
|
|
8918
8343
|
<html lang="en">
|
|
8919
8344
|
<head>
|
|
@@ -8966,7 +8391,7 @@ function injectPreviewScripts(html, pageIsolateChapters) {
|
|
|
8966
8391
|
async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets) {
|
|
8967
8392
|
if (!inputPath) {
|
|
8968
8393
|
cssAssets.clear();
|
|
8969
|
-
await fsp3.writeFile(
|
|
8394
|
+
await fsp3.writeFile(path9.join(tempDir, BOOK_HTML_FILENAME), EMPTY_BOOK_HTML, "utf-8");
|
|
8970
8395
|
return;
|
|
8971
8396
|
}
|
|
8972
8397
|
const nextAssets = new Map;
|
|
@@ -8981,7 +8406,7 @@ async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets) {
|
|
|
8981
8406
|
cssAssets.clear();
|
|
8982
8407
|
for (const [to, from] of nextAssets)
|
|
8983
8408
|
cssAssets.set(to, from);
|
|
8984
|
-
await fsp3.writeFile(
|
|
8409
|
+
await fsp3.writeFile(path9.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html, false), "utf-8");
|
|
8985
8410
|
}
|
|
8986
8411
|
async function renderChapterPreviewHtml(inputPath, file, config) {
|
|
8987
8412
|
const html = await renderPreviewBook(inputPath, config, {
|
|
@@ -8993,12 +8418,12 @@ async function renderChapterPreviewHtml(inputPath, file, config) {
|
|
|
8993
8418
|
function describeChanges(changes, inputResolved) {
|
|
8994
8419
|
const files = [];
|
|
8995
8420
|
for (const [changedPath, event] of changes) {
|
|
8996
|
-
const relative2 =
|
|
8997
|
-
if (relative2 === "" || relative2 === ".." || relative2.startsWith(`..${
|
|
8421
|
+
const relative2 = path9.relative(inputResolved, path9.resolve(changedPath));
|
|
8422
|
+
if (relative2 === "" || relative2 === ".." || relative2.startsWith(`..${path9.sep}`) || path9.isAbsolute(relative2))
|
|
8998
8423
|
continue;
|
|
8999
8424
|
files.push({
|
|
9000
8425
|
relativePath: canonicalChapterId(relative2),
|
|
9001
|
-
ext:
|
|
8426
|
+
ext: path9.extname(changedPath).toLowerCase(),
|
|
9002
8427
|
event
|
|
9003
8428
|
});
|
|
9004
8429
|
}
|
|
@@ -9050,23 +8475,23 @@ function isIgnoredWatchPath(candidatePath, projectRoot) {
|
|
|
9050
8475
|
return isDotPathUnderRoot(candidatePath, projectRoot) || isGeneratedProjectPath(relative2);
|
|
9051
8476
|
}
|
|
9052
8477
|
function isExternalWatchCandidate(candidatePath, targets) {
|
|
9053
|
-
const candidate =
|
|
8478
|
+
const candidate = path9.resolve(candidatePath);
|
|
9054
8479
|
for (const rawTarget of targets) {
|
|
9055
|
-
const target =
|
|
9056
|
-
const rel =
|
|
9057
|
-
if (rel === "" || rel !== ".." && !rel.startsWith(`..${
|
|
8480
|
+
const target = path9.resolve(rawTarget);
|
|
8481
|
+
const rel = path9.relative(candidate, target);
|
|
8482
|
+
if (rel === "" || rel !== ".." && !rel.startsWith(`..${path9.sep}`) && !path9.isAbsolute(rel))
|
|
9058
8483
|
return true;
|
|
9059
8484
|
}
|
|
9060
8485
|
return false;
|
|
9061
8486
|
}
|
|
9062
8487
|
async function nearestExistingDirectory(start) {
|
|
9063
|
-
let current =
|
|
8488
|
+
let current = path9.resolve(start);
|
|
9064
8489
|
while (true) {
|
|
9065
8490
|
try {
|
|
9066
8491
|
if ((await fsp3.stat(current)).isDirectory())
|
|
9067
8492
|
return current;
|
|
9068
8493
|
} catch {}
|
|
9069
|
-
const parent =
|
|
8494
|
+
const parent = path9.dirname(current);
|
|
9070
8495
|
if (parent === current)
|
|
9071
8496
|
return current;
|
|
9072
8497
|
current = parent;
|
|
@@ -9075,7 +8500,7 @@ async function nearestExistingDirectory(start) {
|
|
|
9075
8500
|
async function externalWatchRoots(targets) {
|
|
9076
8501
|
const roots = new Set;
|
|
9077
8502
|
for (const target of targets) {
|
|
9078
|
-
roots.add(await nearestExistingDirectory(
|
|
8503
|
+
roots.add(await nearestExistingDirectory(path9.dirname(path9.resolve(target))));
|
|
9079
8504
|
}
|
|
9080
8505
|
return [...roots];
|
|
9081
8506
|
}
|
|
@@ -9089,19 +8514,19 @@ async function extensionFolderFiles(dir) {
|
|
|
9089
8514
|
const meta = await readExtensionMeta(dir);
|
|
9090
8515
|
const entry = extensionEntry(meta);
|
|
9091
8516
|
return [
|
|
9092
|
-
|
|
9093
|
-
...entry ? [
|
|
8517
|
+
path9.join(dir, EXTENSION_MANIFEST_FILENAME),
|
|
8518
|
+
...entry ? [path9.resolve(dir, entry)] : [],
|
|
9094
8519
|
...await collectStyleDependencies(dir, extensionStyleList(meta))
|
|
9095
8520
|
];
|
|
9096
8521
|
}
|
|
9097
8522
|
async function externalWatchTargets(projectDir, config) {
|
|
9098
|
-
const root =
|
|
8523
|
+
const root = path9.resolve(projectDir);
|
|
9099
8524
|
let canonicalRoot = root;
|
|
9100
8525
|
try {
|
|
9101
8526
|
canonicalRoot = await fsp3.realpath(root);
|
|
9102
8527
|
} catch {}
|
|
9103
8528
|
const styles = await resolveActiveStyles(root, config.styles);
|
|
9104
|
-
const extensionPaths = (config.extensions ?? []).map((p) => p.path).filter((p) => !!p).map((p) =>
|
|
8529
|
+
const extensionPaths = (config.extensions ?? []).map((p) => p.path).filter((p) => !!p).map((p) => path9.resolve(root, p));
|
|
9105
8530
|
const candidates = [
|
|
9106
8531
|
...await collectStyleDependencies(root, styles),
|
|
9107
8532
|
...extensionPaths,
|
|
@@ -9109,17 +8534,17 @@ async function externalWatchTargets(projectDir, config) {
|
|
|
9109
8534
|
];
|
|
9110
8535
|
const external = new Set;
|
|
9111
8536
|
for (const abs of candidates) {
|
|
9112
|
-
const authoredPath =
|
|
9113
|
-
const authoredRel =
|
|
9114
|
-
if (authoredRel !== "" && (authoredRel === ".." || authoredRel.startsWith(`..${
|
|
8537
|
+
const authoredPath = path9.resolve(abs);
|
|
8538
|
+
const authoredRel = path9.relative(root, authoredPath);
|
|
8539
|
+
if (authoredRel !== "" && (authoredRel === ".." || authoredRel.startsWith(`..${path9.sep}`) || path9.isAbsolute(authoredRel))) {
|
|
9115
8540
|
external.add(authoredPath);
|
|
9116
8541
|
}
|
|
9117
8542
|
let canonicalPath = authoredPath;
|
|
9118
8543
|
try {
|
|
9119
8544
|
canonicalPath = await fsp3.realpath(canonicalPath);
|
|
9120
8545
|
} catch {}
|
|
9121
|
-
const rel =
|
|
9122
|
-
if (rel === "" || rel !== ".." && !rel.startsWith(`..${
|
|
8546
|
+
const rel = path9.relative(canonicalRoot, canonicalPath);
|
|
8547
|
+
if (rel === "" || rel !== ".." && !rel.startsWith(`..${path9.sep}`) && !path9.isAbsolute(rel))
|
|
9123
8548
|
continue;
|
|
9124
8549
|
external.add(canonicalPath);
|
|
9125
8550
|
}
|
|
@@ -9146,7 +8571,7 @@ async function addAndAwaitWatch(watcher, roots) {
|
|
|
9146
8571
|
}
|
|
9147
8572
|
}
|
|
9148
8573
|
function createFileWatcher(state) {
|
|
9149
|
-
const inputResolved =
|
|
8574
|
+
const inputResolved = path9.resolve(state.currentInputPath);
|
|
9150
8575
|
const watcher = watch(inputResolved, {
|
|
9151
8576
|
persistent: true,
|
|
9152
8577
|
ignoreInitial: true,
|
|
@@ -9186,7 +8611,7 @@ function createFileWatcher(state) {
|
|
|
9186
8611
|
async function recordChange(event, filePath) {
|
|
9187
8612
|
if (closed)
|
|
9188
8613
|
return;
|
|
9189
|
-
const target =
|
|
8614
|
+
const target = path9.resolve(filePath);
|
|
9190
8615
|
const settled = settledWrites.get(target);
|
|
9191
8616
|
if (settled && event !== "unlink" && Date.now() <= settled.expiresAt) {
|
|
9192
8617
|
const diskContent = await fsp3.readFile(target, "utf8").catch(() => null);
|
|
@@ -9207,7 +8632,7 @@ function createFileWatcher(state) {
|
|
|
9207
8632
|
recordChange(event, filePath);
|
|
9208
8633
|
});
|
|
9209
8634
|
externalWatcher.on("all", (event, filePath) => {
|
|
9210
|
-
const target =
|
|
8635
|
+
const target = path9.resolve(filePath);
|
|
9211
8636
|
if (!desiredExternals.has(target))
|
|
9212
8637
|
return;
|
|
9213
8638
|
if (event === "add" && suppressInitialExternalAdds.delete(target))
|
|
@@ -9215,8 +8640,8 @@ function createFileWatcher(state) {
|
|
|
9215
8640
|
recordChange(event, target);
|
|
9216
8641
|
});
|
|
9217
8642
|
function isWatchedSource(target) {
|
|
9218
|
-
const rel =
|
|
9219
|
-
const inProject = rel === "" || rel !== ".." && !rel.startsWith(`..${
|
|
8643
|
+
const rel = path9.relative(inputResolved, target);
|
|
8644
|
+
const inProject = rel === "" || rel !== ".." && !rel.startsWith(`..${path9.sep}`) && !path9.isAbsolute(rel);
|
|
9220
8645
|
if (inProject)
|
|
9221
8646
|
return !isIgnoredWatchPath(target, inputResolved);
|
|
9222
8647
|
return desiredExternals.has(target);
|
|
@@ -9241,7 +8666,7 @@ function createFileWatcher(state) {
|
|
|
9241
8666
|
state.notifySettledWrite = (filePath, writtenContent) => {
|
|
9242
8667
|
if (closed)
|
|
9243
8668
|
return;
|
|
9244
|
-
const target =
|
|
8669
|
+
const target = path9.resolve(filePath);
|
|
9245
8670
|
if (isWatchedSource(target)) {
|
|
9246
8671
|
acceptSettledWrite(target, writtenContent);
|
|
9247
8672
|
return;
|
|
@@ -9262,7 +8687,7 @@ function createFileWatcher(state) {
|
|
|
9262
8687
|
return;
|
|
9263
8688
|
let nextTargets;
|
|
9264
8689
|
try {
|
|
9265
|
-
nextTargets = new Set((await externalWatchTargets(inputResolved, state.config)).map((target) =>
|
|
8690
|
+
nextTargets = new Set((await externalWatchTargets(inputResolved, state.config)).map((target) => path9.resolve(target)));
|
|
9266
8691
|
} catch (err) {
|
|
9267
8692
|
debug(`Could not resolve external watch targets: ${err}`);
|
|
9268
8693
|
return;
|
|
@@ -9394,7 +8819,7 @@ async function stopFileWatcher(state) {
|
|
|
9394
8819
|
}
|
|
9395
8820
|
|
|
9396
8821
|
// src/preview/lifecycle.ts
|
|
9397
|
-
var TEMP_DIR_BASE =
|
|
8822
|
+
var TEMP_DIR_BASE = path10.join(tmpdir2(), "gutterpress-preview");
|
|
9398
8823
|
var PID_FILE_NAME = ".gutterpress.pid";
|
|
9399
8824
|
function isProcessAlive(pid) {
|
|
9400
8825
|
if (!Number.isFinite(pid) || pid <= 0)
|
|
@@ -9409,15 +8834,15 @@ function isProcessAlive(pid) {
|
|
|
9409
8834
|
async function cleanupOrphanTempDirs() {
|
|
9410
8835
|
let entries;
|
|
9411
8836
|
try {
|
|
9412
|
-
entries = await
|
|
8837
|
+
entries = await readdir3(TEMP_DIR_BASE);
|
|
9413
8838
|
} catch {
|
|
9414
8839
|
return;
|
|
9415
8840
|
}
|
|
9416
8841
|
for (const entry of entries) {
|
|
9417
|
-
const dir =
|
|
9418
|
-
const pidFile =
|
|
8842
|
+
const dir = path10.join(TEMP_DIR_BASE, entry);
|
|
8843
|
+
const pidFile = path10.join(dir, PID_FILE_NAME);
|
|
9419
8844
|
try {
|
|
9420
|
-
const raw = await
|
|
8845
|
+
const raw = await readFile21(pidFile, "utf8");
|
|
9421
8846
|
const pid = parseInt(raw.trim(), 10);
|
|
9422
8847
|
if (!isProcessAlive(pid)) {
|
|
9423
8848
|
debug(`Removing orphan temp dir ${dir} (pid ${pid} not alive)`);
|
|
@@ -9429,10 +8854,10 @@ async function cleanupOrphanTempDirs() {
|
|
|
9429
8854
|
async function initializePreviewDirectories() {
|
|
9430
8855
|
await cleanupOrphanTempDirs();
|
|
9431
8856
|
const tempDirSuffix = randomBytes2(8).toString("hex");
|
|
9432
|
-
const tempDir =
|
|
8857
|
+
const tempDir = path10.join(TEMP_DIR_BASE, tempDirSuffix);
|
|
9433
8858
|
await mkdir4(tempDir);
|
|
9434
8859
|
debug(`Created temporary directory: ${tempDir}`);
|
|
9435
|
-
await writeFile5(
|
|
8860
|
+
await writeFile5(path10.join(tempDir, PID_FILE_NAME), `${process.pid}
|
|
9436
8861
|
`, "utf8");
|
|
9437
8862
|
return tempDir;
|
|
9438
8863
|
}
|
|
@@ -9522,13 +8947,13 @@ function createServerState(inputPath, tempDir, config, options) {
|
|
|
9522
8947
|
// src/preview/http-server.ts
|
|
9523
8948
|
import http from "node:http";
|
|
9524
8949
|
import net from "node:net";
|
|
9525
|
-
import { createHash as
|
|
9526
|
-
import { readFile as
|
|
9527
|
-
import
|
|
8950
|
+
import { createHash as createHash5, randomUUID } from "node:crypto";
|
|
8951
|
+
import { readFile as readFile22, stat as stat4 } from "node:fs/promises";
|
|
8952
|
+
import path12 from "path";
|
|
9528
8953
|
import { WebSocket, WebSocketServer } from "ws";
|
|
9529
8954
|
|
|
9530
8955
|
// src/lib/static-serve.ts
|
|
9531
|
-
import
|
|
8956
|
+
import path11 from "node:path";
|
|
9532
8957
|
var STATIC_MIME = {
|
|
9533
8958
|
".html": "text/html; charset=utf-8",
|
|
9534
8959
|
".htm": "text/html; charset=utf-8",
|
|
@@ -9550,9 +8975,9 @@ var STATIC_MIME = {
|
|
|
9550
8975
|
".otf": "font/otf"
|
|
9551
8976
|
};
|
|
9552
8977
|
function resolveWithinRoot(relPath, root) {
|
|
9553
|
-
const resolvedRoot =
|
|
9554
|
-
const candidate =
|
|
9555
|
-
if (candidate !== resolvedRoot && !candidate.startsWith(resolvedRoot +
|
|
8978
|
+
const resolvedRoot = path11.resolve(root);
|
|
8979
|
+
const candidate = path11.resolve(resolvedRoot, "." + (relPath.startsWith("/") ? relPath : "/" + relPath));
|
|
8980
|
+
if (candidate !== resolvedRoot && !candidate.startsWith(resolvedRoot + path11.sep)) {
|
|
9556
8981
|
return null;
|
|
9557
8982
|
}
|
|
9558
8983
|
return candidate;
|
|
@@ -9805,7 +9230,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
9805
9230
|
try {
|
|
9806
9231
|
const s = await stat4(filePath);
|
|
9807
9232
|
if (s.isDirectory())
|
|
9808
|
-
filePath =
|
|
9233
|
+
filePath = path12.join(filePath, "index.html");
|
|
9809
9234
|
} catch {
|
|
9810
9235
|
res.writeHead(404);
|
|
9811
9236
|
res.end("Not Found");
|
|
@@ -9813,7 +9238,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
9813
9238
|
}
|
|
9814
9239
|
let data;
|
|
9815
9240
|
try {
|
|
9816
|
-
data = await
|
|
9241
|
+
data = await readFile22(filePath);
|
|
9817
9242
|
} catch {
|
|
9818
9243
|
res.writeHead(404);
|
|
9819
9244
|
res.end("Not Found");
|
|
@@ -9826,7 +9251,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
9826
9251
|
res.end(withHmr);
|
|
9827
9252
|
return;
|
|
9828
9253
|
}
|
|
9829
|
-
const ct = STATIC_MIME[
|
|
9254
|
+
const ct = STATIC_MIME[path12.extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
9830
9255
|
res.writeHead(200, { "Content-Type": ct, "Cache-Control": cacheControl, ...extraHeaders });
|
|
9831
9256
|
res.end(data);
|
|
9832
9257
|
}
|
|
@@ -9836,7 +9261,7 @@ var EMBEDDED_CACHE_CONTROL = "public, no-cache";
|
|
|
9836
9261
|
var EMBEDDED_ETAG = `"gutterpress-${PACKAGE_VERSION}"`;
|
|
9837
9262
|
var PROJECT_ASSET_CACHE_CONTROL = "private, no-cache";
|
|
9838
9263
|
async function serveRevalidatedStatic(req, absPath, res, validators) {
|
|
9839
|
-
const extension =
|
|
9264
|
+
const extension = path12.extname(absPath).toLowerCase();
|
|
9840
9265
|
if (extension === ".html" || extension === ".htm") {
|
|
9841
9266
|
await serveStatic(absPath, res);
|
|
9842
9267
|
return;
|
|
@@ -9863,12 +9288,12 @@ async function serveRevalidatedStatic(req, absPath, res, validators) {
|
|
|
9863
9288
|
let data;
|
|
9864
9289
|
if (!validator || validator.stamp !== stamp) {
|
|
9865
9290
|
try {
|
|
9866
|
-
data = await
|
|
9291
|
+
data = await readFile22(absPath);
|
|
9867
9292
|
} catch {
|
|
9868
9293
|
await serveStatic(absPath, res);
|
|
9869
9294
|
return;
|
|
9870
9295
|
}
|
|
9871
|
-
const digest =
|
|
9296
|
+
const digest = createHash5("sha256").update(data).digest("base64url");
|
|
9872
9297
|
validator = { stamp, etag: `"gutterpress-file-${digest}"` };
|
|
9873
9298
|
validators.set(absPath, validator);
|
|
9874
9299
|
}
|
|
@@ -10002,7 +9427,7 @@ async function createPreviewServer(state, port) {
|
|
|
10002
9427
|
const file = rawFile ? canonicalChapterId(rawFile) : "";
|
|
10003
9428
|
const revision = Number(url.searchParams.get("revision"));
|
|
10004
9429
|
const configuredFiles = state.config.source?.files;
|
|
10005
|
-
if (!file || !state.currentInputPath || !incrementalPreviewEnabled() || !Number.isSafeInteger(revision) || revision !== reloadRevision ||
|
|
9430
|
+
if (!file || !state.currentInputPath || !incrementalPreviewEnabled() || !Number.isSafeInteger(revision) || revision !== reloadRevision || path12.extname(file).toLowerCase() !== ".md" || hasDotSegment(file) || !resolveWithinRoot(file, state.currentInputPath) || Array.isArray(configuredFiles) && configuredFiles.length > 0 && !configuredFiles.some((configured) => canonicalChapterId(configured) === file)) {
|
|
10006
9431
|
res.writeHead(400);
|
|
10007
9432
|
res.end("Bad Request");
|
|
10008
9433
|
return;
|
|
@@ -10202,25 +9627,25 @@ async function startPreviewServer(options) {
|
|
|
10202
9627
|
}
|
|
10203
9628
|
|
|
10204
9629
|
// src/lib/project-scaffold.ts
|
|
10205
|
-
import { access, copyFile, cp as cp2, mkdir as mkdir6, readdir as
|
|
9630
|
+
import { access, copyFile, cp as cp2, mkdir as mkdir6, readdir as readdir4, readFile as readFile24, stat as stat5, writeFile as writeFile6 } from "node:fs/promises";
|
|
10206
9631
|
import { constants as FS, existsSync as existsSync11 } from "node:fs";
|
|
10207
|
-
import
|
|
9632
|
+
import path14 from "node:path";
|
|
10208
9633
|
|
|
10209
9634
|
// src/lib/manifest-doc.ts
|
|
10210
9635
|
import { existsSync as existsSync10 } from "node:fs";
|
|
10211
9636
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
10212
|
-
import { mkdir as mkdir5, open, readFile as
|
|
10213
|
-
import
|
|
9637
|
+
import { mkdir as mkdir5, open, readFile as readFile23, rename, rm as rm2 } from "node:fs/promises";
|
|
9638
|
+
import path13 from "node:path";
|
|
10214
9639
|
import { parseDocument, isSeq, YAMLSeq } from "yaml";
|
|
10215
9640
|
function resolveManifestPath(projectDir) {
|
|
10216
|
-
const existing = MANIFEST_FILENAMES.find((name) => existsSync10(
|
|
10217
|
-
return
|
|
9641
|
+
const existing = MANIFEST_FILENAMES.find((name) => existsSync10(path13.join(projectDir, name)));
|
|
9642
|
+
return path13.join(projectDir, existing ?? MANIFEST_FILENAMES[0]);
|
|
10218
9643
|
}
|
|
10219
9644
|
async function loadManifestDoc(projectDir) {
|
|
10220
9645
|
const file = resolveManifestPath(projectDir);
|
|
10221
9646
|
let text = "";
|
|
10222
9647
|
try {
|
|
10223
|
-
text = await
|
|
9648
|
+
text = await readFile23(file, "utf8");
|
|
10224
9649
|
} catch (error2) {
|
|
10225
9650
|
if (error2.code !== "ENOENT")
|
|
10226
9651
|
throw error2;
|
|
@@ -10228,9 +9653,9 @@ async function loadManifestDoc(projectDir) {
|
|
|
10228
9653
|
return { doc: parseDocument(text), file };
|
|
10229
9654
|
}
|
|
10230
9655
|
async function writeManifestDoc(file, doc) {
|
|
10231
|
-
const dir =
|
|
9656
|
+
const dir = path13.dirname(file);
|
|
10232
9657
|
await mkdir5(dir, { recursive: true });
|
|
10233
|
-
const temporary =
|
|
9658
|
+
const temporary = path13.join(dir, `.${path13.basename(file)}.tmp-${process.pid}-${randomUUID2()}`);
|
|
10234
9659
|
try {
|
|
10235
9660
|
const handle = await open(temporary, "wx", 438);
|
|
10236
9661
|
try {
|
|
@@ -10332,7 +9757,7 @@ async function scaffoldProject(options) {
|
|
|
10332
9757
|
throw new CreateProjectErrorImpl("invalid-name", `Could not derive a valid folder name from "${name}".`);
|
|
10333
9758
|
}
|
|
10334
9759
|
const parentDir = options.parentDir;
|
|
10335
|
-
if (!parentDir || !
|
|
9760
|
+
if (!parentDir || !path14.isAbsolute(parentDir)) {
|
|
10336
9761
|
throw new CreateProjectErrorImpl("parent-not-writable", "The save location must be an absolute path.");
|
|
10337
9762
|
}
|
|
10338
9763
|
try {
|
|
@@ -10340,7 +9765,7 @@ async function scaffoldProject(options) {
|
|
|
10340
9765
|
} catch {
|
|
10341
9766
|
throw new CreateProjectErrorImpl("parent-not-writable", `The chosen save location can't be written to: ${parentDir}`);
|
|
10342
9767
|
}
|
|
10343
|
-
const projectDir =
|
|
9768
|
+
const projectDir = path14.join(parentDir, folderName);
|
|
10344
9769
|
let targetExists = true;
|
|
10345
9770
|
try {
|
|
10346
9771
|
await access(projectDir, FS.F_OK);
|
|
@@ -10357,21 +9782,21 @@ async function scaffoldProject(options) {
|
|
|
10357
9782
|
try {
|
|
10358
9783
|
await mkdir6(projectDir, { recursive: true });
|
|
10359
9784
|
if (customTemplateDir) {
|
|
10360
|
-
const entries = await
|
|
9785
|
+
const entries = await readdir4(customTemplateDir, { withFileTypes: true });
|
|
10361
9786
|
for (const entry of entries) {
|
|
10362
9787
|
if (entry.name === ".gutterpress-template.json")
|
|
10363
9788
|
continue;
|
|
10364
|
-
await cp2(
|
|
9789
|
+
await cp2(path14.join(customTemplateDir, entry.name), path14.join(projectDir, entry.name), { recursive: true });
|
|
10365
9790
|
}
|
|
10366
|
-
await mkdir6(
|
|
9791
|
+
await mkdir6(path14.join(projectDir, "assets"), { recursive: true });
|
|
10367
9792
|
} else {
|
|
10368
9793
|
const tplManifest = await getAssetPath(`templates/${template}/manifest.yaml`);
|
|
10369
9794
|
const tplChapter = await getAssetPath(`templates/${template}/chapter-01.md`);
|
|
10370
|
-
await mkdir6(
|
|
10371
|
-
await copyFile(tplManifest,
|
|
10372
|
-
await copyFile(tplChapter,
|
|
10373
|
-
await mkdir6(
|
|
10374
|
-
await writeFile6(
|
|
9795
|
+
await mkdir6(path14.join(projectDir, "assets"), { recursive: true });
|
|
9796
|
+
await copyFile(tplManifest, path14.join(projectDir, MANIFEST_FILENAMES[0]));
|
|
9797
|
+
await copyFile(tplChapter, path14.join(projectDir, "chapter-01.md"));
|
|
9798
|
+
await mkdir6(path14.join(projectDir, "styles"), { recursive: true });
|
|
9799
|
+
await writeFile6(path14.join(projectDir, "styles", "book.css"), starterOverridesCss(), "utf8");
|
|
10375
9800
|
}
|
|
10376
9801
|
} catch (e) {
|
|
10377
9802
|
throw new CreateProjectErrorImpl("scaffold-io", `Could not create the book files: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -10381,13 +9806,13 @@ async function scaffoldProject(options) {
|
|
|
10381
9806
|
} catch (e) {
|
|
10382
9807
|
throw new CreateProjectErrorImpl("scaffold-io", `Could not write .gitignore: ${e instanceof Error ? e.message : String(e)}`);
|
|
10383
9808
|
}
|
|
10384
|
-
const manifestPath = MANIFEST_FILENAMES.map((name2) =>
|
|
9809
|
+
const manifestPath = MANIFEST_FILENAMES.map((name2) => path14.join(projectDir, name2)).find(existsSync11) ?? path14.join(projectDir, MANIFEST_FILENAMES[0]);
|
|
10385
9810
|
const author = (options.author ?? "").trim() || DEFAULT_AUTHOR;
|
|
10386
9811
|
const substitutions = {
|
|
10387
9812
|
"{{TITLE}}": escapeYamlScalar(name),
|
|
10388
9813
|
"{{AUTHOR}}": escapeYamlScalar(author)
|
|
10389
9814
|
};
|
|
10390
|
-
let openFile =
|
|
9815
|
+
let openFile = path14.join(projectDir, "chapter-01.md");
|
|
10391
9816
|
try {
|
|
10392
9817
|
await fillTemplateFile(manifestPath, substitutions);
|
|
10393
9818
|
if (preset) {
|
|
@@ -10408,7 +9833,7 @@ async function scaffoldProject(options) {
|
|
|
10408
9833
|
}
|
|
10409
9834
|
const firstSource = await firstSourceFile(manifestPath);
|
|
10410
9835
|
if (firstSource)
|
|
10411
|
-
openFile =
|
|
9836
|
+
openFile = path14.join(projectDir, firstSource);
|
|
10412
9837
|
await fillTemplateFile(openFile, { "{{TITLE}}": name }).catch(() => {});
|
|
10413
9838
|
} catch (e) {
|
|
10414
9839
|
throw new CreateProjectErrorImpl("scaffold-io", `Could not finalize the book files: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -10418,7 +9843,7 @@ async function scaffoldProject(options) {
|
|
|
10418
9843
|
let versionHistoryError;
|
|
10419
9844
|
if (requested === "local-git") {
|
|
10420
9845
|
try {
|
|
10421
|
-
const { providerFor } = await import("./source-provider-
|
|
9846
|
+
const { providerFor } = await import("./source-provider-gy3cn7kw.js");
|
|
10422
9847
|
const provider = providerFor({ type: "local-folder", path: projectDir });
|
|
10423
9848
|
await provider.initVersionHistory({
|
|
10424
9849
|
projectDir,
|
|
@@ -10430,7 +9855,7 @@ async function scaffoldProject(options) {
|
|
|
10430
9855
|
versionHistory = "none";
|
|
10431
9856
|
const { findEnclosingRepoDir } = await import("./project-source-ekcyp63q.js");
|
|
10432
9857
|
const enclosingRepoDir = await findEnclosingRepoDir(projectDir);
|
|
10433
|
-
versionHistoryError = enclosingRepoDir ? `This book was added to ${
|
|
9858
|
+
versionHistoryError = enclosingRepoDir ? `This book was added to ${path14.basename(enclosingRepoDir)}'s shared version history; save a snapshot to record it.` : e instanceof Error ? e.message : String(e);
|
|
10434
9859
|
}
|
|
10435
9860
|
}
|
|
10436
9861
|
const result = {
|
|
@@ -10445,10 +9870,10 @@ async function scaffoldProject(options) {
|
|
|
10445
9870
|
return result;
|
|
10446
9871
|
}
|
|
10447
9872
|
async function ensureGitignoreHasDist(projectDir) {
|
|
10448
|
-
const gitignorePath =
|
|
9873
|
+
const gitignorePath = path14.join(projectDir, ".gitignore");
|
|
10449
9874
|
let existing = null;
|
|
10450
9875
|
try {
|
|
10451
|
-
existing = await
|
|
9876
|
+
existing = await readFile24(gitignorePath, "utf8");
|
|
10452
9877
|
} catch {}
|
|
10453
9878
|
if (existing === null) {
|
|
10454
9879
|
await writeFile6(gitignorePath, `dist/
|
|
@@ -10468,7 +9893,7 @@ async function ensureGitignoreHasDist(projectDir) {
|
|
|
10468
9893
|
async function firstSourceFile(manifestPath) {
|
|
10469
9894
|
let text;
|
|
10470
9895
|
try {
|
|
10471
|
-
text = await
|
|
9896
|
+
text = await readFile24(manifestPath, "utf8");
|
|
10472
9897
|
} catch {
|
|
10473
9898
|
return;
|
|
10474
9899
|
}
|
|
@@ -10480,7 +9905,7 @@ async function firstSourceFile(manifestPath) {
|
|
|
10480
9905
|
return m ? m[1].trim() : undefined;
|
|
10481
9906
|
}
|
|
10482
9907
|
async function fillTemplateFile(filePath, substitutions) {
|
|
10483
|
-
let text = await
|
|
9908
|
+
let text = await readFile24(filePath, "utf8");
|
|
10484
9909
|
for (const [token, value] of Object.entries(substitutions)) {
|
|
10485
9910
|
text = text.split(token).join(value);
|
|
10486
9911
|
}
|
|
@@ -10488,9 +9913,9 @@ async function fillTemplateFile(filePath, substitutions) {
|
|
|
10488
9913
|
}
|
|
10489
9914
|
|
|
10490
9915
|
// src/lib/extension-scaffold.ts
|
|
10491
|
-
import { access as access2, mkdir as mkdir7, readFile as
|
|
9916
|
+
import { access as access2, mkdir as mkdir7, readFile as readFile25, writeFile as writeFile7 } from "node:fs/promises";
|
|
10492
9917
|
import { constants as FS2 } from "node:fs";
|
|
10493
|
-
import
|
|
9918
|
+
import path15 from "node:path";
|
|
10494
9919
|
var EXTENSION_KINDS = ["plugin", "theme"];
|
|
10495
9920
|
var RESERVED_PREFIX = "gp-";
|
|
10496
9921
|
var TEMPLATE_FILES = {
|
|
@@ -10568,7 +9993,7 @@ async function scaffoldExtension(options) {
|
|
|
10568
9993
|
}
|
|
10569
9994
|
const prefix = resolveExtensionPrefix(slug, options.prefix);
|
|
10570
9995
|
const parentDir = options.parentDir;
|
|
10571
|
-
if (!parentDir || !
|
|
9996
|
+
if (!parentDir || !path15.isAbsolute(parentDir)) {
|
|
10572
9997
|
throw new ExtensionScaffoldError("parent-not-writable", "The save location must be an absolute path.");
|
|
10573
9998
|
}
|
|
10574
9999
|
try {
|
|
@@ -10576,7 +10001,7 @@ async function scaffoldExtension(options) {
|
|
|
10576
10001
|
} catch {
|
|
10577
10002
|
throw new ExtensionScaffoldError("parent-not-writable", `The chosen save location can't be written to: ${parentDir}`);
|
|
10578
10003
|
}
|
|
10579
|
-
const extensionDir =
|
|
10004
|
+
const extensionDir = path15.join(parentDir, slug);
|
|
10580
10005
|
let targetExists = true;
|
|
10581
10006
|
try {
|
|
10582
10007
|
await access2(extensionDir, FS2.F_OK);
|
|
@@ -10603,9 +10028,9 @@ async function scaffoldExtension(options) {
|
|
|
10603
10028
|
await mkdir7(extensionDir, { recursive: true });
|
|
10604
10029
|
for (const [assetName, relDest] of entries) {
|
|
10605
10030
|
const src = await getAssetPath(`extension-templates/${kind}/${assetName}`);
|
|
10606
|
-
const dest =
|
|
10607
|
-
await mkdir7(
|
|
10608
|
-
const text = await
|
|
10031
|
+
const dest = path15.join(extensionDir, relDest);
|
|
10032
|
+
await mkdir7(path15.dirname(dest), { recursive: true });
|
|
10033
|
+
const text = await readFile25(src, "utf8");
|
|
10609
10034
|
const table = relDest.endsWith(".json") ? jsonSafe : plain;
|
|
10610
10035
|
await writeFile7(dest, substitute(text, table), "utf8");
|
|
10611
10036
|
written.push(relDest);
|
|
@@ -10615,11 +10040,11 @@ async function scaffoldExtension(options) {
|
|
|
10615
10040
|
}
|
|
10616
10041
|
return {
|
|
10617
10042
|
extensionDir,
|
|
10618
|
-
manifestPath:
|
|
10043
|
+
manifestPath: path15.join(extensionDir, "package.json"),
|
|
10619
10044
|
kind,
|
|
10620
10045
|
slug,
|
|
10621
10046
|
prefix,
|
|
10622
|
-
openFile:
|
|
10047
|
+
openFile: path15.join(extensionDir, OPEN_FILE[kind]),
|
|
10623
10048
|
files: written
|
|
10624
10049
|
};
|
|
10625
10050
|
}
|
|
@@ -10649,29 +10074,29 @@ var SKIP_ENTRIES = new Set([
|
|
|
10649
10074
|
]);
|
|
10650
10075
|
|
|
10651
10076
|
// src/lib/extension-manager.ts
|
|
10652
|
-
import { cp as cp3, mkdir as mkdir9, readFile as
|
|
10077
|
+
import { cp as cp3, mkdir as mkdir9, readFile as readFile27, rm as rm4, stat as stat6 } from "node:fs/promises";
|
|
10653
10078
|
import { existsSync as existsSync12 } from "node:fs";
|
|
10654
|
-
import
|
|
10079
|
+
import path17 from "node:path";
|
|
10655
10080
|
import { isSeq as isSeq3, isMap as isMap2, isScalar as isScalar2, YAMLMap, Scalar } from "yaml";
|
|
10656
10081
|
|
|
10657
10082
|
// src/lib/npm-plugin-installer.ts
|
|
10658
|
-
import { createHash as
|
|
10659
|
-
import { createReadStream
|
|
10083
|
+
import { createHash as createHash6, randomUUID as randomUUID3, timingSafeEqual } from "node:crypto";
|
|
10084
|
+
import { createReadStream } from "node:fs";
|
|
10660
10085
|
import {
|
|
10661
|
-
lstat
|
|
10086
|
+
lstat,
|
|
10662
10087
|
mkdir as mkdir8,
|
|
10663
10088
|
mkdtemp as mkdtemp2,
|
|
10664
10089
|
open as open2,
|
|
10665
|
-
readFile as
|
|
10666
|
-
realpath
|
|
10090
|
+
readFile as readFile26,
|
|
10091
|
+
realpath,
|
|
10667
10092
|
rename as rename2,
|
|
10668
10093
|
rm as rm3
|
|
10669
10094
|
} from "node:fs/promises";
|
|
10670
|
-
import
|
|
10095
|
+
import path16 from "node:path";
|
|
10671
10096
|
import { Transform } from "node:stream";
|
|
10672
10097
|
import { pipeline } from "node:stream/promises";
|
|
10673
10098
|
import { createGunzip } from "node:zlib";
|
|
10674
|
-
import { maxSatisfying, satisfies
|
|
10099
|
+
import { maxSatisfying, satisfies, validRange } from "semver";
|
|
10675
10100
|
|
|
10676
10101
|
// src/lib/fetch-timeout.ts
|
|
10677
10102
|
class FetchPassthroughError extends Error {
|
|
@@ -10728,11 +10153,11 @@ class PackageUnavailableError extends Error {
|
|
|
10728
10153
|
function isOptionalAvailabilityError(error2) {
|
|
10729
10154
|
return error2 instanceof PackageUnavailableError || error2 instanceof FriendlyHttpError || error2 instanceof FetchUnavailableError;
|
|
10730
10155
|
}
|
|
10731
|
-
function
|
|
10156
|
+
function object2(value) {
|
|
10732
10157
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
10733
10158
|
}
|
|
10734
|
-
function
|
|
10735
|
-
const map =
|
|
10159
|
+
function stringMap(value) {
|
|
10160
|
+
const map = object2(value);
|
|
10736
10161
|
if (!map)
|
|
10737
10162
|
return {};
|
|
10738
10163
|
const result = {};
|
|
@@ -10745,9 +10170,9 @@ function stringMap2(value) {
|
|
|
10745
10170
|
function mb(bytes) {
|
|
10746
10171
|
return `${Math.ceil(bytes / 1024 / 1024)}MB`;
|
|
10747
10172
|
}
|
|
10748
|
-
function
|
|
10749
|
-
const relative2 =
|
|
10750
|
-
return relative2 === "" || !relative2.startsWith("..") && !
|
|
10173
|
+
function isContained(root, candidate) {
|
|
10174
|
+
const relative2 = path16.relative(root, candidate);
|
|
10175
|
+
return relative2 === "" || !relative2.startsWith("..") && !path16.isAbsolute(relative2);
|
|
10751
10176
|
}
|
|
10752
10177
|
function mergeLimits(overrides) {
|
|
10753
10178
|
const limits = { ...DEFAULT_LIMITS, ...overrides };
|
|
@@ -10825,7 +10250,7 @@ async function fetchPackument(ctx, name) {
|
|
|
10825
10250
|
}
|
|
10826
10251
|
let metadata = null;
|
|
10827
10252
|
try {
|
|
10828
|
-
metadata =
|
|
10253
|
+
metadata = object2(JSON.parse(new TextDecoder().decode(bytes)));
|
|
10829
10254
|
} catch {}
|
|
10830
10255
|
if (!metadata || metadata.name !== name) {
|
|
10831
10256
|
throw new FriendlyHttpError(`npm returned invalid metadata for "${name}".`);
|
|
@@ -10881,29 +10306,29 @@ async function selectPackage(ctx, name, selector) {
|
|
|
10881
10306
|
throw new PackageUnavailableError(`Dependency selector "${selector}" for ${name} is not a registry semver range or dist-tag.`);
|
|
10882
10307
|
}
|
|
10883
10308
|
const metadata = await fetchPackument(ctx, name);
|
|
10884
|
-
const versions =
|
|
10885
|
-
const tags =
|
|
10309
|
+
const versions = object2(metadata.versions);
|
|
10310
|
+
const tags = object2(metadata["dist-tags"]);
|
|
10886
10311
|
if (!versions || !tags)
|
|
10887
10312
|
throw new PackageUnavailableError(`npm metadata for "${name}" has no versions.`);
|
|
10888
10313
|
let version;
|
|
10889
10314
|
if (!selector) {
|
|
10890
10315
|
if (typeof tags.latest === "string")
|
|
10891
10316
|
version = tags.latest;
|
|
10892
|
-
} else if (
|
|
10317
|
+
} else if (object2(versions[selector])) {
|
|
10893
10318
|
version = selector;
|
|
10894
10319
|
} else if (typeof tags[selector] === "string") {
|
|
10895
10320
|
version = tags[selector];
|
|
10896
|
-
} else if (
|
|
10321
|
+
} else if (validRange(selector)) {
|
|
10897
10322
|
version = maxSatisfying(Object.keys(versions), selector) ?? undefined;
|
|
10898
10323
|
}
|
|
10899
10324
|
if (!version || !isExactNpmVersion(version)) {
|
|
10900
10325
|
throw new PackageUnavailableError(`No exact npm version of "${name}" satisfies "${selector ?? "latest"}".`);
|
|
10901
10326
|
}
|
|
10902
|
-
const selected =
|
|
10327
|
+
const selected = object2(versions[version]);
|
|
10903
10328
|
if (!selected || selected.name !== name || selected.version !== version) {
|
|
10904
10329
|
throw new SecurityInstallError(`npm metadata identity failed for "${name}@${version}".`);
|
|
10905
10330
|
}
|
|
10906
|
-
const dist =
|
|
10331
|
+
const dist = object2(selected.dist);
|
|
10907
10332
|
if (!dist || typeof dist.tarball !== "string") {
|
|
10908
10333
|
throw new PackageUnavailableError(`npm metadata for "${name}@${version}" has no tarball.`);
|
|
10909
10334
|
}
|
|
@@ -10920,7 +10345,7 @@ async function selectPackage(ctx, name, selector) {
|
|
|
10920
10345
|
}
|
|
10921
10346
|
async function downloadTarball(ctx, pkg, archivePath) {
|
|
10922
10347
|
const file = await open2(archivePath, "wx");
|
|
10923
|
-
const hash =
|
|
10348
|
+
const hash = createHash6(pkg.integrity.algorithm);
|
|
10924
10349
|
try {
|
|
10925
10350
|
await withFetchTimeout({
|
|
10926
10351
|
timeoutMs: TARBALL_TIMEOUT_MS,
|
|
@@ -11042,7 +10467,7 @@ async function extractTarball(ctx, archivePath, packageDir, packageName) {
|
|
|
11042
10467
|
}
|
|
11043
10468
|
});
|
|
11044
10469
|
try {
|
|
11045
|
-
await pipeline(
|
|
10470
|
+
await pipeline(createReadStream(archivePath), createGunzip(), limiter, unpack);
|
|
11046
10471
|
} catch (error2) {
|
|
11047
10472
|
if (rejected)
|
|
11048
10473
|
throw rejected;
|
|
@@ -11054,13 +10479,13 @@ async function extractTarball(ctx, archivePath, packageDir, packageName) {
|
|
|
11054
10479
|
throw rejected;
|
|
11055
10480
|
}
|
|
11056
10481
|
async function readInstalledManifest(packageDir, selected) {
|
|
11057
|
-
const packageJsonPath =
|
|
10482
|
+
const packageJsonPath = path16.join(packageDir, "package.json");
|
|
11058
10483
|
let manifest = null;
|
|
11059
10484
|
try {
|
|
11060
|
-
const info2 = await
|
|
10485
|
+
const info2 = await lstat(packageJsonPath);
|
|
11061
10486
|
if (!info2.isFile() || info2.isSymbolicLink())
|
|
11062
10487
|
throw new Error("not a regular file");
|
|
11063
|
-
manifest =
|
|
10488
|
+
manifest = object2(JSON.parse(await readFile26(packageJsonPath, "utf8")));
|
|
11064
10489
|
} catch (error2) {
|
|
11065
10490
|
throw new SecurityInstallError(`Downloaded ${selected.name}@${selected.version} has no valid package.json (${error2 instanceof Error ? error2.message : String(error2)}).`);
|
|
11066
10491
|
}
|
|
@@ -11123,17 +10548,17 @@ async function installPackage(ctx, name, selector, parentPackagePath, ancestors,
|
|
|
11123
10548
|
}
|
|
11124
10549
|
throw error2;
|
|
11125
10550
|
}
|
|
11126
|
-
const ancestor = ancestors.find((candidate) => candidate.name === name && (candidate.version === selected.version || (selector ?
|
|
10551
|
+
const ancestor = ancestors.find((candidate) => candidate.name === name && (candidate.version === selected.version || (selector ? satisfies(candidate.version, selector) : false)));
|
|
11127
10552
|
if (ancestor)
|
|
11128
10553
|
return { path: ancestor.path };
|
|
11129
10554
|
if (ctx.records.length >= ctx.limits.totalPackages) {
|
|
11130
10555
|
throw new SecurityInstallError(`Dependency graph exceeds ${ctx.limits.totalPackages} packages.`);
|
|
11131
10556
|
}
|
|
11132
10557
|
const relativePath = relativePackagePath(parentPackagePath, name);
|
|
11133
|
-
const packageDir =
|
|
11134
|
-
const archivePath =
|
|
10558
|
+
const packageDir = path16.join(ctx.stageRoot, ...relativePath.split("/"));
|
|
10559
|
+
const archivePath = path16.join(ctx.downloadsDir, `${ctx.archiveCounter++}.tgz`);
|
|
11135
10560
|
try {
|
|
11136
|
-
await mkdir8(
|
|
10561
|
+
await mkdir8(path16.dirname(packageDir), { recursive: true });
|
|
11137
10562
|
await downloadTarball(ctx, selected, archivePath);
|
|
11138
10563
|
await extractTarball(ctx, archivePath, packageDir, selected.name);
|
|
11139
10564
|
const manifest = await readInstalledManifest(packageDir, selected);
|
|
@@ -11167,19 +10592,19 @@ async function installPackage(ctx, name, selector, parentPackagePath, ancestors,
|
|
|
11167
10592
|
if (selected.integrity.legacySha1) {
|
|
11168
10593
|
ctx.warnings.add(`${name}@${selected.version} is an old npm release with only SHA-1 registry integrity. It was accepted for compatibility and recorded as legacy SHA-1 in the vendor receipt.`);
|
|
11169
10594
|
}
|
|
11170
|
-
const dependencies =
|
|
11171
|
-
const optionalDependencies =
|
|
10595
|
+
const dependencies = stringMap(manifest.dependencies);
|
|
10596
|
+
const optionalDependencies = stringMap(manifest.optionalDependencies);
|
|
11172
10597
|
for (const optionalName of Object.keys(optionalDependencies))
|
|
11173
10598
|
delete dependencies[optionalName];
|
|
11174
|
-
const peerDependencies =
|
|
11175
|
-
const peerMeta =
|
|
10599
|
+
const peerDependencies = stringMap(manifest.peerDependencies);
|
|
10600
|
+
const peerMeta = object2(manifest.peerDependenciesMeta) ?? {};
|
|
11176
10601
|
for (const existing of [...Object.keys(dependencies), ...Object.keys(optionalDependencies)]) {
|
|
11177
10602
|
delete peerDependencies[existing];
|
|
11178
10603
|
}
|
|
11179
10604
|
const nextAncestors = [...ancestors, record];
|
|
11180
10605
|
const requiredEntries = [
|
|
11181
10606
|
...Object.entries(dependencies).map(([dependency, range]) => ({ dependency, range, kind: "required" })),
|
|
11182
|
-
...Object.entries(peerDependencies).filter(([dependency]) =>
|
|
10607
|
+
...Object.entries(peerDependencies).filter(([dependency]) => object2(peerMeta[dependency])?.optional !== true).map(([dependency, range]) => ({ dependency, range, kind: "peer" }))
|
|
11183
10608
|
].sort((a, b) => a.dependency.localeCompare(b.dependency));
|
|
11184
10609
|
for (const { dependency, range } of requiredEntries) {
|
|
11185
10610
|
const child = await installPackage(ctx, dependency, range, relativePath, nextAncestors, depth + 1, false);
|
|
@@ -11197,7 +10622,7 @@ async function installPackage(ctx, name, selector, parentPackagePath, ancestors,
|
|
|
11197
10622
|
}
|
|
11198
10623
|
}
|
|
11199
10624
|
for (const [dependency, range] of Object.entries(peerDependencies)) {
|
|
11200
|
-
if (
|
|
10625
|
+
if (object2(peerMeta[dependency])?.optional === true) {
|
|
11201
10626
|
ctx.skipped.push({
|
|
11202
10627
|
from: relativePath,
|
|
11203
10628
|
name: dependency,
|
|
@@ -11221,7 +10646,7 @@ async function installPackage(ctx, name, selector, parentPackagePath, ancestors,
|
|
|
11221
10646
|
}
|
|
11222
10647
|
async function ensureDirectory(pathname) {
|
|
11223
10648
|
try {
|
|
11224
|
-
const info2 = await
|
|
10649
|
+
const info2 = await lstat(pathname);
|
|
11225
10650
|
if (info2.isSymbolicLink() || !info2.isDirectory()) {
|
|
11226
10651
|
throw new Error(`Plugin install path is not a normal directory: ${pathname}`);
|
|
11227
10652
|
}
|
|
@@ -11232,18 +10657,18 @@ async function ensureDirectory(pathname) {
|
|
|
11232
10657
|
}
|
|
11233
10658
|
}
|
|
11234
10659
|
async function prepareVendorParent(projectDir, name) {
|
|
11235
|
-
const plugins =
|
|
11236
|
-
const npm =
|
|
11237
|
-
const packageParent =
|
|
10660
|
+
const plugins = path16.join(projectDir, PLUGINS_DIR);
|
|
10661
|
+
const npm = path16.join(plugins, VENDORED_NPM_DIR);
|
|
10662
|
+
const packageParent = path16.dirname(vendoredNpmPluginRoot(projectDir, name, "0.0.0"));
|
|
11238
10663
|
await mkdir8(projectDir, { recursive: true });
|
|
11239
10664
|
await ensureDirectory(plugins);
|
|
11240
10665
|
await ensureDirectory(npm);
|
|
11241
10666
|
await ensureDirectory(packageParent);
|
|
11242
10667
|
const [realNpm, realPackageParent] = await Promise.all([
|
|
11243
|
-
|
|
11244
|
-
|
|
10668
|
+
realpath(npm),
|
|
10669
|
+
realpath(packageParent)
|
|
11245
10670
|
]);
|
|
11246
|
-
if (!
|
|
10671
|
+
if (!isContained(projectDir, realNpm) || !isContained(projectDir, realPackageParent)) {
|
|
11247
10672
|
throw new Error("Plugin install path resolves outside the book folder.");
|
|
11248
10673
|
}
|
|
11249
10674
|
return realNpm;
|
|
@@ -11262,14 +10687,14 @@ function publicReceipt(record) {
|
|
|
11262
10687
|
};
|
|
11263
10688
|
}
|
|
11264
10689
|
async function installNpmPlugin(projectDirInput, packageSpec, options = {}) {
|
|
11265
|
-
const requestedProjectDir =
|
|
10690
|
+
const requestedProjectDir = path16.resolve(projectDirInput);
|
|
11266
10691
|
await mkdir8(requestedProjectDir, { recursive: true });
|
|
11267
|
-
const projectDir = await
|
|
10692
|
+
const projectDir = await realpath(requestedProjectDir);
|
|
11268
10693
|
const requested = parseNpmPluginSpec(packageSpec);
|
|
11269
10694
|
const npmRoot = await prepareVendorParent(projectDir, requested.name);
|
|
11270
|
-
const container = await mkdtemp2(
|
|
11271
|
-
const stageRoot =
|
|
11272
|
-
const downloadsDir =
|
|
10695
|
+
const container = await mkdtemp2(path16.join(npmRoot, ".install-"));
|
|
10696
|
+
const stageRoot = path16.join(container, "root");
|
|
10697
|
+
const downloadsDir = path16.join(container, "downloads");
|
|
11273
10698
|
await Promise.all([mkdir8(stageRoot), mkdir8(downloadsDir)]);
|
|
11274
10699
|
const ctx = {
|
|
11275
10700
|
fetch: options.fetch ?? globalThis.fetch,
|
|
@@ -11315,7 +10740,7 @@ async function installNpmPlugin(projectDirInput, packageSpec, options = {}) {
|
|
|
11315
10740
|
skipped: ctx.skipped,
|
|
11316
10741
|
tree: { algorithm: "sha256", ...tree }
|
|
11317
10742
|
};
|
|
11318
|
-
const receiptFile = await open2(
|
|
10743
|
+
const receiptFile = await open2(path16.join(stageRoot, VENDOR_RECEIPT_FILE), "wx");
|
|
11319
10744
|
try {
|
|
11320
10745
|
await receiptFile.writeFile(`${JSON.stringify(receipt, null, 2)}
|
|
11321
10746
|
`, "utf8");
|
|
@@ -11324,12 +10749,12 @@ async function installNpmPlugin(projectDirInput, packageSpec, options = {}) {
|
|
|
11324
10749
|
await receiptFile.close();
|
|
11325
10750
|
}
|
|
11326
10751
|
finalRoot = vendoredNpmPluginRoot(projectDir, rootRecord.name, rootRecord.version);
|
|
11327
|
-
const realFinalParent = await
|
|
11328
|
-
if (!
|
|
10752
|
+
const realFinalParent = await realpath(path16.dirname(finalRoot));
|
|
10753
|
+
if (!isContained(projectDir, realFinalParent)) {
|
|
11329
10754
|
throw new Error("Plugin install destination resolves outside the book folder.");
|
|
11330
10755
|
}
|
|
11331
10756
|
try {
|
|
11332
|
-
await
|
|
10757
|
+
await lstat(finalRoot);
|
|
11333
10758
|
backupRoot = `${finalRoot}.backup-${randomUUID3()}`;
|
|
11334
10759
|
await rename2(finalRoot, backupRoot);
|
|
11335
10760
|
} catch (error2) {
|
|
@@ -11402,7 +10827,7 @@ var RECOMMENDED_EXTENSIONS = [
|
|
|
11402
10827
|
var BUILT_IN_STYLE_SET_IDS = ["clean-book", "zine", "technical-doc"];
|
|
11403
10828
|
var mutationQueues = new Map;
|
|
11404
10829
|
function withMutationLock(projectDir, mutation) {
|
|
11405
|
-
const resolved =
|
|
10830
|
+
const resolved = path17.resolve(projectDir);
|
|
11406
10831
|
const key = process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
11407
10832
|
const previous = mutationQueues.get(key) ?? Promise.resolve();
|
|
11408
10833
|
const run = previous.then(mutation, mutation);
|
|
@@ -11464,7 +10889,7 @@ function indexOfUse(seq, use) {
|
|
|
11464
10889
|
});
|
|
11465
10890
|
}
|
|
11466
10891
|
function manifestPathFor(projectDir, abs) {
|
|
11467
|
-
const rel =
|
|
10892
|
+
const rel = path17.relative(path17.resolve(projectDir), abs).split(path17.sep).join("/");
|
|
11468
10893
|
if (rel === "") {
|
|
11469
10894
|
throw new Error("An extension cannot be the book folder itself.");
|
|
11470
10895
|
}
|
|
@@ -11523,21 +10948,21 @@ async function describeEntry(projectDir, raw) {
|
|
|
11523
10948
|
});
|
|
11524
10949
|
}
|
|
11525
10950
|
if (parsed.kind === "path") {
|
|
11526
|
-
const abs =
|
|
10951
|
+
const abs = path17.resolve(projectDir, parsed.path);
|
|
11527
10952
|
const base2 = { ...common, kind: "path", name: parsed.path };
|
|
11528
10953
|
let info2;
|
|
11529
10954
|
try {
|
|
11530
10955
|
info2 = await stat6(abs);
|
|
11531
10956
|
} catch {
|
|
11532
10957
|
warnings.push(`Not found: ${abs}`);
|
|
11533
|
-
return withWarnings({ ...base2, label:
|
|
10958
|
+
return withWarnings({ ...base2, label: path17.basename(abs), carries: NO_CARRIES });
|
|
11534
10959
|
}
|
|
11535
10960
|
if (info2.isDirectory()) {
|
|
11536
|
-
return withWarnings(await describeFolder(abs, base2, prettify(
|
|
10961
|
+
return withWarnings(await describeFolder(abs, base2, prettify(path17.basename(abs))));
|
|
11537
10962
|
}
|
|
11538
10963
|
return withWarnings({
|
|
11539
10964
|
...base2,
|
|
11540
|
-
label:
|
|
10965
|
+
label: path17.basename(abs),
|
|
11541
10966
|
carries: { ...NO_CARRIES, markdown: true }
|
|
11542
10967
|
});
|
|
11543
10968
|
}
|
|
@@ -11562,7 +10987,7 @@ async function describeEntry(projectDir, raw) {
|
|
|
11562
10987
|
return withWarnings({ ...base, label: parsed.name, carries: { ...NO_CARRIES, markdown: true } });
|
|
11563
10988
|
}
|
|
11564
10989
|
const pkgDir = vendoredNpmPluginPackageDir(installRoot, parsed.name);
|
|
11565
|
-
if (!existsSync12(
|
|
10990
|
+
if (!existsSync12(path17.join(pkgDir, EXTENSION_MANIFEST_FILENAME))) {
|
|
11566
10991
|
return withWarnings({ ...base, label: parsed.name, carries: { ...NO_CARRIES, markdown: true } });
|
|
11567
10992
|
}
|
|
11568
10993
|
const described = await describeFolder(pkgDir, base, parsed.name, (name) => name);
|
|
@@ -11698,11 +11123,11 @@ async function addExtension(projectDir, specifier, options = {}) {
|
|
|
11698
11123
|
async function addExtensionUnlocked(projectDir, specifier, options) {
|
|
11699
11124
|
const input = specifier.trim();
|
|
11700
11125
|
const exportName = options.exportName?.trim() || undefined;
|
|
11701
|
-
if (
|
|
11126
|
+
if (path17.isAbsolute(input))
|
|
11702
11127
|
return addPathExtension(projectDir, input, exportName);
|
|
11703
11128
|
const parsed = parseExtensionSpecifier(input);
|
|
11704
11129
|
if (parsed.kind === "path") {
|
|
11705
|
-
return addPathExtension(projectDir,
|
|
11130
|
+
return addPathExtension(projectDir, path17.resolve(projectDir, parsed.path), exportName);
|
|
11706
11131
|
}
|
|
11707
11132
|
if (parsed.kind === "bundled") {
|
|
11708
11133
|
if (exportName)
|
|
@@ -11743,7 +11168,7 @@ async function addExtensionUnlocked(projectDir, specifier, options) {
|
|
|
11743
11168
|
async function uniqueExtensionId(projectDir, base) {
|
|
11744
11169
|
const slug = slugify(base) || "extension";
|
|
11745
11170
|
let candidate = slug;
|
|
11746
|
-
for (let n = 2;existsSync12(
|
|
11171
|
+
for (let n = 2;existsSync12(path17.join(projectDir, EXTENSIONS_DIR, candidate)); n++) {
|
|
11747
11172
|
candidate = `${slug}-${n}`;
|
|
11748
11173
|
}
|
|
11749
11174
|
return candidate;
|
|
@@ -11753,10 +11178,10 @@ async function addBuiltInStyleSet(projectDir, id) {
|
|
|
11753
11178
|
throw new Error(`Unknown built-in look "${id}" (one of: ${BUILT_IN_STYLE_SET_IDS.join(", ")}).`);
|
|
11754
11179
|
}
|
|
11755
11180
|
return withMutationLock(projectDir, async () => {
|
|
11756
|
-
const dest =
|
|
11181
|
+
const dest = path17.join(projectDir, EXTENSIONS_DIR, id);
|
|
11757
11182
|
if (!existsSync12(dest)) {
|
|
11758
|
-
const src =
|
|
11759
|
-
await mkdir9(
|
|
11183
|
+
const src = path17.join(await getAssetsDir(), "themes", id);
|
|
11184
|
+
await mkdir9(path17.dirname(dest), { recursive: true });
|
|
11760
11185
|
await cp3(src, dest, { recursive: true });
|
|
11761
11186
|
}
|
|
11762
11187
|
return addExtensionUnlocked(projectDir, `./${EXTENSIONS_DIR}/${id}`, {});
|
|
@@ -11765,9 +11190,9 @@ async function addBuiltInStyleSet(projectDir, id) {
|
|
|
11765
11190
|
|
|
11766
11191
|
// src/lib/extension-import.ts
|
|
11767
11192
|
import { existsSync as existsSync13 } from "node:fs";
|
|
11768
|
-
import { cp as cp4, mkdir as mkdir10, mkdtemp as mkdtemp3, readFile as
|
|
11193
|
+
import { cp as cp4, mkdir as mkdir10, mkdtemp as mkdtemp3, readFile as readFile28, rm as rm5, writeFile as writeFile8 } from "node:fs/promises";
|
|
11769
11194
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
11770
|
-
import
|
|
11195
|
+
import path18 from "node:path";
|
|
11771
11196
|
import { unzipSync } from "fflate";
|
|
11772
11197
|
var MAX_THEME_ARCHIVE_BYTES = 25 * 1024 * 1024;
|
|
11773
11198
|
var MAX_THEME_UNZIPPED_BYTES = 25 * 1024 * 1024;
|
|
@@ -11834,11 +11259,11 @@ function mb2(n) {
|
|
|
11834
11259
|
return `${Math.round(n / (1024 * 1024))}MB`;
|
|
11835
11260
|
}
|
|
11836
11261
|
async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallbackName) {
|
|
11837
|
-
const cssPath =
|
|
11262
|
+
const cssPath = path18.join(sourceDir, "theme.css");
|
|
11838
11263
|
if (!existsSync13(cssPath)) {
|
|
11839
11264
|
throw new Error("A theme must contain a theme.css file.");
|
|
11840
11265
|
}
|
|
11841
|
-
const css = await
|
|
11266
|
+
const css = await readFile28(cssPath, "utf8");
|
|
11842
11267
|
const { reject, warnings: printFindings } = classifyThemeCssFindings(checkCss(css, "theme.css"));
|
|
11843
11268
|
if (reject) {
|
|
11844
11269
|
throw new Error(`theme.css could not be parsed — ${reject.message}`);
|
|
@@ -11847,7 +11272,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11847
11272
|
for (const w of printFindings) {
|
|
11848
11273
|
warnings.push({ code: "print-safety", message: w.message });
|
|
11849
11274
|
}
|
|
11850
|
-
const metaExists = existsSync13(
|
|
11275
|
+
const metaExists = existsSync13(path18.join(sourceDir, EXTENSION_MANIFEST_FILENAME));
|
|
11851
11276
|
const meta = metaExists ? await readExtensionMeta(sourceDir) : {};
|
|
11852
11277
|
if (!metaExists) {
|
|
11853
11278
|
warnings.push({
|
|
@@ -11866,7 +11291,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11866
11291
|
for (let i = 0;i < extraSheets.length; i++) {
|
|
11867
11292
|
const rel = extraSheets[i];
|
|
11868
11293
|
const sheetPath = extraSheetPaths[i];
|
|
11869
|
-
const sheetCss = await
|
|
11294
|
+
const sheetCss = await readFile28(sheetPath, "utf8");
|
|
11870
11295
|
const { reject: sheetReject, warnings: sheetFindings } = classifyThemeCssFindings(checkCss(sheetCss, rel));
|
|
11871
11296
|
if (sheetReject) {
|
|
11872
11297
|
throw new Error(`${rel} could not be parsed — ${sheetReject.message}`);
|
|
@@ -11885,8 +11310,8 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11885
11310
|
});
|
|
11886
11311
|
}
|
|
11887
11312
|
const id = await uniqueExtensionId(projectDir, meta.name?.trim() || fallbackName);
|
|
11888
|
-
const dest =
|
|
11889
|
-
await mkdir10(
|
|
11313
|
+
const dest = path18.join(projectDir, EXTENSIONS_DIR, id);
|
|
11314
|
+
await mkdir10(path18.dirname(dest), { recursive: true });
|
|
11890
11315
|
await cp4(sourceDir, dest, { recursive: true });
|
|
11891
11316
|
await completeLandedManifest(dest, id, meta, metaExists);
|
|
11892
11317
|
const entry = await addExtension(projectDir, `./${EXTENSIONS_DIR}/${id}`);
|
|
@@ -11895,11 +11320,11 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11895
11320
|
async function completeLandedManifest(dest, id, meta, metaExists) {
|
|
11896
11321
|
if (metaExists && extensionStyleList(meta).length > 0)
|
|
11897
11322
|
return;
|
|
11898
|
-
const file =
|
|
11323
|
+
const file = path18.join(dest, EXTENSION_MANIFEST_FILENAME);
|
|
11899
11324
|
let pkg = {};
|
|
11900
11325
|
if (metaExists) {
|
|
11901
11326
|
try {
|
|
11902
|
-
const parsed = JSON.parse(await
|
|
11327
|
+
const parsed = JSON.parse(await readFile28(file, "utf8"));
|
|
11903
11328
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
11904
11329
|
pkg = parsed;
|
|
11905
11330
|
}
|
|
@@ -11953,7 +11378,7 @@ async function importExtensionFromZip(projectDir, archive) {
|
|
|
11953
11378
|
throw new Error("No theme.css found in the package. A theme needs a theme.css at the package root or inside a single folder.");
|
|
11954
11379
|
}
|
|
11955
11380
|
const prefix = root === "" ? "" : `${root}/`;
|
|
11956
|
-
const tmp = await mkdtemp3(
|
|
11381
|
+
const tmp = await mkdtemp3(path18.join(tmpdir3(), "gutterpress-theme-"));
|
|
11957
11382
|
try {
|
|
11958
11383
|
const relPaths = [];
|
|
11959
11384
|
for (const [name, data] of Object.entries(files)) {
|
|
@@ -11965,12 +11390,12 @@ async function importExtensionFromZip(projectDir, archive) {
|
|
|
11965
11390
|
const rel = prefix ? norm.slice(prefix.length) : norm;
|
|
11966
11391
|
if (!rel || rel.split("/").some((seg) => seg === ".."))
|
|
11967
11392
|
continue;
|
|
11968
|
-
const dest =
|
|
11969
|
-
if (dest !== tmp && !dest.startsWith(tmp +
|
|
11393
|
+
const dest = path18.join(tmp, rel);
|
|
11394
|
+
if (dest !== tmp && !dest.startsWith(tmp + path18.sep)) {
|
|
11970
11395
|
throw new Error(`The package contains an unsafe path and was rejected: ${name}`);
|
|
11971
11396
|
}
|
|
11972
11397
|
relPaths.push(rel);
|
|
11973
|
-
await mkdir10(
|
|
11398
|
+
await mkdir10(path18.dirname(dest), { recursive: true });
|
|
11974
11399
|
await writeFile8(dest, data);
|
|
11975
11400
|
}
|
|
11976
11401
|
return await finalizeExtensionImport(projectDir, tmp, relPaths, root || "imported-look");
|
|
@@ -11984,10 +11409,10 @@ async function importExtensionFromCssText(projectDir, css, name) {
|
|
|
11984
11409
|
throw new Error(`The CSS could not be parsed — ${reject.message}`);
|
|
11985
11410
|
}
|
|
11986
11411
|
const displayName = name.trim() || "Imported theme";
|
|
11987
|
-
const tmp = await mkdtemp3(
|
|
11412
|
+
const tmp = await mkdtemp3(path18.join(tmpdir3(), "gutterpress-theme-"));
|
|
11988
11413
|
try {
|
|
11989
|
-
await writeFile8(
|
|
11990
|
-
await writeFile8(
|
|
11414
|
+
await writeFile8(path18.join(tmp, "theme.css"), css, "utf8");
|
|
11415
|
+
await writeFile8(path18.join(tmp, EXTENSION_MANIFEST_FILENAME), JSON.stringify({ name: slugify(displayName, "look"), gutterpress: { styles: ["theme.css"] } }, null, 2), "utf8");
|
|
11991
11416
|
const result = await finalizeExtensionImport(projectDir, tmp, ["theme.css", EXTENSION_MANIFEST_FILENAME], displayName);
|
|
11992
11417
|
result.warnings.unshift({
|
|
11993
11418
|
code: "no-theme-json",
|
|
@@ -11999,14 +11424,14 @@ async function importExtensionFromCssText(projectDir, css, name) {
|
|
|
11999
11424
|
}
|
|
12000
11425
|
}
|
|
12001
11426
|
async function importExtensionFromFile(projectDir, filePath) {
|
|
12002
|
-
const ext =
|
|
11427
|
+
const ext = path18.extname(filePath).toLowerCase();
|
|
12003
11428
|
if (ext === ".zip") {
|
|
12004
|
-
const buf = await
|
|
11429
|
+
const buf = await readFile28(filePath);
|
|
12005
11430
|
return importExtensionFromZip(projectDir, new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
|
|
12006
11431
|
}
|
|
12007
11432
|
if (ext === ".css") {
|
|
12008
|
-
const css = await
|
|
12009
|
-
return importExtensionFromCssText(projectDir, css, prettify(
|
|
11433
|
+
const css = await readFile28(filePath, "utf8");
|
|
11434
|
+
return importExtensionFromCssText(projectDir, css, prettify(path18.basename(filePath, ".css")));
|
|
12010
11435
|
}
|
|
12011
11436
|
throw new Error("Choose a .zip extension package or a .css stylesheet.");
|
|
12012
11437
|
}
|
|
@@ -12054,7 +11479,7 @@ async function importExtensionFromUrl(projectDir, url) {
|
|
|
12054
11479
|
let baseName;
|
|
12055
11480
|
if (looksLikeCssUrl(trimmed)) {
|
|
12056
11481
|
css = await fetchText(trimmed);
|
|
12057
|
-
baseName =
|
|
11482
|
+
baseName = path18.basename(new URL(trimmed, "https://x/").pathname, ".css") || "look";
|
|
12058
11483
|
} else {
|
|
12059
11484
|
const base = trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
|
|
12060
11485
|
css = await fetchText(`${base}theme.css`);
|
|
@@ -12063,14 +11488,14 @@ async function importExtensionFromUrl(projectDir, url) {
|
|
|
12063
11488
|
if (parsed && typeof parsed === "object")
|
|
12064
11489
|
meta = parsed;
|
|
12065
11490
|
} catch {}
|
|
12066
|
-
baseName = typeof meta.name === "string" && meta.name ||
|
|
11491
|
+
baseName = typeof meta.name === "string" && meta.name || path18.basename(new URL(base, "https://x/").pathname.replace(/\/$/, "")) || "look";
|
|
12067
11492
|
}
|
|
12068
11493
|
if (!css.trim())
|
|
12069
11494
|
throw new Error(`Fetched CSS from ${trimmed} was empty.`);
|
|
12070
11495
|
assertLooksLikeCss(css, trimmed);
|
|
12071
|
-
const tmp = await mkdtemp3(
|
|
11496
|
+
const tmp = await mkdtemp3(path18.join(tmpdir3(), "gutterpress-ext-"));
|
|
12072
11497
|
try {
|
|
12073
|
-
await writeFile8(
|
|
11498
|
+
await writeFile8(path18.join(tmp, "theme.css"), css, "utf8");
|
|
12074
11499
|
const gp = meta.gutterpress && typeof meta.gutterpress === "object" && !Array.isArray(meta.gutterpress) ? meta.gutterpress : {};
|
|
12075
11500
|
const finalMeta = {
|
|
12076
11501
|
name: typeof meta.name === "string" && meta.name || slugify(baseName, "look"),
|
|
@@ -12082,7 +11507,7 @@ async function importExtensionFromUrl(projectDir, url) {
|
|
|
12082
11507
|
preview: typeof gp.preview === "string" ? gp.preview : null
|
|
12083
11508
|
}
|
|
12084
11509
|
};
|
|
12085
|
-
await writeFile8(
|
|
11510
|
+
await writeFile8(path18.join(tmp, EXTENSION_MANIFEST_FILENAME), JSON.stringify(finalMeta, null, 2), "utf8");
|
|
12086
11511
|
return await finalizeExtensionImport(projectDir, tmp, ["theme.css", EXTENSION_MANIFEST_FILENAME], baseName);
|
|
12087
11512
|
} finally {
|
|
12088
11513
|
await rm5(tmp, { recursive: true, force: true });
|
|
@@ -12100,7 +11525,7 @@ var KIND_FOR_KEYWORD = {
|
|
|
12100
11525
|
function invalid(reason) {
|
|
12101
11526
|
throw new Error(`Malformed npm search response: ${reason}.`);
|
|
12102
11527
|
}
|
|
12103
|
-
function
|
|
11528
|
+
function object3(value) {
|
|
12104
11529
|
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
12105
11530
|
}
|
|
12106
11531
|
function str2(value) {
|
|
@@ -12113,7 +11538,7 @@ function parseSearchPage(text, keyword) {
|
|
|
12113
11538
|
} catch {
|
|
12114
11539
|
invalid("not valid JSON");
|
|
12115
11540
|
}
|
|
12116
|
-
const root =
|
|
11541
|
+
const root = object3(parsed);
|
|
12117
11542
|
if (!root)
|
|
12118
11543
|
invalid("root is not an object");
|
|
12119
11544
|
if (!Array.isArray(root.objects))
|
|
@@ -12123,10 +11548,10 @@ function parseSearchPage(text, keyword) {
|
|
|
12123
11548
|
const packages = [];
|
|
12124
11549
|
const scores = [];
|
|
12125
11550
|
root.objects.forEach((raw, i) => {
|
|
12126
|
-
const entry =
|
|
11551
|
+
const entry = object3(raw);
|
|
12127
11552
|
if (!entry)
|
|
12128
11553
|
invalid(`objects[${i}] is not an object`);
|
|
12129
|
-
const pkg =
|
|
11554
|
+
const pkg = object3(entry.package);
|
|
12130
11555
|
if (!pkg)
|
|
12131
11556
|
invalid(`objects[${i}].package is not an object`);
|
|
12132
11557
|
const name = str2(pkg.name);
|
|
@@ -12140,7 +11565,7 @@ function parseSearchPage(text, keyword) {
|
|
|
12140
11565
|
return;
|
|
12141
11566
|
if (name === "markdown-it" || name.startsWith("@types/"))
|
|
12142
11567
|
return;
|
|
12143
|
-
const links =
|
|
11568
|
+
const links = object3(pkg.links) ?? {};
|
|
12144
11569
|
packages.push({
|
|
12145
11570
|
name,
|
|
12146
11571
|
version,
|
|
@@ -12709,24 +12134,24 @@ ${tail}` : ""}`);
|
|
|
12709
12134
|
|
|
12710
12135
|
// src/lib/publish/providers/guided.ts
|
|
12711
12136
|
import { copyFile as copyFile2, mkdir as mkdir11, writeFile as writeFile9 } from "node:fs/promises";
|
|
12712
|
-
import
|
|
12137
|
+
import path19 from "node:path";
|
|
12713
12138
|
async function stageGuidedPackage(req, providerId, listingLines) {
|
|
12714
|
-
const packageDir =
|
|
12139
|
+
const packageDir = path19.join(path19.dirname(req.artifact.path), "publish", providerId);
|
|
12715
12140
|
await mkdir11(packageDir, { recursive: true });
|
|
12716
|
-
await copyFile2(req.artifact.path,
|
|
12141
|
+
await copyFile2(req.artifact.path, path19.join(packageDir, path19.basename(req.artifact.path)));
|
|
12717
12142
|
const { title, authors } = req.project;
|
|
12718
12143
|
const sheet = [
|
|
12719
12144
|
`# ${title || "Untitled"}`,
|
|
12720
12145
|
"",
|
|
12721
12146
|
`- **Authors:** ${authors.length ? authors.join(", ") : "(not set)"}`,
|
|
12722
|
-
`- **File:** ${
|
|
12147
|
+
`- **File:** ${path19.basename(req.artifact.path)}`,
|
|
12723
12148
|
...listingLines,
|
|
12724
12149
|
"",
|
|
12725
12150
|
"Copy these details into the platform's listing form when uploading.",
|
|
12726
12151
|
""
|
|
12727
12152
|
].join(`
|
|
12728
12153
|
`);
|
|
12729
|
-
await writeFile9(
|
|
12154
|
+
await writeFile9(path19.join(packageDir, "LISTING.md"), sheet, "utf8");
|
|
12730
12155
|
return packageDir;
|
|
12731
12156
|
}
|
|
12732
12157
|
|
|
@@ -12789,9 +12214,9 @@ var drivethrurpgProvider = {
|
|
|
12789
12214
|
};
|
|
12790
12215
|
|
|
12791
12216
|
// src/lib/publish/providers/gdrive.ts
|
|
12792
|
-
import { mkdtemp as mkdtemp4, readFile as
|
|
12217
|
+
import { mkdtemp as mkdtemp4, readFile as readFile29, readdir as readdir5, rm as rm6, stat as stat7, writeFile as writeFile10 } from "node:fs/promises";
|
|
12793
12218
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
12794
|
-
import
|
|
12219
|
+
import path20 from "node:path";
|
|
12795
12220
|
import { Zip, ZipDeflate } from "fflate";
|
|
12796
12221
|
|
|
12797
12222
|
// src/lib/publish/google-drive.ts
|
|
@@ -13174,26 +12599,26 @@ function zipEntriesNonBlocking(entries, opts) {
|
|
|
13174
12599
|
});
|
|
13175
12600
|
}
|
|
13176
12601
|
async function collectZipEntries(root, dir, out, onWarn) {
|
|
13177
|
-
const entries = await
|
|
12602
|
+
const entries = await readdir5(dir, { withFileTypes: true });
|
|
13178
12603
|
for (const entry of entries) {
|
|
13179
|
-
const full =
|
|
12604
|
+
const full = path20.join(dir, entry.name);
|
|
13180
12605
|
if (entry.isDirectory()) {
|
|
13181
12606
|
await collectZipEntries(root, full, out, onWarn);
|
|
13182
12607
|
} else if (entry.isFile()) {
|
|
13183
|
-
const rel =
|
|
13184
|
-
out[rel] = await
|
|
12608
|
+
const rel = path20.relative(root, full).split(path20.sep).join("/");
|
|
12609
|
+
out[rel] = await readFile29(full);
|
|
13185
12610
|
} else if (entry.isSymbolicLink()) {
|
|
13186
12611
|
const resolved = await stat7(full).catch(() => null);
|
|
13187
12612
|
if (resolved?.isFile()) {
|
|
13188
|
-
const rel =
|
|
13189
|
-
out[rel] = await
|
|
12613
|
+
const rel = path20.relative(root, full).split(path20.sep).join("/");
|
|
12614
|
+
out[rel] = await readFile29(full);
|
|
13190
12615
|
} else if (resolved?.isDirectory()) {
|
|
13191
12616
|
await collectZipEntries(root, full, out, onWarn);
|
|
13192
12617
|
} else {
|
|
13193
|
-
onWarn?.(`Skipped "${
|
|
12618
|
+
onWarn?.(`Skipped "${path20.relative(root, full)}" in the website export — it's a broken symlink.`);
|
|
13194
12619
|
}
|
|
13195
12620
|
} else {
|
|
13196
|
-
onWarn?.(`Skipped "${
|
|
12621
|
+
onWarn?.(`Skipped "${path20.relative(root, full)}" in the website export — not a file, folder, or symlink.`);
|
|
13197
12622
|
}
|
|
13198
12623
|
}
|
|
13199
12624
|
}
|
|
@@ -13201,9 +12626,9 @@ async function zipHtmlExport(exportDir, title, onWarn, writeArchiveImpl = writeF
|
|
|
13201
12626
|
const entries = {};
|
|
13202
12627
|
await collectZipEntries(exportDir, exportDir, entries, onWarn);
|
|
13203
12628
|
const fileName = `${bookSlug(title)}-website.zip`;
|
|
13204
|
-
const tmpRoot = await mkdtemp4(
|
|
12629
|
+
const tmpRoot = await mkdtemp4(path20.join(tmpdir4(), "gutterpress-gdrive-"));
|
|
13205
12630
|
try {
|
|
13206
|
-
const filePath =
|
|
12631
|
+
const filePath = path20.join(tmpRoot, fileName);
|
|
13207
12632
|
const zipped = await zipEntriesNonBlocking(entries, { level: 6 });
|
|
13208
12633
|
await writeArchiveImpl(filePath, zipped);
|
|
13209
12634
|
return {
|
|
@@ -13251,7 +12676,7 @@ var gdriveProvider = {
|
|
|
13251
12676
|
const isHtml = req.artifact.format === "html";
|
|
13252
12677
|
const source = isHtml ? await zipHtmlExport(req.artifact.path, req.project.title, (msg) => req.deps.onProgress?.(msg)) : {
|
|
13253
12678
|
filePath: req.artifact.path,
|
|
13254
|
-
fileName:
|
|
12679
|
+
fileName: path20.basename(req.artifact.path),
|
|
13255
12680
|
mimeType: "application/pdf",
|
|
13256
12681
|
cleanup: async () => {}
|
|
13257
12682
|
};
|
|
@@ -13302,7 +12727,7 @@ var gdriveProvider = {
|
|
|
13302
12727
|
|
|
13303
12728
|
// src/lib/publish/butler.ts
|
|
13304
12729
|
import { chmod, mkdir as mkdir12, stat as stat8, writeFile as writeFile11 } from "node:fs/promises";
|
|
13305
|
-
import
|
|
12730
|
+
import path21 from "node:path";
|
|
13306
12731
|
import { unzipSync as unzipSync2 } from "fflate";
|
|
13307
12732
|
var BUTLER_DOWNLOAD_TIMEOUT_MS = 300000;
|
|
13308
12733
|
function butlerBrothChannel(platform = process.platform, arch = process.arch) {
|
|
@@ -13339,8 +12764,8 @@ async function ensureButler(deps) {
|
|
|
13339
12764
|
}
|
|
13340
12765
|
if (await commandExists("butler", runCommand, deps.env))
|
|
13341
12766
|
return "butler";
|
|
13342
|
-
const cacheDir =
|
|
13343
|
-
const cached =
|
|
12767
|
+
const cacheDir = path21.join(deps.configDir ?? defaultConfigDir(), "tools", "butler");
|
|
12768
|
+
const cached = path21.join(cacheDir, butlerBinaryName());
|
|
13344
12769
|
if (await fileExists2(cached))
|
|
13345
12770
|
return cached;
|
|
13346
12771
|
const channel = butlerBrothChannel();
|
|
@@ -13367,9 +12792,9 @@ async function ensureButler(deps) {
|
|
|
13367
12792
|
for (const [name, data] of Object.entries(files)) {
|
|
13368
12793
|
if (name.endsWith("/"))
|
|
13369
12794
|
continue;
|
|
13370
|
-
const target =
|
|
12795
|
+
const target = path21.join(cacheDir, path21.basename(name));
|
|
13371
12796
|
await writeFile11(target, data);
|
|
13372
|
-
if (
|
|
12797
|
+
if (path21.basename(name) === butlerBinaryName()) {
|
|
13373
12798
|
await chmod(target, 493).catch(() => {});
|
|
13374
12799
|
foundBinary = true;
|
|
13375
12800
|
}
|
|
@@ -13798,8 +13223,8 @@ function publishProviderFor(id) {
|
|
|
13798
13223
|
}
|
|
13799
13224
|
|
|
13800
13225
|
// src/lib/publish/run-publish.ts
|
|
13801
|
-
import { readdir as
|
|
13802
|
-
import
|
|
13226
|
+
import { readdir as readdir6, stat as stat9 } from "node:fs/promises";
|
|
13227
|
+
import path22 from "node:path";
|
|
13803
13228
|
function resolvePublishFormat(info8, providerConfig) {
|
|
13804
13229
|
if (info8.formats && info8.formats.length > 0) {
|
|
13805
13230
|
const configured = typeof providerConfig.format === "string" ? providerConfig.format.trim() : "";
|
|
@@ -13820,9 +13245,9 @@ async function resolvePublishRequest(options, deps) {
|
|
|
13820
13245
|
const effectiveDeps = effectiveAccount ? { ...deps, credentialAccount: effectiveAccount } : deps;
|
|
13821
13246
|
const outDir = resolveOutputDir(manifestDir, config.title);
|
|
13822
13247
|
const effectiveFormat = resolvePublishFormat(provider.info, providerConfig);
|
|
13823
|
-
const defaultArtifact = effectiveFormat === "pdf" ?
|
|
13248
|
+
const defaultArtifact = effectiveFormat === "pdf" ? path22.join(outDir, artifactName(config.title, "pdf")) : outDir;
|
|
13824
13249
|
const artifact = {
|
|
13825
|
-
path: options.artifactPath ?
|
|
13250
|
+
path: options.artifactPath ? path22.resolve(options.projectDir, options.artifactPath) : defaultArtifact,
|
|
13826
13251
|
format: effectiveFormat
|
|
13827
13252
|
};
|
|
13828
13253
|
return {
|
|
@@ -13886,7 +13311,7 @@ async function artifactIssues(artifact) {
|
|
|
13886
13311
|
var BUILD_FINGERPRINT_FILENAME = "build-fingerprint.json";
|
|
13887
13312
|
async function htmlDirIssues(dir) {
|
|
13888
13313
|
const issues = [];
|
|
13889
|
-
const isThere = async (rel) => stat9(
|
|
13314
|
+
const isThere = async (rel) => stat9(path22.join(dir, rel)).then(() => true, () => false);
|
|
13890
13315
|
if (!await isThere(BOOK_HTML)) {
|
|
13891
13316
|
issues.push({
|
|
13892
13317
|
severity: "error",
|
|
@@ -13894,7 +13319,7 @@ async function htmlDirIssues(dir) {
|
|
|
13894
13319
|
message: `${dir} has no ${BOOK_HTML} — it isn't an HTML export. ${HTML_HINT}`
|
|
13895
13320
|
});
|
|
13896
13321
|
}
|
|
13897
|
-
const entries = await
|
|
13322
|
+
const entries = await readdir6(dir).catch(() => []);
|
|
13898
13323
|
const extras = entries.filter((name) => name.toLowerCase().endsWith(".pdf") || name === BUILD_FINGERPRINT_FILENAME);
|
|
13899
13324
|
if (extras.length > 0) {
|
|
13900
13325
|
issues.push({
|
|
@@ -14117,17 +13542,17 @@ import git6 from "isomorphic-git";
|
|
|
14117
13542
|
import git5 from "isomorphic-git";
|
|
14118
13543
|
import diff3Merge from "diff3";
|
|
14119
13544
|
// src/lib/publish/selections.ts
|
|
14120
|
-
import { chmod as chmod2, mkdir as mkdir13, readFile as
|
|
14121
|
-
import
|
|
13545
|
+
import { chmod as chmod2, mkdir as mkdir13, readFile as readFile30, writeFile as writeFile12 } from "node:fs/promises";
|
|
13546
|
+
import path23 from "node:path";
|
|
14122
13547
|
class PublishSelectionsStore {
|
|
14123
13548
|
filePath;
|
|
14124
13549
|
queue = Promise.resolve();
|
|
14125
13550
|
constructor(filePath) {
|
|
14126
|
-
this.filePath = filePath ??
|
|
13551
|
+
this.filePath = filePath ?? path23.join(defaultConfigDir(), "publish-selections.json");
|
|
14127
13552
|
}
|
|
14128
13553
|
async read() {
|
|
14129
13554
|
try {
|
|
14130
|
-
const raw = await
|
|
13555
|
+
const raw = await readFile30(this.filePath, "utf8");
|
|
14131
13556
|
const parsed = JSON.parse(raw);
|
|
14132
13557
|
if (parsed && typeof parsed === "object") {
|
|
14133
13558
|
return {
|
|
@@ -14140,7 +13565,7 @@ class PublishSelectionsStore {
|
|
|
14140
13565
|
return { version: 1, global: {}, projects: {} };
|
|
14141
13566
|
}
|
|
14142
13567
|
async write(data) {
|
|
14143
|
-
await mkdir13(
|
|
13568
|
+
await mkdir13(path23.dirname(this.filePath), { recursive: true });
|
|
14144
13569
|
await writeFile12(this.filePath, JSON.stringify(data, null, 2), {
|
|
14145
13570
|
encoding: "utf8",
|
|
14146
13571
|
mode: 384
|
|
@@ -14208,7 +13633,7 @@ function normalizeProjectKey(projectDir) {
|
|
|
14208
13633
|
import { isMap as isMap4 } from "yaml";
|
|
14209
13634
|
// src/lib/lint-runner.ts
|
|
14210
13635
|
import { resolve as resolve8 } from "node:path";
|
|
14211
|
-
import { readFile as
|
|
13636
|
+
import { readFile as readFile31 } from "node:fs/promises";
|
|
14212
13637
|
async function runLint(opts = {}) {
|
|
14213
13638
|
const { glob } = await import("glob");
|
|
14214
13639
|
const { manifest, manifestDir } = await loadManifestWithPath(opts.manifest, {
|
|
@@ -14239,7 +13664,7 @@ async function runLint(opts = {}) {
|
|
|
14239
13664
|
for (const file of files) {
|
|
14240
13665
|
let css;
|
|
14241
13666
|
try {
|
|
14242
|
-
css = await
|
|
13667
|
+
css = await readFile31(file, "utf8");
|
|
14243
13668
|
} catch (err) {
|
|
14244
13669
|
log.error(` ${file}`);
|
|
14245
13670
|
log.error(` cannot read stylesheet: ${err instanceof Error ? err.message : String(err)}`);
|