@heroiclands/package-build 24.0.1 → 24.0.3

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 (65) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/assets/preview/gitea.ini +1 -1
  3. package/assets/styles/content-base.css +33 -0
  4. package/bin/content-commands.mjs +17 -17
  5. package/content-config.mjs +1 -52
  6. package/docs/api.md +20 -13
  7. package/docs/authoring/links-and-markup.md +165 -17
  8. package/docs/commands.md +20 -20
  9. package/docs/configuration.md +29 -69
  10. package/docs/content-format.md +4 -2
  11. package/docs/guides/site.md +3 -1
  12. package/docs/guides/writing-in-invented-scripts.md +68 -6
  13. package/docs/reference/format-details.md +160 -118
  14. package/docs/reference/note-types.md +49 -29
  15. package/docs/source-preview.md +2 -2
  16. package/engine/content-blocks.mjs +6 -2
  17. package/engine/content-charset.mjs +1 -0
  18. package/engine/content-format.yaml +2 -0
  19. package/engine/content-index.mjs +5 -1
  20. package/engine/content-render.mjs +2 -1
  21. package/engine/content-scripts.mjs +268 -21
  22. package/engine/content-scroll.mjs +389 -0
  23. package/engine/dictionary-layout.mjs +16 -3
  24. package/engine/font-outlines.mjs +394 -0
  25. package/engine/foreign-catalog.mjs +11 -122
  26. package/engine/language-data.mjs +154 -63
  27. package/engine/language-rows.mjs +35 -9
  28. package/engine/language-terms.mjs +39 -9
  29. package/engine/map-lexicon.mjs +31 -6
  30. package/engine/note-vocabulary.mjs +58 -6
  31. package/engine/pdf-build.mjs +9 -178
  32. package/engine/pdf-fonts.mjs +1 -1
  33. package/engine/pdf-render.mjs +52 -11
  34. package/engine/run-rows.mjs +45 -3
  35. package/engine/script-direction.mjs +67 -0
  36. package/engine/script-emphasis-terms.mjs +11 -3
  37. package/engine/script-font-delivery.mjs +42 -9
  38. package/engine/script-fonts.mjs +7 -0
  39. package/engine/site-reach.mjs +23 -0
  40. package/engine/site-render.mjs +18 -3
  41. package/engine/verse-metre.mjs +1 -0
  42. package/engine/written-in.mjs +208 -0
  43. package/package.json +2 -1
  44. package/site-theme/static/css/style.css +7 -0
  45. package/types/content-config.d.mts +1 -19
  46. package/types/engine/content-scripts.d.mts +31 -6
  47. package/types/engine/content-scroll.d.mts +179 -0
  48. package/types/engine/dictionary-layout.d.mts +5 -0
  49. package/types/engine/font-outlines.d.mts +67 -0
  50. package/types/engine/foreign-catalog.d.mts +0 -38
  51. package/types/engine/language-data.d.mts +13 -0
  52. package/types/engine/language-rows.d.mts +5 -4
  53. package/types/engine/language-terms.d.mts +7 -0
  54. package/types/engine/map-lexicon.d.mts +4 -3
  55. package/types/engine/pdf-build.d.mts +5 -27
  56. package/types/engine/pdf-fonts.d.mts +10 -0
  57. package/types/engine/run-rows.d.mts +28 -6
  58. package/types/engine/script-direction.d.mts +10 -0
  59. package/types/engine/script-emphasis-terms.d.mts +10 -3
  60. package/types/engine/script-font-delivery.d.mts +5 -3
  61. package/types/engine/script-fonts.d.mts +9 -0
  62. package/types/engine/site-reach.d.mts +11 -0
  63. package/types/engine/written-in.d.mts +76 -0
  64. package/engine/asset-replacement.mjs +0 -93
  65. package/types/engine/asset-replacement.d.mts +0 -22
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The outlines of a TrueType face, read once per file.
3
+ *
4
+ * @param {string} file - The `.ttf`.
5
+ * @returns {Outlines} Its outlines.
6
+ * @throws {Error} When the file is not a TrueType face with outlines.
7
+ */
8
+ export function fontOutlines(file: string): Outlines;
9
+ /**
10
+ * One panel of a scroll as SVG: its columns read from the right, `rows` ems
11
+ * tall, each character centred in its column, in red where it is set in the
12
+ * rubric and with a line beside it, on the right, where it is overlined.
13
+ *
14
+ * @param {import("./content-scroll.mjs").ScrollCell[][]} panel - Its columns,
15
+ * the first the rightmost.
16
+ * @param {number} rows - The characters in a column.
17
+ * @param {Outlines} outlines - The face.
18
+ * @returns {string} The SVG.
19
+ */
20
+ export function scrollPanelSvg(panel: import("./content-scroll.mjs").ScrollCell[][], rows: number, outlines: Outlines): string;
21
+ /** The book's rubric red, which its preamble names `book-rubric`. */
22
+ export const BOOK_RUBRIC: "#9c281b";
23
+ /** The book's ink, which its preamble names `book-ink`. */
24
+ export const BOOK_INK: "#241f1a";
25
+ /**
26
+ * A point of a contour.
27
+ */
28
+ export type Point = {
29
+ x: number;
30
+ y: number;
31
+ on: boolean;
32
+ };
33
+ /**
34
+ * One glyph, in font units, its y axis pointing down from the baseline.
35
+ */
36
+ export type Glyph = {
37
+ /**
38
+ * - Its glyph index.
39
+ */
40
+ index: number;
41
+ /**
42
+ * - Its outline as SVG path data; `""` for none.
43
+ */
44
+ d: string;
45
+ /**
46
+ * - Its horizontal advance.
47
+ */
48
+ advance: number;
49
+ /**
50
+ * - From the top of its cell to its baseline.
51
+ */
52
+ top: number;
53
+ };
54
+ /**
55
+ * A face's outlines.
56
+ */
57
+ export type Outlines = {
58
+ /**
59
+ * - Its units per em.
60
+ */
61
+ unitsPerEm: number;
62
+ /**
63
+ * - The glyph a code
64
+ * point is drawn with, or `undefined` when the face does not draw it.
65
+ */
66
+ glyph: (point: number) => Glyph | undefined;
67
+ };
@@ -11,20 +11,6 @@ export function itemCatalogRelationships(config: object): Array<{
11
11
  kind: string;
12
12
  verified: string | undefined;
13
13
  }>;
