@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.
Files changed (75) hide show
  1. package/CHANGELOG.md +173 -0
  2. package/bin/content-build.mjs +44 -118
  3. package/bin/package-build.mjs +27 -69
  4. package/bin/report.mjs +1 -2
  5. package/bundle.mjs +2 -10
  6. package/config.mjs +31 -106
  7. package/container.mjs +13 -57
  8. package/content-config.mjs +45 -164
  9. package/coverage.mjs +14 -55
  10. package/deploy.mjs +4 -13
  11. package/e2e.mjs +16 -55
  12. package/engine/address-diff.mjs +1 -4
  13. package/engine/base-compiler.mjs +10 -28
  14. package/engine/code-fences.mjs +4 -13
  15. package/engine/compendiums.mjs +13 -37
  16. package/engine/content-address.mjs +2 -6
  17. package/engine/content-links.mjs +13 -44
  18. package/engine/content-lint.mjs +4 -15
  19. package/engine/content-slug.mjs +2 -6
  20. package/engine/content-tables.mjs +26 -79
  21. package/engine/diagnostics.mjs +4 -15
  22. package/engine/field-reference.mjs +6 -20
  23. package/engine/field-spec.mjs +1 -3
  24. package/engine/foreign-catalog.mjs +7 -22
  25. package/engine/foreign-manifests.mjs +1 -4
  26. package/engine/frontmatter-lint.mjs +6 -18
  27. package/engine/frontmatter.mjs +3 -8
  28. package/engine/generate.mjs +4 -16
  29. package/engine/helpers.mjs +13 -52
  30. package/engine/homepage.mjs +4 -15
  31. package/engine/ids.mjs +3 -12
  32. package/engine/item-registry.mjs +2 -6
  33. package/engine/journals.mjs +4 -14
  34. package/engine/kb-manifest.mjs +5 -17
  35. package/engine/macros.mjs +2 -10
  36. package/engine/manifest-emit.mjs +6 -17
  37. package/engine/map-notes.mjs +19 -69
  38. package/engine/note-package.mjs +1 -4
  39. package/engine/pack-config.mjs +17 -38
  40. package/engine/pack-router.mjs +1 -2
  41. package/engine/prose-config.mjs +20 -4
  42. package/engine/prose-lint.mjs +6 -14
  43. package/engine/region-events.mjs +1 -3
  44. package/engine/scene-levels.mjs +8 -22
  45. package/engine/scenes.mjs +13 -47
  46. package/engine/schema-check.mjs +1 -4
  47. package/engine/schema-extract.mjs +70 -45
  48. package/engine/site-build.mjs +12 -37
  49. package/engine/site-index.mjs +3 -15
  50. package/engine/web-wikilinks.mjs +4 -13
  51. package/engine/wikilinks.mjs +115 -167
  52. package/index.mjs +1 -5
  53. package/lang.mjs +1 -3
  54. package/manifest.mjs +10 -37
  55. package/markdownlint-config.mjs +1 -5
  56. package/package.json +1 -1
  57. package/sohl/actors.mjs +10 -40
  58. package/sohl/being-info.mjs +3 -6
  59. package/sohl/index.mjs +1 -6
  60. package/sohl/item-builders.mjs +1 -3
  61. package/sohl/item-fields.mjs +16 -34
  62. package/sohl/items.mjs +1 -3
  63. package/sohl/kb-passes.mjs +29 -39
  64. package/sohl/skill-base.mjs +7 -23
  65. package/stage.mjs +3 -13
  66. package/templates.mjs +4 -15
  67. package/types/bundle.d.mts +1 -1
  68. package/types/container.d.mts +2 -2
  69. package/types/coverage.d.mts +1 -1
  70. package/types/e2e.d.mts +4 -4
  71. package/types/engine/generate.d.mts +1 -1
  72. package/types/engine/helpers.d.mts +1 -1
  73. package/types/engine/schema-extract.d.mts +1 -1
  74. package/types/engine/site-index.d.mts +1 -1
  75. 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
- env[`FOUNDRYVTT_${stageUpper}_PORT`] ??
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
- env[`${prefix}WORLD_ID`]?.trim() ||
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
- /** @type {string} */ (program),
613
- [...rest, ...args],
614
- {
615
- stdio: "inherit",
616
- cwd,
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,
@@ -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;
@@ -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
- frontmatter: fm,
593
- body,
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++;
@@ -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
  }
@@ -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
- data.effects.forEach((i) =>
199
- cleanPackEntry(i, { clearSourceId: false }),
200
- );
201
- if (data.items)
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
  }