gutterpress 0.11.23-alpha.6 → 0.11.23-beta.1
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 +2 -0
- package/dist/{README-syg2hn83.md → README-8rct3axy.md} +2 -2
- package/dist/api/index.d.ts +3 -3
- package/dist/api/index.js +7 -5
- package/dist/{build-py643c5a.js → build-dg8a566j.js} +8 -7
- package/dist/{cli-tf9tw6at.js → cli-1fy1pm9d.js} +1 -1
- package/dist/cli-65gy8ask.js +82 -0
- package/dist/{cli-09jj8w5g.js → cli-989eg8jz.js} +186 -83
- package/dist/{cli-55br530w.js → cli-e31jshz6.js} +6 -84
- package/dist/{cli-58fsk6d7.js → cli-k087gj0p.js} +66 -34
- package/dist/{cli-qzkq9em8.js → cli-s1zt779w.js} +1 -1
- package/dist/{cli-x777v272.js → cli-ttj95aex.js} +3 -3
- package/dist/{cli-bnf7eb38.js → cli-vhav8g82.js} +1 -1
- package/dist/cli.js +12 -12
- package/dist/{doctor-tyrzqphh.js → doctor-g4a42tcz.js} +3 -3
- package/dist/engine/shared/page-vars.d.ts +32 -0
- package/dist/{engine-4rabsgwq.js → engine-jvkcahr1.js} +2 -1
- package/dist/{engine-wm4fggac.js → engine-wgkaqjsc.js} +3 -3
- package/dist/{ext-70kt3eft.js → ext-w9d5ftcg.js} +8 -7
- package/dist/{extension-restore-whag4sm0.js → extension-restore-5x4mj79d.js} +3 -3
- package/dist/{extension-restore-fjfn1hrk.js → extension-restore-as0g38p1.js} +5 -4
- package/dist/{gutterpress-viewer-mafsby5k.js → gutterpress-viewer-ze691rrd.js} +52 -13
- package/dist/{index-2vxzxyyj.js → index-798jt0qc.js} +9 -9
- package/dist/{index-1zqkfzcd.js → index-a4j3xcfp.js} +3 -3
- package/dist/{index-ejbfetdn.js → index-c6y0p7jy.js} +272 -151
- package/dist/{index-wm4x7xbf.js → index-fk2kpf2t.js} +58 -26
- package/dist/{index-cmakyf43.js → index-rj5vrhz6.js} +5 -85
- package/dist/index-tayzfr9c.js +99 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +233 -6
- package/dist/lib/book-css.d.ts +56 -0
- package/dist/lib/markdown/assemble.d.ts +58 -0
- 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/revisions.d.ts +5 -1
- package/dist/lib/remote-auth/sync-messages.d.ts +2 -2
- package/dist/lib/root-tokens.d.ts +76 -0
- package/dist/lib/version-changes.d.ts +12 -12
- 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-b27kyr02.js → new-jtk028rf.js} +8 -7
- package/dist/{page-rules-ytj9p04v.css → page-rules-vnja6grt.css} +2 -2
- package/dist/{plugins-racab3cj.js → plugins-bcbe7xs8.js} +1 -1
- package/dist/{plugins-vpx7c53c.js → plugins-zsm6wv79.js} +3 -3
- package/dist/{preflight-akdqz9v8.js → preflight-cs6mj1d6.js} +8 -7
- package/dist/{preview-bck9f88w.js → preview-d1j1g4qf.js} +8 -7
- package/dist/{publish-keyp8rh4.js → publish-7yw4jyrt.js} +24 -8
- package/dist/render.js +65 -24
- package/dist/schema/manifest.types.d.ts +2 -1
- package/dist/{tokens-n82t4k9h.css → tokens-a7grv940.css} +4 -5
- package/dist/{validate-z28cvr5x.js → validate-rkcdnzhw.js} +8 -7
- package/package.json +1 -1
- package/dist/index-46ycxe6r.js +0 -18
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
SYNC_SNAPSHOT_MESSAGE,
|
|
22
22
|
VERSION_CHANGES_SHOWN,
|
|
23
23
|
adoptFolder,
|
|
24
|
+
assembleCssChunks,
|
|
24
25
|
autoSnapshotDelayMs,
|
|
25
26
|
autoSyncDelayMs,
|
|
26
27
|
checkCss,
|
|
@@ -70,6 +71,7 @@ import {
|
|
|
70
71
|
listPublishProviders,
|
|
71
72
|
listRepoBooks,
|
|
72
73
|
listSnippets,
|
|
74
|
+
listVersionChanges,
|
|
73
75
|
openPath,
|
|
74
76
|
parseRemoteOrigin,
|
|
75
77
|
pkceChallengeFromVerifier,
|
|
@@ -80,6 +82,7 @@ import {
|
|
|
80
82
|
readPageSetup,
|
|
81
83
|
readPublishSettings,
|
|
82
84
|
refreshRemoteCopies,
|
|
85
|
+
relDisplay,
|
|
83
86
|
renameRevision,
|
|
84
87
|
repairOnlineBackup,
|
|
85
88
|
reportMissingTools,
|
|
@@ -119,7 +122,7 @@ import {
|
|
|
119
122
|
substituteVariables,
|
|
120
123
|
syncProject,
|
|
121
124
|
testRemoteAccess
|
|
122
|
-
} from "./index-
|
|
125
|
+
} from "./index-c6y0p7jy.js";
|
|
123
126
|
import {
|
|
124
127
|
DEFAULT_NPM_REGISTRY,
|
|
125
128
|
MANIFEST_FILENAMES,
|
|
@@ -149,7 +152,7 @@ import {
|
|
|
149
152
|
setExtensionEnabled,
|
|
150
153
|
updateExtensions,
|
|
151
154
|
validateProjectExtensions
|
|
152
|
-
} from "./index-
|
|
155
|
+
} from "./index-rj5vrhz6.js";
|
|
153
156
|
import {
|
|
154
157
|
BUNDLED_EXTENSIONS,
|
|
155
158
|
EXTENSION_MANIFEST_FILENAME,
|
|
@@ -160,20 +163,21 @@ import {
|
|
|
160
163
|
extensionStyleList,
|
|
161
164
|
isBundledExtension,
|
|
162
165
|
isPathSpecifier,
|
|
166
|
+
loadPluginsWithCss,
|
|
163
167
|
log,
|
|
164
168
|
parseExtensionSpecifier,
|
|
165
169
|
pathEscapesFolder,
|
|
166
170
|
readExtensionMeta,
|
|
167
171
|
resolveExtension
|
|
168
|
-
} from "./index-
|
|
172
|
+
} from "./index-a4j3xcfp.js";
|
|
169
173
|
import"./index-ht1bcqdd.js";
|
|
170
174
|
import {
|
|
171
175
|
BUILD_DIAGNOSTIC_CODES
|
|
172
|
-
} from "./index-
|
|
173
|
-
import"./index-
|
|
176
|
+
} from "./index-fk2kpf2t.js";
|
|
177
|
+
import"./index-798jt0qc.js";
|
|
174
178
|
import {
|
|
175
179
|
BuildError
|
|
176
|
-
} from "./index-
|
|
180
|
+
} from "./index-tayzfr9c.js";
|
|
177
181
|
import"./index-5crpmz42.js";
|
|
178
182
|
import {
|
|
179
183
|
DEFAULT_PRINT_OPTS,
|
|
@@ -207,6 +211,226 @@ import {
|
|
|
207
211
|
repoSubPath
|
|
208
212
|
} from "./index-v5mp7a6q.js";
|
|
209
213
|
import"./index-37x76zdn.js";
|
|
214
|
+
// src/lib/book-css.ts
|
|
215
|
+
import { readFile } from "node:fs/promises";
|
|
216
|
+
import path from "node:path";
|
|
217
|
+
|
|
218
|
+
// src/lib/root-tokens.ts
|
|
219
|
+
import postcss from "postcss";
|
|
220
|
+
var DIRECTIVE_RE = /@(group|label|internal)\b[ \t]*([^\n\r@]*)/gi;
|
|
221
|
+
function fold(into, comment) {
|
|
222
|
+
for (const d of comment.matchAll(DIRECTIVE_RE)) {
|
|
223
|
+
const key = d[1].toLowerCase();
|
|
224
|
+
if (key === "internal") {
|
|
225
|
+
into.internal = true;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const value = d[2].trim();
|
|
229
|
+
if (value)
|
|
230
|
+
into[key] = value;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function rootRules(root) {
|
|
234
|
+
const out = [];
|
|
235
|
+
const visit = (container, layer) => {
|
|
236
|
+
container.each((node) => {
|
|
237
|
+
if (node.type === "rule") {
|
|
238
|
+
if (node.selectors.some((s) => s.trim().toLowerCase() === ":root")) {
|
|
239
|
+
out.push(layer === undefined ? { rule: node } : { rule: node, layer });
|
|
240
|
+
}
|
|
241
|
+
} else if (node.type === "atrule" && node.name.toLowerCase() === "layer" && node.nodes) {
|
|
242
|
+
const name = node.params.trim();
|
|
243
|
+
visit(node, layer === undefined ? name : name ? `${layer}.${name}` : layer);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
};
|
|
247
|
+
visit(root, undefined);
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
function leadingComments(rule) {
|
|
251
|
+
const out = [];
|
|
252
|
+
for (let prev = rule.prev();prev?.type === "comment"; prev = prev.prev())
|
|
253
|
+
out.unshift(prev.text);
|
|
254
|
+
return out;
|
|
255
|
+
}
|
|
256
|
+
function writtenValue(decl) {
|
|
257
|
+
return decl.raws.value?.raw ?? decl.value;
|
|
258
|
+
}
|
|
259
|
+
function rootTokens(css) {
|
|
260
|
+
const out = [];
|
|
261
|
+
for (const { rule, layer } of rootRules(postcss.parse(css))) {
|
|
262
|
+
const defaults = {};
|
|
263
|
+
for (const comment of leadingComments(rule))
|
|
264
|
+
fold(defaults, comment);
|
|
265
|
+
let pending = {};
|
|
266
|
+
rule.each((node) => {
|
|
267
|
+
if (node.type === "comment") {
|
|
268
|
+
fold(pending, node.text);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (node.type !== "decl" || !node.prop.startsWith("--"))
|
|
272
|
+
return;
|
|
273
|
+
const own = pending;
|
|
274
|
+
pending = {};
|
|
275
|
+
const annotated = own.group !== undefined || own.label !== undefined;
|
|
276
|
+
if (own.internal || defaults.internal && !annotated)
|
|
277
|
+
return;
|
|
278
|
+
const group = own.group ?? defaults.group;
|
|
279
|
+
out.push({
|
|
280
|
+
name: node.prop,
|
|
281
|
+
value: writtenValue(node),
|
|
282
|
+
...own.label !== undefined ? { label: own.label } : {},
|
|
283
|
+
...group !== undefined ? { group } : {},
|
|
284
|
+
...layer !== undefined ? { layer } : {}
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
return out;
|
|
289
|
+
}
|
|
290
|
+
function declarationsOf(root, name) {
|
|
291
|
+
return rootRules(root).flatMap(({ rule }) => (rule.nodes ?? []).filter((n) => n.type === "decl" && n.prop === name));
|
|
292
|
+
}
|
|
293
|
+
function removeDeclaration(decl) {
|
|
294
|
+
const rule = decl.parent;
|
|
295
|
+
decl.remove();
|
|
296
|
+
if ((rule.nodes ?? []).length > 0)
|
|
297
|
+
return;
|
|
298
|
+
const parent = rule.parent;
|
|
299
|
+
const next = rule.next();
|
|
300
|
+
const before = rule.raws.before ?? "";
|
|
301
|
+
rule.remove();
|
|
302
|
+
const keep = (after) => before + (after ?? "").replace(/^\n/, "");
|
|
303
|
+
if (next)
|
|
304
|
+
next.raws.before = keep(next.raws.before);
|
|
305
|
+
else
|
|
306
|
+
parent.raws.after = keep(parent.raws.after);
|
|
307
|
+
}
|
|
308
|
+
function insertDeclaration(root, name, value) {
|
|
309
|
+
const rule = rootRules(root)[0]?.rule;
|
|
310
|
+
if (rule) {
|
|
311
|
+
const hadDeclaration = (rule.nodes ?? []).some((n) => n.type === "decl");
|
|
312
|
+
rule.prepend(postcss.decl({ prop: name, value, raws: { before: rule.first?.raws.before ?? `
|
|
313
|
+
`, between: ": " } }));
|
|
314
|
+
if (!hadDeclaration)
|
|
315
|
+
rule.raws.semicolon = true;
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const tail = root.raws.after ?? "";
|
|
319
|
+
const added = postcss.rule({ selector: ":root", raws: { between: " ", after: `
|
|
320
|
+
`, semicolon: true } });
|
|
321
|
+
added.append(postcss.decl({ prop: name, value, raws: { before: `
|
|
322
|
+
`, between: ": " } }));
|
|
323
|
+
root.append(added);
|
|
324
|
+
added.raws.before = tail + ((root.nodes.length > 1 || tail) && !tail.endsWith(`
|
|
325
|
+
`) ? `
|
|
326
|
+
` : "");
|
|
327
|
+
root.raws.after = `
|
|
328
|
+
`;
|
|
329
|
+
}
|
|
330
|
+
function applyTokenUpdates(sheets, target, updates) {
|
|
331
|
+
const roots = sheets.map((css) => postcss.parse(css));
|
|
332
|
+
const changed = new Set;
|
|
333
|
+
for (const { name, value } of updates) {
|
|
334
|
+
const declared = roots.map((root) => declarationsOf(root, name));
|
|
335
|
+
if (value === null) {
|
|
336
|
+
declared.forEach((decls, i) => {
|
|
337
|
+
for (const decl of decls)
|
|
338
|
+
removeDeclaration(decl);
|
|
339
|
+
if (decls.length > 0)
|
|
340
|
+
changed.add(i);
|
|
341
|
+
});
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
let at = declared.length - 1;
|
|
345
|
+
while (at >= 0 && declared[at].length === 0)
|
|
346
|
+
at--;
|
|
347
|
+
if (at >= 0) {
|
|
348
|
+
for (const decl of declared[at])
|
|
349
|
+
decl.value = value;
|
|
350
|
+
changed.add(at);
|
|
351
|
+
} else {
|
|
352
|
+
insertDeclaration(roots[target], name, value);
|
|
353
|
+
changed.add(target);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return sheets.map((css, i) => changed.has(i) ? roots[i].toString() : css);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// src/lib/book-css.ts
|
|
360
|
+
async function readText(file) {
|
|
361
|
+
try {
|
|
362
|
+
return await readFile(file, "utf8");
|
|
363
|
+
} catch (error) {
|
|
364
|
+
if (error.code === "ENOENT")
|
|
365
|
+
return "";
|
|
366
|
+
throw error;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
async function bookSheets(projectDir, styles) {
|
|
370
|
+
return (await resolveActiveStyles(projectDir, styles)).map((rel) => path.resolve(projectDir, rel));
|
|
371
|
+
}
|
|
372
|
+
async function readBookCss(projectDir) {
|
|
373
|
+
const { manifest } = await loadManifestWithPath(projectDir);
|
|
374
|
+
const config = resolveConfig({}, manifest);
|
|
375
|
+
const { pluginStyles } = await loadPluginsWithCss(config.extensions, projectDir, () => {});
|
|
376
|
+
const extensions = [];
|
|
377
|
+
for (const group of pluginStyles) {
|
|
378
|
+
const layered = (css, file) => {
|
|
379
|
+
if (!css.trim())
|
|
380
|
+
return;
|
|
381
|
+
extensions.push({
|
|
382
|
+
origin: { kind: "extension", name: group.name, layer: group.layer, ...file ? { file } : {} },
|
|
383
|
+
css: `@layer ${group.layer} {
|
|
384
|
+
${css.trim()}
|
|
385
|
+
}`
|
|
386
|
+
});
|
|
387
|
+
};
|
|
388
|
+
for (const file of group.paths)
|
|
389
|
+
layered(await readText(file), relDisplay(projectDir, file));
|
|
390
|
+
if (group.css)
|
|
391
|
+
layered(group.css);
|
|
392
|
+
}
|
|
393
|
+
const book = [];
|
|
394
|
+
for (const file of await bookSheets(projectDir, config.styles)) {
|
|
395
|
+
const css = await readText(file);
|
|
396
|
+
if (css.trim())
|
|
397
|
+
book.push({ origin: { kind: "book", file: relDisplay(projectDir, file) }, css });
|
|
398
|
+
}
|
|
399
|
+
const chunks = assembleCssChunks({
|
|
400
|
+
printArea: { ...config.page, preset: config.preset, spec: config.spec },
|
|
401
|
+
extensions,
|
|
402
|
+
book
|
|
403
|
+
});
|
|
404
|
+
return {
|
|
405
|
+
chunks: chunks.map((chunk) => {
|
|
406
|
+
let tokens = [];
|
|
407
|
+
try {
|
|
408
|
+
tokens = rootTokens(chunk.css);
|
|
409
|
+
} catch {}
|
|
410
|
+
return { ...chunk, tokens };
|
|
411
|
+
})
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
async function bookTokenEdits(projectDir, target, updates) {
|
|
415
|
+
const { manifest } = await loadManifestWithPath(projectDir);
|
|
416
|
+
const sheets = await bookSheets(projectDir, Array.isArray(manifest.styles) ? manifest.styles : undefined);
|
|
417
|
+
const at = sheets.indexOf(path.resolve(projectDir, target));
|
|
418
|
+
if (at < 0) {
|
|
419
|
+
throw new Error(`${relDisplay(projectDir, path.resolve(projectDir, target))} is not one of the book's stylesheets.`);
|
|
420
|
+
}
|
|
421
|
+
const before = await Promise.all(sheets.map(readText));
|
|
422
|
+
let after;
|
|
423
|
+
try {
|
|
424
|
+
after = applyTokenUpdates(before, at, updates);
|
|
425
|
+
} catch (error) {
|
|
426
|
+
const e = error;
|
|
427
|
+
const i = e.source === undefined ? -1 : before.indexOf(e.source);
|
|
428
|
+
if (i < 0 || !e.reason)
|
|
429
|
+
throw error;
|
|
430
|
+
throw new Error(`Couldn't read ${relDisplay(projectDir, sheets[i])}${e.line ? ` (line ${e.line})` : ""}: ${e.reason}.`);
|
|
431
|
+
}
|
|
432
|
+
return sheets.flatMap((file, i) => after[i] === before[i] ? [] : [{ path: file, content: after[i] }]);
|
|
433
|
+
}
|
|
210
434
|
export {
|
|
211
435
|
whenGitIdle,
|
|
212
436
|
validateProjectExtensions,
|
|
@@ -271,6 +495,7 @@ export {
|
|
|
271
495
|
readManifestFields,
|
|
272
496
|
readExtensionMeta,
|
|
273
497
|
readExtensionCss,
|
|
498
|
+
readBookCss,
|
|
274
499
|
publishTargetFor,
|
|
275
500
|
publishProviderFor,
|
|
276
501
|
publishCredentialKey,
|
|
@@ -285,6 +510,7 @@ export {
|
|
|
285
510
|
log,
|
|
286
511
|
loadManifestWithPath,
|
|
287
512
|
loadManifest,
|
|
513
|
+
listVersionChanges,
|
|
288
514
|
listSnippets,
|
|
289
515
|
listRevisions,
|
|
290
516
|
listRepoBooks,
|
|
@@ -352,6 +578,7 @@ export {
|
|
|
352
578
|
checkCss,
|
|
353
579
|
capabilitiesFor,
|
|
354
580
|
bundledExtension,
|
|
581
|
+
bookTokenEdits,
|
|
355
582
|
autoSyncDelayMs,
|
|
356
583
|
autoSnapshotDelayMs,
|
|
357
584
|
assertMilestone,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The book's stylesheet as Book setup's design settings see it, and their one
|
|
3
|
+
* writer (#454).
|
|
4
|
+
*
|
|
5
|
+
* What the settings show is what the build assembles: core's CSS, every
|
|
6
|
+
* enabled extension's CSS — its stylesheet files and its `css` export,
|
|
7
|
+
* through the same loader the build uses, each in its cascade layer — then
|
|
8
|
+
* every one of the book's own `styles:` sheets, in the order
|
|
9
|
+
* `assembleCssChunks` gives them. A file's chunk is the file as written, so a
|
|
10
|
+
* value reads back as the author typed it: the build's inlining (fonts and
|
|
11
|
+
* images embedded, local `@import`s followed) is left out here.
|
|
12
|
+
*
|
|
13
|
+
* Writes go to the book's own `styles:` sheets only, never to an extension's
|
|
14
|
+
* files or a sheet the build does not use, with a postcss edit
|
|
15
|
+
* (`root-tokens.ts`).
|
|
16
|
+
*/
|
|
17
|
+
import { type CssChunk } from "./markdown/assemble.ts";
|
|
18
|
+
import { type RootToken, type TokenUpdate } from "./root-tokens.ts";
|
|
19
|
+
export type { RootToken, TokenUpdate } from "./root-tokens.ts";
|
|
20
|
+
export type { CssChunk, CssChunkOrigin } from "./markdown/assemble.ts";
|
|
21
|
+
/** One chunk of the book's stylesheet and the root-level custom properties it declares. */
|
|
22
|
+
export interface BookCssChunk extends CssChunk {
|
|
23
|
+
/**
|
|
24
|
+
* Its root-level `:root` custom properties, in source order (`@internal`
|
|
25
|
+
* ones left out). Empty for a chunk postcss cannot parse: the print-safety
|
|
26
|
+
* check reports that one.
|
|
27
|
+
*/
|
|
28
|
+
tokens: RootToken[];
|
|
29
|
+
}
|
|
30
|
+
/** The book's stylesheet, in cascade order. */
|
|
31
|
+
export interface BookCss {
|
|
32
|
+
/** Joined with blank lines, the chunks cascade as the built book's stylesheet does. */
|
|
33
|
+
chunks: BookCssChunk[];
|
|
34
|
+
}
|
|
35
|
+
/** One stylesheet the token writer changed: its absolute path and new text. */
|
|
36
|
+
export interface StylesheetEdit {
|
|
37
|
+
path: string;
|
|
38
|
+
content: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The book's stylesheet as ordered chunks tagged with their origin, each with
|
|
42
|
+
* its root-level custom properties. An extension that cannot load (not
|
|
43
|
+
* downloaded yet, a broken folder) is left out, as the live preview leaves it
|
|
44
|
+
* out; Book setup's Extensions tab reports it. Throws for a manifest the
|
|
45
|
+
* build would refuse.
|
|
46
|
+
*/
|
|
47
|
+
export declare function readBookCss(projectDir: string): Promise<BookCss>;
|
|
48
|
+
/**
|
|
49
|
+
* Apply token edits to the book's own stylesheets with postcss
|
|
50
|
+
* (`applyTokenUpdates`): a token is changed in place in the last of them that
|
|
51
|
+
* declares it at root level, and a new one is added to the `:root` of
|
|
52
|
+
* `target`, which must be one of them. A removal takes the token out of all
|
|
53
|
+
* of them. Returns the sheets that change, with their new text, for the
|
|
54
|
+
* caller to write; nothing is written here.
|
|
55
|
+
*/
|
|
56
|
+
export declare function bookTokenEdits(projectDir: string, target: string, updates: TokenUpdate[]): Promise<StylesheetEdit[]>;
|
|
@@ -167,6 +167,64 @@ export interface AssembleBookHtmlOptions {
|
|
|
167
167
|
*/
|
|
168
168
|
onImageRefs?: (refs: string[]) => void;
|
|
169
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Split a stylesheet into everything but its `@page` rules, and its `@page`
|
|
172
|
+
* rules, which the caller writes after it, unlayered, in source order. A
|
|
173
|
+
* rule inside an `@layer` loses that layer; one inside `@media` or
|
|
174
|
+
* `@supports` keeps the condition. CSS postcss cannot read is left as it was.
|
|
175
|
+
*
|
|
176
|
+
* Chromium ranks `@page` declarations by cascade layer before page name, so a
|
|
177
|
+
* layered `@page full { margin: 0 }` lost to the manifest's unlayered
|
|
178
|
+
* `@page { margin }` on its own pages, while the engine's reader, which
|
|
179
|
+
* ignores layers, kept it (#454). So no `@page` rule is written inside a
|
|
180
|
+
* layer — core's, an extension's or one in a layer the book declares —
|
|
181
|
+
* except core's 1in default (`defaultPageMarginCss`), which must lose to all
|
|
182
|
+
* of them.
|
|
183
|
+
* Unlayered, page rules cascade by page selector, then order: a named page
|
|
184
|
+
* keeps its margins, and the book's own sheet and the manifest, written
|
|
185
|
+
* later, still win on the pages they share with it.
|
|
186
|
+
*/
|
|
187
|
+
export declare function liftPageRules(css: string): {
|
|
188
|
+
layered: string;
|
|
189
|
+
pages: string;
|
|
190
|
+
};
|
|
191
|
+
/** Where one chunk of a book's stylesheet comes from. */
|
|
192
|
+
export type CssChunkOrigin = {
|
|
193
|
+
kind: "core";
|
|
194
|
+
}
|
|
195
|
+
/** One extension's CSS: `file` (project-relative) for one of its stylesheets, none for its `css` export. */
|
|
196
|
+
| {
|
|
197
|
+
kind: "extension";
|
|
198
|
+
name?: string;
|
|
199
|
+
layer?: string;
|
|
200
|
+
file?: string;
|
|
201
|
+
}
|
|
202
|
+
/** One of the book's own `styles:` sheets (project-relative). */
|
|
203
|
+
| {
|
|
204
|
+
kind: "book";
|
|
205
|
+
file?: string;
|
|
206
|
+
};
|
|
207
|
+
/** One piece of a book's stylesheet, tagged with where it comes from. */
|
|
208
|
+
export interface CssChunk {
|
|
209
|
+
origin: CssChunkOrigin;
|
|
210
|
+
css: string;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* A book's stylesheet as ordered chunks, each tagged with its origin, in
|
|
214
|
+
* cascade order (see the note where {@link assembleBookHtml} calls this).
|
|
215
|
+
* Joined with blank lines, the chunks are the `<style data-project-css>`
|
|
216
|
+
* block that function writes; it passes the extensions' CSS and the book's
|
|
217
|
+
* whole, one chunk each. Book setup's design settings pass them one extension
|
|
218
|
+
* file and one book sheet at a time, so each setting can say where it comes
|
|
219
|
+
* from (`book-css.ts`).
|
|
220
|
+
*/
|
|
221
|
+
export declare function assembleCssChunks(opts: {
|
|
222
|
+
printArea?: PrintAreaSource;
|
|
223
|
+
/** The extensions' CSS, already in their cascade layers, in `extensions:` order. */
|
|
224
|
+
extensions?: CssChunk[];
|
|
225
|
+
/** The book's own stylesheets, in `styles:` order. */
|
|
226
|
+
book?: CssChunk[];
|
|
227
|
+
}): CssChunk[];
|
|
170
228
|
/**
|
|
171
229
|
* Assemble a single `book.html` string from the given markdown files.
|
|
172
230
|
*
|
|
@@ -38,17 +38,25 @@ export interface DriveFolder {
|
|
|
38
38
|
* page — an author with more than one page of app-created folders would
|
|
39
39
|
* otherwise see the destinations picker silently miss folders past the
|
|
40
40
|
* first 100. (`ensureFolder`'s find-by-name lookup uses its own single
|
|
41
|
-
* server-side query, {@link
|
|
41
|
+
* server-side query, {@link findFoldersByName} below, and doesn't paginate.) */
|
|
42
42
|
export declare function listFolders(fetchImpl: typeof fetch, accessToken: string): Promise<DriveFolder[]>;
|
|
43
|
-
/** Look up one folder by id (for verifying a
|
|
44
|
-
*
|
|
43
|
+
/** Look up one folder by id (for verifying a recorded `folderId` is reachable
|
|
44
|
+
* with this account). Returns null when it isn't: trashed or deleted, or never
|
|
45
|
+
* opened through Gutterpress by this account — `drive.file` answers 404 for a
|
|
46
|
+
* folder this connection can't see, so the cases are one (#452). */
|
|
45
47
|
export declare function getFolderById(fetchImpl: typeof fetch, accessToken: string, folderId: string): Promise<DriveFolder | null>;
|
|
46
48
|
/** Create a folder at My Drive root. */
|
|
47
49
|
export declare function createFolder(fetchImpl: typeof fetch, accessToken: string, name: string): Promise<DriveFolder>;
|
|
50
|
+
/** App-visible folders with exactly this name, most recently modified first,
|
|
51
|
+
* at most `limit` of them — a single server-side query (the same `q=` pattern
|
|
52
|
+
* as {@link findFileInFolder}), not a client-side scan of every app-created
|
|
53
|
+
* folder. `ensureFolder` asks for one; a caller that must know whether
|
|
54
|
+
* several share the name (#452: the CLI says which it used) asks for two. */
|
|
55
|
+
export declare function findFoldersByName(fetchImpl: typeof fetch, accessToken: string, name: string, limit?: number): Promise<DriveFolder[]>;
|
|
48
56
|
/** Find-or-create a folder by name at My Drive root (D5's name-resolution
|
|
49
57
|
* path, used when the manifest has no recorded `folderId` yet). One query,
|
|
50
58
|
* not a client-side scan of every app-created folder — see
|
|
51
|
-
* {@link
|
|
59
|
+
* {@link findFoldersByName}. */
|
|
52
60
|
export declare function ensureFolder(fetchImpl: typeof fetch, accessToken: string, name: string): Promise<DriveFolder>;
|
|
53
61
|
export interface DriveFile {
|
|
54
62
|
id: string;
|
|
@@ -25,6 +25,12 @@ export declare function setPublishProviderConfig(projectDir: string, providerId:
|
|
|
25
25
|
* override). A folder inside the book is shared, and is written relative to
|
|
26
26
|
* it (an absolute in-book path is rewritten) so it means the same thing on
|
|
27
27
|
* every computer. An absolute path is never written to the manifest.
|
|
28
|
+
* - their own Google Drive folder for the book (`myFolderId`, `myFolder`; #452),
|
|
29
|
+
* chosen when the book's folder is out of their reach or just for them. It is
|
|
30
|
+
* recorded with the book's folder it stands in for (`myFolderFor`: the
|
|
31
|
+
* manifest's `folderId`, else its `folder` name) and the account it was
|
|
32
|
+
* chosen with (`myFolderAccount`), so it lapses when either changes (see
|
|
33
|
+
* providers/gdrive.ts). Clearing `myFolderId` clears the whole record.
|
|
28
34
|
*
|
|
29
35
|
* Only the values passed are classified; the manifest is not rewritten unless
|
|
30
36
|
* one of them changes it. Empty values clear. Returns the settings as the
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which Google Drive folder one person publishes to (#452), for the `gdrive`
|
|
3
|
+
* provider (gdrive.ts). The book's folder in the manifest is the team's; a
|
|
4
|
+
* person whose Google connection can't reach it publishes to a folder of their
|
|
5
|
+
* own, kept in their private store for the book, and that folder stands in for
|
|
6
|
+
* the book's only while the book's folder and their account are the ones it
|
|
7
|
+
* was chosen for.
|
|
8
|
+
*/
|
|
9
|
+
import { type DriveFolder } from "../google-drive.ts";
|
|
10
|
+
import type { PublishDestinationStatus, PublishProduct, PublishRequest } from "../types.ts";
|
|
11
|
+
/** Default folder name when the manifest sets neither `folder` nor
|
|
12
|
+
* `folderId` (D5, ratified: one shared "Gutterpress" folder, not
|
|
13
|
+
* per-book-title folders). */
|
|
14
|
+
export declare const DEFAULT_FOLDER_NAME = "Gutterpress";
|
|
15
|
+
/**
|
|
16
|
+
* Where this person publishes (#452). The book's folder is the team's: an id,
|
|
17
|
+
* checked with the account this person publishes with (`drive.file` answers 404
|
|
18
|
+
* for a folder it can't see, so "not shared with Gutterpress", "another
|
|
19
|
+
* account" and "trashed" are one case), or a name, which each person finds or
|
|
20
|
+
* creates in their own Drive. Their own folder, from their private store,
|
|
21
|
+
* stands in for it while it was chosen for the book's current folder and with
|
|
22
|
+
* the account they publish with now; one recorded against another folder or
|
|
23
|
+
* account no longer applies, and is dropped once the book's folder is
|
|
24
|
+
* reachable. Never writes the manifest or creates a folder.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveFolder(req: PublishRequest, accessToken: string): Promise<PublishDestinationStatus>;
|
|
27
|
+
/** The folder an upload goes to. An unreachable one fails, naming it and how
|
|
28
|
+
* to publish elsewhere: publishing never creates a folder in its place. */
|
|
29
|
+
export declare function uploadFolder(where: PublishDestinationStatus, fetchImpl: typeof fetch, accessToken: string): Promise<PublishProduct>;
|
|
30
|
+
export declare function toDestination(folder: DriveFolder): PublishProduct;
|
|
@@ -10,6 +10,11 @@ export interface RunPublishOptions {
|
|
|
10
10
|
artifactPath?: string;
|
|
11
11
|
/** Stop after preflight — report issues without contacting the platform. */
|
|
12
12
|
dryRun?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Provider settings for this run only, laid over the saved ones and never
|
|
15
|
+
* written anywhere: the CLI's `--folder-id` / `--folder` (#452).
|
|
16
|
+
*/
|
|
17
|
+
settings?: Record<string, string>;
|
|
13
18
|
/**
|
|
14
19
|
* The provider, when the caller has already resolved it (the CLI does, to
|
|
15
20
|
* map a failed restore to its own exit code): no second lookup or restore.
|
|
@@ -39,7 +44,7 @@ export interface RunPublishResult {
|
|
|
39
44
|
* with a preflight error over one bad manifest field.
|
|
40
45
|
*/
|
|
41
46
|
export declare function resolvePublishFormat(info: PublishProviderInfo, providerConfig: Record<string, unknown>): PublishArtifactFormat;
|
|
42
|
-
type RequestOptions = Pick<RunPublishOptions, "projectDir" | "providerId" | "manifestPath" | "artifactPath">;
|
|
47
|
+
type RequestOptions = Pick<RunPublishOptions, "projectDir" | "providerId" | "manifestPath" | "artifactPath" | "settings">;
|
|
43
48
|
/**
|
|
44
49
|
* Resolve the {@link PublishRequest} for a BUILT-IN provider from the
|
|
45
50
|
* project's manifest — for hosts that call individual provider methods
|
|
@@ -206,12 +206,13 @@ export interface PublishDeps {
|
|
|
206
206
|
credentialAccount?: string;
|
|
207
207
|
/**
|
|
208
208
|
* The person's PRIVATE publishing values for a book (see
|
|
209
|
-
* {@link PublishSelectionsStore}): which saved account to use
|
|
210
|
-
* folder outside the book
|
|
211
|
-
*
|
|
212
|
-
*
|
|
209
|
+
* {@link PublishSelectionsStore}): which saved account to use, an output
|
|
210
|
+
* folder outside the book, and their own Drive folder when the book's is out
|
|
211
|
+
* of their reach (#452). When given, they win over the manifest's
|
|
212
|
+
* `publish.<id>` values in every request, and a provider may drop one that
|
|
213
|
+
* no longer applies. Absent (the CLI today): the manifest alone.
|
|
213
214
|
*/
|
|
214
|
-
selections?: Pick<PublishSelectionsStore, "settings">;
|
|
215
|
+
selections?: Pick<PublishSelectionsStore, "settings" | "setSettings">;
|
|
215
216
|
}
|
|
216
217
|
/**
|
|
217
218
|
* The TokenStore key for a publishing provider credential. Named accounts use a
|
|
@@ -252,6 +253,50 @@ export interface PublishProduct {
|
|
|
252
253
|
title: string;
|
|
253
254
|
url?: string;
|
|
254
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Where one person publishes among a provider's destinations, and whether they
|
|
258
|
+
* must choose one (#452, gdrive's folders). The book's destination is the
|
|
259
|
+
* team's, in the manifest; a person whose account can't reach it chooses their
|
|
260
|
+
* own, kept in their private settings for the book.
|
|
261
|
+
*/
|
|
262
|
+
export type PublishDestinationStatus =
|
|
263
|
+
/** The book has no destination yet: the first choice is the book's. */
|
|
264
|
+
{
|
|
265
|
+
state: "unset";
|
|
266
|
+
}
|
|
267
|
+
/** The book's destination, reachable with this person's account. */
|
|
268
|
+
| {
|
|
269
|
+
state: "book";
|
|
270
|
+
destination: PublishProduct;
|
|
271
|
+
}
|
|
272
|
+
/** The book names its destination by name only: each person's own of that
|
|
273
|
+
* name. `destination` is this account's most recently changed one (absent:
|
|
274
|
+
* it is created on the first publish); `several` when more share the name. */
|
|
275
|
+
| {
|
|
276
|
+
state: "by-name";
|
|
277
|
+
name: string;
|
|
278
|
+
destination?: PublishProduct;
|
|
279
|
+
several: boolean;
|
|
280
|
+
}
|
|
281
|
+
/** This person's own destination stands in for the book's. */
|
|
282
|
+
| {
|
|
283
|
+
state: "mine";
|
|
284
|
+
destination: PublishProduct;
|
|
285
|
+
book: {
|
|
286
|
+
title: string;
|
|
287
|
+
reachable: boolean;
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
/** Out of reach with this person's account: the book's destination, or their
|
|
291
|
+
* own (`mine`, its title) when they have one. They must choose another. */
|
|
292
|
+
| {
|
|
293
|
+
state: "unreachable";
|
|
294
|
+
book: {
|
|
295
|
+
title: string;
|
|
296
|
+
reachable: boolean;
|
|
297
|
+
};
|
|
298
|
+
mine?: string;
|
|
299
|
+
};
|
|
255
300
|
/** Listing metadata for {@link PublishProvider.updateListing}. */
|
|
256
301
|
export interface PublishListingMetadata {
|
|
257
302
|
title?: string;
|
|
@@ -304,6 +349,8 @@ export interface PublishProvider {
|
|
|
304
349
|
listDestinations?(req: PublishRequest): Promise<PublishProduct[]>;
|
|
305
350
|
/** Create a new destination (gdrive: a Drive folder at My Drive root). */
|
|
306
351
|
createDestination?(req: PublishRequest, name: string): Promise<PublishProduct>;
|
|
352
|
+
/** Where this person publishes now, and whether they must choose (#452). */
|
|
353
|
+
destinationStatus?(req: PublishRequest): Promise<PublishDestinationStatus>;
|
|
307
354
|
}
|
|
308
355
|
/**
|
|
309
356
|
* Resolve the credential for a provider: the CI env var wins, then the token
|
|
@@ -29,11 +29,11 @@ export interface ConvergeResult {
|
|
|
29
29
|
keptBothFiles: KeptBothFile[];
|
|
30
30
|
}
|
|
31
31
|
/** Snapshot message for the equalization commit (driver-unreachable cases). */
|
|
32
|
-
export declare const CONVERGE_PREPARE_MESSAGE = "Getting your changes ready to combine with the online
|
|
32
|
+
export declare const CONVERGE_PREPARE_MESSAGE = "Getting your changes ready to combine with the online copy's changes";
|
|
33
33
|
/** Snapshot message for the post-merge restore commit (kept-both/edits). */
|
|
34
|
-
export declare const CONVERGE_RESTORE_MESSAGE = "Kept both
|
|
34
|
+
export declare const CONVERGE_RESTORE_MESSAGE = "Kept both takes of the files that can't be combined";
|
|
35
35
|
/** The merge commit message. */
|
|
36
|
-
export declare const CONVERGE_MERGE_MESSAGE = "Combined your changes with the online
|
|
36
|
+
export declare const CONVERGE_MERGE_MESSAGE = "Combined your changes with the online copy's";
|
|
37
37
|
/**
|
|
38
38
|
* diff3 merge that NEVER reports a conflict: clean hunks merge exactly like
|
|
39
39
|
* git's default driver; clashing hunks keep BOTH versions inside standard
|
|
@@ -33,6 +33,8 @@ export interface DeleteRevisionOptions extends OnlineOptions {
|
|
|
33
33
|
* 3. rename on this computer — only a local failure is possible here, and it
|
|
34
34
|
* leaves the online copy renamed and this computer under the old name, still
|
|
35
35
|
* listed (the new name shows as online-only).
|
|
36
|
+
* A revision kept only in the online copy is renamed there (steps 1 and 2), with
|
|
37
|
+
* the tracking ref (what this computer last heard) standing in for its tip.
|
|
36
38
|
* Offline or a rejected sign-in: nothing is renamed, anywhere.
|
|
37
39
|
*/
|
|
38
40
|
export declare function renameRevision(options: RenameRevisionOptions): Promise<{
|
|
@@ -44,7 +46,9 @@ export declare function renameRevision(options: RenameRevisionOptions): Promise<
|
|
|
44
46
|
* (a copy in the online copy stays, and shows as online-only); `everywhere`
|
|
45
47
|
* removes it from the online copy first, then here — a failure online leaves
|
|
46
48
|
* everything as it was, a failure here leaves it removed online and still
|
|
47
|
-
* listed on this computer.
|
|
49
|
+
* listed on this computer. A revision kept only in the online copy has nothing
|
|
50
|
+
* here to remove: it is deleted from the online copy, as `everywhere` does, with
|
|
51
|
+
* the tracking ref (what this computer last heard) standing in for its tip.
|
|
48
52
|
*
|
|
49
53
|
* Nothing found only in the revision is deleted without a second look: when it
|
|
50
54
|
* holds versions found in no other revision (and, unless deleted everywhere, not
|
|
@@ -19,8 +19,8 @@ export declare const MSG_BUSY = "The online copy is changing very quickly right
|
|
|
19
19
|
* VERSIONS are what broke, and (backing up always has an online copy, so this
|
|
20
20
|
* always applies here) "Repair the online copy" is the way back.
|
|
21
21
|
*/
|
|
22
|
-
export declare const MSG_HISTORY_UNREADABLE = "This book's versions can't be read, so backing up can't continue. Your writing is safe \u2014 every file is still here on this computer. Use \"Repair the online copy\"
|
|
23
|
-
export declare const 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 \u2014 every file is still here on this computer. Check the online copy's address; if it is correct, use \"Repair the online copy\"
|
|
22
|
+
export declare const MSG_HISTORY_UNREADABLE = "This book's versions can't be read, so backing up can't continue. Your writing is safe \u2014 every file is still here on this computer. Use \"Repair the online copy\" in Book setup to get versions and backing up working again.";
|
|
23
|
+
export declare const 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 \u2014 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.";
|
|
24
24
|
export declare const MSG_NO_REMOTE = "This book doesn't have an online copy yet.";
|
|
25
25
|
export declare const MSG_SSH_REMOTE = "This book's online copy uses an SSH address (git@\u2026), which Gutterpress can't back up to. Switch it to the web (HTTPS) address to back up from here.";
|
|
26
26
|
export declare const MSG_NO_BRANCH = "This book isn't on a named revision, so it can't be backed up right now.";
|