gutterpress 0.11.23-alpha.7 → 0.11.23-beta.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 +3 -1
- package/dist/{README-syg2hn83.md → README-8rct3axy.md} +2 -2
- package/dist/api/index.d.ts +3 -3
- package/dist/api/index.js +8 -6
- package/dist/{build-w572wq10.js → build-3e18wnqj.js} +9 -8
- package/dist/{cli-mmgtqnmr.js → cli-0ep11b0q.js} +1 -1
- package/dist/cli-65gy8ask.js +82 -0
- package/dist/{cli-6pv84kf3.js → cli-88arpdke.js} +6 -84
- package/dist/{cli-eatdqx3z.js → cli-9n9kg58f.js} +1 -1
- package/dist/{cli-58fsk6d7.js → cli-pd2cje5b.js} +187 -44
- package/dist/{cli-3cvq3n1c.js → cli-pnrararp.js} +189 -85
- package/dist/{cli-wyg7qstd.js → cli-tgnyvra3.js} +15 -5
- package/dist/{cli-31anr0r9.js → cli-v0evdpdf.js} +68 -28
- package/dist/{cli-5ba6bm6z.js → cli-x7gcdg6w.js} +1 -1
- package/dist/cli.js +12 -12
- package/dist/{doctor-2f3zqmza.js → doctor-2f4s9dyn.js} +3 -3
- package/dist/engine/compiler/postprocess.d.ts +3 -0
- package/dist/engine/shared/page-vars.d.ts +32 -0
- package/dist/engine/shared/pdf-inspect.d.ts +33 -0
- package/dist/{engine-4rabsgwq.js → engine-c4pxz79g.js} +2 -1
- package/dist/{engine-wm4fggac.js → engine-cs5g2ez0.js} +3 -3
- package/dist/{ext-0cgky8n3.js → ext-70gv86v5.js} +9 -8
- package/dist/{extension-restore-vghc58vx.js → extension-restore-chj5nx3t.js} +3 -3
- package/dist/{extension-restore-e7je32pr.js → extension-restore-pd87p8r2.js} +5 -4
- package/dist/{gutterpress-agent-d29ry3ak.js → gutterpress-agent-wyryqehx.js} +13 -4
- package/dist/{gutterpress-viewer-mafsby5k.js → gutterpress-viewer-ywkf49q5.js} +90 -166
- package/dist/{index-nfysshcc.js → index-0k43evt0.js} +309 -155
- package/dist/{index-wm4x7xbf.js → index-2t1gdma7.js} +178 -35
- package/dist/{index-cw03e9p4.js → index-7j3hkwya.js} +68 -28
- package/dist/{index-fp7xwvpr.js → index-80vg3ed6.js} +15 -5
- package/dist/{index-2vxzxyyj.js → index-swfdgn2n.js} +10 -10
- package/dist/index-tayzfr9c.js +99 -0
- package/dist/{index-hphcn7th.js → index-ytjkvwy5.js} +5 -85
- package/dist/index.d.ts +2 -0
- package/dist/index.js +234 -7
- package/dist/lib/book-css.d.ts +56 -0
- package/dist/lib/markdown/assemble.d.ts +58 -0
- package/dist/lib/markdown/gutterpress-css.d.ts +1 -1
- package/dist/lib/publish/google-drive.d.ts +12 -4
- package/dist/lib/publish/manifest-publish.d.ts +6 -0
- package/dist/lib/publish/providers/gdrive-folder.d.ts +30 -0
- package/dist/lib/publish/run-publish.d.ts +6 -1
- package/dist/lib/publish/types.d.ts +52 -5
- package/dist/lib/remote-auth/converge-merge.d.ts +3 -3
- package/dist/lib/remote-auth/sync-messages.d.ts +2 -2
- package/dist/lib/root-tokens.d.ts +76 -0
- package/dist/lib/source-provider.d.ts +27 -0
- package/dist/lib/version-changes.d.ts +26 -16
- package/dist/{manifest-79zsgkpn.yaml → manifest-0vwaskj2.yaml} +1 -1
- package/dist/{manifest-2kc56wvw.yaml → manifest-aya31jwt.yaml} +1 -1
- package/dist/{manifest-41ddzm13.yaml → manifest-mffah3hr.yaml} +1 -1
- package/dist/{manifest-sqzm4681.yaml → manifest-wbcv8cbp.yaml} +2 -2
- package/dist/{manifest.schema-93sd7zje.json → manifest.schema-0jgj0jt1.json} +1 -1
- package/dist/{new-88rkrv71.js → new-d61n5cn0.js} +9 -8
- package/dist/{page-rules-ytj9p04v.css → page-rules-vnja6grt.css} +2 -2
- package/dist/{plugins-3aj0wc5h.js → plugins-0hhw6s17.js} +1 -1
- package/dist/{plugins-sprxsz5d.js → plugins-e0n46s6m.js} +3 -3
- package/dist/{preflight-11dg1qkk.js → preflight-fjayhjr5.js} +9 -8
- package/dist/{preview-nftp782j.js → preview-h815jrfb.js} +9 -8
- package/dist/{publish-6tgxfck9.js → publish-ft39zgma.js} +25 -9
- package/dist/render.js +77 -26
- package/dist/schema/manifest.types.d.ts +2 -1
- package/dist/{source-provider-n7vehh6t.js → source-provider-j1w7byeq.js} +5 -1
- package/dist/{source-provider-tgzet3j5.js → source-provider-tjaf303m.js} +5 -1
- package/dist/test-helpers/vocabulary.d.ts +12 -0
- package/dist/{tokens-n82t4k9h.css → tokens-a7grv940.css} +4 -5
- package/dist/{validate-pktqrye5.js → validate-b201dbrb.js} +9 -8
- package/package.json +1 -1
- package/dist/index-46ycxe6r.js +0 -18
|
@@ -10,20 +10,13 @@ import {
|
|
|
10
10
|
UsageError,
|
|
11
11
|
addExtension,
|
|
12
12
|
applyDefaultPdfStrictChecks,
|
|
13
|
-
cssLength,
|
|
14
13
|
ensureProjectGitignore,
|
|
15
14
|
extensionConfigFor,
|
|
16
|
-
formatPageSize,
|
|
17
|
-
isNonNegativePoints,
|
|
18
|
-
isPositivePoints,
|
|
19
|
-
judgeRect,
|
|
20
15
|
listProjectExtensions,
|
|
21
16
|
loadManifest,
|
|
22
17
|
loadManifestDoc,
|
|
23
18
|
loadManifestWithPath,
|
|
24
19
|
npmRegistryUrl,
|
|
25
|
-
pageBoxes,
|
|
26
|
-
printAreaMeta,
|
|
27
20
|
publishTargetFor,
|
|
28
21
|
readEntryCss,
|
|
29
22
|
resolveConfig,
|
|
@@ -31,13 +24,12 @@ import {
|
|
|
31
24
|
resolvePort,
|
|
32
25
|
resolveTargets,
|
|
33
26
|
restoreForCommand,
|
|
34
|
-
round3,
|
|
35
27
|
scalarString,
|
|
36
28
|
uniqueExtensionId,
|
|
37
29
|
validatePageMargin,
|
|
38
30
|
withFetchTimeout,
|
|
39
31
|
writeManifestDoc
|
|
40
|
-
} from "./index-
|
|
32
|
+
} from "./index-ytjkvwy5.js";
|
|
41
33
|
import {
|
|
42
34
|
EXTENSION_MANIFEST_FILENAME,
|
|
43
35
|
GUTTERPRESS_CSS,
|
|
@@ -73,7 +65,7 @@ import {
|
|
|
73
65
|
setLogLevel,
|
|
74
66
|
slugify,
|
|
75
67
|
warn
|
|
76
|
-
} from "./index-
|
|
68
|
+
} from "./index-80vg3ed6.js";
|
|
77
69
|
import {
|
|
78
70
|
blackTextToGray
|
|
79
71
|
} from "./index-ht1bcqdd.js";
|
|
@@ -84,10 +76,18 @@ import {
|
|
|
84
76
|
getAssetPath,
|
|
85
77
|
lastPageMode,
|
|
86
78
|
resolvePage
|
|
87
|
-
} from "./index-
|
|
79
|
+
} from "./index-swfdgn2n.js";
|
|
88
80
|
import {
|
|
89
|
-
BuildError
|
|
90
|
-
|
|
81
|
+
BuildError,
|
|
82
|
+
cssLength,
|
|
83
|
+
formatPageSize,
|
|
84
|
+
isNonNegativePoints,
|
|
85
|
+
isPositivePoints,
|
|
86
|
+
judgeRect,
|
|
87
|
+
pageBoxes,
|
|
88
|
+
printAreaMeta,
|
|
89
|
+
round3
|
|
90
|
+
} from "./index-tayzfr9c.js";
|
|
91
91
|
import {
|
|
92
92
|
countLinkAnnotations,
|
|
93
93
|
getImageResolutions,
|
|
@@ -124,14 +124,16 @@ import {
|
|
|
124
124
|
hasUncommittedChanges,
|
|
125
125
|
isContained,
|
|
126
126
|
listWorkdirChanges,
|
|
127
|
+
nextInHistory,
|
|
127
128
|
readRevisionRefs,
|
|
128
129
|
resolveGitAuthor,
|
|
129
130
|
resolveLogger,
|
|
130
131
|
revisionNameFrom,
|
|
131
132
|
snapshotStagingMarkerPath,
|
|
132
133
|
snapshotWorkingTreeUnlocked,
|
|
134
|
+
startHistoryWalk,
|
|
133
135
|
withRepoLock
|
|
134
|
-
} from "./index-
|
|
136
|
+
} from "./index-7j3hkwya.js";
|
|
135
137
|
import {
|
|
136
138
|
EXTENSIONS_DIR,
|
|
137
139
|
resolveVendoredPluginInstallRoot,
|
|
@@ -166,8 +168,8 @@ var MSG_OFFLINE = "Your changes are saved on this computer. Gutterpress couldn't
|
|
|
166
168
|
var MSG_AUTH = "The online copy didn't accept the saved sign-in. Sign in again and try again.";
|
|
167
169
|
var MSG_INSECURE_TRANSPORT = "This book's online copy is at an address that isn't secure, so the saved sign-in wasn't sent — sign-ins are never sent over an insecure address. Switch the address to a secure one (starting with https), or to a local loopback address for a server on this computer, to back up.";
|
|
168
170
|
var MSG_BUSY = "The online copy is changing very quickly right now. Your work is saved on this computer — try again in a moment.";
|
|
169
|
-
var MSG_HISTORY_UNREADABLE = `This book's versions can't be read, so backing up can't continue. Your writing is safe — every file is still here on this computer. Use "Repair the online copy"
|
|
170
|
-
var MSG_UNRELATED = `This book and the online copy have no versions in common, so they can't be combined. Either the online copy's address points at a different book, or this book's own versions were lost and started over. Your writing is safe — every file is still here on this computer. Check the online copy's address; if it is correct, use "Repair the online copy"
|
|
171
|
+
var MSG_HISTORY_UNREADABLE = `This book's versions can't be read, so backing up can't continue. Your writing is safe — every file is still here on this computer. Use "Repair the online copy" in Book setup to get versions and backing up working again.`;
|
|
172
|
+
var MSG_UNRELATED = `This book and the online copy have no versions in common, so they can't be combined. Either the online copy's address points at a different book, or this book's own versions were lost and started over. Your writing is safe — every file is still here on this computer. Check the online copy's address; if it is correct, use "Repair the online copy" in Book setup.`;
|
|
171
173
|
var MSG_NO_REMOTE = "This book doesn't have an online copy yet.";
|
|
172
174
|
var MSG_SSH_REMOTE = "This book's online copy uses an SSH address (git@…), which Gutterpress can't back up to. Switch it to the web (HTTPS) address to back up from here.";
|
|
173
175
|
var MSG_NO_BRANCH = "This book isn't on a named revision, so it can't be backed up right now.";
|
|
@@ -202,6 +204,7 @@ function canonicalChapterId(p) {
|
|
|
202
204
|
}
|
|
203
205
|
|
|
204
206
|
// src/lib/markdown/assemble.ts
|
|
207
|
+
import postcss from "postcss";
|
|
205
208
|
function pageGeometryCss(page) {
|
|
206
209
|
if (!page || !isPositivePoints(page.width) || !isPositivePoints(page.height))
|
|
207
210
|
return null;
|
|
@@ -222,6 +225,65 @@ function defaultPageMarginCss(page) {
|
|
|
222
225
|
@page { margin: ${cssLength(DEFAULT_PAGE_MARGIN_PT)}; }
|
|
223
226
|
}`;
|
|
224
227
|
}
|
|
228
|
+
function liftPageRules(css) {
|
|
229
|
+
if (!/@page\b/i.test(css))
|
|
230
|
+
return { layered: css, pages: "" };
|
|
231
|
+
let root;
|
|
232
|
+
try {
|
|
233
|
+
root = postcss.parse(css);
|
|
234
|
+
} catch {
|
|
235
|
+
return { layered: css, pages: "" };
|
|
236
|
+
}
|
|
237
|
+
const found = [];
|
|
238
|
+
root.walkAtRules(/^page$/i, (rule) => {
|
|
239
|
+
found.push(rule);
|
|
240
|
+
});
|
|
241
|
+
const pages = found.map((rule) => {
|
|
242
|
+
let lifted = rule.clone();
|
|
243
|
+
for (let p = rule.parent;p instanceof postcss.AtRule || p instanceof postcss.Rule; p = p.parent) {
|
|
244
|
+
if (p instanceof postcss.AtRule && p.name.toLowerCase() === "layer")
|
|
245
|
+
continue;
|
|
246
|
+
lifted = p.clone({ nodes: [] }).append(lifted);
|
|
247
|
+
}
|
|
248
|
+
rule.remove();
|
|
249
|
+
return lifted.toString();
|
|
250
|
+
});
|
|
251
|
+
return { layered: root.toString(), pages: pages.join(`
|
|
252
|
+
`) };
|
|
253
|
+
}
|
|
254
|
+
var CORE_MARKERS = liftPageRules(`${MARKER_CSS.trim()}
|
|
255
|
+
${TWO_VERSIONS_CSS.trim()}`);
|
|
256
|
+
var CORE_VOCAB = liftPageRules(GUTTERPRESS_CSS.trim());
|
|
257
|
+
function assembleCssChunks(opts) {
|
|
258
|
+
const core = (css) => css ? [{ origin: { kind: "core" }, css }] : [];
|
|
259
|
+
const headed = (title, chunks = []) => chunks.map((chunk, i) => i === 0 ? { ...chunk, css: `/* ${title} */
|
|
260
|
+
${chunk.css.trim()}` } : chunk);
|
|
261
|
+
const pageGeometry = pageGeometryCss(opts.printArea);
|
|
262
|
+
const defaultMargin = defaultPageMarginCss(opts.printArea);
|
|
263
|
+
return [
|
|
264
|
+
...core("@layer gp.marker, gp.vocab;"),
|
|
265
|
+
...core(`/* gutterpress markers */
|
|
266
|
+
@layer gp.marker {
|
|
267
|
+
${CORE_MARKERS.layered.trim()}
|
|
268
|
+
}`),
|
|
269
|
+
...core(CORE_MARKERS.pages && `/* gutterpress markers: page rules */
|
|
270
|
+
${CORE_MARKERS.pages}`),
|
|
271
|
+
...core(`/* gutterpress */
|
|
272
|
+
@layer gp.vocab {
|
|
273
|
+
${CORE_VOCAB.layered.trim()}
|
|
274
|
+
}`),
|
|
275
|
+
...core(CORE_VOCAB.pages && `/* gutterpress: page rules */
|
|
276
|
+
${CORE_VOCAB.pages}`),
|
|
277
|
+
...core(defaultMargin && `/* page margins (default) */
|
|
278
|
+
${defaultMargin}`),
|
|
279
|
+
...headed("extension css", opts.extensions),
|
|
280
|
+
...core(`/* column alignment */
|
|
281
|
+
${TABLE_ALIGN_CSS.trim()}`),
|
|
282
|
+
...headed("project css", opts.book),
|
|
283
|
+
...core(pageGeometry && `/* page geometry (manifest) */
|
|
284
|
+
${pageGeometry}`)
|
|
285
|
+
];
|
|
286
|
+
}
|
|
225
287
|
async function assembleBookHtml(opts) {
|
|
226
288
|
const title = opts.title ?? "Document";
|
|
227
289
|
const projectCss = opts.projectCss ?? "";
|
|
@@ -260,30 +322,11 @@ async function assembleBookHtml(opts) {
|
|
|
260
322
|
imageRefs.add(ref);
|
|
261
323
|
opts.onImageRefs([...imageRefs]);
|
|
262
324
|
}
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
"
|
|
267
|
-
|
|
268
|
-
@layer gp.marker {
|
|
269
|
-
${MARKER_CSS.trim()}
|
|
270
|
-
${TWO_VERSIONS_CSS.trim()}
|
|
271
|
-
}`,
|
|
272
|
-
`/* gutterpress */
|
|
273
|
-
@layer gp.vocab {
|
|
274
|
-
${GUTTERPRESS_CSS.trim()}
|
|
275
|
-
}`,
|
|
276
|
-
defaultMargin ? `/* page margins (default) */
|
|
277
|
-
${defaultMargin}` : null,
|
|
278
|
-
pluginCss ? `/* extension css */
|
|
279
|
-
${pluginCss.trim()}` : null,
|
|
280
|
-
`/* column alignment */
|
|
281
|
-
${TABLE_ALIGN_CSS.trim()}`,
|
|
282
|
-
projectCss ? `/* project css */
|
|
283
|
-
${projectCss.trim()}` : null,
|
|
284
|
-
pageGeometry ? `/* page geometry (manifest) */
|
|
285
|
-
${pageGeometry}` : null
|
|
286
|
-
].filter(Boolean).join(`
|
|
325
|
+
const inlineCss = assembleCssChunks({
|
|
326
|
+
printArea: opts.printArea,
|
|
327
|
+
extensions: pluginCss ? [{ origin: { kind: "extension" }, css: pluginCss }] : [],
|
|
328
|
+
book: projectCss ? [{ origin: { kind: "book" }, css: projectCss }] : []
|
|
329
|
+
}).map((chunk) => chunk.css).join(`
|
|
287
330
|
|
|
288
331
|
`);
|
|
289
332
|
const preloadTags = (opts.preloadImages ?? []).map((href) => `
|
|
@@ -396,7 +439,7 @@ async function listProjectStyles(projectDir, opts = {}) {
|
|
|
396
439
|
import { createHash } from "node:crypto";
|
|
397
440
|
import { readFile } from "node:fs/promises";
|
|
398
441
|
import path2 from "node:path";
|
|
399
|
-
import
|
|
442
|
+
import postcss2 from "postcss";
|
|
400
443
|
var FONT_EXTS = new Set([".woff2", ".woff", ".ttf", ".otf"]);
|
|
401
444
|
var HASHED_ASSET_DIR = "assets";
|
|
402
445
|
var MIME_BY_EXT = {
|
|
@@ -496,15 +539,15 @@ function wrapImportConditions(node, imported) {
|
|
|
496
539
|
const layer = media.match(/\blayer\(([^)]*)\)|\blayer\b/i);
|
|
497
540
|
if (layer) {
|
|
498
541
|
media = media.replace(layer[0], "").trim();
|
|
499
|
-
inner =
|
|
542
|
+
inner = postcss2.atRule({ name: "layer", params: (layer[1] ?? "").trim(), nodes: [inner] });
|
|
500
543
|
}
|
|
501
544
|
const supports = media.match(/\bsupports\(([\s\S]*)\)/i);
|
|
502
545
|
if (supports) {
|
|
503
546
|
media = media.replace(supports[0], "").trim();
|
|
504
|
-
inner =
|
|
547
|
+
inner = postcss2.atRule({ name: "supports", params: (supports[1] ?? "").trim(), nodes: [inner] });
|
|
505
548
|
}
|
|
506
549
|
if (media) {
|
|
507
|
-
inner =
|
|
550
|
+
inner = postcss2.atRule({ name: "media", params: media, nodes: [inner] });
|
|
508
551
|
}
|
|
509
552
|
return inner;
|
|
510
553
|
}
|
|
@@ -526,7 +569,7 @@ async function inlineOne(cssPath, copies, warnings, seen) {
|
|
|
526
569
|
const cssDir = path2.dirname(abs);
|
|
527
570
|
let root;
|
|
528
571
|
try {
|
|
529
|
-
root =
|
|
572
|
+
root = postcss2.parse(source.toString("utf-8"), { from: abs });
|
|
530
573
|
} catch (err) {
|
|
531
574
|
throw new BuildError(`Could not parse CSS: ${abs}
|
|
532
575
|
${err instanceof Error ? err.message : String(err)}`, 1);
|
|
@@ -566,7 +609,7 @@ async function inlineOne(cssPath, copies, warnings, seen) {
|
|
|
566
609
|
for (const { node, target } of imports) {
|
|
567
610
|
const importedAbs = path2.resolve(cssDir, stripUrlSuffix(decodeRef(target)));
|
|
568
611
|
const inlined = await inlineOne(importedAbs, copies, warnings, seen);
|
|
569
|
-
node.replaceWith(wrapImportConditions(node,
|
|
612
|
+
node.replaceWith(wrapImportConditions(node, postcss2.parse(inlined, { from: importedAbs })));
|
|
570
613
|
}
|
|
571
614
|
return root.toString();
|
|
572
615
|
}
|
|
@@ -608,7 +651,7 @@ async function collectStyleDependencies(projectDir, stylePaths) {
|
|
|
608
651
|
}
|
|
609
652
|
let root;
|
|
610
653
|
try {
|
|
611
|
-
root =
|
|
654
|
+
root = postcss2.parse(source, { from: abs });
|
|
612
655
|
} catch {
|
|
613
656
|
return;
|
|
614
657
|
}
|
|
@@ -705,6 +748,7 @@ async function resolveActiveMarkdownFiles(inputDir, configuredFiles) {
|
|
|
705
748
|
async function renderChapters(inputDir, opts = {}) {
|
|
706
749
|
const styles = await resolveActiveStyles(inputDir, opts.styles);
|
|
707
750
|
const inlined = await inlineStyles(inputDir, styles);
|
|
751
|
+
const book = liftPageRules(inlined.css);
|
|
708
752
|
const blocks = [];
|
|
709
753
|
const layers = [];
|
|
710
754
|
const styleWarnings = [];
|
|
@@ -719,10 +763,13 @@ async function renderChapters(inputDir, opts = {}) {
|
|
|
719
763
|
if (!css)
|
|
720
764
|
continue;
|
|
721
765
|
layers.push(group.layer);
|
|
766
|
+
const { layered, pages } = liftPageRules(css);
|
|
722
767
|
blocks.push(`/* ${group.name} */
|
|
723
768
|
@layer ${group.layer} {
|
|
724
|
-
${
|
|
725
|
-
}`
|
|
769
|
+
${layered.trim()}
|
|
770
|
+
}` + (pages ? `
|
|
771
|
+
/* ${group.name}: page rules */
|
|
772
|
+
${pages}` : ""));
|
|
726
773
|
}
|
|
727
774
|
styleWarnings.push(...inlined.warnings);
|
|
728
775
|
if (styleWarnings.length > 0)
|
|
@@ -740,7 +787,8 @@ ${css.trim()}
|
|
|
740
787
|
return assembleBookHtml({
|
|
741
788
|
files,
|
|
742
789
|
readText: (relPath) => readFile2(join(inputDir, canonicalChapterId(relPath)), "utf-8"),
|
|
743
|
-
projectCss:
|
|
790
|
+
projectCss: book.pages ? `${book.layered.trim()}
|
|
791
|
+
${book.pages}` : inlined.css,
|
|
744
792
|
preloadImages: cssAssetCopies.map((c) => c.to),
|
|
745
793
|
title: opts.title,
|
|
746
794
|
language: opts.language,
|
|
@@ -2737,7 +2785,7 @@ registerCheck(check19);
|
|
|
2737
2785
|
import { readFile as readFile11 } from "node:fs/promises";
|
|
2738
2786
|
|
|
2739
2787
|
// src/lib/printsafe.ts
|
|
2740
|
-
import
|
|
2788
|
+
import postcss3 from "postcss";
|
|
2741
2789
|
|
|
2742
2790
|
// src/lib/lint-rules.ts
|
|
2743
2791
|
function ruleLevel(rules, id) {
|
|
@@ -3029,7 +3077,7 @@ function filterFindings(findings, css, rules) {
|
|
|
3029
3077
|
function checkCssRaw(css, from) {
|
|
3030
3078
|
let root;
|
|
3031
3079
|
try {
|
|
3032
|
-
root =
|
|
3080
|
+
root = postcss3.parse(css, from ? { from } : undefined);
|
|
3033
3081
|
} catch (err) {
|
|
3034
3082
|
const e = err;
|
|
3035
3083
|
return [
|
|
@@ -3063,7 +3111,7 @@ function checkCssRaw(css, from) {
|
|
|
3063
3111
|
warnings.push({
|
|
3064
3112
|
rule: rulePageSizeInStylesheet,
|
|
3065
3113
|
severity: "warning",
|
|
3066
|
-
message: `"size" in a stylesheet's @page rule is ignored: the page size comes from the manifest (preset:, or page: width and height —
|
|
3114
|
+
message: `"size" in a stylesheet's @page rule is ignored: the page size comes from the manifest (preset:, or page: width and height — set them in Book setup), which is written after every stylesheet. Remove this declaration; to print at another size, change the manifest.`,
|
|
3067
3115
|
...nodeLoc(decl)
|
|
3068
3116
|
});
|
|
3069
3117
|
return;
|
|
@@ -3199,7 +3247,7 @@ registerCheck(check20);
|
|
|
3199
3247
|
|
|
3200
3248
|
// src/checks/source/css-ownership.ts
|
|
3201
3249
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
3202
|
-
import
|
|
3250
|
+
import postcss4 from "postcss";
|
|
3203
3251
|
import { parse as parseYaml2 } from "yaml";
|
|
3204
3252
|
var CONFIG_NAMES3 = [".gutterpress/css-contract.yaml", ".gutterpress/css-contract.yml"];
|
|
3205
3253
|
async function loadContract(configPath) {
|
|
@@ -3356,7 +3404,7 @@ var check21 = {
|
|
|
3356
3404
|
}
|
|
3357
3405
|
let root;
|
|
3358
3406
|
try {
|
|
3359
|
-
root =
|
|
3407
|
+
root = postcss4.parse(css, { from: absFile });
|
|
3360
3408
|
} catch {
|
|
3361
3409
|
continue;
|
|
3362
3410
|
}
|
|
@@ -3851,7 +3899,7 @@ registerCheck(check25);
|
|
|
3851
3899
|
import { readFile as readFile15 } from "node:fs/promises";
|
|
3852
3900
|
|
|
3853
3901
|
// src/lib/unknown-classes.ts
|
|
3854
|
-
import
|
|
3902
|
+
import postcss5 from "postcss";
|
|
3855
3903
|
var EMITTED = new Set([
|
|
3856
3904
|
"chapter",
|
|
3857
3905
|
"spread",
|
|
@@ -3899,7 +3947,7 @@ function addSelectorClasses(selector, into) {
|
|
|
3899
3947
|
}
|
|
3900
3948
|
function addCssClasses(css, into) {
|
|
3901
3949
|
try {
|
|
3902
|
-
|
|
3950
|
+
postcss5.parse(css).walk((node) => {
|
|
3903
3951
|
if (node.type === "rule")
|
|
3904
3952
|
addSelectorClasses(node.selector, into);
|
|
3905
3953
|
else if (node.type === "atrule") {
|
|
@@ -4097,7 +4145,7 @@ registerCheck(check27);
|
|
|
4097
4145
|
import { readFile as readFile17 } from "node:fs/promises";
|
|
4098
4146
|
|
|
4099
4147
|
// src/checks/source/character-table.ts
|
|
4100
|
-
var CHARACTER_PATTERN = new RegExp("[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u00A0\\u00AD\\u00B4\\u02BC" + "\\u061C\\u2000-\\u2015\\u2018\\u201B-\\u201D\\u201F\\u2022\\u2026" + "\\u2028-\\u202F\\u2032\\u2033\\u205F\\u2060\\u2066-\\u2069\\u2190\\u2192" + "\\u2194\\u21D2\\
|
|
4148
|
+
var CHARACTER_PATTERN = new RegExp("[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u00A0\\u00AD\\u00B4\\u02BC" + "\\u061C\\u2000-\\u2015\\u2018\\u201B-\\u201D\\u201F\\u2022\\u2026" + "\\u2028-\\u202F\\u2032\\u2033\\u205F\\u2060\\u2066-\\u2069\\u2190\\u2192" + "\\u2194\\u21D2\\u25AA\\u25E6\\u2605\\u2713\\u2714\\u2717\\u3000" + "\\uE000-\\uF8FF\\uFEFF\\uFF02\\uFF07\\uFFFD]" + "|\\p{Emoji_Presentation}|\\p{Extended_Pictographic}(?=\\uFE0F)" + "|(?<=[\\p{L}\\p{N}])\\u2019|\\u2019(?![\\p{L}\\p{N}])", "gu");
|
|
4101
4149
|
var NAMED = {
|
|
4102
4150
|
8212: ["Em dash", "typographer", "---"],
|
|
4103
4151
|
8211: ["En dash", "typographer", "--"],
|
|
@@ -4118,7 +4166,6 @@ var NAMED = {
|
|
|
4118
4166
|
8209: ["Non-breaking hyphen", "lookalike", "-"],
|
|
4119
4167
|
8210: ["Figure dash", "lookalike", "-"],
|
|
4120
4168
|
8213: ["Horizontal bar", "lookalike", "---"],
|
|
4121
|
-
8722: ["Minus sign", "lookalike", "-"],
|
|
4122
4169
|
8226: ["Bullet", "symbol", "•"],
|
|
4123
4170
|
9702: ["White bullet", "symbol", "◦"],
|
|
4124
4171
|
9642: ["Small square", "symbol", "▪"],
|
|
@@ -5532,7 +5579,7 @@ class PdfOutput {
|
|
|
5532
5579
|
const rawPdf = pdfxMode ? path7.join(stage, "raw.pdf") : path7.resolve(pdfFile);
|
|
5533
5580
|
await fsp2.mkdir(path7.dirname(path7.resolve(pdfFile)), { recursive: true });
|
|
5534
5581
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
5535
|
-
const { buildNativePdf } = await import("./engine-
|
|
5582
|
+
const { buildNativePdf } = await import("./engine-cs5g2ez0.js");
|
|
5536
5583
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
5537
5584
|
title: config.title,
|
|
5538
5585
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
@@ -7216,10 +7263,10 @@ function starterOverridesCss() {
|
|
|
7216
7263
|
return `/**
|
|
7217
7264
|
* Your book's own styles. Rules here win over your look's.
|
|
7218
7265
|
*
|
|
7219
|
-
* In the app you rarely need this file:
|
|
7220
|
-
*
|
|
7221
|
-
*
|
|
7222
|
-
*
|
|
7266
|
+
* In the app you rarely need this file: Book setup chooses a look, changes
|
|
7267
|
+
* its fonts and colors (those changes are saved here as :root custom
|
|
7268
|
+
* properties), lists every setting, and sets the page size and margins (in
|
|
7269
|
+
* the manifest).
|
|
7223
7270
|
*
|
|
7224
7271
|
* To change the look from the command line: gutterpress ext remove <old>
|
|
7225
7272
|
* <book>, then gutterpress ext add <look> <book>.
|
|
@@ -7361,7 +7408,7 @@ async function scaffoldProject(options) {
|
|
|
7361
7408
|
let versionHistoryError;
|
|
7362
7409
|
if (requested === "local-git") {
|
|
7363
7410
|
try {
|
|
7364
|
-
const { providerFor } = await import("./source-provider-
|
|
7411
|
+
const { providerFor } = await import("./source-provider-tjaf303m.js");
|
|
7365
7412
|
const provider = providerFor({ type: "local-folder", path: projectDir });
|
|
7366
7413
|
await provider.initVersionHistory({
|
|
7367
7414
|
projectDir,
|
|
@@ -7452,7 +7499,7 @@ styles:
|
|
|
7452
7499
|
let versionHistoryError;
|
|
7453
7500
|
if (requested === "local-git") {
|
|
7454
7501
|
try {
|
|
7455
|
-
const { providerFor } = await import("./source-provider-
|
|
7502
|
+
const { providerFor } = await import("./source-provider-tjaf303m.js");
|
|
7456
7503
|
const provider = providerFor({ type: "local-folder", path: dir });
|
|
7457
7504
|
await provider.initVersionHistory({
|
|
7458
7505
|
projectDir: dir,
|
|
@@ -8621,7 +8668,7 @@ async function setActiveStyles(projectDir, paths) {
|
|
|
8621
8668
|
import path19 from "node:path";
|
|
8622
8669
|
import { mkdir as mkdir10, readFile as readFile30, writeFile as writeFile12 } from "node:fs/promises";
|
|
8623
8670
|
import git3 from "isomorphic-git";
|
|
8624
|
-
import
|
|
8671
|
+
import postcss6 from "postcss";
|
|
8625
8672
|
|
|
8626
8673
|
// src/lib/remote-auth/transport.ts
|
|
8627
8674
|
import git2 from "isomorphic-git";
|
|
@@ -9087,7 +9134,7 @@ function paragraphs(text) {
|
|
|
9087
9134
|
}
|
|
9088
9135
|
function cssRules(text) {
|
|
9089
9136
|
try {
|
|
9090
|
-
return
|
|
9137
|
+
return postcss6.parse(text).nodes.map((node) => node.toString().trim()).filter((rule) => rule.length > 0);
|
|
9091
9138
|
} catch {
|
|
9092
9139
|
return textLines(text);
|
|
9093
9140
|
}
|
|
@@ -9108,13 +9155,10 @@ function changeHint(filepath, before, after) {
|
|
|
9108
9155
|
}
|
|
9109
9156
|
return count > 0 ? `${plural(count, unit)} changed` : "changed";
|
|
9110
9157
|
}
|
|
9111
|
-
async function
|
|
9112
|
-
const
|
|
9113
|
-
|
|
9114
|
-
}
|
|
9115
|
-
async function listVersionChanges(dir, oid, options = {}) {
|
|
9116
|
-
const cache2 = options.cache ?? {};
|
|
9117
|
-
const parent = await firstParent(dir, cache2, oid);
|
|
9158
|
+
async function listVersionChanges(projectDir, oid) {
|
|
9159
|
+
const dir = await repoDirFor(projectDir);
|
|
9160
|
+
const cache2 = {};
|
|
9161
|
+
const parent = (await git3.readCommit({ fs: gitFs, dir, cache: cache2, oid })).commit.parent[0] ?? null;
|
|
9118
9162
|
const found = [];
|
|
9119
9163
|
await git3.walk({
|
|
9120
9164
|
fs: gitFs,
|
|
@@ -9185,52 +9229,105 @@ async function trackingTip(dir) {
|
|
|
9185
9229
|
}
|
|
9186
9230
|
return resolveOrNull(dir, `refs/remotes/${remote.remote}/${branch}`);
|
|
9187
9231
|
}
|
|
9188
|
-
async function isAncestor(dir, cache2, oid, ancestor) {
|
|
9189
|
-
if (oid === ancestor)
|
|
9190
|
-
return true;
|
|
9191
|
-
try {
|
|
9192
|
-
return await git3.isDescendent({ fs: gitFs, dir, cache: cache2, oid, ancestor });
|
|
9193
|
-
} catch {
|
|
9194
|
-
return false;
|
|
9195
|
-
}
|
|
9196
|
-
}
|
|
9197
9232
|
async function describeVersions(projectDir, entries) {
|
|
9198
9233
|
const dir = await repoDirFor(projectDir);
|
|
9199
|
-
const cache2 = {};
|
|
9200
9234
|
const tip = await trackingTip(dir);
|
|
9235
|
+
if (!tip)
|
|
9236
|
+
return entries.map((entry) => ({ ...entry, backedUp: null }));
|
|
9237
|
+
const cache2 = {};
|
|
9238
|
+
const shallow = await readFile30(path19.join(dir, ".git", "shallow"), "utf8").catch(() => "");
|
|
9239
|
+
const reached = new Set([tip]);
|
|
9240
|
+
const queued = new Set;
|
|
9241
|
+
const queue = [tip];
|
|
9201
9242
|
const out = [];
|
|
9202
9243
|
let olderAreBackedUp = false;
|
|
9203
9244
|
for (const entry of entries) {
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9245
|
+
try {
|
|
9246
|
+
while (!olderAreBackedUp && !reached.has(entry.id) && queue.length > 0) {
|
|
9247
|
+
const oid = queue.shift();
|
|
9248
|
+
const { commit } = await git3.readCommit({ fs: gitFs, dir, cache: cache2, oid });
|
|
9249
|
+
for (const parent of commit.parent)
|
|
9250
|
+
reached.add(parent);
|
|
9251
|
+
if (shallow.includes(oid))
|
|
9252
|
+
continue;
|
|
9253
|
+
for (const parent of commit.parent) {
|
|
9254
|
+
if (queued.has(parent))
|
|
9255
|
+
continue;
|
|
9256
|
+
queued.add(parent);
|
|
9257
|
+
queue.push(parent);
|
|
9258
|
+
}
|
|
9259
|
+
}
|
|
9260
|
+
} catch {
|
|
9261
|
+
queue.length = 0;
|
|
9210
9262
|
}
|
|
9211
|
-
|
|
9263
|
+
const backedUp = olderAreBackedUp || reached.has(entry.id);
|
|
9264
|
+
if (backedUp)
|
|
9265
|
+
olderAreBackedUp = true;
|
|
9266
|
+
out.push({ ...entry, backedUp });
|
|
9212
9267
|
}
|
|
9213
9268
|
return out;
|
|
9214
9269
|
}
|
|
9270
|
+
var CLOCK_SKEW_SECONDS = 10 * 60;
|
|
9215
9271
|
async function commitsAhead(dir, fromOid, baseOid, cache2 = {}, limit = 100) {
|
|
9216
9272
|
if (fromOid === baseOid)
|
|
9217
9273
|
return [];
|
|
9218
|
-
const
|
|
9219
|
-
const
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
const
|
|
9223
|
-
|
|
9224
|
-
const
|
|
9225
|
-
|
|
9226
|
-
|
|
9274
|
+
const walk = await startHistoryWalk(dir, cache2, [baseOid, fromOid]);
|
|
9275
|
+
const local = new Set([baseOid]);
|
|
9276
|
+
const online = new Set([fromOid]);
|
|
9277
|
+
const parentsOf = new Map;
|
|
9278
|
+
const listed = [];
|
|
9279
|
+
let shared = false;
|
|
9280
|
+
const mark = (oid) => {
|
|
9281
|
+
const stack = [oid];
|
|
9282
|
+
for (let next = stack.pop();next !== undefined; next = stack.pop()) {
|
|
9283
|
+
if (local.has(next))
|
|
9284
|
+
continue;
|
|
9285
|
+
local.add(next);
|
|
9286
|
+
if (online.has(next))
|
|
9287
|
+
shared = true;
|
|
9288
|
+
stack.push(...parentsOf.get(next) ?? []);
|
|
9289
|
+
}
|
|
9290
|
+
};
|
|
9291
|
+
const firstAhead = () => {
|
|
9292
|
+
const out = [];
|
|
9293
|
+
for (const c of listed) {
|
|
9294
|
+
if (out.length === limit)
|
|
9295
|
+
break;
|
|
9296
|
+
if (!local.has(c.oid))
|
|
9297
|
+
out.push(c);
|
|
9298
|
+
}
|
|
9299
|
+
return out;
|
|
9300
|
+
};
|
|
9301
|
+
const time = (c) => c.commit.committer.timestamp;
|
|
9302
|
+
for (;; ) {
|
|
9303
|
+
const searching = walk.tips.some((c2) => !local.has(c2.oid));
|
|
9304
|
+
if (!searching || shared && listed.length >= limit) {
|
|
9305
|
+
const shown = firstAhead();
|
|
9306
|
+
if (!searching || shown.length === limit) {
|
|
9307
|
+
const oldest = Math.min(...shown.map(time));
|
|
9308
|
+
const newestMarked = walk.tips.findLast((c2) => local.has(c2.oid));
|
|
9309
|
+
if (!newestMarked || time(newestMarked) < oldest - CLOCK_SKEW_SECONDS)
|
|
9310
|
+
break;
|
|
9311
|
+
}
|
|
9312
|
+
}
|
|
9313
|
+
const c = await nextInHistory(dir, walk, cache2, 1);
|
|
9314
|
+
if (!c)
|
|
9227
9315
|
break;
|
|
9228
|
-
|
|
9316
|
+
const parents = walk.shallow.has(c.oid) ? [] : c.commit.parent;
|
|
9317
|
+
parentsOf.set(c.oid, parents);
|
|
9318
|
+
if (local.has(c.oid)) {
|
|
9319
|
+
for (const p of parents)
|
|
9320
|
+
mark(p);
|
|
9229
9321
|
continue;
|
|
9230
|
-
|
|
9231
|
-
|
|
9322
|
+
}
|
|
9323
|
+
listed.push(c);
|
|
9324
|
+
for (const p of parents) {
|
|
9325
|
+
online.add(p);
|
|
9326
|
+
if (local.has(p))
|
|
9327
|
+
shared = true;
|
|
9328
|
+
}
|
|
9232
9329
|
}
|
|
9233
|
-
return
|
|
9330
|
+
return shared ? firstAhead().map(toEntry) : [];
|
|
9234
9331
|
}
|
|
9235
9332
|
async function listOnlineVersions(options) {
|
|
9236
9333
|
const http2 = options.httpClient ?? defaultGitHttp;
|
|
@@ -9258,12 +9355,8 @@ async function listOnlineVersions(options) {
|
|
|
9258
9355
|
const local = await resolveOrNull(dir, branch);
|
|
9259
9356
|
if (!tip || !local)
|
|
9260
9357
|
return { entries: [] };
|
|
9261
|
-
const entries =
|
|
9262
|
-
|
|
9263
|
-
const { files, more } = await listVersionChanges(dir, entry.id, { cache: cache2 });
|
|
9264
|
-
entries.push({ ...entry, changes: files, moreChanges: more, backedUp: true });
|
|
9265
|
-
}
|
|
9266
|
-
return { entries };
|
|
9358
|
+
const entries = await commitsAhead(dir, tip, local, cache2);
|
|
9359
|
+
return { entries: entries.map((entry) => ({ ...entry, backedUp: true })) };
|
|
9267
9360
|
} catch (e) {
|
|
9268
9361
|
const message = e?.message ?? "";
|
|
9269
9362
|
if (message === MSG_NO_REMOTE || message === MSG_SSH_REMOTE)
|
|
@@ -9953,9 +10046,9 @@ function isMergeConflictError(e) {
|
|
|
9953
10046
|
function isCheckoutConflict(e) {
|
|
9954
10047
|
return e?.code === "CheckoutConflictError";
|
|
9955
10048
|
}
|
|
9956
|
-
var CONVERGE_PREPARE_MESSAGE = "Getting your changes ready to combine with the online
|
|
9957
|
-
var CONVERGE_RESTORE_MESSAGE = "Kept both
|
|
9958
|
-
var CONVERGE_MERGE_MESSAGE = "Combined your changes with the online
|
|
10049
|
+
var CONVERGE_PREPARE_MESSAGE = "Getting your changes ready to combine with the online copy's changes";
|
|
10050
|
+
var CONVERGE_RESTORE_MESSAGE = "Kept both takes of the files that can't be combined";
|
|
10051
|
+
var CONVERGE_MERGE_MESSAGE = "Combined your changes with the online copy's";
|
|
9959
10052
|
var MARKER_UNSAFE_EXT = /\.svg$/i;
|
|
9960
10053
|
var OUR_LABEL = YOUR_VERSION_LABEL;
|
|
9961
10054
|
var THEIR_LABEL = ONLINE_VERSION_LABEL;
|
|
@@ -10485,8 +10578,8 @@ var API_BASE3 = "https://api.github.com";
|
|
|
10485
10578
|
var REQUEST_TIMEOUT_MS4 = 15000;
|
|
10486
10579
|
var MAX_NAME_LENGTH = 100;
|
|
10487
10580
|
var MSG = {
|
|
10488
|
-
notConnected: "Connect your GitHub account first
|
|
10489
|
-
auth: "GitHub didn't accept the saved sign-in. Sign in to GitHub again
|
|
10581
|
+
notConnected: "Connect your GitHub account first, then set up the online copy.",
|
|
10582
|
+
auth: "GitHub didn't accept the saved sign-in. Sign in to GitHub again and try again.",
|
|
10490
10583
|
offline: "Gutterpress couldn't reach GitHub. Check your internet connection and try again. Nothing was changed.",
|
|
10491
10584
|
noHistory: "Turn on version history for this book first. The online copy holds the versions Gutterpress keeps on this computer.",
|
|
10492
10585
|
alreadyHasRemote: "This book already has an online copy.",
|
|
@@ -11659,13 +11752,13 @@ async function createFolder(fetchImpl, accessToken, name) {
|
|
|
11659
11752
|
throw await driveFailure(res, `Couldn't create the Google Drive folder "${name}"`);
|
|
11660
11753
|
return await res.json();
|
|
11661
11754
|
}
|
|
11662
|
-
async function
|
|
11755
|
+
async function findFoldersByName(fetchImpl, accessToken, name, limit = 1) {
|
|
11663
11756
|
const q = `name='${escapeDriveQueryValue(name)}' and mimeType='${FOLDER_MIME}' and trashed=false`;
|
|
11664
11757
|
const params = new URLSearchParams({
|
|
11665
11758
|
q,
|
|
11666
11759
|
fields: "files(id,name)",
|
|
11667
11760
|
orderBy: "modifiedTime desc",
|
|
11668
|
-
pageSize:
|
|
11761
|
+
pageSize: String(limit)
|
|
11669
11762
|
});
|
|
11670
11763
|
const res = await driveFetch(fetchImpl, `${DRIVE_API_BASE}/files?${params.toString()}`, {
|
|
11671
11764
|
method: "GET",
|
|
@@ -11674,10 +11767,10 @@ async function findFolderByName(fetchImpl, accessToken, name) {
|
|
|
11674
11767
|
if (!res.ok)
|
|
11675
11768
|
throw await driveFailure(res, `Couldn't look up the Google Drive folder "${name}"`);
|
|
11676
11769
|
const body = await res.json();
|
|
11677
|
-
return body.files
|
|
11770
|
+
return body.files ?? [];
|
|
11678
11771
|
}
|
|
11679
11772
|
async function ensureFolder(fetchImpl, accessToken, name) {
|
|
11680
|
-
const found = await
|
|
11773
|
+
const [found] = await findFoldersByName(fetchImpl, accessToken, name);
|
|
11681
11774
|
if (found)
|
|
11682
11775
|
return found;
|
|
11683
11776
|
return createFolder(fetchImpl, accessToken, name);
|
|
@@ -11810,8 +11903,80 @@ async function resumableUpload(fetchImpl, accessToken, opts) {
|
|
|
11810
11903
|
}
|
|
11811
11904
|
}
|
|
11812
11905
|
|
|
11813
|
-
// src/lib/publish/providers/gdrive.ts
|
|
11906
|
+
// src/lib/publish/providers/gdrive-folder.ts
|
|
11814
11907
|
var DEFAULT_FOLDER_NAME = "Gutterpress";
|
|
11908
|
+
var NO_OWN_FOLDER = { myFolderId: "", myFolder: "", myFolderFor: "", myFolderAccount: "" };
|
|
11909
|
+
function readConfig(req) {
|
|
11910
|
+
const text = (key) => {
|
|
11911
|
+
const value = req.config[key];
|
|
11912
|
+
return (typeof value === "string" ? value.trim() : "") || undefined;
|
|
11913
|
+
};
|
|
11914
|
+
return {
|
|
11915
|
+
folder: text("folder"),
|
|
11916
|
+
folderId: text("folderId"),
|
|
11917
|
+
myFolderId: text("myFolderId"),
|
|
11918
|
+
myFolder: text("myFolder"),
|
|
11919
|
+
myFolderFor: text("myFolderFor"),
|
|
11920
|
+
myFolderAccount: text("myFolderAccount"),
|
|
11921
|
+
account: req.deps.credentialAccount?.trim() ?? ""
|
|
11922
|
+
};
|
|
11923
|
+
}
|
|
11924
|
+
async function resolveFolder(req, accessToken) {
|
|
11925
|
+
const fetchImpl = req.deps.fetch ?? globalThis.fetch;
|
|
11926
|
+
const cfg = readConfig(req);
|
|
11927
|
+
let found = [];
|
|
11928
|
+
if (cfg.folderId) {
|
|
11929
|
+
const folder = await getFolderById(fetchImpl, accessToken, cfg.folderId);
|
|
11930
|
+
if (folder)
|
|
11931
|
+
found = [folder];
|
|
11932
|
+
} else if (cfg.folder) {
|
|
11933
|
+
found = await findFoldersByName(fetchImpl, accessToken, cfg.folder, 2);
|
|
11934
|
+
}
|
|
11935
|
+
const first = found[0];
|
|
11936
|
+
const book = { title: cfg.folder || cfg.folderId || "", reachable: !cfg.folderId || !!first };
|
|
11937
|
+
if (cfg.myFolderId) {
|
|
11938
|
+
const bookKey = cfg.folderId || cfg.folder;
|
|
11939
|
+
if (bookKey && cfg.myFolderFor === bookKey && (cfg.myFolderAccount ?? "") === cfg.account) {
|
|
11940
|
+
const mine = await getFolderById(fetchImpl, accessToken, cfg.myFolderId);
|
|
11941
|
+
return mine ? { state: "mine", destination: toDestination(mine), book } : { state: "unreachable", book, mine: cfg.myFolder || cfg.myFolderId };
|
|
11942
|
+
}
|
|
11943
|
+
if (book.reachable) {
|
|
11944
|
+
await req.deps.selections?.setSettings(req.project.projectDir, "gdrive", NO_OWN_FOLDER);
|
|
11945
|
+
}
|
|
11946
|
+
}
|
|
11947
|
+
if (cfg.folderId) {
|
|
11948
|
+
return first ? { state: "book", destination: toDestination(first) } : { state: "unreachable", book };
|
|
11949
|
+
}
|
|
11950
|
+
if (cfg.folder) {
|
|
11951
|
+
return {
|
|
11952
|
+
state: "by-name",
|
|
11953
|
+
name: cfg.folder,
|
|
11954
|
+
...first ? { destination: toDestination(first) } : {},
|
|
11955
|
+
several: found.length > 1
|
|
11956
|
+
};
|
|
11957
|
+
}
|
|
11958
|
+
return { state: "unset" };
|
|
11959
|
+
}
|
|
11960
|
+
async function uploadFolder(where, fetchImpl, accessToken) {
|
|
11961
|
+
switch (where.state) {
|
|
11962
|
+
case "unreachable":
|
|
11963
|
+
throw new Error(where.mine ? `Your own Drive folder "${where.mine}" for this book can't be reached any more: it was trashed or deleted, or belongs to another Google account. Choose a folder you can reach on the Google Drive step (it is saved for you only).` : `This book publishes to the Drive folder "${where.book.title}", which this Google account can't reach: it isn't shared with Gutterpress under this account, or it was trashed or deleted. The book keeps its folder for everyone else. To publish to a folder you can reach, choose one for yourself on the Google Drive step in the desktop app (saved for you only), or pass --folder-id <id> or --folder <name> to "gutterpress publish" for one run.`);
|
|
11964
|
+
case "unset":
|
|
11965
|
+
return toDestination(await ensureFolder(fetchImpl, accessToken, DEFAULT_FOLDER_NAME));
|
|
11966
|
+
case "by-name":
|
|
11967
|
+
return where.destination ?? toDestination(await createFolder(fetchImpl, accessToken, where.name));
|
|
11968
|
+
default:
|
|
11969
|
+
return where.destination;
|
|
11970
|
+
}
|
|
11971
|
+
}
|
|
11972
|
+
function folderUrl(id) {
|
|
11973
|
+
return `https://drive.google.com/drive/folders/${id}`;
|
|
11974
|
+
}
|
|
11975
|
+
function toDestination(folder) {
|
|
11976
|
+
return { id: folder.id, title: folder.name, url: folderUrl(folder.id) };
|
|
11977
|
+
}
|
|
11978
|
+
|
|
11979
|
+
// src/lib/publish/providers/gdrive.ts
|
|
11815
11980
|
var NOT_CONNECTED_MESSAGE = "Google Drive isn't connected. Run `gutterpress publish --provider gdrive --connect` (or set GDRIVE_REFRESH_TOKEN) first.";
|
|
11816
11981
|
var info5 = {
|
|
11817
11982
|
id: "gdrive",
|
|
@@ -11830,13 +11995,6 @@ var info5 = {
|
|
|
11830
11995
|
},
|
|
11831
11996
|
destinations: { label: "Folder", canCreate: true }
|
|
11832
11997
|
};
|
|
11833
|
-
function readConfig(req) {
|
|
11834
|
-
const cfg = req.config;
|
|
11835
|
-
return {
|
|
11836
|
-
folder: cfg.folder?.trim() || undefined,
|
|
11837
|
-
folderId: cfg.folderId?.trim() || undefined
|
|
11838
|
-
};
|
|
11839
|
-
}
|
|
11840
11998
|
function formatBytes(bytes) {
|
|
11841
11999
|
if (bytes < 1024 * 1024)
|
|
11842
12000
|
return `${(bytes / 1024).toFixed(0)} KB`;
|
|
@@ -11858,22 +12016,6 @@ async function getAccessToken(req) {
|
|
|
11858
12016
|
const accessToken = await mintAccessToken(resolved, req.deps.fetch ?? globalThis.fetch);
|
|
11859
12017
|
return { accessToken, source: resolved.source };
|
|
11860
12018
|
}
|
|
11861
|
-
async function resolveFolder(fetchImpl, accessToken, cfg) {
|
|
11862
|
-
if (cfg.folderId) {
|
|
11863
|
-
const folder = await getFolderById(fetchImpl, accessToken, cfg.folderId);
|
|
11864
|
-
if (!folder) {
|
|
11865
|
-
throw new Error(`The Drive folder recorded in publish.gdrive.folderId ("${cfg.folderId}") can't be found — it may have been trashed or the id copied wrong. Pick the folder again.`);
|
|
11866
|
-
}
|
|
11867
|
-
return folder;
|
|
11868
|
-
}
|
|
11869
|
-
return ensureFolder(fetchImpl, accessToken, cfg.folder || DEFAULT_FOLDER_NAME);
|
|
11870
|
-
}
|
|
11871
|
-
function folderUrl(id) {
|
|
11872
|
-
return `https://drive.google.com/drive/folders/${id}`;
|
|
11873
|
-
}
|
|
11874
|
-
function toDestination(folder) {
|
|
11875
|
-
return { id: folder.id, title: folder.name, url: folderUrl(folder.id) };
|
|
11876
|
-
}
|
|
11877
12019
|
var ZIP_CHUNK_BYTES = 64 * 1024;
|
|
11878
12020
|
function yieldToEventLoop() {
|
|
11879
12021
|
return new Promise((resolve6) => setImmediate(resolve6));
|
|
@@ -12000,9 +12142,12 @@ var gdriveProvider = {
|
|
|
12000
12142
|
const folder = await createFolder(req.deps.fetch ?? globalThis.fetch, accessToken, name);
|
|
12001
12143
|
return toDestination(folder);
|
|
12002
12144
|
},
|
|
12145
|
+
async destinationStatus(req) {
|
|
12146
|
+
const { accessToken } = await getAccessToken(req);
|
|
12147
|
+
return resolveFolder(req, accessToken);
|
|
12148
|
+
},
|
|
12003
12149
|
async upload(req) {
|
|
12004
12150
|
const fetchImpl = req.deps.fetch ?? globalThis.fetch;
|
|
12005
|
-
const cfg = readConfig(req);
|
|
12006
12151
|
const { accessToken } = await getAccessToken(req);
|
|
12007
12152
|
const isHtml = req.artifact.format === "html";
|
|
12008
12153
|
const source = isHtml ? await zipHtmlExport(req.artifact.path, req.project.title, (msg) => req.deps.onProgress?.(msg)) : {
|
|
@@ -12020,10 +12165,11 @@ var gdriveProvider = {
|
|
|
12020
12165
|
}
|
|
12021
12166
|
}
|
|
12022
12167
|
req.deps.onProgress?.("Resolving the Drive folder…");
|
|
12023
|
-
const
|
|
12168
|
+
const where = await resolveFolder(req, accessToken);
|
|
12169
|
+
const folder = await uploadFolder(where, fetchImpl, accessToken);
|
|
12024
12170
|
const fileName = source.fileName;
|
|
12025
12171
|
const existing = await findFileInFolder(fetchImpl, accessToken, folder.id, fileName);
|
|
12026
|
-
req.deps.onProgress?.(existing ? `Updating "${fileName}" in "${folder.
|
|
12172
|
+
req.deps.onProgress?.(existing ? `Updating "${fileName}" in "${folder.title}"…` : `Uploading "${fileName}" to "${folder.title}"…`);
|
|
12027
12173
|
let lastReported = -1;
|
|
12028
12174
|
const file = await resumableUpload(fetchImpl, accessToken, {
|
|
12029
12175
|
...existing ? { fileId: existing.id } : {},
|
|
@@ -12043,11 +12189,11 @@ var gdriveProvider = {
|
|
|
12043
12189
|
return {
|
|
12044
12190
|
kind: "published",
|
|
12045
12191
|
url: file.webViewLink,
|
|
12046
|
-
detail: `Uploaded "${fileName}" to the "${folder.
|
|
12192
|
+
detail: `Uploaded "${fileName}" to the "${folder.title}" folder in your Google Drive${existing ? " (updated the existing file)" : ""}.` + (where.state === "by-name" && where.several ? ` Several of your folders are named "${where.name}"; this is the most recently changed one: ${folder.url}` : ""),
|
|
12047
12193
|
followUp: [
|
|
12048
12194
|
"To share it, open it in Drive and use the Share button — Gutterpress never changes who can see your files.",
|
|
12049
12195
|
isHtml ? "Google Drive stores files, not live websites — download and unzip it to view the export, or publish it as a real site with the Azure Static Web Apps provider." : undefined,
|
|
12050
|
-
|
|
12196
|
+
where.state === "unset" ? `Tip (CLI): record the folder id in the manifest (publish.gdrive.folderId: "${folder.id}") so renaming the folder in Drive can never break publishing.` : undefined
|
|
12051
12197
|
].filter((s) => !!s)
|
|
12052
12198
|
};
|
|
12053
12199
|
} finally {
|
|
@@ -12659,7 +12805,7 @@ async function resolvePublishProvider(id, book) {
|
|
|
12659
12805
|
const ext = await bookExtensions(book);
|
|
12660
12806
|
const npmEntry = ext.extensions.some((c) => !c.path && c.name === id);
|
|
12661
12807
|
if (npmEntry) {
|
|
12662
|
-
const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-
|
|
12808
|
+
const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-chj5nx3t.js");
|
|
12663
12809
|
await restoreForCommand2(book.projectDir, { manifestPath: book.manifestPath, failFast: true });
|
|
12664
12810
|
}
|
|
12665
12811
|
const publishers = await discover(ext);
|
|
@@ -12697,7 +12843,7 @@ function infoFor(p) {
|
|
|
12697
12843
|
}
|
|
12698
12844
|
function pluginProvider(p) {
|
|
12699
12845
|
let loading;
|
|
12700
|
-
const load = () => loading ??= import("./plugins-
|
|
12846
|
+
const load = () => loading ??= import("./plugins-0hhw6s17.js").then((m) => m.loadPublishFunction(p));
|
|
12701
12847
|
return {
|
|
12702
12848
|
info: infoFor(p),
|
|
12703
12849
|
async authenticate() {
|
|
@@ -12766,7 +12912,8 @@ async function requestFor(provider, options, deps) {
|
|
|
12766
12912
|
const publishSettings = manifest.publish ?? {};
|
|
12767
12913
|
const providerConfig = {
|
|
12768
12914
|
...publishSettings[provider.info.id] ?? {},
|
|
12769
|
-
...(await deps.selections?.settings(options.projectDir))?.[provider.info.id] ?? {}
|
|
12915
|
+
...(await deps.selections?.settings(options.projectDir))?.[provider.info.id] ?? {},
|
|
12916
|
+
...options.settings
|
|
12770
12917
|
};
|
|
12771
12918
|
const bookAccount = typeof providerConfig.credential === "string" ? providerConfig.credential.trim() : "";
|
|
12772
12919
|
const effectiveAccount = bookAccount || deps.credentialAccount;
|
|
@@ -13145,19 +13292,26 @@ async function savePublishSettings(projectDir, providerId, values, selections) {
|
|
|
13145
13292
|
if (inside !== null) {
|
|
13146
13293
|
shared[key] = inside;
|
|
13147
13294
|
mine[key] = "";
|
|
13148
|
-
} else if (isDir || key === "credential") {
|
|
13295
|
+
} else if (isDir || key === "credential" || providerId === "gdrive" && key.startsWith("myFolder")) {
|
|
13149
13296
|
mine[key] = isDir && text ? path31.resolve(projectDir, text) : text;
|
|
13150
13297
|
shared[key] = "";
|
|
13151
13298
|
} else {
|
|
13152
13299
|
shared[key] = value;
|
|
13153
13300
|
}
|
|
13154
13301
|
}
|
|
13302
|
+
const inManifest = (await readPublishSettings(projectDir))[providerId] ?? {};
|
|
13303
|
+
if ("myFolderId" in mine) {
|
|
13304
|
+
const own = !!mine.myFolderId;
|
|
13305
|
+
const account = mine.credential ?? (await selections.settings(projectDir))[providerId]?.credential;
|
|
13306
|
+
mine.myFolder = own ? mine.myFolder ?? "" : "";
|
|
13307
|
+
mine.myFolderFor = own ? String(inManifest.folderId || inManifest.folder || "") : "";
|
|
13308
|
+
mine.myFolderAccount = own ? account ?? String(inManifest.credential ?? "") : "";
|
|
13309
|
+
}
|
|
13155
13310
|
if (Object.keys(mine).length > 0)
|
|
13156
13311
|
await selections.setSettings(projectDir, providerId, mine);
|
|
13157
|
-
const inManifest = (await readPublishSettings(projectDir))[providerId] ?? {};
|
|
13158
13312
|
const changes = Object.fromEntries(Object.entries(shared).filter(([key, value]) => value !== "" || (key in inManifest)));
|
|
13159
13313
|
if (Object.keys(changes).length > 0)
|
|
13160
13314
|
await setPublishProviderConfig(projectDir, providerId, changes);
|
|
13161
13315
|
return readPublishSettings(projectDir, selections);
|
|
13162
13316
|
}
|
|
13163
|
-
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, renameRevision, deleteRevision, REPAIR_SNAPSHOT_MESSAGE, repairOnlineBackup, scorchedEarth, listPublishProviders, publishProviderFor, listBookPublishProviders, resolvePublishProvider, resolvePublishFormat, resolvePublishRequest, runPublish, connectPublishProvider, disconnectPublishCredential, PublishSelectionsStore, readPublishSettings, setPublishProviderConfig, savePublishSettings };
|
|
13317
|
+
export { SYNC_SNAPSHOT_MESSAGE, assembleCssChunks, relDisplay, 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, listVersionChanges, 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, renameRevision, deleteRevision, REPAIR_SNAPSHOT_MESSAGE, repairOnlineBackup, scorchedEarth, listPublishProviders, publishProviderFor, listBookPublishProviders, resolvePublishProvider, resolvePublishFormat, resolvePublishRequest, runPublish, connectPublishProvider, disconnectPublishCredential, PublishSelectionsStore, readPublishSettings, setPublishProviderConfig, savePublishSettings };
|