gutterpress 0.11.22 → 0.11.23-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +23 -24
  2. package/dist/{README-3nj919rm.md → README-8ef336pp.md} +1 -1
  3. package/dist/api/index.d.ts +4 -4
  4. package/dist/api/index.js +16 -22
  5. package/dist/{build-53bw14h7.js → build-encgnv0c.js} +9 -10
  6. package/dist/{chapter-01-rkpyvjna.md → chapter-01-gsbjt584.md} +1 -1
  7. package/dist/chapter-01-s71xrf5q.md +69 -0
  8. package/dist/{chapter-01-7jwqg1ct.md → chapter-01-wgjfz4w6.md} +1 -1
  9. package/dist/checks/source/character-table.d.ts +12 -6
  10. package/dist/{cli-habkc5ng.js → cli-a35w7wzg.js} +5 -4
  11. package/dist/{cli-kx4hbnx2.js → cli-aqh6brew.js} +1 -1
  12. package/dist/{cli-1hxxwbaq.js → cli-evg8sbht.js} +1 -1
  13. package/dist/{cli-cjftezxr.js → cli-kh8v4vvw.js} +2 -1
  14. package/dist/{cli-ntwqmdnw.js → cli-pwwfxdgb.js} +846 -13
  15. package/dist/{cli-8kg0wjdt.js → cli-pz4bn98t.js} +185 -9
  16. package/dist/{cli-zj5xaepv.js → cli-sdmv4bba.js} +57 -117
  17. package/dist/{cli-ke86zsz6.js → cli-xq6djqbj.js} +51 -54
  18. package/dist/{index-r7jcsk6h.js → cli-y28j019d.js} +19 -10
  19. package/dist/cli.js +12 -12
  20. package/dist/{doctor-xka1frnx.js → doctor-5gtpgpd1.js} +3 -3
  21. package/dist/{engine-p93f06gj.js → engine-mc5br8g8.js} +1 -2
  22. package/dist/{engine-0ndvy8vj.js → engine-tn13gk0b.js} +3 -3
  23. package/dist/{ext-dwfqyy1j.js → ext-wn2a5afq.js} +18 -33
  24. package/dist/{extension-restore-ct1wk344.js → extension-restore-g8jnmmzq.js} +5 -6
  25. package/dist/{extension-restore-7vp3vh77.js → extension-restore-s3x1618g.js} +4 -4
  26. package/dist/{index-apc8w9w8.js → index-0skhjx3e.js} +180 -1
  27. package/dist/{index-4v7e621e.js → index-2bjcghze.js} +847 -13
  28. package/dist/index-46ycxe6r.js +18 -0
  29. package/dist/{index-53nrm5j1.js → index-4x6y8krm.js} +2 -4
  30. package/dist/{cli-h3phdstf.js → index-5x42amh8.js} +19 -10
  31. package/dist/{index-93pqkvty.js → index-h6yk5tjx.js} +5 -4
  32. package/dist/{index-kh7fayjv.js → index-j5z86r4a.js} +57 -54
  33. package/dist/{index-6cmn2wpd.js → index-x3ema2w3.js} +69 -128
  34. package/dist/index.js +17 -23
  35. package/dist/lib/build-runner.d.ts +1 -1
  36. package/dist/lib/extension-manager.d.ts +16 -43
  37. package/dist/lib/extension-scaffold.d.ts +3 -3
  38. package/dist/lib/extension-specifier.d.ts +23 -6
  39. package/dist/lib/markdown/assemble.d.ts +7 -1
  40. package/dist/lib/markdown/gutterpress-css.d.ts +1 -1
  41. package/dist/lib/markdown/index.d.ts +4 -0
  42. package/dist/lib/markdown/math.d.ts +43 -0
  43. package/dist/lib/markdown/renderer.d.ts +11 -8
  44. package/dist/lib/markdown/table.d.ts +46 -0
  45. package/dist/lib/markdown/typography.d.ts +12 -0
  46. package/dist/lib/npm-plugin-installer.d.ts +1 -1
  47. package/dist/lib/plugin-vendor.d.ts +11 -3
  48. package/dist/lib/presets.d.ts +1 -1
  49. package/dist/lib/project-gitignore.d.ts +2 -2
  50. package/dist/lib/project-scaffold.d.ts +6 -8
  51. package/dist/lib/project-templates.d.ts +6 -1
  52. package/dist/lib/snippets.d.ts +2 -1
  53. package/dist/lib/style-resolver.d.ts +1 -2
  54. package/dist/manifest-eagfhba6.yaml +19 -0
  55. package/dist/{manifest.schema-399bx3vc.json → manifest.schema-t7nx2stg.json} +12 -5
  56. package/dist/{new-1pawj2qf.js → new-mhs46pw3.js} +22 -21
  57. package/dist/{plugins-fr4fnzhb.js → plugins-1sxe0dkz.js} +4 -4
  58. package/dist/{plugins-g4dgw7mn.js → plugins-2cek4mz1.js} +2 -2
  59. package/dist/{preflight-ept9q57t.js → preflight-3k2b1x8e.js} +9 -10
  60. package/dist/preview/file-watcher.d.ts +1 -0
  61. package/dist/{preview-fxpnjv7k.js → preview-6p9v6rht.js} +9 -10
  62. package/dist/{publish-zhqqsgmr.js → publish-qgsngy8s.js} +9 -10
  63. package/dist/render.js +470 -11
  64. package/dist/schema/manifest.types.d.ts +8 -0
  65. package/dist/{source-provider-qbfknhyy.js → source-provider-98k5deda.js} +2 -2
  66. package/dist/{source-provider-2r74a7wp.js → source-provider-knmcam50.js} +2 -2
  67. package/dist/{validate-t68gvckq.js → validate-0v7xw6wa.js} +9 -10
  68. package/package.json +2 -1
  69. package/dist/cli-1ngqzg8s.js +0 -196
  70. package/dist/index-vay3ff1f.js +0 -213
  71. package/dist/package-97gm31sg.json +0 -10
  72. package/dist/package-gag6ykwn.json +0 -10
  73. package/dist/package-nrhkwf2m.json +0 -10
  74. package/dist/theme-h5recz6c.css +0 -61
  75. package/dist/theme-j2bagrfx.css +0 -59
  76. package/dist/theme-nn6d53zy.css +0 -71
