@patterkit/compiler 0.12.0 → 0.14.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/dist/index.cjs CHANGED
@@ -430,18 +430,17 @@ function extensionPass(input) {
430
430
  for (const [id, entry] of Object.entries(taken)) {
431
431
  const m = manifests.get(id);
432
432
  const version = typeof entry?.version === "string" ? entry.version : "";
433
- const settings = entry?.settings ?? {};
434
433
  if (m === void 0) {
435
434
  issues.push({
436
435
  severity: "warning",
437
436
  file: projectPath,
438
437
  message: `the project takes the Patter extension "${id}", but has no manifest for it at ${import_model2.EXTENSIONS_DIR}/${id}${import_model2.MANIFEST_EXTENSION}; its data is left as it is, and not compiled`
439
438
  });
440
- takenList.push({ id, settings });
439
+ takenList.push({ id });
441
440
  continue;
442
441
  }
443
442
  if (!m.sound) {
444
- takenList.push({ id, settings });
443
+ takenList.push({ id });
445
444
  continue;
446
445
  }
447
446
  const manifest = m.manifest;
@@ -452,26 +451,34 @@ function extensionPass(input) {
452
451
  message: `the project takes "${id}" ${version === "" ? "with no version" : `at version ${version}`}, but its manifest is version ${manifest.version}; run \`patter format\` to take it (renamed stages are renamed in the data as it does)`
453
452
  });
454
453
  }
455
- const known = new Set((manifest.settings ?? []).map((s) => s.name));
456
- for (const [name, value] of Object.entries(settings)) {
457
- if (!known.has(name)) {
458
- issues.push({ severity: "warning", file: projectPath, message: `"${id}" has no setting "${name}"; it's ignored` });
459
- } else if (typeof value !== "string" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
460
- issues.push({
461
- severity: "error",
462
- file: projectPath,
463
- message: `"${id}"'s setting "${name}" names a gameData field, so it's a field name, not ${JSON.stringify(value)}`
464
- });
465
- }
466
- }
467
- takenList.push({ id, settings, manifest });
454
+ takenList.push({ id, manifest });
468
455
  }
469
456
  const sound = new Map(takenList.filter((t) => t.manifest !== void 0).map((t) => [t.id, t.manifest]));
470
457
  const nodes = extensionNodes(project, scenes);
471
- const claims = takenList.flatMap((t) => t.manifest ? (0, import_extensions.resolvedClaims)(t.manifest, "patter", t.settings) : []);
472
- for (const p of (0, import_extensions.claimClashes)(claims)) issues.push({ severity: p.severity, file: projectPath, code: p.code, message: p.message });
473
- issues.push(...claimIssues(input, nodes, claims));
474
458
  const exists = (kind, id) => kind === "cast" ? (project.cast ?? []).some((c) => c.name === id) : nodes.get(id)?.kind === kind;
459
+ const projectId = project.project.id;
460
+ const projectEntries = /* @__PURE__ */ new Map();
461
+ for (const { shard } of input.shards) {
462
+ const entry = shard.entries?.["project"]?.[projectId];
463
+ if (typeof entry === "object" && entry !== null && !Array.isArray(entry) && !projectEntries.has(shard.extension)) {
464
+ projectEntries.set(shard.extension, entry);
465
+ }
466
+ }
467
+ const contexts = /* @__PURE__ */ new Map();
468
+ for (const [ext, manifest] of sound) {
469
+ contexts.set(ext, { exists, list: (0, import_extensions.pickLists)((0, import_extensions.targetOf)(manifest, "patter", "project"), projectEntries.get(ext)) });
470
+ }
471
+ const nodesOf = (kind) => kind === "cast" ? (project.cast ?? []).map((c) => ({ id: c.name })) : [...nodes].filter(([, n]) => n.kind === kind).map(([id]) => ({ id }));
472
+ for (const [ext, manifest] of [...sound].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
473
+ const said = /* @__PURE__ */ new Set();
474
+ for (const decl of pickDecls(manifest)) {
475
+ for (const p of (0, import_extensions.pickClashes)(decl, { nodes: nodesOf, list: contexts.get(ext).list })) {
476
+ if (said.has(p.message)) continue;
477
+ said.add(p.message);
478
+ issues.push({ severity: p.severity, file: projectPath, node: projectId, code: p.code, message: `the Patter extension "${ext}": ${p.message}` });
479
+ }
480
+ }
481
+ }
475
482
  const sceneIds = new Set(scenes.map((s) => s.id));
