@heroiclands/package-build 19.0.0 → 20.2.0
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.
- package/CHANGELOG.md +1100 -0
- package/CONTENT.md +264 -33
- package/README.md +43 -4
- package/bin/content-build.mjs +94 -3
- package/config.mjs +9 -1
- package/content-config.mjs +99 -19
- package/docs/content-format.md +394 -72
- package/e2e.mjs +297 -3
- package/engine/actor-compiler.mjs +197 -7
- package/engine/address-charset.mjs +23 -5
- package/engine/base-compiler.mjs +63 -2
- package/engine/bundles.mjs +9 -0
- package/engine/content-address.mjs +92 -1
- package/engine/content-charset.mjs +434 -0
- package/engine/content-format.mjs +102 -0
- package/engine/content-icons.mjs +388 -0
- package/engine/content-index.mjs +11 -8
- package/engine/content-links.mjs +37 -21
- package/engine/field-reference.mjs +57 -5
- package/engine/field-spec.mjs +214 -7
- package/engine/folder-notes.mjs +24 -1
- package/engine/foreign-catalog.mjs +112 -7
- package/engine/foundry-entries.mjs +14 -0
- package/engine/frontmatter-lint.mjs +377 -56
- package/engine/frontmatter.mjs +11 -11
- package/engine/generate.mjs +72 -12
- package/engine/helpers.mjs +96 -10
- package/engine/index.mjs +9 -0
- package/engine/item-compiler.mjs +37 -0
- package/engine/journals.mjs +21 -4
- package/engine/macros.mjs +8 -0
- package/engine/map-notes.mjs +7 -7
- package/engine/note-claims.mjs +208 -5
- package/engine/note-ids.mjs +25 -1
- package/engine/note-vocabulary.mjs +76 -9
- package/engine/pack-config.mjs +102 -12
- package/engine/pack-router.mjs +0 -0
- package/engine/prose-config.mjs +42 -0
- package/engine/prose-lint.mjs +126 -0
- package/engine/retired-fields.mjs +57 -16
- package/engine/runtime-only-fields.mjs +204 -0
- package/engine/scenes.mjs +12 -19
- package/engine/schema-check.mjs +23 -1
- package/engine/schema-extract.mjs +13 -0
- package/engine/site-index.mjs +17 -0
- package/engine/subtype-registry.mjs +30 -0
- package/engine/system-block.mjs +81 -3
- package/engine/web-wikilinks.mjs +33 -27
- package/engine/wikilink-syntax.mjs +7 -0
- package/engine/wikilinks.mjs +74 -16
- package/hm3/actors.mjs +63 -13
- package/package.json +2 -2
- package/sohl/actors.mjs +106 -7
- package/sohl/item-fields.mjs +203 -0
- package/sohl/note-schemas.mjs +6 -3
- package/types/config.d.mts +7 -0
- package/types/e2e.d.mts +130 -3
- package/types/engine/actor-compiler.d.mts +83 -3
- package/types/engine/address-charset.d.mts +22 -4
- package/types/engine/base-compiler.d.mts +54 -3
- package/types/engine/content-address.d.mts +64 -0
- package/types/engine/content-charset.d.mts +127 -0
- package/types/engine/content-format.d.mts +9 -0
- package/types/engine/content-icons.d.mts +151 -0
- package/types/engine/field-spec.d.mts +271 -3
- package/types/engine/folder-notes.d.mts +20 -0
- package/types/engine/foreign-catalog.d.mts +38 -2
- package/types/engine/foundry-entries.d.mts +6 -0
- package/types/engine/frontmatter-lint.d.mts +164 -30
- package/types/engine/frontmatter.d.mts +11 -11
- package/types/engine/generate.d.mts +27 -0
- package/types/engine/helpers.d.mts +45 -9
- package/types/engine/index.d.mts +3 -0
- package/types/engine/map-notes.d.mts +2 -2
- package/types/engine/note-claims.d.mts +67 -0
- package/types/engine/note-ids.d.mts +14 -0
- package/types/engine/pack-config.d.mts +35 -0
- package/types/engine/prose-config.d.mts +41 -0
- package/types/engine/prose-lint.d.mts +36 -0
- package/types/engine/retired-fields.d.mts +29 -13
- package/types/engine/runtime-only-fields.d.mts +102 -0
- package/types/engine/schema-check.d.mts +10 -1
- package/types/engine/subtype-registry.d.mts +21 -0
- package/types/engine/system-block.d.mts +28 -2
- package/types/sohl/actors.d.mts +3 -3
|
@@ -41,6 +41,54 @@ export function deepMerge(base: any, overlay: any): any;
|
|
|
41
41
|
* @returns {string} The address, `subType:shortcode`.
|
|
42
42
|
*/
|
|
43
43
|
export function itemAddress(subType: string, shortcode: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* The key one predefined item is held under **for the package that publishes
|
|
46
|
+
* it** — the address a `model:` naming that package resolves through (#334).
|
|
47
|
+
*
|
|
48
|
+
* The unqualified {@link itemAddress} stays beside it, and the two answer
|
|
49
|
+
* different questions. A `model` that names no package means *this* one and
|
|
50
|
+
* takes the unqualified key, where a local definition still shadows a
|
|
51
|
+
* dependency's. A `model` that names a package takes this one, which nothing
|
|
52
|
+
* can shadow: that is the whole point of writing the package down.
|
|
53
|
+
*
|
|
54
|
+
* Not the canonical wikilink address, because this map is keyed in the
|
|
55
|
+
* **document's** vocabulary — a Foundry Item subtype — while a canonical address
|
|
56
|
+
* carries the *note* type. The two differ wherever a system maps a type to a
|
|
57
|
+
* differently-named subtype, and translating here would put the translation in
|
|
58
|
+
* two places.
|
|
59
|
+
*
|
|
60
|
+
* @param {string} pkg - The content package that publishes the item.
|
|
61
|
+
* @param {string} subType - The Foundry Item subtype.
|
|
62
|
+
* @param {string} shortcode - The item's `system.shortcode`.
|
|
63
|
+
* @returns {string} The address, `package:subType:shortcode`.
|
|
64
|
+
*/
|
|
65
|
+
export function packagedItemAddress(pkg: string, subType: string, shortcode: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* The key one predefined item is held under **in the catalogue**, with the
|
|
68
|
+
* shortcode folded to lower case.
|
|
69
|
+
*
|
|
70
|
+
* A shortcode is case-sensitive and routinely mixed — `Clb`, `LtShoe`,
|
|
71
|
+
* `HsTunic` — while an **address** is not: `readQualifier` normalises what it
|
|
72
|
+
* reads, and every canonical address is lowercase. So the moment a `model:` is
|
|
73
|
+
* read as an address (#334), `weapongear-clb` has to find the document whose
|
|
74
|
+
* `system.shortcode` is `Clb`, and an exact match cannot (#346).
|
|
75
|
+
*
|
|
76
|
+
* Folding is safe because the fold is already the address: no two items in any
|
|
77
|
+
* published tree differ only by the case of their shortcode, and #340 will make
|
|
78
|
+
* that impossible rather than merely true.
|
|
79
|
+
*
|
|
80
|
+
* **This is not {@link itemAddress}, and must not become it.** That one seeds
|
|
81
|
+
* {@link embeddedItemId}, so folding there would change the `_id` of every
|
|
82
|
+
* embedded item whose identity carries a capital — a silent re-identification of
|
|
83
|
+
* documents nothing about which had changed. The catalogue is a lookup table;
|
|
84
|
+
* an id is a promise.
|
|
85
|
+
*
|
|
86
|
+
* @param {string} subType - The Foundry Item subtype.
|
|
87
|
+
* @param {string} shortcode - The item's `system.shortcode`, in any case.
|
|
88
|
+
* @param {string} [pkg] - The publishing package, for the qualified form.
|
|
89
|
+
* @returns {string} The catalogue key.
|
|
90
|
+
*/
|
|
91
|
+
export function catalogueKey(subType: string, shortcode: string, pkg?: string): string;
|
|
44
92
|
/**
|
|
45
93
|
* What identifies one embedded item on its actor.
|
|
46
94
|
*
|
|
@@ -136,6 +184,13 @@ export class SystemActorCompiler extends BasePackCompiler {
|
|
|
136
184
|
/** @type {readonly string[]} */
|
|
137
185
|
itemsSourceDirs: readonly string[];
|
|
138
186
|
foreignSourceDirs: any;
|
|
187
|
+
/**
|
|
188
|
+
* Every package a `model:` may name besides this one — the dependencies
|
|
189
|
+
* whose item catalogues were supplied (#334).
|
|
190
|
+
*
|
|
191
|
+
* @returns {Set<string>} The dependency package ids.
|
|
192
|
+
*/
|
|
193
|
+
get foreignPackages(): Set<string>;
|
|
139
194
|
/**
|
|
140
195
|
* This pass's system map, or a message naming the class that forgot it.
|
|
141
196
|
*
|
|
@@ -195,9 +250,34 @@ export class SystemActorCompiler extends BasePackCompiler {
|
|
|
195
250
|
* @returns {object|null} The embedded item, or null when it resolved to
|
|
196
251
|
* nothing — always with a finding emitted.
|
|
197
252
|
*/
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
253
|
+
/**
|
|
254
|
+
* Read an entry's `model:` — the address of the item it is a copy of.
|
|
255
|
+
*
|
|
256
|
+
* The address grammar is the wikilink one (#336), so a `model` is written at
|
|
257
|
+
* whatever length says what it means: `skill-wpnc` within this package,
|
|
258
|
+
* `sohl-sohl-skill-wpnc` to reach another. The system segment defaults from
|
|
259
|
+
* the block the entry sits in — `<system>.items` — which is what makes the
|
|
260
|
+
* short form name an *Item* here while the same string in body prose names
|
|
261
|
+
* a page.
|
|
262
|
+
*
|
|
263
|
+
* It replaced a top-level `shortcode:` that meant something different from
|
|
264
|
+
* the `system.shortcode` beside it and could not say which package a
|
|
265
|
+
* template came from (#334).
|
|
266
|
+
*
|
|
267
|
+
* @param {unknown} model - The authored value.
|
|
268
|
+
* @param {number} index - The entry's position, for the message.
|
|
269
|
+
* @param {string} ctx - Diagnostic context (the actor's label).
|
|
270
|
+
* @returns {{type: string, shortcode: string, package: string|null}|null}
|
|
271
|
+
* The parsed address, or `null` after reporting why it is not one.
|
|
272
|
+
*/
|
|
273
|
+
readModel(model: unknown, index: number, ctx: string): {
|
|
274
|
+
type: string;
|
|
275
|
+
shortcode: string;
|
|
276
|
+
package: string | null;
|
|
277
|
+
} | null;
|
|
278
|
+
resolveEmbedded(itemsMap: any, actorId: any, type: any, shortcode: any, overlay: any, indexKey: any, ctx: any, { fmKey, modelPackage }?: {
|
|
279
|
+
modelPackage?: null | undefined;
|
|
280
|
+
}): any;
|
|
201
281
|
#private;
|
|
202
282
|
}
|
|
203
283
|
import { BasePackCompiler } from "./base-compiler.mjs";
|
|
@@ -37,11 +37,29 @@ export function isAddressSegment(value: unknown): boolean;
|
|
|
37
37
|
* @module
|
|
38
38
|
*/
|
|
39
39
|
/**
|
|
40
|
-
* The shape every address segment must match: ASCII letters and
|
|
40
|
+
* The shape every address segment must match: **lowercase** ASCII letters and
|
|
41
|
+
* digits only.
|
|
41
42
|
*
|
|
42
|
-
* Case
|
|
43
|
-
*
|
|
44
|
-
*
|
|
43
|
+
* Case *was* deliberately unconstrained, on the reasoning that case has no
|
|
44
|
+
* bearing on the separator — which is true, and beside the point (#340).
|
|
45
|
+
*
|
|
46
|
+
* **Two names that differ only in case are two names nobody can tell apart.** A
|
|
47
|
+
* shortcode is how a person names a thing when writing a reference —
|
|
48
|
+
* `model: weapongear-dgr`, `[[skill-melee|…]]` — and `Dgr` beside `dgr` is a
|
|
49
|
+
* distinction you cannot say out loud and can only see by looking twice.
|
|
50
|
+
*
|
|
51
|
+
* The toolchain had already half-decided it: {@link canonicalKey} lowercases the
|
|
52
|
+
* address it builds, so a note declaring `Clb` published
|
|
53
|
+
* `sohl-sohl-weapongear-clb` and its `_id` derived from that. The authored name
|
|
54
|
+
* and its address disagreed, and everything downstream keys on the address —
|
|
55
|
+
* which left two notes differing only in case sharing one address, one `_id` and
|
|
56
|
+
* one URL, with nothing to report it. It also forced two exceptions elsewhere:
|
|
57
|
+
* #336 had to exempt the shortcode from the lowercase rule it pinned on every
|
|
58
|
+
* other segment, and #346 had to fold the shortcode's case in the item catalogue
|
|
59
|
+
* because an address is lowercased when read.
|
|
60
|
+
*
|
|
61
|
+
* One case, one spelling, no exceptions. Every tree already complies but two,
|
|
62
|
+
* and nothing in any of them collides when folded.
|
|
45
63
|
*
|
|
46
64
|
* @type {RegExp}
|
|
47
65
|
*/
|
|
@@ -99,14 +99,42 @@ export class BasePackCompiler {
|
|
|
99
99
|
* @type {boolean}
|
|
100
100
|
*/
|
|
101
101
|
static requiresSystemBlock: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* The **art fields** this pass reads off a note and writes onto its
|
|
104
|
+
* document — `img`, `portrait`, whichever of them reaches the output.
|
|
105
|
+
*
|
|
106
|
+
* Empty by default, and every shipped pass states its own, for the reason
|
|
107
|
+
* {@link BasePackCompiler.readsPackOutputOf} does: the fact belongs to the
|
|
108
|
+
* class that does the writing, and a second list of it somewhere else is a
|
|
109
|
+
* list free to disagree with what is actually emitted.
|
|
110
|
+
*
|
|
111
|
+
* The reader is the frontmatter lint. `img` is a *shared top-level* field —
|
|
112
|
+
* legal on every note whatever its type, because
|
|
113
|
+
* `BLOCK_DOCUMENT_PROPERTIES` maps it onto `document.img` — so a note whose
|
|
114
|
+
* document has no such property authors it, validates, compiles, and loses
|
|
115
|
+
* the value with nothing said. That is #349: `Parrot` in `sohl-thalorna`
|
|
116
|
+
* had declared `img:` since long before the art rule existed and compiled
|
|
117
|
+
* `img: null` exactly as a note declaring nothing does. Naming the fields
|
|
118
|
+
* here is what lets the lint tell an inert key from a live one.
|
|
119
|
+
*
|
|
120
|
+
* A pass that emits art **anywhere** in its document declares it, not only
|
|
121
|
+
* one that writes a top-level `img`: the scenes pass puts the path on the
|
|
122
|
+
* scene's background rather than on a property called `img`, and the value
|
|
123
|
+
* is no less live for it. The question this answers is whether the authored
|
|
124
|
+
* path reaches the output at all.
|
|
125
|
+
*
|
|
126
|
+
* @type {readonly string[]}
|
|
127
|
+
*/
|
|
128
|
+
static emitsArt: readonly string[];
|
|
102
129
|
/**
|
|
103
130
|
* @param {object} options
|
|
104
131
|
* @param {string} options.contentBase - Root of the content tree.
|
|
105
132
|
* @param {string} options.dest - Where this pass writes its JSON.
|
|
106
133
|
* @param {readonly string[]} options.skipDirectories - Directories the walk
|
|
107
134
|
* never descends into. Required: see {@link assertStatedScope}.
|
|
108
|
-
* @param {(
|
|
109
|
-
* Resolves a `
|
|
135
|
+
* @param {(address: string|null) => string|null} [options.folderResolver] -
|
|
136
|
+
* Resolves a `packFolder` — a folder note's address — to the Foundry
|
|
137
|
+
* folder id it materialises as in this pack (#255, #257).
|
|
110
138
|
* @param {string} [options.packName] - The pack this pass writes.
|
|
111
139
|
* @param {string} [options.docType] - The Foundry document type it holds.
|
|
112
140
|
* @param {{resolve: Function}} [options.router] - The pack router. Omit it
|
|
@@ -118,7 +146,7 @@ export class BasePackCompiler {
|
|
|
118
146
|
contentBase: string;
|
|
119
147
|
dest: string;
|
|
120
148
|
skipDirectories: readonly string[];
|
|
121
|
-
folderResolver?: ((
|
|
149
|
+
folderResolver?: ((address: string | null) => string | null) | undefined;
|
|
122
150
|
packName?: string | undefined;
|
|
123
151
|
docType?: string | undefined;
|
|
124
152
|
router?: {
|
|
@@ -236,6 +264,29 @@ export class BasePackCompiler {
|
|
|
236
264
|
* the note's own file can be read.
|
|
237
265
|
*/
|
|
238
266
|
eligibleFor(fm: object): boolean;
|
|
267
|
+
/**
|
|
268
|
+
* A refusal only this pass can make, because its subject is the note's
|
|
269
|
+
* **type** (#330).
|
|
270
|
+
*
|
|
271
|
+
* The `assertNo*Field` family above it in the walk is type-agnostic by
|
|
272
|
+
* construction: it runs before `selects`, so that a note declaring a
|
|
273
|
+
* retired field is answered whichever pass would have claimed it. A rule
|
|
274
|
+
* about what a *`trauma`* may write cannot live there — it needs the type's
|
|
275
|
+
* field declaration, which only the pass that compiles the type can reach.
|
|
276
|
+
*
|
|
277
|
+
* So it is a hook, called once the note is known to be this pass's, and its
|
|
278
|
+
* throw is counted and located exactly as the family's is: the note is
|
|
279
|
+
* declined rather than skipped, and the build fails naming the line.
|
|
280
|
+
*
|
|
281
|
+
* The default refuses nothing, which is the honest position for a pass
|
|
282
|
+
* whose documents have no schema to have opinions about.
|
|
283
|
+
*
|
|
284
|
+
* @param {object} fm - The note's frontmatter.
|
|
285
|
+
* @returns {void}
|
|
286
|
+
* @throws {Error} When the note authors something its type forbids. The
|
|
287
|
+
* error may carry a `position` for the diagnostic.
|
|
288
|
+
*/
|
|
289
|
+
assertAuthorable(fm: object): void;
|
|
239
290
|
/**
|
|
240
291
|
* Whether this pass claims a note. **Required.**
|
|
241
292
|
*
|
|
@@ -113,6 +113,70 @@ export function packageAddress(fm: object): string;
|
|
|
113
113
|
* @returns {string} `package-system-type-shortcode`, lowercased.
|
|
114
114
|
*/
|
|
115
115
|
export function canonicalKey(pkg: string, system: string, type: string, shortcode: string): string;
|
|
116
|
+
/**
|
|
117
|
+
* Which system a frontmatter key path is written under.
|
|
118
|
+
*
|
|
119
|
+
* The **enclosing system block** decides, at any depth within it, and nothing
|
|
120
|
+
* else does: `sohl.items[3].model` and `sohl.system.body.structure` are both
|
|
121
|
+
* `sohl` because both sit under `sohl:`. Everywhere else is {@link NO_SYSTEM} —
|
|
122
|
+
* top-level frontmatter, the shared `data:` container, and body prose, which has
|
|
123
|
+
* no key path at all and passes `undefined`.
|
|
124
|
+
*
|
|
125
|
+
* It is the block rather than the field, so a `WikiLink` field needs no opinion
|
|
126
|
+
* about systems and no per-field table has to be kept in step with the schema.
|
|
127
|
+
*
|
|
128
|
+
* The first segment must **be** a declared system, not merely look like one:
|
|
129
|
+
* `sohlish.items` is a key called `sohlish`, and `notes.sohl.thing` names no
|
|
130
|
+
* block at all.
|
|
131
|
+
*
|
|
132
|
+
* @param {string} [keyPath] - The dotted frontmatter key path, or `undefined`
|
|
133
|
+
* for body prose.
|
|
134
|
+
* @returns {string} The system id, or `none`.
|
|
135
|
+
*/
|
|
136
|
+
export function blockSystem(keyPath?: string): string;
|
|
137
|
+
/**
|
|
138
|
+
* Expand a written address to the one canonical address it names.
|
|
139
|
+
*
|
|
140
|
+
* **An omitted segment defaults from where the link is written** (#336) — it is
|
|
141
|
+
* not a wildcard, and resolution is not a search. Package omitted means the
|
|
142
|
+
* citing note's own; system omitted means {@link blockSystem} of the key path it
|
|
143
|
+
* was written under. So every short form has exactly one expansion, computed
|
|
144
|
+
* before anything is looked up, and there is no candidate set to disambiguate.
|
|
145
|
+
*
|
|
146
|
+
* **Under `none`, a system-bearing type addresses its documentation journal.**
|
|
147
|
+
* A note's `none` address *is* its `doc<type>` entry — the Item is the one with
|
|
148
|
+
* a system — so a prose `[[affiliation-sirvadar|…]]` names the page, which is
|
|
149
|
+
* almost always what prose means. A link that means the Item states the system
|
|
150
|
+
* and gets it. This is the defaulting rule applied, not an exception carved out
|
|
151
|
+
* of it.
|
|
152
|
+
*
|
|
153
|
+
* **Only a type whose own document carries a system is redirected.** A `macro`
|
|
154
|
+
* and the map types have documentation journals too, but their own documents
|
|
155
|
+
* are core ones and already live at `none` — so `<pkg>-none-macro-x` names the
|
|
156
|
+
* Macro and `<pkg>-none-docmacro-x` its journal, two live addresses that the
|
|
157
|
+
* redirect would collapse into one. The test is the note type's own system,
|
|
158
|
+
* not merely whether it has a doc entry.
|
|
159
|
+
*
|
|
160
|
+
* A `doc<type>` written explicitly is `none` **wherever** it appears, even
|
|
161
|
+
* inside a system block: no game system defines a JournalEntry, so there is no
|
|
162
|
+
* other system for one to belong to.
|
|
163
|
+
*
|
|
164
|
+
* @param {{type: string, shortcode: string, package?: string, system?: string,
|
|
165
|
+
* itemDoc?: boolean}} read - A qualifier, as `readQualifier` returns one.
|
|
166
|
+
* @param {{package: string, system?: string}} where - The citing context: the
|
|
167
|
+
* tree's own content package, and the system of the block the link sits in.
|
|
168
|
+
* @returns {string} The canonical `package-system-type-shortcode`.
|
|
169
|
+
*/
|
|
170
|
+
export function expandAddress(read: {
|
|
171
|
+
type: string;
|
|
172
|
+
shortcode: string;
|
|
173
|
+
package?: string;
|
|
174
|
+
system?: string;
|
|
175
|
+
itemDoc?: boolean;
|
|
176
|
+
}, where: {
|
|
177
|
+
package: string;
|
|
178
|
+
system?: string;
|
|
179
|
+
}): string;
|
|
116
180
|
/**
|
|
117
181
|
* Reads a canonical key back into its parts.
|
|
118
182
|
*
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier 1 — the letters, and the two whitespace characters a file is made of.
|
|
3
|
+
*
|
|
4
|
+
* The Latin-1 range is split around `U+00D7` and `U+00F7` deliberately: `×` and
|
|
5
|
+
* `÷` sit inside the letter block but are operators, and they are admitted
|
|
6
|
+
* below in Tier 3 on their own merits rather than smuggled in as letters.
|
|
7
|
+
*
|
|
8
|
+
* @param {number} cp - A Unicode code point.
|
|
9
|
+
* @returns {boolean} Whether Tier 1 admits it.
|
|
10
|
+
*/
|
|
11
|
+
export function isLetterTier(cp: number): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Whether the charset admits a code point anywhere in a note.
|
|
14
|
+
*
|
|
15
|
+
* @param {number} cp - A Unicode code point.
|
|
16
|
+
* @returns {boolean} Whether it is allowed outside a code fence.
|
|
17
|
+
*/
|
|
18
|
+
export function isAllowedCodePoint(cp: number): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Whether a code point is diagram furniture, admitted inside a fence only.
|
|
21
|
+
*
|
|
22
|
+
* A fenced block is set in the mono face, and the mono face is not the book
|
|
23
|
+
* face — so the question "does the text font have this" is the wrong question
|
|
24
|
+
* to ask about a character in an ASCII-art org chart. All three ranges were
|
|
25
|
+
* confirmed present in DejaVu Sans Mono, the mono face Typst embeds.
|
|
26
|
+
*
|
|
27
|
+
* @param {number} cp - A Unicode code point.
|
|
28
|
+
* @returns {boolean} Whether a fence may carry it.
|
|
29
|
+
*/
|
|
30
|
+
export function isDiagramCodePoint(cp: number): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* The reason a code point is refused.
|
|
33
|
+
*
|
|
34
|
+
* @param {number} cp - A Unicode code point.
|
|
35
|
+
* @returns {string} A clause naming what it is and what to do instead.
|
|
36
|
+
*/
|
|
37
|
+
export function refusalFor(cp: number): string;
|
|
38
|
+
/**
|
|
39
|
+
* Every non-NFC run in a string, with the composed form it should have been.
|
|
40
|
+
*
|
|
41
|
+
* Reported as **runs** rather than as bare combining marks, because `y` plus
|
|
42
|
+
* `U+0301` is one authoring mistake and pointing at the accent alone would name
|
|
43
|
+
* the half the author did not type.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} text - File contents.
|
|
46
|
+
* @returns {Array<{sequence: string, composed: string, index: number}>} Each
|
|
47
|
+
* offending run, in the order it appears.
|
|
48
|
+
*/
|
|
49
|
+
export function decomposedRuns(text: string): Array<{
|
|
50
|
+
sequence: string;
|
|
51
|
+
composed: string;
|
|
52
|
+
index: number;
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* Check one file's text against the charset and the normalization rule.
|
|
56
|
+
*
|
|
57
|
+
* Findings are **deduplicated per character per line**: a 60-cell table of
|
|
58
|
+
* `━` is one mistake made once, and sixty findings would bury the other
|
|
59
|
+
* fifty-nine things wrong with the tree.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} text - The file's contents.
|
|
62
|
+
* @param {string} file - Path to report, relative to the tree.
|
|
63
|
+
* @returns {Array<{file: string, line: number, column: number,
|
|
64
|
+
* severity: "error", message: string}>} What is wrong, in file order.
|
|
65
|
+
*/
|
|
66
|
+
export function checkText(text: string, file: string): Array<{
|
|
67
|
+
file: string;
|
|
68
|
+
line: number;
|
|
69
|
+
column: number;
|
|
70
|
+
severity: "error";
|
|
71
|
+
message: string;
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Walk a content tree and check every authored file in it.
|
|
75
|
+
*
|
|
76
|
+
* Dot-directories are skipped: `.obsidian` carries editor state, and a plugin
|
|
77
|
+
* manifest's CRLF line endings are not this tree's prose. That is not a
|
|
78
|
+
* theoretical exclusion — it was the first thing a run over `sohl-thalorna`
|
|
79
|
+
* reported before the skip existed.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} contentBase - Root of the content tree.
|
|
82
|
+
* @param {object} [opts]
|
|
83
|
+
* @param {readonly string[]} [opts.skipDirectories] - Directory names to ignore
|
|
84
|
+
* in addition to the dot-directories always skipped.
|
|
85
|
+
* @param {readonly string[]} [opts.extensions] - File extensions to read.
|
|
86
|
+
* @returns {{findings: Array<{file: string, line: number, column: number,
|
|
87
|
+
* severity: "error", message: string}>, files: number}} The findings, and how
|
|
88
|
+
* many files produced them.
|
|
89
|
+
*/
|
|
90
|
+
export function lintContentCharset(contentBase: string, { skipDirectories, extensions }?: {
|
|
91
|
+
skipDirectories?: readonly string[] | undefined;
|
|
92
|
+
extensions?: readonly string[] | undefined;
|
|
93
|
+
}): {
|
|
94
|
+
findings: Array<{
|
|
95
|
+
file: string;
|
|
96
|
+
line: number;
|
|
97
|
+
column: number;
|
|
98
|
+
severity: "error";
|
|
99
|
+
message: string;
|
|
100
|
+
}>;
|
|
101
|
+
files: number;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Tier 2 — typography, enumerated one codepoint at a time.
|
|
105
|
+
*
|
|
106
|
+
* **Not the General Punctuation block.** `U+2000`–`U+206F` carries `U+200B`
|
|
107
|
+
* ZERO WIDTH SPACE, the `U+200E`/`U+200F` direction marks, the `U+2028`/`U+2029`
|
|
108
|
+
* separators, the `U+202A`–`U+202E` bidi overrides and `U+2060` WORD JOINER —
|
|
109
|
+
* precisely the invisibles this charset exists to refuse. Admitting the block
|
|
110
|
+
* to reach the em dash would admit all of them, so the ten that are wanted are
|
|
111
|
+
* named and the block is not.
|
|
112
|
+
*
|
|
113
|
+
* @type {ReadonlySet<number>}
|
|
114
|
+
*/
|
|
115
|
+
export const TYPOGRAPHY: ReadonlySet<number>;
|
|
116
|
+
/**
|
|
117
|
+
* Tier 3 — the notation the rules and price tables are written in.
|
|
118
|
+
*
|
|
119
|
+
* Every one of these is carried by seven or eight of the eight probed faces, so
|
|
120
|
+
* the tier costs nothing in font freedom. It is a separate tier from the
|
|
121
|
+
* typography above only because it is a separate argument: these earn their
|
|
122
|
+
* place by being *needed* — a Shock threshold reads `≥ 10`, a wall is `10′ ×
|
|
123
|
+
* 11′` — where the typography earns it by being unavoidable.
|
|
124
|
+
*
|
|
125
|
+
* @type {ReadonlySet<number>}
|
|
126
|
+
*/
|
|
127
|
+
export const NOTATION: ReadonlySet<number>;
|
|
@@ -40,6 +40,9 @@ export const CONTENT_FORMAT_PATH: string;
|
|
|
40
40
|
* property — what a note actually writes. `appearance.eye_color` is authored
|
|
41
41
|
* as `appearance`, so that is the key recorded.
|
|
42
42
|
* @property {Set<string>} dataPaths - The declared paths, whole.
|
|
43
|
+
* @property {string[]} subTypes - The `subType` values the section enumerates,
|
|
44
|
+
* in document order — empty when it states none, which is the ordinary case
|
|
45
|
+
* for a type that has no `subType` at all.
|
|
43
46
|
*/
|
|
44
47
|
/**
|
|
45
48
|
* One `system.*` target the specification names for one note type.
|
|
@@ -99,6 +102,12 @@ export type TypeSpec = {
|
|
|
99
102
|
* - The declared paths, whole.
|
|
100
103
|
*/
|
|
101
104
|
dataPaths: Set<string>;
|
|
105
|
+
/**
|
|
106
|
+
* - The `subType` values the section enumerates,
|
|
107
|
+
* in document order — empty when it states none, which is the ordinary case
|
|
108
|
+
* for a type that has no `subType` at all.
|
|
109
|
+
*/
|
|
110
|
+
subTypes: string[];
|
|
102
111
|
};
|
|
103
112
|
/**
|
|
104
113
|
* One `system.*` target the specification names for one note type.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Look one name up.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} name - The name written between the colons, without `icon-`.
|
|
5
|
+
* @param {Record<string, object>} [registry] - Defaults to {@link DEFAULT_ICONS}.
|
|
6
|
+
* @returns {{style: string, icon: string, label: string}|null} The entry, or
|
|
7
|
+
* `null` when the registry does not declare it.
|
|
8
|
+
*/
|
|
9
|
+
export function resolveIcon(name: string, registry?: Record<string, object>): {
|
|
10
|
+
style: string;
|
|
11
|
+
icon: string;
|
|
12
|
+
label: string;
|
|
13
|
+
} | null;
|
|
14
|
+
/**
|
|
15
|
+
* The HTML the journals and the website emit — what the system already renders.
|
|
16
|
+
*
|
|
17
|
+
* Carries an accessible name rather than `aria-hidden`. The system's own
|
|
18
|
+
* templates hide their icons because a labelled parent element speaks for them;
|
|
19
|
+
* an icon dropped into a sentence has no such parent, and "the ☆ toggles it"
|
|
20
|
+
* read aloud as "the toggles it" is a sentence with a hole in it.
|
|
21
|
+
*
|
|
22
|
+
* @param {{style: string, icon: string, label: string}} entry - A registry entry.
|
|
23
|
+
* @returns {string} An `<i>` element.
|
|
24
|
+
*/
|
|
25
|
+
export function iconHtml(entry: {
|
|
26
|
+
style: string;
|
|
27
|
+
icon: string;
|
|
28
|
+
label: string;
|
|
29
|
+
}): string;
|
|
30
|
+
/**
|
|
31
|
+
* Every icon a string names, in the order written.
|
|
32
|
+
*
|
|
33
|
+
* @param {string} text - Markdown source.
|
|
34
|
+
* @returns {Array<{name: string, index: number, raw: string}>} What it names.
|
|
35
|
+
*/
|
|
36
|
+
export function iconsIn(text: string): Array<{
|
|
37
|
+
name: string;
|
|
38
|
+
index: number;
|
|
39
|
+
raw: string;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* Report every icon a tree names that its registry does not declare.
|
|
43
|
+
*
|
|
44
|
+
* The whole point of a registry is that a typo is answerable, so this is the
|
|
45
|
+
* half that makes `:icon-stra:` a finding rather than three words of literal
|
|
46
|
+
* text nobody notices in a rendered page.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} text - The file's contents.
|
|
49
|
+
* @param {string} file - Path to report.
|
|
50
|
+
* @param {Record<string, object>} [registry] - Defaults to {@link DEFAULT_ICONS}.
|
|
51
|
+
* @returns {Array<{file: string, line: number, column: number,
|
|
52
|
+
* severity: "error", message: string}>} The unknown names.
|
|
53
|
+
*/
|
|
54
|
+
export function lintIcons(text: string, file: string, registry?: Record<string, object>): Array<{
|
|
55
|
+
file: string;
|
|
56
|
+
line: number;
|
|
57
|
+
column: number;
|
|
58
|
+
severity: "error";
|
|
59
|
+
message: string;
|
|
60
|
+
}>;
|
|
61
|
+
/**
|
|
62
|
+
* Refuse a registry that names a style Font Awesome Free does not ship.
|
|
63
|
+
*
|
|
64
|
+
* @param {Record<string, object>} registry - A package's icon table.
|
|
65
|
+
* @param {string} [where="icons"] - Where to say the fault is.
|
|
66
|
+
* @returns {Array<{severity: "error", message: string}>} What is wrong with it.
|
|
67
|
+
*/
|
|
68
|
+
export function checkIconRegistry(registry: Record<string, object>, where?: string): Array<{
|
|
69
|
+
severity: "error";
|
|
70
|
+
message: string;
|
|
71
|
+
}>;
|
|
72
|
+
/**
|
|
73
|
+
* Walk a content tree and report every icon name its registry does not declare.
|
|
74
|
+
*
|
|
75
|
+
* Its own walk rather than the charset check's, so both modules stay leaves
|
|
76
|
+
* with nothing imported between them. The cost is one extra pass over the tree,
|
|
77
|
+
* which is the cheaper half of a lint that already parses every note.
|
|
78
|
+
*
|
|
79
|
+
* @param {string} contentBase - Root of the content tree.
|
|
80
|
+
* @param {object} [opts]
|
|
81
|
+
* @param {readonly string[]} [opts.skipDirectories] - Directory names to ignore.
|
|
82
|
+
* @param {Record<string, object>} [opts.registry] - The package's icon table.
|
|
83
|
+
* @returns {{findings: Array<{file: string, line: number, column: number,
|
|
84
|
+
* severity: "error", message: string}>, files: number}} What it found.
|
|
85
|
+
*/
|
|
86
|
+
export function lintContentIcons(contentBase: string, { skipDirectories, registry }?: {
|
|
87
|
+
skipDirectories?: readonly string[] | undefined;
|
|
88
|
+
registry?: Record<string, object> | undefined;
|
|
89
|
+
}): {
|
|
90
|
+
findings: Array<{
|
|
91
|
+
file: string;
|
|
92
|
+
line: number;
|
|
93
|
+
column: number;
|
|
94
|
+
severity: "error";
|
|
95
|
+
message: string;
|
|
96
|
+
}>;
|
|
97
|
+
files: number;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* A markdown-it plugin rendering `:icon-name:` inline.
|
|
101
|
+
*
|
|
102
|
+
* An unknown name is left **exactly as written** rather than dropped. The name
|
|
103
|
+
* is reported by {@link lintIcons}, and a rendered page that still shows
|
|
104
|
+
* `:icon-stra:` is how the author finds it without reading a log.
|
|
105
|
+
*
|
|
106
|
+
* @param {Record<string, object>} [registry] - Defaults to {@link DEFAULT_ICONS}.
|
|
107
|
+
* @returns {(md: object) => void} A markdown-it plugin.
|
|
108
|
+
*/
|
|
109
|
+
export function iconPlugin(registry?: Record<string, object>): (md: object) => void;
|
|
110
|
+
/**
|
|
111
|
+
* The Font Awesome styles a registry entry may name.
|
|
112
|
+
*
|
|
113
|
+
* Free ships these three and no others, so a `light` or `duotone` entry would
|
|
114
|
+
* name a glyph the shipped font does not contain — refused here rather than
|
|
115
|
+
* discovered as a blank space in a printed book.
|
|
116
|
+
*
|
|
117
|
+
* @type {readonly string[]}
|
|
118
|
+
*/
|
|
119
|
+
export const ICON_STYLES: readonly string[];
|
|
120
|
+
/**
|
|
121
|
+
* The icons the user guide already depicts, under the names it should call them.
|
|
122
|
+
*
|
|
123
|
+
* Each entry was read off the interface it describes rather than invented: the
|
|
124
|
+
* `star`/`star-outline` pair is the filled and hollow star the mastery row and
|
|
125
|
+
* the improve flag draw, and `edit` is the pencil the formula editor opens
|
|
126
|
+
* from. The names are what a *writer* would reach for — `delete`, not
|
|
127
|
+
* `trash-can` — because the writer is the one typing them; the Font Awesome
|
|
128
|
+
* spelling is this table's business, not theirs.
|
|
129
|
+
*
|
|
130
|
+
* @type {Readonly<Record<string, {style: string, icon: string, label: string}>>}
|
|
131
|
+
*/
|
|
132
|
+
export const DEFAULT_ICONS: Readonly<Record<string, {
|
|
133
|
+
style: string;
|
|
134
|
+
icon: string;
|
|
135
|
+
label: string;
|
|
136
|
+
}>>;
|
|
137
|
+
/**
|
|
138
|
+
* The shape a note writes, and the one this module claims.
|
|
139
|
+
*
|
|
140
|
+
* The `icon-` prefix is what keeps it out of the way of an emoji shortcode: a
|
|
141
|
+
* surface that also renders `:smile:` can tell the two apart without a lookup,
|
|
142
|
+
* and a reader can tell what `:icon-star:` is without knowing this module
|
|
143
|
+
* exists. Names are lowercase, digits and hyphens — the charset an address
|
|
144
|
+
* segment already uses (#59), so nothing new has to be explained.
|
|
145
|
+
*
|
|
146
|
+
* Not `:name[content]`. That is remark-directive syntax, and this toolchain
|
|
147
|
+
* parses with markdown-it; a directive would render as its own literal text.
|
|
148
|
+
*
|
|
149
|
+
* @type {RegExp}
|
|
150
|
+
*/
|
|
151
|
+
export const ICON_PATTERN: RegExp;
|