@heroiclands/package-build 23.12.6 → 23.12.7

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 (83) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/assets/styles/content-base.css +25 -0
  3. package/bin/content-commands.mjs +264 -3
  4. package/bin/package-build.mjs +25 -0
  5. package/content-config.mjs +72 -0
  6. package/docs/api.md +10 -9
  7. package/docs/authoring/links-and-markup.md +439 -47
  8. package/docs/commands.md +127 -0
  9. package/docs/configuration.md +66 -3
  10. package/docs/content-format.md +3 -1
  11. package/docs/guides/book.md +11 -0
  12. package/docs/guides/module-project.md +16 -0
  13. package/docs/guides/site.md +19 -0
  14. package/docs/reference/format-details.md +372 -6
  15. package/docs/reference/note-types.md +36 -11
  16. package/engine/base-compiler.mjs +2 -0
  17. package/engine/bundles.mjs +7 -2
  18. package/engine/compile-corpus.mjs +3 -1
  19. package/engine/content-charset.mjs +21 -2
  20. package/engine/content-format.yaml +1 -0
  21. package/engine/content-index.mjs +12 -0
  22. package/engine/content-poetry.mjs +22 -4
  23. package/engine/content-scripts.mjs +1219 -0
  24. package/engine/content-session.mjs +2 -0
  25. package/engine/data-keys.mjs +5 -1
  26. package/engine/dictionary-layout.mjs +18 -12
  27. package/engine/dictionary-rows.mjs +11 -2
  28. package/engine/helpers.mjs +8 -2
  29. package/engine/index-records.mjs +1 -0
  30. package/engine/journals.mjs +14 -3
  31. package/engine/language-data.mjs +137 -1
  32. package/engine/language-tag.mjs +103 -0
  33. package/engine/language-terms.mjs +16 -4
  34. package/engine/map-lexicon.mjs +762 -0
  35. package/engine/markup-validation.mjs +17 -3
  36. package/engine/names.mjs +1 -1
  37. package/engine/note-vocabulary.mjs +113 -0
  38. package/engine/pdf-build.mjs +28 -10
  39. package/engine/pdf-render.mjs +116 -11
  40. package/engine/run-rows.mjs +119 -0
  41. package/engine/run-words.mjs +266 -0
  42. package/engine/scenes.mjs +1 -0
  43. package/engine/script-font-delivery.mjs +321 -0
  44. package/engine/script-fonts.mjs +172 -0
  45. package/engine/site-build.mjs +10 -3
  46. package/engine/site-config.mjs +5 -0
  47. package/engine/site-root.mjs +19 -5
  48. package/engine/sql-tables.mjs +111 -1
  49. package/engine/text-maps.mjs +607 -0
  50. package/engine/verb-pattern.mjs +99 -0
  51. package/engine/word-pattern.mjs +27 -0
  52. package/engine/word-rows.mjs +8 -2
  53. package/engine/word-terms.mjs +44 -0
  54. package/hugo-theme/layouts/_default/baseof.html +3 -0
  55. package/hugo-theme/static/css/style.css +10 -0
  56. package/manifest.mjs +9 -1
  57. package/package.json +1 -1
  58. package/types/content-config.d.mts +26 -0
  59. package/types/engine/compile-corpus.d.mts +2 -1
  60. package/types/engine/content-index.d.mts +11 -3
  61. package/types/engine/content-poetry.d.mts +7 -0
  62. package/types/engine/content-scripts.d.mts +226 -0
  63. package/types/engine/data-keys.d.mts +2 -2
  64. package/types/engine/dictionary-rows.d.mts +15 -0
  65. package/types/engine/helpers.d.mts +5 -1
  66. package/types/engine/journals.d.mts +7 -2
  67. package/types/engine/language-data.d.mts +2 -0
  68. package/types/engine/language-tag.d.mts +8 -0
  69. package/types/engine/language-terms.d.mts +2 -1
  70. package/types/engine/map-lexicon.d.mts +274 -0
  71. package/types/engine/markup-validation.d.mts +7 -2
  72. package/types/engine/pdf-build.d.mts +26 -3
  73. package/types/engine/pdf-render.d.mts +17 -2
  74. package/types/engine/run-rows.d.mts +89 -0
  75. package/types/engine/run-words.d.mts +86 -0
  76. package/types/engine/script-font-delivery.d.mts +134 -0
  77. package/types/engine/script-fonts.d.mts +168 -0
  78. package/types/engine/site-build.d.mts +2 -1
  79. package/types/engine/site-root.d.mts +11 -3
  80. package/types/engine/text-maps.d.mts +175 -0
  81. package/types/engine/verb-pattern.d.mts +65 -0
  82. package/types/engine/word-pattern.d.mts +13 -0
  83. package/types/engine/word-terms.d.mts +6 -0
@@ -162,6 +162,19 @@ export const NOTATION = Object.freeze(
162
162
  ]),
