@heroiclands/package-build 20.4.0 → 20.6.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 +288 -0
- package/CONTENT.md +213 -20
- package/README.md +19 -1
- package/bin/content-build.mjs +135 -32
- package/bin/package-build.mjs +46 -13
- package/content-config.mjs +345 -101
- package/docs/api.md +1352 -0
- package/docs/commands.md +1609 -0
- package/docs/configuration.md +1432 -0
- package/docs/content-format.md +16 -6
- package/docs/diagnostics.md +356 -0
- package/docs/getting-started.md +813 -0
- package/docs/project-setup.md +469 -0
- package/engine/actor-compiler.mjs +30 -27
- package/engine/address-diff.mjs +45 -41
- package/engine/base-compiler.mjs +6 -0
- package/engine/bundles.mjs +9 -0
- package/engine/content-address.mjs +9 -9
- package/engine/content-index.mjs +44 -23
- package/engine/content-links.mjs +44 -11
- package/engine/content-lint.mjs +44 -10
- package/engine/content-tables.mjs +32 -27
- package/engine/folder-notes.mjs +4 -2
- package/engine/frontmatter-lint.mjs +35 -38
- package/engine/generate.mjs +5 -0
- package/engine/helpers.mjs +86 -32
- package/engine/index.mjs +12 -2
- package/engine/journals.mjs +9 -0
- package/engine/note-claims.mjs +18 -10
- package/engine/note-schemas.mjs +0 -5
- package/engine/note-vocabulary.mjs +32 -31
- package/engine/pack-config.mjs +26 -12
- 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 +876 -0
- package/engine/pdf-toc.mjs +525 -0
- package/engine/scenes.mjs +14 -5
- package/engine/schema-check.mjs +1 -1
- package/engine/site-build.mjs +21 -3
- package/engine/web-wikilinks.mjs +6 -3
- package/engine/wikilinks.mjs +2 -4
- package/hm3/actors.mjs +8 -0
- package/hm3/items.mjs +8 -0
- package/package.json +1 -1
- package/release.mjs +63 -3
- package/sohl/actors.mjs +8 -0
- package/sohl/items.mjs +8 -0
- package/sohl/note-schemas.mjs +5 -5
- package/types/content-config.d.mts +66 -15
- package/types/engine/actor-compiler.d.mts +34 -30
- package/types/engine/address-diff.d.mts +57 -3
- package/types/engine/base-compiler.d.mts +10 -2
- package/types/engine/bundles.d.mts +9 -0
- package/types/engine/content-address.d.mts +9 -9
- package/types/engine/content-index.d.mts +57 -13
- package/types/engine/content-lint.d.mts +6 -4
- package/types/engine/content-tables.d.mts +49 -18
- package/types/engine/frontmatter-lint.d.mts +3 -2
- package/types/engine/helpers.d.mts +105 -31
- package/types/engine/index.d.mts +4 -0
- package/types/engine/journals.d.mts +9 -0
- package/types/engine/note-claims.d.mts +17 -10
- package/types/engine/note-vocabulary.d.mts +23 -196
- package/types/engine/pack-config.d.mts +4 -4
- 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 +156 -0
- package/types/engine/pdf-toc.d.mts +114 -0
- package/types/engine/scenes.d.mts +10 -1
- package/types/engine/schema-check.d.mts +2 -2
- package/types/engine/site-build.d.mts +34 -6
- package/types/engine/wikilinks.d.mts +2 -3
- package/types/hm3/actors.d.mts +8 -0
- package/types/hm3/items.d.mts +8 -0
- package/types/release.d.mts +15 -4
- package/types/sohl/actors.d.mts +10 -2
- package/types/sohl/items.d.mts +8 -0
package/engine/helpers.mjs
CHANGED
|
@@ -130,24 +130,6 @@ export function parseMarkdownFile(filePath) {
|
|
|
130
130
|
return { frontmatter, body, description, bodyLine, bodyColumn };
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
/**
|
|
134
|
-
* Recursively yields every `.md` file under `rootDir`, parsed.
|
|
135
|
-
* Yields `{ frontmatter, body, description, file, absPath, bodyLine,
|
|
136
|
-
* bodyColumn }` for each match — the last two from
|
|
137
|
-
* {@link parseMarkdownFile}, so a caller can report a position inside the
|
|
138
|
-
* body as a position in the file.
|
|
139
|
-
* Silently skips directories that don't exist.
|
|
140
|
-
*
|
|
141
|
-
* Directory names in `skipDirectories` are ignored wherever they appear. The
|
|
142
|
-
* walk itself knows nothing about what they mean: `Templates/` is an Obsidian
|
|
143
|
-
* templater convention this repository's vault happens to use, not a property
|
|
144
|
-
* of a content tree, so it is configured rather than hard-coded.
|
|
145
|
-
*
|
|
146
|
-
* @param {string} rootDir - Root of the tree to walk.
|
|
147
|
-
* @param {object} [opts]
|
|
148
|
-
* @param {readonly string[]} [opts.skipDirectories] - Directory names to ignore.
|
|
149
|
-
* Defaults to the configured list.
|
|
150
|
-
*/
|
|
151
133
|
/**
|
|
152
134
|
* Refuse a corpus read whose scope its caller did not state.
|
|
153
135
|
*
|
|
@@ -203,6 +185,31 @@ export function assertSuppliedCorpus(records, who) {
|
|
|
203
185
|
}
|
|
204
186
|
}
|
|
205
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Recursively yields every `.md` file under `rootDir`, parsed.
|
|
190
|
+
*
|
|
191
|
+
* Yields `{ frontmatter, body, description, file, absPath, bodyLine,
|
|
192
|
+
* bodyColumn }` for each match — the last from {@link parseMarkdownFile}, so a
|
|
193
|
+
* caller can report a position inside the body as a position in the file. A
|
|
194
|
+
* root that does not exist yields nothing, and a directory that cannot be read
|
|
195
|
+
* is warned about and skipped.
|
|
196
|
+
*
|
|
197
|
+
* Directory names in `skipDirectories` are ignored wherever they appear. The
|
|
198
|
+
* walk itself knows nothing about what they mean: `Templates/` is an Obsidian
|
|
199
|
+
* templater convention this repository's vault happens to use, not a property
|
|
200
|
+
* of a content tree, so it is stated by the caller rather than hard-coded.
|
|
201
|
+
*
|
|
202
|
+
* @param {string} rootDir - Root of the tree to walk.
|
|
203
|
+
* @param {object} opts
|
|
204
|
+
* @param {readonly string[]} opts.skipDirectories - Directory names to ignore.
|
|
205
|
+
* Required: the scope is the caller's to state, so two passes cannot
|
|
206
|
+
* disagree about which files are the corpus.
|
|
207
|
+
* @yields {{frontmatter: object|null, body: string, description: string,
|
|
208
|
+
* file: string, absPath: string, bodyLine?: number, bodyColumn?: number}}
|
|
209
|
+
* One entry per `.md` file found.
|
|
210
|
+
* @throws {Error} When `skipDirectories` is not stated — see
|
|
211
|
+
* {@link assertStatedScope}.
|
|
212
|
+
*/
|
|
206
213
|
export function* walkMarkdownTree(rootDir, { skipDirectories } = {}) {
|
|
207
214
|
// Stated by the caller, never resolved here. A default here
|
|
208
215
|
// — `loadPackConfig().skipDirectories` — read whichever configuration
|
|
@@ -415,11 +422,6 @@ export function makeFilename(name, id) {
|
|
|
415
422
|
return `${unidecode(name)}_${id}`.replace(/[^0-9a-zA-Z]+/g, "_") + ".json";
|
|
416
423
|
}
|
|
417
424
|
|
|
418
|
-
/**
|
|
419
|
-
* Standardize a name into a slug: lowercase, apostrophes removed,
|
|
420
|
-
* non-alphanumerics collapsed to single hyphens.
|
|
421
|
-
*/
|
|
422
|
-
|
|
423
425
|
/**
|
|
424
426
|
* The path prefixes that name a package other than the one being compiled.
|
|
425
427
|
*
|
|
@@ -537,11 +539,19 @@ function addressesAnotherPackage(s) {
|
|
|
537
539
|
* `itemArt()`, which runs the path back through this function so a registry
|
|
538
540
|
* entry and a note's `img:` are spelled the same way (#7).
|
|
539
541
|
*
|
|
542
|
+
* **A package with no asset root cannot answer at all.** `assetRoot` is derived
|
|
543
|
+
* from the package kind, and a `documentation` package has none: Foundry serves
|
|
544
|
+
* no files for it. Only a compiling pass reaches here, and a documentation
|
|
545
|
+
* package runs none, so a path arriving with no root to put it under is a pass
|
|
546
|
+
* running where it should not — reported as that, rather than emitted as
|
|
547
|
+
* `null/icons/relic.svg` into a document nobody would check.
|
|
548
|
+
*
|
|
540
549
|
* @param {string | null | undefined} raw - content-relative path from frontmatter.
|
|
541
|
-
* @param {{assetRoot: string}} [config] - The resolved build configuration.
|
|
550
|
+
* @param {{assetRoot: string|null}} [config] - The resolved build configuration.
|
|
542
551
|
* Defaults to this repository's.
|
|
543
552
|
* @returns {string | null} the Foundry-relative path; `""` for a deliberate
|
|
544
553
|
* blank, and `null` when the note names no art at all.
|
|
554
|
+
* @throws {Error} When the configuration has no asset root.
|
|
545
555
|
*/
|
|
546
556
|
export function resolveImg(raw, config = loadPackConfig()) {
|
|
547
557
|
// Unset — the caller's default applies. An absent key arrives as
|
|
@@ -552,6 +562,14 @@ export function resolveImg(raw, config = loadPackConfig()) {
|
|
|
552
562
|
if (s === "") return "";
|
|
553
563
|
// Somebody else's to serve — emit it exactly as authored.
|
|
554
564
|
if (addressesAnotherPackage(s)) return s;
|
|
565
|
+
if (!config.assetRoot) {
|
|
566
|
+
throw new Error(
|
|
567
|
+
`package-build: \`${s}\` names a file this package serves, and a ` +
|
|
568
|
+
`\`documentation\` package has no asset root to serve it from — ` +
|
|
569
|
+
`Foundry installs no such package. Address the owning package ` +
|
|
570
|
+
`(\`systems/…\`, \`modules/…\`) or a URL.`,
|
|
571
|
+
);
|
|
572
|
+
}
|
|
555
573
|
// Ours, so root it where Foundry serves this package's files from.
|
|
556
574
|
return `${config.assetRoot}/${s}`;
|
|
557
575
|
}
|
|
@@ -689,6 +707,13 @@ export function defaultStats() {
|
|
|
689
707
|
return cachedDefaultStats;
|
|
690
708
|
}
|
|
691
709
|
|
|
710
|
+
// The one slug rule, re-exported so callers keep a single import path.
|
|
711
|
+
/**
|
|
712
|
+
* Standardize a name into a slug: lowercase, apostrophes removed,
|
|
713
|
+
* non-alphanumerics collapsed to single hyphens.
|
|
714
|
+
*/
|
|
715
|
+
export { slugify } from "./content-slug.mjs";
|
|
716
|
+
|
|
692
717
|
/**
|
|
693
718
|
* Stable 16-char hex id derived from `${namespace}:${value}`.
|
|
694
719
|
*
|
|
@@ -696,9 +721,6 @@ export function defaultStats() {
|
|
|
696
721
|
* resolver this one imports can derive ids too — and re-exported here for the
|
|
697
722
|
* passes that have always reached it through `helpers`.
|
|
698
723
|
*/
|
|
699
|
-
// The one slug rule, re-exported so callers keep a single import path.
|
|
700
|
-
export { slugify } from "./content-slug.mjs";
|
|
701
|
-
|
|
702
724
|
export { makeId } from "./ids.mjs";
|
|
703
725
|
|
|
704
726
|
// The content-type → document-type map, which decides *which* pack list a
|
|
@@ -730,6 +752,15 @@ import { collectAnchors } from "./anchors.mjs";
|
|
|
730
752
|
* @param {object} [router] - The pack router. Supplied by the calling pass so
|
|
731
753
|
* the index and the compile agree about where each note landed; defaults to
|
|
732
754
|
* this repository's own.
|
|
755
|
+
* @param {object} [opts]
|
|
756
|
+
* @param {readonly string[]} [opts.skipDirectories] - Part of the options bag
|
|
757
|
+
* every corpus reader takes; the scope is already settled by `records`.
|
|
758
|
+
* @param {object} [opts.config] - The resolved build configuration; loaded when
|
|
759
|
+
* omitted.
|
|
760
|
+
* @param {readonly object[]} [opts.records] - The corpus, derived once per
|
|
761
|
+
* compile and handed in. Required: see {@link assertSuppliedCorpus}.
|
|
762
|
+
* @param {object[]} [opts.problems] - Part of the same options bag; the notes
|
|
763
|
+
* the index cannot record are collected where the corpus is derived.
|
|
733
764
|
* @returns {{byShortcode: Map, types: Set}} From `buildWikilinkIndex`.
|
|
734
765
|
*/
|
|
735
766
|
export function buildContentLinkIndex(
|
|
@@ -827,11 +858,21 @@ export function buildContentLinkIndex(
|
|
|
827
858
|
* inventing a position.
|
|
828
859
|
*
|
|
829
860
|
* @param {string} body - The note's markdown body, tables already expanded.
|
|
830
|
-
* @param {object} ctx
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
861
|
+
* @param {object} ctx
|
|
862
|
+
* @param {string} ctx.type - The source note's content type.
|
|
863
|
+
* @param {string} ctx.id - The source note's document id.
|
|
864
|
+
* @param {string} ctx.pack - The pack the note's own document lands in, which
|
|
865
|
+
* addresses a `[[#slug]]` self-link: its target is the source note itself, so
|
|
866
|
+
* it has no index entry.
|
|
867
|
+
* @param {string} ctx.docPack - The pack the note's documentation journal lands
|
|
868
|
+
* in, addressing a self-link the same way.
|
|
869
|
+
* @param {object} ctx.index - The address index every link resolves through.
|
|
870
|
+
* @param {string} ctx.name - The note, for the message.
|
|
871
|
+
* @param {string} [ctx.file] - The note's file, so a report names it.
|
|
872
|
+
* @param {number} [ctx.bodyLine] - 1-based file line of the body's first line.
|
|
873
|
+
* @param {number} [ctx.bodyColumn] - 1-based file column of the same character.
|
|
874
|
+
* @param {Array<{line: number, generated: boolean}>} [ctx.lineMap] - Which
|
|
875
|
+
* authored line each body line came from, from {@link expandNoteTables}.
|
|
835
876
|
* @returns {{markdown: string, unresolved: Array<object>}}
|
|
836
877
|
* @throws {Error} On any link that does not resolve — an unlabelled one, a
|
|
837
878
|
* target that is not an address, or an address nothing publishes. The error
|
|
@@ -908,6 +949,15 @@ export function convertNoteWikilinks(
|
|
|
908
949
|
* a table that leaves rows tied still emits identically on every build.
|
|
909
950
|
*
|
|
910
951
|
* @param {string} contentBase - Root of the content tree.
|
|
952
|
+
* @param {object} [opts]
|
|
953
|
+
* @param {readonly string[]} [opts.skipDirectories] - Part of the options bag
|
|
954
|
+
* every corpus reader takes; the scope is already settled by `records`.
|
|
955
|
+
* @param {object} [opts.config] - The resolved build configuration; loaded when
|
|
956
|
+
* omitted.
|
|
957
|
+
* @param {readonly object[]} [opts.records] - The corpus, derived once per
|
|
958
|
+
* compile and handed in. Required: see {@link assertSuppliedCorpus}.
|
|
959
|
+
* @param {object[]} [opts.problems] - Part of the same options bag; the notes
|
|
960
|
+
* the walk cannot read are collected where the corpus is derived.
|
|
911
961
|
* @returns {Array<{fm: object, path: string, tld: string, folder: string,
|
|
912
962
|
* absPath: string}>}
|
|
913
963
|
*/
|
|
@@ -972,6 +1022,10 @@ const packLinkable = (doc) => Boolean(doc.fm?.shortcode) && Boolean(doc.fm?.type
|
|
|
972
1022
|
* query's `this` reads. Its entry in `docs` supplies the path as well.
|
|
973
1023
|
* @param {number} [ctx.bodyLine] - 1-based file line of the body's first line,
|
|
974
1024
|
* so a failing directive can be reported at its position in the file.
|
|
1025
|
+
* @param {object[]} [ctx.sqlTables] - This note's prepared `sql` results, in
|
|
1026
|
+
* document order, from
|
|
1027
|
+
* {@link module:engine/sql-tables.prepareSqlTables}. An `sql` directive with
|
|
1028
|
+
* no prepared result fails the note: nothing here runs a query.
|
|
975
1029
|
* @returns {{markdown: string, lineMap: Array<{line: number,
|
|
976
1030
|
* generated: boolean}>}} The body with every table expanded, and where each
|
|
977
1031
|
* emitted line came from — which is what lets a diagnostic about the
|
package/engine/index.mjs
CHANGED
|
@@ -87,8 +87,6 @@ export * as noteVocabulary from "./note-vocabulary.mjs";
|
|
|
87
87
|
/** The closed registry of system ids, and the `none` that stands for no system. */
|
|
88
88
|
export * as systems from "./systems.mjs";
|
|
89
89
|
|
|
90
|
-
/** The shipped Foundry manifest: locating it, reading it, guarding its id. */
|
|
91
|
-
|
|
92
90
|
/** The one normalisation this build makes: prose to a URL-safe token. */
|
|
93
91
|
export * as contentSlug from "./content-slug.mjs";
|
|
94
92
|
|
|
@@ -158,6 +156,18 @@ export * as wikilinkSyntax from "./wikilink-syntax.mjs";
|
|
|
158
156
|
/** The address index a site build resolves its wikilinks against. */
|
|
159
157
|
export * as siteIndex from "./site-index.mjs";
|
|
160
158
|
|
|
159
|
+
/** The document tree a book selects, and the plan it resolves to. */
|
|
160
|
+
export * as pdfToc from "./pdf-toc.mjs";
|
|
161
|
+
|
|
162
|
+
/** Markdown and a plan, rendered as Typst source. Pure. */
|
|
163
|
+
export * as pdfRender from "./pdf-render.mjs";
|
|
164
|
+
|
|
165
|
+
/** Which glyph an icon name resolves to, read from the font that carries it. */
|
|
166
|
+
export * as pdfFonts from "./pdf-fonts.mjs";
|
|
167
|
+
|
|
168
|
+
/** The content tree, built into a book. Reads the tree; runs the compiler. */
|
|
169
|
+
export * as pdfBuild from "./pdf-build.mjs";
|
|
170
|
+
|
|
161
171
|
/** The shape every pack compiler shares. */
|
|
162
172
|
export * as baseCompiler from "./base-compiler.mjs";
|
|
163
173
|
|
package/engine/journals.mjs
CHANGED
|
@@ -303,6 +303,15 @@ export function buildJournalEntry({
|
|
|
303
303
|
};
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Journals pack compiler.
|
|
308
|
+
*
|
|
309
|
+
* Walks the content tree and compiles every `type: doc` note, and every note of
|
|
310
|
+
* a doc-carrying type, into one JournalEntry document: the body split into
|
|
311
|
+
* pages on its top-level H1 headings, each rendered to HTML. A doc-carrying
|
|
312
|
+
* note's entry is that document's documentation, filed in the document's own
|
|
313
|
+
* folder.
|
|
314
|
+
*/
|
|
306
315
|
export class Journals extends BasePackCompiler {
|
|
307
316
|
static id = "journals";
|
|
308
317
|
static label = "journal";
|
package/engine/note-claims.mjs
CHANGED
|
@@ -111,6 +111,23 @@ import { NOTE_VOCABULARY } from "./note-vocabulary.mjs";
|
|
|
111
111
|
*/
|
|
112
112
|
export const NEVER_PACKED_TYPES = Object.freeze(new Set([HOMEPAGE_TYPE]));
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* The whole note vocabulary of a package that compiles no Foundry documents.
|
|
116
|
+
*
|
|
117
|
+
* Every other type in the vocabulary exists to *become* a document: a `skill` is
|
|
118
|
+
* an Item, a `being` an Actor, a `place` a JournalEntry, a `folder` the Folder
|
|
119
|
+
* they are filed under. In a `documentation` package none of them has a
|
|
120
|
+
* destination, so a note carrying one would be authored, validated, walked and
|
|
121
|
+
* then published as a page of something that was meant to be a compendium
|
|
122
|
+
* entry — the plausible-looking result that reads as success.
|
|
123
|
+
*
|
|
124
|
+
* `doc` is prose whose single document *is* the prose, and `homepage` is the
|
|
125
|
+
* authored front page every package publishes. Both are already pages first.
|
|
126
|
+
*
|
|
127
|
+
* @type {ReadonlySet<string>}
|
|
128
|
+
*/
|
|
129
|
+
export const DOCUMENTATION_NOTE_TYPES = Object.freeze(new Set(["doc", HOMEPAGE_TYPE]));
|
|
130
|
+
|
|
114
131
|
/**
|
|
115
132
|
* Content types the specification states and this toolchain does not yet
|
|
116
133
|
* compile.
|
|
@@ -229,10 +246,7 @@ function mappingSystems(maps, type) {
|
|
|
229
246
|
* pack from appearing to answer for any note.
|
|
230
247
|
*
|
|
231
248
|
* @param {string} docType - The Foundry document type a pack holds.
|
|
232
|
-
* @param {ClaimSources} [sources] - What to answer from.
|
|
233
|
-
* @param {object} [opts] - Options.
|
|
234
|
-
* @param {readonly object[]} [opts.records] - The corpus, derived once by the
|
|
235
|
-
* compile and handed in — required, for the reason above. Defaults to the
|
|
249
|
+
* @param {ClaimSources} [sources] - What to answer from. Defaults to the
|
|
236
250
|
* configured registries and the systems this toolchain ships.
|
|
237
251
|
* @returns {ReadonlySet<string>} The note types such a pass would claim.
|
|
238
252
|
*/
|
|
@@ -403,9 +417,6 @@ function noteHasProse(absPath) {
|
|
|
403
417
|
* @param {object} [config] - The resolved build configuration. Defaults to this
|
|
404
418
|
* repository's.
|
|
405
419
|
* @param {ClaimSources} [sources] - What to answer from.
|
|
406
|
-
* @param {object} [opts] - Options.
|
|
407
|
-
* @param {readonly object[]} [opts.records] - The corpus, derived once by the
|
|
408
|
-
* compile and handed in — required, for the reason above.
|
|
409
420
|
* @returns {ReadonlySet<string>} The claimed note types.
|
|
410
421
|
*/
|
|
411
422
|
export function claimedNoteTypes(config = loadPackConfig(), sources) {
|
|
@@ -427,9 +438,6 @@ export function claimedNoteTypes(config = loadPackConfig(), sources) {
|
|
|
427
438
|
* declare on top.
|
|
428
439
|
*
|
|
429
440
|
* @param {ClaimSources} [sources] - What to answer from.
|
|
430
|
-
* @param {object} [opts] - Options.
|
|
431
|
-
* @param {readonly object[]} [opts.records] - The corpus, derived once by the
|
|
432
|
-
* compile and handed in — required, for the reason above.
|
|
433
441
|
* @returns {ReadonlySet<string>} The vocabulary.
|
|
434
442
|
*/
|
|
435
443
|
export function noteTypeVocabulary(sources) {
|
package/engine/note-schemas.mjs
CHANGED
|
@@ -34,11 +34,6 @@
|
|
|
34
34
|
|
|
35
35
|
import { HOMEPAGE_FIELDS, HOMEPAGE_TYPE } from "./homepage.mjs";
|
|
36
36
|
|
|
37
|
-
/**
|
|
38
|
-
* Every engine-level content type, and what a note of that type may write.
|
|
39
|
-
*
|
|
40
|
-
* @type {Readonly<Record<string, readonly import("./field-spec.mjs").FieldSpec[]>>}
|
|
41
|
-
*/
|
|
42
37
|
/**
|
|
43
38
|
* A note that compiles to a JournalEntry and nothing else.
|
|
44
39
|
*
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
* have swept.
|
|
54
54
|
*
|
|
55
55
|
* **A type name and a subType value are held to the address charset**, so
|
|
56
|
-
* both
|
|
57
|
-
* and the shortcode is already held to. For a type that is literal: it is a
|
|
56
|
+
* both match `ADDRESS_SEGMENT_PATTERN` — the charset `engine/address-charset.mjs`
|
|
57
|
+
* states and the shortcode is already held to. For a type that is literal: it is a
|
|
58
58
|
* segment of every address — the first of the short form an author writes
|
|
59
59
|
* (`type-shortcode`), the third of the canonical
|
|
60
60
|
* `package-system-type-shortcode` — the hyphen is the separator between
|
|
@@ -223,17 +223,20 @@ const CHARGES = Object.freeze([
|
|
|
223
223
|
/* --------------------------------------------------------------------- */
|
|
224
224
|
|
|
225
225
|
/**
|
|
226
|
-
*
|
|
227
|
-
* declares.
|
|
226
|
+
* The declared tag that marks a note as **unfinished**.
|
|
228
227
|
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
* key will be called — and the disagreement is recorded on the field rather
|
|
233
|
-
* than resolved silently.
|
|
228
|
+
* Named once and referenced from the declaration below, because a second
|
|
229
|
+
* spelling is how the two come apart: rename the tag in `DECLARED_TAGS` and a
|
|
230
|
+
* private copy elsewhere keeps matching the old word, silently.
|
|
234
231
|
*
|
|
235
|
-
*
|
|
232
|
+
* It is a **presentation** fact and nothing more. A draft note compiles,
|
|
233
|
+
* validates, publishes and resolves like any other; only a link *into* it
|
|
234
|
+
* renders marked. What it emphatically is not is the retired `draft:` field,
|
|
235
|
+
* whose entire effect was to move a note from published to unresolvable — see
|
|
236
|
+
* {@link draftRetiredMessage}.
|
|
236
237
|
*/
|
|
238
|
+
export const DRAFT_TAG = "draft";
|
|
239
|
+
|
|
237
240
|
/**
|
|
238
241
|
* The tags that **classify** a note, grouped by what they classify.
|
|
239
242
|
*
|
|
@@ -260,21 +263,6 @@ const CHARGES = Object.freeze([
|
|
|
260
263
|
* fishing village is a `village` that is `fishing`, and the single-valued field
|
|
261
264
|
* this replaced had to spell it `Fishing Village` as a value of its own.
|
|
262
265
|
*/
|
|
263
|
-
/**
|
|
264
|
-
* The declared tag that marks a note as **unfinished**.
|
|
265
|
-
*
|
|
266
|
-
* Named once and referenced from the declaration below, because a second
|
|
267
|
-
* spelling is how the two come apart: rename the tag in `DECLARED_TAGS` and a
|
|
268
|
-
* private copy elsewhere keeps matching the old word, silently.
|
|
269
|
-
*
|
|
270
|
-
* It is a **presentation** fact and nothing more. A draft note compiles,
|
|
271
|
-
* validates, publishes and resolves like any other; only a link *into* it
|
|
272
|
-
* renders marked. What it emphatically is not is the retired `draft:` field,
|
|
273
|
-
* whose entire effect was to move a note from published to unresolvable — see
|
|
274
|
-
* {@link draftRetiredMessage}.
|
|
275
|
-
*/
|
|
276
|
-
export const DRAFT_TAG = "draft";
|
|
277
|
-
|
|
278
266
|
export const DECLARED_TAGS = Object.freeze({
|
|
279
267
|
/** What a place *is*. */
|
|
280
268
|
placeKind: Object.freeze({
|
|
@@ -413,6 +401,18 @@ export function isDraftNote(fm) {
|
|
|
413
401
|
return hasTag(fm, DRAFT_TAG);
|
|
414
402
|
}
|
|
415
403
|
|
|
404
|
+
/**
|
|
405
|
+
* Every note type this toolchain compiles, and the closed vocabulary it
|
|
406
|
+
* declares.
|
|
407
|
+
*
|
|
408
|
+
* Taken from the content-format specification, one `### type:` section per
|
|
409
|
+
* entry. Where the specification and the shape notes are authored in today
|
|
410
|
+
* disagree, the specification wins on the **name** — that is what a `data:`
|
|
411
|
+
* key will be called — and the disagreement is recorded on the field rather
|
|
412
|
+
* than resolved silently.
|
|
413
|
+
*
|
|
414
|
+
* @type {Readonly<Record<string, TypeVocabulary>>}
|
|
415
|
+
*/
|
|
416
416
|
export const NOTE_VOCABULARY = Object.freeze({
|
|
417
417
|
/* ----- actors --------------------------------------------------- */
|
|
418
418
|
|
|
@@ -793,12 +793,13 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
793
793
|
/* ----- core documents ------------------------------------------- */
|
|
794
794
|
|
|
795
795
|
doc: Object.freeze({
|
|
796
|
-
//
|
|
797
|
-
//
|
|
798
|
-
//
|
|
799
|
-
//
|
|
800
|
-
//
|
|
801
|
-
|
|
796
|
+
// Five genres, and a genre is all this field carries: what kind of page
|
|
797
|
+
// it is, never who reads it. An audience term alongside them would give
|
|
798
|
+
// a developer how-to two valid values and no rule for choosing.
|
|
799
|
+
//
|
|
800
|
+
// `userguide` and `howto`, not `user-guide` and `how-to`: a subType is
|
|
801
|
+
// held to the address charset, and a segment carries no hyphen.
|
|
802
|
+
subTypes: Object.freeze(["rules", "userguide", "reference", "howto", "concept"]),
|
|
802
803
|
data: Object.freeze([]),
|
|
803
804
|
}),
|
|
804
805
|
|
package/engine/pack-config.mjs
CHANGED
|
@@ -104,7 +104,7 @@ import path from "node:path";
|
|
|
104
104
|
import { createRequire } from "node:module";
|
|
105
105
|
import YAML from "yaml";
|
|
106
106
|
|
|
107
|
-
import { defineConfig, DERIVED_SYSTEM_VERSION } from "../content-config.mjs";
|
|
107
|
+
import { defineConfig, DERIVED_SYSTEM_VERSION, DOCUMENTATION_KIND } from "../content-config.mjs";
|
|
108
108
|
import {
|
|
109
109
|
emitDiagnostic,
|
|
110
110
|
formatDiagnostic,
|
|
@@ -513,7 +513,7 @@ export function locateConfigError(err, configPath) {
|
|
|
513
513
|
*
|
|
514
514
|
* @param {unknown} data - The parsed configuration document.
|
|
515
515
|
* @param {string} configPath - Absolute path of the file it was parsed from.
|
|
516
|
-
* @returns {import("../config.mjs").ContentBuildConfig} The frozen configuration.
|
|
516
|
+
* @returns {import("../content-config.mjs").ContentBuildConfig} The frozen configuration.
|
|
517
517
|
* @throws {Error} When the document is not a mapping, declares `rootDir`, or
|
|
518
518
|
* names an item-builder registry this package does not ship.
|
|
519
519
|
*/
|
|
@@ -541,14 +541,19 @@ export function configFromData(data, configPath) {
|
|
|
541
541
|
// Every consumer's copy matched exactly, which is what a transcription
|
|
542
542
|
// looks like right up until it does not (a transcribed one freezes
|
|
543
543
|
// four releases while nothing said so).
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
544
|
+
// A documentation package has no Foundry package, so there is no id to
|
|
545
|
+
// derive: the validator refuses the key by name, with a locator, and
|
|
546
|
+
// deriving one here would hand it the very key it is about to refuse.
|
|
547
|
+
if (input.packageKind !== DOCUMENTATION_KIND) {
|
|
548
|
+
if (input.foundryPackage !== undefined) {
|
|
549
|
+
throw new Error(
|
|
550
|
+
`package-build: ${configPath} declares \`foundryPackage\`, which ` +
|
|
551
|
+
`a data configuration may not: it is the \`name\` of the ` +
|
|
552
|
+
`\`package.json\` beside it. Remove the key.`,
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
input.foundryPackage = foundryPackageId(rootDir);
|
|
550
556
|
}
|
|
551
|
-
input.foundryPackage = foundryPackageId(rootDir);
|
|
552
557
|
|
|
553
558
|
if (input.itemBuilders !== undefined) {
|
|
554
559
|
const declared = input.itemBuilders;
|
|
@@ -590,7 +595,16 @@ export function configFromData(data, configPath) {
|
|
|
590
595
|
}
|
|
591
596
|
|
|
592
597
|
const stats = input.stats;
|
|
593
|
-
|
|
598
|
+
// Skipped for a documentation package, which stamps no `_stats` and
|
|
599
|
+
// declares no system to take a version from: the derivation would throw a
|
|
600
|
+
// bare error about a missing system relationship, burying the located
|
|
601
|
+
// refusal of the `stats:` key that is the finding an author needs.
|
|
602
|
+
if (
|
|
603
|
+
input.packageKind !== DOCUMENTATION_KIND &&
|
|
604
|
+
stats !== null &&
|
|
605
|
+
typeof stats === "object" &&
|
|
606
|
+
!Array.isArray(stats)
|
|
607
|
+
) {
|
|
594
608
|
const declared = /** @type {Record<string, unknown>} */ (stats);
|
|
595
609
|
// `stats.systemId` and `stats.systemVersion` are both refused by
|
|
596
610
|
// `defineConfig`, which reports them with a locator — so nothing is
|
|
@@ -620,7 +634,7 @@ export function configFromData(data, configPath) {
|
|
|
620
634
|
* Load an `.mjs` configuration — one that called `defineConfig` itself.
|
|
621
635
|
*
|
|
622
636
|
* @param {string} configPath - Absolute path of the file.
|
|
623
|
-
* @returns {import("../config.mjs").ContentBuildConfig} What it exported.
|
|
637
|
+
* @returns {import("../content-config.mjs").ContentBuildConfig} What it exported.
|
|
624
638
|
* @throws {Error} When its module graph uses top-level `await`, which a
|
|
625
639
|
* synchronously-read configuration cannot.
|
|
626
640
|
*/
|
|
@@ -662,7 +676,7 @@ let loadedFrom;
|
|
|
662
676
|
* configuration (#2). The result is memoised, so calling it in a default
|
|
663
677
|
* parameter — the usual spelling here — costs one property read per call.
|
|
664
678
|
*
|
|
665
|
-
* @returns {import("../config.mjs").ContentBuildConfig} The frozen configuration.
|
|
679
|
+
* @returns {import("../content-config.mjs").ContentBuildConfig} The frozen configuration.
|
|
666
680
|
* @throws {Error} When no configuration file can be found, or the one named
|
|
667
681
|
* cannot be loaded. Absence is a defect, not a fallback: without it the
|
|
668
682
|
* compilers know neither what to compile nor where to put it.
|
package/engine/pack-router.mjs
CHANGED
|
Binary file
|