pixifact 0.1.3

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 (168) hide show
  1. package/README.md +44 -0
  2. package/dist/authoring/ActionRegistry.d.ts +10 -0
  3. package/dist/authoring/ActionRegistry.js +26 -0
  4. package/dist/authoring/ComponentPalette.d.ts +16 -0
  5. package/dist/authoring/ComponentPalette.js +50 -0
  6. package/dist/authoring/DesignToken.d.ts +16 -0
  7. package/dist/authoring/DesignToken.js +33 -0
  8. package/dist/authoring/DiffModel.d.ts +10 -0
  9. package/dist/authoring/DiffModel.js +104 -0
  10. package/dist/authoring/InspectorModel.d.ts +27 -0
  11. package/dist/authoring/InspectorModel.js +139 -0
  12. package/dist/authoring/LogicGraph.d.ts +66 -0
  13. package/dist/authoring/LogicGraph.js +184 -0
  14. package/dist/authoring/OverrideJournal.d.ts +18 -0
  15. package/dist/authoring/OverrideJournal.js +23 -0
  16. package/dist/authoring/PreferenceMemory.d.ts +21 -0
  17. package/dist/authoring/PreferenceMemory.js +39 -0
  18. package/dist/authoring/ProjectConfig.d.ts +20 -0
  19. package/dist/authoring/ProjectConfig.js +78 -0
  20. package/dist/authoring/RuntimePreview.d.ts +11 -0
  21. package/dist/authoring/RuntimePreview.js +25 -0
  22. package/dist/authoring/SceneDocument.d.ts +74 -0
  23. package/dist/authoring/SceneDocument.js +332 -0
  24. package/dist/authoring/SceneProjectState.d.ts +20 -0
  25. package/dist/authoring/SceneProjectState.js +8 -0
  26. package/dist/authoring/SceneSelection.d.ts +11 -0
  27. package/dist/authoring/SceneSelection.js +1 -0
  28. package/dist/authoring/index.d.ts +13 -0
  29. package/dist/authoring/index.js +13 -0
  30. package/dist/commands/Command.d.ts +63 -0
  31. package/dist/commands/Command.js +1 -0
  32. package/dist/commands/CommandStack.d.ts +14 -0
  33. package/dist/commands/CommandStack.js +45 -0
  34. package/dist/commands/applyCommand.d.ts +23 -0
  35. package/dist/commands/applyCommand.js +316 -0
  36. package/dist/commands/index.d.ts +4 -0
  37. package/dist/commands/index.js +4 -0
  38. package/dist/commands/utils.d.ts +17 -0
  39. package/dist/commands/utils.js +77 -0
  40. package/dist/commands/validateCommand.d.ts +12 -0
  41. package/dist/commands/validateCommand.js +285 -0
  42. package/dist/compiler/commands/index.d.ts +109 -0
  43. package/dist/compiler/commands/index.js +492 -0
  44. package/dist/compiler/compileScenes.d.ts +11 -0
  45. package/dist/compiler/compileScenes.js +220 -0
  46. package/dist/compiler/decorators.d.ts +6 -0
  47. package/dist/compiler/decorators.js +79 -0
  48. package/dist/compiler/eventRuntime.d.ts +8 -0
  49. package/dist/compiler/eventRuntime.js +29 -0
  50. package/dist/compiler/index.d.ts +14 -0
  51. package/dist/compiler/index.js +14 -0
  52. package/dist/compiler/pixiNodeSchema.d.ts +39 -0
  53. package/dist/compiler/pixiNodeSchema.js +267 -0
  54. package/dist/compiler/sceneAssetPair.d.ts +11 -0
  55. package/dist/compiler/sceneAssetPair.js +92 -0
  56. package/dist/compiler/scenePartValidation.d.ts +6 -0
  57. package/dist/compiler/scenePartValidation.js +24 -0
  58. package/dist/compiler/sceneRuntime.d.ts +13 -0
  59. package/dist/compiler/sceneRuntime.js +22 -0
  60. package/dist/compiler/sceneValidation.d.ts +48 -0
  61. package/dist/compiler/sceneValidation.js +383 -0
  62. package/dist/compiler/scriptInterfaceExtractor.d.ts +6 -0
  63. package/dist/compiler/scriptInterfaceExtractor.js +258 -0
  64. package/dist/compiler/slotRuntime.d.ts +3 -0
  65. package/dist/compiler/slotRuntime.js +17 -0
  66. package/dist/compiler/spec.d.ts +95 -0
  67. package/dist/compiler/spec.js +1 -0
  68. package/dist/compiler/templateParser.d.ts +2 -0
  69. package/dist/compiler/templateParser.js +302 -0
  70. package/dist/compiler/templateSerializer.d.ts +2 -0
  71. package/dist/compiler/templateSerializer.js +101 -0
  72. package/dist/compiler/typescriptCompiler.d.ts +2 -0
  73. package/dist/compiler/typescriptCompiler.js +494 -0
  74. package/dist/compiler-node/index.d.ts +2 -0
  75. package/dist/compiler-node/index.js +2 -0
  76. package/dist/compiler-node/vite.d.ts +8 -0
  77. package/dist/compiler-node/vite.js +21 -0
  78. package/dist/index.d.ts +6 -0
  79. package/dist/index.js +6 -0
  80. package/dist/nodes/Input.d.ts +91 -0
  81. package/dist/nodes/Input.js +352 -0
  82. package/dist/nodes/Textarea.d.ts +10 -0
  83. package/dist/nodes/Textarea.js +38 -0
  84. package/dist/nodes/components/Button.d.ts +8 -0
  85. package/dist/nodes/components/Button.js +45 -0
  86. package/dist/nodes/components/InputField.d.ts +12 -0
  87. package/dist/nodes/components/InputField.js +53 -0
  88. package/dist/nodes/components/ProgressBar.d.ts +14 -0
  89. package/dist/nodes/components/ProgressBar.js +66 -0
  90. package/dist/nodes/components/ScrollRect.d.ts +23 -0
  91. package/dist/nodes/components/ScrollRect.js +108 -0
  92. package/dist/nodes/components/Selectable.d.ts +32 -0
  93. package/dist/nodes/components/Selectable.js +153 -0
  94. package/dist/nodes/components/index.d.ts +5 -0
  95. package/dist/nodes/components/index.js +5 -0
  96. package/dist/nodes/graphics/Graphic.d.ts +14 -0
  97. package/dist/nodes/graphics/Graphic.js +47 -0
  98. package/dist/nodes/graphics/ImageGraphic.d.ts +9 -0
  99. package/dist/nodes/graphics/ImageGraphic.js +42 -0
  100. package/dist/nodes/graphics/RoundedRectGraphic.d.ts +13 -0
  101. package/dist/nodes/graphics/RoundedRectGraphic.js +65 -0
  102. package/dist/nodes/graphics/TextGraphic.d.ts +13 -0
  103. package/dist/nodes/graphics/TextGraphic.js +70 -0
  104. package/dist/nodes/graphics/index.d.ts +4 -0
  105. package/dist/nodes/graphics/index.js +4 -0
  106. package/dist/nodes/index.d.ts +3 -0
  107. package/dist/nodes/index.js +3 -0
  108. package/dist/runtime/Application.d.ts +15 -0
  109. package/dist/runtime/Application.js +67 -0
  110. package/dist/runtime/GameObject.d.ts +100 -0
  111. package/dist/runtime/GameObject.js +297 -0
  112. package/dist/runtime/GameObjectEvent.d.ts +52 -0
  113. package/dist/runtime/GameObjectEvent.js +38 -0
  114. package/dist/runtime/Transform.d.ts +32 -0
  115. package/dist/runtime/Transform.js +157 -0
  116. package/dist/runtime/component/Component.d.ts +13 -0
  117. package/dist/runtime/component/Component.js +13 -0
  118. package/dist/runtime/component/ComponentRegistry.d.ts +49 -0
  119. package/dist/runtime/component/ComponentRegistry.js +25 -0
  120. package/dist/runtime/component/Flex.d.ts +6 -0
  121. package/dist/runtime/component/Flex.js +15 -0
  122. package/dist/runtime/component/FlexGroup.d.ts +26 -0
  123. package/dist/runtime/component/FlexGroup.js +136 -0
  124. package/dist/runtime/component/GridLayout.d.ts +47 -0
  125. package/dist/runtime/component/GridLayout.js +129 -0
  126. package/dist/runtime/component/Layout.d.ts +104 -0
  127. package/dist/runtime/component/Layout.js +248 -0
  128. package/dist/runtime/component/decorators.d.ts +4 -0
  129. package/dist/runtime/component/decorators.js +52 -0
  130. package/dist/runtime/component/index.d.ts +7 -0
  131. package/dist/runtime/component/index.js +7 -0
  132. package/dist/runtime/graphics/index.d.ts +11 -0
  133. package/dist/runtime/graphics/index.js +29 -0
  134. package/dist/runtime/group/index.d.ts +39 -0
  135. package/dist/runtime/group/index.js +102 -0
  136. package/dist/runtime/image/Image.d.ts +19 -0
  137. package/dist/runtime/image/Image.js +60 -0
  138. package/dist/runtime/image/NineSliceImage.d.ts +23 -0
  139. package/dist/runtime/image/NineSliceImage.js +64 -0
  140. package/dist/runtime/image/index.d.ts +2 -0
  141. package/dist/runtime/image/index.js +2 -0
  142. package/dist/runtime/index.d.ts +9 -0
  143. package/dist/runtime/index.js +9 -0
  144. package/dist/runtime/label/Label.d.ts +11 -0
  145. package/dist/runtime/label/Label.js +26 -0
  146. package/dist/runtime/label/LabelStyle.d.ts +3 -0
  147. package/dist/runtime/label/LabelStyle.js +3 -0
  148. package/dist/runtime/label/index.d.ts +2 -0
  149. package/dist/runtime/label/index.js +2 -0
  150. package/dist/runtime/utils/index.d.ts +1 -0
  151. package/dist/runtime/utils/index.js +1 -0
  152. package/dist/runtime/utils/onlyOnceQueueMicrotask.d.ts +1 -0
  153. package/dist/runtime/utils/onlyOnceQueueMicrotask.js +12 -0
  154. package/dist/runtime/utils/setProps.d.ts +10 -0
  155. package/dist/runtime/utils/setProps.js +17 -0
  156. package/dist/scene/dsl.d.ts +25 -0
  157. package/dist/scene/dsl.js +112 -0
  158. package/dist/scene/index.d.ts +5 -0
  159. package/dist/scene/index.js +5 -0
  160. package/dist/scene/instantiate.d.ts +8 -0
  161. package/dist/scene/instantiate.js +208 -0
  162. package/dist/scene/nodesDsl.d.ts +42 -0
  163. package/dist/scene/nodesDsl.js +169 -0
  164. package/dist/scene/spec.d.ts +100 -0
  165. package/dist/scene/spec.js +1 -0
  166. package/dist/scene/templates.d.ts +21 -0
  167. package/dist/scene/templates.js +239 -0
  168. package/package.json +90 -0