@@ -1,22 +1,26 @@
1
1
  import {
2
+ BUNDLED_EXTENSIONS,
2
3
  EXTENSION_MANIFEST_FILENAME,
3
4
  TWO_VERSIONS_OPEN,
5
+ bundledExtension,
4
6
  extensionCarries,
5
7
  extensionStyleList,
8
+ isPathSpecifier,
6
9
  loadPlugin,
7
10
  log,
11
+ parseExtensionSpecifier,
12
+ pinnedNpmSpecifier,
8
13
  prettify,
9
14
  readExtensionMeta,
10
15
  resolveExtension,
11
16
  slugify
12
- } from "./index-4v7e621e.js";
17
+ } from "./index-2bjcghze.js";
13
18
  import {
14
19
  BuildError,
15
- EXIT_CODES,
16
- getAssetsDir
17
- } from "./index-vay3ff1f.js";
20
+ EXIT_CODES
21
+ } from "./index-46ycxe6r.js";
18
22
  import {
19
- PLUGINS_DIR,
23
+ EXTENSIONS_DIR,
20
24
  VENDORED_NPM_DIR,
21
25
  assertWindowsSafeRelativePath,
22
26
  isExactNpmVersion,
@@ -25,8 +29,9 @@ import {
25
29
  resolveVendoredPluginInstallRoot,
26
30
  vendoredNpmPluginPackageDir,
27
31
  vendoredNpmPluginRoot,
32
+ vendoredNpmPluginRoots,
28
33
  windowsPathKey
29
- } from "./index-r7jcsk6h.js";
34
+ } from "./index-5x42amh8.js";
30
35
  import {
31
36
  __require
32
37
  } from "./index-37x76zdn.js";
@@ -68,7 +73,7 @@ async function withFetchTimeout(options, run) {
68
73
  }
69
74
 
70
75
  // src/lib/extension-manager.ts
71
- import { cp, mkdir as mkdir3, readdir, readFile as readFile5, rm as rm3, stat } from "node:fs/promises";
76
+ import { readdir, readFile as readFile5, rm as rm3, stat } from "node:fs/promises";
72
77
  import { existsSync as existsSync3 } from "node:fs";
73
78
  import path4 from "node:path";
74
79
  import { isSeq as isSeq2, isMap, isScalar, YAMLMap, Scalar } from "yaml";
@@ -86,43 +91,6 @@ import { existsSync, statSync } from "node:fs";
86
91
  import { resolve, dirname } from "node:path";
87
92
  import { parse as parseYaml, YAMLParseError } from "yaml";
88
93
 
89
- // src/lib/extension-specifier.ts
90
- var BUNDLED_EXTENSIONS = [
91
- "markdown-it-mark",
92
- "markdown-it-sub",
93
- "markdown-it-sup",
94
- "markdown-it-abbr",
95
- "gutterpress-gfm-alerts"
96
- ];
97
- function isBundledExtension(name) {
98
- return BUNDLED_EXTENSIONS.includes(name);
99
- }
100
- function isPathSpecifier(use) {
101
- return use.startsWith("./") || use.startsWith("../") || use.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(use);
102
- }
103
- function parseExtensionSpecifier(use) {
104
- const spec = use.trim();
105
- if (!spec) {
106
- throw new Error("An extension specifier is required — an npm package name, or a ./ path.");
107
- }
108
- if (isPathSpecifier(spec))
109
- return { kind: "path", path: spec };
110
- if (/\.(m?js|cjs)$/i.test(spec) || !spec.startsWith("@") && spec.includes("/")) {
111
- throw new Error(`"${spec}" looks like a path — write it as "./${spec}". A specifier that does not ` + "start with ./, ../ or / is an npm package name.");
112
- }
113
- const parsed = parseNpmPluginSpec(spec);
114
- if (isBundledExtension(parsed.name)) {
115
- if (parsed.selector !== undefined) {
116
- throw new Error(`"${parsed.name}" is bundled with Gutterpress and always resolves to the bundled copy — ` + `drop "@${parsed.selector}".`);
117
- }
118
- return { kind: "bundled", name: parsed.name };
119
- }
120
- return parsed.selector === undefined ? { kind: "npm", name: parsed.name } : { kind: "npm", name: parsed.name, version: parsed.selector };
121
- }
122
- function pinnedNpmSpecifier(name, version) {
123
- return `${name}@${version}`;
124
- }
125
-
126
94
  // src/constants.ts
