@patterkit/compiler 0.8.0 → 0.9.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 +303 -18
- package/dist/index.d.cts +77 -2
- package/dist/index.d.ts +77 -2
- package/dist/index.js +309 -15
- package/package.json +6 -5
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,8 @@ __export(index_exports, {
|
|
|
24
24
|
PATTER_SCOPE: () => PATTER_SCOPE,
|
|
25
25
|
compileExpression: () => compileExpression,
|
|
26
26
|
exportBundle: () => exportBundle,
|
|
27
|
+
extensionNodes: () => extensionNodes,
|
|
28
|
+
extensionPass: () => extensionPass,
|
|
27
29
|
externalGameScopes: () => externalGameScopes,
|
|
28
30
|
hostScopesToSpec: () => import_dialect4.hostScopesToSpec,
|
|
29
31
|
projectScopes: () => projectScopes,
|
|
@@ -53,14 +55,14 @@ function sameScope(a, b) {
|
|
|
53
55
|
return norm(a) === norm(b);
|
|
54
56
|
}
|
|
55
57
|
function projectScopes(project, merged) {
|
|
56
|
-
const
|
|
57
|
-
if (!merged) return { host:
|
|
58
|
+
const own2 = project.scopeRegistry;
|
|
59
|
+
if (!merged) return { host: own2, external: [], notes: [] };
|
|
58
60
|
const notes = [];
|
|
59
61
|
const hosted = [];
|
|
60
62
|
const bundled = /* @__PURE__ */ new Set();
|
|
61
63
|
let changed = false;
|
|
62
64
|
const shared = (token) => token === PATTER_SCOPE ? void 0 : merged.spec.scopes.find((s) => s.token === token);
|
|
63
|
-
for (const h of
|
|
65
|
+
for (const h of own2?.scopes ?? []) {
|
|
64
66
|
const s = shared(h.token);
|
|
65
67
|
const who = merged.owners.get(h.token);
|
|
66
68
|
if (!s || !who) {
|
|
@@ -96,7 +98,7 @@ function projectScopes(project, merged) {
|
|
|
96
98
|
changed = true;
|
|
97
99
|
}
|
|
98
100
|
const external = merged.spec.scopes.filter((s) => s.token !== PATTER_SCOPE && !bundled.has(s.token));
|
|
99
|
-
const host = !changed ?
|
|
101
|
+
const host = !changed ? own2 : hosted.length ? { version: own2?.version ?? 1, scopes: hosted } : void 0;
|
|
100
102
|
return { host, external, notes };
|
|
101
103
|
}
|
|
102
104
|
function externalGameScopes(strict, merged) {
|
|
@@ -349,11 +351,276 @@ var import_dialect4 = require("@patterkit/dialect");
|
|
|
349
351
|
var import_scoperegistry = require("@wildwinter/scoperegistry");
|
|
350
352
|
var import_core = require("@patterkit/core");
|
|
351
353
|
var import_dialect3 = require("@patterkit/dialect");
|
|
354
|
+
var import_model3 = require("@patterkit/model");
|
|
355
|
+
|
|
356
|
+
// src/extensions.ts
|
|
352
357
|
var import_model2 = require("@patterkit/model");
|
|
358
|
+
var import_extensions = require("@wildwinter/toolkit/extensions");
|
|
359
|
+
function extensionNodes(project, scenes) {
|
|
360
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
361
|
+
const add = (id, kind, scene, gameData) => {
|
|
362
|
+
if (!nodes.has(id)) nodes.set(id, { kind, ...scene !== void 0 ? { scene } : {}, ...gameData !== void 0 ? { gameData } : {} });
|
|
363
|
+
};
|
|
364
|
+
add(project.project.id, "project", void 0, void 0);
|
|
365
|
+
for (const scene of scenes) {
|
|
366
|
+
const s = scene.id;
|
|
367
|
+
add(s, "scene", s, scene.gameData);
|
|
368
|
+
const walk = (children, inChoice) => {
|
|
369
|
+
for (const node of children ?? []) {
|
|
370
|
+
if (node.type === "group") {
|
|
371
|
+
if (inChoice) {
|
|
372
|
+
add(node.id, "option", s, node.gameData);
|
|
373
|
+
if (node.prompt) add(node.prompt.id, node.prompt.kind, s, node.prompt.gameData);
|
|
374
|
+
}
|
|
375
|
+
walk(node.children ?? [], node.selector === "choice");
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
add(node.id, "snippet", s, node.gameData);
|
|
379
|
+
for (const beat of node.beats ?? []) add(beat.id, beat.kind, s, beat.gameData);
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
for (const block of scene.blocks ?? []) {
|
|
383
|
+
add(block.id, "block", s, block.gameData);
|
|
384
|
+
walk(block.children ?? [], false);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return nodes;
|
|
388
|
+
}
|
|
389
|
+
var issueOf = (file, p, prefix = "", node) => ({
|
|
390
|
+
severity: p.severity,
|
|
391
|
+
file,
|
|
392
|
+
code: p.code,
|
|
393
|
+
...node !== void 0 ? { node } : {},
|
|
394
|
+
message: p.path === "" ? `${prefix}${p.message}` : `${prefix}${p.path}: ${p.message}`
|
|
395
|
+
});
|
|
396
|
+
var folderOf = (path) => {
|
|
397
|
+
const parts = path.split(/[\\/]/);
|
|
398
|
+
return parts.length >= 2 ? parts[parts.length - 2] : void 0;
|
|
399
|
+
};
|
|
400
|
+
var own = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
401
|
+
function extensionPass(input) {
|
|
402
|
+
const { project, scenes } = input;
|
|
403
|
+
const issues = [];
|
|
404
|
+
const projectPath = input.projectPath ?? "";
|
|
405
|
+
const taken = project.extensions ?? {};
|
|
406
|
+
const manifests = /* @__PURE__ */ new Map();
|
|
407
|
+
for (const m of input.manifests) {
|
|
408
|
+
const problems = (0, import_extensions.manifestProblems)(m.manifest);
|
|
409
|
+
for (const p of problems) issues.push(issueOf(m.path, p, "manifest "));
|
|
410
|
+
let sound2 = !problems.some((p) => p.severity === "error");
|
|
411
|
+
const id = m.manifest?.id;
|
|
412
|
+
if (sound2 && id !== m.id) {
|
|
413
|
+
issues.push({
|
|
414
|
+
severity: "error",
|
|
415
|
+
file: m.path,
|
|
416
|
+
message: `the manifest's id is "${String(id)}", but its file is named for "${m.id}"; a manifest is called <id>${import_model2.MANIFEST_EXTENSION}`
|
|
417
|
+
});
|
|
418
|
+
sound2 = false;
|
|
419
|
+
}
|
|
420
|
+
if (sound2 && m.manifest.targets.patter === void 0) {
|
|
421
|
+
issues.push({
|
|
422
|
+
severity: "warning",
|
|
423
|
+
file: m.path,
|
|
424
|
+
message: `"${m.id}" isn't a Patter extension: its manifest targets no Patter node kind, so it adds nothing here`
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
manifests.set(m.id, { path: m.path, manifest: m.manifest, sound: sound2 });
|
|
428
|
+
}
|
|
429
|
+
const takenList = [];
|
|
430
|
+
for (const [id, entry] of Object.entries(taken)) {
|
|
431
|
+
const m = manifests.get(id);
|
|
432
|
+
const version = typeof entry?.version === "string" ? entry.version : "";
|
|
433
|
+
const settings = entry?.settings ?? {};
|
|
434
|
+
if (m === void 0) {
|
|
435
|
+
issues.push({
|
|
436
|
+
severity: "warning",
|
|
437
|
+
file: projectPath,
|
|
438
|
+
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
|
+
});
|
|
440
|
+
takenList.push({ id, settings });
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (!m.sound) {
|
|
444
|
+
takenList.push({ id, settings });
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
const manifest = m.manifest;
|
|
448
|
+
if (version !== manifest.version) {
|
|
449
|
+
issues.push({
|
|
450
|
+
severity: "warning",
|
|
451
|
+
file: projectPath,
|
|
452
|
+
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
|
+
});
|
|
454
|
+
}
|
|
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 });
|
|
468
|
+
}
|
|
469
|
+
const sound = new Map(takenList.filter((t) => t.manifest !== void 0).map((t) => [t.id, t.manifest]));
|
|
470
|
+
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
|
+
const exists = (kind, id) => kind === "cast" ? (project.cast ?? []).some((c) => c.name === id) : nodes.get(id)?.kind === kind;
|
|
475
|
+
const sceneIds = new Set(scenes.map((s) => s.id));
|
|
476
|
+
const values = /* @__PURE__ */ new Map();
|
|
477
|
+
const projectValues = /* @__PURE__ */ new Map();
|
|
478
|
+
for (const { path, shard } of [...input.shards].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)) {
|
|
479
|
+
const folder = folderOf(path);
|
|
480
|
+
if (folder !== void 0 && shard.extension !== folder) {
|
|
481
|
+
issues.push({
|
|
482
|
+
severity: "error",
|
|
483
|
+
file: path,
|
|
484
|
+
message: `this shard says it holds "${String(shard.extension)}", but it sits in the folder for "${folder}"`
|
|
485
|
+
});
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
const ext = shard.extension;
|
|
489
|
+
if (!own(taken, ext)) {
|
|
490
|
+
issues.push({
|
|
491
|
+
severity: "warning",
|
|
492
|
+
file: path,
|
|
493
|
+
message: `data for the Patter extension "${ext}", which the project doesn't take; it's left as it is, and not compiled`
|
|
494
|
+
});
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
const entries = shard.entries;
|
|
498
|
+
if (typeof entries !== "object" || entries === null || Array.isArray(entries)) {
|
|
499
|
+
issues.push({ severity: "error", file: path, message: "entries are an object, by node kind and then node id" });
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
const shardScene = shard.scene === import_model2.PROJECT_LOCALE_SCENE ? void 0 : shard.scene;
|
|
503
|
+
const sceneKnown = shardScene === void 0 || sceneIds.has(shardScene);
|
|
504
|
+
if (!sceneKnown) {
|
|
505
|
+
issues.push({
|
|
506
|
+
severity: "warning",
|
|
507
|
+
file: path,
|
|
508
|
+
message: `this shard is for scene "${shard.scene}", which the project doesn't have; its entries are compiled where their nodes are`
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
for (const p of (0, import_extensions.orphanedEntries)(shard, exists)) issues.push(issueOf(path, p));
|
|
512
|
+
const manifest = sound.get(ext);
|
|
513
|
+
if (manifest === void 0) continue;
|
|
514
|
+
for (const [kind, byNode] of Object.entries(entries)) {
|
|
515
|
+
if (!import_extensions.HOSTS.patter.kinds.includes(kind)) {
|
|
516
|
+
issues.push({
|
|
517
|
+
severity: "warning",
|
|
518
|
+
file: path,
|
|
519
|
+
message: `entries.${kind}: "${kind}" isn't a Patter node kind; these entries are kept, and not compiled`
|
|
520
|
+
});
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
const target = (0, import_extensions.targetOf)(manifest, "patter", kind);
|
|
524
|
+
if (target === void 0) {
|
|
525
|
+
issues.push({
|
|
526
|
+
severity: "warning",
|
|
527
|
+
file: path,
|
|
528
|
+
message: `entries.${kind}: "${ext}" declares nothing for a ${kind}; these entries are kept, and not compiled`
|
|
529
|
+
});
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
if (typeof byNode !== "object" || byNode === null || Array.isArray(byNode)) continue;
|
|
533
|
+
for (const [id, entry] of Object.entries(byNode)) {
|
|
534
|
+
const node = nodes.get(id);
|
|
535
|
+
if (node === void 0 || node.kind !== kind) continue;
|
|
536
|
+
if (sceneKnown && node.scene !== shardScene) {
|
|
537
|
+
issues.push({
|
|
538
|
+
severity: "warning",
|
|
539
|
+
file: path,
|
|
540
|
+
node: id,
|
|
541
|
+
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
|
+
});
|
|
543
|
+
}
|
|
544
|
+
const problems = (0, import_extensions.entryProblems)(target, entry, { exists });
|
|
545
|
+
for (const p of problems) issues.push(issueOf(path, p, `entries.${kind}.${id}${p.path === "" ? ": " : "."}`, id));
|
|
546
|
+
if (problems.some((p) => p.severity === "error")) continue;
|
|
547
|
+
const compiled = (0, import_extensions.compileEntry)(target, entry);
|
|
548
|
+
if (compiled === void 0) continue;
|
|
549
|
+
if (kind === "project") {
|
|
550
|
+
projectValues.set(ext, compiled);
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
values.set(id, { ...values.get(id) ?? {}, [ext]: compiled });
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
const bundle = {};
|
|
558
|
+
for (const [id, manifest] of [...sound.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
|
|
559
|
+
const fields = {};
|
|
560
|
+
for (const kind of Object.keys(manifest.targets.patter ?? {}).sort()) {
|
|
561
|
+
const target = (0, import_extensions.targetOf)(manifest, "patter", kind);
|
|
562
|
+
if (target !== void 0) fields[kind] = (0, import_extensions.compiledTarget)(target);
|
|
563
|
+
}
|
|
564
|
+
const projectEntry = projectValues.get(id);
|
|
565
|
+
bundle[id] = { version: manifest.version, fields, ...projectEntry !== void 0 ? { project: projectEntry } : {} };
|
|
566
|
+
}
|
|
567
|
+
const sortedNodes = new Map([...values.entries()].map(([id, v]) => [id, sortRecord(v)]));
|
|
568
|
+
return { issues, compiled: { bundle, nodes: sortedNodes } };
|
|
569
|
+
}
|
|
570
|
+
function sortRecord(r) {
|
|
571
|
+
return Object.fromEntries(Object.keys(r).sort().map((k) => [k, r[k]]));
|
|
572
|
+
}
|
|
573
|
+
function claimIssues(input, nodes, claims) {
|
|
574
|
+
const issues = [];
|
|
575
|
+
const projectPath = input.projectPath ?? "";
|
|
576
|
+
const declared = input.project.gameDataFields ?? {};
|
|
577
|
+
for (const claim of claims) {
|
|
578
|
+
const decl = (declared[claim.kind] ?? []).find((d) => d.name === claim.field);
|
|
579
|
+
if (decl === void 0) {
|
|
580
|
+
issues.push({
|
|
581
|
+
severity: "warning",
|
|
582
|
+
file: projectPath,
|
|
583
|
+
message: `the Patter extension "${claim.extension}" claims the ${claim.kind} gameData field "${claim.field}", which the project doesn't declare`
|
|
584
|
+
});
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
if (decl.type !== claim.claim.type) {
|
|
588
|
+
issues.push({
|
|
589
|
+
severity: "error",
|
|
590
|
+
file: projectPath,
|
|
591
|
+
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}`
|
|
592
|
+
});
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
for (const [id, node] of nodes) {
|
|
596
|
+
if (node.kind !== claim.kind || node.gameData === void 0 || !own(node.gameData, claim.field)) continue;
|
|
597
|
+
const file = (node.scene !== void 0 ? input.sceneFiles?.[node.scene] : void 0) ?? projectPath;
|
|
598
|
+
for (const p of (0, import_extensions.valueProblems)(claim.claim, node.gameData[claim.field])) {
|
|
599
|
+
issues.push({
|
|
600
|
+
severity: p.severity,
|
|
601
|
+
file,
|
|
602
|
+
node: id,
|
|
603
|
+
code: p.code,
|
|
604
|
+
message: `${claim.kind} '${id}' gameData "${claim.field}", as the Patter extension "${claim.extension}" claims it: ${p.message}`
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return issues;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// src/export.ts
|
|
613
|
+
function compileBeat(beat, ctx) {
|
|
614
|
+
const values = ctx.extensions.get(beat.id);
|
|
615
|
+
if (values === void 0 && !Object.prototype.hasOwnProperty.call(beat, "extensions")) return beat;
|
|
616
|
+
const { extensions: _source, ...rest } = beat;
|
|
617
|
+
return values !== void 0 ? { ...rest, extensions: values } : rest;
|
|
618
|
+
}
|
|
353
619
|
function compileEffect(e, foreign) {
|
|
354
620
|
return { kind: "set", target: e.target, value: compileExpression(e.value, foreign) };
|
|
355
621
|
}
|
|
356
|
-
function compileNode(node,
|
|
622
|
+
function compileNode(node, ctx) {
|
|
623
|
+
const { foreign } = ctx;
|
|
357
624
|
if (node.type === "group") {
|
|
358
625
|
return {
|
|
359
626
|
id: node.id,
|
|
@@ -362,13 +629,15 @@ function compileNode(node, foreign) {
|
|
|
362
629
|
selector: node.selector,
|
|
363
630
|
shared: node.shared,
|
|
364
631
|
options: node.options,
|
|
365
|
-
children: node.children.map((c) => compileNode(c,
|
|
632
|
+
children: node.children.map((c) => compileNode(c, ctx)),
|
|
366
633
|
padAfterDefault: node.padAfterDefault,
|
|
367
634
|
// line padding: the default for the beats inside
|
|
368
635
|
gameData: node.gameData,
|
|
636
|
+
extensions: ctx.extensions.get(node.id),
|
|
637
|
+
// a choice's option's extension values
|
|
369
638
|
tags: node.tags,
|
|
370
639
|
// author tags (#215), accumulated at runtime
|
|
371
|
-
prompt: node.prompt,
|
|
640
|
+
prompt: node.prompt && compileBeat(node.prompt, ctx),
|
|
372
641
|
// option-position only (spec §5)
|
|
373
642
|
secretUntilEligible: node.secretUntilEligible,
|
|
374
643
|
sticky: node.sticky,
|
|
@@ -379,12 +648,13 @@ function compileNode(node, foreign) {
|
|
|
379
648
|
id: node.id,
|
|
380
649
|
type: "snippet",
|
|
381
650
|
condition: node.condition !== void 0 ? compileExpression(node.condition, foreign) : void 0,
|
|
382
|
-
beats: node.beats,
|
|
651
|
+
beats: node.beats && node.beats.map((b) => compileBeat(b, ctx)),
|
|
383
652
|
onEnter: node.onEnter?.map((e) => compileEffect(e, foreign)),
|
|
384
653
|
onExit: node.onExit?.map((e) => compileEffect(e, foreign)),
|
|
385
654
|
padAfterDefault: node.padAfterDefault,
|
|
386
655
|
// line padding: the default for the beats inside
|
|
387
656
|
gameData: node.gameData,
|
|
657
|
+
extensions: ctx.extensions.get(node.id),
|
|
388
658
|
tags: node.tags,
|
|
389
659
|
// author tags (#215)
|
|
390
660
|
jump: node.jump,
|
|
@@ -393,22 +663,24 @@ function compileNode(node, foreign) {
|
|
|
393
663
|
fallback: node.fallback
|
|
394
664
|
};
|
|
395
665
|
}
|
|
396
|
-
function compileBlock(block,
|
|
666
|
+
function compileBlock(block, ctx) {
|
|
397
667
|
return {
|
|
398
668
|
id: block.id,
|
|
399
669
|
type: "block",
|
|
400
670
|
name: block.name,
|
|
401
671
|
...block.gameId ? { gameId: block.gameId } : {},
|
|
402
672
|
// host-facing address; runtime falls back to the name slug
|
|
403
|
-
children: block.children.map((c) => compileNode(c,
|
|
673
|
+
children: block.children.map((c) => compileNode(c, ctx)),
|
|
404
674
|
padAfterDefault: block.padAfterDefault,
|
|
405
675
|
// line padding: the default for the beats inside
|
|
406
676
|
gameData: block.gameData,
|
|
677
|
+
extensions: ctx.extensions.get(block.id),
|
|
407
678
|
tags: block.tags
|
|
408
679
|
// author tags (#215)
|
|
409
680
|
};
|
|
410
681
|
}
|
|
411
|
-
function compileScene(scene,
|
|
682
|
+
function compileScene(scene, ctx) {
|
|
683
|
+
const { foreign } = ctx;
|
|
412
684
|
return {
|
|
413
685
|
id: scene.id,
|
|
414
686
|
type: "scene",
|
|
@@ -418,11 +690,12 @@ function compileScene(scene, foreign) {
|
|
|
418
690
|
padAfterDefault: scene.padAfterDefault,
|
|
419
691
|
// line padding: the default for the beats inside
|
|
420
692
|
gameData: scene.gameData,
|
|
693
|
+
extensions: ctx.extensions.get(scene.id),
|
|
421
694
|
tags: scene.tags,
|
|
422
695
|
// author tags (#215)
|
|
423
696
|
onEntry: scene.onEntry?.map((e) => compileEffect(e, foreign)),
|
|
424
697
|
sceneProps: scene.sceneProps,
|
|
425
|
-
blocks: scene.blocks.map((b) => compileBlock(b,
|
|
698
|
+
blocks: scene.blocks.map((b) => compileBlock(b, ctx))
|
|
426
699
|
};
|
|
427
700
|
}
|
|
428
701
|
function externalScopesIn(scenes, external) {
|
|
@@ -456,12 +729,16 @@ function exportBundle(input) {
|
|
|
456
729
|
version: foreignScopes?.version ?? 1,
|
|
457
730
|
scopes: [...foreignScopes?.scopes ?? [], ...gameExternal.map((s) => ({ token: s.token }))]
|
|
458
731
|
});
|
|
732
|
+
const ext = input.extensions;
|
|
733
|
+
const compiledExt = project.extensions !== void 0 || ext !== void 0 && (ext.manifests.length > 0 || ext.shards.length > 0) ? extensionPass({ project, scenes, manifests: ext?.manifests ?? [], shards: ext?.shards ?? [] }).compiled : void 0;
|
|
734
|
+
const extensionSection = compiledExt !== void 0 && Object.keys(compiledExt.bundle).length > 0 ? compiledExt.bundle : void 0;
|
|
735
|
+
const ctx = { foreign: compileScopes, extensions: compiledExt?.nodes ?? /* @__PURE__ */ new Map() };
|
|
459
736
|
const scenesOut = {};
|
|
460
|
-
for (const scene of scenes) scenesOut[scene.id] = compileScene(scene,
|
|
737
|
+
for (const scene of scenes) scenesOut[scene.id] = compileScene(scene, ctx);
|
|
461
738
|
const externalScopes = externalScopesIn(scenesOut, /* @__PURE__ */ new Set([...import_dialect3.EXTERNAL_SCOPES, ...gameExternal.map((s) => s.token)]));
|
|
462
|
-
const qualifiers = (0,
|
|
463
|
-
const usedPairs = new Set((0,
|
|
464
|
-
const speakerPattern = qualifiers.length > 0 && project.speakerPattern !== void 0 && project.speakerPattern !==
|
|
739
|
+
const qualifiers = (0, import_model3.usedQualifiers)(project, scenes).map((q) => ({ gameId: q.gameId, name: q.name }));
|
|
740
|
+
const usedPairs = new Set((0, import_model3.usedSpeakerPairs)(scenes).map((p) => `${p.character}\0${p.qualifier}`));
|
|
741
|
+
const speakerPattern = qualifiers.length > 0 && project.speakerPattern !== void 0 && project.speakerPattern !== import_model3.DEFAULT_SPEAKER_PATTERN ? project.speakerPattern : void 0;
|
|
465
742
|
const strings = {};
|
|
466
743
|
for (const loc of locales) {
|
|
467
744
|
if (includeLocales && !includeLocales.includes(loc.locale)) continue;
|
|
@@ -488,7 +765,9 @@ function exportBundle(input) {
|
|
|
488
765
|
properties: project.properties ?? [],
|
|
489
766
|
...foreignScopes ? { scopeRegistry: foreignScopes } : {},
|
|
490
767
|
...qualifiers.length > 0 ? { qualifiers } : {},
|
|
491
|
-
...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {}
|
|
768
|
+
...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {},
|
|
769
|
+
// Each node's values are in `scenes`; the declarations and the project's own values are here.
|
|
770
|
+
...extensionSection !== void 0 ? { extensions: extensionSection } : {}
|
|
492
771
|
}, { trailingComma: false })),
|
|
493
772
|
// Structure-only fingerprint (live bundle refresh): the same hash with the string tables left
|
|
494
773
|
// out. Same structureHash + a different hash = a text-only edit, safe to swap in place with
|
|
@@ -499,7 +778,8 @@ function exportBundle(input) {
|
|
|
499
778
|
properties: project.properties ?? [],
|
|
500
779
|
...foreignScopes ? { scopeRegistry: foreignScopes } : {},
|
|
501
780
|
...qualifiers.length > 0 ? { qualifiers } : {},
|
|
502
|
-
...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {}
|
|
781
|
+
...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {},
|
|
782
|
+
...extensionSection !== void 0 ? { extensions: extensionSection } : {}
|
|
503
783
|
}, { trailingComma: false }))
|
|
504
784
|
},
|
|
505
785
|
voiced: project.voiced ?? false,
|
|
@@ -528,6 +808,9 @@ function exportBundle(input) {
|
|
|
528
808
|
gameDataFields: project.gameDataFields && Object.fromEntries(
|
|
529
809
|
Object.entries(project.gameDataFields).map(([kind, fields]) => [kind, resolved(fields)])
|
|
530
810
|
),
|
|
811
|
+
// The extensions the project takes: each one's version, its compiled declarations by node kind (every
|
|
812
|
+
// default resolved, editor-only fields and the editor's words left out) and the project node's values.
|
|
813
|
+
...extensionSection !== void 0 ? { extensions: extensionSection } : {},
|
|
531
814
|
// Closed-caption delimiters ride along only when the project pins a non-default pair (#214); the
|
|
532
815
|
// runtime falls back to `[` / `]` when absent, so default projects keep their exact prior bundle.
|
|
533
816
|
...project.closedCaptions ? { closedCaptions: project.closedCaptions } : {},
|
|
@@ -543,6 +826,8 @@ function exportBundle(input) {
|
|
|
543
826
|
PATTER_SCOPE,
|
|
544
827
|
compileExpression,
|
|
545
828
|
exportBundle,
|
|
829
|
+
extensionNodes,
|
|
830
|
+
extensionPass,
|
|
546
831
|
externalGameScopes,
|
|
547
832
|
hostScopesToSpec,
|
|
548
833
|
projectScopes,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ScopeRegistrySpec, ScopeSpec } from '@wildwinter/scoperegistry';
|
|
2
2
|
import { MergedScopes } from '@wildwinter/scoperegistry/scopes';
|
|
3
|
-
import { Expression, ProjectFile, Scene, LocaleFile, Bundle, HostScopeRegistry } from '@patterkit/model';
|
|
3
|
+
import { Expression, ProjectFile, Scene, LocaleFile, BundleExtension, NodeExtensions, ExtensionShard, GameData, Bundle, HostScopeRegistry } from '@patterkit/model';
|
|
4
4
|
export { EXTERNAL_SCOPES, hostScopesToSpec } from '@patterkit/dialect';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -62,6 +62,70 @@ declare function validateInterpolation(input: {
|
|
|
62
62
|
locales?: LocaleFile[];
|
|
63
63
|
}, options?: ValidateOptions): ConditionIssue[];
|
|
64
64
|
|
|
65
|
+
/** One extension's manifest, as a project keeps it (`extensions/<id>.extension.json`). */
|
|
66
|
+
interface ExtensionManifestFile {
|
|
67
|
+
/** Where it is, for the report. */
|
|
68
|
+
path: string;
|
|
69
|
+
/** The file's stem, which names the extension it should be. */
|
|
70
|
+
id: string;
|
|
71
|
+
/** The parsed JSON, unchecked: the pass says whether it is sound. */
|
|
72
|
+
manifest: unknown;
|
|
73
|
+
}
|
|
74
|
+
/** One extension data shard (`.patterextension`), with where it is. */
|
|
75
|
+
interface ExtensionShardFile {
|
|
76
|
+
/** `<layout.extensions>/<extension id>/<scene file stem>.patterextension`: the folder names the extension. */
|
|
77
|
+
path: string;
|
|
78
|
+
shard: ExtensionShard;
|
|
79
|
+
}
|
|
80
|
+
/** What the pass reads: the project, its scenes, and its extension files. */
|
|
81
|
+
interface ExtensionPassInput {
|
|
82
|
+
project: ProjectFile;
|
|
83
|
+
scenes: Scene[];
|
|
84
|
+
manifests: readonly ExtensionManifestFile[];
|
|
85
|
+
shards: readonly ExtensionShardFile[];
|
|
86
|
+
/** The project file's path, for the report (default `""`). */
|
|
87
|
+
projectPath?: string;
|
|
88
|
+
/** Each scene's file, by scene id, for the report. */
|
|
89
|
+
sceneFiles?: Readonly<Record<string, string>>;
|
|
90
|
+
}
|
|
91
|
+
/** One thing the pass found. `file` is the file to open; `node` the node id, when it is about one. */
|
|
92
|
+
interface ExtensionIssue {
|
|
93
|
+
severity: "error" | "warning";
|
|
94
|
+
file: string;
|
|
95
|
+
node?: string;
|
|
96
|
+
/** The toolkit's stable name for the problem, when it is one of the toolkit's. */
|
|
97
|
+
code?: string;
|
|
98
|
+
message: string;
|
|
99
|
+
}
|
|
100
|
+
/** What the pass hands the bundle. */
|
|
101
|
+
interface CompiledExtensions {
|
|
102
|
+
/** The bundle's `extensions` section, by extension id. Empty when the project takes none. */
|
|
103
|
+
readonly bundle: Record<string, BundleExtension>;
|
|
104
|
+
/** Each node's compiled values, by node id: what goes on the node as `extensions`. */
|
|
105
|
+
readonly nodes: ReadonlyMap<string, NodeExtensions>;
|
|
106
|
+
}
|
|
107
|
+
/** A node the project has: its kind, the scene it belongs to (none for the project's own), and its gameData. */
|
|
108
|
+
interface ExtensionNode {
|
|
109
|
+
readonly kind: string;
|
|
110
|
+
readonly scene?: string;
|
|
111
|
+
readonly gameData?: GameData;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Every node an extension can attach to, by id (ids are project-unique): the project, each scene, block and
|
|
115
|
+
* snippet, a choice's options (the groups directly inside a `choice` group, as gameData's `option` is), and
|
|
116
|
+
* every beat by its kind, an option's prompt beat included. A bare snippet in a choice stays a snippet.
|
|
117
|
+
*/
|
|
118
|
+
declare function extensionNodes(project: ProjectFile, scenes: readonly Scene[]): Map<string, ExtensionNode>;
|
|
119
|
+
/**
|
|
120
|
+
* Validate every extension, and compile what may be compiled. `issues` is the pass's report; `compiled` is
|
|
121
|
+
* what the bundle carries. Data whose manifest is missing or unsound is reported and left out of the bundle,
|
|
122
|
+
* never changed; an entry with an error is left out on its own.
|
|
123
|
+
*/
|
|
124
|
+
declare function extensionPass(input: ExtensionPassInput): {
|
|
125
|
+
issues: ExtensionIssue[];
|
|
126
|
+
compiled: CompiledExtensions;
|
|
127
|
+
};
|
|
128
|
+
|
|
65
129
|
interface ExportInput {
|
|
66
130
|
project: ProjectFile;
|
|
67
131
|
scenes: Scene[];
|
|
@@ -82,6 +146,17 @@ interface ExportInput {
|
|
|
82
146
|
* does: named in `externalScopes`, never baked in.
|
|
83
147
|
*/
|
|
84
148
|
gameScopes?: MergedScopes;
|
|
149
|
+
/**
|
|
150
|
+
* The project's Patter extensions: its manifests and its extension data shards. Each node's values are
|
|
151
|
+
* compiled inline under `extensions`, and the bundle's `extensions` section carries each extension the
|
|
152
|
+
* project takes. Absent or empty, with no extension taken, the bundle and its hashes are exactly what they
|
|
153
|
+
* were. What is wrong with the data is validate's to report (`extensionPass`); here an entry with an
|
|
154
|
+
* error is simply left out.
|
|
155
|
+
*/
|
|
156
|
+
extensions?: {
|
|
157
|
+
manifests: readonly ExtensionManifestFile[];
|
|
158
|
+
shards: readonly ExtensionShardFile[];
|
|
159
|
+
};
|
|
85
160
|
}
|
|
86
161
|
/** Compile a project's source into the runtime bundle. */
|
|
87
162
|
declare function exportBundle(input: ExportInput): Bundle;
|
|
@@ -116,4 +191,4 @@ declare function projectScopes(project: ProjectFile, merged?: MergedScopes): Pro
|
|
|
116
191
|
*/
|
|
117
192
|
declare function externalGameScopes(strict: ScopeRegistrySpec | undefined, merged?: MergedScopes): ScopeSpec[];
|
|
118
193
|
|
|
119
|
-
export { type ConditionIssue, type ExportInput, type HostScopeNote, PATTER_SCOPE, type ProjectScopes, type ValidateOptions, compileExpression, exportBundle, externalGameScopes, projectScopes, validateConditions, validateInterpolation };
|
|
194
|
+
export { type CompiledExtensions, type ConditionIssue, type ExportInput, type ExtensionIssue, type ExtensionManifestFile, type ExtensionNode, type ExtensionPassInput, type ExtensionShardFile, type HostScopeNote, PATTER_SCOPE, type ProjectScopes, type ValidateOptions, compileExpression, exportBundle, extensionNodes, extensionPass, externalGameScopes, projectScopes, validateConditions, validateInterpolation };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ScopeRegistrySpec, ScopeSpec } from '@wildwinter/scoperegistry';
|
|
2
2
|
import { MergedScopes } from '@wildwinter/scoperegistry/scopes';
|
|
3
|
-
import { Expression, ProjectFile, Scene, LocaleFile, Bundle, HostScopeRegistry } from '@patterkit/model';
|
|
3
|
+
import { Expression, ProjectFile, Scene, LocaleFile, BundleExtension, NodeExtensions, ExtensionShard, GameData, Bundle, HostScopeRegistry } from '@patterkit/model';
|
|
4
4
|
export { EXTERNAL_SCOPES, hostScopesToSpec } from '@patterkit/dialect';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -62,6 +62,70 @@ declare function validateInterpolation(input: {
|
|
|
62
62
|
locales?: LocaleFile[];
|
|
63
63
|
}, options?: ValidateOptions): ConditionIssue[];
|
|
64
64
|
|
|
65
|
+
/** One extension's manifest, as a project keeps it (`extensions/<id>.extension.json`). */
|
|
66
|
+
interface ExtensionManifestFile {
|
|
67
|
+
/** Where it is, for the report. */
|
|
68
|
+
path: string;
|
|
69
|
+
/** The file's stem, which names the extension it should be. */
|
|
70
|
+
id: string;
|
|
71
|
+
/** The parsed JSON, unchecked: the pass says whether it is sound. */
|
|
72
|
+
manifest: unknown;
|
|
73
|
+
}
|
|
74
|
+
/** One extension data shard (`.patterextension`), with where it is. */
|
|
75
|
+
interface ExtensionShardFile {
|
|
76
|
+
/** `<layout.extensions>/<extension id>/<scene file stem>.patterextension`: the folder names the extension. */
|
|
77
|
+
path: string;
|
|
78
|
+
shard: ExtensionShard;
|
|
79
|
+
}
|
|
80
|
+
/** What the pass reads: the project, its scenes, and its extension files. */
|
|
81
|
+
interface ExtensionPassInput {
|
|
82
|
+
project: ProjectFile;
|
|
83
|
+
scenes: Scene[];
|
|
84
|
+
manifests: readonly ExtensionManifestFile[];
|
|
85
|
+
shards: readonly ExtensionShardFile[];
|
|
86
|
+
/** The project file's path, for the report (default `""`). */
|
|
87
|
+
projectPath?: string;
|
|
88
|
+
/** Each scene's file, by scene id, for the report. */
|
|
89
|
+
sceneFiles?: Readonly<Record<string, string>>;
|
|
90
|
+
}
|
|
91
|
+
/** One thing the pass found. `file` is the file to open; `node` the node id, when it is about one. */
|
|
92
|
+
interface ExtensionIssue {
|
|
93
|
+
severity: "error" | "warning";
|
|
94
|
+
file: string;
|
|
95
|
+
node?: string;
|
|
96
|
+
/** The toolkit's stable name for the problem, when it is one of the toolkit's. */
|
|
97
|
+
code?: string;
|
|
98
|
+
message: string;
|
|
99
|
+
}
|
|
100
|
+
/** What the pass hands the bundle. */
|
|
101
|
+
interface CompiledExtensions {
|
|
102
|
+
/** The bundle's `extensions` section, by extension id. Empty when the project takes none. */
|
|
103
|
+
readonly bundle: Record<string, BundleExtension>;
|
|
104
|
+
/** Each node's compiled values, by node id: what goes on the node as `extensions`. */
|
|
105
|
+
readonly nodes: ReadonlyMap<string, NodeExtensions>;
|
|
106
|
+
}
|
|
107
|
+
/** A node the project has: its kind, the scene it belongs to (none for the project's own), and its gameData. */
|
|
108
|
+
interface ExtensionNode {
|
|
109
|
+
readonly kind: string;
|
|
110
|
+
readonly scene?: string;
|
|
111
|
+
readonly gameData?: GameData;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Every node an extension can attach to, by id (ids are project-unique): the project, each scene, block and
|
|
115
|
+
* snippet, a choice's options (the groups directly inside a `choice` group, as gameData's `option` is), and
|
|
116
|
+
* every beat by its kind, an option's prompt beat included. A bare snippet in a choice stays a snippet.
|
|
117
|
+
*/
|
|
118
|
+
declare function extensionNodes(project: ProjectFile, scenes: readonly Scene[]): Map<string, ExtensionNode>;
|
|
119
|
+
/**
|
|
120
|
+
* Validate every extension, and compile what may be compiled. `issues` is the pass's report; `compiled` is
|
|
121
|
+
* what the bundle carries. Data whose manifest is missing or unsound is reported and left out of the bundle,
|
|
122
|
+
* never changed; an entry with an error is left out on its own.
|
|
123
|
+
*/
|
|
124
|
+
declare function extensionPass(input: ExtensionPassInput): {
|
|
125
|
+
issues: ExtensionIssue[];
|
|
126
|
+
compiled: CompiledExtensions;
|
|
127
|
+
};
|
|
128
|
+
|
|
65
129
|
interface ExportInput {
|
|
66
130
|
project: ProjectFile;
|
|
67
131
|
scenes: Scene[];
|
|
@@ -82,6 +146,17 @@ interface ExportInput {
|
|
|
82
146
|
* does: named in `externalScopes`, never baked in.
|
|
83
147
|
*/
|
|
84
148
|
gameScopes?: MergedScopes;
|
|
149
|
+
/**
|
|
150
|
+
* The project's Patter extensions: its manifests and its extension data shards. Each node's values are
|
|
151
|
+
* compiled inline under `extensions`, and the bundle's `extensions` section carries each extension the
|
|
152
|
+
* project takes. Absent or empty, with no extension taken, the bundle and its hashes are exactly what they
|
|
153
|
+
* were. What is wrong with the data is validate's to report (`extensionPass`); here an entry with an
|
|
154
|
+
* error is simply left out.
|
|
155
|
+
*/
|
|
156
|
+
extensions?: {
|
|
157
|
+
manifests: readonly ExtensionManifestFile[];
|
|
158
|
+
shards: readonly ExtensionShardFile[];
|
|
159
|
+
};
|
|
85
160
|
}
|
|
86
161
|
/** Compile a project's source into the runtime bundle. */
|
|
87
162
|
declare function exportBundle(input: ExportInput): Bundle;
|
|
@@ -116,4 +191,4 @@ declare function projectScopes(project: ProjectFile, merged?: MergedScopes): Pro
|
|
|
116
191
|
*/
|
|
117
192
|
declare function externalGameScopes(strict: ScopeRegistrySpec | undefined, merged?: MergedScopes): ScopeSpec[];
|
|
118
193
|
|
|
119
|
-
export { type ConditionIssue, type ExportInput, type HostScopeNote, PATTER_SCOPE, type ProjectScopes, type ValidateOptions, compileExpression, exportBundle, externalGameScopes, projectScopes, validateConditions, validateInterpolation };
|
|
194
|
+
export { type CompiledExtensions, type ConditionIssue, type ExportInput, type ExtensionIssue, type ExtensionManifestFile, type ExtensionNode, type ExtensionPassInput, type ExtensionShardFile, type HostScopeNote, PATTER_SCOPE, type ProjectScopes, type ValidateOptions, compileExpression, exportBundle, extensionNodes, extensionPass, externalGameScopes, projectScopes, validateConditions, validateInterpolation };
|
package/dist/index.js
CHANGED
|
@@ -19,14 +19,14 @@ function sameScope(a, b) {
|
|
|
19
19
|
return norm(a) === norm(b);
|
|
20
20
|
}
|
|
21
21
|
function projectScopes(project, merged) {
|
|
22
|
-
const
|
|
23
|
-
if (!merged) return { host:
|
|
22
|
+
const own2 = project.scopeRegistry;
|
|
23
|
+
if (!merged) return { host: own2, external: [], notes: [] };
|
|
24
24
|
const notes = [];
|
|
25
25
|
const hosted = [];
|
|
26
26
|
const bundled = /* @__PURE__ */ new Set();
|
|
27
27
|
let changed = false;
|
|
28
28
|
const shared = (token) => token === PATTER_SCOPE ? void 0 : merged.spec.scopes.find((s) => s.token === token);
|
|
29
|
-
for (const h of
|
|
29
|
+
for (const h of own2?.scopes ?? []) {
|
|
30
30
|
const s = shared(h.token);
|
|
31
31
|
const who = merged.owners.get(h.token);
|
|
32
32
|
if (!s || !who) {
|
|
@@ -62,7 +62,7 @@ function projectScopes(project, merged) {
|
|
|
62
62
|
changed = true;
|
|
63
63
|
}
|
|
64
64
|
const external = merged.spec.scopes.filter((s) => s.token !== PATTER_SCOPE && !bundled.has(s.token));
|
|
65
|
-
const host = !changed ?
|
|
65
|
+
const host = !changed ? own2 : hosted.length ? { version: own2?.version ?? 1, scopes: hosted } : void 0;
|
|
66
66
|
return { host, external, notes };
|
|
67
67
|
}
|
|
68
68
|
function externalGameScopes(strict, merged) {
|
|
@@ -316,10 +316,286 @@ import { defaultFor } from "@wildwinter/scoperegistry";
|
|
|
316
316
|
import { canonicalStringify, hash32 } from "@patterkit/core";
|
|
317
317
|
import { hostScopesToSpec as hostScopesToSpec3, withEngineScopes as withEngineScopes2, EXTERNAL_SCOPES } from "@patterkit/dialect";
|
|
318
318
|
import { usedQualifiers, usedSpeakerPairs, DEFAULT_SPEAKER_PATTERN } from "@patterkit/model";
|
|
319
|
+
|
|
320
|
+
// src/extensions.ts
|
|
321
|
+
import { EXTENSIONS_DIR, MANIFEST_EXTENSION, PROJECT_LOCALE_SCENE } from "@patterkit/model";
|
|
322
|
+
import {
|
|
323
|
+
HOSTS,
|
|
324
|
+
claimClashes,
|
|
325
|
+
compileEntry,
|
|
326
|
+
compiledTarget,
|
|
327
|
+
entryProblems,
|
|
328
|
+
manifestProblems,
|
|
329
|
+
orphanedEntries,
|
|
330
|
+
resolvedClaims,
|
|
331
|
+
targetOf,
|
|
332
|
+
valueProblems
|
|
333
|
+
} from "@wildwinter/toolkit/extensions";
|
|
334
|
+
function extensionNodes(project, scenes) {
|
|
335
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
336
|
+
const add = (id, kind, scene, gameData) => {
|
|
337
|
+
if (!nodes.has(id)) nodes.set(id, { kind, ...scene !== void 0 ? { scene } : {}, ...gameData !== void 0 ? { gameData } : {} });
|
|
338
|
+
};
|
|
339
|
+
add(project.project.id, "project", void 0, void 0);
|
|
340
|
+
for (const scene of scenes) {
|
|
341
|
+
const s = scene.id;
|
|
342
|
+
add(s, "scene", s, scene.gameData);
|
|
343
|
+
const walk = (children, inChoice) => {
|
|
344
|
+
for (const node of children ?? []) {
|
|
345
|
+
if (node.type === "group") {
|
|
346
|
+
if (inChoice) {
|
|
347
|
+
add(node.id, "option", s, node.gameData);
|
|
348
|
+
if (node.prompt) add(node.prompt.id, node.prompt.kind, s, node.prompt.gameData);
|
|
349
|
+
}
|
|
350
|
+
walk(node.children ?? [], node.selector === "choice");
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
add(node.id, "snippet", s, node.gameData);
|
|
354
|
+
for (const beat of node.beats ?? []) add(beat.id, beat.kind, s, beat.gameData);
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
for (const block of scene.blocks ?? []) {
|
|
358
|
+
add(block.id, "block", s, block.gameData);
|
|
359
|
+
walk(block.children ?? [], false);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return nodes;
|
|
363
|
+
}
|
|
364
|
+
var issueOf = (file, p, prefix = "", node) => ({
|
|
365
|
+
severity: p.severity,
|
|
366
|
+
file,
|
|
367
|
+
code: p.code,
|
|
368
|
+
...node !== void 0 ? { node } : {},
|
|
369
|
+
message: p.path === "" ? `${prefix}${p.message}` : `${prefix}${p.path}: ${p.message}`
|
|
370
|
+
});
|
|
371
|
+
var folderOf = (path) => {
|
|
372
|
+
const parts = path.split(/[\\/]/);
|
|
373
|
+
return parts.length >= 2 ? parts[parts.length - 2] : void 0;
|
|
374
|
+
};
|
|
375
|
+
var own = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
376
|
+
function extensionPass(input) {
|
|
377
|
+
const { project, scenes } = input;
|
|
378
|
+
const issues = [];
|
|
379
|
+
const projectPath = input.projectPath ?? "";
|
|
380
|
+
const taken = project.extensions ?? {};
|
|
381
|
+
const manifests = /* @__PURE__ */ new Map();
|
|
382
|
+
for (const m of input.manifests) {
|
|
383
|
+
const problems = manifestProblems(m.manifest);
|
|
384
|
+
for (const p of problems) issues.push(issueOf(m.path, p, "manifest "));
|
|
385
|
+
let sound2 = !problems.some((p) => p.severity === "error");
|
|
386
|
+
const id = m.manifest?.id;
|
|
387
|
+
if (sound2 && id !== m.id) {
|
|
388
|
+
issues.push({
|
|
389
|
+
severity: "error",
|
|
390
|
+
file: m.path,
|
|
391
|
+
message: `the manifest's id is "${String(id)}", but its file is named for "${m.id}"; a manifest is called <id>${MANIFEST_EXTENSION}`
|
|
392
|
+
});
|
|
393
|
+
sound2 = false;
|
|
394
|
+
}
|
|
395
|
+
if (sound2 && m.manifest.targets.patter === void 0) {
|
|
396
|
+
issues.push({
|
|
397
|
+
severity: "warning",
|
|
398
|
+
file: m.path,
|
|
399
|
+
message: `"${m.id}" isn't a Patter extension: its manifest targets no Patter node kind, so it adds nothing here`
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
manifests.set(m.id, { path: m.path, manifest: m.manifest, sound: sound2 });
|
|
403
|
+
}
|
|
404
|
+
const takenList = [];
|
|
405
|
+
for (const [id, entry] of Object.entries(taken)) {
|
|
406
|
+
const m = manifests.get(id);
|
|
407
|
+
const version = typeof entry?.version === "string" ? entry.version : "";
|
|
408
|
+
const settings = entry?.settings ?? {};
|
|
409
|
+
if (m === void 0) {
|
|
410
|
+
issues.push({
|
|
411
|
+
severity: "warning",
|
|
412
|
+
file: projectPath,
|
|
413
|
+
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`
|
|
414
|
+
});
|
|
415
|
+
takenList.push({ id, settings });
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (!m.sound) {
|
|
419
|
+
takenList.push({ id, settings });
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
const manifest = m.manifest;
|
|
423
|
+
if (version !== manifest.version) {
|
|
424
|
+
issues.push({
|
|
425
|
+
severity: "warning",
|
|
426
|
+
file: projectPath,
|
|
427
|
+
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)`
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
const known = new Set((manifest.settings ?? []).map((s) => s.name));
|
|
431
|
+
for (const [name, value] of Object.entries(settings)) {
|
|
432
|
+
if (!known.has(name)) {
|
|
433
|
+
issues.push({ severity: "warning", file: projectPath, message: `"${id}" has no setting "${name}"; it's ignored` });
|
|
434
|
+
} else if (typeof value !== "string" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
|
|
435
|
+
issues.push({
|
|
436
|
+
severity: "error",
|
|
437
|
+
file: projectPath,
|
|
438
|
+
message: `"${id}"'s setting "${name}" names a gameData field, so it's a field name, not ${JSON.stringify(value)}`
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
takenList.push({ id, settings, manifest });
|
|
443
|
+
}
|
|
444
|
+
const sound = new Map(takenList.filter((t) => t.manifest !== void 0).map((t) => [t.id, t.manifest]));
|
|
445
|
+
const nodes = extensionNodes(project, scenes);
|
|
446
|
+
const claims = takenList.flatMap((t) => t.manifest ? resolvedClaims(t.manifest, "patter", t.settings) : []);
|
|
447
|
+
for (const p of claimClashes(claims)) issues.push({ severity: p.severity, file: projectPath, code: p.code, message: p.message });
|
|
448
|
+
issues.push(...claimIssues(input, nodes, claims));
|
|
449
|
+
const exists = (kind, id) => kind === "cast" ? (project.cast ?? []).some((c) => c.name === id) : nodes.get(id)?.kind === kind;
|
|
450
|
+
const sceneIds = new Set(scenes.map((s) => s.id));
|
|
451
|
+
const values = /* @__PURE__ */ new Map();
|
|
452
|
+
const projectValues = /* @__PURE__ */ new Map();
|
|
453
|
+
for (const { path, shard } of [...input.shards].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)) {
|
|
454
|
+
const folder = folderOf(path);
|
|
455
|
+
if (folder !== void 0 && shard.extension !== folder) {
|
|
456
|
+
issues.push({
|
|
457
|
+
severity: "error",
|
|
458
|
+
file: path,
|
|
459
|
+
message: `this shard says it holds "${String(shard.extension)}", but it sits in the folder for "${folder}"`
|
|
460
|
+
});
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
const ext = shard.extension;
|
|
464
|
+
if (!own(taken, ext)) {
|
|
465
|
+
issues.push({
|
|
466
|
+
severity: "warning",
|
|
467
|
+
file: path,
|
|
468
|
+
message: `data for the Patter extension "${ext}", which the project doesn't take; it's left as it is, and not compiled`
|
|
469
|
+
});
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
const entries = shard.entries;
|
|
473
|
+
if (typeof entries !== "object" || entries === null || Array.isArray(entries)) {
|
|
474
|
+
issues.push({ severity: "error", file: path, message: "entries are an object, by node kind and then node id" });
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
const shardScene = shard.scene === PROJECT_LOCALE_SCENE ? void 0 : shard.scene;
|
|
478
|
+
const sceneKnown = shardScene === void 0 || sceneIds.has(shardScene);
|
|
479
|
+
if (!sceneKnown) {
|
|
480
|
+
issues.push({
|
|
481
|
+
severity: "warning",
|
|
482
|
+
file: path,
|
|
483
|
+
message: `this shard is for scene "${shard.scene}", which the project doesn't have; its entries are compiled where their nodes are`
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
for (const p of orphanedEntries(shard, exists)) issues.push(issueOf(path, p));
|
|
487
|
+
const manifest = sound.get(ext);
|
|
488
|
+
if (manifest === void 0) continue;
|
|
489
|
+
for (const [kind, byNode] of Object.entries(entries)) {
|
|
490
|
+
if (!HOSTS.patter.kinds.includes(kind)) {
|
|
491
|
+
issues.push({
|
|
492
|
+
severity: "warning",
|
|
493
|
+
file: path,
|
|
494
|
+
message: `entries.${kind}: "${kind}" isn't a Patter node kind; these entries are kept, and not compiled`
|
|
495
|
+
});
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
const target = targetOf(manifest, "patter", kind);
|
|
499
|
+
if (target === void 0) {
|
|
500
|
+
issues.push({
|
|
501
|
+
severity: "warning",
|
|
502
|
+
file: path,
|
|
503
|
+
message: `entries.${kind}: "${ext}" declares nothing for a ${kind}; these entries are kept, and not compiled`
|
|
504
|
+
});
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
if (typeof byNode !== "object" || byNode === null || Array.isArray(byNode)) continue;
|
|
508
|
+
for (const [id, entry] of Object.entries(byNode)) {
|
|
509
|
+
const node = nodes.get(id);
|
|
510
|
+
if (node === void 0 || node.kind !== kind) continue;
|
|
511
|
+
if (sceneKnown && node.scene !== shardScene) {
|
|
512
|
+
issues.push({
|
|
513
|
+
severity: "warning",
|
|
514
|
+
file: path,
|
|
515
|
+
node: id,
|
|
516
|
+
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`
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
const problems = entryProblems(target, entry, { exists });
|
|
520
|
+
for (const p of problems) issues.push(issueOf(path, p, `entries.${kind}.${id}${p.path === "" ? ": " : "."}`, id));
|
|
521
|
+
if (problems.some((p) => p.severity === "error")) continue;
|
|
522
|
+
const compiled = compileEntry(target, entry);
|
|
523
|
+
if (compiled === void 0) continue;
|
|
524
|
+
if (kind === "project") {
|
|
525
|
+
projectValues.set(ext, compiled);
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
values.set(id, { ...values.get(id) ?? {}, [ext]: compiled });
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
const bundle = {};
|
|
533
|
+
for (const [id, manifest] of [...sound.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
|
|
534
|
+
const fields = {};
|
|
535
|
+
for (const kind of Object.keys(manifest.targets.patter ?? {}).sort()) {
|
|
536
|
+
const target = targetOf(manifest, "patter", kind);
|
|
537
|
+
if (target !== void 0) fields[kind] = compiledTarget(target);
|
|
538
|
+
}
|
|
539
|
+
const projectEntry = projectValues.get(id);
|
|
540
|
+
bundle[id] = { version: manifest.version, fields, ...projectEntry !== void 0 ? { project: projectEntry } : {} };
|
|
541
|
+
}
|
|
542
|
+
const sortedNodes = new Map([...values.entries()].map(([id, v]) => [id, sortRecord(v)]));
|
|
543
|
+
return { issues, compiled: { bundle, nodes: sortedNodes } };
|
|
544
|
+
}
|
|
545
|
+
function sortRecord(r) {
|
|
546
|
+
return Object.fromEntries(Object.keys(r).sort().map((k) => [k, r[k]]));
|
|
547
|
+
}
|
|
548
|
+
function claimIssues(input, nodes, claims) {
|
|
549
|
+
const issues = [];
|
|
550
|
+
const projectPath = input.projectPath ?? "";
|
|
551
|
+
const declared = input.project.gameDataFields ?? {};
|
|
552
|
+
for (const claim of claims) {
|
|
553
|
+
const decl = (declared[claim.kind] ?? []).find((d) => d.name === claim.field);
|
|
554
|
+
if (decl === void 0) {
|
|
555
|
+
issues.push({
|
|
556
|
+
severity: "warning",
|
|
557
|
+
file: projectPath,
|
|
558
|
+
message: `the Patter extension "${claim.extension}" claims the ${claim.kind} gameData field "${claim.field}", which the project doesn't declare`
|
|
559
|
+
});
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
if (decl.type !== claim.claim.type) {
|
|
563
|
+
issues.push({
|
|
564
|
+
severity: "error",
|
|
565
|
+
file: projectPath,
|
|
566
|
+
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}`
|
|
567
|
+
});
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
for (const [id, node] of nodes) {
|
|
571
|
+
if (node.kind !== claim.kind || node.gameData === void 0 || !own(node.gameData, claim.field)) continue;
|
|
572
|
+
const file = (node.scene !== void 0 ? input.sceneFiles?.[node.scene] : void 0) ?? projectPath;
|
|
573
|
+
for (const p of valueProblems(claim.claim, node.gameData[claim.field])) {
|
|
574
|
+
issues.push({
|
|
575
|
+
severity: p.severity,
|
|
576
|
+
file,
|
|
577
|
+
node: id,
|
|
578
|
+
code: p.code,
|
|
579
|
+
message: `${claim.kind} '${id}' gameData "${claim.field}", as the Patter extension "${claim.extension}" claims it: ${p.message}`
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return issues;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// src/export.ts
|
|
588
|
+
function compileBeat(beat, ctx) {
|
|
589
|
+
const values = ctx.extensions.get(beat.id);
|
|
590
|
+
if (values === void 0 && !Object.prototype.hasOwnProperty.call(beat, "extensions")) return beat;
|
|
591
|
+
const { extensions: _source, ...rest } = beat;
|
|
592
|
+
return values !== void 0 ? { ...rest, extensions: values } : rest;
|
|
593
|
+
}
|
|
319
594
|
function compileEffect(e, foreign) {
|
|
320
595
|
return { kind: "set", target: e.target, value: compileExpression(e.value, foreign) };
|
|
321
596
|
}
|
|
322
|
-
function compileNode(node,
|
|
597
|
+
function compileNode(node, ctx) {
|
|
598
|
+
const { foreign } = ctx;
|
|
323
599
|
if (node.type === "group") {
|
|
324
600
|
return {
|
|
325
601
|
id: node.id,
|
|
@@ -328,13 +604,15 @@ function compileNode(node, foreign) {
|
|
|
328
604
|
selector: node.selector,
|
|
329
605
|
shared: node.shared,
|
|
330
606
|
options: node.options,
|
|
331
|
-
children: node.children.map((c) => compileNode(c,
|
|
607
|
+
children: node.children.map((c) => compileNode(c, ctx)),
|
|
332
608
|
padAfterDefault: node.padAfterDefault,
|
|
333
609
|
// line padding: the default for the beats inside
|
|
334
610
|
gameData: node.gameData,
|
|
611
|
+
extensions: ctx.extensions.get(node.id),
|
|
612
|
+
// a choice's option's extension values
|
|
335
613
|
tags: node.tags,
|
|
336
614
|
// author tags (#215), accumulated at runtime
|
|
337
|
-
prompt: node.prompt,
|
|
615
|
+
prompt: node.prompt && compileBeat(node.prompt, ctx),
|
|
338
616
|
// option-position only (spec §5)
|
|
339
617
|
secretUntilEligible: node.secretUntilEligible,
|
|
340
618
|
sticky: node.sticky,
|
|
@@ -345,12 +623,13 @@ function compileNode(node, foreign) {
|
|
|
345
623
|
id: node.id,
|
|
346
624
|
type: "snippet",
|
|
347
625
|
condition: node.condition !== void 0 ? compileExpression(node.condition, foreign) : void 0,
|
|
348
|
-
beats: node.beats,
|
|
626
|
+
beats: node.beats && node.beats.map((b) => compileBeat(b, ctx)),
|
|
349
627
|
onEnter: node.onEnter?.map((e) => compileEffect(e, foreign)),
|
|
350
628
|
onExit: node.onExit?.map((e) => compileEffect(e, foreign)),
|
|
351
629
|
padAfterDefault: node.padAfterDefault,
|
|
352
630
|
// line padding: the default for the beats inside
|
|
353
631
|
gameData: node.gameData,
|
|
632
|
+
extensions: ctx.extensions.get(node.id),
|
|
354
633
|
tags: node.tags,
|
|
355
634
|
// author tags (#215)
|
|
356
635
|
jump: node.jump,
|
|
@@ -359,22 +638,24 @@ function compileNode(node, foreign) {
|
|
|
359
638
|
fallback: node.fallback
|
|
360
639
|
};
|
|
361
640
|
}
|
|
362
|
-
function compileBlock(block,
|
|
641
|
+
function compileBlock(block, ctx) {
|
|
363
642
|
return {
|
|
364
643
|
id: block.id,
|
|
365
644
|
type: "block",
|
|
366
645
|
name: block.name,
|
|
367
646
|
...block.gameId ? { gameId: block.gameId } : {},
|
|
368
647
|
// host-facing address; runtime falls back to the name slug
|
|
369
|
-
children: block.children.map((c) => compileNode(c,
|
|
648
|
+
children: block.children.map((c) => compileNode(c, ctx)),
|
|
370
649
|
padAfterDefault: block.padAfterDefault,
|
|
371
650
|
// line padding: the default for the beats inside
|
|
372
651
|
gameData: block.gameData,
|
|
652
|
+
extensions: ctx.extensions.get(block.id),
|
|
373
653
|
tags: block.tags
|
|
374
654
|
// author tags (#215)
|
|
375
655
|
};
|
|
376
656
|
}
|
|
377
|
-
function compileScene(scene,
|
|
657
|
+
function compileScene(scene, ctx) {
|
|
658
|
+
const { foreign } = ctx;
|
|
378
659
|
return {
|
|
379
660
|
id: scene.id,
|
|
380
661
|
type: "scene",
|
|
@@ -384,11 +665,12 @@ function compileScene(scene, foreign) {
|
|
|
384
665
|
padAfterDefault: scene.padAfterDefault,
|
|
385
666
|
// line padding: the default for the beats inside
|
|
386
667
|
gameData: scene.gameData,
|
|
668
|
+
extensions: ctx.extensions.get(scene.id),
|
|
387
669
|
tags: scene.tags,
|
|
388
670
|
// author tags (#215)
|
|
389
671
|
onEntry: scene.onEntry?.map((e) => compileEffect(e, foreign)),
|
|
390
672
|
sceneProps: scene.sceneProps,
|
|
391
|
-
blocks: scene.blocks.map((b) => compileBlock(b,
|
|
673
|
+
blocks: scene.blocks.map((b) => compileBlock(b, ctx))
|
|
392
674
|
};
|
|
393
675
|
}
|
|
394
676
|
function externalScopesIn(scenes, external) {
|
|
@@ -422,8 +704,12 @@ function exportBundle(input) {
|
|
|
422
704
|
version: foreignScopes?.version ?? 1,
|
|
423
705
|
scopes: [...foreignScopes?.scopes ?? [], ...gameExternal.map((s) => ({ token: s.token }))]
|
|
424
706
|
});
|
|
707
|
+
const ext = input.extensions;
|
|
708
|
+
const compiledExt = project.extensions !== void 0 || ext !== void 0 && (ext.manifests.length > 0 || ext.shards.length > 0) ? extensionPass({ project, scenes, manifests: ext?.manifests ?? [], shards: ext?.shards ?? [] }).compiled : void 0;
|
|
709
|
+
const extensionSection = compiledExt !== void 0 && Object.keys(compiledExt.bundle).length > 0 ? compiledExt.bundle : void 0;
|
|
710
|
+
const ctx = { foreign: compileScopes, extensions: compiledExt?.nodes ?? /* @__PURE__ */ new Map() };
|
|
425
711
|
const scenesOut = {};
|
|
426
|
-
for (const scene of scenes) scenesOut[scene.id] = compileScene(scene,
|
|
712
|
+
for (const scene of scenes) scenesOut[scene.id] = compileScene(scene, ctx);
|
|
427
713
|
const externalScopes = externalScopesIn(scenesOut, /* @__PURE__ */ new Set([...EXTERNAL_SCOPES, ...gameExternal.map((s) => s.token)]));
|
|
428
714
|
const qualifiers = usedQualifiers(project, scenes).map((q) => ({ gameId: q.gameId, name: q.name }));
|
|
429
715
|
const usedPairs = new Set(usedSpeakerPairs(scenes).map((p) => `${p.character}\0${p.qualifier}`));
|
|
@@ -454,7 +740,9 @@ function exportBundle(input) {
|
|
|
454
740
|
properties: project.properties ?? [],
|
|
455
741
|
...foreignScopes ? { scopeRegistry: foreignScopes } : {},
|
|
456
742
|
...qualifiers.length > 0 ? { qualifiers } : {},
|
|
457
|
-
...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {}
|
|
743
|
+
...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {},
|
|
744
|
+
// Each node's values are in `scenes`; the declarations and the project's own values are here.
|
|
745
|
+
...extensionSection !== void 0 ? { extensions: extensionSection } : {}
|
|
458
746
|
}, { trailingComma: false })),
|
|
459
747
|
// Structure-only fingerprint (live bundle refresh): the same hash with the string tables left
|
|
460
748
|
// out. Same structureHash + a different hash = a text-only edit, safe to swap in place with
|
|
@@ -465,7 +753,8 @@ function exportBundle(input) {
|
|
|
465
753
|
properties: project.properties ?? [],
|
|
466
754
|
...foreignScopes ? { scopeRegistry: foreignScopes } : {},
|
|
467
755
|
...qualifiers.length > 0 ? { qualifiers } : {},
|
|
468
|
-
...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {}
|
|
756
|
+
...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {},
|
|
757
|
+
...extensionSection !== void 0 ? { extensions: extensionSection } : {}
|
|
469
758
|
}, { trailingComma: false }))
|
|
470
759
|
},
|
|
471
760
|
voiced: project.voiced ?? false,
|
|
@@ -494,6 +783,9 @@ function exportBundle(input) {
|
|
|
494
783
|
gameDataFields: project.gameDataFields && Object.fromEntries(
|
|
495
784
|
Object.entries(project.gameDataFields).map(([kind, fields]) => [kind, resolved(fields)])
|
|
496
785
|
),
|
|
786
|
+
// The extensions the project takes: each one's version, its compiled declarations by node kind (every
|
|
787
|
+
// default resolved, editor-only fields and the editor's words left out) and the project node's values.
|
|
788
|
+
...extensionSection !== void 0 ? { extensions: extensionSection } : {},
|
|
497
789
|
// Closed-caption delimiters ride along only when the project pins a non-default pair (#214); the
|
|
498
790
|
// runtime falls back to `[` / `]` when absent, so default projects keep their exact prior bundle.
|
|
499
791
|
...project.closedCaptions ? { closedCaptions: project.closedCaptions } : {},
|
|
@@ -508,6 +800,8 @@ export {
|
|
|
508
800
|
PATTER_SCOPE,
|
|
509
801
|
compileExpression,
|
|
510
802
|
exportBundle,
|
|
803
|
+
extensionNodes,
|
|
804
|
+
extensionPass,
|
|
511
805
|
externalGameScopes,
|
|
512
806
|
hostScopesToSpec4 as hostScopesToSpec,
|
|
513
807
|
projectScopes,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patterkit/compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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,10 +34,11 @@
|
|
|
34
34
|
"build": "tsup src/index.ts --format esm,cjs --dts --clean"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@patterkit/model": "0.
|
|
38
|
-
"@patterkit/core": "0.
|
|
39
|
-
"@patterkit/dialect": "0.2.
|
|
40
|
-
"@wildwinter/expr": "^0.6.0"
|
|
37
|
+
"@patterkit/model": "0.16.0",
|
|
38
|
+
"@patterkit/core": "0.9.0",
|
|
39
|
+
"@patterkit/dialect": "0.2.13",
|
|
40
|
+
"@wildwinter/expr": "^0.6.0",
|
|
41
|
+
"@wildwinter/toolkit": "^0.2.0"
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
43
44
|
"@wildwinter/scoperegistry": ">=0.9.0 <1.0.0"
|