@@ -0,0 +1,258 @@
1
+ import ts from 'typescript';
2
+ export function extractSceneScriptInterface(source, fileName = 'scene-script.ts', options) {
3
+ const sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
4
+ const structClasses = collectStructClasses(sourceFile);
5
+ for (const statement of sourceFile.statements) {
6
+ if (!ts.isClassDeclaration(statement)) {
7
+ continue;
8
+ }
9
+ if (!hasSceneDecorator(statement)) {
10
+ continue;
11
+ }
12
+ const className = statement.name?.text;
13
+ if (!className) {
14
+ throw new Error('Scene script class is missing a name.');
15
+ }
16
+ const props = {};
17
+ const events = {};
18
+ const slots = {};
19
+ const parts = {};
20
+ for (const member of statement.members) {
21
+ const name = memberName(member.name);
22
+ if (!name) {
23
+ continue;
24
+ }
25
+ const prop = memberDecoratorOptions(member, 'prop');
26
+ if (prop) {
27
+ props[name] = propContract(prop, structClasses);
28
+ }
29
+ const event = memberDecoratorOptions(member, 'event');
30
+ if (event) {
31
+ events[typeof event.name === 'string' ? event.name : name] = { type: 'action' };
32
+ }
33
+ const slot = memberDecoratorOptions(member, 'slot');
34
+ if (slot) {
35
+ slots[typeof slot.name === 'string' ? slot.name : name] = {};
36
+ }
37
+ const part = memberDecoratorOptions(member, 'part');
38
+ if (part) {
39
+ parts[name] = typeof part.id === 'string' ? part.id : name;
40
+ }
41
+ }
42
+ return {
43
+ scene: options.scene,
44
+ className,
45
+ interface: {
46
+ props,
47
+ events,
48
+ slots,
49
+ },
50
+ parts,
51
+ };
52
+ }
53
+ throw new Error('No @scene decorator found.');
54
+ }
55
+ export function emitSceneScriptInterfaceDescriptor(source, fileName = 'scene-script.ts', options) {
56
+ return `${JSON.stringify(extractSceneScriptInterface(source, fileName, options), null, 2)}\n`;
57
+ }
58
+ function hasSceneDecorator(node) {
59
+ const decorator = decorators(node).find((item) => decoratorName(item) === 'scene');
60
+ if (!decorator) {
61
+ return false;
62
+ }
63
+ const args = decoratorArguments(decorator);
64
+ if (args.length !== 0) {
65
+ throw new Error('@scene does not accept arguments. Pair scripts by colocating a same-basename .ts file next to the .scene file.');
66
+ }
67
+ return true;
68
+ }
69
+ function memberDecoratorOptions(member, name) {
70
+ const decorator = decorators(member).find((item) => decoratorName(item) === name);
71
+ if (!decorator) {
72
+ return undefined;
73
+ }
74
+ const args = decoratorArguments(decorator);
75
+ if (args.length === 0) {
76
+ return {};
77
+ }
78
+ if (args.length !== 1) {
79
+ throw new Error(`@${name} accepts at most one argument.`);
80
+ }
81
+ return objectLiteralValue(args[0], `@${name} argument`);
82
+ }
83
+ function decorators(node) {
84
+ return ts.canHaveDecorators(node) ? ts.getDecorators(node) ?? [] : [];
85
+ }
86
+ function decoratorName(decorator) {
87
+ const expression = decorator.expression;
88
+ if (ts.isIdentifier(expression)) {
89
+ return expression.text;
90
+ }
91
+ if (ts.isCallExpression(expression) && ts.isIdentifier(expression.expression)) {
92
+ return expression.expression.text;
93
+ }
94
+ return undefined;
95
+ }
96
+ function decoratorArguments(decorator) {
97
+ const expression = decorator.expression;
98
+ return ts.isCallExpression(expression) ? [...expression.arguments] : [];
99
+ }
100
+ function memberName(name) {
101
+ if (!name) {
102
+ return undefined;
103
+ }
104
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
105
+ return name.text;
106
+ }
107
+ return undefined;
108
+ }
109
+ function collectStructClasses(sourceFile) {
110
+ const classes = new Map();
111
+ for (const statement of sourceFile.statements) {
112
+ if (!ts.isClassDeclaration(statement) || !statement.name) {
113
+ continue;
114
+ }
115
+ classes.set(statement.name.text, {
116
+ exported: statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) ?? false,
117
+ node: statement,
118
+ });
119
+ }
120
+ return classes;
121
+ }
122
+ function structFields(node) {
123
+ const fields = {};
124
+ for (const member of node.members) {
125
+ if (!ts.isPropertyDeclaration(member)) {
126
+ continue;
127
+ }
128
+ const name = memberName(member.name);
129
+ if (!name) {
130
+ throw new Error(`Struct prop type ${node.name?.text ?? 'anonymous'} only supports literal field names.`);
131
+ }
132
+ if (!member.initializer) {
133
+ throw new Error(`Struct prop type ${node.name?.text ?? 'anonymous'} field ${name} requires a primitive initializer.`);
134
+ }
135
+ const value = literalValue(member.initializer, `struct ${node.name?.text ?? 'anonymous'}.${name}`);
136
+ if (!isSceneTemplateScalarValue(value)) {
137
+ throw new Error(`Struct prop type ${node.name?.text ?? 'anonymous'} field ${name} requires a primitive initializer.`);
138
+ }
139
+ fields[name] = {
140
+ type: sceneTemplateScalarType(value),
141
+ default: value,
142
+ };
143
+ }
144
+ return fields;
145
+ }
146
+ function propContract(prop, structClasses) {
147
+ const type = prop.type;
148
+ const defaultValue = prop.default;
149
+ if (type === 'string' || type === 'number' || type === 'boolean') {
150
+ throw new Error('@prop type must be String, Number, Boolean, or a struct class.');
151
+ }
152
+ if (!isIdentifierValue(type)) {
153
+ throw new Error('@prop type must be String, Number, Boolean, or a struct class.');
154
+ }
155
+ const primitiveType = primitiveConstructorType(type.text);
156
+ if (primitiveType) {
157
+ if (defaultValue !== undefined && (!isSceneTemplateScalarValue(defaultValue) || sceneTemplateScalarType(defaultValue) !== primitiveType)) {
158
+ throw new Error(`@prop default for ${type.text} must be a ${primitiveType} literal.`);
159
+ }
160
+ return {
161
+ type: primitiveType,
162
+ ...(defaultValue !== undefined ? { default: defaultValue } : {}),
163
+ };
164
+ }
165
+ const structClass = structClasses.get(type.text);
166
+ if (!structClass) {
167
+ throw new Error(`Struct prop type ${type.text} was not found.`);
168
+ }
169
+ if (!structClass.exported) {
170
+ throw new Error(`Struct prop type ${type.text} must be exported.`);
171
+ }
172
+ if (defaultValue !== undefined) {
173
+ throw new Error('@prop default is only supported for primitive props.');
174
+ }
175
+ const struct = structContractInfo(structClass.node);
176
+ if (struct.hasRequiredConstructorParameters) {
177
+ throw new Error(`Struct prop type ${type.text} must be constructable with no required parameters.`);
178
+ }
179
+ return {
180
+ type: 'struct',
181
+ struct: type.text,
182
+ fields: struct.fields,
183
+ };
184
+ }
185
+ function structContractInfo(node) {
186
+ return {
187
+ fields: structFields(node),
188
+ hasRequiredConstructorParameters: node.members.some((member) => ts.isConstructorDeclaration(member) && member.parameters.some((parameter) => !parameter.questionToken && !parameter.initializer)),
189
+ };
190
+ }
191
+ function primitiveConstructorType(name) {
192
+ if (name === 'String') {
193
+ return 'string';
194
+ }
195
+ if (name === 'Number') {
196
+ return 'number';
197
+ }
198
+ if (name === 'Boolean') {
199
+ return 'boolean';
200
+ }
201
+ return undefined;
202
+ }
203
+ function isIdentifierValue(value) {
204
+ return ts.isIdentifier(value);
205
+ }
206
+ function isSceneTemplateScalarValue(value) {
207
+ return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
208
+ }
209
+ function sceneTemplateScalarType(value) {
210
+ if (typeof value === 'string') {
211
+ return 'string';
212
+ }
213
+ if (typeof value === 'number') {
214
+ return 'number';
215
+ }
216
+ return 'boolean';
217
+ }
218
+ function objectLiteralValue(expression, label) {
219
+ if (!ts.isObjectLiteralExpression(expression)) {
220
+ throw new Error(`${label} must be an object literal.`);
221
+ }
222
+ const result = {};
223
+ for (const property of expression.properties) {
224
+ if (!ts.isPropertyAssignment(property)) {
225
+ throw new Error(`${label} only supports property assignments.`);
226
+ }
227
+ const name = memberName(property.name);
228
+ if (!name) {
229
+ throw new Error(`${label} only supports literal property names.`);
230
+ }
231
+ result[name] = decoratorValue(property.initializer, `${label}.${name}`);
232
+ }
233
+ return result;
234
+ }
235
+ function decoratorValue(expression, label) {
236
+ if (ts.isIdentifier(expression)) {
237
+ return expression;
238
+ }
239
+ return literalValue(expression, label);
240
+ }
241
+ function literalValue(expression, label) {
242
+ if (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression)) {
243
+ return expression.text;
244
+ }
245
+ if (expression.kind === ts.SyntaxKind.TrueKeyword) {
246
+ return true;
247
+ }
248
+ if (expression.kind === ts.SyntaxKind.FalseKeyword) {
249
+ return false;
250
+ }
251
+ if (ts.isNumericLiteral(expression)) {
252
+ return Number(expression.text);
253
+ }
254
+ if (ts.isPrefixUnaryExpression(expression) && expression.operator === ts.SyntaxKind.MinusToken && ts.isNumericLiteral(expression.operand)) {
255
+ return -Number(expression.operand.text);
256
+ }
257
+ throw new Error(`${label} must be a string, number, or boolean literal.`);
258
+ }
@@ -0,0 +1,3 @@
1
+ import type { Container } from 'pixi.js';
2
+ export declare function registerSlot(target: Container, name: string, host: Container): void;
3
+ export declare function mount<T extends Container>(target: Container, child: T, slot?: string): T;
@@ -0,0 +1,17 @@
1
+ const sceneSlots = new WeakMap();
2
+ export function registerSlot(target, name, host) {
3
+ let slots = sceneSlots.get(target);
4
+ if (!slots) {
5
+ slots = new Map();
6
+ sceneSlots.set(target, slots);
7
+ }
8
+ slots.set(name, host);
9
+ }
10
+ export function mount(target, child, slot = 'default') {
11
+ const host = sceneSlots.get(target)?.get(slot);
12
+ if (!host) {
13
+ throw new Error(`Unknown slot "${slot}".`);
14
+ }
15
+ host.addChild(child);
16
+ return child;
17
+ }
@@ -0,0 +1,95 @@
1
+ export type SceneTemplatePrimitiveType = 'Container' | 'Sprite' | 'Text' | 'Graphics' | 'BitmapText' | 'HTMLText' | 'Mesh' | 'NineSliceSprite' | 'TilingSprite' | 'DOMContainer';
2
+ export type SceneTemplateScalarValue = string | number | boolean;
3
+ export type SceneTemplateStructValue = Record<string, SceneTemplateScalarValue>;
4
+ export type SceneTemplateValue = SceneTemplateScalarValue | SceneTemplateStructValue;
5
+ export type SceneTemplatePrimitivePropType = 'string' | 'number' | 'boolean';
6
+ export interface SceneTemplatePrimitivePropContract {
7
+ type: SceneTemplatePrimitivePropType;
8
+ default?: SceneTemplateScalarValue;
9
+ }
10
+ export interface SceneTemplateStructFieldContract {
11
+ type: SceneTemplatePrimitivePropType;
12
+ default: SceneTemplateScalarValue;
13
+ }
14
+ export interface SceneTemplateStructPropContract {
15
+ type: 'struct';
16
+ struct: string;
17
+ fields: Record<string, SceneTemplateStructFieldContract>;
18
+ }
19
+ export type SceneTemplatePropContract = SceneTemplatePrimitivePropContract | SceneTemplateStructPropContract;
20
+ export interface SceneTemplateEventContract {
21
+ type: 'action';
22
+ }
23
+ export interface SceneTemplateSlotContract {
24
+ }
25
+ export interface SceneTemplateInterface {
26
+ props: Record<string, SceneTemplatePropContract>;
27
+ events: Record<string, SceneTemplateEventContract>;
28
+ slots: Record<string, SceneTemplateSlotContract>;
29
+ }
30
+ export interface SceneTemplate {
31
+ version: 2;
32
+ name: string;
33
+ props: Record<string, SceneTemplateValue>;
34
+ interface: SceneTemplateInterface;
35
+ children: SceneTemplateNode[];
36
+ }
37
+ export type SceneTemplateNode = PixiTemplateNode | SceneInstanceTemplateNode | SlotOutletTemplateNode;
38
+ export interface PixiTemplateNode {
39
+ kind: 'pixi';
40
+ type: SceneTemplatePrimitiveType;
41
+ id?: string;
42
+ props: Record<string, SceneTemplateValue>;
43
+ children: SceneTemplateNode[];
44
+ }
45
+ export interface SceneInstanceTemplateNode {
46
+ kind: 'sceneInstance';
47
+ type: string;
48
+ id?: string;
49
+ scene: string;
50
+ props: Record<string, SceneTemplateValue>;
51
+ events: Record<string, string>;
52
+ slots: Record<string, SceneTemplateNode[]>;
53
+ }
54
+ export interface SlotOutletTemplateNode {
55
+ kind: 'slotOutlet';
56
+ name: string;
57
+ }
58
+ export interface SceneTemplateScriptImport {
59
+ exportName: string;
60
+ localName: string;
61
+ source: string;
62
+ }
63
+ export interface CompileSceneTemplateOptions {
64
+ functionName?: string;
65
+ actionsParameter?: string;
66
+ registrationPath?: string;
67
+ scriptImport?: SceneTemplateScriptImport;
68
+ sceneImports?: SceneTemplateScriptImport[];
69
+ sceneClassAliases?: Record<string, string>;
70
+ sceneInterfaces?: Record<string, SceneTemplateInterface>;
71
+ textureImports?: Record<string, string>;
72
+ }
73
+ export interface SceneScriptInterface {
74
+ scene: string;
75
+ className: string;
76
+ interface: SceneTemplateInterface;
77
+ parts: Record<string, string>;
78
+ }
79
+ export type SceneClassDecorator = ClassDecorator;
80
+ export type SceneMemberDecorator = PropertyDecorator & MethodDecorator;
81
+ export type SceneStructConstructor = new () => object;
82
+ export type ScenePropType = StringConstructor | NumberConstructor | BooleanConstructor | SceneStructConstructor;
83
+ export interface ScenePropDecoratorOptions {
84
+ type: ScenePropType;
85
+ default?: SceneTemplateValue;
86
+ }
87
+ export interface SceneEventDecoratorOptions {
88
+ name?: string;
89
+ }
90
+ export interface SceneSlotDecoratorOptions {
91
+ name?: string;
92
+ }
93
+ export interface ScenePartDecoratorOptions {
94
+ id?: string;
95
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { SceneTemplate } from './spec.js';
2
+ export declare function parseSceneTemplate(source: string): SceneTemplate;
@@ -0,0 +1,302 @@
1
+ import { pixiSceneFieldSchema } from './pixiNodeSchema.js';
2
+ const pixiTypes = new Set([
3
+ 'Container',
4
+ 'Sprite',
5
+ 'Text',
6
+ 'Graphics',
7
+ 'BitmapText',
8
+ 'HTMLText',
9
+ 'Mesh',
10
+ 'NineSliceSprite',
11
+ 'TilingSprite',
12
+ 'DOMContainer',
13
+ ]);
14
+ export function parseSceneTemplate(source) {
15
+ const root = new TemplateXmlParser(source).parse();
16
+ if (root.name !== 'Scene') {
17
+ throw new Error(`Expected <Scene> root, received <${root.name}>.`);
18
+ }
19
+ const name = root.attributes.name;
20
+ if (!name) {
21
+ throw new Error('Scene template is missing name.');
22
+ }
23
+ if (root.attributes.class !== undefined) {
24
+ throw new Error('Scene class is inferred from the paired script @scene class; remove the class attribute.');
25
+ }
26
+ if (root.attributes.script !== undefined) {
27
+ throw new Error('Scene script binding is inferred from the colocated TypeScript file.');
28
+ }
29
+ const children = root.children.map(parseTemplateNode);
30
+ assertUniqueIds(name, children);
31
+ return {
32
+ version: 2,
33
+ name,
34
+ props: parseProps(root, ['name']),
35
+ interface: emptyInterface(),
36
+ children,
37
+ };
38
+ }
39
+ function emptyInterface() {
40
+ return {
41
+ props: {},
42
+ events: {},
43
+ slots: {},
44
+ };
45
+ }
46
+ function parseTemplateNode(element) {
47
+ if (element.name === 'slot') {
48
+ const node = {
49
+ kind: 'slotOutlet',
50
+ name: element.attributes.name || 'default',
51
+ };
52
+ return node;
53
+ }
54
+ if (pixiTypes.has(element.name)) {
55
+ const node = {
56
+ kind: 'pixi',
57
+ type: element.name,
58
+ props: parseProps(element, ['id', 'slot']),
59
+ children: element.children.map(parseTemplateNode),
60
+ ...(element.attributes.id ? { id: element.attributes.id } : {}),
61
+ };
62
+ return node;
63
+ }
64
+ if (element.attributes.scene) {
65
+ const slots = {};
66
+ for (const child of element.children) {
67
+ const slot = child.attributes.slot || 'default';
68
+ slots[slot] = slots[slot] ?? [];
69
+ slots[slot].push(parseTemplateNode(removeAttribute(child, 'slot')));
70
+ }
71
+ const node = {
72
+ kind: 'sceneInstance',
73
+ type: element.name,
74
+ scene: element.attributes.scene,
75
+ props: parseProps(element, ['id', 'scene', 'slot'], isEventAttribute),
76
+ events: parseEvents(element),
77
+ slots,
78
+ ...(element.attributes.id ? { id: element.attributes.id } : {}),
79
+ };
80
+ return node;
81
+ }
82
+ throw new Error(`Unsupported template tag <${element.name}>.`);
83
+ }
84
+ function parseProps(element, omitted, shouldOmit = () => false) {
85
+ const props = {};
86
+ for (const [name, value] of Object.entries(element.attributes)) {
87
+ if (omitted.includes(name) || shouldOmit(name)) {
88
+ continue;
89
+ }
90
+ const parsed = parseAttributeValue(name, value);
91
+ const path = name.split('.');
92
+ if (path.length === 1) {
93
+ if (props[name] && typeof props[name] === 'object') {
94
+ throw new Error(`Prop "${name}" cannot be both scalar and structured.`);
95
+ }
96
+ props[name] = parsed;
97
+ continue;
98
+ }
99
+ const [root, field, ...rest] = path;
100
+ if (!root || !field || rest.length > 0) {
101
+ throw new Error(`Unsupported structured prop path "${name}".`);
102
+ }
103
+ if (props[root] !== undefined && typeof props[root] !== 'object') {
104
+ throw new Error(`Prop "${root}" cannot be both scalar and structured.`);
105
+ }
106
+ if (typeof parsed === 'object') {
107
+ throw new Error(`Structured prop field "${name}" cannot contain an object value.`);
108
+ }
109
+ const objectValue = (props[root] ?? {});
110
+ objectValue[field] = parsed;
111
+ props[root] = objectValue;
112
+ }
113
+ return props;
114
+ }
115
+ function parseEvents(element) {
116
+ const events = {};
117
+ for (const [name, value] of Object.entries(element.attributes)) {
118
+ const event = eventName(name);
119
+ if (event) {
120
+ events[event] = value;
121
+ }
122
+ }
123
+ return events;
124
+ }
125
+ function eventName(name) {
126
+ if (name.startsWith('@')) {
127
+ return name.slice(1);
128
+ }
129
+ if (name.startsWith('on-')) {
130
+ return name.slice(3);
131
+ }
132
+ if (name.startsWith('on:')) {
133
+ return name.slice(3);
134
+ }
135
+ return undefined;
136
+ }
137
+ function isEventAttribute(name) {
138
+ return eventName(name) !== undefined;
139
+ }
140
+ function parseAttributeValue(name, value) {
141
+ if (pixiSceneFieldSchema(name)?.type === 'string') {
142
+ return value;
143
+ }
144
+ if (value === 'true')
145
+ return true;
146
+ if (value === 'false')
147
+ return false;
148
+ if (/^-?\d+(\.\d+)?$/.test(value))
149
+ return Number(value);
150
+ if (/^#[0-9a-fA-F]{6}$/.test(value))
151
+ return Number.parseInt(value.slice(1), 16);
152
+ return value;
153
+ }
154
+ function removeAttribute(element, name) {
155
+ const attributes = { ...element.attributes };
156
+ delete attributes[name];
157
+ return {
158
+ ...element,
159
+ attributes,
160
+ };
161
+ }
162
+ function assertUniqueIds(sceneName, children) {
163
+ const seen = new Set();
164
+ function visit(node) {
165
+ if (node.kind === 'slotOutlet') {
166
+ return;
167
+ }
168
+ if (node.id) {
169
+ if (seen.has(node.id)) {
170
+ throw new Error(`Scene "${sceneName}" has duplicate id "${node.id}".`);
171
+ }
172
+ seen.add(node.id);
173
+ }
174
+ if (node.kind === 'pixi') {
175
+ for (const child of node.children) {
176
+ visit(child);
177
+ }
178
+ return;
179
+ }
180
+ for (const slottedChildren of Object.values(node.slots)) {
181
+ for (const child of slottedChildren) {
182
+ visit(child);
183
+ }
184
+ }
185
+ }
186
+ for (const child of children) {
187
+ visit(child);
188
+ }
189
+ }
190
+ class TemplateXmlParser {
191
+ source;
192
+ #index = 0;
193
+ constructor(source) {
194
+ this.source = source;
195
+ }
196
+ parse() {
197
+ this.#skipWhitespace();
198
+ const element = this.#parseElement();
199
+ this.#skipWhitespace();
200
+ if (this.#index !== this.source.length) {
201
+ throw new Error(`Unexpected content after <${element.name}>.`);
202
+ }
203
+ return element;
204
+ }
205
+ #parseElement() {
206
+ this.#consume('<');
207
+ if (this.#peek('/')) {
208
+ throw new Error('Unexpected closing tag.');
209
+ }
210
+ const name = this.#readName();
211
+ const attributes = {};
212
+ while (true) {
213
+ this.#skipWhitespace();
214
+ if (this.#consumeIf('/>')) {
215
+ return { name, attributes, children: [] };
216
+ }
217
+ if (this.#consumeIf('>')) {
218
+ break;
219
+ }
220
+ const attributeName = this.#readName();
221
+ this.#skipWhitespace();
222
+ this.#consume('=');
223
+ this.#skipWhitespace();
224
+ attributes[attributeName] = this.#readQuotedValue();
225
+ }
226
+ const children = [];
227
+ while (true) {
228
+ this.#skipWhitespace();
229
+ if (this.#consumeIf(`</${name}>`)) {
230
+ return { name, attributes, children };
231
+ }
232
+ if (this.#peek('<!--')) {
233
+ this.#skipComment();
234
+ continue;
235
+ }
236
+ if (this.#peek('<')) {
237
+ children.push(this.#parseElement());
238
+ continue;
239
+ }
240
+ throw new Error(`Unexpected text inside <${name}>.`);
241
+ }
242
+ }
243
+ #readName() {
244
+ const start = this.#index;
245
+ while (this.#index < this.source.length && /[A-Za-z0-9_:@.-]/.test(this.source[this.#index])) {
246
+ this.#index += 1;
247
+ }
248
+ if (start === this.#index) {
249
+ throw new Error(`Expected name at offset ${this.#index}.`);
250
+ }
251
+ return this.source.slice(start, this.#index);
252
+ }
253
+ #readQuotedValue() {
254
+ const quote = this.source[this.#index];
255
+ if (quote !== '"' && quote !== "'") {
256
+ throw new Error(`Expected quoted value at offset ${this.#index}.`);
257
+ }
258
+ this.#index += 1;
259
+ const start = this.#index;
260
+ while (this.#index < this.source.length && this.source[this.#index] !== quote) {
261
+ this.#index += 1;
262
+ }
263
+ const value = this.source.slice(start, this.#index);
264
+ this.#consume(quote);
265
+ return decodeEntities(value);
266
+ }
267
+ #skipWhitespace() {
268
+ while (this.#index < this.source.length && /\s/.test(this.source[this.#index])) {
269
+ this.#index += 1;
270
+ }
271
+ }
272
+ #skipComment() {
273
+ const end = this.source.indexOf('-->', this.#index + 4);
274
+ if (end === -1) {
275
+ throw new Error('Unterminated comment.');
276
+ }
277
+ this.#index = end + 3;
278
+ }
279
+ #consume(value) {
280
+ if (!this.#consumeIf(value)) {
281
+ throw new Error(`Expected "${value}" at offset ${this.#index}.`);
282
+ }
283
+ }
284
+ #consumeIf(value) {
285
+ if (!this.source.startsWith(value, this.#index)) {
286
+ return false;
287
+ }
288
+ this.#index += value.length;
289
+ return true;
290
+ }
291
+ #peek(value) {
292
+ return this.source.startsWith(value, this.#index);
293
+ }
294
+ }
295
+ function decodeEntities(value) {
296
+ return value
297
+ .replaceAll('&quot;', '"')
298
+ .replaceAll('&apos;', "'")
299
+ .replaceAll('&lt;', '<')
300
+ .replaceAll('&gt;', '>')
301
+ .replaceAll('&amp;', '&');
302
+ }
@@ -0,0 +1,2 @@
1
+ import type { SceneTemplate } from './spec.js';
2
+ export declare function serializeSceneTemplate(template: SceneTemplate): string;