gutterpress 0.10.9 → 0.10.10-alpha.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.
Files changed (58) hide show
  1. package/README.md +66 -5
  2. package/dist/{README-jntqyj1z.md → README-3zk4tgnj.md} +4 -4
  3. package/dist/{README-6c7fda1s.md → README-7x5akcns.md} +5 -4
  4. package/dist/api/index.d.ts +7 -4
  5. package/dist/api/index.js +22 -12
  6. package/dist/{audit-fq7s1gsz.js → audit-ek36tf4q.js} +5 -5
  7. package/dist/{build-dyat3fqd.js → build-bxavfz3j.js} +6 -6
  8. package/dist/{cli-ypfvnhag.js → cli-12epgrva.js} +1 -1
  9. package/dist/{cli-8pg731p5.js → cli-5fqr02je.js} +4914 -4707
  10. package/dist/{cli-bzxqtvrk.js → cli-75qkn0k5.js} +1 -1
  11. package/dist/{cli-bx950tb4.js → cli-k8f9eb12.js} +22 -26
  12. package/dist/{cli-rh05byck.js → cli-tvpq2ht9.js} +1 -1
  13. package/dist/{cli-w692g6dx.js → cli-vje8g3w8.js} +88 -1
  14. package/dist/cli.js +16 -16
  15. package/dist/{components-0mmswvvm.yaml → components-dhgv2sm7.yaml} +2 -2
  16. package/dist/{doctor-yr85n080.js → doctor-tgmedr7g.js} +2 -2
  17. package/dist/{engine-d46wgg99.js → engine-pt2acjb5.js} +2 -2
  18. package/dist/{engine-19mshh07.js → engine-qtmvaqt7.js} +1 -1
  19. package/dist/{ext-jrg8c48s.js → ext-jjzmafve.js} +52 -8
  20. package/dist/{index-zwcv1njy.js → index-5dgxeevv.js} +22 -26
  21. package/dist/{index-dhgzkhs7.js → index-a0e2253t.js} +1 -1
  22. package/dist/{index-mey5vraz.js → index-d4wrww4b.js} +918 -796
  23. package/dist/{index-dtrftayr.js → index-f833dtg9.js} +88 -1
  24. package/dist/index.js +102 -15
  25. package/dist/lib/build-error.d.ts +6 -5
  26. package/dist/lib/build-preflight.d.ts +11 -1
  27. package/dist/lib/build-runner.d.ts +49 -16
  28. package/dist/lib/extension-import.d.ts +4 -4
  29. package/dist/lib/extension-manager.d.ts +3 -3
  30. package/dist/lib/extension-manifest.d.ts +91 -87
  31. package/dist/lib/extension-scaffold.d.ts +1 -1
  32. package/dist/lib/extension-search.d.ts +46 -0
  33. package/dist/lib/host-policy.d.ts +10 -12
  34. package/dist/lib/lint-runner.d.ts +16 -9
  35. package/dist/lib/npm-registry.d.ts +22 -0
  36. package/dist/lib/source-provider.d.ts +58 -0
  37. package/dist/lib/validation-exec.d.ts +11 -0
  38. package/dist/{lint-enasqbqx.js → lint-5vbq6yjb.js} +5 -5
  39. package/dist/{new-r9c99wsn.js → new-qws45t02.js} +6 -6
  40. package/dist/package-97gm31sg.json +10 -0
  41. package/dist/{package-rm9d2tht.json → package-9rv69pxm.json} +4 -1
  42. package/dist/package-aenwjew3.json +28 -0
  43. package/dist/package-gag6ykwn.json +10 -0
  44. package/dist/package-nrhkwf2m.json +10 -0
  45. package/dist/{plugin.test.js-npnfhyca.tpl → plugin.test.js-k2r87k04.tpl} +9 -8
  46. package/dist/{preflight-shk7vqbs.js → preflight-4ywt3399.js} +5 -5
  47. package/dist/{preview-52918mxk.js → preview-4c3tcsn4.js} +6 -6
  48. package/dist/{publish-8s60vhx7.js → publish-m6bme9a8.js} +5 -5
  49. package/dist/{source-provider-2j5x2hyw.js → source-provider-2cnc5hry.js} +7 -1
  50. package/dist/{source-provider-kn41jmbs.js → source-provider-y1kscr1h.js} +7 -1
  51. package/dist/{tokens-e4xk3x4v.css → tokens-2sracx1w.css} +1 -1
  52. package/dist/{validate-awccx32r.js → validate-avtb9drn.js} +5 -5
  53. package/package.json +1 -1
  54. package/dist/gutterpress-m98e2w2d.json +0 -8
  55. package/dist/gutterpress-x2ycdsb8.json +0 -17
  56. package/dist/theme-etj4gxck.json +0 -9
  57. package/dist/theme-s2rg7xe7.json +0 -9
  58. package/dist/theme-vktedy8q.json +0 -9