127
95
  var NETWORK = {
128
96
  DEFAULT_PORT: 3579
@@ -603,6 +571,15 @@ function mergeShape(cli, manifest, preset) {
603
571
  }
604
572
  return out;
605
573
  }
574
+ var LANGUAGE_TAG = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$/;
575
+ function resolveLanguage(value) {
576
+ if (value === undefined)
577
+ return "en";
578
+ if (typeof value !== "string" || !LANGUAGE_TAG.test(value)) {
579
+ throw new UsageError(`Manifest field \`language\` must be a language tag such as "en", "de" or "pt-BR", got ${JSON.stringify(value)}.`);
580
+ }
581
+ return value;
582
+ }
606
583
  function resolveConfig(cliOverrides, manifest) {
607
584
  const presetName = cliOverrides.preset ?? manifest.preset;
608
585
  return resolveWithPreset(cliOverrides, manifest, resolvePreset(presetName));
@@ -690,6 +667,7 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
690
667
  return {
691
668
  title: c.title ?? m.title ?? "Document",
692
669
  authors: c.authors ?? m.authors ?? [],
670
+ language: resolveLanguage(c.language ?? m.language),
693
671
  preset: preset.id,
694
672
  spec: preset.spec,
695
673
  styles: c.styles ?? m.styles,
@@ -1313,13 +1291,13 @@ async function removeEmptyDirs(dirs) {
1313
1291
  }
1314
1292
  }
1315
1293
  async function prepareVendorParent(projectDir, name) {
1316
- const plugins = path2.join(projectDir, PLUGINS_DIR);
1317
- const npm = path2.join(plugins, VENDORED_NPM_DIR);
1294
+ const extensions = path2.join(projectDir, EXTENSIONS_DIR);
1295
+ const npm = path2.join(extensions, VENDORED_NPM_DIR);
1318
1296
  const packageParent = path2.dirname(vendoredNpmPluginRoot(projectDir, name, "0.0.0"));
1319
1297
  await mkdir2(projectDir, { recursive: true });
1320
1298
  const createdDirs = [];
1321
1299
  try {
1322
- for (const dir of [plugins, npm, packageParent]) {
1300
+ for (const dir of [extensions, npm, packageParent]) {
1323
1301
  if (await ensureDirectory(dir))
1324
1302
  createdDirs.push(dir);
1325
1303
  }
@@ -1438,7 +1416,7 @@ function ignoreStatus(gitignore, entry) {
1438
1416
  return "ignored";
1439
1417
  return dir.unignored || file.unignored ? "negated" : "open";
1440
1418
  }
1441
- var PROJECT_GITIGNORE_ENTRIES = ["dist/", "plugins/npm/"];
1419
+ var PROJECT_GITIGNORE_ENTRIES = ["dist/", "extensions/npm/"];
1442
1420
  async function ensureProjectGitignore(projectDir) {
1443
1421
  const gitignorePath = path3.join(projectDir, ".gitignore");
1444
1422
  let existing = null;
@@ -1466,35 +1444,6 @@ async function ensureProjectGitignore(projectDir) {
1466
1444
  }
1467
1445
 
1468
1446
  // src/lib/extension-manager.ts
1469
- var EXTENSIONS_DIR = "extensions";
1470
- var RECOMMENDED_EXTENSIONS = [
1471
- {
1472
- use: "markdown-it-mark",
1473
- label: "Highlight",
1474
- description: "Highlight text by wrapping it in `==double equals==`."
1475
- },
1476
- {
1477
- use: "markdown-it-sub",
1478
- label: "Subscript",
1479
- description: "Lower text below the line, like the 2 in `H~2~O`."
1480
- },
1481
- {
1482
- use: "markdown-it-sup",
1483
- label: "Superscript",
1484
- description: "Raise text above the line, like the th in `29^th^`."
1485
- },
1486
- {
1487
- use: "markdown-it-abbr",
1488
- label: "Abbreviations",
1489
- description: "Explain an abbreviation when readers hover over it. Define it once, like `*[GPS]: Global Positioning System`."
1490
- },
1491
- {
1492
- use: "gutterpress-gfm-alerts",
1493
- label: "Callouts",
1494
- description: "Turn a quote into a boxed note: start it with `> [!NOTE]` (or `TIP`, `IMPORTANT`, `WARNING`, `CAUTION`)."
1495
- }
1496
- ];
1497
- var BUILT_IN_STYLE_SET_IDS = ["clean-book", "zine", "technical-doc"];
1498
1447
  var mutationQueues = new Map;
1499
1448
  function projectKey(projectDir) {
1500
1449
  const resolved = path4.resolve(projectDir);
@@ -1610,15 +1559,14 @@ async function describeEntry(projectDir, raw) {
1610
1559
  return withWarnings({ ...common, kind: "npm", name: raw.use, label: raw.use, carries: NO_CARRIES });
1611
1560
  }
1612
1561
  if (parsed.kind === "bundled") {
1613
- const bundledName = parsed.name;
1614
- const rec = RECOMMENDED_EXTENSIONS.find((r) => r.use === bundledName);
1562
+ const bundled = bundledExtension(parsed.name);
1615
1563
  return withWarnings({
1616
1564
  ...common,
1617
1565
  kind: "bundled",
1618
- name: bundledName,
1619
- label: rec?.label ?? bundledName,
1620
- ...rec ? { description: rec.description } : {},
1621
- carries: { ...NO_CARRIES, markdown: true }
1566
+ name: parsed.name,
1567
+ label: bundled.label,
1568
+ description: bundled.description,
1569
+ carries: { ...NO_CARRIES, ...bundled.css !== undefined ? { styles: true } : { markdown: true } }
1622
1570
  });
1623
1571
  }
1624
1572
  if (parsed.kind === "path") {
@@ -1763,9 +1711,10 @@ async function removeExtension(projectDir, use) {
1763
1711
  return;
1764
1712
  }
1765
1713
  if (parsed.kind === "npm" && parsed.version) {
1766
- const root = vendoredNpmPluginRoot(projectDir, parsed.name, parsed.version);
1767
- await rm3(root, { recursive: true, force: true });
1768
- await removeEmptyDirs([path4.dirname(root)]);
1714
+ for (const root of vendoredNpmPluginRoots(projectDir, parsed.name, parsed.version)) {
1715
+ await rm3(root, { recursive: true, force: true });
1716
+ await removeEmptyDirs([path4.dirname(root)]);
1717
+ }
1769
1718
  }
1770
1719
  });
1771
1720
  }
@@ -1883,8 +1832,8 @@ async function vendorNpmExtension(projectDir, input, exportName, options, commit
1883
1832
  });
1884
1833
  try {
1885
1834
  const { negated } = await ensureProjectGitignore(projectDir);
1886
- if (negated.includes("plugins/npm/")) {
1887
- warnings.push("Your .gitignore re-includes plugins/npm/, so downloaded extensions will be committed with " + "your book. They shouldn't be: the manifest pins each version and a build downloads a " + "missing copy again.");
1835
+ if (negated.includes("extensions/npm/")) {
1836
+ warnings.push("Your .gitignore re-includes extensions/npm/, so downloaded extensions will be committed with " + "your book. They shouldn't be: the manifest pins each version and a build downloads a " + "missing copy again.");
1888
1837
  }
1889
1838
  } catch (error) {
1890
1839
  warnings.push(`The extension was installed, but .gitignore could not be updated to keep it out of version history: ` + `${error instanceof Error ? error.message : String(error)}`);
@@ -1910,24 +1859,32 @@ async function pinnedVersionsOf(projectDir, name) {
1910
1859
  return versions;
1911
1860
  }
1912
1861
  async function pruneStaleVersions(projectDir, name, keep) {
1913
- const parent = path4.dirname(vendoredNpmPluginRoot(projectDir, name, "0.0.0"));
1862
+ const parents = vendoredNpmPluginRoots(projectDir, name, "0.0.0").map((root) => path4.dirname(root));
1914
1863
  try {
1915
- for (const entry of await readdir(parent, { withFileTypes: true })) {
1916
- if (!entry.isDirectory())
1917
- continue;
1918
- let version;
1864
+ for (const parent of parents) {
1865
+ let entries;
1919
1866
  try {
1920
- version = decodeURIComponent(entry.name);
1921
- } catch {
1922
- continue;
1867
+ entries = await readdir(parent, { withFileTypes: true });
1868
+ } catch (error) {
1869
+ if (error.code === "ENOENT")
1870
+ continue;
1871
+ throw error;
1872
+ }
1873
+ for (const entry of entries) {
1874
+ if (!entry.isDirectory())
1875
+ continue;
1876
+ let version;
1877
+ try {
1878
+ version = decodeURIComponent(entry.name);
1879
+ } catch {
1880
+ continue;
1881
+ }
1882
+ if (!isExactNpmVersion(version) || keep.includes(version))
1883
+ continue;
1884
+ await rm3(path4.join(parent, entry.name), { recursive: true, force: true });
1923
1885
  }
1924
- if (!isExactNpmVersion(version) || keep.includes(version))
1925
- continue;
1926
- await rm3(path4.join(parent, entry.name), { recursive: true, force: true });
1927
1886
  }
1928
1887
  } catch (error) {
1929
- if (error.code === "ENOENT")
1930
- return [];
1931
1888
  return [
1932
1889
  `The extension was installed, but a previous version's downloaded copy could not be removed: ` + `${error instanceof Error ? error.message : String(error)}`
1933
1890
  ];
@@ -2013,18 +1970,13 @@ async function updateExtensions(projectDir, options = {}) {
2013
1970
  }
2014
1971
  return updated;
2015
1972
  }
2016
- async function listBuiltInStyleSets() {
2017
- const assets = await getAssetsDir();
2018
- const out = [];
2019
- for (const id of BUILT_IN_STYLE_SET_IDS) {
2020
- const meta = await readExtensionMeta(path4.join(assets, "themes", id));
2021
- out.push({
2022
- id,
2023
- name: prettify(meta.name?.trim() || id),
2024
- description: meta.description?.trim() || ""
2025
- });
2026
- }
2027
- return out;
1973
+ function listBundledExtensions() {
1974
+ return BUNDLED_EXTENSIONS.map((b) => ({
1975
+ use: b.name,
1976
+ label: b.label,
1977
+ description: b.description,
1978
+ look: b.css !== undefined
1979
+ }));
2028
1980
  }
2029
1981
  async function uniqueExtensionId(projectDir, base) {
2030
1982
  const slug = slugify(base) || "extension";
@@ -2034,20 +1986,6 @@ async function uniqueExtensionId(projectDir, base) {
2034
1986
  }
2035
1987
  return candidate;
2036
1988
  }
2037
- async function addBuiltInStyleSet(projectDir, id) {
2038
- if (!BUILT_IN_STYLE_SET_IDS.includes(id)) {
2039
- throw new Error(`Unknown built-in look "${id}" (one of: ${BUILT_IN_STYLE_SET_IDS.join(", ")}).`);
2040
- }
2041
- return withMutationLock(projectDir, async () => {
2042
- const dest = path4.join(projectDir, EXTENSIONS_DIR, id);
2043
- if (!existsSync3(dest)) {
2044
- const src = path4.join(await getAssetsDir(), "themes", id);
2045
- await mkdir3(path4.dirname(dest), { recursive: true });
2046
- await cp(src, dest, { recursive: true });
2047
- }
2048
- return addExtensionUnlocked(projectDir, `./${EXTENSIONS_DIR}/${id}`, {});
2049
- });
2050
- }
2051
1989
  async function validateProjectExtensions(projectDir) {
2052
1990
  const out = [];
2053
1991
  for (const entry of await listProjectExtensions(projectDir)) {
@@ -2074,6 +2012,9 @@ async function readExtensionCss(projectDir, use) {
2074
2012
  const entry = await describeExtension(projectDir, use);
2075
2013
  if (!entry)
2076
2014
  throw new Error(`Extension "${use}" is not in the manifest.`);
2015
+ const bundledCss = entry.kind === "bundled" ? bundledExtension(entry.name)?.css : undefined;
2016
+ if (bundledCss !== undefined)
2017
+ return bundledCss;
2077
2018
  if (!entry.dir)
2078
2019
  throw new Error(`Extension "${use}" has no stylesheets to preview.`);
2079
2020
  const meta = await readExtensionMeta(entry.dir);
@@ -2205,4 +2146,4 @@ async function restoreForCommand(dir, options) {
2205
2146
  log.warn(problem);
2206
2147
  }
2207
2148
 
2208
- export { BUNDLED_EXTENSIONS, isBundledExtension, isPathSpecifier, parseExtensionSpecifier, DEBOUNCE, UsageError, resolvePort, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, resolveTargets, applyDefaultPdfStrictChecks, MANIFEST_FILENAMES, hasProjectManifest, loadManifest, loadManifestWithPath, resolveConfig, resolveConfigForTarget, FriendlyHttpError, withFetchTimeout, loadManifestDoc, writeManifestDoc, ensureSeq, scalarString, DEFAULT_NPM_REGISTRY, npmRegistryUrl, ensureProjectGitignore, EXTENSIONS_DIR, RECOMMENDED_EXTENSIONS, BUILT_IN_STYLE_SET_IDS, sameExtension, listProjectExtensions, describeExtension, extensionConfigFor, setExtensionEnabled, reorderExtensions, removeExtension, addExtension, listNpmVersions, checkExtensionUpdates, updateExtensions, listBuiltInStyleSets, uniqueExtensionId, addBuiltInStyleSet, validateProjectExtensions, readExtensionCss, restorePinnedExtensions, restoreFailureMessage, restoreOfflineMessage, restoreForCommand };
2149
+ export { DEBOUNCE, UsageError, resolvePort, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, resolveTargets, applyDefaultPdfStrictChecks, MANIFEST_FILENAMES, hasProjectManifest, loadManifest, loadManifestWithPath, resolveConfig, resolveConfigForTarget, FriendlyHttpError, withFetchTimeout, loadManifestDoc, writeManifestDoc, ensureSeq, scalarString, DEFAULT_NPM_REGISTRY, npmRegistryUrl, ensureProjectGitignore, sameExtension, listProjectExtensions, describeExtension, extensionConfigFor, setExtensionEnabled, reorderExtensions, removeExtension, addExtension, listNpmVersions, checkExtensionUpdates, updateExtensions, listBundledExtensions, uniqueExtensionId, validateProjectExtensions, readExtensionCss, restorePinnedExtensions, restoreFailureMessage, restoreOfflineMessage, restoreForCommand };
package/dist/index.js CHANGED
@@ -116,33 +116,25 @@ import {
116
116
  substituteVariables,
117
117
  syncProject,
118
118
  testRemoteAccess
119
- } from "./index-kh7fayjv.js";
119
+ } from "./index-j5z86r4a.js";
120
120
  import {
121
- BUILT_IN_STYLE_SET_IDS,
122
- BUNDLED_EXTENSIONS,
123
121
  DEFAULT_NPM_REGISTRY,
124
- EXTENSIONS_DIR,
125
122
  MANIFEST_FILENAMES,
126
123
  PRESETS,
127
124
  PRESET_IDS,
128
- RECOMMENDED_EXTENSIONS,
129
125
  TARGETS,
130
126
  TARGET_IDS,
131
- addBuiltInStyleSet,
132
127
  addExtension,
133
128
  checkExtensionUpdates,
134
129
  describeExtension,
135
130
  ensureProjectGitignore,
136
131
  hasProjectManifest,
137
- isBundledExtension,
138
- isPathSpecifier,
139
- listBuiltInStyleSets,
132
+ listBundledExtensions,
140
133
  listNpmVersions,
141
134
  listProjectExtensions,
142
135
  loadManifest,
143
136
  loadManifestWithPath,
144
137
  npmRegistryUrl,
145
- parseExtensionSpecifier,
146
138
  publishTargetFor,
147
139
  readExtensionCss,
148
140
  removeExtension,
@@ -154,26 +146,31 @@ import {
154
146
  setExtensionEnabled,
155
147
  updateExtensions,
156
148
  validateProjectExtensions
157
- } from "./index-6cmn2wpd.js";
149
+ } from "./index-x3ema2w3.js";
158
150
  import {
151
+ BUNDLED_EXTENSIONS,
159
152
  EXTENSION_MANIFEST_FILENAME,
160
153
  assertExtensionContained,
154
+ bundledExtension,
161
155
  extensionCarries,
162
156
  extensionEntry,
163
157
  extensionStyleList,
158
+ isBundledExtension,
159
+ isPathSpecifier,
164
160
  log,
161
+ parseExtensionSpecifier,
165
162
  pathEscapesFolder,
166
163
  readExtensionMeta,
167
164
  resolveExtension
168
- } from "./index-4v7e621e.js";
165
+ } from "./index-2bjcghze.js";
169
166
  import"./index-ht1bcqdd.js";
170
167
  import {
171
168
  BUILD_DIAGNOSTIC_CODES
172
- } from "./index-53nrm5j1.js";
173
- import"./index-apc8w9w8.js";
169
+ } from "./index-4x6y8krm.js";
170
+ import"./index-0skhjx3e.js";
174
171
  import {
175
172
  BuildError
176
- } from "./index-vay3ff1f.js";
173
+ } from "./index-46ycxe6r.js";
177
174
  import"./index-2ppw6q9v.js";
178
175
  import {
179
176
  DEFAULT_PRINT_OPTS,
@@ -193,10 +190,10 @@ import {
193
190
  restoreVersionWithBackup,
194
191
  switchBranch,
195
192
  whenGitIdle
196
- } from "./index-93pqkvty.js";
193
+ } from "./index-h6yk5tjx.js";
197
194
  import {
198
- PLUGINS_DIR
199
- } from "./index-r7jcsk6h.js";
195
+ EXTENSIONS_DIR
196
+ } from "./index-5x42amh8.js";
200
197
  import {
201
198
  capabilitiesFor,
202
199
  detectProjectSource,
@@ -295,8 +292,8 @@ export {
295
292
  listGitHubRepositories,
296
293
  listGitHubBranches,
297
294
  listCustomTemplates,
295
+ listBundledExtensions,
298
296
  listBuiltInTemplates,
299
- listBuiltInStyleSets,
300
297
  listBookPublishProviders,
301
298
  knownForgeTokenUrl,
302
299
  isValidRepositoryName,
@@ -344,13 +341,13 @@ export {
344
341
  checkExtensionUpdates,
345
342
  checkCss,
346
343
  capabilitiesFor,
344
+ bundledExtension,
347
345
  autoSyncDelayMs,
348
346
  autoSnapshotDelayMs,
349
347
  assertMilestone,
350
348
  assertExtensionContained,
351
349
  adoptFolder,
352
350
  addExtension,
353
- addBuiltInStyleSet,
354
351
  VERSION_CHANGES_SHOWN,
355
352
  TARGET_IDS,
356
353
  TARGETS,
@@ -361,11 +358,9 @@ export {
361
358
  RESTORE_BACKUP_MESSAGE,
362
359
  RESERVED_PREFIX,
363
360
  REPAIR_SNAPSHOT_MESSAGE,
364
- RECOMMENDED_EXTENSIONS,
365
361
  PublishSelectionsStore,
366
362
  PRESET_IDS,
367
363
  PRESETS,
368
- PLUGINS_DIR,
369
364
  MAX_SEARCH_BYTES,
370
365
  MANIFEST_FILENAMES,
371
366
  HISTORY_PAGE_LIMIT,
@@ -383,7 +378,6 @@ export {
383
378
  BuildError,
384
379
  BUNDLED_EXTENSIONS,
385
380
  BUILT_IN_TEMPLATE_IDS,
386
- BUILT_IN_STYLE_SET_IDS,
387
381
  BUILD_DIAGNOSTIC_CODES,
388
382
  AUTO_SYNC_PUSH_INTERVAL_MINUTES,
389
383
  AUTO_SYNC_MIN_MINUTES,
@@ -169,7 +169,7 @@ export interface BuildContext {
169
169
  * The build's ONE plugin load (#262). `null` until {@link loadBuildPlugins}
170
170
  * runs; every stage that needs plugins (the preValidate gate in
171
171
  * {@link runQualityGates}, and {@link renderBook}) calls that function and
172
- * gets the SAME resolved `{ plugins, pluginCss, pluginStylePaths }` back,
172
+ * gets the SAME resolved `{ plugins, pluginStyles, pluginStylePaths }` back,
173
173
  * whichever of them runs first.
174
174
  *
175
175
  * One load means one answer: the validation gate checks exactly the plugin
@@ -1,11 +1,10 @@
1
1
  import { type NpmPluginInstallOptions } from "./npm-plugin-installer.ts";
2
2
  import { type ExtensionCarries } from "./extension-manifest.ts";
3
3
  import type { ResolvedExtensionConfig } from "../schema/manifest.types.ts";
4
- /** Folder (relative to the project root) extensions this module COPIES land in
5
- * (built-in looks, zip/URL imports). Path entries elsewhere are never moved. */
6
- export declare const EXTENSIONS_DIR = "extensions";
7
- /** The vendored npm tree stays where 0.10.8 put it. */
8
- export { PLUGINS_DIR } from "./plugin-vendor.ts";
4
+ /** The folder (relative to the project root) a book's extensions live under:
5
+ * vendored npm copies in `extensions/npm/`, zip and URL imports beside them
6
+ * (extension-import.ts). Path entries elsewhere are never moved. */
7
+ export { EXTENSIONS_DIR } from "./plugin-vendor.ts";
9
8
  /** How an `extensions:` entry resolves — the form of its specifier decides. */
10
9
  export type ExtensionSourceKind = "bundled" | "path" | "npm";
11
10
  /** One configured extension, as surfaced to the CLI and the desktop. */
@@ -54,34 +53,6 @@ export interface ExtensionValidationResult {
54
53
  /** The loader's fail-fast error message when `ok` is `false`. */
55
54
  error?: string;
56
55
  }
57
- /** A curated, bundled markdown feature an author can turn on. */
58
- export interface RecommendedExtension {
59
- /** The bundled name — also the manifest entry "Add" writes. */
60
- use: string;
61
- /** Short, plain-language feature name shown as the row title. */
62
- label: string;
63
- /** One-line author-friendly description in plain words. Backtick spans mark
64
- * "what to type" (the desktop sets them in code type), so keep them paired
65
- * and keep HTML tag names out of the prose. */
66
- description: string;
67
- }
68
- /**
69
- * The bundled markdown features (`BUNDLED_EXTENSIONS`): adding one writes the
70
- * manifest entry AND the feature works immediately — no terminal, no install,
71
- * offline. The always-on defaults (attrs/footnote/deflist, applied
72
- * unconditionally in renderer.ts) are deliberately NOT listed: recommending
73
- * the author "add" something already active is pure confusion.
74
- */
75
- export declare const RECOMMENDED_EXTENSIONS: RecommendedExtension[];
76
- /** The built-in looks shipped as embedded assets (ids are folder names). */
77
- export declare const BUILT_IN_STYLE_SET_IDS: readonly ["clean-book", "zine", "technical-doc"];
78
- export type BuiltInStyleSetId = (typeof BUILT_IN_STYLE_SET_IDS)[number];
79
- /** A built-in look, as listed for the desktop's Look view. */
80
- export interface BuiltInStyleSet {
81
- id: BuiltInStyleSetId;
82
- name: string;
83
- description: string;
84
- }
85
56
  /** One key per book folder, however the path is spelled (Windows paths are case-insensitive). */
86
57
  export declare function projectKey(projectDir: string): string;
87
58
  /**
@@ -157,7 +128,7 @@ export declare function restoreNpmExtension(projectDir: string, name: string, ve
157
128
  /** Every version of `name` an enabled manifest entry pins — never pruned. */
158
129
  keepVersions?: readonly string[];
159
130
  }): Promise<string[]>;
160
- /** True when `plugins/npm/<name>/<version>/` is absent or has no loadable entry — what the loader calls "not installed" or "incomplete". */
131
+ /** True when `extensions/npm/<name>/<version>/` is absent or has no loadable entry — what the loader calls "not installed" or "incomplete". */
161
132
  export declare function vendoredCopyNeedsInstall(projectDir: string, name: string, version: string): Promise<boolean>;
162
133
  /** One pinned npm extension against npm's `latest` tag. */
163
134
  export interface ExtensionUpdateCheck {
@@ -215,17 +186,19 @@ export interface UpdateExtensionsOptions extends CheckExtensionUpdatesOptions {
215
186
  * Returns what moved; an up-to-date book returns `[]`.
216
187
  */
217
188
  export declare function updateExtensions(projectDir: string, options?: UpdateExtensionsOptions): Promise<ExtensionUpdateResult[]>;
218
- /** List the built-in looks (metadata read from the embedded assets). */
219
- export declare function listBuiltInStyleSets(): Promise<BuiltInStyleSet[]>;
189
+ /** A bundled extension as the desktop lists it: "Add" writes `use`. */
190
+ export interface BundledExtensionInfo {
191
+ use: string;
192
+ /** Short, plain-language name shown as the row title. */
193
+ label: string;
194
+ description: string;
195
+ /** A look (styles only), as opposed to a markdown feature. */
196
+ look: boolean;
197
+ }
198
+ /** The bundled extensions, in table order. */
199
+ export declare function listBundledExtensions(): BundledExtensionInfo[];
220
200
  /** A folder name under `extensions/` not yet taken: `<slug>`, `<slug>-2`, … */
221
201
  export declare function uniqueExtensionId(projectDir: string, base: string): Promise<string>;
222
- /**
223
- * Copy a built-in look into `extensions/<id>/` and add it to the manifest as
224
- * `./extensions/<id>`. The copy is deliberate (see the module doc): the look
225
- * becomes the author's own editable files. Idempotent — a folder already
226
- * there is kept as it is and only (re-)referenced.
227
- */
228
- export declare function addBuiltInStyleSet(projectDir: string, id: string): Promise<ProjectExtensionEntry>;
229
202
  /**
230
203
  * Validate every configured extension by attempting to LOAD it through the
231
204
  * one loader. Disabled entries are reported as disabled and NOT load-tested.
@@ -1,7 +1,7 @@
1
1
  /** Which starter package to create. */
2
- export type ExtensionKind = "plugin" | "theme";
2
+ export type ExtensionKind = "plugin" | "look";
3
3
  /** The kinds `--kind` accepts, for CLI help text and pickers. */
4
- export declare const EXTENSION_KINDS: readonly ["plugin", "theme"];
4
+ export declare const EXTENSION_KINDS: readonly ["plugin", "look"];
5
5
  /**
6
6
  * The class/property prefix a scaffolded extension is FORBIDDEN to take.
7
7
  * `gp-` is Gutterpress core's own vocabulary (CLAUDE.md §6); an extension
@@ -56,7 +56,7 @@ export interface ScaffoldExtensionResult {
56
56
  */
57
57
  export declare function resolveExtensionPrefix(slug: string, requested?: string): string;
58
58
  /**
59
- * Create a plugin or theme starter package.
59
+ * Create a plugin or look starter package.
60
60
  *
61
61
  * Resolves with a {@link ScaffoldExtensionResult}; throws a
62
62
  * {@link CreateProjectError} (the SAME error type `scaffoldProject` throws, so
@@ -1,11 +1,28 @@
1
1
  /**
2
- * The markdown features compiled into Gutterpress (`BUILTIN_OPTIONAL_PLUGINS`,
3
- * `markdown/renderer.ts` — a test pins the two lists together). Writing one
4
- * of these names installs nothing: the loader resolves it from the bundle.
2
+ * The extensions compiled into Gutterpress. Writing one of these names in
3
+ * `extensions:` installs nothing: the loader resolves it from the bundle, so
4
+ * it works offline and in the compiled binary.
5
+ *
6
+ * A markdown feature loads from `BUILTIN_OPTIONAL_PLUGINS`
7
+ * (`markdown/renderer.ts`, keyed by the same name; a test pins the two
8
+ * together). A look is its stylesheet, inlined here as text, so the loader
9
+ * hands it straight to the cascade and nothing is read from disk or copied
10
+ * into a book. Both carry their author-facing words here.
5
11
  */
6
- export declare const BUNDLED_EXTENSIONS: readonly ["markdown-it-mark", "markdown-it-sub", "markdown-it-sup", "markdown-it-abbr", "gutterpress-gfm-alerts"];
7
- export type BundledExtensionName = (typeof BUNDLED_EXTENSIONS)[number];
8
- export declare function isBundledExtension(name: string): name is BundledExtensionName;
12
+ export interface BundledExtension {
13
+ /** The specifier an author writes. */
14
+ name: string;
15
+ /** Short, plain-language name shown as the row title. */
16
+ label: string;
17
+ /** One line in plain words. Backtick spans mark "what to type" (the desktop
18
+ * sets them in code type), so keep them paired and keep HTML tags out. */
19
+ description: string;
20
+ /** A look's stylesheet. Absent for a markdown feature. */
21
+ css?: string;
22
+ }
23
+ export declare const BUNDLED_EXTENSIONS: readonly BundledExtension[];
24
+ export declare function bundledExtension(name: string): BundledExtension | undefined;
25
+ export declare function isBundledExtension(name: string): boolean;
9
26
  /** `./x`, `../x`, `/x`, or a Windows drive path (`C:\x`). Nothing else is a path. */
10
27
  export declare function isPathSpecifier(use: string): boolean;
11
28
  export type ParsedExtensionSpecifier = {
@@ -4,7 +4,7 @@ import { type PrintAreaSource } from "../../engine/shared/print-area.ts";
4
4
  /** Reader injected by the host: resolve a project-root-relative file → its text. */
5
5
  export type ReadText = (relPath: string) => Promise<string>;
6
6
  /**
7
- * One author-mistake warning emitted by Gutterpress's marker parser.
7
+ * One author-mistake warning, most emitted by Gutterpress's marker parser.
8
8
  * Mirrors the shape `markers.js`'s `warn()` pushes onto
9
9
  * `env.layoutWarnings` — see that file's header comment for the warning
10
10
  * `type`s (`ambiguous_marker_token`, `unrecognized_marker_token`,
@@ -36,6 +36,10 @@ export type ReadText = (relPath: string) => Promise<string>;
36
36
  * for an `@page` / `@section` word that is a near-miss of a declared page /
37
37
  * section variant (any other word there is a free name and stays silent).
38
38
  *
39
+ * Two come from outside the marker parser: `table_extra_cells` (a table
40
+ * row with more cells than the header, `table.ts`) and `math_error` (a
41
+ * formula MathJax could not typeset, `math.ts`).
42
+ *
39
43
  * There is deliberately no `section_without_page`: a @section with no open
40
44
  * @page is valid authoring (17/17 false positives across two real books).
41
45
  */
@@ -60,6 +64,8 @@ export declare const PAGE_BACKGROUND_FIXED_MILESTONE = 152;
60
64
  export interface AssembleBookHtmlOptions {
61
65
  /** Ordered list of project-root-relative `.md` files to concatenate. */
62
66
  files: string[];
67
+ /** The book's BCP 47 language tag (manifest `language`); `en` when absent. */
68
+ language?: string;
63
69
  /** Async reader the assembler uses to fetch each file's contents. */
64
70
  readText: ReadText;
65
71
  /**