14
- /**
15
- * Every declared relationship that opted into supplying its release archive
16
- * for asset bytes alone, with no item catalogue built from it.
17
- *
18
- * @param {object} config - The resolved build configuration.
19
- * @returns {Array<{id: string, manifest: string, kind: string, verified: string|undefined}>}
20
- * The opted-in relationships, in declaration order.
21
- */
22
- export function assetArchiveRelationships(config: object): Array<{
23
- id: string;
24
- manifest: string;
25
- kind: string;
26
- verified: string | undefined;
27
- }>;
28
14
  /**
29
15
  * The cache directory for one dependency at one version.
30
16
  *
@@ -109,26 +95,6 @@ export function fetchCatalog(config: object, rel: {
109
95
  id: string;
110
96
  manifest: string;
111
97
  }): Promise<string>;
112
- /**
113
- * Fetch one dependency's release archive for its asset bytes alone, building
114
- * no item catalogue from it.
115
- *
116
- * Idempotent: a complete cache for the resolved version is left alone. Where a
117
- * relationship also declares `itemCatalog: true`, {@link fetchCatalog} already
118
- * unpacked this same archive into this same cache directory, and
119
- * {@link fetchAllCatalogs} does not call this function for it — the two never
120
- * download the same bytes twice.
121
- *
122
- * @param {object} config - The resolved build configuration.
123
- * @param {{id: string, manifest: string, verified?: string}} rel - The
124
- * declared relationship.
125
- * @returns {Promise<string>} The dependency's cache directory.
126
- */
127
- export function fetchAssetArchive(config: object, rel: {
128
- id: string;
129
- manifest: string;
130
- verified?: string;
131
- }): Promise<string>;
132
98
  /**
133
99
  * Fill the cache from a locally built artifact rather than a release.
134
100
  *
@@ -202,10 +168,6 @@ export function fetchAllMetadata(config: object): Promise<number>;
202
168
  /**
203
169
  * Fetch every opted-in dependency. The `deps fetch` command.
204
170
  *
205
- * A relationship declaring both `itemCatalog: true` and `assetArchive: true`
206
- * is fetched once: {@link fetchCatalog} already unpacks the archive on its
207
- * way to extracting the item catalogue, so this skips the archive-only fetch
208
- * for any id {@link fetchCatalog} already handled.
209
171
  *
210
172
  * @param {object} config - The resolved build configuration.
211
173
  * @returns {Promise<number>} How many dependencies were fetched.
@@ -6,6 +6,19 @@
6
6
  * @returns {boolean}
7
7
  */