@@ -508,5 +508,92 @@ async function restoreVersionWithBackup(options) {
508
508
  return { restoredId: id, backupId };
509
509
  });
510
510
  }
511
+ var SWITCH_BRANCH_SNAPSHOT_MESSAGE = "Saved before switching copy";
512
+ async function listLocalBranches(dir) {
513
+ const source = await detectProjectSource(dir);
514
+ if (source.type !== "local-git-folder")
515
+ return null;
516
+ const repoDir = gitScopeFor(source);
517
+ const [branches, current] = await Promise.all([
518
+ git.listBranches({ fs: gitFs, dir: repoDir }),
519
+ git.currentBranch({ fs: gitFs, dir: repoDir, fullname: false }).catch(() => {
520
+ return;
521
+ })
522
+ ]);
523
+ return { current: current ?? null, branches };
524
+ }
525
+ function isCheckoutConflictError(e) {
526
+ return e?.code === "CheckoutConflictError";
527
+ }
528
+ async function diffCommitPaths(dir, fromOid, toOid, cache) {
529
+ const changed = [];
530
+ await git.walk({
531
+ fs: gitFs,
532
+ dir,
533
+ cache,
534
+ trees: [git.TREE({ ref: fromOid }), git.TREE({ ref: toOid })],
535
+ map: async (filepath, [from, to]) => {
536
+ if (filepath === ".")
537
+ return;
538
+ const [fType, tType] = await Promise.all([
539
+ from ? from.type() : Promise.resolve(undefined),
540
+ to ? to.type() : Promise.resolve(undefined)
541
+ ]);
542
+ if (fType === "tree" || tType === "tree")
543
+ return;
544
+ if (!fType && !tType)
545
+ return;
546
+ if (!fType || !tType) {
547
+ changed.push(filepath);
548
+ return;
549
+ }
550
+ const [fOid, tOid] = await Promise.all([from.oid(), to.oid()]);
551
+ if (fOid !== tOid)
552
+ changed.push(filepath);
553
+ }
554
+ });
555
+ return changed;
556
+ }
557
+ async function switchBranch(options) {
558
+ const { projectDir, branch, authorName, authorEmail } = options;
559
+ const source = await detectProjectSource(projectDir);
560
+ if (source.type !== "local-git-folder") {
561
+ throw new Error("This project has no version history yet. Enable version history first.");
562
+ }
563
+ const repoDir = gitScopeFor(source);
564
+ return withRepoLock(repoDir, async () => {
565
+ const cache = {};
566
+ try {
567
+ await snapshotWorkingTreeUnlocked({
568
+ projectDir,
569
+ repoRoot: repoDir,
570
+ message: SWITCH_BRANCH_SNAPSHOT_MESSAGE,
571
+ authorName,
572
+ authorEmail,
573
+ cache
574
+ });
575
+ } catch (e) {
576
+ if (!isNoChangesError(e))
577
+ throw e;
578
+ }
579
+ const fromOid = await git.resolveRef({ fs: gitFs, dir: repoDir, ref: "HEAD" }).catch(() => {
580
+ return;
581
+ });
582
+ const toOid = await git.resolveRef({ fs: gitFs, dir: repoDir, ref: branch });
583
+ try {
584
+ await git.checkout({ fs: gitFs, dir: repoDir, cache, ref: branch, force: false });
585
+ } catch (e) {
586
+ if (isCheckoutConflictError(e)) {
587
+ throw new Error("Couldn't switch copies — some files changed on disk right as the switch " + "started. Try again.", { cause: e });
588
+ }
589
+ throw e;
590
+ }
591
+ const changedPaths = fromOid && fromOid !== toOid ? await diffCommitPaths(repoDir, fromOid, toOid, cache) : [];
592
+ return {
593
+ current: branch,
594
+ changedFiles: changedPaths.map((p) => path2.join(repoDir, p))
595
+ };
596
+ });
597
+ }
511
598
 
