@heroiclands/package-build 8.0.0 → 9.0.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 +173 -0
- package/bin/content-build.mjs +44 -118
- package/bin/package-build.mjs +27 -69
- package/bin/report.mjs +1 -2
- package/bundle.mjs +2 -10
- package/config.mjs +31 -106
- package/container.mjs +13 -57
- package/content-config.mjs +45 -164
- package/coverage.mjs +14 -55
- package/deploy.mjs +4 -13
- package/e2e.mjs +16 -55
- package/engine/address-diff.mjs +1 -4
- package/engine/base-compiler.mjs +10 -28
- package/engine/code-fences.mjs +4 -13
- package/engine/compendiums.mjs +13 -37
- package/engine/content-address.mjs +2 -6
- package/engine/content-links.mjs +13 -44
- package/engine/content-lint.mjs +4 -15
- package/engine/content-slug.mjs +2 -6
- package/engine/content-tables.mjs +26 -79
- package/engine/diagnostics.mjs +4 -15
- package/engine/field-reference.mjs +6 -20
- package/engine/field-spec.mjs +1 -3
- package/engine/foreign-catalog.mjs +7 -22
- package/engine/foreign-manifests.mjs +1 -4
- package/engine/frontmatter-lint.mjs +6 -18
- package/engine/frontmatter.mjs +3 -8
- package/engine/generate.mjs +4 -16
- package/engine/helpers.mjs +13 -52
- package/engine/homepage.mjs +4 -15
- package/engine/ids.mjs +3 -12
- package/engine/item-registry.mjs +2 -6
- package/engine/journals.mjs +4 -14
- package/engine/kb-manifest.mjs +5 -17
- package/engine/macros.mjs +2 -10
- package/engine/manifest-emit.mjs +6 -17
- package/engine/map-notes.mjs +19 -69
- package/engine/note-package.mjs +1 -4
- package/engine/pack-config.mjs +17 -38
- package/engine/pack-router.mjs +1 -2
- package/engine/prose-config.mjs +20 -4
- package/engine/prose-lint.mjs +6 -14
- package/engine/region-events.mjs +1 -3
- package/engine/scene-levels.mjs +8 -22
- package/engine/scenes.mjs +13 -47
- package/engine/schema-check.mjs +1 -4
- package/engine/schema-extract.mjs +70 -45
- package/engine/site-build.mjs +12 -37
- package/engine/site-index.mjs +3 -15
- package/engine/web-wikilinks.mjs +4 -13
- package/engine/wikilinks.mjs +115 -167
- package/index.mjs +1 -5
- package/lang.mjs +1 -3
- package/manifest.mjs +10 -37
- package/markdownlint-config.mjs +1 -5
- package/package.json +1 -1
- package/sohl/actors.mjs +10 -40
- package/sohl/being-info.mjs +3 -6
- package/sohl/index.mjs +1 -6
- package/sohl/item-builders.mjs +1 -3
- package/sohl/item-fields.mjs +16 -34
- package/sohl/items.mjs +1 -3
- package/sohl/kb-passes.mjs +29 -39
- package/sohl/skill-base.mjs +7 -23
- package/stage.mjs +3 -13
- package/templates.mjs +4 -15
- package/types/bundle.d.mts +1 -1
- package/types/container.d.mts +2 -2
- package/types/coverage.d.mts +1 -1
- package/types/e2e.d.mts +4 -4
- package/types/engine/generate.d.mts +1 -1
- package/types/engine/helpers.d.mts +1 -1
- package/types/engine/schema-extract.d.mts +1 -1
- package/types/engine/site-index.d.mts +1 -1
- package/types/manifest.d.mts +1 -1
package/coverage.mjs
CHANGED
|
@@ -230,12 +230,7 @@ function shapeOf(literal) {
|
|
|
230
230
|
export function collectScriptReferences(source, { file, roots }) {
|
|
231
231
|
const { whole, scan } = patternsFor(roots);
|
|
232
232
|
const result = emptySet();
|
|
233
|
-
const sourceFile = ts.createSourceFile(
|
|
234
|
-
file,
|
|
235
|
-
source,
|
|
236
|
-
ts.ScriptTarget.Latest,
|
|
237
|
-
true,
|
|
238
|
-
);
|
|
233
|
+
const sourceFile = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true);
|
|
239
234
|
|
|
240
235
|
// `parseDiagnostics` is TypeScript's own and not part of its published
|
|
241
236
|
// surface, hence the guard. A file that does not parse contributes no
|
|
@@ -278,11 +273,7 @@ export function collectScriptReferences(source, { file, roots }) {
|
|
|
278
273
|
* @returns {boolean} Whether its `.text` is the whole literal.
|
|
279
274
|
*/
|
|
280
275
|
const isPlainString = (node) =>
|
|
281
|
-
Boolean(
|
|
282
|
-
node &&
|
|
283
|
-
(ts.isStringLiteral(node) ||
|
|
284
|
-
ts.isNoSubstitutionTemplateLiteral(node)),
|
|
285
|
-
);
|
|
276
|
+
Boolean(node && (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)));
|
|
286
277
|
|
|
287
278
|
/**
|
|
288
279
|
* Read the concrete keys out of one literal chunk of a template.
|
|
@@ -316,11 +307,7 @@ export function collectScriptReferences(source, { file, roots }) {
|
|
|
316
307
|
const prefixLiterals = new Set();
|
|
317
308
|
|
|
318
309
|
const visit = (node) => {
|
|
319
|
-
if (
|
|
320
|
-
ts.isStringLiteral(node) &&
|
|
321
|
-
whole.test(node.text) &&
|
|
322
|
-
!prefixLiterals.has(node)
|
|
323
|
-
) {
|
|
310
|
+
if (ts.isStringLiteral(node) && whole.test(node.text) && !prefixLiterals.has(node)) {
|
|
324
311
|
addKey(node.text, node.getStart());
|
|
325
312
|
}
|
|
326
313
|
|
|
@@ -328,20 +315,12 @@ export function collectScriptReferences(source, { file, roots }) {
|
|
|
328
315
|
// not string-literal nodes — inline markup in a helper puts them inside
|
|
329
316
|
// the literal's text — so they would otherwise be invisible.
|
|
330
317
|
if (ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
331
|
-
addChunkKeys(
|
|
332
|
-
node.text,
|
|
333
|
-
{ openStart: false, openEnd: false },
|
|
334
|
-
node.getStart(),
|
|
335
|
-
);
|
|
318
|
+
addChunkKeys(node.text, { openStart: false, openEnd: false }, node.getStart());
|
|
336
319
|
}
|
|
337
320
|
|
|
338
321
|
if (ts.isTemplateExpression(node)) {
|
|
339
322
|
const spans = node.templateSpans;
|
|
340
|
-
addChunkKeys(
|
|
341
|
-
node.head.text,
|
|
342
|
-
{ openStart: false, openEnd: true },
|
|
343
|
-
node.getStart(),
|
|
344
|
-
);
|
|
323
|
+
addChunkKeys(node.head.text, { openStart: false, openEnd: true }, node.getStart());
|
|
345
324
|
spans.forEach((span, index) => {
|
|
346
325
|
addChunkKeys(
|
|
347
326
|
span.literal.text,
|
|
@@ -366,8 +345,7 @@ export function collectScriptReferences(source, { file, roots }) {
|
|
|
366
345
|
// it, so those leaves are never named in any source file.
|
|
367
346
|
if (
|
|
368
347
|
(ts.isPropertyDeclaration(node) || ts.isPropertyAssignment(node)) &&
|
|
369
|
-
node.name?.getText(sourceFile).replace(/["']/g, "") ===
|
|
370
|
-
"LOCALIZATION_PREFIXES" &&
|
|
348
|
+
node.name?.getText(sourceFile).replace(/["']/g, "") === "LOCALIZATION_PREFIXES" &&
|
|
371
349
|
node.initializer &&
|
|
372
350
|
ts.isArrayLiteralExpression(node.initializer)
|
|
373
351
|
) {
|
|
@@ -423,10 +401,7 @@ export function collectTemplateReferences(source, { file, roots }) {
|
|
|
423
401
|
|
|
424
402
|
// A substitution written directly against the token completes its last
|
|
425
403
|
// segment, so the namespace is one segment shorter than the token.
|
|
426
|
-
const namespace =
|
|
427
|
-
rest.startsWith("${") ?
|
|
428
|
-
token.replace(/\.[A-Za-z0-9_]*$/, "")
|
|
429
|
-
: token;
|
|
404
|
+
const namespace = rest.startsWith("${") ? token.replace(/\.[A-Za-z0-9_]*$/, "") : token;
|
|
430
405
|
result.namespaces.push(namespace);
|
|
431
406
|
|
|
432
407
|
// Recover the whole literal so its shape, not merely its head, decides
|
|
@@ -436,9 +411,7 @@ export function collectTemplateReferences(source, { file, roots }) {
|
|
|
436
411
|
const quote = source[index - 1];
|
|
437
412
|
if (quote === "'" || quote === '"' || quote === "`") {
|
|
438
413
|
const end = source.indexOf(quote, index);
|
|
439
|
-
const pattern = shapeOf(
|
|
440
|
-
source.slice(index, end === -1 ? undefined : end),
|
|
441
|
-
);
|
|
414
|
+
const pattern = shapeOf(source.slice(index, end === -1 ? undefined : end));
|
|
442
415
|
if (pattern) result.patterns.push(pattern);
|
|
443
416
|
}
|
|
444
417
|
}
|
|
@@ -478,9 +451,7 @@ export function mergeReferences(sets) {
|
|
|
478
451
|
* @returns {RegExp} The matcher.
|
|
479
452
|
*/
|
|
480
453
|
function matcherFor(pattern) {
|
|
481
|
-
return new RegExp(
|
|
482
|
-
`^${pattern.split("*").map(escapeRegExp).join("[^.]+")}$`,
|
|
483
|
-
);
|
|
454
|
+
return new RegExp(`^${pattern.split("*").map(escapeRegExp).join("[^.]+")}$`);
|
|
484
455
|
}
|
|
485
456
|
|
|
486
457
|
/**
|
|
@@ -502,13 +473,7 @@ function matcherFor(pattern) {
|
|
|
502
473
|
* questions: one says the package is broken, the other that it carries
|
|
503
474
|
* something nobody could see a use for.
|
|
504
475
|
*/
|
|
505
|
-
export function analyzeCoverage({
|
|
506
|
-
langSource,
|
|
507
|
-
langFile,
|
|
508
|
-
references,
|
|
509
|
-
retained = [],
|
|
510
|
-
roots,
|
|
511
|
-
}) {
|
|
476
|
+
export function analyzeCoverage({ langSource, langFile, references, retained = [], roots }) {
|
|
512
477
|
let declared;
|
|
513
478
|
try {
|
|
514
479
|
declared = JSON.parse(langSource);
|
|
@@ -578,17 +543,14 @@ export function analyzeCoverage({
|
|
|
578
543
|
});
|
|
579
544
|
}
|
|
580
545
|
|
|
581
|
-
const referenced = new Set(
|
|
582
|
-
(references.keys ?? []).map((reference) => reference.key),
|
|
583
|
-
);
|
|
546
|
+
const referenced = new Set((references.keys ?? []).map((reference) => reference.key));
|
|
584
547
|
const shapes = (references.patterns ?? []).map(matcherFor);
|
|
585
548
|
// Foundry localizes a DataModel's field labels and hints off the declared
|
|
586
549
|
// prefix, so no source names them one by one.
|
|
587
550
|
const fieldShapes = [...namespaces].map(
|
|
588
551
|
(namespace) =>
|
|
589
552
|
new RegExp(
|
|
590
|
-
`^${escapeRegExp(namespace)}\\.FIELDS\\.` +
|
|
591
|
-
`[A-Za-z0-9_.]+\\.(?:label|hint)$`,
|
|
553
|
+
`^${escapeRegExp(namespace)}\\.FIELDS\\.` + `[A-Za-z0-9_.]+\\.(?:label|hint)$`,
|
|
592
554
|
),
|
|
593
555
|
);
|
|
594
556
|
|
|
@@ -596,13 +558,10 @@ export function analyzeCoverage({
|
|
|
596
558
|
referenced.has(key) ||
|
|
597
559
|
shapes.some((shape) => shape.test(key)) ||
|
|
598
560
|
fieldShapes.some((shape) => shape.test(key));
|
|
599
|
-
const isRetained = (key) =>
|
|
600
|
-
retained.some((prefix) => key === prefix || key.startsWith(prefix));
|
|
561
|
+
const isRetained = (key) => retained.some((prefix) => key === prefix || key.startsWith(prefix));
|
|
601
562
|
|
|
602
563
|
const unreferenced = declaredKeys
|
|
603
|
-
.filter((key) =>
|
|
604
|
-
known.some((root) => key === root || key.startsWith(`${root}.`)),
|
|
605
|
-
)
|
|
564
|
+
.filter((key) => known.some((root) => key === root || key.startsWith(`${root}.`)))
|
|
606
565
|
.filter((key) => !isReferenced(key) && !isRetained(key))
|
|
607
566
|
.map((key) => ({
|
|
608
567
|
file: langFile,
|
package/deploy.mjs
CHANGED
|
@@ -146,9 +146,7 @@ export function resolveAgent(env, stageUpper, prefix = "SOHL") {
|
|
|
146
146
|
env[`FOUNDRYVTT_${stageUpper}_AGENT`] ||
|
|
147
147
|
env[`${prefix}_SFTP_AGENT`] ||
|
|
148
148
|
env.SSH_AUTH_SOCK ||
|
|
149
|
-
(process.platform === "win32" ?
|
|
150
|
-
"\\\\.\\pipe\\openssh-ssh-agent"
|
|
151
|
-
: undefined)
|
|
149
|
+
(process.platform === "win32" ? "\\\\.\\pipe\\openssh-ssh-agent" : undefined)
|
|
152
150
|
);
|
|
153
151
|
}
|
|
154
152
|
|
|
@@ -171,13 +169,8 @@ export async function buildConnection(
|
|
|
171
169
|
remote,
|
|
172
170
|
{ env = process.env, prefix = "SOHL" } = {},
|
|
173
171
|
) {
|
|
174
|
-
const port = Number(
|
|
175
|
-
|
|
176
|
-
env[`${prefix}_SFTP_PORT`] ??
|
|
177
|
-
22,
|
|
178
|
-
);
|
|
179
|
-
const username =
|
|
180
|
-
remote.username || env[`FOUNDRYVTT_${stageUpper}_USER`] || env.USER;
|
|
172
|
+
const port = Number(env[`FOUNDRYVTT_${stageUpper}_PORT`] ?? env[`${prefix}_SFTP_PORT`] ?? 22);
|
|
173
|
+
const username = remote.username || env[`FOUNDRYVTT_${stageUpper}_USER`] || env.USER;
|
|
181
174
|
|
|
182
175
|
const conn = { host: remote.host, port, username };
|
|
183
176
|
|
|
@@ -318,9 +311,7 @@ export async function deployStage({
|
|
|
318
311
|
env,
|
|
319
312
|
prefix,
|
|
320
313
|
});
|
|
321
|
-
log(
|
|
322
|
-
`Deploying ${source} → ${conn.username}@${conn.host}:${remoteDir} (sftp)`,
|
|
323
|
-
);
|
|
314
|
+
log(`Deploying ${source} → ${conn.username}@${conn.host}:${remoteDir} (sftp)`);
|
|
324
315
|
await deployRemote(conn, source, remoteDir, {
|
|
325
316
|
onUpload: (f) => log(` ${f}`),
|
|
326
317
|
});
|
package/e2e.mjs
CHANGED
|
@@ -93,9 +93,7 @@ const EXACT_BUILD = /^\d+\.\d+$/;
|
|
|
93
93
|
* @returns {string} The hex hash.
|
|
94
94
|
*/
|
|
95
95
|
export function hashPassword(password, salt) {
|
|
96
|
-
return crypto
|
|
97
|
-
.pbkdf2Sync(password, salt, 1000, 64, "sha512")
|
|
98
|
-
.toString("hex");
|
|
96
|
+
return crypto.pbkdf2Sync(password, salt, 1000, 64, "sha512").toString("hex");
|
|
99
97
|
}
|
|
100
98
|
|
|
101
99
|
/**
|
|
@@ -127,23 +125,13 @@ export function resolveE2EWorld(config, env = process.env) {
|
|
|
127
125
|
const world = config.e2eWorld ?? {};
|
|
128
126
|
const gm = config.e2eGm ?? {};
|
|
129
127
|
return {
|
|
130
|
-
worldId:
|
|
131
|
-
|
|
132
|
-
world.id ||
|
|
133
|
-
`${config.packageId}-e2e`,
|
|
134
|
-
worldTitle:
|
|
135
|
-
env[`${prefix}WORLD_TITLE`]?.trim() ||
|
|
136
|
-
world.title ||
|
|
137
|
-
`${config.packageId} E2E`,
|
|
128
|
+
worldId: env[`${prefix}WORLD_ID`]?.trim() || world.id || `${config.packageId}-e2e`,
|
|
129
|
+
worldTitle: env[`${prefix}WORLD_TITLE`]?.trim() || world.title || `${config.packageId} E2E`,
|
|
138
130
|
worldDescription:
|
|
139
|
-
world.description ||
|
|
140
|
-
`Disposable world for ${config.packageId} end-to-end tests.`,
|
|
131
|
+
world.description || `Disposable world for ${config.packageId} end-to-end tests.`,
|
|
141
132
|
gmId: E2E_GM_ID,
|
|
142
133
|
gmName: env[`${prefix}GM_NAME`]?.trim() || gm.name || "Gamemaster",
|
|
143
|
-
gmPassword:
|
|
144
|
-
env[`${prefix}GM_PASSWORD`]?.trim() ||
|
|
145
|
-
gm.password ||
|
|
146
|
-
`${config.packageId}-e2e`,
|
|
134
|
+
gmPassword: env[`${prefix}GM_PASSWORD`]?.trim() || gm.password || `${config.packageId}-e2e`,
|
|
147
135
|
};
|
|
148
136
|
}
|
|
149
137
|
|
|
@@ -330,8 +318,7 @@ export function parseFastArgs(argv, build) {
|
|
|
330
318
|
|
|
331
319
|
for (let i = 0; i < own.length; i += 1) {
|
|
332
320
|
const arg = /** @type {string} */ (own[i]);
|
|
333
|
-
if (arg.startsWith("--build="))
|
|
334
|
-
requested = arg.slice("--build=".length);
|
|
321
|
+
if (arg.startsWith("--build=")) requested = arg.slice("--build=".length);
|
|
335
322
|
else if (arg === "--recreate") recreate = true;
|
|
336
323
|
else if (arg === "--no-run") runSuite = false;
|
|
337
324
|
else if (arg === "--spec") {
|
|
@@ -387,12 +374,7 @@ export function parseFastArgs(argv, build) {
|
|
|
387
374
|
* @param {(message: string) => void} [opts.log] - Progress reporting.
|
|
388
375
|
* @returns {Promise<{worldDir: string, world: E2EWorld}>} Where it landed.
|
|
389
376
|
*/
|
|
390
|
-
export async function seedTestWorld({
|
|
391
|
-
config,
|
|
392
|
-
packageJson,
|
|
393
|
-
env = process.env,
|
|
394
|
-
log = () => {},
|
|
395
|
-
}) {
|
|
377
|
+
export async function seedTestWorld({ config, packageJson, env = process.env, log = () => {} }) {
|
|
396
378
|
const stage = config.e2eStage;
|
|
397
379
|
const dataRoot = requireIsolatedDataRoot(stage, env);
|
|
398
380
|
const world = resolveE2EWorld(config, env);
|
|
@@ -530,13 +512,7 @@ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
530
512
|
* @returns {Promise<void>} Resolves once the world is active.
|
|
531
513
|
* @throws {Error} On a licence failure or a timeout.
|
|
532
514
|
*/
|
|
533
|
-
export async function waitForWorld({
|
|
534
|
-
url,
|
|
535
|
-
container,
|
|
536
|
-
stage,
|
|
537
|
-
timeoutMs = 180_000,
|
|
538
|
-
log = () => {},
|
|
539
|
-
}) {
|
|
515
|
+
export async function waitForWorld({ url, container, stage, timeoutMs = 180_000, log = () => {} }) {
|
|
540
516
|
const join = `${url}/join`;
|
|
541
517
|
const deadline = Date.now() + timeoutMs;
|
|
542
518
|
log(`Waiting for the world at ${join} …`);
|
|
@@ -597,27 +573,17 @@ function captureContainerLog(container) {
|
|
|
597
573
|
* @param {(message: string) => void} [opts.log] - Progress reporting.
|
|
598
574
|
* @returns {number} The suite's exit status.
|
|
599
575
|
*/
|
|
600
|
-
export function runSuite({
|
|
601
|
-
command,
|
|
602
|
-
args = [],
|
|
603
|
-
cwd,
|
|
604
|
-
env = process.env,
|
|
605
|
-
log = () => {},
|
|
606
|
-
}) {
|
|
576
|
+
export function runSuite({ command, args = [], cwd, env = process.env, log = () => {} }) {
|
|
607
577
|
const [program, ...rest] = command;
|
|
608
578
|
const childEnv = { ...env };
|
|
609
579
|
delete childEnv.ELECTRON_RUN_AS_NODE;
|
|
610
580
|
log(`▸ ${[...command, ...args].join(" ")}`);
|
|
611
|
-
const result = spawnSync(
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
env: childEnv,
|
|
618
|
-
shell: process.platform === "win32",
|
|
619
|
-
},
|
|
620
|
-
);
|
|
581
|
+
const result = spawnSync(/** @type {string} */ (program), [...rest, ...args], {
|
|
582
|
+
stdio: "inherit",
|
|
583
|
+
cwd,
|
|
584
|
+
env: childEnv,
|
|
585
|
+
shell: process.platform === "win32",
|
|
586
|
+
});
|
|
621
587
|
if (result.error) throw result.error;
|
|
622
588
|
return result.status ?? 1;
|
|
623
589
|
}
|
|
@@ -756,12 +722,7 @@ export async function e2eRun({
|
|
|
756
722
|
* @param {(message: string) => void} [opts.log] - Progress reporting.
|
|
757
723
|
* @returns {Promise<number>} The suite's exit status.
|
|
758
724
|
*/
|
|
759
|
-
export async function e2eFast({
|
|
760
|
-
config,
|
|
761
|
-
argv = [],
|
|
762
|
-
env = process.env,
|
|
763
|
-
log = () => {},
|
|
764
|
-
}) {
|
|
725
|
+
export async function e2eFast({ config, argv = [], env = process.env, log = () => {} }) {
|
|
765
726
|
const {
|
|
766
727
|
targets,
|
|
767
728
|
recreate,
|
package/engine/address-diff.mjs
CHANGED
|
@@ -198,10 +198,7 @@ export function diffItemAddresses(baseline, current, { baseline: label }) {
|
|
|
198
198
|
export function noteFilesById(contentBase, { skipDirectories } = {}) {
|
|
199
199
|
const byId = new Map();
|
|
200
200
|
const walkOpts = skipDirectories ? { skipDirectories } : undefined;
|
|
201
|
-
for (const { frontmatter: fm, absPath } of walkMarkdownTree(
|
|
202
|
-
contentBase,
|
|
203
|
-
walkOpts,
|
|
204
|
-
)) {
|
|
201
|
+
for (const { frontmatter: fm, absPath } of walkMarkdownTree(contentBase, walkOpts)) {
|
|
205
202
|
if (fm?.id && !byId.has(fm.id)) byId.set(fm.id, absPath);
|
|
206
203
|
}
|
|
207
204
|
return byId;
|
package/engine/base-compiler.mjs
CHANGED
|
@@ -244,9 +244,7 @@ export class BasePackCompiler {
|
|
|
244
244
|
routingReporter = false,
|
|
245
245
|
} = {}) {
|
|
246
246
|
if (!contentBase) {
|
|
247
|
-
throw new Error(
|
|
248
|
-
`${this.constructor.name} compiler requires \`contentBase\``,
|
|
249
|
-
);
|
|
247
|
+
throw new Error(`${this.constructor.name} compiler requires \`contentBase\``);
|
|
250
248
|
}
|
|
251
249
|
if (!fs.existsSync(contentBase)) {
|
|
252
250
|
throw new Error(`Content tree not found at ${contentBase}`);
|
|
@@ -358,10 +356,7 @@ export class BasePackCompiler {
|
|
|
358
356
|
*/
|
|
359
357
|
async prepare() {
|
|
360
358
|
if (this.constructor.convertsWikilinks) {
|
|
361
|
-
this.linkIndex = buildContentLinkIndex(
|
|
362
|
-
this.contentBase,
|
|
363
|
-
this.router,
|
|
364
|
-
);
|
|
359
|
+
this.linkIndex = buildContentLinkIndex(this.contentBase, this.router);
|
|
365
360
|
this.contentDocs = collectContentDocs(this.contentBase);
|
|
366
361
|
}
|
|
367
362
|
this.unresolvedLinks = 0;
|
|
@@ -457,9 +452,7 @@ export class BasePackCompiler {
|
|
|
457
452
|
*/
|
|
458
453
|
// eslint-disable-next-line no-unused-vars
|
|
459
454
|
buildEntry(fm, markdown) {
|
|
460
|
-
throw new Error(
|
|
461
|
-
`${this.constructor.name} must implement buildEntry(fm, markdown)`,
|
|
462
|
-
);
|
|
455
|
+
throw new Error(`${this.constructor.name} must implement buildEntry(fm, markdown)`);
|
|
463
456
|
}
|
|
464
457
|
|
|
465
458
|
/**
|
|
@@ -526,9 +519,7 @@ export class BasePackCompiler {
|
|
|
526
519
|
*/
|
|
527
520
|
reportCompiled(stats) {
|
|
528
521
|
const label = this.constructor.label;
|
|
529
|
-
log.info(
|
|
530
|
-
`Compiled ${stats.compiled} ${label}${stats.compiled === 1 ? "" : "s"}`,
|
|
531
|
-
);
|
|
522
|
+
log.info(`Compiled ${stats.compiled} ${label}${stats.compiled === 1 ? "" : "s"}`);
|
|
532
523
|
}
|
|
533
524
|
|
|
534
525
|
/**
|
|
@@ -538,9 +529,7 @@ export class BasePackCompiler {
|
|
|
538
529
|
* @param {PassStats} stats - The pass's tallies.
|
|
539
530
|
*/
|
|
540
531
|
reportDetail(stats) {
|
|
541
|
-
log.debug(
|
|
542
|
-
`Skipped ${stats.skippedOther} file(s) this pack does not claim`,
|
|
543
|
-
);
|
|
532
|
+
log.debug(`Skipped ${stats.skippedOther} file(s) this pack does not claim`);
|
|
544
533
|
}
|
|
545
534
|
|
|
546
535
|
/**
|
|
@@ -563,8 +552,7 @@ export class BasePackCompiler {
|
|
|
563
552
|
// them in the skipped tally is the defect (#56). Each one has
|
|
564
553
|
// already been named individually as a diagnostic.
|
|
565
554
|
log.error(
|
|
566
|
-
`Declined ${stats.declined} note(s) declaring a retired ` +
|
|
567
|
-
`frontmatter field`,
|
|
555
|
+
`Declined ${stats.declined} note(s) declaring a retired ` + `frontmatter field`,
|
|
568
556
|
);
|
|
569
557
|
}
|
|
570
558
|
this.reportDetail(stats);
|
|
@@ -588,13 +576,9 @@ export class BasePackCompiler {
|
|
|
588
576
|
const label = this.constructor.label;
|
|
589
577
|
const Label = label.charAt(0).toUpperCase() + label.slice(1);
|
|
590
578
|
|
|
591
|
-
for (const {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
absPath,
|
|
595
|
-
bodyLine,
|
|
596
|
-
bodyColumn,
|
|
597
|
-
} of walkMarkdownTree(this.contentBase)) {
|
|
579
|
+
for (const { frontmatter: fm, body, absPath, bodyLine, bodyColumn } of walkMarkdownTree(
|
|
580
|
+
this.contentBase,
|
|
581
|
+
)) {
|
|
598
582
|
// Which note this pass is on, so anything it calls can report a
|
|
599
583
|
// position without every method having to be handed one (#17).
|
|
600
584
|
this.currentNote = { absPath, bodyLine, bodyColumn };
|
|
@@ -665,9 +649,7 @@ export class BasePackCompiler {
|
|
|
665
649
|
continue;
|
|
666
650
|
}
|
|
667
651
|
|
|
668
|
-
log.debug(
|
|
669
|
-
`Processing ${this.noteLabel(fm)}: ${resolveName(fm)} (${absPath})`,
|
|
670
|
-
);
|
|
652
|
+
log.debug(`Processing ${this.noteLabel(fm)}: ${resolveName(fm)} (${absPath})`);
|
|
671
653
|
try {
|
|
672
654
|
const doc = this.compileNote(fm, this.convertBody(fm, body));
|
|
673
655
|
stats.compiled++;
|
package/engine/code-fences.mjs
CHANGED
|
@@ -111,9 +111,7 @@ export function codeRegions(markdown, { spans = true } = {}) {
|
|
|
111
111
|
const fence = FENCE_LINE.exec(line);
|
|
112
112
|
if (fence) {
|
|
113
113
|
const marker = fence[2];
|
|
114
|
-
const closer = new RegExp(
|
|
115
|
-
`^[ \\t]*${marker[0]}{${marker.length},}[ \\t]*$`,
|
|
116
|
-
);
|
|
114
|
+
const closer = new RegExp(`^[ \\t]*${marker[0]}{${marker.length},}[ \\t]*$`);
|
|
117
115
|
let close = i + 1;
|
|
118
116
|
while (close < lines.length && !closer.test(lines[close])) close++;
|
|
119
117
|
// An unclosed fence runs to the end of the document.
|
|
@@ -225,9 +223,7 @@ function inside(regions, offset) {
|
|
|
225
223
|
*/
|
|
226
224
|
function offsetOf(args) {
|
|
227
225
|
const last = args[args.length - 1];
|
|
228
|
-
return typeof last === "string" ?
|
|
229
|
-
args[args.length - 2]
|
|
230
|
-
: args[args.length - 3];
|
|
226
|
+
return typeof last === "string" ? args[args.length - 2] : args[args.length - 3];
|
|
231
227
|
}
|
|
232
228
|
|
|
233
229
|
/**
|
|
@@ -263,9 +259,7 @@ export function replaceOutsideCode(markdown, pattern, replacer, options) {
|
|
|
263
259
|
export function matchAllOutsideCode(markdown, pattern, options) {
|
|
264
260
|
const src = String(markdown ?? "");
|
|
265
261
|
const regions = codeRegions(src, options);
|
|
266
|
-
return [...src.matchAll(pattern)].filter(
|
|
267
|
-
(match) => !inside(regions, match.index),
|
|
268
|
-
);
|
|
262
|
+
return [...src.matchAll(pattern)].filter((match) => !inside(regions, match.index));
|
|
269
263
|
}
|
|
270
264
|
|
|
271
265
|
/**
|
|
@@ -304,8 +298,5 @@ export function protectCode(body, transform) {
|
|
|
304
298
|
last = region.end;
|
|
305
299
|
}
|
|
306
300
|
masked += src.slice(last);
|
|
307
|
-
return transform(masked).replace(
|
|
308
|
-
/\u0000(\d+)\u0000/g,
|
|
309
|
-
(_m, i) => stash[Number(i)],
|
|
310
|
-
);
|
|
301
|
+
return transform(masked).replace(/\u0000(\d+)\u0000/g, (_m, i) => stash[Number(i)]);
|
|
311
302
|
}
|
package/engine/compendiums.mjs
CHANGED
|
@@ -87,17 +87,13 @@ export async function compilePacks({
|
|
|
87
87
|
stageDest = config.paths.stage,
|
|
88
88
|
packName,
|
|
89
89
|
} = {}) {
|
|
90
|
-
const packNames = sourcePacks.filter(
|
|
91
|
-
(name) => !packName || name === packName,
|
|
92
|
-
);
|
|
90
|
+
const packNames = sourcePacks.filter((name) => !packName || name === packName);
|
|
93
91
|
|
|
94
92
|
// A prebuilt pack's JSON is checked in rather than generated, so it is
|
|
95
93
|
// compiled from where it lives. Anything else comes from the build-only
|
|
96
94
|
// intermediate the generation pass writes.
|
|
97
95
|
const prebuiltDirs = new Map(
|
|
98
|
-
config.packs
|
|
99
|
-
.filter((pack) => pack.prebuilt)
|
|
100
|
-
.map((pack) => [pack.name, pack.prebuilt]),
|
|
96
|
+
config.packs.filter((pack) => pack.prebuilt).map((pack) => [pack.name, pack.prebuilt]),
|
|
101
97
|
);
|
|
102
98
|
const needsGeneration = packNames.some((name) => !prebuiltDirs.has(name));
|
|
103
99
|
|
|
@@ -185,8 +181,7 @@ function cleanPackEntry(
|
|
|
185
181
|
}
|
|
186
182
|
delete data.flags?.importSource;
|
|
187
183
|
delete data.flags?.exportSource;
|
|
188
|
-
if (data._stats?.lastModifiedBy)
|
|
189
|
-
data._stats.lastModifiedBy = lastModifiedBy;
|
|
184
|
+
if (data._stats?.lastModifiedBy) data._stats.lastModifiedBy = lastModifiedBy;
|
|
190
185
|
|
|
191
186
|
// Remove empty entries in flags
|
|
192
187
|
if (!data.flags) data.flags = {};
|
|
@@ -194,18 +189,11 @@ function cleanPackEntry(
|
|
|
194
189
|
if (Object.keys(contents).length === 0) delete data.flags[key];
|
|
195
190
|
});
|
|
196
191
|
|
|
197
|
-
if (data.effects)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (data.
|
|
202
|
-
data.items.forEach((i) => cleanPackEntry(i, { clearSourceId: false }));
|
|
203
|
-
if (data.pages)
|
|
204
|
-
data.pages.forEach((i) => cleanPackEntry(i, { ownership: -1 }));
|
|
205
|
-
if (data.system?.description)
|
|
206
|
-
data.system.description = cleanString(data.system.description);
|
|
207
|
-
if (data.system?.biography)
|
|
208
|
-
data.system.biography = cleanString(data.system.biography);
|
|
192
|
+
if (data.effects) data.effects.forEach((i) => cleanPackEntry(i, { clearSourceId: false }));
|
|
193
|
+
if (data.items) data.items.forEach((i) => cleanPackEntry(i, { clearSourceId: false }));
|
|
194
|
+
if (data.pages) data.pages.forEach((i) => cleanPackEntry(i, { ownership: -1 }));
|
|
195
|
+
if (data.system?.description) data.system.description = cleanString(data.system.description);
|
|
196
|
+
if (data.system?.biography) data.system.biography = cleanString(data.system.biography);
|
|
209
197
|
if (data.system?.textReference)
|
|
210
198
|
data.system.textReference = cleanString(data.system.textReference);
|
|
211
199
|
if (data.system?.notes) data.system.notes = cleanString(data.system.notes);
|
|
@@ -249,10 +237,7 @@ export async function cleanPacks({
|
|
|
249
237
|
|
|
250
238
|
const folders = fs
|
|
251
239
|
.readdirSync(packDest, { withFileTypes: true })
|
|
252
|
-
.filter(
|
|
253
|
-
(file) =>
|
|
254
|
-
file.isDirectory() && (!packName || packName === file.name),
|
|
255
|
-
);
|
|
240
|
+
.filter((file) => file.isDirectory() && (!packName || packName === file.name));
|
|
256
241
|
|
|
257
242
|
/**
|
|
258
243
|
* Walk through directories to find JSON files.
|
|
@@ -273,9 +258,7 @@ export async function cleanPacks({
|
|
|
273
258
|
const json = JSON.parse(await readFile(src, { encoding: "utf8" }));
|
|
274
259
|
if (entryName && entryName !== json.name.toLowerCase()) continue;
|
|
275
260
|
if (!json._id || !json._key) {
|
|
276
|
-
log.info(
|
|
277
|
-
`Failed to clean \x1b[31m${src}\x1b[0m, must have _id and _key.`,
|
|
278
|
-
);
|
|
261
|
+
log.info(`Failed to clean \x1b[31m${src}\x1b[0m, must have _id and _key.`);
|
|
279
262
|
continue;
|
|
280
263
|
}
|
|
281
264
|
cleanPackEntry(json);
|
|
@@ -351,21 +334,14 @@ export async function unpackPacks({
|
|
|
351
334
|
await extractPack(src, dest, {
|
|
352
335
|
log: true,
|
|
353
336
|
transformEntry: (entry) => {
|
|
354
|
-
if (entryName && entryName !== entry.name.toLowerCase())
|
|
355
|
-
return false;
|
|
337
|
+
if (entryName && entryName !== entry.name.toLowerCase()) return false;
|
|
356
338
|
cleanPackEntry(entry);
|
|
357
339
|
},
|
|
358
340
|
transformName: (entry) => {
|
|
359
341
|
if (entry._id in folders)
|
|
360
|
-
return path.join(
|
|
361
|
-
"folder_",
|
|
362
|
-
folders[entry._id].path,
|
|
363
|
-
".json",
|
|
364
|
-
);
|
|
342
|
+
return path.join("folder_", folders[entry._id].path, ".json");
|
|
365
343
|
const outputName = slugify(entry.name);
|
|
366
|
-
const parent =
|
|
367
|
-
containers[entry.system?.container] ??
|
|
368
|
-
folders[entry.folder];
|
|
344
|
+
const parent = containers[entry.system?.container] ?? folders[entry.folder];
|
|
369
345
|
return path.join(parent?.path ?? "", `${outputName}.json`);
|
|
370
346
|
},
|
|
371
347
|
});
|
|
@@ -80,9 +80,7 @@ export function contentAddress(fm, name, isReadme) {
|
|
|
80
80
|
*/
|
|
81
81
|
function landingOf(fm, isReadme, landing) {
|
|
82
82
|
if (landing === "readme") {
|
|
83
|
-
return isReadme ?
|
|
84
|
-
{ landing: true, segment: sectionOf(fm) }
|
|
85
|
-
: { landing: false };
|
|
83
|
+
return isReadme ? { landing: true, segment: sectionOf(fm) } : { landing: false };
|
|
86
84
|
}
|
|
87
85
|
// `collection`. The section is authored rather than derived: it is the
|
|
88
86
|
// identity of the section being introduced, and the note's own title
|
|
@@ -127,9 +125,7 @@ export function packageAddress(fm, name, { isReadme = false, scheme } = {}) {
|
|
|
127
125
|
const land = landingOf(fm, isReadme, landing);
|
|
128
126
|
if (land.landing) {
|
|
129
127
|
if (typeof land.segment !== "string" || !land.segment) {
|
|
130
|
-
throw new Error(
|
|
131
|
-
`landing note declares no section, so it lands nowhere`,
|
|
132
|
-
);
|
|
128
|
+
throw new Error(`landing note declares no section, so it lands nowhere`);
|
|
133
129
|
}
|
|
134
130
|
return `${prefix}${land.segment}/`;
|
|
135
131
|
}
|