476
483
  const values = /* @__PURE__ */ new Map();
477
484
  const projectValues = /* @__PURE__ */ new Map();
@@ -541,7 +548,7 @@ function extensionPass(input) {
541
548
  message: `entries.${kind}.${id} belongs ${node.scene === void 0 ? "in the project's own shard" : `in scene "${node.scene}"'s shard`}; it's compiled from here all the same`
542
549
  });
543
550
  }
544
- const problems = (0, import_extensions.entryProblems)(target, entry, { exists });
551
+ const problems = (0, import_extensions.entryProblems)(target, entry, contexts.get(ext) ?? { exists });
545
552
  for (const p of problems) issues.push(issueOf(path, p, `entries.${kind}.${id}${p.path === "" ? ": " : "."}`, id));
546
553
  if (problems.some((p) => p.severity === "error")) continue;
547
554
  const compiled = (0, import_extensions.compileEntry)(target, entry);
@@ -555,7 +562,6 @@ function extensionPass(input) {
555
562
  }
556
563
  }
557
564
  const bundle = {};
558
- const chosen = new Map(takenList.map((t) => [t.id, t.settings]));
559
565
  for (const [id, manifest] of [...sound.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
560
566
  const fields = {};
561
567
  for (const kind of Object.keys(manifest.targets.patter ?? {}).sort()) {
@@ -563,59 +569,24 @@ function extensionPass(input) {
563
569
  if (target !== void 0) fields[kind] = (0, import_extensions.compiledTarget)(target);
564
570
  }
565
571
  const projectEntry = projectValues.get(id);
566
- const settings = (manifest.settings ?? []).length > 0 ? sortRecord((0, import_extensions.settingValues)(manifest, chosen.get(id))) : void 0;
567
- bundle[id] = {
568
- version: manifest.version,
569
- fields,
570
- ...projectEntry !== void 0 ? { project: projectEntry } : {},
571
- ...settings !== void 0 ? { settings } : {}
572
- };
572
+ bundle[id] = { version: manifest.version, fields, ...projectEntry !== void 0 ? { project: projectEntry } : {} };
573
573
  }
574
574
  const sortedNodes = new Map([...values.entries()].map(([id, v]) => [id, sortRecord(v)]));
575
575
  return { issues, compiled: { bundle, nodes: sortedNodes } };
576
576
  }
577
+ function pickDecls(manifest) {
578
+ const out = [];
579
+ const walk = (decl) => {
580
+ if (decl.type === "pick") out.push(decl);
581
+ for (const f of decl.fields ?? []) walk(f);
582
+ if (decl.of !== void 0) walk(decl.of);
583
+ };
584
+ for (const target of Object.values(manifest.targets.patter ?? {})) for (const f of target.fields ?? []) walk(f);
585
+ return out;
586
+ }
577
587
  function sortRecord(r) {
578
588
  return Object.fromEntries(Object.keys(r).sort().map((k) => [k, r[k]]));
579
589
  }
580
- function claimIssues(input, nodes, claims) {
581
- const issues = [];
582
- const projectPath = input.projectPath ?? "";
583
- const declared = input.project.gameDataFields ?? {};
584
- for (const claim of claims) {
585
- const decl = (declared[claim.kind] ?? []).find((d) => d.name === claim.field);
586
- if (decl === void 0) {
587
- issues.push({
588
- severity: "warning",
589
- file: projectPath,
590
- message: `the Patter extension "${claim.extension}" claims the ${claim.kind} gameData field "${claim.field}", which the project doesn't declare`
591
- });
592
- continue;
593
- }
594
- if (decl.type !== claim.claim.type) {
595
- issues.push({
596
- severity: "error",
597
- file: projectPath,
598
- message: `the Patter extension "${claim.extension}" claims the ${claim.kind} gameData field "${claim.field}" as ${claim.claim.type}, but the project declares it as ${decl.type}`
599
- });
600
- continue;
601
- }
602
- const effective = (0, import_extensions.effectiveClaim)(claim.claim, decl);
603
- for (const [id, node] of nodes) {
604
- if (node.kind !== claim.kind || node.gameData === void 0 || !own(node.gameData, claim.field)) continue;
605
- const file = (node.scene !== void 0 ? input.sceneFiles?.[node.scene] : void 0) ?? projectPath;
606
- for (const p of (0, import_extensions.valueProblems)(effective, node.gameData[claim.field])) {
607
- issues.push({
608
- severity: p.severity,
609
- file,
610
- node: id,
611
- code: p.code,
612
- message: `${claim.kind} '${id}' gameData "${claim.field}", as the Patter extension "${claim.extension}" claims it: ${p.message}`
613
- });
614
- }
615
- }
616
- }
617
- return issues;
618
- }
619
590
 