163
163
  );
164
164
 
165
+ /**
166
+ * Tier 4 — the blocks the HeroicLands script fonts are registered in, so a
167
+ * mapping note or a `font=` run never draws a warning for a script character.
168
+ *
169
+ * @type {ReadonlyArray<readonly [number, number]>}
170
+ */
171
+ const SCRIPT_BLOCKS = Object.freeze([
172
+ [0xf0000, 0xf2fff], // plane 15: Pirzath, Thuravark, Semaron, the reserved alphabets, Teng Chou
173
+ [0x1e960, 0x1ec6f], // Zethu overflow, Qalezu, Zethu
174
+ [0x1ecc0, 0x1ecff], // Qalezu catch-before forms
175
+ [0x13430, 0x1345f], // the Egyptian format controls Zethu uses
176
+ ]);
177
+
165
178
  /**
166
179
  * Whether the charset admits a code point anywhere in a note.
167
180
  *
@@ -169,7 +182,12 @@ export const NOTATION = Object.freeze(
169
182
  * @returns {boolean} Whether it is allowed outside a code fence.
170
183
  */
171
184
  export function isAllowedCodePoint(cp) {
172
- return isLetterTier(cp) || TYPOGRAPHY.has(cp) || NOTATION.has(cp);
185
+ return (
186
+ isLetterTier(cp) ||
187
+ TYPOGRAPHY.has(cp) ||
188
+ NOTATION.has(cp) ||
189
+ SCRIPT_BLOCKS.some(([from, to]) => cp >= from && cp <= to)
190
+ );
173
191
  }
174
192
 
175
193
  /**
@@ -362,7 +380,8 @@ export function checkText(text, file) {
362
380
  line: i + 1,
363
381
  column,
364
382
  severity: /** @type {const} */ ("warning"),