512
- export { gitFs, resolveLogger, gitScopeFor, HISTORY_PAGE_LIMIT, withRepoLock, __repoLockQueueSizeForTests, resolveGitAuthor, listWorkdirChanges, stageChanges, hasPendingChanges, hasUncommittedChanges, snapshotWorkingTreeUnlocked, isNoChangesError, providerFor, gitDirFor, snapshotStagingMarkerPath, RESTORE_BACKUP_MESSAGE, AUTO_SNAPSHOT_MESSAGE, restoreVersionWithBackup };
599
+ export { gitFs, resolveLogger, gitScopeFor, HISTORY_PAGE_LIMIT, withRepoLock, __repoLockQueueSizeForTests, resolveGitAuthor, listWorkdirChanges, stageChanges, hasPendingChanges, hasUncommittedChanges, snapshotWorkingTreeUnlocked, isNoChangesError, providerFor, gitDirFor, snapshotStagingMarkerPath, RESTORE_BACKUP_MESSAGE, AUTO_SNAPSHOT_MESSAGE, restoreVersionWithBackup, SWITCH_BRANCH_SNAPSHOT_MESSAGE, listLocalBranches, switchBranch };
package/dist/index.js CHANGED
@@ -1,7 +1,5 @@
1
1
  import {
2
2
  AUTO_SNAPSHOT_DEFAULT_MINUTES,
3
- AUTO_SNAPSHOT_MAX_MINUTES,
4
- AUTO_SNAPSHOT_MIN_MINUTES,
5
3
  AUTO_SYNC_DEFAULT_MINUTES,
6
4
  AUTO_SYNC_MAX_MINUTES,
7
5
  AUTO_SYNC_MIN_MINUTES,
@@ -9,6 +7,7 @@ import {
9
7
  BUILT_IN_STYLE_SET_IDS,
10
8
  BUILT_IN_TEMPLATE_IDS,
11
9
  BUNDLED_EXTENSIONS,
10
+ DEFAULT_NPM_REGISTRY,
12
11
  EXTENSIONS_DIR,
13
12
  EXTENSION_KINDS,
14
13
  EXTENSION_MANIFEST_FILENAME,
@@ -18,14 +17,15 @@ import {
18
17
  GOOGLE_NOT_CONFIGURED_MESSAGE,
19
18
  GitHubAuthProvider,
20
19
  GoogleAuthProvider,
21
- LEGACY_THEME_MANIFEST_FILENAME,
22
20
  MANIFEST_FILENAMES,
21
+ MAX_SEARCH_BYTES,
23
22
  PLUGINS_DIR,
24
23
  PRESETS,
25
24
  PRESET_IDS,
26
25
  PublishSelectionsStore,
27
26
  RECOMMENDED_EXTENSIONS,
28
27
  RESERVED_PREFIX,
28
+ SEARCH_KEYWORDS,
29
29
  SNIPPETS_DIR,
30
30
  SYNC_SNAPSHOT_MESSAGE,
31
31
  TARGETS,
@@ -51,8 +51,8 @@ import {
51
51
  executeAndReport,
52
52
  executeValidation,
53
53
  extensionCarries,
54
+ extensionEntry,
54
55
  extensionStyleList,
55
- extensionStyleListWithDefault,
56
56
  extractUrlCredential,
57
57
  extractVariables,
58
58
  forgeKindForHost,
@@ -84,7 +84,9 @@ import {
84
84
  listSnippets,
85
85
  loadManifest,
86
86
  loadManifestWithPath,
87
+ loadPluginsWithCss,
87
88
  log,
89
+ npmRegistryUrl,
88
90
  openPath,
89
91
  parseExtensionSpecifier,
90
92
  parseRemoteOrigin,
@@ -115,12 +117,12 @@ import {
115
117
  resolvePublishFormat,
116
118
  resolvePublishRequest,
117
119
  revokeGoogleCredential,
120
+ rulePageContainment,
118
121
  ruleRemoteUrls,
119
122
  ruleRiskyProps,
120
123
  ruleSyntax,
121
124
  runBuild,
122
125
  runChecks,
123
- runLint,
124
126
  runPublish,
125
127
  sameExtension,
126
128
  sanitizeCloneFolderName,
@@ -128,6 +130,7 @@ import {
128
130
  saveSnippet,
129
131
  scaffoldExtension,
130
132
  scaffoldProject,
133
+ searchNpmExtensions,
131
134
  setActiveStyles,
132
135
  setExtensionEnabled,
133
136
  setManifestFields,
@@ -139,24 +142,27 @@ import {
139
142
  syncProject,
140
143
  testRemoteAccess,
141
144
  validateProjectExtensions
142
- } from "./index-mey5vraz.js";
145
+ } from "./index-d4wrww4b.js";
143
146
  import {
144
147
  BUILD_DIAGNOSTIC_CODES
145
- } from "./index-dhgzkhs7.js";
148
+ } from "./index-a0e2253t.js";
146
149
  import {
147
150
  BuildError,
148
151
  DEFAULT_PRINT_OPTS,
149
152
  readyProbeExpr
150
- } from "./index-zwcv1njy.js";
153
+ } from "./index-5dgxeevv.js";
151
154
  import {
152
155
  AUTO_SNAPSHOT_MESSAGE,
153
156
  HISTORY_PAGE_LIMIT,
154
157
  RESTORE_BACKUP_MESSAGE,
158
+ SWITCH_BRANCH_SNAPSHOT_MESSAGE,
155
159
  isNoChangesError,
160
+ listLocalBranches,
156
161
  providerFor,
157
162
  resolveLogger,
158
- restoreVersionWithBackup
159
- } from "./index-dtrftayr.js";
163
+ restoreVersionWithBackup,
164
+ switchBranch
165
+ } from "./index-f833dtg9.js";
160
166
  import {
161
167
  capabilitiesFor,
162
168
  detectProjectSource,
@@ -164,11 +170,88 @@ import {
164
170
  repoRootForSource,
165
171
  repoSubPath
166
172
  } from "./index-v5mp7a6q.js";
167
- import"./index-37x76zdn.js";
173
+ import {
174
+ __require
175
+ } from "./index-37x76zdn.js";
176
+ // src/lib/lint-runner.ts
177
+ import { resolve } from "node:path";
178
+ import { readFile } from "node:fs/promises";
179
+ async function runLint(opts = {}) {
180
+ const { glob } = await import("glob");
181
+ const { manifest, manifestDir } = await loadManifestWithPath(opts.manifest, {
182
+ explicit: opts.manifest !== undefined
183
+ });
184
+ const resolved = resolveConfig({}, manifest);
185
+ let files;
186
+ if (opts.files) {
187
+ files = await glob([opts.files], { nodir: true, ignore: ["**/*.min.css"] });
188
+ } else {
189
+ const relStyles = await resolveActiveStyles(manifestDir, resolved.styles);
190
+ const projectFiles = relStyles.map((rel) => resolve(manifestDir, rel));
191
+ let pluginStylePaths = opts.pluginStylePaths;
192
+ if (pluginStylePaths === undefined) {
193
+ ({ pluginStylePaths } = await loadPluginsWithCss(resolved.extensions, manifestDir, (ref, err) => log.warn(`Skipping plugin "${ref}" for lint — ${err.message}`)));
194
+ }
195
+ files = [...projectFiles, ...pluginStylePaths].filter((f) => !f.endsWith(".min.css"));
196
+ }
197
+ if (files.length === 0) {
198
+ log.warn("No CSS files found to lint");
199
+ return { ok: true, riskyCount: 0, containmentCount: 0, filesLinted: 0 };
200
+ }
201
+ log.info(`Linting ${files.length} CSS file(s)`);
202
+ let errorCount = 0;
203
+ let riskyCount = 0;
204
+ let containmentCount = 0;
205
+ let linted = 0;
206
+ for (const file of files) {
207
+ let css;
208
+ try {
209
+ css = await readFile(file, "utf8");
210
+ } catch (err) {
211
+ log.error(` ${file}`);
212
+ log.error(` cannot read stylesheet: ${err instanceof Error ? err.message : String(err)}`);
213
+ errorCount++;
214
+ continue;
215
+ }
216
+ linted++;
217
+ const warnings = checkCss(css, file);
218
+ const errors = warnings.filter((w) => w.severity === "error");
219
+ const risky = warnings.filter((w) => w.rule === ruleRiskyProps);
220
+ const containment = warnings.filter((w) => w.rule === rulePageContainment);
221
+ riskyCount += risky.length;
222
+ containmentCount += containment.length;
223
+ if (errors.length > 0) {
224
+ log.error(` ${file}`);
225
+ for (const w of errors) {
226
+ log.error(` ${w.line}:${w.column} ${w.message} (${w.rule})`);
227
+ }
228
+ errorCount += errors.length;
229
+ }
230
+ const nonErrorWarnings = [...risky, ...containment].sort((a, b) => a.line - b.line);
231
+ if (nonErrorWarnings.length > 0) {
232
+ log.warn(` ${file}`);
233
+ for (const w of nonErrorWarnings) {
234
+ log.warn(` ${w.line}:${w.column} ${w.message} (${w.rule})`);
235
+ }
236
+ }
237
+ }
238
+ if (errorCount > 0) {
239
+ log.error("CSS lint errors found");
240
+ return { ok: false, riskyCount, containmentCount, filesLinted: linted };
241
+ }
242
+ const totalWarnings = riskyCount + containmentCount;
243
+ if (totalWarnings > 0) {
244
+ log.warn(`${totalWarnings} print-safety warning(s): ${riskyCount} risky effect(s), ${containmentCount} page-containment`);
245
+ } else {
246
+ log.success("CSS lint passed");
247
+ }
248
+ return { ok: true, riskyCount, containmentCount, filesLinted: linted };
249
+ }
168
250
  export {
169
251
  validateProjectExtensions,
170
252
  testRemoteAccess,
171
253
  syncProject,
254
+ switchBranch,
172
255
  substituteVariables,
173
256
  startPreviewServer,
174
257
  splitOutPath,
@@ -177,6 +260,7 @@ export {
177
260
  setManifestFields,
178
261
  setExtensionEnabled,
179
262
  setActiveStyles,
263
+ searchNpmExtensions,
180
264
  scaffoldProject,
181
265
  scaffoldExtension,
182
266
  saveSnippet,
@@ -226,6 +310,7 @@ export {
226
310
  parseRemoteOrigin,
227
311
  parseExtensionSpecifier,
228
312
  openPath,
313
+ npmRegistryUrl,
229
314
  log,
230
315
  loadManifestWithPath,
231
316
  loadManifest,
@@ -236,6 +321,7 @@ export {
236
321
  listProjectStyles,
237
322
  listProjectExtensions,
238
323
  listMergedSnippets,
324
+ listLocalBranches,
239
325
  listGitHubRepositories,
240
326
  listGitHubBranches,
241
327
  listCustomTemplates,
@@ -260,8 +346,8 @@ export {
260
346
  findEnclosingRepoDir,
261
347
  extractVariables,
262
348
  extractUrlCredential,
263
- extensionStyleListWithDefault,
264
349
  extensionStyleList,
350
+ extensionEntry,
265
351
  extensionCarries,
266
352
  executeValidation,
267
353
  executeAndReport,
@@ -288,7 +374,9 @@ export {
288
374
  TARGET_IDS,
289
375
  TARGETS,
290
376
  SYNC_SNAPSHOT_MESSAGE,
377
+ SWITCH_BRANCH_SNAPSHOT_MESSAGE,
291
378
  SNIPPETS_DIR,
379
+ SEARCH_KEYWORDS,
292
380
  RESTORE_BACKUP_MESSAGE,
293
381
  RESERVED_PREFIX,
294
382
  RECOMMENDED_EXTENSIONS,
@@ -296,8 +384,8 @@ export {
296
384
  PRESET_IDS,
297
385
  PRESETS,
298
386
  PLUGINS_DIR,
387
+ MAX_SEARCH_BYTES,
299
388
  MANIFEST_FILENAMES,
300
- LEGACY_THEME_MANIFEST_FILENAME,
301
389
  HISTORY_PAGE_LIMIT,
302
390
  GoogleAuthProvider,
303
391
  GitHubAuthProvider,
@@ -309,6 +397,7 @@ export {
309
397
  EXTENSION_KINDS,
310
398
  EXTENSIONS_DIR,
311
399
  DEFAULT_PRINT_OPTS,
400
+ DEFAULT_NPM_REGISTRY,
312
401
  BuildError,
313
402
  BUNDLED_EXTENSIONS,
314
403
  BUILT_IN_TEMPLATE_IDS,
@@ -318,8 +407,6 @@ export {
318
407
  AUTO_SYNC_MIN_MINUTES,
319
408
  AUTO_SYNC_MAX_MINUTES,
320
409
  AUTO_SYNC_DEFAULT_MINUTES,
321
- AUTO_SNAPSHOT_MIN_MINUTES,
322
410
  AUTO_SNAPSHOT_MESSAGE,
323
- AUTO_SNAPSHOT_MAX_MINUTES,
324
411
  AUTO_SNAPSHOT_DEFAULT_MINUTES
325
412
  };
@@ -9,11 +9,12 @@
9
9
  * failures (validate/preflight/audit findings, a build's
10
10
  * quality-gate rejection). The invocation itself was fine;
11
11
  * the content wasn't. Standalone `gutterpress lint` uses this
12
- * code for CSS lint failures too, but the `build` pipeline's
13
- * own CSS-lint gate is a documented historical exception
14
- * that exits 2 instead (see build-runner.ts's
15
- * runQualityGates) — not a bug, just an inconsistency kept
16
- * for back-compat.
12
+ * code for CSS lint failures too. `gutterpress build`'s CSS
13
+ * print-safety check (`source.stylelint`) now runs as one
14
+ * of pre-build validation's checks (#272 — one CSS gate,
15
+ * not two) and fails the build through this same code; the
16
+ * `build` pipeline's former separate CSS-lint gate, and its
17
+ * own exit code 2, are gone.
17
18
  * 2 USAGE — the invocation itself was wrong: a bad flag, positional,
18
19
  * preset, or value. See {@link UsageError} in ./cli-args.ts.
19
20
  * 3 PIPELINE — the build/render/export pipeline failed for a reason
@@ -58,7 +58,17 @@ export declare function preflightBuildTools(format: BuildFormat, opts: {
58
58
  */
59
59
  export declare function verifyNativeChromiumMilestone(): Promise<void>;
60
60
  export interface Gates {
61
- lint: boolean;
61
+ /**
62
+ * Disable the `source.stylelint` check (CSS print-safety: remote urls,
63
+ * risky print effects, page-containment) for this run, without touching
64
+ * `preValidate` itself (#272 — one CSS gate, not two: this used to be a
65
+ * whole second gate, `runLint`, that duplicated `source.stylelint`'s own
66
+ * `checkCss` pass one phase later). `true` when `--skip-lint` was passed or
67
+ * `config.lint.enabled === false`; consulted only when `preValidate` is
68
+ * also true — with `preValidate` off, no pre-build check (CSS included)
69
+ * runs at all, `--skip-lint` or not.
70
+ */
71
+ skipStylelint: boolean;
62
72
  preValidate: boolean;
63
73
  postValidate: boolean;
64
74
  }
@@ -20,6 +20,14 @@ export interface BuildRunnerOptions {
20
20
  iccPath?: string;
21
21
  manifestPath?: string;
22
22
  stripAnnotations?: boolean;
23
+ /**
24
+ * Skip the CSS print-safety check (#272 — one CSS gate, not two). This used
25
+ * to gate a separate `runLint` pass; it now disables just the
26
+ * `source.stylelint` check inside pre-build validation (see
27
+ * {@link computeGates}'s `skipStylelint`). Has no effect when
28
+ * `skipPreValidate` is also set — pre-build validation, CSS check
29
+ * included, does not run at all in that case.
30
+ */
23
31
  skipLint?: boolean;
24
32
  skipPreValidate?: boolean;
25
33
  skipPostValidate?: boolean;
@@ -54,10 +62,12 @@ export interface BuildRunnerOptions {
54
62
  export interface BuildRunnerResult {
55
63
  outDir: string;
56
64
  /**
57
- * The published `book.html`, or `null` for a one-file delivery (`--out x.pdf`,
58
- * a desktop export) where only the PDF is delivered and everything else is
59
- * discarded with the work dir. Returning a work-dir path here would hand the
60
- * caller a filename that is already deleted by the time they see it.
65
+ * The published `book.html`, or `null` when only the PDF is delivered and
66
+ * everything else is discarded with the work dir — a one-file delivery
67
+ * (`--out x.pdf`, a desktop export), or a `pdf`/`pdfx` build into a
68
+ * `--out <dir>` (#270, #271; see {@link PublishTarget}'s `directory` case).
69
+ * Returning a work-dir path here would hand the caller a filename that is
70
+ * already deleted by the time they see it.
61
71
  */
62
72
  htmlPath: string | null;
63
73
  pdfPath: string | null;
@@ -102,7 +112,15 @@ type PublishTarget =
102
112
  kind: "project";
103
113
  dir: string;
104
114
  }
105
- /** `--out <dir>`: the user's directory. Files are added; nothing is removed. */
115
+ /**
116
+ * `--out <dir>`: the user's directory. Files are added; nothing is removed.
117
+ * An `html` build delivers its whole bundle (book.html + viewer, index.html,
118
+ * assets, fingerprint) here. A `pdf`/`pdfx` build delivers ONLY its own PDF
119
+ * — never book.html/index.html/assets — so building both formats into one
120
+ * shared directory (the documented `--out ./_site` sequence) cannot let the
121
+ * pdf build's staged, viewer-less, link-stripped `book.html` clobber the
122
+ * html build's published one (#270, #271).
123
+ */
106
124
  | {
107
125
  kind: "directory";
108
126
  dir: string;
@@ -161,10 +179,10 @@ export interface BuildContext {
161
179
  prevalidatedLayoutWarningKeys: Set<string>;
162
180
  /**
163
181
  * The build's ONE plugin load (#262). `null` until {@link loadBuildPlugins}
164
- * runs; every stage that needs plugins (the lint gate and preValidate gate
165
- * in {@link runQualityGates}, and {@link renderBook}) calls that function
166
- * and gets the SAME resolved `{ plugins, pluginCss, pluginStylePaths }`
167
- * back, whichever of them runs first.
182
+ * runs; every stage that needs plugins (the preValidate gate in
183
+ * {@link runQualityGates}, and {@link renderBook}) calls that function and
184
+ * gets the SAME resolved `{ plugins, pluginCss, pluginStylePaths }` back,
185
+ * whichever of them runs first.
168
186
  *
169
187
  * Before this, `runQualityGates`'s lint gate and `renderBook` each called
170
188
  * `loadPluginsWithCss` independently for the identical manifest. For an
@@ -192,13 +210,13 @@ export interface BuildContext {
192
210
  export declare function resolveBuildContext(opts: BuildRunnerOptions): Promise<BuildContext>;
193
211
  /**
194
212
  * Load every plugin the manifest configures, exactly once for the whole
195
- * build (#262). Memoized on `ctx.plugins`: the lint gate, the preValidate
196
- * gate (both in {@link runQualityGates}), and {@link renderBook} each call
197
- * this instead of `loadPluginsWithCss` directly, and only the first caller
198
- * does real work — the rest get the cached result back, however the gates
199
- * are configured and whichever stage happens to run first (a test calling
200
- * {@link renderBook} directly, without going through `runQualityGates`, gets
201
- * a fresh load here exactly as it would have before this existed).
213
+ * build (#262). Memoized on `ctx.plugins`: the preValidate gate (in
214
+ * {@link runQualityGates}) and {@link renderBook} each call this instead of
215
+ * `loadPluginsWithCss` directly, and only the first caller does real work —
216
+ * the other gets the cached result back, whichever stage happens to run
217
+ * first (a test calling {@link renderBook} directly, without going through
218
+ * `runQualityGates`, gets a fresh load here exactly as it would have before
219
+ * this existed).
202
220
  *
203
221
  * Fail-fast (no `onError`), matching `renderBook`'s pre-existing behavior:
204
222
  * a build/export must never silently omit author-configured formatting (see
@@ -241,6 +259,21 @@ export declare function renderBook(ctx: BuildContext): Promise<string>;
241
259
  * copy shipped in the binary. Throws BuildError(exitCode 2) if nothing resolves.
242
260
  */
243
261
  export declare function resolveIccProfile(icc: string, manifestDir: string, explicitIccPath: string | undefined): Promise<string>;
262
+ /**
263
+ * Publish a completed work dir into a `--out <dir>` target: the pure copy
264
+ * decision behind {@link PublishTarget}'s `directory` case, pulled out of
265
+ * {@link publishBuild} so it is unit-testable without a full
266
+ * {@link BuildContext}.
267
+ *
268
+ * `html` delivers the whole bundle (book.html + viewer, index.html, assets,
269
+ * fingerprint) — files are added, never removed. `pdf`/`pdfx` delivers ONLY
270
+ * its own PDF: its staged `book.html` has no viewer script
271
+ * (`HtmlOutput.finish` never runs for these formats) and, since #263, no
272
+ * relative hrefs either, so copying the whole work dir would silently
273
+ * clobber an html build's published book.html/index.html/assets in the same
274
+ * `--out` directory (#270, #271).
275
+ */
276
+ export declare function publishToDirectory(workDir: string, targetDir: string, format: BuildFormat, artifactName: string | null): Promise<void>;
244
277
  /**
245
278
  * Orchestrate a build: resolve the context, mkdir the output, preflight tools
246
279
  * (non-html), pre-warm the browser when this build will render in Chromium,
@@ -40,7 +40,7 @@ export declare function classifyThemeCssFindings(findings: PrintSafeWarning[]):
40
40
  };
41
41
  /**
42
42
  * From a theme folder's relative file paths, return the ones that are neither
43
- * `theme.css`/`theme.json`/`gutterpress.json`, a recognized bundled asset
43
+ * `theme.css`/`package.json`, a recognized bundled asset
44
44
  * (font/image/css), nor one the metadata itself declared (#241 —
45
45
  * `declaredExtras`: the extension's `markdown`/`components`/`snippets`
46
46
  * entries, when present, passed as relative paths; a `snippets` folder
@@ -59,8 +59,8 @@ export declare function unexpectedThemeFiles(relPaths: string[], declaredExtras?
59
59
  export declare function importExtensionFromZip(projectDir: string, archive: Uint8Array): Promise<ExtensionImportResult>;
60
60
  /**
61
61
  * Import a theme from a bare `.css` file by wrapping it into a one-file theme
62
- * folder (`theme.css` + a synthesized `theme.json` naming it). REJECTS a CSS
63
- * that fails to parse.
62
+ * folder (`theme.css` + a synthesized `package.json` naming it and declaring
63
+ * it). REJECTS a CSS that fails to parse.
64
64
  */
65
65
  export declare function importExtensionFromCssText(projectDir: string, css: string, name: string): Promise<ExtensionImportResult>;
66
66
  /**
@@ -73,7 +73,7 @@ export declare function importExtensionFromFile(projectDir: string, filePath: st
73
73
  * Import a look from a URL using the global `fetch` (bundle-safe — no node
74
74
  * http client). Two shapes: a `.css` URL is the look's `theme.css`, with
75
75
  * metadata synthesised from the URL; a base URL (no `.css`) fetches
76
- * `<base>/theme.json` (optional) and `<base>/theme.css` (required). Bundled
76
+ * `<base>/package.json` (optional) and `<base>/theme.css` (required). Bundled
77
77
  * fonts are not followed — authors wanting bundled assets use a folder or a
78
78
  * `.zip`. The fetched files go through the same validation as a `.zip`.
79
79
  */
@@ -29,10 +29,10 @@ export interface ProjectExtensionEntry {
29
29
  /** Preview image path relative to the extension folder, when declared. */
30
30
  preview?: string | null;
31
31
  /** The sheet carrying the `:root` token surface, relative to the folder,
32
- * when declared (`gutterpress.json`'s `tokensFile`). */
32
+ * when declared (package.json's `gutterpress.tokensFile`). */
33
33
  tokensFile?: string;
34
- /** Declared stylesheets relative to the folder, in cascade order (`theme.css`
35
- * by default for a metadata-less look). Absent when there is no folder. */
34
+ /** Declared stylesheets relative to the folder, in cascade order
35
+ * (`gutterpress.styles`). Absent when the folder declares none. */
36
36
  styles?: string[];
37
37
  /** What the extension declares — the desktop shows styles-carrying entries
38
38
  * in its Look view and markdown-carrying ones in Features; one list. */