620
591
  // src/export.ts
621
592
  function compileBeat(beat, ctx, asAction = false) {
@@ -844,8 +815,7 @@ function exportBundle(input) {
844
815
  Object.entries(project.gameDataFields).map(([kind, fields]) => [kind, resolved(fields)])
845
816
  ),
846
817
  // The extensions the project takes: each one's version, its compiled declarations by node kind (every
847
- // default resolved, editor-only fields and the editor's words left out), the project node's values, and
848
- // its settings, each the project's value or else the default.
818
+ // default resolved, editor-only fields and the editor's words left out), and the project node's values.
849
819
  ...extensionSection !== void 0 ? { extensions: extensionSection } : {},
850
820
  // Closed-caption delimiters ride along only when the project pins a non-default pair (#214); the
851
821
  // runtime falls back to `[` / `]` when absent, so default projects keep their exact prior bundle.
package/dist/index.js CHANGED
@@ -321,17 +321,14 @@ import { textIsAction, usedQualifiers, usedSpeakerPairs, walkNodes as walkNodes2
321
321
  import { EXTENSIONS_DIR, MANIFEST_EXTENSION, PROJECT_LOCALE_SCENE } from "@patterkit/model";
322
322
  import {
323
323
  HOSTS,
324
- claimClashes,
325
324
  compileEntry,
326
325
  compiledTarget,
327
- effectiveClaim,
328
326
  entryProblems,
329
327
  manifestProblems,
330
328
  orphanedEntries,
331
- resolvedClaims,
332
- settingValues,
333
- targetOf,
334
- valueProblems
329
+ pickClashes,
330
+ pickLists,
331
+ targetOf
335
332
  } from "@wildwinter/toolkit/extensions";
336
333
  function extensionNodes(project, scenes) {
337
334
  const nodes = /* @__PURE__ */ new Map();
@@ -407,18 +404,17 @@ function extensionPass(input) {
407
404
  for (const [id, entry] of Object.entries(taken)) {
408
405
  const m = manifests.get(id);
409
406
  const version = typeof entry?.version === "string" ? entry.version : "";
410
- const settings = entry?.settings ?? {};
411
407
  if (m === void 0) {
412
408
  issues.push({
413
409
  severity: "warning",
414
410
  file: projectPath,
415
411
  message: `the project takes the Patter extension "${id}", but has no manifest for it at ${EXTENSIONS_DIR}/${id}${MANIFEST_EXTENSION}; its data is left as it is, and not compiled`
416
412
  });
417
- takenList.push({ id, settings });
413
+ takenList.push({ id });
418
414
  continue;
419
415
  }
420
416
  if (!m.sound) {
421
- takenList.push({ id, settings });
417
+ takenList.push({ id });
422
418
  continue;
423
419
  }
424
420
  const manifest = m.manifest;
@@ -429,26 +425,34 @@ function extensionPass(input) {
429
425
  message: `the project takes "${id}" ${version === "" ? "with no version" : `at version ${version}`}, but its manifest is version ${manifest.version}; run \`patter format\` to take it (renamed stages are renamed in the data as it does)`
430
426
  });
431
427
  }
432
- const known = new Set((manifest.settings ?? []).map((s) => s.name));
433
- for (const [name, value] of Object.entries(settings)) {
434
- if (!known.has(name)) {
435
- issues.push({ severity: "warning", file: projectPath, message: `"${id}" has no setting "${name}"; it's ignored` });
436
- } else if (typeof value !== "string" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
437
- issues.push({
438
- severity: "error",
439
- file: projectPath,
440
- message: `"${id}"'s setting "${name}" names a gameData field, so it's a field name, not ${JSON.stringify(value)}`
441
- });
442
- }
443
- }
444
- takenList.push({ id, settings, manifest });
428
+ takenList.push({ id, manifest });
445
429
  }
446
430
  const sound = new Map(takenList.filter((t) => t.manifest !== void 0).map((t) => [t.id, t.manifest]));
447
431
  const nodes = extensionNodes(project, scenes);
448
- const claims = takenList.flatMap((t) => t.manifest ? resolvedClaims(t.manifest, "patter", t.settings) : []);
449
- for (const p of claimClashes(claims)) issues.push({ severity: p.severity, file: projectPath, code: p.code, message: p.message });
450
- issues.push(...claimIssues(input, nodes, claims));
451
432
  const exists = (kind, id) => kind === "cast" ? (project.cast ?? []).some((c) => c.name === id) : nodes.get(id)?.kind === kind;
433
+ const projectId = project.project.id;
434
+ const projectEntries = /* @__PURE__ */ new Map();
435
+ for (const { shard } of input.shards) {
436
+ const entry = shard.entries?.["project"]?.[projectId];
437
+ if (typeof entry === "object" && entry !== null && !Array.isArray(entry) && !projectEntries.has(shard.extension)) {
438
+ projectEntries.set(shard.extension, entry);
439
+ }
440
+ }
441
+ const contexts = /* @__PURE__ */ new Map();
442
+ for (const [ext, manifest] of sound) {
443
+ contexts.set(ext, { exists, list: pickLists(targetOf(manifest, "patter", "project"), projectEntries.get(ext)) });
444
+ }
445
+ const nodesOf = (kind) => kind === "cast" ? (project.cast ?? []).map((c) => ({ id: c.name })) : [...nodes].filter(([, n]) => n.kind === kind).map(([id]) => ({ id }));
446
+ for (const [ext, manifest] of [...sound].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
447
+ const said = /* @__PURE__ */ new Set();
448
+ for (const decl of pickDecls(manifest)) {
449
+ for (const p of pickClashes(decl, { nodes: nodesOf, list: contexts.get(ext).list })) {
450
+ if (said.has(p.message)) continue;
451
+ said.add(p.message);
452
+ issues.push({ severity: p.severity, file: projectPath, node: projectId, code: p.code, message: `the Patter extension "${ext}": ${p.message}` });
453
+ }
454
+ }
455
+ }
452
456
  const sceneIds = new Set(scenes.map((s) => s.id));
453
457
  const values = /* @__PURE__ */ new Map();
454
458
  const projectValues = /* @__PURE__ */ new Map();
@@ -518,7 +522,7 @@ function extensionPass(input) {
518
522
  message: `entries.${kind}.${id} belongs ${node.scene === void 0 ? "in the project's own shard" : `in scene "${node.scene}"'s shard`}; it's compiled from here all the same`
519
523
  });
520
524
  }
521
- const problems = entryProblems(target, entry, { exists });
525
+ const problems = entryProblems(target, entry, contexts.get(ext) ?? { exists });
522
526
  for (const p of problems) issues.push(issueOf(path, p, `entries.${kind}.${id}${p.path === "" ? ": " : "."}`, id));
523
527
  if (problems.some((p) => p.severity === "error")) continue;
524
528
  const compiled = compileEntry(target, entry);
@@ -532,7 +536,6 @@ function extensionPass(input) {
532
536
  }
533
537
  }
534
538
  const bundle = {};
535
- const chosen = new Map(takenList.map((t) => [t.id, t.settings]));
536
539
  for (const [id, manifest] of [...sound.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
537
540
  const fields = {};
538
541
  for (const kind of Object.keys(manifest.targets.patter ?? {}).sort()) {
@@ -540,59 +543,24 @@ function extensionPass(input) {
540
543
  if (target !== void 0) fields[kind] = compiledTarget(target);
541
544
  }
542
545
  const projectEntry = projectValues.get(id);
543
- const settings = (manifest.settings ?? []).length > 0 ? sortRecord(settingValues(manifest, chosen.get(id))) : void 0;
544
- bundle[id] = {
545
- version: manifest.version,
546
- fields,
547
- ...projectEntry !== void 0 ? { project: projectEntry } : {},
548
- ...settings !== void 0 ? { settings } : {}
549
- };
546
+ bundle[id] = { version: manifest.version, fields, ...projectEntry !== void 0 ? { project: projectEntry } : {} };
550
547
  }
551
548
  const sortedNodes = new Map([...values.entries()].map(([id, v]) => [id, sortRecord(v)]));
552
549
  return { issues, compiled: { bundle, nodes: sortedNodes } };
553
550
  }
551
+ function pickDecls(manifest) {
552
+ const out = [];
553
+ const walk = (decl) => {
554
+ if (decl.type === "pick") out.push(decl);
555
+ for (const f of decl.fields ?? []) walk(f);
556
+ if (decl.of !== void 0) walk(decl.of);
557
+ };
558
+ for (const target of Object.values(manifest.targets.patter ?? {})) for (const f of target.fields ?? []) walk(f);
559
+ return out;
560
+ }
554
561
  function sortRecord(r) {
555
562
  return Object.fromEntries(Object.keys(r).sort().map((k) => [k, r[k]]));
556
563
  }
557
- function claimIssues(input, nodes, claims) {
558
- const issues = [];
559
- const projectPath = input.projectPath ?? "";
560
- const declared = input.project.gameDataFields ?? {};
561
- for (const claim of claims) {
562
- const decl = (declared[claim.kind] ?? []).find((d) => d.name === claim.field);
563
- if (decl === void 0) {
564
- issues.push({
565
- severity: "warning",
566
- file: projectPath,
567
- message: `the Patter extension "${claim.extension}" claims the ${claim.kind} gameData field "${claim.field}", which the project doesn't declare`
568
- });
569
- continue;
570
- }
571
- if (decl.type !== claim.claim.type) {
572
- issues.push({
573
- severity: "error",
574
- file: projectPath,
575
- message: `the Patter extension "${claim.extension}" claims the ${claim.kind} gameData field "${claim.field}" as ${claim.claim.type}, but the project declares it as ${decl.type}`
576
- });
577
- continue;
578
- }
579
- const effective = effectiveClaim(claim.claim, decl);
580
- for (const [id, node] of nodes) {
581
- if (node.kind !== claim.kind || node.gameData === void 0 || !own(node.gameData, claim.field)) continue;
582
- const file = (node.scene !== void 0 ? input.sceneFiles?.[node.scene] : void 0) ?? projectPath;
583
- for (const p of valueProblems(effective, node.gameData[claim.field])) {
584
- issues.push({
585
- severity: p.severity,
586
- file,
587
- node: id,
588
- code: p.code,
589
- message: `${claim.kind} '${id}' gameData "${claim.field}", as the Patter extension "${claim.extension}" claims it: ${p.message}`
590
- });
591
- }
592
- }
593
- }
594
- return issues;
595
- }
596
564
 