365
- message: `\`${ch}\` ${hex(cp)} is ${refusalFor(cp)}`,
383
+ // A Private Use character draws as nothing, so quoting it leaves an empty span.
384
+ message: `${/\p{Co}/u.test(ch) ? "" : `\`${ch}\` `}${hex(cp)} is ${refusalFor(cp)}`,
366
385
  });
367
386
  }
368
387
  }
@@ -363,6 +363,7 @@ types:
363
363
  doc:
364
364
  data:
365
365
  - culture
366
+ - map
366
367
  subTypes:
367
368
  - rules
368
369
  - userguide
@@ -106,6 +106,7 @@ import { AddressLink } from "./address-values.mjs";
106
106
  import { scanSpans } from "./content-spans.mjs";
107
107
  import { rebaseNameSpans } from "./name-markup.mjs";
108
108
  import { analyzeNameSpans } from "./names.mjs";
109
+ import { recordRuns } from "./run-rows.mjs";
109
110
  import fs from "node:fs";
110
111
  import path from "node:path";
111
112
 
@@ -448,6 +449,8 @@ function assertNoDerivedKeys(frontmatter, relPath, absPath, contentPackage, raw)
448
449
  * `name` reads `Map 1` rather than `Figure 1` where it is due — see
449
450
  * {@link module:engine/content-figures.scanFigures}.
450
451
  * @param {import("./content-session.mjs").ContentSyntax} [options.syntax] - Session scanner projections.
452
+ * @param {import("./content-scripts.mjs").RunContext} [options.runContext] -
453
+ * The configured languages, which give each run's `lang=` its tag.
451
454
  * @returns {Record<string, any>} The record, keys sorted at every depth. A
452
455
  * **stub** — a note with an empty body, on a type an empty body suppresses —
453
456
  * carries `address` and `anchors` as `null`: it publishes no page, so it
@@ -474,6 +477,7 @@ export function buildIndexRecord({
474
477
  dateContext,
475
478
  resolveRole,
476
479
  syntax,
480
+ runContext,
477
481
  }) {
478
482
  assertNoDerivedKeys(frontmatter, relPath, absPath, contentPackage, raw);
479
483
 
@@ -541,6 +545,7 @@ export function buildIndexRecord({
541
545
  ...(Object.keys(resolvedDates).length ? { resolvedDates } : {}),
542
546
  package: contentPackage,
543
547
  names: marked.names,
548
+ runs: recordRuns(source, { context: runContext, bodyLine, bodyColumn, syntax }),
544
549
  address: stub ? null : address,
545
550
  nameAscii: asciiName(frontmatter?.name?.full),
546
551
  aliasesAscii: asciiAliases(frontmatter?.name?.aliases),
@@ -695,6 +700,7 @@ export function indexRecordsForNote({
695
700
  dateContext,
696
701
  resolveRole,
697
702
  syntax,
703
+ runContext,
698
704
  }) {
699
705
  resolveNoteId(frontmatter, { pkg: contentPackage });
700
706
  const record = buildIndexRecord({
@@ -712,6 +718,7 @@ export function indexRecordsForNote({
712
718
  dateContext,
713
719
  resolveRole,
714
720
  syntax,
721
+ runContext,
715
722
  });
716
723
  const records = [decodeIndexAddresses(record, addressContext ?? { package: contentPackage })];
717
724
  const address = noteAddress(frontmatter, contentPackage);
@@ -1067,6 +1074,8 @@ export function resolveCultures(
1067
1074
  * calendar note's shortcode; see {@link module:engine/note-calendars}.
1068
1075
  * @param {string} [options.configFile] - The configuration file, where an
1069
1076
  * unknown `defaultCalendar` is reported.
1077
+ * @param {Readonly<Record<string, {tag: string}>>} [options.languages] - The
1078
+ * configured languages, which give each run's `lang=` its tag.
1070
1079
  * @param {(key: string) => ({culture?: unknown}|undefined)} [options.foreignRecord] -
1071
1080
  * Another package's entry for a canonical address, read for a parent that
1072
1081
  * package publishes; see {@link resolveCultures}.
@@ -1086,6 +1095,7 @@ export function collectContentIndex(
1086
1095
  currentDate,
1087
1096
  defaultCalendar,
1088
1097
  configFile,
1098
+ languages,
1089
1099
  skipDirectories,
1090
1100
  assetsBase,
1091
1101
  manifest,
@@ -1192,6 +1202,7 @@ export function collectContentIndex(
1192
1202
  dateContext: dates,
1193
1203
  resolveRole,
1194
1204
  syntax,
1205
+ runContext: { languages },
1195
1206
  });
1196
1207
  records.push(...made);
1197
1208
  bodies.push({ record: made[0], body });
@@ -1478,6 +1489,7 @@ export function indexRecordsFor({
1478
1489
  currentDate: resolved.currentDate,
1479
1490
  defaultCalendar: resolved.defaultCalendar ?? undefined,
1480
1491
  configFile: configFileIn(resolved.rootDir, CONFIG_FILENAMES),
1492
+ languages: resolved.languages,
1481
1493
  addressContext: {
1482
1494
  ...noteAddressContext(resolved),
1483
1495
  declaresAddress: dependencyAddresses(resolved),
@@ -77,6 +77,24 @@ import { parseExtensionAttributes, refusedAttributes } from "./extension-attribu
77
77
 
78
78
  const structureParser = new MarkdownIt({ html: true });
79
79
 
80
+ /**
81
+ * The keys a poetry fence's attribute block takes. `lang`, `font` and `map`
82
+ * are read by `scanRuns` in content-scripts.mjs; `dir`, and on the site
83
+ * `data-pagefind-ignore`, are written by its rewrite of a fence set in one of
84
+ * the faces, and reach the element as they are written.
85
+ */
86
+ export const POETRY_KEYS = Object.freeze([
87
+ "form",
88
+ "meter",
89
+ "rhyme",
90
+ "syllables",
91
+ "lang",
92
+ "font",
93
+ "map",
94
+ "dir",
95
+ "data-pagefind-ignore",
96
+ ]);
97
+
80
98
  /** Read poetry code fences; start/end are inclusive zero-based source lines.
81
99
  * @param {string} source - Authored Markdown.
82
100
  */
@@ -157,15 +175,13 @@ export function scanPoetry(source) {
157
175
  for (const message of [...parsed.problems, ...refusedAttributes(parsed.values)])
158
176
  errors.push({ ...at, message });
159
177
  for (const [key, value] of Object.entries(parsed.values)) {
160
- if (!["form", "meter", "rhyme", "syllables", "lang"].includes(key))
178
+ if (!POETRY_KEYS.includes(key))
161
179
  errors.push({ ...at, message: `${key}= is not a poetry attribute` });
162
180
  else if (key === "syllables" && !/^[1-9]\d*(?:,[1-9]\d*)*$/.test(value))
163
181
  errors.push({
164
182
  ...at,
165
183
  message: "poetry syllables= needs positive comma-separated counts",
166
184
  });
167
- else if (key === "lang" && !/^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/.test(value))
168
- errors.push({ ...at, message: "poetry lang= needs a language tag" });
169
185
  }
170
186
  }
171
187
  if (opening?.name === "poetry")
@@ -197,7 +213,9 @@ export function renderPoetry(source) {
197
213
  if (block.id) attrs.push(`id="${escape(slugify(block.id))}"`);
198
214
  for (const [key, value] of Object.entries(block.attributes))
199
215
  if (!refusedAttributes({ [key]: value }).length)
200
- attrs.push(`${key === "lang" ? key : `data-${key}`}="${escape(value)}"`);
216
+ attrs.push(
217
+ `${["lang", "dir", "data-pagefind-ignore"].includes(key) ? key : `data-${key}`}="${escape(value)}"`,
218
+ );
201
219
  let indent = block.indent;
202
220
  if (
203
221
  indent.length >= 4 &&