gutterpress 0.11.23-alpha.1 → 0.11.23-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -22
- package/dist/api/index.d.ts +4 -4
- package/dist/api/index.js +16 -22
- package/dist/{build-09cd1n7s.js → build-encgnv0c.js} +9 -10
- package/dist/{cli-habkc5ng.js → cli-a35w7wzg.js} +5 -4
- package/dist/{cli-b0vyjvmt.js → cli-aqh6brew.js} +1 -1
- package/dist/{cli-ccp7sxz3.js → cli-evg8sbht.js} +1 -1
- package/dist/{cli-nr6j6300.js → cli-kh8v4vvw.js} +1 -1
- package/dist/{cli-h17e3mtz.js → cli-pwwfxdgb.js} +385 -10
- package/dist/{cli-dtckwda1.js → cli-pz4bn98t.js} +185 -9
- package/dist/{cli-dd115fa8.js → cli-sdmv4bba.js} +47 -123
- package/dist/{cli-5hkmp2wy.js → cli-xq6djqbj.js} +55 -88
- package/dist/{index-r7jcsk6h.js → cli-y28j019d.js} +19 -10
- package/dist/cli.js +12 -12
- package/dist/{doctor-nddspx4n.js → doctor-5gtpgpd1.js} +3 -3
- package/dist/{engine-892swzzc.js → engine-mc5br8g8.js} +1 -2
- package/dist/{engine-yjzknt0g.js → engine-tn13gk0b.js} +3 -3
- package/dist/{ext-zjpen4yx.js → ext-wn2a5afq.js} +18 -33
- package/dist/{extension-restore-w8xt4kbq.js → extension-restore-g8jnmmzq.js} +5 -6
- package/dist/{extension-restore-jvs60x9b.js → extension-restore-s3x1618g.js} +4 -4
- package/dist/{index-apc8w9w8.js → index-0skhjx3e.js} +180 -1
- package/dist/{index-2jpk25en.js → index-2bjcghze.js} +385 -10
- package/dist/index-46ycxe6r.js +18 -0
- package/dist/{index-0w2azsk1.js → index-4x6y8krm.js} +2 -4
- package/dist/{cli-h3phdstf.js → index-5x42amh8.js} +19 -10
- package/dist/{index-93pqkvty.js → index-h6yk5tjx.js} +5 -4
- package/dist/{index-zrhyb967.js → index-j5z86r4a.js} +335 -349
- package/dist/{index-m4hhzg3c.js → index-x3ema2w3.js} +59 -134
- package/dist/index.js +17 -23
- package/dist/lib/extension-manager.d.ts +16 -43
- package/dist/lib/extension-scaffold.d.ts +3 -3
- package/dist/lib/extension-specifier.d.ts +23 -6
- package/dist/lib/markdown/math.d.ts +1 -1
- package/dist/lib/npm-plugin-installer.d.ts +1 -1
- package/dist/lib/plugin-vendor.d.ts +11 -3
- package/dist/lib/project-gitignore.d.ts +2 -2
- package/dist/lib/project-scaffold.d.ts +5 -7
- package/dist/lib/project-templates.d.ts +0 -3
- package/dist/lib/snippets.d.ts +2 -1
- package/dist/lib/style-resolver.d.ts +1 -2
- package/dist/{manifest-8f10dr4f.yaml → manifest-eagfhba6.yaml} +2 -0
- package/dist/{manifest.schema-9aeaered.json → manifest.schema-t7nx2stg.json} +5 -5
- package/dist/{new-xhq8wczn.js → new-mhs46pw3.js} +22 -21
- package/dist/{plugins-9en8m9y0.js → plugins-1sxe0dkz.js} +4 -4
- package/dist/{plugins-gf0gkjmv.js → plugins-2cek4mz1.js} +2 -2
- package/dist/{preflight-negnxg7b.js → preflight-3k2b1x8e.js} +9 -10
- package/dist/{preview-cep2s9cg.js → preview-6p9v6rht.js} +9 -10
- package/dist/{publish-xery6qc6.js → publish-qgsngy8s.js} +9 -10
- package/dist/{source-provider-qbfknhyy.js → source-provider-98k5deda.js} +2 -2
- package/dist/{source-provider-2r74a7wp.js → source-provider-knmcam50.js} +2 -2
- package/dist/{validate-3d0sgf4g.js → validate-0v7xw6wa.js} +9 -10
- package/package.json +1 -1
- package/dist/cli-fe43evmf.js +0 -212
- package/dist/index-dsvk7q6s.js +0 -229
- package/dist/package-97gm31sg.json +0 -10
- package/dist/package-gag6ykwn.json +0 -10
- package/dist/package-nrhkwf2m.json +0 -10
- package/dist/package-zttkgydq.json +0 -10
- package/dist/theme-9c58td2d.css +0 -76
- package/dist/theme-h5recz6c.css +0 -61
- package/dist/theme-j2bagrfx.css +0 -59
- package/dist/theme-nn6d53zy.css +0 -71
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEBOUNCE,
|
|
3
|
-
EXTENSIONS_DIR,
|
|
4
3
|
FriendlyHttpError,
|
|
5
4
|
MANIFEST_FILENAMES,
|
|
6
5
|
PRESETS,
|
|
@@ -8,13 +7,11 @@ import {
|
|
|
8
7
|
TARGETS,
|
|
9
8
|
TARGET_IDS,
|
|
10
9
|
UsageError,
|
|
11
|
-
addBuiltInStyleSet,
|
|
12
10
|
addExtension,
|
|
13
11
|
applyDefaultPdfStrictChecks,
|
|
14
12
|
ensureProjectGitignore,
|
|
15
13
|
ensureSeq,
|
|
16
14
|
extensionConfigFor,
|
|
17
|
-
isPathSpecifier,
|
|
18
15
|
listProjectExtensions,
|
|
19
16
|
loadManifest,
|
|
20
17
|
loadManifestDoc,
|
|
@@ -30,7 +27,7 @@ import {
|
|
|
30
27
|
uniqueExtensionId,
|
|
31
28
|
withFetchTimeout,
|
|
32
29
|
writeManifestDoc
|
|
33
|
-
} from "./index-
|
|
30
|
+
} from "./index-x3ema2w3.js";
|
|
34
31
|
import {
|
|
35
32
|
EXTENSION_MANIFEST_FILENAME,
|
|
36
33
|
GUTTERPRESS_CSS,
|
|
@@ -52,6 +49,7 @@ import {
|
|
|
52
49
|
extensionEntry,
|
|
53
50
|
extensionStyleList,
|
|
54
51
|
info,
|
|
52
|
+
isPathSpecifier,
|
|
55
53
|
loadPlugin,
|
|
56
54
|
loadPlugins,
|
|
57
55
|
loadPluginsWithCss,
|
|
@@ -65,21 +63,21 @@ import {
|
|
|
65
63
|
setLogLevel,
|
|
66
64
|
slugify,
|
|
67
65
|
warn
|
|
68
|
-
} from "./index-
|
|
66
|
+
} from "./index-2bjcghze.js";
|
|
69
67
|
import {
|
|
70
68
|
blackTextToGray
|
|
71
69
|
} from "./index-ht1bcqdd.js";
|
|
72
70
|
import {
|
|
73
71
|
MARGIN_BOX_IGNORED_PROPERTIES,
|
|
74
72
|
extract,
|
|
73
|
+
getAssetPath,
|
|
75
74
|
lastPageMode,
|
|
76
75
|
parseSize,
|
|
77
76
|
resolvePage
|
|
78
|
-
} from "./index-
|
|
77
|
+
} from "./index-0skhjx3e.js";
|
|
79
78
|
import {
|
|
80
|
-
BuildError
|
|
81
|
-
|
|
82
|
-
} from "./index-dsvk7q6s.js";
|
|
79
|
+
BuildError
|
|
80
|
+
} from "./index-46ycxe6r.js";
|
|
83
81
|
import {
|
|
84
82
|
countLinkAnnotations,
|
|
85
83
|
getImageResolutions,
|
|
@@ -117,12 +115,13 @@ import {
|
|
|
117
115
|
snapshotStagingMarkerPath,
|
|
118
116
|
snapshotWorkingTreeUnlocked,
|
|
119
117
|
withRepoLock
|
|
120
|
-
} from "./index-
|
|
118
|
+
} from "./index-h6yk5tjx.js";
|
|
121
119
|
import {
|
|
120
|
+
EXTENSIONS_DIR,
|
|
122
121
|
resolveVendoredPluginInstallRoot,
|
|
123
122
|
toPosixPath,
|
|
124
123
|
vendoredNpmPluginPackageDir
|
|
125
|
-
} from "./index-
|
|
124
|
+
} from "./index-5x42amh8.js";
|
|
126
125
|
import {
|
|
127
126
|
detectProjectSource,
|
|
128
127
|
repoSubPath,
|
|
@@ -364,13 +363,7 @@ import { existsSync } from "node:fs";
|
|
|
364
363
|
import { readdir } from "node:fs/promises";
|
|
365
364
|
import path from "node:path";
|
|
366
365
|
var STYLES_SUBDIR = "styles";
|
|
367
|
-
var
|
|
368
|
-
"styles/book.css",
|
|
369
|
-
"css/print.css",
|
|
370
|
-
"css/index.css",
|
|
371
|
-
"css/style.css",
|
|
372
|
-
"css/main.css"
|
|
373
|
-
];
|
|
366
|
+
var DEFAULT_STYLESHEET = "styles/book.css";
|
|
374
367
|
function relDisplay(projectDir, absPath) {
|
|
375
368
|
return path.relative(projectDir, absPath).split(path.sep).join("/");
|
|
376
369
|
}
|
|
@@ -408,10 +401,8 @@ async function resolveActiveStyles(projectDir, manifestStyles) {
|
|
|
408
401
|
const manifest = configured.filter((s) => typeof s === "string" && s.trim().length > 0);
|
|
409
402
|
if (manifest.length > 0)
|
|
410
403
|
return manifest;
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
return [rel];
|
|
414
|
-
}
|
|
404
|
+
if (existsSync(path.join(projectDir, DEFAULT_STYLESHEET)))
|
|
405
|
+
return [DEFAULT_STYLESHEET];
|
|
415
406
|
const discovered = await discoverCssFiles(projectDir);
|
|
416
407
|
if (discovered.length > 0) {
|
|
417
408
|
const first = discovered.map((abs) => relDisplay(projectDir, abs)).sort((a, b) => a.localeCompare(b))[0];
|
|
@@ -5678,7 +5669,7 @@ class PdfOutput {
|
|
|
5678
5669
|
const rawPdf = pdfxMode ? path7.join(stage, "raw.pdf") : path7.resolve(pdfFile);
|
|
5679
5670
|
await fsp2.mkdir(path7.dirname(path7.resolve(pdfFile)), { recursive: true });
|
|
5680
5671
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
5681
|
-
const { buildNativePdf } = await import("./engine-
|
|
5672
|
+
const { buildNativePdf } = await import("./engine-tn13gk0b.js");
|
|
5682
5673
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
5683
5674
|
title: config.title,
|
|
5684
5675
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
@@ -6029,7 +6020,7 @@ function isGeneratedProjectPath(relative) {
|
|
|
6029
6020
|
const segment = segments[i];
|
|
6030
6021
|
if (segment === "dist" || segment === "node_modules")
|
|
6031
6022
|
return true;
|
|
6032
|
-
if (segment === "plugins" && segments[i + 1] === "npm")
|
|
6023
|
+
if ((segment === "extensions" || segment === "plugins") && segments[i + 1] === "npm")
|
|
6033
6024
|
return true;
|
|
6034
6025
|
}
|
|
6035
6026
|
return false;
|
|
@@ -7215,9 +7206,9 @@ async function getSystemDiagnostics() {
|
|
|
7215
7206
|
};
|
|
7216
7207
|
}
|
|
7217
7208
|
// src/lib/project-scaffold.ts
|
|
7218
|
-
import { access
|
|
7219
|
-
import { constants as
|
|
7220
|
-
import
|
|
7209
|
+
import { access, copyFile, cp, mkdir as mkdir6, readdir as readdir6, readFile as readFile25, stat as stat4, writeFile as writeFile8 } from "node:fs/promises";
|
|
7210
|
+
import { constants as FS, existsSync as existsSync9 } from "node:fs";
|
|
7211
|
+
import path14 from "node:path";
|
|
7221
7212
|
|
|
7222
7213
|
// src/lib/page-size.ts
|
|
7223
7214
|
import { existsSync as existsSync8 } from "node:fs";
|
|
@@ -7379,265 +7370,16 @@ async function setPageSetup(projectDir, choice) {
|
|
|
7379
7370
|
return readPageSetup(projectDir);
|
|
7380
7371
|
}
|
|
7381
7372
|
|
|
7382
|
-
// src/lib/project-templates.ts
|
|
7383
|
-
import { cp, mkdir as mkdir6, readFile as readFile25, readdir as readdir6, stat as stat4, writeFile as writeFile8 } from "node:fs/promises";
|
|
7384
|
-
import { constants as FS, existsSync as existsSync9 } from "node:fs";
|
|
7385
|
-
import { access } from "node:fs/promises";
|
|
7386
|
-
import path14 from "node:path";
|
|
7387
|
-
import { isMap as isMap2, isScalar, isSeq, parseDocument } from "yaml";
|
|
7388
|
-
var BUILT_IN_TEMPLATE_IDS = [
|
|
7389
|
-
"book",
|
|
7390
|
-
"zine",
|
|
7391
|
-
"technical",
|
|
7392
|
-
"paper"
|
|
7393
|
-
];
|
|
7394
|
-
async function readTemplateChoices(manifestPath) {
|
|
7395
|
-
try {
|
|
7396
|
-
const text = await readFile25(manifestPath, "utf8");
|
|
7397
|
-
const doc = parseDocument(text);
|
|
7398
|
-
const out = {};
|
|
7399
|
-
const preset = doc.get("preset");
|
|
7400
|
-
if (typeof preset === "string" && preset)
|
|
7401
|
-
out.preset = preset;
|
|
7402
|
-
const targets = doc.get("targets", true);
|
|
7403
|
-
if (isSeq(targets)) {
|
|
7404
|
-
out.targets = targets.items.map((i) => scalarString(i)).filter((s) => s !== null);
|
|
7405
|
-
}
|
|
7406
|
-
return out;
|
|
7407
|
-
} catch {
|
|
7408
|
-
return {};
|
|
7409
|
-
}
|
|
7410
|
-
}
|
|
7411
|
-
var BUILT_IN_META = {
|
|
7412
|
-
book: {
|
|
7413
|
-
label: "Book",
|
|
7414
|
-
description: "A clean starting point for a novel, memoir, or any long-form book."
|
|
7415
|
-
},
|
|
7416
|
-
zine: {
|
|
7417
|
-
label: "Zine",
|
|
7418
|
-
description: "A short, personal zine made to be printed, folded, and shared."
|
|
7419
|
-
},
|
|
7420
|
-
technical: {
|
|
7421
|
-
label: "Technical document",
|
|
7422
|
-
description: "A manual or guide with steps, reference tables, and code examples."
|
|
7423
|
-
},
|
|
7424
|
-
paper: {
|
|
7425
|
-
label: "Academic paper",
|
|
7426
|
-
description: "A journal-style article with LaTeX math, numbered equations, figures, tables, notes and references.",
|
|
7427
|
-
look: "academic"
|
|
7428
|
-
}
|
|
7429
|
-
};
|
|
7430
|
-
async function listBuiltInTemplates() {
|
|
7431
|
-
return Promise.all(BUILT_IN_TEMPLATE_IDS.map(async (id) => ({
|
|
7432
|
-
id,
|
|
7433
|
-
label: BUILT_IN_META[id].label,
|
|
7434
|
-
description: BUILT_IN_META[id].description,
|
|
7435
|
-
kind: "builtin",
|
|
7436
|
-
...await readTemplateChoices(await getAssetPath(`templates/${id}/manifest.yaml`))
|
|
7437
|
-
})));
|
|
7438
|
-
}
|
|
7439
|
-
var SKIP_ENTRIES = new Set([
|
|
7440
|
-
".git",
|
|
7441
|
-
"node_modules",
|
|
7442
|
-
"out",
|
|
7443
|
-
"dist",
|
|
7444
|
-
"build",
|
|
7445
|
-
".gutterpress",
|
|
7446
|
-
".DS_Store"
|
|
7447
|
-
]);
|
|
7448
|
-
var VENDOR_DIR = "shared";
|
|
7449
|
-
function collectEscaping(nodes, getValue, escapes) {
|
|
7450
|
-
const out = [];
|
|
7451
|
-
for (const node of nodes) {
|
|
7452
|
-
const value = getValue(node);
|
|
7453
|
-
if (value !== null && escapes(value))
|
|
7454
|
-
out.push({ node, value });
|
|
7455
|
-
}
|
|
7456
|
-
return out;
|
|
7457
|
-
}
|
|
7458
|
-
function extensionEntryPath(node) {
|
|
7459
|
-
const use = isMap2(node) ? scalarString(node.get("use", true)) : scalarString(node);
|
|
7460
|
-
return use !== null && isPathSpecifier(use) ? use : null;
|
|
7461
|
-
}
|
|
7462
|
-
function dropEscaping(doc, seq, key, escaping) {
|
|
7463
|
-
if (!isSeq(seq))
|
|
7464
|
-
return;
|
|
7465
|
-
const drop = new Set(escaping.map((e) => e.node));
|
|
7466
|
-
seq.items = seq.items.filter((node) => !drop.has(node));
|
|
7467
|
-
if (seq.items.length === 0)
|
|
7468
|
-
doc.delete(key);
|
|
7469
|
-
}
|
|
7470
|
-
function rewriteEntry(node, newPath) {
|
|
7471
|
-
if (isMap2(node))
|
|
7472
|
-
node.set("use", newPath);
|
|
7473
|
-
else if (isScalar(node))
|
|
7474
|
-
node.value = newPath;
|
|
7475
|
-
}
|
|
7476
|
-
function commonAncestorDir(absFiles) {
|
|
7477
|
-
const dirSegs = absFiles.map((f) => path14.resolve(f).split(path14.sep).slice(0, -1));
|
|
7478
|
-
if (dirSegs.length === 0)
|
|
7479
|
-
return "";
|
|
7480
|
-
let common = dirSegs[0];
|
|
7481
|
-
for (const segs of dirSegs.slice(1)) {
|
|
7482
|
-
let i = 0;
|
|
7483
|
-
while (i < common.length && i < segs.length && common[i] === segs[i])
|
|
7484
|
-
i++;
|
|
7485
|
-
common = common.slice(0, i);
|
|
7486
|
-
}
|
|
7487
|
-
return common.join(path14.sep) || path14.sep;
|
|
7488
|
-
}
|
|
7489
|
-
function pickVendorDir(templateDir, base) {
|
|
7490
|
-
const preferred = path14.basename(base) || VENDOR_DIR;
|
|
7491
|
-
if (!existsSync9(path14.join(templateDir, preferred)))
|
|
7492
|
-
return preferred;
|
|
7493
|
-
for (let n = 1;; n++) {
|
|
7494
|
-
const candidate = `${preferred}-${n}`;
|
|
7495
|
-
if (!existsSync9(path14.join(templateDir, candidate)))
|
|
7496
|
-
return candidate;
|
|
7497
|
-
}
|
|
7498
|
-
}
|
|
7499
|
-
var TEMPLATE_META_FILE = ".gutterpress-template.json";
|
|
7500
|
-
async function reconcileSharedRefs(templateDir, sourceProjectDir, mode) {
|
|
7501
|
-
const { doc, file } = await loadManifestDoc(templateDir);
|
|
7502
|
-
const escapes = (p) => escapesProjectRoot(sourceProjectDir, path14.resolve(sourceProjectDir, p));
|
|
7503
|
-
const stylesSeq = doc.get("styles", true);
|
|
7504
|
-
const extensionsSeq = doc.get("extensions", true);
|
|
7505
|
-
const escapingStyles = collectEscaping(isSeq(stylesSeq) ? stylesSeq.items : [], scalarString, escapes);
|
|
7506
|
-
const escapingExtensions = collectEscaping(isSeq(extensionsSeq) ? extensionsSeq.items : [], extensionEntryPath, escapes);
|
|
7507
|
-
if (escapingStyles.length === 0 && escapingExtensions.length === 0) {
|
|
7508
|
-
return { vendoredRefs: [], excludedRefs: [] };
|
|
7509
|
-
}
|
|
7510
|
-
if (mode === "exclude") {
|
|
7511
|
-
const excludedRefs = [...escapingStyles, ...escapingExtensions].map((e) => e.value);
|
|
7512
|
-
dropEscaping(doc, stylesSeq, "styles", escapingStyles);
|
|
7513
|
-
dropEscaping(doc, extensionsSeq, "extensions", escapingExtensions);
|
|
7514
|
-
await writeManifestDoc(file, doc);
|
|
7515
|
-
return { vendoredRefs: [], excludedRefs };
|
|
7516
|
-
}
|
|
7517
|
-
const styleClosure = await collectStyleDependencies(sourceProjectDir, escapingStyles.map((s) => s.value));
|
|
7518
|
-
const extensionFiles = escapingExtensions.map((p) => path14.resolve(sourceProjectDir, p.value));
|
|
7519
|
-
const externalFiles = [...new Set([...styleClosure, ...extensionFiles])].filter((f) => escapesProjectRoot(sourceProjectDir, f));
|
|
7520
|
-
const base = commonAncestorDir(externalFiles);
|
|
7521
|
-
const vendorDir = pickVendorDir(templateDir, base);
|
|
7522
|
-
await Promise.all(externalFiles.filter((abs) => existsSync9(abs)).map(async (abs) => {
|
|
7523
|
-
const dest = path14.join(templateDir, vendorDir, path14.relative(base, abs));
|
|
7524
|
-
await mkdir6(path14.dirname(dest), { recursive: true });
|
|
7525
|
-
await cp(abs, dest, { recursive: true });
|
|
7526
|
-
}));
|
|
7527
|
-
const bookLocal = (value) => toPosixPath(path14.join(vendorDir, path14.relative(base, path14.resolve(sourceProjectDir, value))));
|
|
7528
|
-
const vendoredRefs = [];
|
|
7529
|
-
for (const { node, value } of escapingStyles) {
|
|
7530
|
-
const rewritten = bookLocal(value);
|
|
7531
|
-
rewriteEntry(node, rewritten);
|
|
7532
|
-
vendoredRefs.push(rewritten);
|
|
7533
|
-
}
|
|
7534
|
-
for (const { node, value } of escapingExtensions) {
|
|
7535
|
-
const rewritten = bookLocal(value);
|
|
7536
|
-
rewriteEntry(node, `./${rewritten}`);
|
|
7537
|
-
vendoredRefs.push(rewritten);
|
|
7538
|
-
}
|
|
7539
|
-
await writeManifestDoc(file, doc);
|
|
7540
|
-
return { vendoredRefs, excludedRefs: [] };
|
|
7541
|
-
}
|
|
7542
|
-
async function saveProjectAsTemplate(options) {
|
|
7543
|
-
const { projectDir, name, templatesRoot } = options;
|
|
7544
|
-
const id = slugify(name);
|
|
7545
|
-
if (!id)
|
|
7546
|
-
throw new Error(`Could not derive a template id from "${name}".`);
|
|
7547
|
-
const dir = path14.join(templatesRoot, id);
|
|
7548
|
-
let exists = true;
|
|
7549
|
-
try {
|
|
7550
|
-
await access(dir, FS.F_OK);
|
|
7551
|
-
} catch {
|
|
7552
|
-
exists = false;
|
|
7553
|
-
}
|
|
7554
|
-
if (exists) {
|
|
7555
|
-
throw new Error(`A template named "${name}" already exists. Choose a different name.`);
|
|
7556
|
-
}
|
|
7557
|
-
await mkdir6(dir, { recursive: true });
|
|
7558
|
-
const entries = await readdir6(projectDir, { withFileTypes: true });
|
|
7559
|
-
for (const entry of entries) {
|
|
7560
|
-
if (SKIP_ENTRIES.has(entry.name))
|
|
7561
|
-
continue;
|
|
7562
|
-
if (entry.name === TEMPLATE_META_FILE)
|
|
7563
|
-
continue;
|
|
7564
|
-
await cp(path14.join(projectDir, entry.name), path14.join(dir, entry.name), { recursive: true });
|
|
7565
|
-
}
|
|
7566
|
-
const manifestName = MANIFEST_FILENAMES.find((name2) => entries.some((entry) => entry.name === name2));
|
|
7567
|
-
const shared = manifestName ? await reconcileSharedRefs(dir, projectDir, options.sharedRefs ?? "vendor") : { vendoredRefs: [], excludedRefs: [] };
|
|
7568
|
-
if (manifestName)
|
|
7569
|
-
await retokeniseManifest(path14.join(dir, manifestName));
|
|
7570
|
-
await writeFile8(path14.join(dir, TEMPLATE_META_FILE), JSON.stringify({ label: name }, null, 2), "utf8");
|
|
7571
|
-
return {
|
|
7572
|
-
id,
|
|
7573
|
-
label: name,
|
|
7574
|
-
description: "Your saved template.",
|
|
7575
|
-
kind: "custom",
|
|
7576
|
-
dir,
|
|
7577
|
-
...shared
|
|
7578
|
-
};
|
|
7579
|
-
}
|
|
7580
|
-
async function retokeniseManifest(manifestPath) {
|
|
7581
|
-
let text;
|
|
7582
|
-
try {
|
|
7583
|
-
text = await readFile25(manifestPath, "utf8");
|
|
7584
|
-
} catch {
|
|
7585
|
-
return;
|
|
7586
|
-
}
|
|
7587
|
-
text = text.replace(/^title:\s*.*$/m, 'title: "{{TITLE}}"').replace(/^(authors:[^\n]*\n[ \t]*-[ \t]*).*$/m, '$1"{{AUTHOR}}"');
|
|
7588
|
-
await writeFile8(manifestPath, text, "utf8");
|
|
7589
|
-
}
|
|
7590
|
-
async function listCustomTemplates(templatesRoot) {
|
|
7591
|
-
let entries;
|
|
7592
|
-
try {
|
|
7593
|
-
entries = await readdir6(templatesRoot, { withFileTypes: true });
|
|
7594
|
-
} catch {
|
|
7595
|
-
return [];
|
|
7596
|
-
}
|
|
7597
|
-
const out = [];
|
|
7598
|
-
for (const entry of entries) {
|
|
7599
|
-
if (!entry.isDirectory())
|
|
7600
|
-
continue;
|
|
7601
|
-
const dir = path14.join(templatesRoot, entry.name);
|
|
7602
|
-
let label = prettify(entry.name);
|
|
7603
|
-
try {
|
|
7604
|
-
const meta = JSON.parse(await readFile25(path14.join(dir, TEMPLATE_META_FILE), "utf8"));
|
|
7605
|
-
if (typeof meta.label === "string" && meta.label)
|
|
7606
|
-
label = meta.label;
|
|
7607
|
-
} catch {}
|
|
7608
|
-
out.push({
|
|
7609
|
-
id: entry.name,
|
|
7610
|
-
label,
|
|
7611
|
-
description: "Your saved template.",
|
|
7612
|
-
kind: "custom",
|
|
7613
|
-
dir,
|
|
7614
|
-
...await readTemplateChoices(path14.join(dir, MANIFEST_FILENAMES[0]))
|
|
7615
|
-
});
|
|
7616
|
-
}
|
|
7617
|
-
out.sort((a, b) => a.label.localeCompare(b.label));
|
|
7618
|
-
return out;
|
|
7619
|
-
}
|
|
7620
|
-
async function importTemplateFromFolder(options) {
|
|
7621
|
-
const { sourceDir, templatesRoot } = options;
|
|
7622
|
-
const srcStat = await stat4(sourceDir);
|
|
7623
|
-
if (!srcStat.isDirectory()) {
|
|
7624
|
-
throw new Error("The chosen path is not a folder.");
|
|
7625
|
-
}
|
|
7626
|
-
const name = options.name?.trim() || prettify(path14.basename(sourceDir));
|
|
7627
|
-
return saveProjectAsTemplate({ projectDir: sourceDir, name, templatesRoot });
|
|
7628
|
-
}
|
|
7629
|
-
|
|
7630
7373
|
// src/lib/project-scaffold.ts
|
|
7631
7374
|
function starterOverridesCss() {
|
|
7632
7375
|
return `/**
|
|
7633
7376
|
* Your book's own styles.
|
|
7634
7377
|
*
|
|
7635
7378
|
* This file loads AFTER every extension, so any rule you add here overrides
|
|
7636
|
-
* a look's defaults. A new book has no look until
|
|
7637
|
-
*
|
|
7638
|
-
* gutterpress ext add
|
|
7639
|
-
* gutterpress ext add <package> <book>
|
|
7640
|
-
* A book has one look at a time; adding another turns the current one off.
|
|
7379
|
+
* a look's defaults. A new book has no look until its template or you add
|
|
7380
|
+
* one:
|
|
7381
|
+
* gutterpress ext add academic <book> (a built-in look)
|
|
7382
|
+
* gutterpress ext add <package> <book> (a look from npm)
|
|
7641
7383
|
*/
|
|
7642
7384
|
`;
|
|
7643
7385
|
}
|
|
@@ -7658,9 +7400,8 @@ function escapeYamlScalar(value) {
|
|
|
7658
7400
|
}
|
|
7659
7401
|
var DEFAULT_AUTHOR = "Anonymous";
|
|
7660
7402
|
function requirePreset(preset, customPage) {
|
|
7661
|
-
if (!
|
|
7662
|
-
|
|
7663
|
-
throw new CreateProjectErrorImpl("preset-required", `A preset is required to create a book${got}. Choose one of: ${PRESET_IDS.join(", ")}.`);
|
|
7403
|
+
if (!PRESET_IDS.includes(preset)) {
|
|
7404
|
+
throw new CreateProjectErrorImpl("preset-required", `Unknown preset "${preset}". Choose one of: ${PRESET_IDS.join(", ")}.`);
|
|
7664
7405
|
}
|
|
7665
7406
|
if (preset === "custom") {
|
|
7666
7407
|
const missing = [];
|
|
@@ -7698,18 +7439,18 @@ async function scaffoldProject(options) {
|
|
|
7698
7439
|
throw new CreateProjectErrorImpl("invalid-name", `Could not derive a valid folder name from "${name}".`);
|
|
7699
7440
|
}
|
|
7700
7441
|
const parentDir = options.parentDir;
|
|
7701
|
-
if (!parentDir || !
|
|
7442
|
+
if (!parentDir || !path14.isAbsolute(parentDir)) {
|
|
7702
7443
|
throw new CreateProjectErrorImpl("parent-not-writable", "The save location must be an absolute path.");
|
|
7703
7444
|
}
|
|
7704
7445
|
try {
|
|
7705
|
-
await
|
|
7446
|
+
await access(parentDir, FS.W_OK);
|
|
7706
7447
|
} catch {
|
|
7707
7448
|
throw new CreateProjectErrorImpl("parent-not-writable", `The chosen save location can't be written to: ${parentDir}`);
|
|
7708
7449
|
}
|
|
7709
|
-
const projectDir =
|
|
7450
|
+
const projectDir = path14.join(parentDir, folderName);
|
|
7710
7451
|
let targetExists = true;
|
|
7711
7452
|
try {
|
|
7712
|
-
await
|
|
7453
|
+
await access(projectDir, FS.F_OK);
|
|
7713
7454
|
} catch {
|
|
7714
7455
|
targetExists = false;
|
|
7715
7456
|
}
|
|
@@ -7718,26 +7459,26 @@ async function scaffoldProject(options) {
|
|
|
7718
7459
|
}
|
|
7719
7460
|
const template = options.template ?? "book";
|
|
7720
7461
|
const customTemplateDir = options.templateDir;
|
|
7721
|
-
const preset =
|
|
7462
|
+
const preset = options.preset === undefined ? undefined : requirePreset(options.preset, options.customPage);
|
|
7722
7463
|
const targets = preset ? resolveScaffoldTargets(options.targets, preset) : undefined;
|
|
7723
7464
|
try {
|
|
7724
|
-
await
|
|
7465
|
+
await mkdir6(projectDir, { recursive: true });
|
|
7725
7466
|
if (customTemplateDir) {
|
|
7726
|
-
const entries = await
|
|
7467
|
+
const entries = await readdir6(customTemplateDir, { withFileTypes: true });
|
|
7727
7468
|
for (const entry of entries) {
|
|
7728
7469
|
if (entry.name === ".gutterpress-template.json")
|
|
7729
7470
|
continue;
|
|
7730
|
-
await
|
|
7471
|
+
await cp(path14.join(customTemplateDir, entry.name), path14.join(projectDir, entry.name), { recursive: true });
|
|
7731
7472
|
}
|
|
7732
|
-
await
|
|
7473
|
+
await mkdir6(path14.join(projectDir, "assets"), { recursive: true });
|
|
7733
7474
|
} else {
|
|
7734
7475
|
const tplManifest = await getAssetPath(`templates/${template}/manifest.yaml`);
|
|
7735
7476
|
const tplChapter = await getAssetPath(`templates/${template}/chapter-01.md`);
|
|
7736
|
-
await
|
|
7737
|
-
await copyFile(tplManifest,
|
|
7738
|
-
await copyFile(tplChapter,
|
|
7739
|
-
await
|
|
7740
|
-
await
|
|
7477
|
+
await mkdir6(path14.join(projectDir, "assets"), { recursive: true });
|
|
7478
|
+
await copyFile(tplManifest, path14.join(projectDir, MANIFEST_FILENAMES[0]));
|
|
7479
|
+
await copyFile(tplChapter, path14.join(projectDir, "chapter-01.md"));
|
|
7480
|
+
await mkdir6(path14.join(projectDir, "styles"), { recursive: true });
|
|
7481
|
+
await writeFile8(path14.join(projectDir, "styles", "book.css"), starterOverridesCss(), "utf8");
|
|
7741
7482
|
}
|
|
7742
7483
|
} catch (e) {
|
|
7743
7484
|
throw new CreateProjectErrorImpl("scaffold-io", `Could not create the book files: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -7747,29 +7488,28 @@ async function scaffoldProject(options) {
|
|
|
7747
7488
|
} catch (e) {
|
|
7748
7489
|
throw new CreateProjectErrorImpl("scaffold-io", `Could not write .gitignore: ${e instanceof Error ? e.message : String(e)}`);
|
|
7749
7490
|
}
|
|
7750
|
-
const manifestPath = MANIFEST_FILENAMES.map((name2) =>
|
|
7491
|
+
const manifestPath = MANIFEST_FILENAMES.map((name2) => path14.join(projectDir, name2)).find(existsSync9) ?? path14.join(projectDir, MANIFEST_FILENAMES[0]);
|
|
7751
7492
|
const author = (options.author ?? "").trim() || DEFAULT_AUTHOR;
|
|
7752
7493
|
const substitutions = {
|
|
7753
7494
|
"{{TITLE}}": escapeYamlScalar(name),
|
|
7754
7495
|
"{{AUTHOR}}": escapeYamlScalar(author)
|
|
7755
7496
|
};
|
|
7756
|
-
let openFile =
|
|
7497
|
+
let openFile = path14.join(projectDir, "chapter-01.md");
|
|
7757
7498
|
try {
|
|
7758
7499
|
await fillTemplateFile(manifestPath, substitutions);
|
|
7759
|
-
|
|
7760
|
-
if (look)
|
|
7761
|
-
await addBuiltInStyleSet(projectDir, look);
|
|
7762
|
-
if (preset) {
|
|
7500
|
+
if (preset || options.customPage) {
|
|
7763
7501
|
const { doc, file } = await loadManifestDoc(projectDir);
|
|
7764
|
-
|
|
7765
|
-
|
|
7502
|
+
if (preset) {
|
|
7503
|
+
doc.set("preset", preset);
|
|
7504
|
+
doc.set("targets", doc.createNode(targets ?? []));
|
|
7505
|
+
}
|
|
7766
7506
|
if (options.customPage)
|
|
7767
7507
|
writePageBounds(doc, options.customPage);
|
|
7768
7508
|
await writeManifestDoc(file, doc);
|
|
7769
7509
|
}
|
|
7770
7510
|
const firstSource = await firstSourceFile(manifestPath);
|
|
7771
7511
|
if (firstSource)
|
|
7772
|
-
openFile =
|
|
7512
|
+
openFile = path14.join(projectDir, firstSource);
|
|
7773
7513
|
await fillTemplateFile(openFile, { "{{TITLE}}": name }).catch(() => {});
|
|
7774
7514
|
} catch (e) {
|
|
7775
7515
|
throw new CreateProjectErrorImpl("scaffold-io", `Could not finalize the book files: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -7779,7 +7519,7 @@ async function scaffoldProject(options) {
|
|
|
7779
7519
|
let versionHistoryError;
|
|
7780
7520
|
if (requested === "local-git") {
|
|
7781
7521
|
try {
|
|
7782
|
-
const { providerFor } = await import("./source-provider-
|
|
7522
|
+
const { providerFor } = await import("./source-provider-98k5deda.js");
|
|
7783
7523
|
const provider = providerFor({ type: "local-folder", path: projectDir });
|
|
7784
7524
|
await provider.initVersionHistory({
|
|
7785
7525
|
projectDir,
|
|
@@ -7791,7 +7531,7 @@ async function scaffoldProject(options) {
|
|
|
7791
7531
|
versionHistory = "none";
|
|
7792
7532
|
const { findEnclosingRepoDir } = await import("./project-source-ekcyp63q.js");
|
|
7793
7533
|
const enclosingRepoDir = await findEnclosingRepoDir(projectDir);
|
|
7794
|
-
versionHistoryError = enclosingRepoDir ? `This book was added to ${
|
|
7534
|
+
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);
|
|
7795
7535
|
}
|
|
7796
7536
|
}
|
|
7797
7537
|
const result = {
|
|
@@ -7811,38 +7551,38 @@ function prettifyFolderName(base) {
|
|
|
7811
7551
|
}
|
|
7812
7552
|
async function adoptFolder(options) {
|
|
7813
7553
|
const dir = options.dir;
|
|
7814
|
-
if (typeof dir !== "string" || !
|
|
7554
|
+
if (typeof dir !== "string" || !path14.isAbsolute(dir)) {
|
|
7815
7555
|
throw new CreateProjectErrorImpl("scaffold-io", "An absolute folder path is required.");
|
|
7816
7556
|
}
|
|
7817
7557
|
let st;
|
|
7818
7558
|
try {
|
|
7819
|
-
st = await
|
|
7559
|
+
st = await stat4(dir);
|
|
7820
7560
|
} catch {
|
|
7821
7561
|
throw new CreateProjectErrorImpl("scaffold-io", `Folder not found: ${dir}`);
|
|
7822
7562
|
}
|
|
7823
7563
|
if (!st.isDirectory()) {
|
|
7824
7564
|
throw new CreateProjectErrorImpl("scaffold-io", `Not a folder: ${dir}`);
|
|
7825
7565
|
}
|
|
7826
|
-
if (MANIFEST_FILENAMES.some((name) =>
|
|
7566
|
+
if (MANIFEST_FILENAMES.some((name) => existsSync9(path14.join(dir, name)))) {
|
|
7827
7567
|
throw new CreateProjectErrorImpl("target-exists", "This folder is already a Gutterpress book.");
|
|
7828
7568
|
}
|
|
7829
7569
|
const template = options.template ?? "book";
|
|
7830
|
-
const title = (options.title ?? "").trim() || prettifyFolderName(
|
|
7570
|
+
const title = (options.title ?? "").trim() || prettifyFolderName(path14.basename(dir));
|
|
7831
7571
|
const author = (options.author ?? "").trim() || DEFAULT_AUTHOR;
|
|
7832
7572
|
let mdFiles;
|
|
7833
7573
|
try {
|
|
7834
|
-
const entries = await
|
|
7574
|
+
const entries = await readdir6(dir, { withFileTypes: true });
|
|
7835
7575
|
mdFiles = entries.filter((e) => e.isFile() && /\.(md|markdown)$/i.test(e.name)).map((e) => e.name).sort((a, b) => a.localeCompare(b));
|
|
7836
7576
|
if (mdFiles.length === 0) {
|
|
7837
7577
|
const tplChapter = await getAssetPath(`templates/${template}/chapter-01.md`);
|
|
7838
|
-
await copyFile(tplChapter,
|
|
7839
|
-
await fillTemplateFile(
|
|
7578
|
+
await copyFile(tplChapter, path14.join(dir, "chapter-01.md"));
|
|
7579
|
+
await fillTemplateFile(path14.join(dir, "chapter-01.md"), { "{{TITLE}}": title }).catch(() => {});
|
|
7840
7580
|
mdFiles = ["chapter-01.md"];
|
|
7841
7581
|
}
|
|
7842
|
-
await
|
|
7843
|
-
const bookCssPath =
|
|
7844
|
-
if (!
|
|
7845
|
-
await
|
|
7582
|
+
await mkdir6(path14.join(dir, "styles"), { recursive: true });
|
|
7583
|
+
const bookCssPath = path14.join(dir, "styles", "book.css");
|
|
7584
|
+
if (!existsSync9(bookCssPath)) {
|
|
7585
|
+
await writeFile8(bookCssPath, starterOverridesCss(), "utf8");
|
|
7846
7586
|
}
|
|
7847
7587
|
const filesYaml = mdFiles.map((f) => ` - "${escapeYamlScalar(f)}"`).join(`
|
|
7848
7588
|
`);
|
|
@@ -7858,8 +7598,8 @@ ${filesYaml}
|
|
|
7858
7598
|
styles:
|
|
7859
7599
|
- styles/book.css
|
|
7860
7600
|
`;
|
|
7861
|
-
await
|
|
7862
|
-
await
|
|
7601
|
+
await writeFile8(path14.join(dir, MANIFEST_FILENAMES[0]), manifest, "utf8");
|
|
7602
|
+
await mkdir6(path14.join(dir, "assets"), { recursive: true });
|
|
7863
7603
|
await ensureProjectGitignore(dir);
|
|
7864
7604
|
} catch (e) {
|
|
7865
7605
|
if (e instanceof CreateProjectErrorImpl)
|
|
@@ -7871,7 +7611,7 @@ styles:
|
|
|
7871
7611
|
let versionHistoryError;
|
|
7872
7612
|
if (requested === "local-git") {
|
|
7873
7613
|
try {
|
|
7874
|
-
const { providerFor } = await import("./source-provider-
|
|
7614
|
+
const { providerFor } = await import("./source-provider-98k5deda.js");
|
|
7875
7615
|
const provider = providerFor({ type: "local-folder", path: dir });
|
|
7876
7616
|
await provider.initVersionHistory({
|
|
7877
7617
|
projectDir: dir,
|
|
@@ -7886,8 +7626,8 @@ styles:
|
|
|
7886
7626
|
}
|
|
7887
7627
|
const result = {
|
|
7888
7628
|
projectDir: dir,
|
|
7889
|
-
manifestPath:
|
|
7890
|
-
openFile:
|
|
7629
|
+
manifestPath: path14.join(dir, MANIFEST_FILENAMES[0]),
|
|
7630
|
+
openFile: path14.join(dir, mdFiles[0]),
|
|
7891
7631
|
versionHistory
|
|
7892
7632
|
};
|
|
7893
7633
|
if (versionHistoryError !== undefined)
|
|
@@ -7897,7 +7637,7 @@ styles:
|
|
|
7897
7637
|
async function firstSourceFile(manifestPath) {
|
|
7898
7638
|
let text;
|
|
7899
7639
|
try {
|
|
7900
|
-
text = await
|
|
7640
|
+
text = await readFile25(manifestPath, "utf8");
|
|
7901
7641
|
} catch {
|
|
7902
7642
|
return;
|
|
7903
7643
|
}
|
|
@@ -7909,17 +7649,17 @@ async function firstSourceFile(manifestPath) {
|
|
|
7909
7649
|
return m ? m[1].trim() : undefined;
|
|
7910
7650
|
}
|
|
7911
7651
|
async function fillTemplateFile(filePath, substitutions) {
|
|
7912
|
-
let text = await
|
|
7652
|
+
let text = await readFile25(filePath, "utf8");
|
|
7913
7653
|
for (const [token, value] of Object.entries(substitutions)) {
|
|
7914
7654
|
text = text.split(token).join(value);
|
|
7915
7655
|
}
|
|
7916
|
-
await
|
|
7656
|
+
await writeFile8(filePath, text, "utf8");
|
|
7917
7657
|
}
|
|
7918
7658
|
// src/lib/extension-scaffold.ts
|
|
7919
|
-
import { access as
|
|
7920
|
-
import { constants as
|
|
7921
|
-
import
|
|
7922
|
-
var EXTENSION_KINDS = ["plugin", "
|
|
7659
|
+
import { access as access2, mkdir as mkdir7, readFile as readFile26, writeFile as writeFile9 } from "node:fs/promises";
|
|
7660
|
+
import { constants as FS2 } from "node:fs";
|
|
7661
|
+
import path15 from "node:path";
|
|
7662
|
+
var EXTENSION_KINDS = ["plugin", "look"];
|
|
7923
7663
|
var RESERVED_PREFIX = "gp-";
|
|
7924
7664
|
var TEMPLATE_FILES = {
|
|
7925
7665
|
plugin: [
|
|
@@ -7932,7 +7672,7 @@ var TEMPLATE_FILES = {
|
|
|
7932
7672
|
["test/expected.html", "test/expected.html"],
|
|
7933
7673
|
["test/plugin.test.js.tpl", "test/plugin.test.js"]
|
|
7934
7674
|
],
|
|
7935
|
-
|
|
7675
|
+
look: [
|
|
7936
7676
|
["package.json", "package.json"],
|
|
7937
7677
|
["README.md", "README.md"],
|
|
7938
7678
|
["components.yaml", "components.yaml"],
|
|
@@ -7947,11 +7687,11 @@ var TEMPLATE_FILES = {
|
|
|
7947
7687
|
};
|
|
7948
7688
|
var OPEN_FILE = {
|
|
7949
7689
|
plugin: "plugin.js",
|
|
7950
|
-
|
|
7690
|
+
look: "styles/tokens.css"
|
|
7951
7691
|
};
|
|
7952
7692
|
var DEFAULT_DESCRIPTION = {
|
|
7953
7693
|
plugin: "A Gutterpress plugin.",
|
|
7954
|
-
|
|
7694
|
+
look: "A Gutterpress look."
|
|
7955
7695
|
};
|
|
7956
7696
|
var DEFAULT_AUTHOR2 = "Anonymous";
|
|
7957
7697
|
|
|
@@ -7983,7 +7723,7 @@ function escapeJsonScalar(value) {
|
|
|
7983
7723
|
}
|
|
7984
7724
|
async function scaffoldExtension(options) {
|
|
7985
7725
|
const kind = options.kind;
|
|
7986
|
-
if (kind !== "plugin" && kind !== "
|
|
7726
|
+
if (kind !== "plugin" && kind !== "look") {
|
|
7987
7727
|
throw new ExtensionScaffoldError("invalid-name", `Unknown extension kind "${String(kind)}". Choose one of: ${EXTENSION_KINDS.join(", ")}.`);
|
|
7988
7728
|
}
|
|
7989
7729
|
const name = (options.name ?? "").trim();
|
|
@@ -7996,18 +7736,18 @@ async function scaffoldExtension(options) {
|
|
|
7996
7736
|
}
|
|
7997
7737
|
const prefix = resolveExtensionPrefix(slug, options.prefix);
|
|
7998
7738
|
const parentDir = options.parentDir;
|
|
7999
|
-
if (!parentDir || !
|
|
7739
|
+
if (!parentDir || !path15.isAbsolute(parentDir)) {
|
|
8000
7740
|
throw new ExtensionScaffoldError("parent-not-writable", "The save location must be an absolute path.");
|
|
8001
7741
|
}
|
|
8002
7742
|
try {
|
|
8003
|
-
await
|
|
7743
|
+
await access2(parentDir, FS2.W_OK);
|
|
8004
7744
|
} catch {
|
|
8005
7745
|
throw new ExtensionScaffoldError("parent-not-writable", `The chosen save location can't be written to: ${parentDir}`);
|
|
8006
7746
|
}
|
|
8007
|
-
const extensionDir =
|
|
7747
|
+
const extensionDir = path15.join(parentDir, slug);
|
|
8008
7748
|
let targetExists = true;
|
|
8009
7749
|
try {
|
|
8010
|
-
await
|
|
7750
|
+
await access2(extensionDir, FS2.F_OK);
|
|
8011
7751
|
} catch {
|
|
8012
7752
|
targetExists = false;
|
|
8013
7753
|
}
|
|
@@ -8028,14 +7768,14 @@ async function scaffoldExtension(options) {
|
|
|
8028
7768
|
const entries = TEMPLATE_FILES[kind];
|
|
8029
7769
|
const written = [];
|
|
8030
7770
|
try {
|
|
8031
|
-
await
|
|
7771
|
+
await mkdir7(extensionDir, { recursive: true });
|
|
8032
7772
|
for (const [assetName, relDest] of entries) {
|
|
8033
7773
|
const src = await getAssetPath(`extension-templates/${kind}/${assetName}`);
|
|
8034
|
-
const dest =
|
|
8035
|
-
await
|
|
8036
|
-
const text = await
|
|
7774
|
+
const dest = path15.join(extensionDir, relDest);
|
|
7775
|
+
await mkdir7(path15.dirname(dest), { recursive: true });
|
|
7776
|
+
const text = await readFile26(src, "utf8");
|
|
8037
7777
|
const table = relDest.endsWith(".json") ? jsonSafe : plain;
|
|
8038
|
-
await
|
|
7778
|
+
await writeFile9(dest, substitute(text, table), "utf8");
|
|
8039
7779
|
written.push(relDest);
|
|
8040
7780
|
}
|
|
8041
7781
|
} catch (e) {
|
|
@@ -8043,11 +7783,11 @@ async function scaffoldExtension(options) {
|
|
|
8043
7783
|
}
|
|
8044
7784
|
return {
|
|
8045
7785
|
extensionDir,
|
|
8046
|
-
manifestPath:
|
|
7786
|
+
manifestPath: path15.join(extensionDir, "package.json"),
|
|
8047
7787
|
kind,
|
|
8048
7788
|
slug,
|
|
8049
7789
|
prefix,
|
|
8050
|
-
openFile:
|
|
7790
|
+
openFile: path15.join(extensionDir, OPEN_FILE[kind]),
|
|
8051
7791
|
files: written
|
|
8052
7792
|
};
|
|
8053
7793
|
}
|
|
@@ -8058,6 +7798,252 @@ function substitute(text, table) {
|
|
|
8058
7798
|
}
|
|
8059
7799
|
return out;
|
|
8060
7800
|
}
|
|
7801
|
+
// src/lib/project-templates.ts
|
|
7802
|
+
import { cp as cp2, mkdir as mkdir8, readFile as readFile27, readdir as readdir7, stat as stat5, writeFile as writeFile10 } from "node:fs/promises";
|
|
7803
|
+
import { constants as FS3, existsSync as existsSync10 } from "node:fs";
|
|
7804
|
+
import { access as access3 } from "node:fs/promises";
|
|
7805
|
+
import path16 from "node:path";
|
|
7806
|
+
import { isMap as isMap2, isScalar, isSeq, parseDocument } from "yaml";
|
|
7807
|
+
var BUILT_IN_TEMPLATE_IDS = [
|
|
7808
|
+
"book",
|
|
7809
|
+
"zine",
|
|
7810
|
+
"technical",
|
|
7811
|
+
"paper"
|
|
7812
|
+
];
|
|
7813
|
+
async function readTemplateChoices(manifestPath) {
|
|
7814
|
+
try {
|
|
7815
|
+
const text = await readFile27(manifestPath, "utf8");
|
|
7816
|
+
const doc = parseDocument(text);
|
|
7817
|
+
const out = {};
|
|
7818
|
+
const preset = doc.get("preset");
|
|
7819
|
+
if (typeof preset === "string" && preset)
|
|
7820
|
+
out.preset = preset;
|
|
7821
|
+
const targets = doc.get("targets", true);
|
|
7822
|
+
if (isSeq(targets)) {
|
|
7823
|
+
out.targets = targets.items.map((i) => scalarString(i)).filter((s) => s !== null);
|
|
7824
|
+
}
|
|
7825
|
+
return out;
|
|
7826
|
+
} catch {
|
|
7827
|
+
return {};
|
|
7828
|
+
}
|
|
7829
|
+
}
|
|
7830
|
+
var BUILT_IN_META = {
|
|
7831
|
+
book: {
|
|
7832
|
+
label: "Book",
|
|
7833
|
+
description: "A clean starting point for a novel, memoir, or any long-form book."
|
|
7834
|
+
},
|
|
7835
|
+
zine: {
|
|
7836
|
+
label: "Zine",
|
|
7837
|
+
description: "A short, personal zine made to be printed, folded, and shared."
|
|
7838
|
+
},
|
|
7839
|
+
technical: {
|
|
7840
|
+
label: "Technical document",
|
|
7841
|
+
description: "A manual or guide with steps, reference tables, and code examples."
|
|
7842
|
+
},
|
|
7843
|
+
paper: {
|
|
7844
|
+
label: "Academic paper",
|
|
7845
|
+
description: "A journal-style article with LaTeX math, numbered equations, figures, tables, notes and references."
|
|
7846
|
+
}
|
|
7847
|
+
};
|
|
7848
|
+
async function listBuiltInTemplates() {
|
|
7849
|
+
return Promise.all(BUILT_IN_TEMPLATE_IDS.map(async (id) => ({
|
|
7850
|
+
id,
|
|
7851
|
+
label: BUILT_IN_META[id].label,
|
|
7852
|
+
description: BUILT_IN_META[id].description,
|
|
7853
|
+
kind: "builtin",
|
|
7854
|
+
...await readTemplateChoices(await getAssetPath(`templates/${id}/manifest.yaml`))
|
|
7855
|
+
})));
|
|
7856
|
+
}
|
|
7857
|
+
var SKIP_ENTRIES = new Set([
|
|
7858
|
+
".git",
|
|
7859
|
+
"node_modules",
|
|
7860
|
+
"out",
|
|
7861
|
+
"dist",
|
|
7862
|
+
"build",
|
|
7863
|
+
".gutterpress",
|
|
7864
|
+
".DS_Store"
|
|
7865
|
+
]);
|
|
7866
|
+
var VENDOR_DIR = "shared";
|
|
7867
|
+
function collectEscaping(nodes, getValue, escapes) {
|
|
7868
|
+
const out = [];
|
|
7869
|
+
for (const node of nodes) {
|
|
7870
|
+
const value = getValue(node);
|
|
7871
|
+
if (value !== null && escapes(value))
|
|
7872
|
+
out.push({ node, value });
|
|
7873
|
+
}
|
|
7874
|
+
return out;
|
|
7875
|
+
}
|
|
7876
|
+
function extensionEntryPath(node) {
|
|
7877
|
+
const use = isMap2(node) ? scalarString(node.get("use", true)) : scalarString(node);
|
|
7878
|
+
return use !== null && isPathSpecifier(use) ? use : null;
|
|
7879
|
+
}
|
|
7880
|
+
function dropEscaping(doc, seq, key, escaping) {
|
|
7881
|
+
if (!isSeq(seq))
|
|
7882
|
+
return;
|
|
7883
|
+
const drop = new Set(escaping.map((e) => e.node));
|
|
7884
|
+
seq.items = seq.items.filter((node) => !drop.has(node));
|
|
7885
|
+
if (seq.items.length === 0)
|
|
7886
|
+
doc.delete(key);
|
|
7887
|
+
}
|
|
7888
|
+
function rewriteEntry(node, newPath) {
|
|
7889
|
+
if (isMap2(node))
|
|
7890
|
+
node.set("use", newPath);
|
|
7891
|
+
else if (isScalar(node))
|
|
7892
|
+
node.value = newPath;
|
|
7893
|
+
}
|
|
7894
|
+
function commonAncestorDir(absFiles) {
|
|
7895
|
+
const dirSegs = absFiles.map((f) => path16.resolve(f).split(path16.sep).slice(0, -1));
|
|
7896
|
+
if (dirSegs.length === 0)
|
|
7897
|
+
return "";
|
|
7898
|
+
let common = dirSegs[0];
|
|
7899
|
+
for (const segs of dirSegs.slice(1)) {
|
|
7900
|
+
let i = 0;
|
|
7901
|
+
while (i < common.length && i < segs.length && common[i] === segs[i])
|
|
7902
|
+
i++;
|
|
7903
|
+
common = common.slice(0, i);
|
|
7904
|
+
}
|
|
7905
|
+
return common.join(path16.sep) || path16.sep;
|
|
7906
|
+
}
|
|
7907
|
+
function pickVendorDir(templateDir, base) {
|
|
7908
|
+
const preferred = path16.basename(base) || VENDOR_DIR;
|
|
7909
|
+
if (!existsSync10(path16.join(templateDir, preferred)))
|
|
7910
|
+
return preferred;
|
|
7911
|
+
for (let n = 1;; n++) {
|
|
7912
|
+
const candidate = `${preferred}-${n}`;
|
|
7913
|
+
if (!existsSync10(path16.join(templateDir, candidate)))
|
|
7914
|
+
return candidate;
|
|
7915
|
+
}
|
|
7916
|
+
}
|
|
7917
|
+
var TEMPLATE_META_FILE = ".gutterpress-template.json";
|
|
7918
|
+
async function reconcileSharedRefs(templateDir, sourceProjectDir, mode) {
|
|
7919
|
+
const { doc, file } = await loadManifestDoc(templateDir);
|
|
7920
|
+
const escapes = (p) => escapesProjectRoot(sourceProjectDir, path16.resolve(sourceProjectDir, p));
|
|
7921
|
+
const stylesSeq = doc.get("styles", true);
|
|
7922
|
+
const extensionsSeq = doc.get("extensions", true);
|
|
7923
|
+
const escapingStyles = collectEscaping(isSeq(stylesSeq) ? stylesSeq.items : [], scalarString, escapes);
|
|
7924
|
+
const escapingExtensions = collectEscaping(isSeq(extensionsSeq) ? extensionsSeq.items : [], extensionEntryPath, escapes);
|
|
7925
|
+
if (escapingStyles.length === 0 && escapingExtensions.length === 0) {
|
|
7926
|
+
return { vendoredRefs: [], excludedRefs: [] };
|
|
7927
|
+
}
|
|
7928
|
+
if (mode === "exclude") {
|
|
7929
|
+
const excludedRefs = [...escapingStyles, ...escapingExtensions].map((e) => e.value);
|
|
7930
|
+
dropEscaping(doc, stylesSeq, "styles", escapingStyles);
|
|
7931
|
+
dropEscaping(doc, extensionsSeq, "extensions", escapingExtensions);
|
|
7932
|
+
await writeManifestDoc(file, doc);
|
|
7933
|
+
return { vendoredRefs: [], excludedRefs };
|
|
7934
|
+
}
|
|
7935
|
+
const styleClosure = await collectStyleDependencies(sourceProjectDir, escapingStyles.map((s) => s.value));
|
|
7936
|
+
const extensionFiles = escapingExtensions.map((p) => path16.resolve(sourceProjectDir, p.value));
|
|
7937
|
+
const externalFiles = [...new Set([...styleClosure, ...extensionFiles])].filter((f) => escapesProjectRoot(sourceProjectDir, f));
|
|
7938
|
+
const base = commonAncestorDir(externalFiles);
|
|
7939
|
+
const vendorDir = pickVendorDir(templateDir, base);
|
|
7940
|
+
await Promise.all(externalFiles.filter((abs) => existsSync10(abs)).map(async (abs) => {
|
|
7941
|
+
const dest = path16.join(templateDir, vendorDir, path16.relative(base, abs));
|
|
7942
|
+
await mkdir8(path16.dirname(dest), { recursive: true });
|
|
7943
|
+
await cp2(abs, dest, { recursive: true });
|
|
7944
|
+
}));
|
|
7945
|
+
const bookLocal = (value) => toPosixPath(path16.join(vendorDir, path16.relative(base, path16.resolve(sourceProjectDir, value))));
|
|
7946
|
+
const vendoredRefs = [];
|
|
7947
|
+
for (const { node, value } of escapingStyles) {
|
|
7948
|
+
const rewritten = bookLocal(value);
|
|
7949
|
+
rewriteEntry(node, rewritten);
|
|
7950
|
+
vendoredRefs.push(rewritten);
|
|
7951
|
+
}
|
|
7952
|
+
for (const { node, value } of escapingExtensions) {
|
|
7953
|
+
const rewritten = bookLocal(value);
|
|
7954
|
+
rewriteEntry(node, `./${rewritten}`);
|
|
7955
|
+
vendoredRefs.push(rewritten);
|
|
7956
|
+
}
|
|
7957
|
+
await writeManifestDoc(file, doc);
|
|
7958
|
+
return { vendoredRefs, excludedRefs: [] };
|
|
7959
|
+
}
|
|
7960
|
+
async function saveProjectAsTemplate(options) {
|
|
7961
|
+
const { projectDir, name, templatesRoot } = options;
|
|
7962
|
+
const id = slugify(name);
|
|
7963
|
+
if (!id)
|
|
7964
|
+
throw new Error(`Could not derive a template id from "${name}".`);
|
|
7965
|
+
const dir = path16.join(templatesRoot, id);
|
|
7966
|
+
let exists = true;
|
|
7967
|
+
try {
|
|
7968
|
+
await access3(dir, FS3.F_OK);
|
|
7969
|
+
} catch {
|
|
7970
|
+
exists = false;
|
|
7971
|
+
}
|
|
7972
|
+
if (exists) {
|
|
7973
|
+
throw new Error(`A template named "${name}" already exists. Choose a different name.`);
|
|
7974
|
+
}
|
|
7975
|
+
await mkdir8(dir, { recursive: true });
|
|
7976
|
+
const entries = await readdir7(projectDir, { withFileTypes: true });
|
|
7977
|
+
for (const entry of entries) {
|
|
7978
|
+
if (SKIP_ENTRIES.has(entry.name))
|
|
7979
|
+
continue;
|
|
7980
|
+
if (entry.name === TEMPLATE_META_FILE)
|
|
7981
|
+
continue;
|
|
7982
|
+
await cp2(path16.join(projectDir, entry.name), path16.join(dir, entry.name), { recursive: true });
|
|
7983
|
+
}
|
|
7984
|
+
const manifestName = MANIFEST_FILENAMES.find((name2) => entries.some((entry) => entry.name === name2));
|
|
7985
|
+
const shared = manifestName ? await reconcileSharedRefs(dir, projectDir, options.sharedRefs ?? "vendor") : { vendoredRefs: [], excludedRefs: [] };
|
|
7986
|
+
if (manifestName)
|
|
7987
|
+
await retokeniseManifest(path16.join(dir, manifestName));
|
|
7988
|
+
await writeFile10(path16.join(dir, TEMPLATE_META_FILE), JSON.stringify({ label: name }, null, 2), "utf8");
|
|
7989
|
+
return {
|
|
7990
|
+
id,
|
|
7991
|
+
label: name,
|
|
7992
|
+
description: "Your saved template.",
|
|
7993
|
+
kind: "custom",
|
|
7994
|
+
dir,
|
|
7995
|
+
...shared
|
|
7996
|
+
};
|
|
7997
|
+
}
|
|
7998
|
+
async function retokeniseManifest(manifestPath) {
|
|
7999
|
+
let text;
|
|
8000
|
+
try {
|
|
8001
|
+
text = await readFile27(manifestPath, "utf8");
|
|
8002
|
+
} catch {
|
|
8003
|
+
return;
|
|
8004
|
+
}
|
|
8005
|
+
text = text.replace(/^title:\s*.*$/m, 'title: "{{TITLE}}"').replace(/^(authors:[^\n]*\n[ \t]*-[ \t]*).*$/m, '$1"{{AUTHOR}}"');
|
|
8006
|
+
await writeFile10(manifestPath, text, "utf8");
|
|
8007
|
+
}
|
|
8008
|
+
async function listCustomTemplates(templatesRoot) {
|
|
8009
|
+
let entries;
|
|
8010
|
+
try {
|
|
8011
|
+
entries = await readdir7(templatesRoot, { withFileTypes: true });
|
|
8012
|
+
} catch {
|
|
8013
|
+
return [];
|
|
8014
|
+
}
|
|
8015
|
+
const out = [];
|
|
8016
|
+
for (const entry of entries) {
|
|
8017
|
+
if (!entry.isDirectory())
|
|
8018
|
+
continue;
|
|
8019
|
+
const dir = path16.join(templatesRoot, entry.name);
|
|
8020
|
+
let label = prettify(entry.name);
|
|
8021
|
+
try {
|
|
8022
|
+
const meta = JSON.parse(await readFile27(path16.join(dir, TEMPLATE_META_FILE), "utf8"));
|
|
8023
|
+
if (typeof meta.label === "string" && meta.label)
|
|
8024
|
+
label = meta.label;
|
|
8025
|
+
} catch {}
|
|
8026
|
+
out.push({
|
|
8027
|
+
id: entry.name,
|
|
8028
|
+
label,
|
|
8029
|
+
description: "Your saved template.",
|
|
8030
|
+
kind: "custom",
|
|
8031
|
+
dir,
|
|
8032
|
+
...await readTemplateChoices(path16.join(dir, MANIFEST_FILENAMES[0]))
|
|
8033
|
+
});
|
|
8034
|
+
}
|
|
8035
|
+
out.sort((a, b) => a.label.localeCompare(b.label));
|
|
8036
|
+
return out;
|
|
8037
|
+
}
|
|
8038
|
+
async function importTemplateFromFolder(options) {
|
|
8039
|
+
const { sourceDir, templatesRoot } = options;
|
|
8040
|
+
const srcStat = await stat5(sourceDir);
|
|
8041
|
+
if (!srcStat.isDirectory()) {
|
|
8042
|
+
throw new Error("The chosen path is not a folder.");
|
|
8043
|
+
}
|
|
8044
|
+
const name = options.name?.trim() || prettify(path16.basename(sourceDir));
|
|
8045
|
+
return saveProjectAsTemplate({ projectDir: sourceDir, name, templatesRoot });
|
|
8046
|
+
}
|
|
8061
8047
|
// src/lib/snippets.ts
|
|
8062
8048
|
import { readdir as readdir8, readFile as readFile28, writeFile as writeFile11, mkdir as mkdir9, rm } from "node:fs/promises";
|
|
8063
8049
|
import path17 from "node:path";
|
|
@@ -12646,7 +12632,7 @@ async function resolvePublishProvider(id, book) {
|
|
|
12646
12632
|
const ext = await bookExtensions(book);
|
|
12647
12633
|
const npmEntry = ext.extensions.some((c) => !c.path && c.name === id);
|
|
12648
12634
|
if (npmEntry) {
|
|
12649
|
-
const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-
|
|
12635
|
+
const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-s3x1618g.js");
|
|
12650
12636
|
await restoreForCommand2(book.projectDir, { manifestPath: book.manifestPath, failFast: true });
|
|
12651
12637
|
}
|
|
12652
12638
|
const publishers = await discover(ext);
|
|
@@ -12684,7 +12670,7 @@ function infoFor(p) {
|
|
|
12684
12670
|
}
|
|
12685
12671
|
function pluginProvider(p) {
|
|
12686
12672
|
let loading;
|
|
12687
|
-
const load = () => loading ??= import("./plugins-
|
|
12673
|
+
const load = () => loading ??= import("./plugins-2cek4mz1.js").then((m) => m.loadPublishFunction(p));
|
|
12688
12674
|
return {
|
|
12689
12675
|
info: infoFor(p),
|
|
12690
12676
|
async authenticate() {
|
|
@@ -13081,4 +13067,4 @@ async function setPublishProviderConfig(projectDir, providerId, values) {
|
|
|
13081
13067
|
await writeManifestDoc(file, doc);
|
|
13082
13068
|
return settingsFromDoc(doc);
|
|
13083
13069
|
}
|
|
13084
|
-
export { SYNC_SNAPSHOT_MESSAGE, resolveActiveStyles, listProjectStyles, getChecks, getCheckById, resolveCheckSelectors, formatReport, ruleRemoteUrls, ruleRiskyProps, ruleSyntax, checkCss, inspectImage, runChecks, checkToolAvailability, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, defaultConfigDir, FileTokenStore, extractUrlCredential, getSystemDiagnostics, readPageSetup, setPageSetup,
|
|
13070
|
+
export { SYNC_SNAPSHOT_MESSAGE, resolveActiveStyles, listProjectStyles, getChecks, getCheckById, resolveCheckSelectors, formatReport, ruleRemoteUrls, ruleRiskyProps, ruleSyntax, checkCss, inspectImage, runChecks, checkToolAvailability, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, defaultConfigDir, FileTokenStore, extractUrlCredential, getSystemDiagnostics, readPageSetup, setPageSetup, slugifyProjectName, escapeYamlScalar, scaffoldProject, adoptFolder, EXTENSION_KINDS, RESERVED_PREFIX, resolveExtensionPrefix, scaffoldExtension, BUILT_IN_TEMPLATE_IDS, listBuiltInTemplates, saveProjectAsTemplate, listCustomTemplates, importTemplateFromFolder, SNIPPETS_DIR, extractVariables, substituteVariables, listSnippets, saveSnippet, deleteSnippet, listMergedSnippets, listMarkerComponents, listLocationVariants, importExtensionFromFile, importExtensionFromUrl, MAX_SEARCH_BYTES, SEARCH_KEYWORDS, searchNpmExtensions, setManifestFields, readManifestFields, setActiveStyles, VERSION_CHANGES_SHOWN, describeVersions, listOnlineVersions, compareVersionFile, exportVersionTree, AUTO_SNAPSHOT_DEFAULT_MINUTES, AUTO_SYNC_MIN_MINUTES, AUTO_SYNC_MAX_MINUTES, AUTO_SYNC_DEFAULT_MINUTES, AUTO_SYNC_PUSH_INTERVAL_MINUTES, autoSnapshotDelayMs, autoSyncDelayMs, isGitInternalPath, resolveGitHubClientId, GITHUB_HOST, GitHubAuthProvider, GDRIVE_HOST, resolveGoogleClientId, resolveGoogleClientSecret, GOOGLE_NOT_CONFIGURED_MESSAGE, requireGoogleClientCredentials, pkceChallengeFromVerifier, GoogleAuthProvider, revokeGoogleCredential, publishCredentialKey, publishConnectionStatus, listPublishAccounts, connectGoogleDrive, listGitHubRepositories, listRepoBooks, listGitHubBranches, syncProject, refreshRemoteCopies, checkRemoteUpdates, isValidRepositoryName, setUpOnlineBackup, sanitizeCloneFolderName, cloneRepository, isSshRemoteUrl, testRemoteAccess, knownForgeTokenUrl, connectGenericHost, parseRemoteOrigin, forgeKindForHost, diagnoseProjectRemote, REPAIR_SNAPSHOT_MESSAGE, repairOnlineBackup, scorchedEarth, listPublishProviders, publishProviderFor, listBookPublishProviders, resolvePublishProvider, resolvePublishFormat, resolvePublishRequest, runPublish, connectPublishProvider, disconnectPublishCredential, PublishSelectionsStore, readPublishSettings, setPublishProviderConfig };
|