597
565
  // src/export.ts
598
566
  function compileBeat(beat, ctx, asAction = false) {
@@ -821,8 +789,7 @@ function exportBundle(input) {
821
789
  Object.entries(project.gameDataFields).map(([kind, fields]) => [kind, resolved(fields)])
822
790
  ),
823
791
  // The extensions the project takes: each one's version, its compiled declarations by node kind (every
824
- // default resolved, editor-only fields and the editor's words left out), the project node's values, and
825
- // its settings, each the project's value or else the default.
792
+ // default resolved, editor-only fields and the editor's words left out), and the project node's values.
826
793
  ...extensionSection !== void 0 ? { extensions: extensionSection } : {},
827
794
  // Closed-caption delimiters ride along only when the project pins a non-default pair (#214); the
828
795
  // runtime falls back to `[` / `]` when absent, so default projects keep their exact prior bundle.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patterkit/compiler",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Patter compiler: compile condition/effect expressions, validate them, and export source to the runtime bundle.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,11 +34,11 @@
34
34
  "build": "tsup src/index.ts --format esm,cjs --dts --clean"
35
35
  },
36
36
  "dependencies": {
37
- "@patterkit/model": "0.19.0",
38
- "@patterkit/core": "0.9.3",
39
- "@patterkit/dialect": "0.2.16",
37
+ "@patterkit/model": "0.20.0",
38
+ "@patterkit/core": "0.9.5",
39
+ "@patterkit/dialect": "0.2.17",
40
40
  "@wildwinter/expr": "^0.6.0",
41
- "@wildwinter/toolkit": "^0.2.2"
41
+ "@wildwinter/toolkit": "^0.3.0"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "@wildwinter/scoperegistry": ">=0.9.0 <1.0.0"