8
8
  export function tongueDeclared(tag: string, declared: Set<string>): boolean;
9
+ /**
10
+ * Check a `doc` note's `data.sounds`, a spelling guide's letters and marks:
11
+ * each letter row's `tongue` names language notes. The data-key check owns
12
+ * the keys, so a sound rule a guide does not state is refused there.
13
+ *
14
+ * @param {object} note - A `doc` note: `file`, `raw` and parsed `fm`.
15
+ * @param {object} [context]
16
+ * @param {object} [context.index] - The index, for whether an Address resolves.
17
+ * @returns {object[]} The findings.
18
+ */
19
+ export function checkGuideSounds(note: object, { index }?: {
20
+ index?: object | undefined;
21
+ }): object[];
9
22
  /**
10
23
  * Check a `skill` note's language data against what it means.
11
24
  *
@@ -42,8 +42,8 @@ export function tongueRows(records: Iterable<Record<string, any>>): object[];
42
42
  */
43
43
  export function scopeRows(records: Iterable<Record<string, any>>): object[];
44
44
  /**
45
- * The `letters` rows: one per `data.sounds.letters` entry, `letters` and
46
- * `tongue` lists.
45
+ * The `letters` rows: one per `data.sounds.letters` entry of a language note
46
+ * or a spelling guide, `letters` and `tongue` lists.
47
47
  *
48
48
  * @param {Iterable<Record<string, any>>} records - Index records.
49
49
  * @returns {object[]}
@@ -207,14 +207,15 @@ export function joiningRows(records: Iterable<Record<string, any>>): object[];
207
207
  */
208
208
  export function joinRows(records: Iterable<Record<string, any>>): object[];
209
209
  /**
210
- * The `marks` rows: one per `data.sounds.marks` entry.
210
+ * The `marks` rows: one per `data.sounds.marks` entry of a language note or a
211
+ * spelling guide.
211
212
  *
212
213
  * @param {Iterable<Record<string, any>>} records - Index records.
213
214
  * @returns {object[]}
214
215
  */
215
216
  export function markRows(records: Iterable<Record<string, any>>): object[];
216
217
  /**
217
- * The `skeleton` rows: one per `data.sounds.skeleton` entry, `second` a list.
218
+ * The `skeleton` rows: one per `data.sounds.skeleton` entry, `second` and `third` lists.
218
219
  *
219
220
  * @param {Iterable<Record<string, any>>} records - Index records.
220
221
  * @returns {object[]}
@@ -233,6 +233,13 @@ export const MUTATION_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
233
233
  * @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
234
234
  */
235
235
  export const SOUND_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
236
+ /**
237
+ * The keys of a spelling guide's `data.sounds`: a `doc` note stating the
238
+ * letters and marks every tongue of a setting is written by.
239
+ *
240
+ * @type {readonly import("./data-keys.mjs").InnerKeySpec[]}
241
+ */
242
+ export const GUIDE_SOUND_FIELDS: readonly import("./data-keys.mjs").InnerKeySpec[];
236
243
  /**
237
244
  * The keys of one `data.soundChanges` entry.
238
245
  *
@@ -151,14 +151,15 @@ export function transliterate(ctx: object, keys: {
151
151
  * writing, the signs a run writes directly for that sense.
152
152
  *
153
153
  * @param {object} ctx - The run context.
154
- * @param {{lang: string, map: string, font?: string, derived?: boolean}} keys -
155
- * The language, the map, the face, and whether to list derived forms.
154
+ * @param {{lang: string, map?: string, font?: string, derived?: boolean}} keys -
155
+ * The language, the map, the face, and whether to list derived forms. A map
156
+ * or a face left out is the one the language's `data.writtenIn` names.
156
157
  * @returns {{entries: Array<{form: string, native: string, exception?: boolean, source?: "own"|"parts"|"none", ambiguous?: true, class: string|null, gloss: string|null, from?: string}>}|{problem: string, about: string}}
157
158
  * The words in `data.words` order, then any derived forms, or the problem.
158
159
  */
