@heroiclands/package-build 23.9.0 → 23.11.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 +33 -0
- package/README.md +2 -0
- package/bin/content-commands.mjs +197 -68
- package/docs/api.md +365 -70
- package/docs/authoring/first-note.md +10 -0
- package/docs/authoring/frontmatter.md +1 -1
- package/docs/authoring/links-and-markup.md +186 -49
- package/docs/commands.md +92 -3
- package/docs/content-format.md +10 -3
- package/docs/design.md +18 -0
- package/docs/guides/book.md +7 -0
- package/docs/guides/site.md +7 -0
- package/docs/reference/format-details.md +186 -15
- package/docs/reference/note-types.md +4 -4
- package/engine/anchors.mjs +43 -9
- 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 +14 -1
- package/engine/content-charset.mjs +13 -1
- 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 +102 -23
- package/engine/content-links.mjs +68 -30
- package/engine/content-session.mjs +247 -0
- package/engine/content-sources.mjs +110 -0
- package/engine/content-spans.mjs +118 -14
- package/engine/content-tables.mjs +1 -0
- package/engine/content-tasklists.mjs +20 -4
- package/engine/diagnostics.mjs +17 -6
- package/engine/extension-attributes.mjs +28 -5
- package/engine/first-use.mjs +371 -0
- package/engine/foundry-entries.mjs +12 -9
- package/engine/generate.mjs +13 -4
- package/engine/helpers.mjs +47 -16
- package/engine/index-records.mjs +1 -0
- package/engine/index.mjs +3 -0
- package/engine/journals.mjs +9 -5
- package/engine/language-data.mjs +2 -2
- package/engine/language-rows.mjs +4 -4
- package/engine/language-terms.mjs +4 -4
- package/engine/lexical-sections.mjs +5 -5
- 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/path-charset.mjs +24 -1
- package/engine/pdf-render.mjs +15 -1
- package/engine/site-index.mjs +25 -2
- package/engine/sql-tables.mjs +76 -15
- package/engine/stub-lint.mjs +4 -3
- package/engine/web-wikilinks.mjs +33 -12
- package/engine/wikilink-syntax.mjs +65 -8
- package/engine/wikilinks.mjs +29 -11
- package/engine/yaml-lint.mjs +3 -5
- package/hugo-theme/layouts/partials/footer.html +2 -0
- package/hugo-theme/static/css/style.css +20 -0
- package/hugo-theme/static/js/inline-spoilers.js +23 -0
- package/package.json +1 -1
- package/types/engine/anchors.d.mts +6 -3
- 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 +39 -19
- 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 +8 -6
- package/types/engine/content-tasklists.d.mts +5 -1
- package/types/engine/diagnostics.d.mts +10 -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 +11 -4
- package/types/engine/generate.d.mts +5 -1
- package/types/engine/helpers.d.mts +30 -7
- package/types/engine/index.d.mts +1 -0
- package/types/engine/journals.d.mts +5 -2
- package/types/engine/lexical-sections.d.mts +3 -2
- 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/path-charset.d.mts +16 -1
- package/types/engine/site-build.d.mts +2 -2
- package/types/engine/site-index.d.mts +3 -1
- package/types/engine/sql-tables.d.mts +7 -2
- package/types/engine/stub-lint.d.mts +3 -1
- package/types/engine/wikilink-syntax.d.mts +40 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 23.11.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- References to private notes and secret sections now conceal their labels on the web; authors can keep selected references visible.
|
|
8
|
+
- Inline spoilers preserve formatted text across websites, Foundry journals and books.
|
|
9
|
+
|
|
10
|
+
## 23.10.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
**Notes**
|
|
15
|
+
|
|
16
|
+
- Notes with CRLF frontmatter retain their identity, body and links in compiled content and published indexes.
|
|
17
|
+
- Correct malformed YAML in CRLF frontmatter before building; YAML errors in these notes prevent publication.
|
|
18
|
+
|
|
19
|
+
**Language notes**
|
|
20
|
+
|
|
21
|
+
- Language notes write a name element's joining form as `joining`; `genitive` is refused.
|
|
22
|
+
- 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`.
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
**Names**
|
|
27
|
+
|
|
28
|
+
- 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.
|
|
29
|
+
- Mark unwritten names with `[Name]{.name data-type="place"}`; query indexed uses and review grouped lint advice about unwritten names and conflicting types.
|
|
30
|
+
|
|
31
|
+
**Commands and release notes**
|
|
32
|
+
|
|
33
|
+
- Selected content checks and compendium builds can run together in one command.
|
|
34
|
+
- Release notes retain every summary when paragraphs and bullets are combined.
|
|
35
|
+
|
|
3
36
|
## 23.9.0
|
|
4
37
|
|
|
5
38
|
### 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
|
@@ -118,6 +118,9 @@ import {
|
|
|
118
118
|
} from "../engine/site-build.mjs";
|
|
119
119
|
import { auditLinks, buildLinkIndex, walkReachability } from "../engine/content-links.mjs";
|
|
120
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";
|
|
121
124
|
// The one place a link finding is worded, shared with both builds.
|
|
122
125
|
import { linkFindingMessage } from "../engine/wikilink-syntax.mjs";
|
|
123
126
|
import {
|
|
@@ -183,6 +186,11 @@ prefix.apply(log, {
|
|
|
183
186
|
},
|
|
184
187
|
});
|
|
185
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
|
+
|
|
186
194
|
/**
|
|
187
195
|
* Report a command's failure.
|
|
188
196
|
*
|
|
@@ -203,6 +211,29 @@ function reportFailure(err) {
|
|
|
203
211
|
else log.error(message);
|
|
204
212
|
}
|
|
205
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
|
+
|
|
206
237
|
/**
|
|
207
238
|
* A note's source text, or `""` when it cannot be read.
|
|
208
239
|
*
|
|
@@ -235,7 +266,12 @@ export function registerContentCommands(cli) {
|
|
|
235
266
|
return cli
|
|
236
267
|
.command(
|
|
237
268
|
withIndexPreflight(
|
|
238
|
-
withBodyPreflight(
|
|
269
|
+
withBodyPreflight(
|
|
270
|
+
packageCommand(),
|
|
271
|
+
(_config, argv) => argv.action === "compile",
|
|
272
|
+
undefined,
|
|
273
|
+
true,
|
|
274
|
+
),
|
|
239
275
|
(_config, argv) => argv.action === "compile",
|
|
240
276
|
),
|
|
241
277
|
)
|
|
@@ -245,6 +281,7 @@ export function registerContentCommands(cli) {
|
|
|
245
281
|
.command(proseCommand())
|
|
246
282
|
.command(contentFormatCommand())
|
|
247
283
|
.command(withIndexPreflight(linksCommand()))
|
|
284
|
+
.command(withIndexPreflight(contentCommand()))
|
|
248
285
|
.command(formatCommand())
|
|
249
286
|
.command(markdownCommand())
|
|
250
287
|
.command(
|
|
@@ -252,6 +289,7 @@ export function registerContentCommands(cli) {
|
|
|
252
289
|
contentIndexCommand(),
|
|
253
290
|
() => true,
|
|
254
291
|
(config, argv) => argv.root || config.paths.content,
|
|
292
|
+
true,
|
|
255
293
|
),
|
|
256
294
|
)
|
|
257
295
|
.command(withIndexPreflight(withBodyPreflight(siteCommand()), publishesContentPages))
|
|
@@ -404,19 +442,33 @@ function withBodyPreflight(
|
|
|
404
442
|
command,
|
|
405
443
|
shouldCheck = () => true,
|
|
406
444
|
contentRoot = (config) => config.paths.content,
|
|
445
|
+
shareCapture = false,
|
|
407
446
|
) {
|
|
408
447
|
const handler = command.handler;
|
|
409
448
|
return {
|
|
410
449
|
...command,
|
|
411
450
|
handler: async (argv) => {
|
|
451
|
+
let owned;
|
|
412
452
|
try {
|
|
413
|
-
const config = loadPackConfig();
|
|
453
|
+
const config = argv[CONTENT_CONFIG] ?? loadPackConfig();
|
|
454
|
+
let next = argv;
|
|
414
455
|
if (shouldCheck(config, argv)) {
|
|
415
456
|
const contentBase = contentRoot(config, argv);
|
|
416
457
|
if (!fs.existsSync(contentBase)) return handler(argv);
|
|
417
|
-
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
|
+
}
|
|
418
469
|
const { findings } = lintNoteStates(contentBase, {
|
|
419
470
|
records,
|
|
471
|
+
parsed: session?.parsed,
|
|
420
472
|
contentPackage: config.contentPackage,
|
|
421
473
|
});
|
|
422
474
|
const errors = findings.filter((finding) => finding.severity === "error");
|
|
@@ -425,13 +477,16 @@ function withBodyPreflight(
|
|
|
425
477
|
process.exitCode = 1;
|
|
426
478
|
return;
|
|
427
479
|
}
|
|
480
|
+
if (session) next = { ...argv, [CONTENT_SESSION]: session };
|
|
428
481
|
}
|
|
482
|
+
return await handler(next);
|
|
429
483
|
} catch (err) {
|
|
430
484
|
reportFailure(err);
|
|
431
485
|
process.exitCode = 1;
|
|
432
486
|
return;
|
|
487
|
+
} finally {
|
|
488
|
+
owned?.dispose();
|
|
433
489
|
}
|
|
434
|
-
return handler(argv);
|
|
435
490
|
},
|
|
436
491
|
};
|
|
437
492
|
}
|
|
@@ -457,7 +512,7 @@ function withIndexPreflight(command, shouldCheck = () => true) {
|
|
|
457
512
|
process.exitCode = 1;
|
|
458
513
|
return;
|
|
459
514
|
}
|
|
460
|
-
return handler(argv);
|
|
515
|
+
return handler({ ...argv, [CONTENT_CONFIG]: config });
|
|
461
516
|
},
|
|
462
517
|
};
|
|
463
518
|
}
|
|
@@ -906,8 +961,10 @@ function lintCommand() {
|
|
|
906
961
|
});
|
|
907
962
|
},
|
|
908
963
|
handler: async (argv) => {
|
|
964
|
+
let session;
|
|
909
965
|
try {
|
|
910
|
-
const config =
|
|
966
|
+
const config =
|
|
967
|
+
argv[CONTENT_SESSION]?.config ?? argv[CONTENT_CONFIG] ?? loadPackConfig();
|
|
911
968
|
const root = argv.root ?? config.paths.content;
|
|
912
969
|
|
|
913
970
|
// The corpus, enumerated once for this command and handed to
|
|
@@ -921,30 +978,22 @@ function lintCommand() {
|
|
|
921
978
|
// finding rather than thrown: one malformed note must
|
|
922
979
|
// not take every other finding in the tree with it, and the
|
|
923
980
|
// reader needs a line to open, not a stack.
|
|
924
|
-
const corpusProblems = [];
|
|
925
|
-
const parsed = new Map();
|
|
926
981
|
const indexStarted = performance.now();
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
config,
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
parsed,
|
|
933
|
-
});
|
|
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;
|
|
934
987
|
const indexMs = performance.now() - indexStarted;
|
|
935
|
-
|
|
988
|
+
if (!argv[PREPARATION_REPORTED])
|
|
989
|
+
for (const problem of corpusProblems) emitDiagnostic(problem);
|
|
936
990
|
// An error whatever the command's own strictness: the note is
|
|
937
991
|
// absent from every answer below, so reporting it and exiting 0
|
|
938
992
|
// would call the tree clean while silently omitting a note.
|
|
939
993
|
if (corpusProblems.length) process.exitCode = 1;
|
|
940
994
|
|
|
941
995
|
const sqlStarted = performance.now();
|
|
942
|
-
const sqlTables = await
|
|
943
|
-
config,
|
|
944
|
-
records,
|
|
945
|
-
parsed,
|
|
946
|
-
skipDirectories: config.skipDirectories,
|
|
947
|
-
});
|
|
996
|
+
const sqlTables = await session.sqlTables("all");
|
|
948
997
|
const sqlMs = performance.now() - sqlStarted;
|
|
949
998
|
const validationStarted = performance.now();
|
|
950
999
|
|
|
@@ -959,12 +1008,19 @@ function lintCommand() {
|
|
|
959
1008
|
// One index, built once, for the reference check. It is the
|
|
960
1009
|
// same resolver the wikilink audit uses, so a frontmatter
|
|
961
1010
|
// reference and a body link answer the same way.
|
|
962
|
-
const index =
|
|
963
|
-
|
|
1011
|
+
const index = await session.audit("lint");
|
|
1012
|
+
const catalog = createNameCatalog({ records, index });
|
|
1013
|
+
const firstUseChecker = createFirstUseChecker({
|
|
964
1014
|
records,
|
|
965
|
-
|
|
966
|
-
|
|
1015
|
+
index,
|
|
1016
|
+
parsed: session.parsed,
|
|
1017
|
+
syntax: session.syntax,
|
|
1018
|
+
catalog,
|
|
967
1019
|
});
|
|
1020
|
+
const names = namesReportFinding(
|
|
1021
|
+
createNamesReport({ package: config.contentPackage, records, catalog }),
|
|
1022
|
+
{ contentBase: root },
|
|
1023
|
+
);
|
|
968
1024
|
// Which system blocks this tree carries, derived from what it
|
|
969
1025
|
// declares it ships for. Read before the lint so the
|
|
970
1026
|
// systems it will *not* check can be said out loud below.
|
|
@@ -1104,6 +1160,7 @@ function lintCommand() {
|
|
|
1104
1160
|
// the subject is every character authored, including the ones
|
|
1105
1161
|
// that stopped a note parsing at all.
|
|
1106
1162
|
const charset = lintContentCharset(root, {
|
|
1163
|
+
sources: session.sources,
|
|
1107
1164
|
skipDirectories: config.skipDirectories,
|
|
1108
1165
|
});
|
|
1109
1166
|
|
|
@@ -1112,6 +1169,7 @@ function lintCommand() {
|
|
|
1112
1169
|
// than the parsed notes, so a picture, which has no note,
|
|
1113
1170
|
// is covered by the same rule.
|
|
1114
1171
|
const pathCharset = lintPathCharset({
|
|
1172
|
+
inventory: session.inventory,
|
|
1115
1173
|
contentBase: root,
|
|
1116
1174
|
assetsBase: config.paths.assets,
|
|
1117
1175
|
skipDirectories: config.skipDirectories,
|
|
@@ -1126,6 +1184,7 @@ function lintCommand() {
|
|
|
1126
1184
|
// package's own entries merged over it, which is where a
|
|
1127
1185
|
// Game-Icons glyph becomes a name a note may write.
|
|
1128
1186
|
const icons = lintContentIcons(root, {
|
|
1187
|
+
sources: session.sources,
|
|
1129
1188
|
skipDirectories: config.skipDirectories,
|
|
1130
1189
|
registry: config.icons,
|
|
1131
1190
|
});
|
|
@@ -1135,6 +1194,7 @@ function lintCommand() {
|
|
|
1135
1194
|
// still unrenderable in a book — the character rule one level
|
|
1136
1195
|
// up.
|
|
1137
1196
|
const html = lintContentHtml(root, {
|
|
1197
|
+
sources: session.sources,
|
|
1138
1198
|
skipDirectories: config.skipDirectories,
|
|
1139
1199
|
});
|
|
1140
1200
|
|
|
@@ -1143,6 +1203,7 @@ function lintCommand() {
|
|
|
1143
1203
|
// ordinary width looks exactly like a directive that worked —
|
|
1144
1204
|
// so these are errors and they fail the run.
|
|
1145
1205
|
const images = lintContentImages(root, {
|
|
1206
|
+
sources: session.sources,
|
|
1146
1207
|
skipDirectories: config.skipDirectories,
|
|
1147
1208
|
// Which packages this build can reach is configuration, and
|
|
1148
1209
|
// an address naming one it cannot reaches a journal as a
|
|
@@ -1154,6 +1215,7 @@ function lintCommand() {
|
|
|
1154
1215
|
// miscompiled, since every surface sets the marker as the
|
|
1155
1216
|
// item's own text and all three agree, so this is advisory.
|
|
1156
1217
|
const taskLists = lintContentTaskLists(root, {
|
|
1218
|
+
sources: session.sources,
|
|
1157
1219
|
skipDirectories: config.skipDirectories,
|
|
1158
1220
|
});
|
|
1159
1221
|
|
|
@@ -1163,6 +1225,7 @@ function lintCommand() {
|
|
|
1163
1225
|
// wrong, and 464 warnings a reader cannot clear is how a report
|
|
1164
1226
|
// teaches its reader to stop looking at it.
|
|
1165
1227
|
const states = lintNoteStates(root, {
|
|
1228
|
+
parsed,
|
|
1166
1229
|
records,
|
|
1167
1230
|
contentPackage: config.contentPackage,
|
|
1168
1231
|
});
|
|
@@ -1195,7 +1258,11 @@ function lintCommand() {
|
|
|
1195
1258
|
});
|
|
1196
1259
|
|
|
1197
1260
|
const findings = [
|
|
1198
|
-
...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] : []),
|
|
1199
1266
|
...addresses.findings,
|
|
1200
1267
|
...frontmatter.findings,
|
|
1201
1268
|
...schemaFindings,
|
|
@@ -1234,8 +1301,10 @@ function lintCommand() {
|
|
|
1234
1301
|
`SQL ${sqlMs.toFixed(1)} ms; validation ${validationMs.toFixed(1)} ms.`,
|
|
1235
1302
|
);
|
|
1236
1303
|
} catch (err) {
|
|
1237
|
-
|
|
1304
|
+
reportContentFailure(err, argv);
|
|
1238
1305
|
process.exitCode = 1;
|
|
1306
|
+
} finally {
|
|
1307
|
+
if (!argv[CONTENT_SESSION]) session?.dispose();
|
|
1239
1308
|
}
|
|
1240
1309
|
},
|
|
1241
1310
|
};
|
|
@@ -1407,6 +1476,73 @@ function markdownCommand() {
|
|
|
1407
1476
|
* @returns {object} The yargs command module.
|
|
1408
1477
|
*/
|
|
1409
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
|
+
|
|
1410
1546
|
function linksCommand() {
|
|
1411
1547
|
return {
|
|
1412
1548
|
command: "links [root]",
|
|
@@ -1418,38 +1554,23 @@ function linksCommand() {
|
|
|
1418
1554
|
});
|
|
1419
1555
|
},
|
|
1420
1556
|
handler: async (argv) => {
|
|
1557
|
+
let session;
|
|
1421
1558
|
try {
|
|
1422
|
-
const config =
|
|
1559
|
+
const config =
|
|
1560
|
+
argv[CONTENT_SESSION]?.config ?? argv[CONTENT_CONFIG] ?? loadPackConfig();
|
|
1423
1561
|
const contentBase = argv.root ?? config.paths.content;
|
|
1424
1562
|
|
|
1425
1563
|
const scope = { skipDirectories: config.skipDirectories };
|
|
1426
1564
|
// Enumerated once and shared, as in `lint`, and a note
|
|
1427
1565
|
// it cannot record is reported rather than thrown.
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
problems: corpusProblems,
|
|
1435
|
-
parsed,
|
|
1436
|
-
});
|
|
1437
|
-
for (const problem of corpusProblems) emitDiagnostic(problem);
|
|
1438
|
-
// An error whatever the command's own strictness: the note is
|
|
1439
|
-
// absent from every answer below, so reporting it and exiting 0
|
|
1440
|
-
// 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);
|
|
1441
1572
|
if (corpusProblems.length) process.exitCode = 1;
|
|
1442
|
-
const index =
|
|
1443
|
-
config,
|
|
1444
|
-
records,
|
|
1445
|
-
...scope,
|
|
1446
|
-
sqlTables: await prepareLinkSqlTables(contentBase, {
|
|
1447
|
-
config,
|
|
1448
|
-
records,
|
|
1449
|
-
parsed,
|
|
1450
|
-
...scope,
|
|
1451
|
-
}),
|
|
1452
|
-
});
|
|
1573
|
+
const index = await session.audit("links");
|
|
1453
1574
|
|
|
1454
1575
|
// An unusable manifest would otherwise surface as a pile of
|
|
1455
1576
|
// dead addresses pointing at the notes that cite it, rather
|
|
@@ -1576,8 +1697,10 @@ function linksCommand() {
|
|
|
1576
1697
|
);
|
|
1577
1698
|
}
|
|
1578
1699
|
} catch (err) {
|
|
1579
|
-
|
|
1700
|
+
reportContentFailure(err, argv);
|
|
1580
1701
|
process.exitCode = 1;
|
|
1702
|
+
} finally {
|
|
1703
|
+
if (!argv[CONTENT_SESSION]) session?.dispose();
|
|
1581
1704
|
}
|
|
1582
1705
|
},
|
|
1583
1706
|
};
|
|
@@ -1622,10 +1745,15 @@ function contentIndexCommand() {
|
|
|
1622
1745
|
});
|
|
1623
1746
|
},
|
|
1624
1747
|
handler: async (argv) => {
|
|
1748
|
+
let session;
|
|
1625
1749
|
try {
|
|
1626
|
-
const config =
|
|
1750
|
+
const config =
|
|
1751
|
+
argv[CONTENT_SESSION]?.config ?? argv[CONTENT_CONFIG] ?? loadPackConfig();
|
|
1627
1752
|
const contentBase = argv.root ?? config.paths.content;
|
|
1628
|
-
|
|
1753
|
+
session =
|
|
1754
|
+
argv[CONTENT_SESSION] ?? (await createContentSession({ config, contentBase }));
|
|
1755
|
+
session.assertContext({ config, contentBase });
|
|
1756
|
+
const publication = await session.publication();
|
|
1629
1757
|
const { file, notes, bytes, full, draft, stub, dictionary, terms } =
|
|
1630
1758
|
emitContentIndex({
|
|
1631
1759
|
config,
|
|
@@ -1644,8 +1772,10 @@ function contentIndexCommand() {
|
|
|
1644
1772
|
`(${written.rows} ${relation} rows)`,
|
|
1645
1773
|
);
|
|
1646
1774
|
} catch (err) {
|
|
1647
|
-
|
|
1775
|
+
reportContentFailure(err, argv);
|
|
1648
1776
|
process.exitCode = 1;
|
|
1777
|
+
} finally {
|
|
1778
|
+
if (!argv[CONTENT_SESSION]) session?.dispose();
|
|
1649
1779
|
}
|
|
1650
1780
|
},
|
|
1651
1781
|
};
|
|
@@ -2538,19 +2668,13 @@ function packageCommand() {
|
|
|
2538
2668
|
// an unhandled-rejection stack trace. Report the message and set a
|
|
2539
2669
|
// failing exit code, so a build guard reads as a build failure.
|
|
2540
2670
|
try {
|
|
2541
|
-
const config =
|
|
2671
|
+
const config =
|
|
2672
|
+
argv[CONTENT_SESSION]?.config ?? argv[CONTENT_CONFIG] ?? loadPackConfig();
|
|
2542
2673
|
// The compile passes do not run for a package that declares no
|
|
2543
2674
|
// packs by rule. Refused rather than exiting 0 having compiled
|
|
2544
2675
|
// nothing: a build that succeeds and produces no documents is
|
|
2545
2676
|
// the quiet failure this toolchain reports everywhere else.
|
|
2546
|
-
|
|
2547
|
-
throw new Error(
|
|
2548
|
-
`\`packageKind: ${config.packageKind}\` compiles no ` +
|
|
2549
|
-
`compendium, so there is nothing to ${action}. Build ` +
|
|
2550
|
-
`its site with \`package-build site\` and its book ` +
|
|
2551
|
-
`with \`package-build pdf\`.`,
|
|
2552
|
-
);
|
|
2553
|
-
}
|
|
2677
|
+
assertCompendiumOperation(config, action);
|
|
2554
2678
|
// The one directory the pipeline creates rather than expects:
|
|
2555
2679
|
// `unpack` writes the extracted JSON there and `compile` reads
|
|
2556
2680
|
// it back. Created here rather than at module scope so that
|
|
@@ -2563,7 +2687,12 @@ function packageCommand() {
|
|
|
2563
2687
|
// from the resolved configuration, so nothing is restated
|
|
2564
2688
|
// here.
|
|
2565
2689
|
case "compile":
|
|
2566
|
-
return await compilePacks({
|
|
2690
|
+
return await compilePacks({
|
|
2691
|
+
packName: pack,
|
|
2692
|
+
config,
|
|
2693
|
+
session: argv[CONTENT_SESSION],
|
|
2694
|
+
reportPreparation: !argv[PREPARATION_REPORTED],
|
|
2695
|
+
});
|
|
2567
2696
|
case "clean":
|
|
2568
2697
|
return await cleanPacks({
|
|
2569
2698
|
packName: pack,
|
|
@@ -2577,7 +2706,7 @@ function packageCommand() {
|
|
|
2577
2706
|
});
|
|
2578
2707
|
}
|
|
2579
2708
|
} catch (err) {
|
|
2580
|
-
|
|
2709
|
+
reportContentFailure(err, argv);
|
|
2581
2710
|
process.exitCode = 1;
|
|
2582
2711
|
}
|
|
2583
2712
|
},
|