@heroiclands/package-build 20.3.1 → 20.5.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 +351 -0
- package/CONTENT.md +322 -110
- package/README.md +21 -10
- package/bin/content-build.mjs +204 -92
- package/bin/package-build.mjs +48 -15
- package/config.mjs +6 -8
- package/content-config.mjs +518 -151
- package/docs/api.md +1352 -0
- package/docs/commands.md +1609 -0
- package/docs/configuration.md +1432 -0
- package/docs/content-format.md +73 -74
- package/docs/diagnostics.md +356 -0
- package/docs/getting-started.md +813 -0
- package/docs/project-setup.md +469 -0
- package/e2e.mjs +4 -4
- package/engine/actor-compiler.mjs +64 -61
- package/engine/address-charset.mjs +6 -6
- package/engine/address-diff.mjs +57 -52
- package/engine/anchored-sections.mjs +1 -1
- package/engine/anchors.mjs +1 -1
- package/engine/base-compiler.mjs +42 -38
- package/engine/bundle-notes.mjs +4 -4
- package/engine/bundles.mjs +17 -8
- package/engine/code-fences.mjs +3 -3
- package/engine/compendiums.mjs +8 -8
- package/engine/compile-corpus.mjs +4 -5
- package/engine/content-address.mjs +24 -24
- package/engine/content-charset.mjs +1 -1
- package/engine/content-format-check.mjs +24 -25
- package/engine/content-format.mjs +8 -8
- package/engine/content-html.mjs +202 -0
- package/engine/content-icons.mjs +196 -165
- package/engine/content-index.mjs +61 -40
- package/engine/content-links.mjs +74 -41
- package/engine/content-lint.mjs +64 -34
- package/engine/content-package.mjs +7 -9
- package/engine/content-slug.mjs +2 -2
- package/engine/content-tables.mjs +38 -34
- package/engine/content-tree.mjs +1 -1
- package/engine/derived-fields.mjs +174 -0
- package/engine/diagnostics.mjs +3 -4
- package/engine/document-subtypes.mjs +6 -7
- package/engine/field-reference.mjs +8 -8
- package/engine/field-spec.mjs +29 -30
- package/engine/folder-notes.mjs +18 -16
- package/engine/foreign-catalog.mjs +8 -9
- package/engine/foundry-entries.mjs +19 -20
- package/engine/frontmatter-lint.mjs +95 -103
- package/engine/frontmatter.mjs +11 -11
- package/engine/generate.mjs +44 -39
- package/engine/helpers.mjs +147 -84
- package/engine/homepage.mjs +20 -22
- package/engine/ids.mjs +15 -15
- package/engine/index-records.mjs +4 -4
- package/engine/index.mjs +26 -10
- package/engine/item-compiler.mjs +54 -29
- package/engine/item-docs.mjs +11 -13
- package/engine/item-registry.mjs +13 -13
- package/engine/journals.mjs +24 -15
- package/engine/macros.mjs +3 -3
- package/engine/map-notes.mjs +6 -6
- package/engine/metadata-index.mjs +5 -5
- package/engine/note-claims.mjs +51 -41
- package/engine/note-ids.mjs +5 -5
- package/engine/note-package.mjs +4 -4
- package/engine/note-renames.mjs +3 -3
- package/engine/note-schemas.mjs +3 -8
- package/engine/note-vocabulary.mjs +56 -54
- package/engine/pack-config.mjs +39 -25
- package/engine/pack-router.mjs +0 -0
- package/engine/pdf-build.mjs +464 -0
- package/engine/pdf-fonts.mjs +420 -0
- package/engine/pdf-render.mjs +798 -0
- package/engine/pdf-toc.mjs +525 -0
- package/engine/prose-config.mjs +9 -9
- package/engine/prose-lint.mjs +12 -13
- package/engine/region-events.mjs +2 -2
- package/engine/retired-fields.mjs +24 -22
- package/engine/runtime-only-fields.mjs +1 -1
- package/engine/scene-levels.mjs +1 -1
- package/engine/scenes.mjs +33 -25
- package/engine/schema-check.mjs +21 -21
- package/engine/schema-extract.mjs +1 -1
- package/engine/site-build.mjs +66 -50
- package/engine/site-index.mjs +17 -18
- package/engine/sql-tables.mjs +21 -13
- package/engine/subtype-registry.mjs +5 -4
- package/engine/system-block.mjs +18 -19
- package/engine/systems.mjs +2 -2
- package/engine/web-wikilinks.mjs +41 -38
- package/engine/wikilink-syntax.mjs +16 -16
- package/engine/wikilinks.mjs +40 -42
- package/hm3/actors.mjs +30 -22
- package/hm3/document-subtypes.mjs +5 -5
- package/hm3/index.mjs +2 -2
- package/hm3/item-builders.mjs +1 -1
- package/hm3/item-fields.mjs +3 -3
- package/hm3/items.mjs +28 -8
- package/hm3/template-priority.mjs +2 -2
- package/lang.mjs +3 -3
- package/manifest.mjs +17 -20
- package/package.json +1 -2
- package/release.mjs +66 -6
- package/sohl/actors.mjs +36 -28
- package/sohl/affiliation-standings.mjs +2 -2
- package/sohl/being-info.mjs +5 -5
- package/sohl/default-item-art.mjs +5 -5
- package/sohl/document-subtypes.mjs +5 -5
- package/sohl/index.mjs +3 -3
- package/sohl/item-builders.mjs +7 -7
- package/sohl/item-fields.mjs +8 -9
- package/sohl/items.mjs +28 -6
- package/sohl/kb-passes.mjs +5 -5
- package/sohl/note-schemas.mjs +12 -12
- package/sohl/skill-base.mjs +1 -1
- package/types/content-config.d.mts +91 -39
- package/types/e2e.d.mts +3 -3
- package/types/engine/actor-compiler.d.mts +45 -40
- package/types/engine/address-charset.d.mts +6 -6
- package/types/engine/address-diff.d.mts +60 -6
- package/types/engine/base-compiler.d.mts +27 -21
- package/types/engine/bundle-notes.d.mts +3 -3
- package/types/engine/bundles.d.mts +10 -1
- package/types/engine/code-fences.d.mts +3 -3
- package/types/engine/compendiums.d.mts +3 -3
- package/types/engine/compile-corpus.d.mts +1 -1
- package/types/engine/content-address.d.mts +20 -20
- package/types/engine/content-format-check.d.mts +6 -6
- package/types/engine/content-format.d.mts +2 -2
- package/types/engine/content-html.d.mts +78 -0
- package/types/engine/content-icons.d.mts +139 -92
- package/types/engine/content-index.d.mts +59 -15
- package/types/engine/content-links.d.mts +8 -8
- package/types/engine/content-lint.d.mts +8 -6
- package/types/engine/content-package.d.mts +6 -8
- package/types/engine/content-tables.d.mts +49 -18
- package/types/engine/derived-fields.d.mts +101 -0
- package/types/engine/diagnostics.d.mts +2 -2
- package/types/engine/document-subtypes.d.mts +3 -3
- package/types/engine/field-spec.d.mts +37 -39
- package/types/engine/folder-notes.d.mts +4 -5
- package/types/engine/foreign-catalog.d.mts +3 -3
- package/types/engine/foundry-entries.d.mts +6 -7
- package/types/engine/frontmatter-lint.d.mts +10 -9
- package/types/engine/frontmatter.d.mts +7 -7
- package/types/engine/generate.d.mts +7 -7
- package/types/engine/helpers.d.mts +128 -56
- package/types/engine/homepage.d.mts +16 -18
- package/types/engine/ids.d.mts +13 -13
- package/types/engine/index-records.d.mts +3 -3
- package/types/engine/index.d.mts +6 -0
- package/types/engine/item-compiler.d.mts +21 -5
- package/types/engine/item-docs.d.mts +2 -2
- package/types/engine/item-registry.d.mts +6 -6
- package/types/engine/journals.d.mts +12 -3
- package/types/engine/map-notes.d.mts +2 -2
- package/types/engine/metadata-index.d.mts +4 -4
- package/types/engine/note-claims.d.mts +30 -22
- package/types/engine/note-ids.d.mts +4 -4
- package/types/engine/note-package.d.mts +1 -1
- package/types/engine/note-renames.d.mts +3 -3
- package/types/engine/note-vocabulary.d.mts +31 -203
- package/types/engine/pack-config.d.mts +7 -7
- package/types/engine/pack-router.d.mts +1 -1
- package/types/engine/pdf-build.d.mts +42 -0
- package/types/engine/pdf-fonts.d.mts +30 -0
- package/types/engine/pdf-render.d.mts +144 -0
- package/types/engine/pdf-toc.d.mts +114 -0
- package/types/engine/prose-config.d.mts +9 -9
- package/types/engine/prose-lint.d.mts +3 -4
- package/types/engine/region-events.d.mts +2 -2
- package/types/engine/retired-fields.d.mts +10 -9
- package/types/engine/scenes.d.mts +10 -1
- package/types/engine/schema-check.d.mts +13 -13
- package/types/engine/site-build.d.mts +52 -25
- package/types/engine/site-index.d.mts +3 -4
- package/types/engine/sql-tables.d.mts +11 -5
- package/types/engine/subtype-registry.d.mts +3 -3
- package/types/engine/system-block.d.mts +3 -3
- package/types/engine/web-wikilinks.d.mts +7 -7
- package/types/engine/wikilink-syntax.d.mts +17 -17
- package/types/engine/wikilinks.d.mts +14 -14
- package/types/hm3/actors.d.mts +9 -1
- package/types/hm3/document-subtypes.d.mts +1 -1
- package/types/hm3/items.d.mts +9 -2
- package/types/hm3/template-priority.d.mts +1 -1
- package/types/manifest.d.mts +8 -8
- package/types/release.d.mts +15 -4
- package/types/sohl/actors.d.mts +12 -4
- package/types/sohl/affiliation-standings.d.mts +2 -2
- package/types/sohl/being-info.d.mts +2 -2
- package/types/sohl/document-subtypes.d.mts +1 -1
- package/types/sohl/items.d.mts +9 -2
- package/types/sohl/note-schemas.d.mts +1 -1
- package/MIGRATING.md +0 -608
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* **The note-type → document-subtype map** — the mechanism that stops a build
|
|
16
|
-
* inferring a Foundry document's subtype from the markdown note's `type
|
|
17
|
-
* (#79).
|
|
16
|
+
* inferring a Foundry document's subtype from the markdown note's `type`.
|
|
18
17
|
*
|
|
19
18
|
* A note's `type` and the subtype of the document it compiles into are two
|
|
20
19
|
* vocabularies, and until now they were the same identifier for one reason
|
|
@@ -26,7 +25,7 @@
|
|
|
26
25
|
* risk with **one** system, not merely with two.
|
|
27
26
|
*
|
|
28
27
|
* **The mechanism is here; the declaration is the system's.** That is the
|
|
29
|
-
* `engine/` ÷ `sohl/` line everywhere else in this package
|
|
28
|
+
* `engine/` ÷ `sohl/` line everywhere else in this package: note-format
|
|
30
29
|
* knowledge here, game-system knowledge there. `sohl/document-subtypes.mjs`
|
|
31
30
|
* declares SoHL's own map, *including its identity rows* — the coincidence of
|
|
32
31
|
* names may never stand in for a mapping, so `skill` → `skill` is written out
|
|
@@ -59,7 +58,7 @@
|
|
|
59
58
|
* *compiled documents*, which carry only the subtype. {@link referencedSubtype}
|
|
60
59
|
* is the translation for that side, and the reason it is separate from
|
|
61
60
|
* {@link documentSubtype} is that a reference has no frontmatter of its own to
|
|
62
|
-
* read a discriminator from
|
|
61
|
+
* read a discriminator from.
|
|
63
62
|
*
|
|
64
63
|
* @module
|
|
65
64
|
*/
|
|
@@ -207,7 +206,7 @@ function frozenRow(system, noteType, row) {
|
|
|
207
206
|
* {@link mapsNoteType}, {@link noteTypesFor}'s consumers and
|
|
208
207
|
* {@link systemOf} all arrive here. A note still spelling `armorgear` finds the
|
|
209
208
|
* `armor` row, compiles into the `armorgear` document it always did, and keeps
|
|
210
|
-
* the address it publishes at; only the *report* tells it to move
|
|
209
|
+
* the address it publishes at; only the *report* tells it to move.
|
|
211
210
|
*
|
|
212
211
|
* @param {DocumentSubtypeMap} map - The system's map.
|
|
213
212
|
* @param {string|undefined} noteType - The note's declared `type`.
|
|
@@ -317,7 +316,7 @@ export function documentSubtype(map, noteType, fm, { file, absPath } = {}) {
|
|
|
317
316
|
*/
|
|
318
317
|
|
|
319
318
|
/**
|
|
320
|
-
* The document subtype a `(type, shortcode)` **reference** addresses
|
|
319
|
+
* The document subtype a `(type, shortcode)` **reference** addresses.
|
|
321
320
|
*
|
|
322
321
|
* A being's frontmatter names each embedded item by the *note's* type — the
|
|
323
322
|
* vocabulary an author writes — while the predefined items it resolves against
|
|
@@ -354,7 +353,7 @@ export function documentSubtype(map, noteType, fm, { file, absPath } = {}) {
|
|
|
354
353
|
* retirement exists to stop.
|
|
355
354
|
*
|
|
356
355
|
* A **renamed** spelling is the opposite case and resolves normally, through
|
|
357
|
-
* the same {@link currentType} every other lookup goes through
|
|
356
|
+
* the same {@link currentType} every other lookup goes through. It has to
|
|
358
357
|
* be this side as much as the note's own `type:`: the overwhelming majority of
|
|
359
358
|
* the 31,000 occurrences of the old names are `(type, shortcode)` references
|
|
360
359
|
* inside a being's `items:` list, so a window that resolved notes but not
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* types, each with a shape, a default and a requiredness, is exactly the kind of
|
|
21
21
|
* prose that drifts silently from the code it describes. The declarations
|
|
22
22
|
* (`fields` on each `itemBuilders` entry) already *are* that table, so the page
|
|
23
|
-
* is rendered from them rather than transcribed
|
|
23
|
+
* is rendered from them rather than transcribed.
|
|
24
24
|
*
|
|
25
25
|
* **It ships here, not in the consumer.** Any repository can define an item
|
|
26
26
|
* type of its own, so "what frontmatter does this type accept?" is a question
|
|
@@ -104,7 +104,7 @@ function fieldTable(fields) {
|
|
|
104
104
|
`\`${field.name}\``,
|
|
105
105
|
cell(field.shape ?? "as authored"),
|
|
106
106
|
field.required ? "**yes**" : "no",
|
|
107
|
-
// Three different answers, and the third is not a value
|
|
107
|
+
// Three different answers, and the third is not a value. A
|
|
108
108
|
// required field has no default because omitting it fails the
|
|
109
109
|
// build; an `omitWhenAbsent` field has none because omitting it
|
|
110
110
|
// omits the *key*, leaving the data model to answer. Rendering
|
|
@@ -128,7 +128,7 @@ function fieldTable(fields) {
|
|
|
128
128
|
* the note level the fallback is off, and an author has no way to tell from the
|
|
129
129
|
* table — the field is there, the value is written, and the document ships the
|
|
130
130
|
* default. So the reason each such field declares is rendered beside its table
|
|
131
|
-
* rather than left in the source
|
|
131
|
+
* rather than left in the source.
|
|
132
132
|
*
|
|
133
133
|
* Below the table, not inside it: the reason is a sentence or two, and
|
|
134
134
|
* {@link padTable} pads every column to its widest cell, so a cell holding it
|
|
@@ -148,7 +148,7 @@ function sharedExemptions(fields) {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/**
|
|
151
|
-
* The fields of one type that a note may **never** write
|
|
151
|
+
* The fields of one type that a note may **never** write.
|
|
152
152
|
*
|
|
153
153
|
* A type's table lists what an author writes, and says nothing about the rest
|
|
154
154
|
* of its schema — which is right for a constant or a derived value, since
|
|
@@ -208,15 +208,15 @@ function workedExample(type, fields) {
|
|
|
208
208
|
`type: ${type}`,
|
|
209
209
|
"shortcode: xmpl",
|
|
210
210
|
// No `package:`. A note's package is the repository's configured
|
|
211
|
-
// `contentPackage`, and declaring the field is a build error
|
|
211
|
+
// `contentPackage`, and declaring the field is a build error —
|
|
212
212
|
// this example is the smallest note that compiles.
|
|
213
213
|
//
|
|
214
214
|
// No `id:` either, for the same reason it is not shown optional-first:
|
|
215
|
-
// a note's document `_id` derives from its canonical address
|
|
216
|
-
//
|
|
215
|
+
// a note's document `_id` derives from its canonical address, and the
|
|
216
|
+
// authored field is the escape hatch for keeping a
|
|
217
217
|
// document's identity across a shortcode rename, not part of the
|
|
218
218
|
// envelope. This block is the one an author copies as a template, so
|
|
219
|
-
// showing the field taught every note in the tree to write it
|
|
219
|
+
// showing the field taught every note in the tree to write it.
|
|
220
220
|
"sohl:",
|
|
221
221
|
" templatePriority: null",
|
|
222
222
|
];
|
package/engine/field-spec.mjs
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
* **The declaration is the builder, not a description of one.** A builder used
|
|
18
18
|
* to be an opaque function: the mapping from a note's `sohl:` frontmatter to
|
|
19
19
|
* the emitted `system` block existed only inside its body, so nothing could
|
|
20
|
-
* read it — not a documentation generator, not a validator, not a person
|
|
21
|
-
*
|
|
20
|
+
* read it — not a documentation generator, not a validator, not a person.
|
|
21
|
+
* The obvious repair, a table written *beside* the function, buys very
|
|
22
22
|
* little: two statements of one rule drift, and nothing notices.
|
|
23
23
|
*
|
|
24
24
|
* So the table is the only statement. {@link buildFromFields} turns a field
|
|
@@ -50,19 +50,19 @@ export { legacyKeyOf, retiredTopLevelKey, setPath };
|
|
|
50
50
|
/**
|
|
51
51
|
* @typedef {object} FieldSpec
|
|
52
52
|
* @property {string} to - Dotted path in the emitted `system` block — and,
|
|
53
|
-
*
|
|
53
|
+
* the path a note authors the field at directly:
|
|
54
54
|
* `<system>.system.<to>`.
|
|
55
55
|
* @property {string} [name] - The **shared, top-level property this field draws
|
|
56
56
|
* from** when the note authors no value at `<system>.system.<to>`. Dotted for
|
|
57
57
|
* a path into a shared container (`data.portrait`), which is now the ordinary
|
|
58
|
-
* case: `data:`
|
|
58
|
+
* case: `data:` puts every type-specific fact under one.
|
|
59
59
|
*
|
|
60
|
-
*
|
|
60
|
+
* Read as "frontmatter key under `sohl:`", that meaning is the
|
|
61
61
|
* degenerate case where the shared source and the system destination happen
|
|
62
62
|
* to share a name. They constantly do not — one shared `data.portrait` feeds
|
|
63
63
|
* `sohl.system.portrait` *and* `hm3.system.bioImage` — so the source is
|
|
64
|
-
* declared rather than matched by spelling
|
|
65
|
-
* still read, second, until
|
|
64
|
+
* declared rather than matched by spelling. The in-block position is
|
|
65
|
+
* still read, second, until the corpus moves off it — keyed on
|
|
66
66
|
* `legacyKey` where the two spellings differ.
|
|
67
67
|
*
|
|
68
68
|
* Absent means the value is not authored at all — see `value`.
|
|
@@ -70,8 +70,8 @@ export { legacyKeyOf, retiredTopLevelKey, setPath };
|
|
|
70
70
|
* the system block** — the second position of the resolution order — when
|
|
71
71
|
* that is not `name`. Absent, the position is keyed on `name`.
|
|
72
72
|
*
|
|
73
|
-
*
|
|
74
|
-
* shared source and its in-block key were the same word. `data:`
|
|
73
|
+
* As one property the two would hold only while a field's
|
|
74
|
+
* shared source and its in-block key were the same word. `data:` ended
|
|
75
75
|
* that: a shared source is a path into a container, so `data.species` and
|
|
76
76
|
* `species` name two different places and no single value reached both.
|
|
77
77
|
* `name: "species"` could not see `data.species`; `name: "data.species"`
|
|
@@ -87,7 +87,7 @@ export { legacyKeyOf, retiredTopLevelKey, setPath };
|
|
|
87
87
|
* @property {string} [topLevelMeans] - **What the note's top-level key of this
|
|
88
88
|
* name means instead** — declared only where it means something else, and
|
|
89
89
|
* stating it removes the shared top-level position from this field's
|
|
90
|
-
* resolution order
|
|
90
|
+
* resolution order.
|
|
91
91
|
*
|
|
92
92
|
* A field's `name` doubles as its identity and as the shared property it
|
|
93
93
|
* draws from, which is right wherever the two levels state the same quantity
|
|
@@ -114,7 +114,7 @@ export { legacyKeyOf, retiredTopLevelKey, setPath };
|
|
|
114
114
|
* if the two positions hold unrelated quantities then the *in-block* position
|
|
115
115
|
* is not the note-level field either, so a check about the note-level field
|
|
116
116
|
* reads past it. `engine/frontmatter-lint.mjs` resolves that through
|
|
117
|
-
* `collidingBlockKeys`.
|
|
117
|
+
* `collidingBlockKeys`. Reading it for the emitted field alone is
|
|
118
118
|
* how an affiliation's office style came to answer for its page heading.
|
|
119
119
|
* @property {string} [shape] - Human-readable shape, for documentation. Comes
|
|
120
120
|
* paired with `read` from one of the coercion constants below.
|
|
@@ -130,10 +130,9 @@ export { legacyKeyOf, retiredTopLevelKey, setPath };
|
|
|
130
130
|
* third step of {@link module:engine/system-block.resolveFieldValue}'s order,
|
|
131
131
|
* so the value resolves whichever region carries it. What it tells the
|
|
132
132
|
* author-facing surfaces is which of the two is the field's *home*, so a
|
|
133
|
-
* message about it names `img` rather than sending an author to `sohl.img
|
|
134
|
-
* (#142).
|
|
133
|
+
* message about it names `img` rather than sending an author to `sohl.img`.
|
|
135
134
|
* @property {"string"|"number"|"boolean"|"list"|"map"} [kind] - The value's
|
|
136
|
-
* shape, for the frontmatter linter
|
|
135
|
+
* shape, for the frontmatter linter. Distinct from `shape`, which is
|
|
137
136
|
* prose for a reader, and from `read`, which is what the compiler does: a
|
|
138
137
|
* field may declare `kind` without changing a byte of what it emits, and
|
|
139
138
|
* several do — `weight` is coerced leniently but is still a number, and
|
|
@@ -146,7 +145,7 @@ export { legacyKeyOf, retiredTopLevelKey, setPath };
|
|
|
146
145
|
* @property {any|((fm: object) => any)} [value] - For a field with no `name`:
|
|
147
146
|
* the constant, or a function deriving it from the frontmatter.
|
|
148
147
|
* @property {boolean} [omitWhenAbsent] - **The key is left out entirely when
|
|
149
|
-
* the note does not carry the field
|
|
148
|
+
* the note does not carry the field**, rather than written from a
|
|
150
149
|
* declared default.
|
|
151
150
|
*
|
|
152
151
|
* Every other field answers absence with a value: an unauthored `weight` is
|
|
@@ -181,7 +180,7 @@ export { legacyKeyOf, retiredTopLevelKey, setPath };
|
|
|
181
180
|
* which the generated reference states in the field's own row.
|
|
182
181
|
* @property {string} [runtimeOnly] - **What the field holds once play has
|
|
183
182
|
* started** — declared on a field the *document* writes for itself, which no
|
|
184
|
-
* note may author
|
|
183
|
+
* note may author.
|
|
185
184
|
*
|
|
186
185
|
* A schema declares plenty of fields a compiled document has no business
|
|
187
186
|
* carrying: an affliction's `onsetDate` is the world time its onset fired
|
|
@@ -295,7 +294,7 @@ export const BLANK_IS_DEFAULT = Object.freeze({
|
|
|
295
294
|
*
|
|
296
295
|
* **Only for a field that declares a `legacyKey`.** Every other field's
|
|
297
296
|
* in-block position is simply where it lives; reporting those would put a
|
|
298
|
-
* finding on every field of every note in every tree, which is
|
|
297
|
+
* finding on every field of every note in every tree, which is the corpus
|
|
299
298
|
* migration rather than a signal anyone could act on.
|
|
300
299
|
*
|
|
301
300
|
* @param {FieldSpec} field - The declaration.
|
|
@@ -309,7 +308,7 @@ export function readsLegacyKey(field, from) {
|
|
|
309
308
|
|
|
310
309
|
/**
|
|
311
310
|
* Whether a resolution read a field from the top-level key `data:` gathered it
|
|
312
|
-
* off — the shared level's retiring position
|
|
311
|
+
* off — the shared level's retiring position.
|
|
313
312
|
*
|
|
314
313
|
* {@link readsLegacyKey}'s sibling, and the same signal: a finding here counts
|
|
315
314
|
* one note still on the pre-`data:` spelling, so the sweep has something to
|
|
@@ -335,7 +334,7 @@ export function readsRetiredTopLevel(field, from) {
|
|
|
335
334
|
*
|
|
336
335
|
* The *position* is resolved by {@link resolveFieldValue} — `<system>.system`
|
|
337
336
|
* first, then the legacy in-block key, then the declared shared source, then
|
|
338
|
-
* the default
|
|
337
|
+
* the default. The **coercion** is applied here, once, wherever the value
|
|
339
338
|
* came from: a field's `read` is a statement about the field, not about where
|
|
340
339
|
* an author happened to write it, so `weight: "7"` reads as `7` at every one of
|
|
341
340
|
* those positions.
|
|
@@ -347,12 +346,12 @@ export function readsRetiredTopLevel(field, from) {
|
|
|
347
346
|
* against. The default is the one block every existing tree authors; a
|
|
348
347
|
* second system passes its own.
|
|
349
348
|
* @param {(field: FieldSpec) => void} [options.onLegacyKey] - Called with each
|
|
350
|
-
* field read from the position it is being swept off
|
|
349
|
+
* field read from the position it is being swept off. A callback
|
|
351
350
|
* rather than a returned list because the caller is a compiler, which already
|
|
352
351
|
* knows the note and how to locate a key in it; this module knows neither and
|
|
353
352
|
* would have to invent a finding shape to say so.
|
|
354
353
|
* @param {(field: FieldSpec) => void} [options.onRetiredTopLevel] - Called with
|
|
355
|
-
* each field read from the top-level key `data:` gathered it off
|
|
354
|
+
* each field read from the top-level key `data:` gathered it off. The
|
|
356
355
|
* shared level's counterpart to `onLegacyKey`, and a separate callback
|
|
357
356
|
* because it is a separate position: a note may have moved one of the two and
|
|
358
357
|
* not the other, and a caller that conflated them would tell its author to
|
|
@@ -370,7 +369,7 @@ export function readField(field, fm, options = {}) {
|
|
|
370
369
|
* every caller wants. A builder has one further question — *should the key be
|
|
371
370
|
* written at all* — and it cannot be answered from the value: `null` from a
|
|
372
371
|
* note and `null` from a declared default are the same value and opposite
|
|
373
|
-
* facts
|
|
372
|
+
* facts.
|
|
374
373
|
*
|
|
375
374
|
* So the position rides back beside the value, resolved **once**. The
|
|
376
375
|
* alternative is a builder that calls {@link resolveFieldValue} for the source
|
|
@@ -403,7 +402,7 @@ export function readFieldEntry(field, fm, { block = "sohl", onLegacyKey, onRetir
|
|
|
403
402
|
* The question {@link FieldSpec.omitWhenAbsent} turns on, asked of the
|
|
404
403
|
* *position* rather than of the value — which cannot answer it, since a
|
|
405
404
|
* declared `default: null` and an authored `null` are indistinguishable once
|
|
406
|
-
* the value is in hand
|
|
405
|
+
* the value is in hand.
|
|
407
406
|
*
|
|
408
407
|
* `undefined` counts as absent whatever position reported it, because writing
|
|
409
408
|
* the key then emits a value `JSON.stringify` drops — the key present in the
|
|
@@ -434,17 +433,17 @@ export function isAuthored(from, value) {
|
|
|
434
433
|
* systems declare the same shared source and different destinations.
|
|
435
434
|
* @param {(field: FieldSpec) => void} [options.onLegacyKey] - Passed through to
|
|
436
435
|
* {@link readField}: called with each field the note authored at the position
|
|
437
|
-
* it is being swept off
|
|
436
|
+
* it is being swept off.
|
|
438
437
|
* @param {(field: FieldSpec) => void} [options.onRetiredTopLevel] - Passed
|
|
439
438
|
* through to {@link readField}: called with each field the note authored at
|
|
440
|
-
* the top-level key `data:` gathered it off
|
|
439
|
+
* the top-level key `data:` gathered it off.
|
|
441
440
|
* @returns {(fm: object) => object} A `system`-block builder.
|
|
442
441
|
*/
|
|
443
442
|
export function buildFromFields(fields, { block = "sohl", onLegacyKey, onRetiredTopLevel } = {}) {
|
|
444
443
|
return function buildDeclaredSystem(fm) {
|
|
445
444
|
const out = {};
|
|
446
445
|
for (const field of fields) {
|
|
447
|
-
// A runtime-only field is not this builder's to write
|
|
446
|
+
// A runtime-only field is not this builder's to write. It is
|
|
448
447
|
// declared so that the path is *claimed* — so the verbatim
|
|
449
448
|
// passthrough leaves it alone and the refusal has a name — not so
|
|
450
449
|
// that a compile-time answer is invented for a question only play
|
|
@@ -457,8 +456,8 @@ export function buildFromFields(fields, { block = "sohl", onLegacyKey, onRetired
|
|
|
457
456
|
onLegacyKey,
|
|
458
457
|
onRetiredTopLevel,
|
|
459
458
|
});
|
|
460
|
-
// The other conditional row: a field whose *absence* is meaningful
|
|
461
|
-
//
|
|
459
|
+
// The other conditional row: a field whose *absence* is meaningful.
|
|
460
|
+
// Writing a declared default would answer a question the
|
|
462
461
|
// note did not ask — "this affliction's onset takes `null` days" —
|
|
463
462
|
// and would make the field's unset state indistinguishable from an
|
|
464
463
|
// authored one for every reader downstream. Omitting the key leaves
|
|
@@ -490,8 +489,8 @@ export function authoredFields(fields) {
|
|
|
490
489
|
* The fields of a declaration a note may **never** write.
|
|
491
490
|
*
|
|
492
491
|
* The complement of {@link authoredFields} in the direction that matters: those
|
|
493
|
-
* are the fields an author may write, these are the ones authoring is an error
|
|
494
|
-
*
|
|
492
|
+
* are the fields an author may write, these are the ones authoring is an error.
|
|
493
|
+
* Everything else in a declaration — a constant, a derived value — is
|
|
495
494
|
* simply not authored, which is a statement about the *builder* rather than
|
|
496
495
|
* about the author, and says nothing about what happens if a note writes the
|
|
497
496
|
* path anyway.
|
package/engine/folder-notes.mjs
CHANGED
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
*
|
|
17
17
|
* A `Folder` is a real Foundry document, and it was the last kind this package
|
|
18
18
|
* compiled from bespoke configuration — `*-folders.yaml`, five files per tree —
|
|
19
|
-
* rather than from a note. That
|
|
19
|
+
* rather than from a note. That is the one hole in the rule,
|
|
20
20
|
* *the compiler follows the index*: a pass cannot follow the index for things
|
|
21
|
-
* the index does not contain
|
|
21
|
+
* the index does not contain.
|
|
22
22
|
*
|
|
23
23
|
* Three things follow from a folder being a note, and each is a defect that
|
|
24
24
|
* becomes unrepresentable rather than a tidiness win:
|
|
@@ -26,14 +26,14 @@
|
|
|
26
26
|
* 1. **`parent` is an address**, resolved and checked like every other
|
|
27
27
|
* reference. A dangling parent stops being a special-cased
|
|
28
28
|
* `Unknown folder id` and becomes an ordinary dead-address finding.
|
|
29
|
-
* 2. **Where a folder materialises is derived from what references it
|
|
29
|
+
* 2. **Where a folder materialises is derived from what references it**.
|
|
30
30
|
* A documentation journal is filed beside the item it describes, which used
|
|
31
31
|
* to mean passing the *items* pack's folder id into the *journals* pack —
|
|
32
32
|
* verbatim, validated nowhere, and correct only where the two folder files
|
|
33
33
|
* happened to mirror each other. They did in one tree of three. With one
|
|
34
34
|
* folder note and one address there is no second file to disagree with the
|
|
35
35
|
* first, so a pack cannot fail to declare a folder something in it points at.
|
|
36
|
-
* 3. **The Foundry id is derived from the address
|
|
36
|
+
* 3. **The Foundry id is derived from the address**, the way a
|
|
37
37
|
* `JournalEntryPage` id is already hashed from its anchor — with an authored
|
|
38
38
|
* `id` still winning, so a world already holding these folders keeps
|
|
39
39
|
* resolving them.
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
*
|
|
45
45
|
* **A folder note carries no prose.** It is structure, not content, so it wants
|
|
46
46
|
* no documentation journal and takes no part in `docEntryTypes` — the one
|
|
47
|
-
* decision
|
|
47
|
+
* open decision, settled the way it recommended.
|
|
48
48
|
*
|
|
49
49
|
* @module
|
|
50
50
|
*/
|
|
@@ -54,7 +54,7 @@ import path from "node:path";
|
|
|
54
54
|
import log from "loglevel";
|
|
55
55
|
|
|
56
56
|
import { NO_SYSTEM, canonicalKey } from "./content-address.mjs";
|
|
57
|
-
import { isAddressSegment } from "./address-charset.mjs";
|
|
57
|
+
import { ADDRESS_SEGMENT_PATTERN, isAddressSegment } from "./address-charset.mjs";
|
|
58
58
|
import { makeId } from "./ids.mjs";
|
|
59
59
|
import { locateFrontmatterKey } from "./retired-fields.mjs";
|
|
60
60
|
|
|
@@ -109,7 +109,8 @@ export const DEFAULT_PARENT = "default";
|
|
|
109
109
|
* `parent` and `color` are `data:` properties, which is where the
|
|
110
110
|
* specification's `### type: folder` table puts them — the closed container,
|
|
111
111
|
* so a misspelled `colour` is a finding rather than a theme parameter. They are
|
|
112
|
-
* accepted at the top level too, because that is where
|
|
112
|
+
* accepted at the top level too, because that is where the canonical example
|
|
113
|
+
* writes
|
|
113
114
|
* them and an author following the issue rather than the specification should
|
|
114
115
|
* get a folder, not a silent default.
|
|
115
116
|
*
|
|
@@ -194,7 +195,7 @@ export function folderAddress(pkg, shortcode) {
|
|
|
194
195
|
* never run this build. Deriving it twice is how the index came to publish a
|
|
195
196
|
* plausible-looking id that resolved to nothing — the general id rule hashes a
|
|
196
197
|
* note's address under the `document` namespace, and a folder's is hashed under
|
|
197
|
-
* {@link FOLDER_ID_NAMESPACE}
|
|
198
|
+
* {@link FOLDER_ID_NAMESPACE}.
|
|
198
199
|
*
|
|
199
200
|
* An **authored `id` still wins**, and is applied by the caller: this is the
|
|
200
201
|
* derivation, not the resolution, so the pin rule stays stated once, where
|
|
@@ -212,8 +213,7 @@ export function folderDocId(pkg, shortcode) {
|
|
|
212
213
|
* Collect every folder note in a content tree.
|
|
213
214
|
*
|
|
214
215
|
* The walk is the caller's to supply, so this stays testable without a tree on
|
|
215
|
-
* disk and so one build cannot disagree with another about what the corpus is
|
|
216
|
-
* (#243).
|
|
216
|
+
* disk and so one build cannot disagree with another about what the corpus is.
|
|
217
217
|
*
|
|
218
218
|
* @param {Iterable<{frontmatter: object|null, absPath: string}>} notes - As
|
|
219
219
|
* yielded by `walkMarkdownTree`.
|
|
@@ -237,12 +237,14 @@ export function collectFolderNotes(notes, pkg) {
|
|
|
237
237
|
// The charset rule is load-bearing here rather than tidy: the address
|
|
238
238
|
// is parsed by counting separators, so a hyphenated shortcode would be
|
|
239
239
|
// read back as two segments and resolve to nothing, reporting nothing
|
|
240
|
-
// about why
|
|
240
|
+
// about why.
|
|
241
241
|
if (!isAddressSegment(shortcode)) {
|
|
242
242
|
throw Object.assign(
|
|
243
243
|
new Error(
|
|
244
244
|
`folder shortcode "${shortcode}" is not strictly ` +
|
|
245
|
-
`alphanumeric
|
|
245
|
+
`alphanumeric — lowercase letters and digits only ` +
|
|
246
|
+
`(${ADDRESS_SEGMENT_PATTERN.source}) — so its address ` +
|
|
247
|
+
`would not parse: ${absPath}`,
|
|
246
248
|
),
|
|
247
249
|
{ absPath },
|
|
248
250
|
);
|
|
@@ -266,7 +268,7 @@ export function collectFolderNotes(notes, pkg) {
|
|
|
266
268
|
Object.entries(parent).map(([pack, value]) => [pack, bareAddress(value)]),
|
|
267
269
|
),
|
|
268
270
|
// An authored id is kept, and a folder without one derives a
|
|
269
|
-
// stable one from its address
|
|
271
|
+
// stable one from its address. Keeping the authored id is
|
|
270
272
|
// what makes this a build change rather than a world migration: a
|
|
271
273
|
// world already holding these folders goes on resolving them.
|
|
272
274
|
id: authoredId || folderDocId(pkg, shortcode),
|
|
@@ -283,7 +285,7 @@ export function collectFolderNotes(notes, pkg) {
|
|
|
283
285
|
*
|
|
284
286
|
* Three keys per folder, and no more: the canonical address, the
|
|
285
287
|
* `folder-<shortcode>` short form, and the bare shortcode. They are the
|
|
286
|
-
* suffixes of the canonical address the grammar admits
|
|
288
|
+
* suffixes of the canonical address the grammar admits — a `packFolder`
|
|
287
289
|
* or `parent` field supplies the type itself, so a bare shortcode is a complete
|
|
288
290
|
* address there.
|
|
289
291
|
*
|
|
@@ -402,7 +404,7 @@ export function buildFolderNoteIndex(folders) {
|
|
|
402
404
|
*
|
|
403
405
|
* A folder cannot materialise without them: a `Folder` whose parent is
|
|
404
406
|
* absent from the pack is an orphan Foundry renders at the root, so the
|
|
405
|
-
* tree would be broken at the top rather than merely incomplete
|
|
407
|
+
* tree would be broken at the top rather than merely incomplete.
|
|
406
408
|
*
|
|
407
409
|
* @param {FolderNote} folder - The folder.
|
|
408
410
|
* @param {string} [pack] - The pack being compiled.
|
|
@@ -497,7 +499,7 @@ export function folderDocument(folder, parent, documentType, stats) {
|
|
|
497
499
|
* Refuse a note that declares the retired `folder:` spelling.
|
|
498
500
|
*
|
|
499
501
|
* `folder:` named a compendium folder by the raw Foundry id declared in a
|
|
500
|
-
* per-pack `*-folders.yaml`. Both halves are retired together
|
|
502
|
+
* per-pack `*-folders.yaml`. Both halves are retired together: the id
|
|
501
503
|
* spelling has nothing left to resolve against once the YAML is gone, and the
|
|
502
504
|
* YAML has no reader once the spelling is refused.
|
|
503
505
|
*
|
|
@@ -120,7 +120,7 @@ export function catalogDir(config, id, version) {
|
|
|
120
120
|
const itemsDir = (dir) => path.join(dir, "items");
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
|
-
* The file a system publishes its `system` field sets as
|
|
123
|
+
* The file a system publishes its `system` field sets as.
|
|
124
124
|
*
|
|
125
125
|
* @type {string}
|
|
126
126
|
*/
|
|
@@ -166,7 +166,7 @@ function cacheSchemaArtifact(root, dir) {
|
|
|
166
166
|
|
|
167
167
|
/**
|
|
168
168
|
* What each extracted pack is, written beside the items rather than inferred
|
|
169
|
-
* from the directory it landed in
|
|
169
|
+
* from the directory it landed in.
|
|
170
170
|
*
|
|
171
171
|
* A dependency may ship a pack per system — `items-sohl` and `items-hm3` — and
|
|
172
172
|
* the two hold documents of the *same* `(type, shortcode)` addresses with
|
|
@@ -219,7 +219,7 @@ function cachedItemPacks(dir) {
|
|
|
219
219
|
* Whether a dependency's cache is present and complete.
|
|
220
220
|
*
|
|
221
221
|
* **A cache without its pack manifest is incomplete**, not merely unlabelled.
|
|
222
|
-
*
|
|
222
|
+
* An older one holds the items and not what they are, and the two
|
|
223
223
|
* ways of proceeding without it are both wrong: reading every pack resolves an
|
|
224
224
|
* `hm3` reference against `sohl` documents — the silent-wrong-output failure
|
|
225
225
|
* this scoping exists to remove — and reading none fails a build that was
|
|
@@ -309,7 +309,7 @@ async function extractItemPacks(id, version, manifest, root, dir) {
|
|
|
309
309
|
);
|
|
310
310
|
}
|
|
311
311
|
// What each pack is, from the only place that knows: the manifest that
|
|
312
|
-
// declared it
|
|
312
|
+
// declared it. Written before the stamp, so the stamp continues to
|
|
313
313
|
// mean the cache is whole.
|
|
314
314
|
fs.writeFileSync(
|
|
315
315
|
path.join(dir, ITEM_PACKS),
|
|
@@ -519,7 +519,7 @@ export async function fetchCatalogFromPath(config, rel, source) {
|
|
|
519
519
|
}
|
|
520
520
|
|
|
521
521
|
/**
|
|
522
|
-
* Fetch one dependency's published content index
|
|
522
|
+
* Fetch one dependency's published content index.
|
|
523
523
|
*
|
|
524
524
|
* **The chain is entirely declared.** The relationship names the dependency's
|
|
525
525
|
* manifest, the manifest advertises `flags.metadataUrl`, and that URL is the
|
|
@@ -720,7 +720,7 @@ export async function fetchAllCatalogs(config) {
|
|
|
720
720
|
* Reads the cache only. A cold cache is an error naming the command that fills
|
|
721
721
|
* it, rather than a download nobody asked for.
|
|
722
722
|
*
|
|
723
|
-
* **Scoped to one system when the caller compiles for one
|
|
723
|
+
* **Scoped to one system when the caller compiles for one**, exactly as
|
|
724
724
|
* {@link module:engine/generate.itemPackJsonDirs} scopes the local half. The
|
|
725
725
|
* two halves answer the same lookup — `loadItemsMap` merges them into one
|
|
726
726
|
* address space keyed by `subType:shortcode` — so scoping only the local one
|
|
@@ -759,8 +759,7 @@ export function foreignItemCatalogDirs(config, system = null) {
|
|
|
759
759
|
// the currently declared one, so that is the one to use. The
|
|
760
760
|
// comparison is the content-index cache's, shared rather than
|
|
761
761
|
// rewritten: a plain string sort would put `0.8.10` before `0.8.2` and
|
|
762
|
-
// silently resolve every embedded item against the older catalogue
|
|
763
|
-
// (#272).
|
|
762
|
+
// silently resolve every embedded item against the older catalogue.
|
|
764
763
|
const newest = newestVersionDir(cached);
|
|
765
764
|
const packSystems = cachedItemPacks(newest);
|
|
766
765
|
const items = itemsDir(newest);
|
|
@@ -768,7 +767,7 @@ export function foreignItemCatalogDirs(config, system = null) {
|
|
|
768
767
|
if (!entry.isDirectory()) continue;
|
|
769
768
|
const packSystem = packSystems.get(entry.name) ?? null;
|
|
770
769
|
if (system != null && packSystem != null && packSystem !== system) continue;
|
|
771
|
-
// The dependency's own id travels with its directory
|
|
770
|
+
// The dependency's own id travels with its directory: a
|
|
772
771
|
// being's `model:` names the package its template comes from, and
|
|
773
772
|
// the address cannot be built from the path.
|
|
774
773
|
dirs.push({ dir: path.join(items, entry.name), package: rel.id });
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Emitting this package's cross-package link manifest
|
|
15
|
+
* Emitting this package's cross-package link manifest.
|
|
16
16
|
*
|
|
17
17
|
* `engine/content-address.mjs` owns the address *grammar* — how a key is
|
|
18
18
|
* version is read, how a foreign file resolves. This module owns the *pass*:
|
|
@@ -27,16 +27,16 @@
|
|
|
27
27
|
* to strip that same prefix back off; the value never reached the file. So
|
|
28
28
|
* nothing here composes one. An address is derived package-relative from the
|
|
29
29
|
* start, by {@link packageAddress}, and the emitting build's mount point is not
|
|
30
|
-
* a fact it has to be told
|
|
30
|
+
* a fact it has to be told.
|
|
31
31
|
*
|
|
32
|
-
* **An entry's `path` is derivable from the key it is filed under
|
|
32
|
+
* **An entry's `path` is derivable from the key it is filed under**.
|
|
33
33
|
* `sohl-sohl-affliction-aconite` publishes at `affliction-aconite/` — the key
|
|
34
34
|
* with its package and system segments dropped — because a page's URL *is* its
|
|
35
35
|
* address; nothing in it comes from a display name, so a rename moves no URL and
|
|
36
36
|
* no uniqueness check stands between the two. The system segment goes with the
|
|
37
37
|
* package because a note publishes one page however many systems' documents it
|
|
38
|
-
* compiles into
|
|
39
|
-
* derivable that way
|
|
38
|
+
* compiles into. Every entry is
|
|
39
|
+
* derivable that way, sections being retired — the section landing was the one
|
|
40
40
|
* that was not. The field is still written rather than left for a consumer to
|
|
41
41
|
* compute, because an absent `path` already means something else entirely (a
|
|
42
42
|
* package that publishes no pages).
|
|
@@ -124,8 +124,8 @@ export function anchorsOf(entryUuid, entryId, body, name) {
|
|
|
124
124
|
* An item note produces **two**: the item, and separately the JournalEntry its
|
|
125
125
|
* prose compiles into. They are two documents with two UUIDs, so they get two
|
|
126
126
|
* addresses; the item's entry points at the other by address rather than
|
|
127
|
-
* repeating its UUID, because the doc entry owns that fact
|
|
128
|
-
* note is the same arrangement
|
|
127
|
+
* repeating its UUID, because the doc entry owns that fact. A `macro`
|
|
128
|
+
* note is the same arrangement, which is why the type set comes from
|
|
129
129
|
* {@link hasDocEntry} rather than being spelled here — the journals compiler
|
|
130
130
|
* reads the same one, so a manifest cannot claim documentation nothing compiled.
|
|
131
131
|
*
|
|
@@ -137,7 +137,7 @@ export function anchorsOf(entryUuid, entryId, body, name) {
|
|
|
137
137
|
* UUID. The two are one fact — a UUID ends in the id — but only the entry knows
|
|
138
138
|
* which derivation produced it: an item's is its note's `fm.id`, and its
|
|
139
139
|
* documentation journal's is {@link itemDocEntryId} of that. Stating it here is
|
|
140
|
-
* what lets the content index publish an identity it did not re-derive
|
|
140
|
+
* what lets the content index publish an identity it did not re-derive.
|
|
141
141
|
*
|
|
142
142
|
* @param {object} ctx - Resolved identities: `{ contentPackage,
|
|
143
143
|
* foundryPackageId, packRouter }`.
|
|
@@ -161,11 +161,11 @@ export function entriesForNote(fm, name, address, body, ctx) {
|
|
|
161
161
|
|
|
162
162
|
// A published address must name the pack the document actually shipped in:
|
|
163
163
|
// a consumer resolves the UUID verbatim, and a repository may ship several
|
|
164
|
-
// packs of one type
|
|
164
|
+
// packs of one type.
|
|
165
165
|
const uuidFor = (type, id, routeFm) =>
|
|
166
166
|
// A type this cannot name a single compendium document for has no UUID
|
|
167
167
|
// to publish, whatever id it derives. That used to follow from such a
|
|
168
|
-
// note authoring no `id:`;
|
|
168
|
+
// note authoring no `id:`; every addressable note derives
|
|
169
169
|
// one, so "has an id" stopped being evidence a document exists and the
|
|
170
170
|
// rule is stated where it belongs — beside the addresses — rather than
|
|
171
171
|
// resting on an absent field. `collectFoundryEntries` skips such a note
|
|
@@ -175,7 +175,7 @@ export function entriesForNote(fm, name, address, body, ctx) {
|
|
|
175
175
|
// Two sets, for opposite reasons (see `note-claims.mjs`). A **homepage**
|
|
176
176
|
// is in no pack: it compiles to a page and there is nothing to address.
|
|
177
177
|
// A **folder** may be in several — it materialises in every pack holding
|
|
178
|
-
// a document that references it
|
|
178
|
+
// a document that references it — so no one UUID identifies it,
|
|
179
179
|
// and its id is hashed under the `folder` namespace against its own
|
|
180
180
|
// address rather than under `document`. Emitting one would publish an
|
|
181
181
|
// `Item` UUID for a `Folder`, at an id no document carries.
|
|
@@ -222,7 +222,7 @@ export function entriesForNote(fm, name, address, body, ctx) {
|
|
|
222
222
|
// content index publishes it beside the UUID, so an entry the
|
|
223
223
|
// index gives an identity to states both halves of it rather
|
|
224
224
|
// than leaving a consumer to parse the id back out of the
|
|
225
|
-
// UUID's last segment
|
|
225
|
+
// UUID's last segment.
|
|
226
226
|
id: docEntryId,
|
|
227
227
|
uuid: docUuid,
|
|
228
228
|
anchors: docUuid ? anchorsOf(docUuid, docEntryId, body ?? "", name) : undefined,
|
|
@@ -250,12 +250,11 @@ export function entriesForNote(fm, name, address, body, ctx) {
|
|
|
250
250
|
* Every note this package publishes, as manifest entries.
|
|
251
251
|
*
|
|
252
252
|
* Every note in the tree is this package's, so nothing here selects by package:
|
|
253
|
-
* the key's first segment is `contentPackage
|
|
253
|
+
* the key's first segment is `contentPackage`. A note still declaring the
|
|
254
254
|
* retired `package:` or `draft:` field **throws** rather than being skipped —
|
|
255
255
|
* skipping one silently is how a whole tree came to be filtered out of a
|
|
256
256
|
* manifest that then claimed the package published nothing, and it is what let
|
|
257
|
-
* a drafted note's inbound links look like links to a note that never existed
|
|
258
|
-
* (#69).
|
|
257
|
+
* a drafted note's inbound links look like links to a note that never existed.
|
|
259
258
|
*
|
|
260
259
|
* A note that has no address is **reported, not guessed** — the finding carries
|
|
261
260
|
* the file and the reason, so a caller can print it or fail on it. Inventing an
|
|
@@ -278,8 +277,8 @@ export function collectFoundryEntries(contentBase, ctx) {
|
|
|
278
277
|
skipDirectories: ctx.skipDirectories,
|
|
279
278
|
})) {
|
|
280
279
|
if (!fm) continue;
|
|
281
|
-
// Its authored pin, or the id derived from its canonical address
|
|
282
|
-
//
|
|
280
|
+
// Its authored pin, or the id derived from its canonical address.
|
|
281
|
+
// Resolved before anything reads `fm.id`, so the UUID this
|
|
283
282
|
// pass publishes is the one the pack passes compiled under.
|
|
284
283
|
resolveNoteId(fm, { pkg: ctx.contentPackage });
|
|
285
284
|
const rel = path.relative(contentBase, absPath);
|
|
@@ -294,7 +293,7 @@ export function collectFoundryEntries(contentBase, ctx) {
|
|
|
294
293
|
assertNoSectionField(fm, { file: rel, absPath });
|
|
295
294
|
assertNoTraitsField(fm, { file: rel, absPath });
|
|
296
295
|
if (!fm.type || !fm.shortcode) continue;
|
|
297
|
-
// A homepage is addressed like every other note
|
|
296
|
+
// A homepage is addressed like every other note, and a
|
|
298
297
|
// shortcode alone would now put it here. It stays out for the reason it
|
|
299
298
|
// always did, which that change does not touch: a manifest entry is how
|
|
300
299
|
// another package resolves a **document**, and a homepage compiles into
|
|
@@ -337,7 +336,7 @@ export function foundryIdentities(config = loadPackConfig()) {
|
|
|
337
336
|
// Carried in the context rather than read from the global config at the
|
|
338
337
|
// call site, so the pass really is a pure function of what it is handed
|
|
339
338
|
// — which is what lets the content index drive the same derivation with
|
|
340
|
-
// a configuration it resolved itself
|
|
339
|
+
// a configuration it resolved itself.
|
|
341
340
|
docEntryTypes: config.docEntryTypes,
|
|
342
341
|
};
|
|
343
342
|
}
|
|
@@ -348,7 +347,7 @@ export function foundryIdentities(config = loadPackConfig()) {
|
|
|
348
347
|
* {@link foundryIdentities} plus what only a *manifest* emission needs. The
|
|
349
348
|
* split is what lets the content index derive the same Foundry addresses from
|
|
350
349
|
* the same code without also depending on whether the package publishes pages,
|
|
351
|
-
* which is no part of a UUID
|
|
350
|
+
* which is no part of a UUID.
|
|
352
351
|
*
|
|
353
352
|
* @param {object} [config] - A resolved configuration; loaded when omitted.
|
|
354
353
|
* @returns {{contentPackage: string, foundryPackageId: string, packRouter: object,
|