@heroiclands/package-build 23.8.0 → 23.10.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 +53 -0
- package/README.md +2 -0
- package/bin/content-commands.mjs +207 -70
- package/docs/api.md +646 -78
- package/docs/authoring/first-note.md +1 -1
- package/docs/authoring/links-and-markup.md +253 -102
- package/docs/commands.md +111 -13
- package/docs/content-format.md +7 -2
- package/docs/diagnostics.md +1 -1
- package/docs/guides/packs.md +2 -1
- package/docs/guides/reference-pages.md +86 -63
- package/docs/reference/format-details.md +398 -137
- package/docs/reference/note-types.md +188 -174
- package/engine/anchors.mjs +15 -26
- package/engine/base-compiler.mjs +6 -4
- package/engine/changelog-group.mjs +58 -34
- package/engine/changelog-lint.mjs +6 -2
- package/engine/compendiums.mjs +15 -1
- package/engine/compile-corpus.mjs +17 -3
- package/engine/content-charset.mjs +13 -1
- package/engine/content-format.yaml +1 -0
- package/engine/content-html.mjs +18 -4
- package/engine/content-icons.mjs +15 -1
- package/engine/content-images.mjs +28 -9
- package/engine/content-index.mjs +496 -16
- package/engine/content-links.mjs +70 -27
- package/engine/content-session.mjs +247 -0
- package/engine/content-sources.mjs +110 -0
- package/engine/content-spans.mjs +87 -8
- package/engine/content-tables.mjs +9 -4
- package/engine/content-tasklists.mjs +20 -4
- package/engine/culture-reach.mjs +61 -34
- package/engine/data-keys.mjs +3 -1
- package/engine/diagnostics.mjs +17 -6
- package/engine/dictionary-layout.mjs +151 -19
- package/engine/dictionary-rows.mjs +129 -79
- package/engine/extension-attributes.mjs +28 -5
- package/engine/first-use.mjs +371 -0
- package/engine/foundry-entries.mjs +29 -8
- package/engine/generate.mjs +13 -4
- package/engine/glossary-entries.mjs +11 -11
- package/engine/heading-attributes.mjs +21 -28
- package/engine/helpers.mjs +54 -19
- package/engine/index-records.mjs +1 -0
- package/engine/index.mjs +3 -0
- package/engine/journals.mjs +13 -6
- package/engine/language-data.mjs +55 -2
- package/engine/language-rows.mjs +4 -4
- package/engine/language-terms.mjs +90 -4
- package/engine/language-words.mjs +2 -2
- package/engine/lexical-sections.mjs +247 -0
- package/engine/markup-validation.mjs +7 -2
- package/engine/metadata-index.mjs +1 -0
- package/engine/name-markup.mjs +114 -0
- package/engine/name-rows.mjs +70 -0
- package/engine/names.mjs +460 -0
- package/engine/note-addresses.mjs +25 -4
- package/engine/note-claims.mjs +2 -0
- package/engine/note-state.mjs +9 -3
- package/engine/note-vocabulary.mjs +7 -6
- package/engine/path-charset.mjs +24 -1
- package/engine/pdf-build.mjs +9 -9
- package/engine/pdf-render.mjs +15 -1
- package/engine/site-build.mjs +16 -2
- package/engine/site-index.mjs +5 -1
- package/engine/sql-tables.mjs +324 -44
- package/engine/stub-lint.mjs +10 -5
- package/engine/web-wikilinks.mjs +8 -6
- package/engine/word-rows.mjs +20 -12
- package/engine/word-terms.mjs +15 -6
- package/engine/yaml-lint.mjs +3 -5
- package/hugo-theme/static/css/style.css +4 -0
- package/package.json +1 -1
- package/types/engine/anchors.d.mts +4 -2
- package/types/engine/compendiums.d.mts +5 -1
- package/types/engine/compile-corpus.d.mts +5 -2
- package/types/engine/content-charset.d.mts +5 -1
- package/types/engine/content-html.d.mts +5 -1
- package/types/engine/content-icons.d.mts +5 -1
- package/types/engine/content-images.d.mts +5 -1
- package/types/engine/content-index.d.mts +102 -18
- package/types/engine/content-links.d.mts +22 -8
- package/types/engine/content-session.d.mts +108 -0
- package/types/engine/content-sources.d.mts +32 -0
- package/types/engine/content-spans.d.mts +3 -5
- package/types/engine/content-tables.d.mts +2 -1
- package/types/engine/content-tasklists.d.mts +5 -1
- package/types/engine/culture-reach.d.mts +22 -2
- package/types/engine/data-keys.d.mts +7 -0
- package/types/engine/diagnostics.d.mts +10 -0
- package/types/engine/dictionary-layout.d.mts +35 -4
- package/types/engine/dictionary-rows.d.mts +9 -0
- package/types/engine/extension-attributes.d.mts +16 -2
- package/types/engine/first-use.d.mts +31 -0
- package/types/engine/foundry-entries.d.mts +14 -5
- package/types/engine/generate.d.mts +5 -1
- package/types/engine/heading-attributes.d.mts +2 -2
- package/types/engine/helpers.d.mts +33 -7
- package/types/engine/index.d.mts +1 -0
- package/types/engine/journals.d.mts +5 -2
- package/types/engine/language-terms.d.mts +7 -0
- package/types/engine/lexical-sections.d.mts +96 -0
- package/types/engine/markup-validation.d.mts +2 -1
- package/types/engine/name-markup.d.mts +176 -0
- package/types/engine/name-rows.d.mts +35 -0
- package/types/engine/names.d.mts +356 -0
- package/types/engine/note-addresses.d.mts +3 -3
- package/types/engine/note-state.d.mts +2 -1
- package/types/engine/path-charset.d.mts +16 -1
- package/types/engine/site-build.d.mts +6 -3
- package/types/engine/site-index.d.mts +3 -1
- package/types/engine/sql-tables.d.mts +39 -4
- package/types/engine/stub-lint.d.mts +3 -1
- package/types/engine/word-rows.d.mts +9 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 23.10.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
**Notes**
|
|
8
|
+
|
|
9
|
+
- Notes with CRLF frontmatter retain their identity, body and links in compiled content and published indexes.
|
|
10
|
+
- Correct malformed YAML in CRLF frontmatter before building; YAML errors in these notes prevent publication.
|
|
11
|
+
|
|
12
|
+
**Language notes**
|
|
13
|
+
|
|
14
|
+
- Language notes write a name element's joining form as `joining`; `genitive` is refused.
|
|
15
|
+
- Compiled-skill references in culture, literature and affiliation language fields are refused instead of silently dropping glossary entries; write `skill-provnzlng` or `<pkg>-note-skill-provnzlng`.
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
**Names**
|
|
20
|
+
|
|
21
|
+
- Mark names without notes as `[Name]{.name}` and link indexed names; lint checks introductions and repeats, and marked names appear bold on sites and in books.
|
|
22
|
+
- Mark unwritten names with `[Name]{.name data-type="place"}`; query indexed uses and review grouped lint advice about unwritten names and conflicting types.
|
|
23
|
+
|
|
24
|
+
**Commands and release notes**
|
|
25
|
+
|
|
26
|
+
- Selected content checks and compendium builds can run together in one command.
|
|
27
|
+
- Release notes retain every summary when paragraphs and bullets are combined.
|
|
28
|
+
|
|
29
|
+
## 23.9.0
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
**Dictionaries and glossaries**
|
|
34
|
+
|
|
35
|
+
- Language dictionaries and declared culture or affiliation glossaries appear automatically in journals, sites and books; remove SQL fences for these defaults.
|
|
36
|
+
- Dictionaries use one full alphabetical list with field tags and one key; existing anchored Dictionary, Lexicon and Glossary headings keep their introductions and receive the lists.
|
|
37
|
+
- Language notes with nonempty `data.words`, and cultures or affiliations with nonempty `data.glossary`, need no prose body; glossary sections show only their owner's declared entries.
|
|
38
|
+
- Glossary entries naming dictionary words show their class and word link, with dictionary senses or the entry's context definition; sole-owner sense labels are omitted on that owner's page, while other attribution remains.
|
|
39
|
+
- Common marks belong only to glossary terms; select or filter `terms.common`, because queries using `dictionary.common` fail.
|
|
40
|
+
- Custom culture views select the culture's own entries and reached ranks or offices, applying common marks only to entries the culture owns.
|
|
41
|
+
- Glossary entries without definitions must name a word in their resolved language or a reachable rank or office; specify the correct language or supply a definition when a name cannot resolve.
|
|
42
|
+
|
|
43
|
+
**Links and publishing**
|
|
44
|
+
|
|
45
|
+
- Correct links to headings inside HTML comments or raw HTML, which publish no section; builds also refuse section links absent from a dependency's published notes, including pages without prose anchors or journal documents.
|
|
46
|
+
- Rebuild and fetch package indexes so dependent packages can link to generated sections and SQL-expression anchors; readable item documentation anchors target their documentation journals.
|
|
47
|
+
- Engine publishers with dynamic content must await `prepareContentIndex` and pass its publication to `emitContentIndex` before writing metadata.
|
|
48
|
+
|
|
49
|
+
### Patch Changes
|
|
50
|
+
|
|
51
|
+
- Language dictionaries can show optional noun plurals and one verb pattern beside existing gender labels; languages without these details retain their output.
|
|
52
|
+
- Dictionary fragments without a key omit the verb table; key-only fragments print abbreviations alone.
|
|
53
|
+
- Built words show element senses in dictionary etymologies and pass the built-piece check, including declared variants, inherited elements and front-vowel affix forms.
|
|
54
|
+
- Public dictionaries omit GM-only citations, preventing false link errors from hidden notes.
|
|
55
|
+
|
|
3
56
|
## 23.8.0
|
|
4
57
|
|
|
5
58
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ npm install -D @heroiclands/package-build
|
|
|
16
16
|
|
|
17
17
|
The generated project includes a homepage, website and book targets, npm scripts, and a local `.gitignore`. [Getting started](docs/getting-started.md) routes you to a [system](docs/guides/system-project.md), [module](docs/guides/module-project.md), or [content authoring](docs/authoring/first-note.md) path. The [content-language-server](https://github.com/HeroicLands/content-language-server) provides editor navigation and completion.
|
|
18
18
|
|
|
19
|
+
Run selected content operations together with `package-build content run index lint links compile`. The [shared content command](docs/commands.md#package-build-content-run-targets) captures one source tree for the selected checks and outputs; standalone commands remain available.
|
|
20
|
+
|
|
19
21
|
## Working on this repository
|
|
20
22
|
|
|
21
23
|
Run `npm ci` in each fresh clone or worktree. Its `prepare` step installs the
|
package/bin/content-commands.mjs
CHANGED
|
@@ -105,14 +105,22 @@ import { markupFindings } from "../engine/markup-validation.mjs";
|
|
|
105
105
|
import { checkFormatting, checkPrettierConventions, lintMarkdown } from "../engine/prose-lint.mjs";
|
|
106
106
|
import { lintProse, scoreProseTree } from "../engine/readability-lint.mjs";
|
|
107
107
|
import { loadPackageBuildConfig } from "../config.mjs";
|
|
108
|
-
import {
|
|
108
|
+
import {
|
|
109
|
+
emitContentIndex,
|
|
110
|
+
prepareContentIndex,
|
|
111
|
+
indexRecordsFor,
|
|
112
|
+
isAssetRecord,
|
|
113
|
+
} from "../engine/content-index.mjs";
|
|
109
114
|
import {
|
|
110
115
|
buildSite,
|
|
111
116
|
gatesFailed,
|
|
112
117
|
formatUnaddressableFinding as formatUnaddressable,
|
|
113
118
|
} from "../engine/site-build.mjs";
|
|
114
119
|
import { auditLinks, buildLinkIndex, walkReachability } from "../engine/content-links.mjs";
|
|
115
|
-
import { prepareTreeSqlTables } from "../engine/sql-tables.mjs";
|
|
120
|
+
import { prepareLinkSqlTables, prepareTreeSqlTables } from "../engine/sql-tables.mjs";
|
|
121
|
+
import { createContentSession, CONTENT_TARGETS } from "../engine/content-session.mjs";
|
|
122
|
+
import { createFirstUseChecker } from "../engine/first-use.mjs";
|
|
123
|
+
import { createNameCatalog, createNamesReport, namesReportFinding } from "../engine/names.mjs";
|
|
116
124
|
// The one place a link finding is worded, shared with both builds.
|
|
117
125
|
import { linkFindingMessage } from "../engine/wikilink-syntax.mjs";
|
|
118
126
|
import {
|
|
@@ -178,6 +186,11 @@ prefix.apply(log, {
|
|
|
178
186
|
},
|
|
179
187
|
});
|
|
180
188
|
|
|
189
|
+
const CONTENT_SESSION = Symbol("content session");
|
|
190
|
+
const CONTENT_CONFIG = Symbol("content configuration");
|
|
191
|
+
const PREPARATION_REPORTED = Symbol("preparation reported");
|
|
192
|
+
const REPORTED_FAILURES = Symbol("reported preparation failures");
|
|
193
|
+
|
|
181
194
|
/**
|
|
182
195
|
* Report a command's failure.
|
|
183
196
|
*
|
|
@@ -198,6 +211,29 @@ function reportFailure(err) {
|
|
|
198
211
|
else log.error(message);
|
|
199
212
|
}
|
|
200
213
|
|
|
214
|
+
/** Report one shared preparation failure once in a combined invocation. */
|
|
215
|
+
function reportContentFailure(err, argv) {
|
|
216
|
+
const reported = argv[REPORTED_FAILURES];
|
|
217
|
+
if (reported?.has(err)) return;
|
|
218
|
+
reported?.add(err);
|
|
219
|
+
reportFailure(err);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Apply the package command's existing eligibility rule before a selected writer.
|
|
223
|
+
* @param {object} config - Resolved configuration.
|
|
224
|
+
* @param {string} action - The requested package action.
|
|
225
|
+
*/
|
|
226
|
+
function assertCompendiumOperation(config, action) {
|
|
227
|
+
if (!compilesFoundryDocuments(config)) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
`\`packageKind: ${config.packageKind}\` compiles no ` +
|
|
230
|
+
`compendium, so there is nothing to ${action}. Build ` +
|
|
231
|
+
`its site with \`package-build site\` and its book ` +
|
|
232
|
+
`with \`package-build pdf\`.`,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
201
237
|
/**
|
|
202
238
|
* A note's source text, or `""` when it cannot be read.
|
|
203
239
|
*
|
|
@@ -230,7 +266,12 @@ export function registerContentCommands(cli) {
|
|
|
230
266
|
return cli
|
|
231
267
|
.command(
|
|
232
268
|
withIndexPreflight(
|
|
233
|
-
withBodyPreflight(
|
|
269
|
+
withBodyPreflight(
|
|
270
|
+
packageCommand(),
|
|
271
|
+
(_config, argv) => argv.action === "compile",
|
|
272
|
+
undefined,
|
|
273
|
+
true,
|
|
274
|
+
),
|
|
234
275
|
(_config, argv) => argv.action === "compile",
|
|
235
276
|
),
|
|
236
277
|
)
|
|
@@ -240,6 +281,7 @@ export function registerContentCommands(cli) {
|
|
|
240
281
|
.command(proseCommand())
|
|
241
282
|
.command(contentFormatCommand())
|
|
242
283
|
.command(withIndexPreflight(linksCommand()))
|
|
284
|
+
.command(withIndexPreflight(contentCommand()))
|
|
243
285
|
.command(formatCommand())
|
|
244
286
|
.command(markdownCommand())
|
|
245
287
|
.command(
|
|
@@ -247,6 +289,7 @@ export function registerContentCommands(cli) {
|
|
|
247
289
|
contentIndexCommand(),
|
|
248
290
|
() => true,
|
|
249
291
|
(config, argv) => argv.root || config.paths.content,
|
|
292
|
+
true,
|
|
250
293
|
),
|
|
251
294
|
)
|
|
252
295
|
.command(withIndexPreflight(withBodyPreflight(siteCommand()), publishesContentPages))
|
|
@@ -399,19 +442,33 @@ function withBodyPreflight(
|
|
|
399
442
|
command,
|
|
400
443
|
shouldCheck = () => true,
|
|
401
444
|
contentRoot = (config) => config.paths.content,
|
|
445
|
+
shareCapture = false,
|
|
402
446
|
) {
|
|
403
447
|
const handler = command.handler;
|
|
404
448
|
return {
|
|
405
449
|
...command,
|
|
406
450
|
handler: async (argv) => {
|
|
451
|
+
let owned;
|
|
407
452
|
try {
|
|
408
|
-
const config = loadPackConfig();
|
|
453
|
+
const config = argv[CONTENT_CONFIG] ?? loadPackConfig();
|
|
454
|
+
let next = argv;
|
|
409
455
|
if (shouldCheck(config, argv)) {
|
|
410
456
|
const contentBase = contentRoot(config, argv);
|
|
411
457
|
if (!fs.existsSync(contentBase)) return handler(argv);
|
|
412
|
-
const
|
|
458
|
+
const session =
|
|
459
|
+
shareCapture ?
|
|
460
|
+
(argv[CONTENT_SESSION] ??
|
|
461
|
+
(owned = await createContentSession({ config, contentBase })))
|
|
462
|
+
: undefined;
|
|
463
|
+
const records = session?.records ?? indexRecordsFor({ config, contentBase });
|
|
464
|
+
if (session?.problems.length) {
|
|
465
|
+
emitDiagnostic(session.problems[0]);
|
|
466
|
+
process.exitCode = 1;
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
413
469
|
const { findings } = lintNoteStates(contentBase, {
|
|
414
470
|
records,
|
|
471
|
+
parsed: session?.parsed,
|
|
415
472
|
contentPackage: config.contentPackage,
|
|
416
473
|
});
|
|
417
474
|
const errors = findings.filter((finding) => finding.severity === "error");
|
|
@@ -420,13 +477,16 @@ function withBodyPreflight(
|
|
|
420
477
|
process.exitCode = 1;
|
|
421
478
|
return;
|
|
422
479
|
}
|
|
480
|
+
if (session) next = { ...argv, [CONTENT_SESSION]: session };
|
|
423
481
|
}
|
|
482
|
+
return await handler(next);
|
|
424
483
|
} catch (err) {
|
|
425
484
|
reportFailure(err);
|
|
426
485
|
process.exitCode = 1;
|
|
427
486
|
return;
|
|
487
|
+
} finally {
|
|
488
|
+
owned?.dispose();
|
|
428
489
|
}
|
|
429
|
-
return handler(argv);
|
|
430
490
|
},
|
|
431
491
|
};
|
|
432
492
|
}
|
|
@@ -452,7 +512,7 @@ function withIndexPreflight(command, shouldCheck = () => true) {
|
|
|
452
512
|
process.exitCode = 1;
|
|
453
513
|
return;
|
|
454
514
|
}
|
|
455
|
-
return handler(argv);
|
|
515
|
+
return handler({ ...argv, [CONTENT_CONFIG]: config });
|
|
456
516
|
},
|
|
457
517
|
};
|
|
458
518
|
}
|
|
@@ -901,8 +961,10 @@ function lintCommand() {
|
|
|
901
961
|
});
|
|
902
962
|
},
|
|
903
963
|
handler: async (argv) => {
|
|
964
|
+
let session;
|
|
904
965
|
try {
|
|
905
|
-
const config =
|
|
966
|
+
const config =
|
|
967
|
+
argv[CONTENT_SESSION]?.config ?? argv[CONTENT_CONFIG] ?? loadPackConfig();
|
|
906
968
|
const root = argv.root ?? config.paths.content;
|
|
907
969
|
|
|
908
970
|
// The corpus, enumerated once for this command and handed to
|
|
@@ -916,30 +978,22 @@ function lintCommand() {
|
|
|
916
978
|
// finding rather than thrown: one malformed note must
|
|
917
979
|
// not take every other finding in the tree with it, and the
|
|
918
980
|
// reader needs a line to open, not a stack.
|
|
919
|
-
const corpusProblems = [];
|
|
920
|
-
const parsed = new Map();
|
|
921
981
|
const indexStarted = performance.now();
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
config,
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
parsed,
|
|
928
|
-
});
|
|
982
|
+
session =
|
|
983
|
+
argv[CONTENT_SESSION] ??
|
|
984
|
+
(await createContentSession({ config, contentBase: root }));
|
|
985
|
+
session.assertContext({ config, contentBase: root });
|
|
986
|
+
const { problems: corpusProblems, parsed, records } = session;
|
|
929
987
|
const indexMs = performance.now() - indexStarted;
|
|
930
|
-
|
|
988
|
+
if (!argv[PREPARATION_REPORTED])
|
|
989
|
+
for (const problem of corpusProblems) emitDiagnostic(problem);
|
|
931
990
|
// An error whatever the command's own strictness: the note is
|
|
932
991
|
// absent from every answer below, so reporting it and exiting 0
|
|
933
992
|
// would call the tree clean while silently omitting a note.
|
|
934
993
|
if (corpusProblems.length) process.exitCode = 1;
|
|
935
994
|
|
|
936
995
|
const sqlStarted = performance.now();
|
|
937
|
-
const sqlTables = await
|
|
938
|
-
config,
|
|
939
|
-
records,
|
|
940
|
-
parsed,
|
|
941
|
-
skipDirectories: config.skipDirectories,
|
|
942
|
-
});
|
|
996
|
+
const sqlTables = await session.sqlTables("all");
|
|
943
997
|
const sqlMs = performance.now() - sqlStarted;
|
|
944
998
|
const validationStarted = performance.now();
|
|
945
999
|
|
|
@@ -954,12 +1008,19 @@ function lintCommand() {
|
|
|
954
1008
|
// One index, built once, for the reference check. It is the
|
|
955
1009
|
// same resolver the wikilink audit uses, so a frontmatter
|
|
956
1010
|
// reference and a body link answer the same way.
|
|
957
|
-
const index =
|
|
958
|
-
|
|
1011
|
+
const index = await session.audit("lint");
|
|
1012
|
+
const catalog = createNameCatalog({ records, index });
|
|
1013
|
+
const firstUseChecker = createFirstUseChecker({
|
|
959
1014
|
records,
|
|
960
|
-
|
|
961
|
-
|
|
1015
|
+
index,
|
|
1016
|
+
parsed: session.parsed,
|
|
1017
|
+
syntax: session.syntax,
|
|
1018
|
+
catalog,
|
|
962
1019
|
});
|
|
1020
|
+
const names = namesReportFinding(
|
|
1021
|
+
createNamesReport({ package: config.contentPackage, records, catalog }),
|
|
1022
|
+
{ contentBase: root },
|
|
1023
|
+
);
|
|
963
1024
|
// Which system blocks this tree carries, derived from what it
|
|
964
1025
|
// declares it ships for. Read before the lint so the
|
|
965
1026
|
// systems it will *not* check can be said out loud below.
|
|
@@ -1099,6 +1160,7 @@ function lintCommand() {
|
|
|
1099
1160
|
// the subject is every character authored, including the ones
|
|
1100
1161
|
// that stopped a note parsing at all.
|
|
1101
1162
|
const charset = lintContentCharset(root, {
|
|
1163
|
+
sources: session.sources,
|
|
1102
1164
|
skipDirectories: config.skipDirectories,
|
|
1103
1165
|
});
|
|
1104
1166
|
|
|
@@ -1107,6 +1169,7 @@ function lintCommand() {
|
|
|
1107
1169
|
// than the parsed notes, so a picture, which has no note,
|
|
1108
1170
|
// is covered by the same rule.
|
|
1109
1171
|
const pathCharset = lintPathCharset({
|
|
1172
|
+
inventory: session.inventory,
|
|
1110
1173
|
contentBase: root,
|
|
1111
1174
|
assetsBase: config.paths.assets,
|
|
1112
1175
|
skipDirectories: config.skipDirectories,
|
|
@@ -1121,6 +1184,7 @@ function lintCommand() {
|
|
|
1121
1184
|
// package's own entries merged over it, which is where a
|
|
1122
1185
|
// Game-Icons glyph becomes a name a note may write.
|
|
1123
1186
|
const icons = lintContentIcons(root, {
|
|
1187
|
+
sources: session.sources,
|
|
1124
1188
|
skipDirectories: config.skipDirectories,
|
|
1125
1189
|
registry: config.icons,
|
|
1126
1190
|
});
|
|
@@ -1130,6 +1194,7 @@ function lintCommand() {
|
|
|
1130
1194
|
// still unrenderable in a book — the character rule one level
|
|
1131
1195
|
// up.
|
|
1132
1196
|
const html = lintContentHtml(root, {
|
|
1197
|
+
sources: session.sources,
|
|
1133
1198
|
skipDirectories: config.skipDirectories,
|
|
1134
1199
|
});
|
|
1135
1200
|
|
|
@@ -1138,6 +1203,7 @@ function lintCommand() {
|
|
|
1138
1203
|
// ordinary width looks exactly like a directive that worked —
|
|
1139
1204
|
// so these are errors and they fail the run.
|
|
1140
1205
|
const images = lintContentImages(root, {
|
|
1206
|
+
sources: session.sources,
|
|
1141
1207
|
skipDirectories: config.skipDirectories,
|
|
1142
1208
|
// Which packages this build can reach is configuration, and
|
|
1143
1209
|
// an address naming one it cannot reaches a journal as a
|
|
@@ -1149,6 +1215,7 @@ function lintCommand() {
|
|
|
1149
1215
|
// miscompiled, since every surface sets the marker as the
|
|
1150
1216
|
// item's own text and all three agree, so this is advisory.
|
|
1151
1217
|
const taskLists = lintContentTaskLists(root, {
|
|
1218
|
+
sources: session.sources,
|
|
1152
1219
|
skipDirectories: config.skipDirectories,
|
|
1153
1220
|
});
|
|
1154
1221
|
|
|
@@ -1158,6 +1225,7 @@ function lintCommand() {
|
|
|
1158
1225
|
// wrong, and 464 warnings a reader cannot clear is how a report
|
|
1159
1226
|
// teaches its reader to stop looking at it.
|
|
1160
1227
|
const states = lintNoteStates(root, {
|
|
1228
|
+
parsed,
|
|
1161
1229
|
records,
|
|
1162
1230
|
contentPackage: config.contentPackage,
|
|
1163
1231
|
});
|
|
@@ -1190,7 +1258,11 @@ function lintCommand() {
|
|
|
1190
1258
|
});
|
|
1191
1259
|
|
|
1192
1260
|
const findings = [
|
|
1193
|
-
...index.notes.flatMap((note) =>
|
|
1261
|
+
...index.notes.flatMap((note) =>
|
|
1262
|
+
markupFindings(note.raw, note.file, session.syntax),
|
|
1263
|
+
),
|
|
1264
|
+
...index.notes.flatMap(firstUseChecker),
|
|
1265
|
+
...(names ? [names] : []),
|
|
1194
1266
|
...addresses.findings,
|
|
1195
1267
|
...frontmatter.findings,
|
|
1196
1268
|
...schemaFindings,
|
|
@@ -1229,8 +1301,10 @@ function lintCommand() {
|
|
|
1229
1301
|
`SQL ${sqlMs.toFixed(1)} ms; validation ${validationMs.toFixed(1)} ms.`,
|
|
1230
1302
|
);
|
|
1231
1303
|
} catch (err) {
|
|
1232
|
-
|
|
1304
|
+
reportContentFailure(err, argv);
|
|
1233
1305
|
process.exitCode = 1;
|
|
1306
|
+
} finally {
|
|
1307
|
+
if (!argv[CONTENT_SESSION]) session?.dispose();
|
|
1234
1308
|
}
|
|
1235
1309
|
},
|
|
1236
1310
|
};
|
|
@@ -1402,6 +1476,73 @@ function markdownCommand() {
|
|
|
1402
1476
|
* @returns {object} The yargs command module.
|
|
1403
1477
|
*/
|
|
1404
1478
|
// eslint-disable-next-line
|
|
1479
|
+
function contentCommand() {
|
|
1480
|
+
return {
|
|
1481
|
+
command: "content <action> <targets..>",
|
|
1482
|
+
describe: "Run selected content operations with one in-memory source capture",
|
|
1483
|
+
builder: (yargs) =>
|
|
1484
|
+
yargs
|
|
1485
|
+
.positional("action", { type: "string", choices: ["run"] })
|
|
1486
|
+
.positional("targets", { type: "string", array: true, choices: CONTENT_TARGETS })
|
|
1487
|
+
.option("references", {
|
|
1488
|
+
type: "boolean",
|
|
1489
|
+
default: true,
|
|
1490
|
+
describe: "Check frontmatter references during lint",
|
|
1491
|
+
})
|
|
1492
|
+
.option("pack", {
|
|
1493
|
+
type: "string",
|
|
1494
|
+
describe: "Select one pack for the compile target",
|
|
1495
|
+
})
|
|
1496
|
+
.option("out", {
|
|
1497
|
+
type: "string",
|
|
1498
|
+
describe: "Output directory for the index target",
|
|
1499
|
+
}),
|
|
1500
|
+
handler: async (argv) => {
|
|
1501
|
+
let session;
|
|
1502
|
+
const failures = new Set();
|
|
1503
|
+
const problems = new Set();
|
|
1504
|
+
const shared = { ...argv, [PREPARATION_REPORTED]: true, [REPORTED_FAILURES]: failures };
|
|
1505
|
+
try {
|
|
1506
|
+
const config = argv[CONTENT_CONFIG] ?? loadPackConfig();
|
|
1507
|
+
session = await createContentSession({ config });
|
|
1508
|
+
shared[CONTENT_SESSION] = session;
|
|
1509
|
+
const targets = CONTENT_TARGETS.filter((target) => argv.targets.includes(target));
|
|
1510
|
+
let preparationFailed = false;
|
|
1511
|
+
try {
|
|
1512
|
+
if (targets.includes("compile")) assertCompendiumOperation(config, "compile");
|
|
1513
|
+
await session.prepare(targets);
|
|
1514
|
+
} catch (error) {
|
|
1515
|
+
preparationFailed = true;
|
|
1516
|
+
process.exitCode = 1;
|
|
1517
|
+
if (error.findings) {
|
|
1518
|
+
for (const finding of error.findings) {
|
|
1519
|
+
emitDiagnostic(finding);
|
|
1520
|
+
problems.add(finding);
|
|
1521
|
+
}
|
|
1522
|
+
failures.add(error);
|
|
1523
|
+
} else reportContentFailure(error, shared);
|
|
1524
|
+
}
|
|
1525
|
+
for (const problem of session.problems) {
|
|
1526
|
+
if (!problems.has(problem)) emitDiagnostic(problem);
|
|
1527
|
+
process.exitCode = 1;
|
|
1528
|
+
}
|
|
1529
|
+
for (const target of targets) {
|
|
1530
|
+
if (preparationFailed && (target === "index" || target === "compile")) continue;
|
|
1531
|
+
if (target === "index") await contentIndexCommand().handler(shared);
|
|
1532
|
+
else if (target === "lint") await lintCommand().handler(shared);
|
|
1533
|
+
else if (target === "links") await linksCommand().handler(shared);
|
|
1534
|
+
else await packageCommand().handler({ ...shared, action: "compile" });
|
|
1535
|
+
}
|
|
1536
|
+
} catch (error) {
|
|
1537
|
+
reportContentFailure(error, shared);
|
|
1538
|
+
process.exitCode = 1;
|
|
1539
|
+
} finally {
|
|
1540
|
+
session?.dispose();
|
|
1541
|
+
}
|
|
1542
|
+
},
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1405
1546
|
function linksCommand() {
|
|
1406
1547
|
return {
|
|
1407
1548
|
command: "links [root]",
|
|
@@ -1413,38 +1554,23 @@ function linksCommand() {
|
|
|
1413
1554
|
});
|
|
1414
1555
|
},
|
|
1415
1556
|
handler: async (argv) => {
|
|
1557
|
+
let session;
|
|
1416
1558
|
try {
|
|
1417
|
-
const config =
|
|
1559
|
+
const config =
|
|
1560
|
+
argv[CONTENT_SESSION]?.config ?? argv[CONTENT_CONFIG] ?? loadPackConfig();
|
|
1418
1561
|
const contentBase = argv.root ?? config.paths.content;
|
|
1419
1562
|
|
|
1420
1563
|
const scope = { skipDirectories: config.skipDirectories };
|
|
1421
1564
|
// Enumerated once and shared, as in `lint`, and a note
|
|
1422
1565
|
// it cannot record is reported rather than thrown.
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
problems: corpusProblems,
|
|
1430
|
-
parsed,
|
|
1431
|
-
});
|
|
1432
|
-
for (const problem of corpusProblems) emitDiagnostic(problem);
|
|
1433
|
-
// An error whatever the command's own strictness: the note is
|
|
1434
|
-
// absent from every answer below, so reporting it and exiting 0
|
|
1435
|
-
// would call the tree clean while silently omitting a note.
|
|
1566
|
+
session =
|
|
1567
|
+
argv[CONTENT_SESSION] ?? (await createContentSession({ config, contentBase }));
|
|
1568
|
+
session.assertContext({ config, contentBase });
|
|
1569
|
+
const { problems: corpusProblems } = session;
|
|
1570
|
+
if (!argv[PREPARATION_REPORTED])
|
|
1571
|
+
for (const problem of corpusProblems) emitDiagnostic(problem);
|
|
1436
1572
|
if (corpusProblems.length) process.exitCode = 1;
|
|
1437
|
-
const index =
|
|
1438
|
-
config,
|
|
1439
|
-
records,
|
|
1440
|
-
...scope,
|
|
1441
|
-
sqlTables: await prepareTreeSqlTables(contentBase, {
|
|
1442
|
-
config,
|
|
1443
|
-
records,
|
|
1444
|
-
parsed,
|
|
1445
|
-
...scope,
|
|
1446
|
-
}),
|
|
1447
|
-
});
|
|
1573
|
+
const index = await session.audit("links");
|
|
1448
1574
|
|
|
1449
1575
|
// An unusable manifest would otherwise surface as a pile of
|
|
1450
1576
|
// dead addresses pointing at the notes that cite it, rather
|
|
@@ -1571,8 +1697,10 @@ function linksCommand() {
|
|
|
1571
1697
|
);
|
|
1572
1698
|
}
|
|
1573
1699
|
} catch (err) {
|
|
1574
|
-
|
|
1700
|
+
reportContentFailure(err, argv);
|
|
1575
1701
|
process.exitCode = 1;
|
|
1702
|
+
} finally {
|
|
1703
|
+
if (!argv[CONTENT_SESSION]) session?.dispose();
|
|
1576
1704
|
}
|
|
1577
1705
|
},
|
|
1578
1706
|
};
|
|
@@ -1616,12 +1744,20 @@ function contentIndexCommand() {
|
|
|
1616
1744
|
type: "string",
|
|
1617
1745
|
});
|
|
1618
1746
|
},
|
|
1619
|
-
handler: (argv) => {
|
|
1747
|
+
handler: async (argv) => {
|
|
1748
|
+
let session;
|
|
1620
1749
|
try {
|
|
1621
|
-
const config =
|
|
1750
|
+
const config =
|
|
1751
|
+
argv[CONTENT_SESSION]?.config ?? argv[CONTENT_CONFIG] ?? loadPackConfig();
|
|
1752
|
+
const contentBase = argv.root ?? config.paths.content;
|
|
1753
|
+
session =
|
|
1754
|
+
argv[CONTENT_SESSION] ?? (await createContentSession({ config, contentBase }));
|
|
1755
|
+
session.assertContext({ config, contentBase });
|
|
1756
|
+
const publication = await session.publication();
|
|
1622
1757
|
const { file, notes, bytes, full, draft, stub, dictionary, terms } =
|
|
1623
1758
|
emitContentIndex({
|
|
1624
1759
|
config,
|
|
1760
|
+
publication,
|
|
1625
1761
|
...(argv.root ? { contentBase: argv.root } : {}),
|
|
1626
1762
|
...(argv.out ? { outDir: argv.out } : {}),
|
|
1627
1763
|
});
|
|
@@ -1636,8 +1772,10 @@ function contentIndexCommand() {
|
|
|
1636
1772
|
`(${written.rows} ${relation} rows)`,
|
|
1637
1773
|
);
|
|
1638
1774
|
} catch (err) {
|
|
1639
|
-
|
|
1775
|
+
reportContentFailure(err, argv);
|
|
1640
1776
|
process.exitCode = 1;
|
|
1777
|
+
} finally {
|
|
1778
|
+
if (!argv[CONTENT_SESSION]) session?.dispose();
|
|
1641
1779
|
}
|
|
1642
1780
|
},
|
|
1643
1781
|
};
|
|
@@ -2530,19 +2668,13 @@ function packageCommand() {
|
|
|
2530
2668
|
// an unhandled-rejection stack trace. Report the message and set a
|
|
2531
2669
|
// failing exit code, so a build guard reads as a build failure.
|
|
2532
2670
|
try {
|
|
2533
|
-
const config =
|
|
2671
|
+
const config =
|
|
2672
|
+
argv[CONTENT_SESSION]?.config ?? argv[CONTENT_CONFIG] ?? loadPackConfig();
|
|
2534
2673
|
// The compile passes do not run for a package that declares no
|
|
2535
2674
|
// packs by rule. Refused rather than exiting 0 having compiled
|
|
2536
2675
|
// nothing: a build that succeeds and produces no documents is
|
|
2537
2676
|
// the quiet failure this toolchain reports everywhere else.
|
|
2538
|
-
|
|
2539
|
-
throw new Error(
|
|
2540
|
-
`\`packageKind: ${config.packageKind}\` compiles no ` +
|
|
2541
|
-
`compendium, so there is nothing to ${action}. Build ` +
|
|
2542
|
-
`its site with \`package-build site\` and its book ` +
|
|
2543
|
-
`with \`package-build pdf\`.`,
|
|
2544
|
-
);
|
|
2545
|
-
}
|
|
2677
|
+
assertCompendiumOperation(config, action);
|
|
2546
2678
|
// The one directory the pipeline creates rather than expects:
|
|
2547
2679
|
// `unpack` writes the extracted JSON there and `compile` reads
|
|
2548
2680
|
// it back. Created here rather than at module scope so that
|
|
@@ -2555,7 +2687,12 @@ function packageCommand() {
|
|
|
2555
2687
|
// from the resolved configuration, so nothing is restated
|
|
2556
2688
|
// here.
|
|
2557
2689
|
case "compile":
|
|
2558
|
-
return await compilePacks({
|
|
2690
|
+
return await compilePacks({
|
|
2691
|
+
packName: pack,
|
|
2692
|
+
config,
|
|
2693
|
+
session: argv[CONTENT_SESSION],
|
|
2694
|
+
reportPreparation: !argv[PREPARATION_REPORTED],
|
|
2695
|
+
});
|
|
2559
2696
|
case "clean":
|
|
2560
2697
|
return await cleanPacks({
|
|
2561
2698
|
packName: pack,
|
|
@@ -2569,7 +2706,7 @@ function packageCommand() {
|
|
|
2569
2706
|
});
|
|
2570
2707
|
}
|
|
2571
2708
|
} catch (err) {
|
|
2572
|
-
|
|
2709
|
+
reportContentFailure(err, argv);
|
|
2573
2710
|
process.exitCode = 1;
|
|
2574
2711
|
}
|
|
2575
2712
|
},
|