159
160
  export function lexiconEntries(ctx: object, { lang, map, font, derived }: {
160
161
  lang: string;
161
- map: string;
162
+ map?: string;
162
163
  font?: string;
163
164
  derived?: boolean;
164
165
  }): {
@@ -14,33 +14,16 @@
14
14
  * a build from touching a path outside its own output.
15
15
  *
16
16
  * A file **this** package ships stages directly. A pathname naming another
17
- * package's file stages instead from a declared replacement's cached archive,
18
- * when `opts.resolveReplacement` — the pure function
19
- * {@link module:engine/asset-replacement.resolveAssetReplacement} —
20
- * answers it; `opts.foreignIndex` is the merged index that answer is read
21
- * against. A pathname naming a package no replacement carries it for, or a
22
- * URL, names something no build here can open — a build reaches no network —
23
- * and the caller reports it as a picture the book will not carry.
17
+ * package's file, or a URL, names something no build here can open — a build
18
+ * reaches no network — and the caller reports it as a picture the book will
19
+ * not carry.
24
20
  *
25
21
  * @param {string} src - The pathname, as authored.
26
22
  * @param {object} config - The resolved configuration.
27
- * @param {object} [opts]
28
- * @param {Map<string, object>} [opts.foreignIndex] - Every fetched
29
- * dependency's own records, keyed by canonical address — the index a
30
- * replacement's record is read out of.
31
- * @param {Function} [opts.resolveReplacement] - {@link
32
- * module:engine/asset-replacement.resolveAssetReplacement}, or an
33
- * equivalent a caller injects for a test. Omitted, a foreign pathname this
34
- * package does not own stays declined exactly as it always has.
35
23
  * @returns {{from: string, to: string}|null} The file, and where under the
36
24
  * output directory it is staged.
37
- * @throws {Error} When a declared replacement answers the address but its
38
- * archive was never fetched.
39
25
  */
40
- export function stagedImagePath(src: string, config: object, opts?: {
41
- foreignIndex?: Map<string, object> | undefined;
42
- resolveReplacement?: Function | undefined;
43
- }): {
26
+ export function stagedImagePath(src: string, config: object): {
44
27
  from: string;
45
28
  to: string;
46
29
  } | null;
@@ -134,10 +117,6 @@ export function listBookAssets({ config, version, out }?: {
134
117
  * {@link module:engine/pdf-pages}.
135
118
  * @param {Function} [opts.readPages] - {@link
136
119
  * module:engine/pdf-pages.readEntryPages}, for tests.
137
- * @param {Function} [opts.resolveReplacement] - {@link
138
- * module:engine/asset-replacement.resolveAssetReplacement}, for tests.
139
- * Loaded from that module otherwise, and only when a relationship declares
140
- * `assetReplacement: true` — a build with none never imports it.
141
120
  * @returns {Promise<{built: boolean, reason: string|null, findings: object[],
142
121
  * imageReports: object[], books: Array<{id: string, title: string,
143
122
  * built: boolean, typ: string, pdf: string|null, stats: object}>,
@@ -149,7 +128,7 @@ export function listBookAssets({ config, version, out }?: {
149
128
  * every declared book whichever was requested; both are empty when a book's
150
129
  * selection could not be read.
151
130
  */
152
- export function buildPdf({ config, configPath, book, out, version, compile, readPages, resolveReplacement, }?: {
131
+ export function buildPdf({ config, configPath, book, out, version, compile, readPages, }?: {
153
132
  config?: object | undefined;
154
133
  configPath?: string | undefined;
155
134
  book?: string | undefined;
@@ -157,7 +136,6 @@ export function buildPdf({ config, configPath, book, out, version, compile, read
157
136
  version?: string | undefined;
158
137
  compile?: boolean | undefined;
159
138
  readPages?: Function | undefined;
160
- resolveReplacement?: Function | undefined;
161
139
  }): Promise<{
162
140
  built: boolean;
163
141
  reason: string | null;
@@ -1,3 +1,13 @@
1
+ /**
2
+ * The tables an sfnt file holds, by tag.
3
+ *
4
+ * @param {Buffer} buf - The font file.
5
+ * @returns {Map<string, {offset: number, length: number}>} Its table directory.
6
+ */
7
+ export function tableDirectory(buf: Buffer): Map<string, {
8
+ offset: number;
9
+ length: number;
10
+ }>;
1
11
  /**
2
12
  * Every glyph name a font carries, with the codepoint that reaches it.
3
13
  *
@@ -2,12 +2,14 @@
2
2
  * One run as an index record carries it.
3
3
  *
4
4
  * @typedef {object} RecordRun
5
- * @property {"span"|"block"|"verse"} kind - The construct.
5
+ * @property {"span"|"block"|"verse"|"scroll"} kind - The construct.
6
6
  * @property {string|null} lang - The language it is set in, as written: its
7
7
  * own `lang=`, else the one the div or fence around it states.
8
8
  * @property {string|null} tag - The BCP 47 tag it resolves to, or null.
9
- * @property {string|null} font - Its font, as written and inherited the same way.
10
- * @property {string|null} map - Its map, as written and inherited the same way.
9
+ * @property {string|null} font - Its font, as written and inherited the same
10
+ * way, else its language's script's.
11
+ * @property {string|null} map - Its map, as written and inherited the same way,
12
+ * else its language's script's.
11
13
  * @property {number} line - 1-based file line of its opening.
12
14
  * @property {number} column - 1-based UTF-16 file column of its opening.
13
15
  */
@@ -33,6 +35,24 @@ export function recordRuns(body: string, { context, bodyLine, bodyColumn, syntax
33
35
  bodyColumn?: number | undefined;
34
36
  syntax?: import("./content-session.mjs").ContentSyntax | undefined;
35
37
  }): RecordRun[];
38
+ /**
39
+ * Give every recorded run the map and face its language's note names in
40
+ * `data.writtenIn`, where the run leaves them unstated, as a run is set. Read
41
+ * once every record exists, since a run's language note may be recorded after
42
+ * the note that marks it.
43
+ *
44
+ * @param {Record<string, any>[]} records - The package's index records; each
45
+ * note record's `runs` is updated in place.
46
+ * @param {{languages?: Readonly<Record<string, {tag: string, note?: string}>>, contentPackage?: string}} config -
47
+ * The configured languages, whose `note` names each language note.
48
+ */
49
+ export function fillWrittenIn(records: Record<string, any>[], config: {
50
+ languages?: Readonly<Record<string, {
51
+ tag: string;
52
+ note?: string;
53
+ }>>;
54
+ contentPackage?: string;
55
+ }): void;
36
56
  /**
37
57
  * The containing page's NOTE identity.
38
58
  *
@@ -57,7 +77,7 @@ export type RecordRun = {
57
77
  /**
58
78
  * - The construct.
59
79
  */
60
- kind: "span" | "block" | "verse";
80
+ kind: "span" | "block" | "verse" | "scroll";
61
81
  /**
62
82
  * - The language it is set in, as written: its
63
83
  * own `lang=`, else the one the div or fence around it states.
@@ -68,11 +88,13 @@ export type RecordRun = {
68
88
  */
69
89
  tag: string | null;
70
90
  /**
71
- * - Its font, as written and inherited the same way.
91
+ * - Its font, as written and inherited the same
92
+ * way, else its language's script's.
72
93
  */
73
94
  font: string | null;
74
95
  /**
75
- * - Its map, as written and inherited the same way.
96
+ * - Its map, as written and inherited the same way,
97
+ * else its language's script's.
76
98
  */
77
99
  map: string | null;
78
100
  /**
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Each face's direction, from the script notes among the index records. The
3
+ * first note to list a face keeps it; a face whose note states no direction,
4
+ * or one the build does not know, is absent.
5
+ *
6
+ * @param {Iterable<Record<string, any>>|undefined} records - Index records.
7
+ * @returns {Map<string, "vertical-rl">} The directions, by face slug.
8
+ */
9
+ export function directionsFromRecords(records: Iterable<Record<string, any>> | undefined): Map<string, "vertical-rl">;
10
+ export { SCRIPT_DIRECTIONS } from "./script-emphasis-terms.mjs";
@@ -1,7 +1,8 @@
1
1
  /**
2
- * **The words a script note and a language note state emphasis by.** Read by
3
- * the vocabulary as it is declared, so this module imports nothing; the
4
- * checks and the conventions' use are in {@link module:engine/script-emphasis}.
2
+ * **The words a script note and a language note state emphasis and direction
3
+ * by.** Read by the vocabulary as it is declared, so this module imports
4
+ * nothing; the checks and the conventions' use are in
5
+ * {@link module:engine/script-emphasis} and {@link module:engine/script-direction}.
5
6
  *
6
7
  * @module
7
8
  */
@@ -17,5 +18,11 @@ export const EMPHASIS_CLASSES: Readonly<{
17
18
  * `words` marks emphasis with a word, so a mark in its text is a finding.
18
19
  */
19
20
  export const LANGUAGE_EMPHASES: readonly string[];
21
+ /**
22
+ * The directions a script states it is written in. `vertical-rl` is columns,
23
+ * top to bottom, read from the right; a script stating none is written in
24
+ * lines.
25
+ */
26
+ export const SCRIPT_DIRECTIONS: readonly string[];
20
27
  /** The `subType` of a script note. */
21
28
  export const SCRIPT_SUBTYPE: "script";
@@ -1,7 +1,9 @@
1
1
  /**
2
- * The faces the package's notes set text in with `font=`, read from the
3
- * notes themselves: every Markdown file under the content tree that writes
4
- * `font=` is scanned for its runs.
2
+ * The faces the package's notes set text in, read from the notes
3
+ * themselves: every Markdown file under the content tree that writes `font=`
4
+ * is scanned for its runs, and the face every language note names in
5
+ * `data.writtenIn` counts, since a run marked with the language alone is set
6
+ * in it.
5
7
  *
6
8
  * @param {DeliveryConfig} config - The resolved configuration.
7
9
  * @param {import("./script-fonts.mjs").ScriptFonts} installed - The fonts package.
@@ -28,6 +28,9 @@
28
28
  * @property {string} direction - `ltr` or `rtl`.
29
29
  * @property {string[]} features - OpenType features it turns on.
30
30
  * @property {ReadonlySet<number>} draws - Every code point it draws.
31
+ * @property {string|undefined} file - The absolute path of its TrueType file,
32
+ * which the book draws a `scroll` fence's outlines from; `undefined` when the
33
+ * manifest lists none.
31
34
  */
32
35
  /**
33
36
  * The installed fonts package, as a consumer sees it.
@@ -140,6 +143,12 @@ export type Face = {
140
143
  * - Every code point it draws.
141
144
  */
142
145
  draws: ReadonlySet<number>;
146
+ /**
147
+ * - The absolute path of its TrueType file,
148
+ * which the book draws a `scroll` fence's outlines from; `undefined` when the
149
+ * manifest lists none.
150
+ */
151
+ file: string | undefined;
143
152
  };
144
153
  /**
145
154
  * The installed fonts package, as a consumer sees it.
@@ -67,6 +67,17 @@ export function splitContent(html: string): {
67
67
  * @returns {string} The sections, or `""` when nothing is left.
68
68
  */
69
69
  export function pruneGenerated(html: string, outside: (href: string) => boolean): string;
70
+ /**
71
+ * Entries' links set as plain text where they lead outside a set of pages.
72
+ * Each link is the only child of a span carrying the class.
73
+ *
74
+ * @param {string} html - A page body.
75
+ * @param {(href: string) => boolean} outside - Whether an `href` names a page
76
+ * of the site that is not to be linked.
77
+ * @param {string} className - The class the entries' links carry.
78
+ * @returns {string} The body with each such `<a>` replaced by its content.
79
+ */
80
+ export function unlinkEntries(html: string, outside: (href: string) => boolean, className: string): string;
70
81
  /**
71
82
  * The comment that separates a page's authored text from the sections the
72
83
  * build generated after it, in the HTML the renderer writes.
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Whether a map is drawn in a face: it lists the face, or lists none.
3
+ *
4
+ * @param {import("./text-maps.mjs").TextMap} map - The map.
5
+ * @param {string} font - The face's slug.
6
+ * @returns {boolean} Whether the map may set text in it.
7
+ */
8
+ export function drawnIn(map: import("./text-maps.mjs").TextMap, font: string): boolean;
9
+ /**
10
+ * The script each configured language's note names, where the map is known
11
+ * and is drawn in the face. A default that names an unknown map or a face its
12
+ * map is not drawn in is the note's finding, and fills no run.
13
+ *
14
+ * @param {ReadonlyMap<string, Record<string, any>>} byName - Each configured
15
+ * language's note record, by language name, from
16
+ * {@link module:engine/map-lexicon.languageRecordsByName}.
17
+ * @param {ReadonlyMap<string, import("./text-maps.mjs").TextMap>} maps - The
18
+ * maps by name.
19
+ * @returns {Map<string, WrittenIn>} The scripts, by language name.
20
+ */
21
+ export function writtenInOf(byName: ReadonlyMap<string, Record<string, any>>, maps: ReadonlyMap<string, import("./text-maps.mjs").TextMap>): Map<string, WrittenIn>;
22
+ /**
23
+ * The keys a run takes from its language's script: each of `map` and `font`
24
+ * the run does not state, where it agrees with the one the run does.
25
+ *
26
+ * @param {{lang?: string, map?: string, font?: string}} keys - The run's keys,
27
+ * its own and those it inherits.
28
+ * @param {ReadonlyMap<string, WrittenIn>|undefined} scripts - Each language's
29
+ * script, from {@link writtenInOf}.
30
+ * @param {ReadonlyMap<string, import("./text-maps.mjs").TextMap>|undefined} maps -
31
+ * The maps by name.
32
+ * @returns {{map?: string, font?: string}} What the run takes; empty when it
33
+ * takes nothing.
34
+ */
35
+ export function fromWrittenIn(keys: {
36
+ lang?: string;
37
+ map?: string;
38
+ font?: string;
39
+ }, scripts: ReadonlyMap<string, WrittenIn> | undefined, maps: ReadonlyMap<string, import("./text-maps.mjs").TextMap> | undefined): {
40
+ map?: string;
41
+ font?: string;
42
+ };
43
+ /**
44
+ * Check a note's `data.writtenIn`: that the note is a language, that `map`
45
+ * names a map a mapping note declares, and that the map is drawn in `font`.
46
+ * The data-key check owns the keys and their kinds.
47
+ *
48
+ * @param {{file: string, raw?: string, fm?: Record<string, any>}} note - The note.
49
+ * @param {{index?: {notes?: Iterable<object>}}} [context] - The lint index;
50
+ * without one, only the note's `subType` is checked.
51
+ * @returns {object[]} The findings, each at its key or value.
52
+ */
53
+ export function checkWrittenIn(note: {
54
+ file: string;
55
+ raw?: string;
56
+ fm?: Record<string, any>;
57
+ }, { index }?: {
58
+ index?: {
59
+ notes?: Iterable<object>;
60
+ };
61
+ }): object[];
62
+ /** The `data` key a language note names its script in. */
63
+ export const WRITTEN_IN_KEY: "writtenIn";
64
+ /**
65
+ * A language's script.
66
+ */
67
+ export type WrittenIn = {
68
+ /**
69
+ * - The `data.map.name` of the mapping note.
70
+ */
71
+ map: string;
72
+ /**
73
+ * - The slug of the face, one the map is drawn in.
74
+ */
75
+ font: string;
76
+ };
@@ -1,93 +0,0 @@
1
- /*
2
- * This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
3
- * Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
4
- *
5
- * This work is licensed under the GNU General Public License v3.0 (GPLv3).
6
- * You may copy, modify, and distribute it under the terms of that license.
7
- *
8
- * For full terms, see the LICENSE.md file in the project root or visit:
9
- * https://www.gnu.org/licenses/gpl-3.0.html
10
- *
11
- * SPDX-License-Identifier: GPL-3.0-or-later
12
- */
13
-
14
- /**
15
- * A declared replacement package's asset record, picked over the local one,
16
- * by address.
17
- *
18
- * A package may declare, on a `relationships.<kind>[]` entry, that a
19
- * dependency's asset tree answers its own asset addresses —
20
- * `assetReplacement: true` in `package-build.config.yaml`:
21
- *
22
- * ```yaml
23
- * relationships:
24
- * requires:
25
- * - id: thalornaaltart
26
- * manifest: https://…/releases/latest/download/module.json
27
- * assetReplacement: true
28
- * contentIndex: true
29
- * ```
30
- *
31
- * `assetReplacement: true` opts that relationship in. `resolveAssetReplacement`
32
- * is what turns the declaration into an answer, one address at a time, against
33
- * four rules:
34
- *
35
- * 1. **Scoped to asset types.** A non-asset address, or one whose package
36
- * segment is not the compiling package's own, resolves to `null` at once —
37
- * tested with {@link module:engine/asset-types.isAssetType} rather than
38
- * reimplemented. A being, an item or a lore note is never replaced this
39
- * way.
40
- * 2. **Ordered, single pass.** Replacements are tried in declaration order —
41
- * the order their relationships appear in `package-build.config.yaml` —
42
- * and the first one carrying the address wins.
43
- * 3. **Never recursive.** A replacement's own output address is not itself
44
- * looked up against another replacement: resolving
45
- * `thalornaaltart-none-image-thorn` never asks whether something replaces
46
- * `thalornaaltart`. This forecloses a cycle by construction — there is
47
- * nothing to loop.
48
- * 4. **A miss falls through.** `null` means no declared replacement carries
49
- * the address, and the caller falls back to the local address exactly as
50
- * it does today. This function never decides that a missing replacement is
51
- * an error; a caller that wants that is free to build it on the answer.
52
- *
53
- * This resolver answers a different question from
54
- * {@link module:engine/asset-bindings.checkForeignAssetBindings}: that check
55
- * validates an art module's own staged files against another package's
56
- * addresses, at that module's own build time. This one runs at the *replaced*
57
- * package's build, consulted by its book, and asks whether a declared
58
- * replacement carries an address this package would otherwise answer itself.
59
- *
60
- * @module
61
- */
62
-
63
- import { isAssetType } from "./asset-types.mjs";
64
- import { canonicalKey, readCanonicalKey } from "./content-address.mjs";
65
-
66
- /**
67
- * The replacement record for one asset address, or `null`.
68
- *
69
- * @param {string} address - A canonical asset address.
70
- * @param {object} opts
71
- * @param {string} opts.localPackage - The compiling package's own segment.
72
- * @param {readonly string[]} opts.replacements - The package segments of
73
- * every relationship declaring `assetReplacement: true`, in declaration
74
- * order.
75
- * @param {Map<string, object>} opts.index - The merged content index, keyed
76
- * by canonical address.
77
- * @returns {{record: object, package: string}|null} The first replacement
78
- * carrying the address, or `null` when none does.
79
- */
80
- export function resolveAssetReplacement(address, { localPackage, replacements, index }) {
81
- const parts = readCanonicalKey(address);
82
- if (!parts) return null;
83
- if (!isAssetType(parts.type)) return null;
84
- if (parts.package !== localPackage) return null;
85
- for (const replacementPackage of replacements ?? []) {
86
- const key = canonicalKey(replacementPackage, parts.system, parts.type, parts.shortcode);
87
- const record = index?.get(key);
88
- if (record?.asset?.path && record.package) {
89
- return { record, package: replacementPackage };
90
- }
91
- }
92
- return null;
93
- }
@@ -1,22 +0,0 @@
1
- /**
2
- * The replacement record for one asset address, or `null`.
3
- *
4
- * @param {string} address - A canonical asset address.
5
- * @param {object} opts
6
- * @param {string} opts.localPackage - The compiling package's own segment.
7
- * @param {readonly string[]} opts.replacements - The package segments of
8
- * every relationship declaring `assetReplacement: true`, in declaration
9
- * order.
10
- * @param {Map<string, object>} opts.index - The merged content index, keyed
11
- * by canonical address.
12
- * @returns {{record: object, package: string}|null} The first replacement
13
- * carrying the address, or `null` when none does.
14
- */
15
- export function resolveAssetReplacement(address: string, { localPackage, replacements, index }: {
16
- localPackage: string;
17
- replacements: readonly string[];
18
- index: Map<string, object>;
19
- }): {
20
- record: object;
21
- package: string;
22
- } | null;