gutterpress 0.10.9 → 0.10.10-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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-wcmbgh39.js} +5 -5
  7. package/dist/{build-dyat3fqd.js → build-c1w6kqpv.js} +6 -6
  8. package/dist/{cli-ypfvnhag.js → cli-2rswy18t.js} +1 -1
  9. package/dist/{cli-w692g6dx.js → cli-4mtf9ajw.js} +127 -1
  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-8pg731p5.js → cli-n0vgey6x.js} +4914 -4707
  13. package/dist/{cli-rh05byck.js → cli-scqp638v.js} +1 -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-drv36btj.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-xqx26xvy.js} +52 -8
  20. package/dist/{index-mey5vraz.js → index-1he93sjd.js} +918 -796
  21. package/dist/{index-zwcv1njy.js → index-5dgxeevv.js} +22 -26
  22. package/dist/{index-dhgzkhs7.js → index-a0e2253t.js} +1 -1
  23. package/dist/{index-dtrftayr.js → index-y01v2fe0.js} +127 -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 +71 -0
  37. package/dist/lib/validation-exec.d.ts +11 -0
  38. package/dist/{lint-enasqbqx.js → lint-ejzmx14j.js} +5 -5
  39. package/dist/{new-r9c99wsn.js → new-g3q3h4s5.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-w5z2vfhx.js} +5 -5
  47. package/dist/{preview-52918mxk.js → preview-9s22dq77.js} +6 -6
  48. package/dist/{publish-8s60vhx7.js → publish-mt22ewpk.js} +5 -5
  49. package/dist/{source-provider-2j5x2hyw.js → source-provider-51g6mt6y.js} +7 -1
  50. package/dist/{source-provider-kn41jmbs.js → source-provider-jkppq9fc.js} +7 -1
  51. package/dist/{tokens-e4xk3x4v.css → tokens-2sracx1w.css} +1 -1
  52. package/dist/{validate-awccx32r.js → validate-kkn8rmy5.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
@@ -631,14 +631,11 @@ var manifest_default3 = "./manifest-n1gh3qw5.yaml";
631
631
  // src/assets/templates/technical/chapter-01.md
632
632
  var chapter_01_default3 = "./chapter-01-6fn9pvd0.md";
633
633
 
634
- // src/assets/extension-templates/plugin/gutterpress.json
635
- var gutterpress_default = "./gutterpress-m98e2w2d.json";
636
-
637
634
  // src/assets/extension-templates/plugin/package.json
638
- var package_default = "./package-rm9d2tht.json";
635
+ var package_default = "./package-9rv69pxm.json";
639
636
 
640
637
  // src/assets/extension-templates/plugin/README.md
641
- var README_default = "./README-jntqyj1z.md";
638
+ var README_default = "./README-3zk4tgnj.md";
642
639
 
643
640
  // src/assets/extension-templates/plugin/plugin.js.tpl
644
641
  var plugin_js_default = "./plugin.js-aszz0zgh.tpl";
@@ -656,22 +653,22 @@ var fixture_default = "./fixture-2qnyqgb5.md";
656
653
  var expected_default = "./expected-ycmacem8.html";
657
654
 
658
655
  // src/assets/extension-templates/plugin/test/plugin.test.js.tpl
659
- var plugin_test_js_default = "./plugin.test.js-npnfhyca.tpl";
656
+ var plugin_test_js_default = "./plugin.test.js-k2r87k04.tpl";
660
657
 
661
- // src/assets/extension-templates/theme/gutterpress.json
662
- var gutterpress_default2 = "./gutterpress-x2ycdsb8.json";
658
+ // src/assets/extension-templates/theme/package.json
659
+ var package_default2 = "./package-aenwjew3.json";
663
660
 
664
661
  // src/assets/extension-templates/theme/README.md
665
- var README_default2 = "./README-6c7fda1s.md";
662
+ var README_default2 = "./README-7x5akcns.md";
666
663
 
667
664
  // src/assets/extension-templates/theme/components.yaml
668
- var components_default = "./components-0mmswvvm.yaml";
665
+ var components_default = "./components-dhgv2sm7.yaml";
669
666
 
670
667
  // src/assets/extension-templates/theme/snippets/callout.md
671
668
  var callout_default = "./callout-hv56m16h.md";
672
669
 
673
670
  // src/assets/extension-templates/theme/styles/tokens.css
674
- var tokens_default = "./tokens-e4xk3x4v.css";
671
+ var tokens_default = "./tokens-2sracx1w.css";
675
672
 
676
673
  // src/assets/extension-templates/theme/styles/base.css
677
674
  var base_default = "./base-ypqh9n7f.css";
@@ -691,20 +688,20 @@ var book_default = "./book-ted3tzrp.css";
691
688
  // src/assets/themes/clean-book/theme.css
692
689
  var theme_default = "./theme-j2bagrfx.css";
693
690
 
694
- // src/assets/themes/clean-book/theme.json
695
- var theme_default2 = "./theme-vktedy8q.json";
691
+ // src/assets/themes/clean-book/package.json
692
+ var package_default3 = "./package-97gm31sg.json";
696
693
 
697
694
  // src/assets/themes/zine/theme.css
698
- var theme_default3 = "./theme-h5recz6c.css";
695
+ var theme_default2 = "./theme-h5recz6c.css";
699
696
 
700
- // src/assets/themes/zine/theme.json
701
- var theme_default4 = "./theme-etj4gxck.json";
697
+ // src/assets/themes/zine/package.json
698
+ var package_default4 = "./package-gag6ykwn.json";
702
699
 
703
700
  // src/assets/themes/technical-doc/theme.css
704
- var theme_default5 = "./theme-nn6d53zy.css";
701
+ var theme_default3 = "./theme-nn6d53zy.css";
705
702
 
706
- // src/assets/themes/technical-doc/theme.json
707
- var theme_default6 = "./theme-s2rg7xe7.json";
703
+ // src/assets/themes/technical-doc/package.json
704
+ var package_default5 = "./package-nrhkwf2m.json";
708
705
 
709
706
  // src/lib/embedded-assets.ts
710
707
  var __libdir = dirname(fileURLToPath(import.meta.url));
@@ -725,7 +722,6 @@ var EMBEDDED_ASSETS = {
725
722
  "templates/zine/chapter-01.md": abs(filePath(chapter_01_default2)),
726
723
  "templates/technical/manifest.yaml": abs(filePath(manifest_default3)),
727
724
  "templates/technical/chapter-01.md": abs(filePath(chapter_01_default3)),
728
- "extension-templates/plugin/gutterpress.json": abs(filePath(gutterpress_default)),
729
725
  "extension-templates/plugin/package.json": abs(filePath(package_default)),
730
726
  "extension-templates/plugin/README.md": abs(filePath(README_default)),
731
727
  "extension-templates/plugin/plugin.js.tpl": abs(filePath(plugin_js_default)),
@@ -734,7 +730,7 @@ var EMBEDDED_ASSETS = {
734
730
  "extension-templates/plugin/test/fixture.md": abs(filePath(fixture_default)),
735
731
  "extension-templates/plugin/test/expected.html": abs(filePath(expected_default)),
736
732
  "extension-templates/plugin/test/plugin.test.js.tpl": abs(filePath(plugin_test_js_default)),
737
- "extension-templates/theme/gutterpress.json": abs(filePath(gutterpress_default2)),
733
+ "extension-templates/theme/package.json": abs(filePath(package_default2)),
738
734
  "extension-templates/theme/README.md": abs(filePath(README_default2)),
739
735
  "extension-templates/theme/components.yaml": abs(filePath(components_default)),
740
736
  "extension-templates/theme/snippets/callout.md": abs(filePath(callout_default)),
@@ -745,11 +741,11 @@ var EMBEDDED_ASSETS = {
745
741
  "extension-templates/theme/styles/page-rules.css": abs(filePath(page_rules_default)),
746
742
  "extension-templates/theme/styles/book.css": abs(filePath(book_default)),
747
743
  "themes/clean-book/theme.css": abs(filePath(theme_default)),
748
- "themes/clean-book/theme.json": abs(filePath(theme_default2)),
749
- "themes/zine/theme.css": abs(filePath(theme_default3)),
750
- "themes/zine/theme.json": abs(filePath(theme_default4)),
751
- "themes/technical-doc/theme.css": abs(filePath(theme_default5)),
752
- "themes/technical-doc/theme.json": abs(filePath(theme_default6))
744
+ "themes/clean-book/package.json": abs(filePath(package_default3)),
745
+ "themes/zine/theme.css": abs(filePath(theme_default2)),
746
+ "themes/zine/package.json": abs(filePath(package_default4)),
747
+ "themes/technical-doc/theme.css": abs(filePath(theme_default3)),
748
+ "themes/technical-doc/package.json": abs(filePath(package_default5))
753
749
  };
754
750
  var extractPromise = null;
755
751
  async function extractAssets() {
@@ -4,7 +4,7 @@ import {
4
4
  launchChromium,
5
5
  marginBoxAlign,
6
6
  marginBoxRectPt
7
- } from "./index-zwcv1njy.js";
7
+ } from "./index-5dgxeevv.js";
8
8
 
9
9
  // src/engine/compiler/build.ts
10
10
  import { readFile } from "node:fs/promises";
@@ -508,5 +508,131 @@ 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 [local, current, remotes] = await Promise.all([
518
+ git.listBranches({ fs: gitFs, dir: repoDir }),
519
+ git.currentBranch({ fs: gitFs, dir: repoDir, fullname: false }).catch(() => {
520
+ return;
521
+ }),
522
+ git.listRemotes({ fs: gitFs, dir: repoDir }).catch(() => [])
523
+ ]);
524
+ const localNames = new Set(local);
525
+ const remoteOnly = new Set;
526
+ for (const { remote } of remotes) {
527
+ const names = await git.listBranches({ fs: gitFs, dir: repoDir, remote }).catch(() => []);
528
+ for (const name of names) {
529
+ if (name === "HEAD")
530
+ continue;
531
+ if (!localNames.has(name))
532
+ remoteOnly.add(name);
533
+ }
534
+ }
535
+ return {
536
+ current: current ?? null,
537
+ branches: [...localNames, ...remoteOnly].sort(),
538
+ remoteOnly: [...remoteOnly].sort()
539
+ };
540
+ }
541
+ async function resolveCopy(repoDir, branch) {
542
+ const local = await git.resolveRef({ fs: gitFs, dir: repoDir, ref: `refs/heads/${branch}` }).catch(() => {
543
+ return;
544
+ });
545
+ if (local)
546
+ return { oid: local };
547
+ const remotes = await git.listRemotes({ fs: gitFs, dir: repoDir }).catch(() => []);
548
+ for (const { remote } of remotes) {
549
+ const oid = await git.resolveRef({ fs: gitFs, dir: repoDir, ref: `refs/remotes/${remote}/${branch}` }).catch(() => {
550
+ return;
551
+ });
552
+ if (oid)
553
+ return { oid, remote };
554
+ }
555
+ throw new Error(`Couldn't find a copy named "${branch}" — it may have been renamed or deleted.`);
556
+ }
557
+ function isCheckoutConflictError(e) {
558
+ return e?.code === "CheckoutConflictError";
559
+ }
560
+ async function diffCommitPaths(dir, fromOid, toOid, cache) {
561
+ const changed = [];
562
+ await git.walk({
563
+ fs: gitFs,
564
+ dir,
565
+ cache,
566
+ trees: [git.TREE({ ref: fromOid }), git.TREE({ ref: toOid })],
567
+ map: async (filepath, [from, to]) => {
568
+ if (filepath === ".")
569
+ return;
570
+ const [fType, tType] = await Promise.all([
571
+ from ? from.type() : Promise.resolve(undefined),
572
+ to ? to.type() : Promise.resolve(undefined)
573
+ ]);
574
+ if (fType === "tree" || tType === "tree")
575
+ return;
576
+ if (!fType && !tType)
577
+ return;
578
+ if (!fType || !tType) {
579
+ changed.push(filepath);
580
+ return;
581
+ }
582
+ const [fOid, tOid] = await Promise.all([from.oid(), to.oid()]);
583
+ if (fOid !== tOid)
584
+ changed.push(filepath);
585
+ }
586
+ });
587
+ return changed;
588
+ }
589
+ async function switchBranch(options) {
590
+ const { projectDir, branch, authorName, authorEmail } = options;
591
+ const source = await detectProjectSource(projectDir);
592
+ if (source.type !== "local-git-folder") {
593
+ throw new Error("This project has no version history yet. Enable version history first.");
594
+ }
595
+ const repoDir = gitScopeFor(source);
596
+ return withRepoLock(repoDir, async () => {
597
+ const cache = {};
598
+ try {
599
+ await snapshotWorkingTreeUnlocked({
600
+ projectDir,
601
+ repoRoot: repoDir,
602
+ message: SWITCH_BRANCH_SNAPSHOT_MESSAGE,
603
+ authorName,
604
+ authorEmail,
605
+ cache
606
+ });
607
+ } catch (e) {
608
+ if (!isNoChangesError(e))
609
+ throw e;
610
+ }
611
+ const fromOid = await git.resolveRef({ fs: gitFs, dir: repoDir, ref: "HEAD" }).catch(() => {
612
+ return;
613
+ });
614
+ const { oid: toOid, remote } = await resolveCopy(repoDir, branch);
615
+ try {
616
+ await git.checkout({
617
+ fs: gitFs,
618
+ dir: repoDir,
619
+ cache,
620
+ ref: branch,
621
+ force: false,
622
+ ...remote ? { remote } : {}
623
+ });
624
+ } catch (e) {
625
+ if (isCheckoutConflictError(e)) {
626
+ throw new Error("Couldn't switch copies — some files changed on disk right as the switch " + "started. Try again.", { cause: e });
627
+ }
628
+ throw e;
629
+ }
630
+ const changedPaths = fromOid && fromOid !== toOid ? await diffCommitPaths(repoDir, fromOid, toOid, cache) : [];
631
+ return {
632
+ current: branch,
633
+ changedFiles: changedPaths.map((p) => path2.join(repoDir, p))
634
+ };
635
+ });
636
+ }
511
637
 
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 };
638
+ 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-1he93sjd.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-y01v2fe0.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,