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.
- package/README.md +44 -0
- package/dist/authoring/ActionRegistry.d.ts +10 -0
- package/dist/authoring/ActionRegistry.js +26 -0
- package/dist/authoring/ComponentPalette.d.ts +16 -0
- package/dist/authoring/ComponentPalette.js +50 -0
- package/dist/authoring/DesignToken.d.ts +16 -0
- package/dist/authoring/DesignToken.js +33 -0
- package/dist/authoring/DiffModel.d.ts +10 -0
- package/dist/authoring/DiffModel.js +104 -0
- package/dist/authoring/InspectorModel.d.ts +27 -0
- package/dist/authoring/InspectorModel.js +139 -0
- package/dist/authoring/LogicGraph.d.ts +66 -0
- package/dist/authoring/LogicGraph.js +184 -0
- package/dist/authoring/OverrideJournal.d.ts +18 -0
- package/dist/authoring/OverrideJournal.js +23 -0
- package/dist/authoring/PreferenceMemory.d.ts +21 -0
- package/dist/authoring/PreferenceMemory.js +39 -0
- package/dist/authoring/ProjectConfig.d.ts +20 -0
- package/dist/authoring/ProjectConfig.js +78 -0
- package/dist/authoring/RuntimePreview.d.ts +11 -0
- package/dist/authoring/RuntimePreview.js +25 -0
- package/dist/authoring/SceneDocument.d.ts +74 -0
- package/dist/authoring/SceneDocument.js +332 -0
- package/dist/authoring/SceneProjectState.d.ts +20 -0
- package/dist/authoring/SceneProjectState.js +8 -0
- package/dist/authoring/SceneSelection.d.ts +11 -0
- package/dist/authoring/SceneSelection.js +1 -0
- package/dist/authoring/index.d.ts +13 -0
- package/dist/authoring/index.js +13 -0
- package/dist/commands/Command.d.ts +63 -0
- package/dist/commands/Command.js +1 -0
- package/dist/commands/CommandStack.d.ts +14 -0
- package/dist/commands/CommandStack.js +45 -0
- package/dist/commands/applyCommand.d.ts +23 -0
- package/dist/commands/applyCommand.js +316 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.js +4 -0
- package/dist/commands/utils.d.ts +17 -0
- package/dist/commands/utils.js +77 -0
- package/dist/commands/validateCommand.d.ts +12 -0
- package/dist/commands/validateCommand.js +285 -0
- package/dist/compiler/commands/index.d.ts +109 -0
- package/dist/compiler/commands/index.js +492 -0
- package/dist/compiler/compileScenes.d.ts +11 -0
- package/dist/compiler/compileScenes.js +220 -0
- package/dist/compiler/decorators.d.ts +6 -0
- package/dist/compiler/decorators.js +79 -0
- package/dist/compiler/eventRuntime.d.ts +8 -0
- package/dist/compiler/eventRuntime.js +29 -0
- package/dist/compiler/index.d.ts +14 -0
- package/dist/compiler/index.js +14 -0
- package/dist/compiler/pixiNodeSchema.d.ts +39 -0
- package/dist/compiler/pixiNodeSchema.js +267 -0
- package/dist/compiler/sceneAssetPair.d.ts +11 -0
- package/dist/compiler/sceneAssetPair.js +92 -0
- package/dist/compiler/scenePartValidation.d.ts +6 -0
- package/dist/compiler/scenePartValidation.js +24 -0
- package/dist/compiler/sceneRuntime.d.ts +13 -0
- package/dist/compiler/sceneRuntime.js +22 -0
- package/dist/compiler/sceneValidation.d.ts +48 -0
- package/dist/compiler/sceneValidation.js +383 -0
- package/dist/compiler/scriptInterfaceExtractor.d.ts +6 -0
- package/dist/compiler/scriptInterfaceExtractor.js +258 -0
- package/dist/compiler/slotRuntime.d.ts +3 -0
- package/dist/compiler/slotRuntime.js +17 -0
- package/dist/compiler/spec.d.ts +95 -0
- package/dist/compiler/spec.js +1 -0
- package/dist/compiler/templateParser.d.ts +2 -0
- package/dist/compiler/templateParser.js +302 -0
- package/dist/compiler/templateSerializer.d.ts +2 -0
- package/dist/compiler/templateSerializer.js +101 -0
- package/dist/compiler/typescriptCompiler.d.ts +2 -0
- package/dist/compiler/typescriptCompiler.js +494 -0
- package/dist/compiler-node/index.d.ts +2 -0
- package/dist/compiler-node/index.js +2 -0
- package/dist/compiler-node/vite.d.ts +8 -0
- package/dist/compiler-node/vite.js +21 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/nodes/Input.d.ts +91 -0
- package/dist/nodes/Input.js +352 -0
- package/dist/nodes/Textarea.d.ts +10 -0
- package/dist/nodes/Textarea.js +38 -0
- package/dist/nodes/components/Button.d.ts +8 -0
- package/dist/nodes/components/Button.js +45 -0
- package/dist/nodes/components/InputField.d.ts +12 -0
- package/dist/nodes/components/InputField.js +53 -0
- package/dist/nodes/components/ProgressBar.d.ts +14 -0
- package/dist/nodes/components/ProgressBar.js +66 -0
- package/dist/nodes/components/ScrollRect.d.ts +23 -0
- package/dist/nodes/components/ScrollRect.js +108 -0
- package/dist/nodes/components/Selectable.d.ts +32 -0
- package/dist/nodes/components/Selectable.js +153 -0
- package/dist/nodes/components/index.d.ts +5 -0
- package/dist/nodes/components/index.js +5 -0
- package/dist/nodes/graphics/Graphic.d.ts +14 -0
- package/dist/nodes/graphics/Graphic.js +47 -0
- package/dist/nodes/graphics/ImageGraphic.d.ts +9 -0
- package/dist/nodes/graphics/ImageGraphic.js +42 -0
- package/dist/nodes/graphics/RoundedRectGraphic.d.ts +13 -0
- package/dist/nodes/graphics/RoundedRectGraphic.js +65 -0
- package/dist/nodes/graphics/TextGraphic.d.ts +13 -0
- package/dist/nodes/graphics/TextGraphic.js +70 -0
- package/dist/nodes/graphics/index.d.ts +4 -0
- package/dist/nodes/graphics/index.js +4 -0
- package/dist/nodes/index.d.ts +3 -0
- package/dist/nodes/index.js +3 -0
- package/dist/runtime/Application.d.ts +15 -0
- package/dist/runtime/Application.js +67 -0
- package/dist/runtime/GameObject.d.ts +100 -0
- package/dist/runtime/GameObject.js +297 -0
- package/dist/runtime/GameObjectEvent.d.ts +52 -0
- package/dist/runtime/GameObjectEvent.js +38 -0
- package/dist/runtime/Transform.d.ts +32 -0
- package/dist/runtime/Transform.js +157 -0
- package/dist/runtime/component/Component.d.ts +13 -0
- package/dist/runtime/component/Component.js +13 -0
- package/dist/runtime/component/ComponentRegistry.d.ts +49 -0
- package/dist/runtime/component/ComponentRegistry.js +25 -0
- package/dist/runtime/component/Flex.d.ts +6 -0
- package/dist/runtime/component/Flex.js +15 -0
- package/dist/runtime/component/FlexGroup.d.ts +26 -0
- package/dist/runtime/component/FlexGroup.js +136 -0
- package/dist/runtime/component/GridLayout.d.ts +47 -0
- package/dist/runtime/component/GridLayout.js +129 -0
- package/dist/runtime/component/Layout.d.ts +104 -0
- package/dist/runtime/component/Layout.js +248 -0
- package/dist/runtime/component/decorators.d.ts +4 -0
- package/dist/runtime/component/decorators.js +52 -0
- package/dist/runtime/component/index.d.ts +7 -0
- package/dist/runtime/component/index.js +7 -0
- package/dist/runtime/graphics/index.d.ts +11 -0
- package/dist/runtime/graphics/index.js +29 -0
- package/dist/runtime/group/index.d.ts +39 -0
- package/dist/runtime/group/index.js +102 -0
- package/dist/runtime/image/Image.d.ts +19 -0
- package/dist/runtime/image/Image.js +60 -0
- package/dist/runtime/image/NineSliceImage.d.ts +23 -0
- package/dist/runtime/image/NineSliceImage.js +64 -0
- package/dist/runtime/image/index.d.ts +2 -0
- package/dist/runtime/image/index.js +2 -0
- package/dist/runtime/index.d.ts +9 -0
- package/dist/runtime/index.js +9 -0
- package/dist/runtime/label/Label.d.ts +11 -0
- package/dist/runtime/label/Label.js +26 -0
- package/dist/runtime/label/LabelStyle.d.ts +3 -0
- package/dist/runtime/label/LabelStyle.js +3 -0
- package/dist/runtime/label/index.d.ts +2 -0
- package/dist/runtime/label/index.js +2 -0
- package/dist/runtime/utils/index.d.ts +1 -0
- package/dist/runtime/utils/index.js +1 -0
- package/dist/runtime/utils/onlyOnceQueueMicrotask.d.ts +1 -0
- package/dist/runtime/utils/onlyOnceQueueMicrotask.js +12 -0
- package/dist/runtime/utils/setProps.d.ts +10 -0
- package/dist/runtime/utils/setProps.js +17 -0
- package/dist/scene/dsl.d.ts +25 -0
- package/dist/scene/dsl.js +112 -0
- package/dist/scene/index.d.ts +5 -0
- package/dist/scene/index.js +5 -0
- package/dist/scene/instantiate.d.ts +8 -0
- package/dist/scene/instantiate.js +208 -0
- package/dist/scene/nodesDsl.d.ts +42 -0
- package/dist/scene/nodesDsl.js +169 -0
- package/dist/scene/spec.d.ts +100 -0
- package/dist/scene/spec.js +1 -0
- package/dist/scene/templates.d.ts +21 -0
- package/dist/scene/templates.js +239 -0
- package/package.json +90 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const colorPropNames = new Set([
|
|
2
|
+
'backgroundColor',
|
|
3
|
+
'borderColor',
|
|
4
|
+
'disabledColor',
|
|
5
|
+
'fill',
|
|
6
|
+
'highlightedColor',
|
|
7
|
+
'normalColor',
|
|
8
|
+
'pressedColor',
|
|
9
|
+
'strokeColor',
|
|
10
|
+
'textColor',
|
|
11
|
+
'tint',
|
|
12
|
+
]);
|
|
13
|
+
export function serializeSceneTemplate(template) {
|
|
14
|
+
const rootAttributes = [
|
|
15
|
+
['name', template.name],
|
|
16
|
+
...Object.entries(template.props),
|
|
17
|
+
];
|
|
18
|
+
const lines = [`<Scene${serializeAttributes(rootAttributes)}>`];
|
|
19
|
+
for (const child of template.children) {
|
|
20
|
+
lines.push(...serializeTemplateNode(child, 1));
|
|
21
|
+
}
|
|
22
|
+
lines.push('</Scene>');
|
|
23
|
+
return `${lines.join('\n')}\n`;
|
|
24
|
+
}
|
|
25
|
+
function serializeTemplateNode(node, depth, extraAttributes = []) {
|
|
26
|
+
if (node.kind === 'slotOutlet') {
|
|
27
|
+
const attributes = [
|
|
28
|
+
...(node.name === 'default' ? [] : [['name', node.name]]),
|
|
29
|
+
...extraAttributes,
|
|
30
|
+
];
|
|
31
|
+
return [`${indent(depth)}<slot${serializeAttributes(attributes)} />`];
|
|
32
|
+
}
|
|
33
|
+
if (node.kind === 'pixi') {
|
|
34
|
+
const attributes = [
|
|
35
|
+
...(node.id ? [['id', node.id]] : []),
|
|
36
|
+
...extraAttributes,
|
|
37
|
+
...Object.entries(node.props),
|
|
38
|
+
];
|
|
39
|
+
return serializeElement(node.type, attributes, node.children, depth);
|
|
40
|
+
}
|
|
41
|
+
const attributes = [
|
|
42
|
+
...(node.id ? [['id', node.id]] : []),
|
|
43
|
+
['scene', node.scene],
|
|
44
|
+
...extraAttributes,
|
|
45
|
+
...Object.entries(node.props),
|
|
46
|
+
...Object.entries(node.events).map(([name, value]) => [`@${name}`, value]),
|
|
47
|
+
];
|
|
48
|
+
return serializeElement(node.type, attributes, serializeSceneInstanceChildren(node), depth);
|
|
49
|
+
}
|
|
50
|
+
function serializeSceneInstanceChildren(node) {
|
|
51
|
+
return Object.entries(node.slots).flatMap(([slot, children]) => children.map((child) => ({
|
|
52
|
+
node: child,
|
|
53
|
+
slot,
|
|
54
|
+
})));
|
|
55
|
+
}
|
|
56
|
+
function serializeElement(tag, attributes, children, depth) {
|
|
57
|
+
if (children.length === 0) {
|
|
58
|
+
return [`${indent(depth)}<${tag}${serializeAttributes(attributes)} />`];
|
|
59
|
+
}
|
|
60
|
+
const lines = [`${indent(depth)}<${tag}${serializeAttributes(attributes)}>`];
|
|
61
|
+
for (const child of children) {
|
|
62
|
+
if ('slot' in child) {
|
|
63
|
+
const slotAttributes = child.slot === 'default' ? [] : [['slot', child.slot]];
|
|
64
|
+
lines.push(...serializeTemplateNode(child.node, depth + 1, slotAttributes));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
lines.push(...serializeTemplateNode(child, depth + 1));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
lines.push(`${indent(depth)}</${tag}>`);
|
|
71
|
+
return lines;
|
|
72
|
+
}
|
|
73
|
+
function serializeAttributes(attributes) {
|
|
74
|
+
return flattenAttributes(attributes)
|
|
75
|
+
.map(([name, value]) => ` ${name}="${escapeAttribute(formatAttributeValue(name, value))}"`)
|
|
76
|
+
.join('');
|
|
77
|
+
}
|
|
78
|
+
function flattenAttributes(attributes) {
|
|
79
|
+
return attributes.flatMap(([name, value]) => {
|
|
80
|
+
if (!value || typeof value !== 'object') {
|
|
81
|
+
return [[name, value]];
|
|
82
|
+
}
|
|
83
|
+
return Object.entries(value).map(([field, fieldValue]) => [`${name}.${field}`, fieldValue]);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function formatAttributeValue(name, value) {
|
|
87
|
+
if (typeof value === 'number' && colorPropNames.has(name)) {
|
|
88
|
+
return `#${value.toString(16).padStart(6, '0').slice(-6)}`;
|
|
89
|
+
}
|
|
90
|
+
return String(value);
|
|
91
|
+
}
|
|
92
|
+
function escapeAttribute(value) {
|
|
93
|
+
return value
|
|
94
|
+
.replaceAll('&', '&')
|
|
95
|
+
.replaceAll('"', '"')
|
|
96
|
+
.replaceAll('<', '<')
|
|
97
|
+
.replaceAll('>', '>');
|
|
98
|
+
}
|
|
99
|
+
function indent(depth) {
|
|
100
|
+
return ' '.repeat(depth);
|
|
101
|
+
}
|
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import { pixiSceneDisplayProps, pixiSceneGraphicsProps, pixiSceneSpriteLikeProps, pixiSceneTextStyleProps, pixiSceneTransformProps, } from './pixiNodeSchema.js';
|
|
2
|
+
const transformProps = new Set(pixiSceneTransformProps);
|
|
3
|
+
const pixiProps = new Set(pixiSceneDisplayProps);
|
|
4
|
+
const spriteProps = new Set(pixiSceneSpriteLikeProps);
|
|
5
|
+
const graphicsProps = new Set(pixiSceneGraphicsProps);
|
|
6
|
+
const textStyleProps = new Set(pixiSceneTextStyleProps);
|
|
7
|
+
export function compileSceneTemplateToTs(template, options = {}) {
|
|
8
|
+
const context = new CompileContext(template, options);
|
|
9
|
+
return context.compile();
|
|
10
|
+
}
|
|
11
|
+
class CompileContext {
|
|
12
|
+
template;
|
|
13
|
+
options;
|
|
14
|
+
#imports = new Set();
|
|
15
|
+
#pixiImports = new Set();
|
|
16
|
+
#runtimeImports = new Set();
|
|
17
|
+
#lines = [];
|
|
18
|
+
#parts = [];
|
|
19
|
+
#textures = new Map();
|
|
20
|
+
#nextId = 0;
|
|
21
|
+
#nextTextureId = 0;
|
|
22
|
+
constructor(template, options) {
|
|
23
|
+
this.template = template;
|
|
24
|
+
this.options = options;
|
|
25
|
+
}
|
|
26
|
+
compile() {
|
|
27
|
+
for (const child of this.template.children) {
|
|
28
|
+
this.#collectImports(child);
|
|
29
|
+
this.#collectParts(child);
|
|
30
|
+
this.#collectTextures(child);
|
|
31
|
+
}
|
|
32
|
+
const functionName = this.options.functionName || `mount${this.template.name}Scene`;
|
|
33
|
+
const actionsParameter = this.options.actionsParameter || 'actions';
|
|
34
|
+
const partsType = this.#formatPartsType();
|
|
35
|
+
const textureLoads = this.#formatTextureLoads();
|
|
36
|
+
this.#lines.push(`export function ${functionName}(root: Container${this.#hasEvents() ? `, ${actionsParameter}: Record<string, () => void> = {}` : ''}) {`);
|
|
37
|
+
this.#lines.push(' const __pixifactSlots: Record<string, Container> = {};');
|
|
38
|
+
this.#applyPixiProps('root', this.template.props);
|
|
39
|
+
for (const child of this.template.children) {
|
|
40
|
+
this.#compileNode(child, 'root', actionsParameter);
|
|
41
|
+
}
|
|
42
|
+
this.#lines.push(' return {');
|
|
43
|
+
this.#lines.push(' root,');
|
|
44
|
+
this.#lines.push(` parts: { ${this.#parts.map((part) => part.id).join(', ')} },`);
|
|
45
|
+
this.#lines.push(' slots: __pixifactSlots,');
|
|
46
|
+
this.#lines.push(' };');
|
|
47
|
+
this.#lines.push('}');
|
|
48
|
+
if (this.options.registrationPath) {
|
|
49
|
+
this.#runtimeImports.add('registerScene');
|
|
50
|
+
if (this.options.scriptImport) {
|
|
51
|
+
this.#runtimeImports.add('registerSceneClass');
|
|
52
|
+
}
|
|
53
|
+
this.#lines.push('');
|
|
54
|
+
this.#lines.push(`registerScene(${JSON.stringify(this.options.registrationPath)}, {`);
|
|
55
|
+
this.#lines.push(` mount: ${functionName},`);
|
|
56
|
+
this.#lines.push('});');
|
|
57
|
+
if (this.options.scriptImport) {
|
|
58
|
+
this.#lines.push(`registerSceneClass(${this.options.scriptImport.localName}, ${JSON.stringify(this.options.registrationPath)});`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const imports = this.#formatImports();
|
|
62
|
+
return [
|
|
63
|
+
imports,
|
|
64
|
+
textureLoads,
|
|
65
|
+
partsType,
|
|
66
|
+
this.#lines.join('\n'),
|
|
67
|
+
].filter(Boolean).join('\n\n') + '\n';
|
|
68
|
+
}
|
|
69
|
+
#formatImports() {
|
|
70
|
+
const imports = [...new Set([
|
|
71
|
+
'Container',
|
|
72
|
+
...[...this.#imports].filter((item) => item !== 'Container').sort(),
|
|
73
|
+
...[...this.#pixiImports].sort(),
|
|
74
|
+
])];
|
|
75
|
+
const lines = [`import { ${imports.join(', ')} } from 'pixi.js';`];
|
|
76
|
+
for (const [texture, variable] of this.#textureImportEntries()) {
|
|
77
|
+
lines.push(`import ${variable} from ${JSON.stringify(this.options.textureImports?.[texture])};`);
|
|
78
|
+
}
|
|
79
|
+
for (const sceneImport of this.options.sceneImports ?? []) {
|
|
80
|
+
const classImport = sceneImport.exportName === sceneImport.localName
|
|
81
|
+
? sceneImport.exportName
|
|
82
|
+
: `${sceneImport.exportName} as ${sceneImport.localName}`;
|
|
83
|
+
const imports = [classImport, ...this.#sceneStructImports(sceneImport)];
|
|
84
|
+
lines.push(`import { ${imports.join(', ')} } from ${JSON.stringify(sceneImport.source)};`);
|
|
85
|
+
}
|
|
86
|
+
if (this.options.scriptImport) {
|
|
87
|
+
lines.push(`import { ${this.options.scriptImport.exportName} as ${this.options.scriptImport.localName} } from ${JSON.stringify(this.options.scriptImport.source)};`);
|
|
88
|
+
}
|
|
89
|
+
if (this.#runtimeImports.size > 0) {
|
|
90
|
+
lines.push(`import { ${[...this.#runtimeImports].sort().join(', ')} } from 'pixifact/compiler';`);
|
|
91
|
+
}
|
|
92
|
+
return lines.join('\n');
|
|
93
|
+
}
|
|
94
|
+
#formatPartsType() {
|
|
95
|
+
const lines = [`export type ${this.template.name}Parts = {`, ' root: Container;'];
|
|
96
|
+
lines.push(' parts: {');
|
|
97
|
+
for (const part of this.#parts) {
|
|
98
|
+
lines.push(` ${part.id}: ${part.type};`);
|
|
99
|
+
}
|
|
100
|
+
lines.push(' };');
|
|
101
|
+
lines.push(' slots: Record<string, Container>;');
|
|
102
|
+
lines.push('};');
|
|
103
|
+
return lines.join('\n');
|
|
104
|
+
}
|
|
105
|
+
#collectImports(node) {
|
|
106
|
+
if (node.kind === 'pixi') {
|
|
107
|
+
this.#imports.add(node.type);
|
|
108
|
+
for (const child of node.children) {
|
|
109
|
+
this.#collectImports(child);
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (node.kind === 'sceneInstance') {
|
|
114
|
+
for (const children of Object.values(node.slots)) {
|
|
115
|
+
for (const child of children) {
|
|
116
|
+
this.#collectImports(child);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
#collectParts(node) {
|
|
122
|
+
if (node.kind === 'pixi') {
|
|
123
|
+
if (node.id) {
|
|
124
|
+
this.#parts.push({ id: node.id, type: node.type });
|
|
125
|
+
}
|
|
126
|
+
for (const child of node.children) {
|
|
127
|
+
this.#collectParts(child);
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (node.kind === 'sceneInstance') {
|
|
132
|
+
if (node.id) {
|
|
133
|
+
this.#parts.push({ id: node.id, type: this.#sceneConstructorName(node) });
|
|
134
|
+
}
|
|
135
|
+
for (const children of Object.values(node.slots)) {
|
|
136
|
+
for (const child of children) {
|
|
137
|
+
this.#collectParts(child);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
#collectTextures(node) {
|
|
143
|
+
if (node.kind === 'slotOutlet') {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (node.kind === 'pixi') {
|
|
147
|
+
if (typeof node.props.texture === 'string') {
|
|
148
|
+
this.#textureVariable(node.props.texture);
|
|
149
|
+
}
|
|
150
|
+
for (const child of node.children) {
|
|
151
|
+
this.#collectTextures(child);
|
|
152
|
+
}
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
for (const children of Object.values(node.slots)) {
|
|
156
|
+
for (const child of children) {
|
|
157
|
+
this.#collectTextures(child);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
#formatTextureLoads() {
|
|
162
|
+
if (this.#textures.size === 0) {
|
|
163
|
+
return '';
|
|
164
|
+
}
|
|
165
|
+
this.#pixiImports.add('Assets');
|
|
166
|
+
return [...this.#textures.entries()]
|
|
167
|
+
.map(([texture, variable]) => `const ${variable} = await Assets.load(${this.#textureLoadSource(texture)});`)
|
|
168
|
+
.join('\n');
|
|
169
|
+
}
|
|
170
|
+
#compileNode(node, parent, actionsParameter) {
|
|
171
|
+
if (node.kind === 'slotOutlet') {
|
|
172
|
+
this.#runtimeImports.add('registerSlot');
|
|
173
|
+
this.#lines.push(` __pixifactSlots[${JSON.stringify(node.name)}] = ${parent};`);
|
|
174
|
+
this.#lines.push(` registerSlot(root, ${JSON.stringify(node.name)}, ${parent});`);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (node.kind === 'sceneInstance') {
|
|
178
|
+
this.#compileSceneInstance(node, parent, actionsParameter);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
this.#compilePixiNode(node, parent, actionsParameter);
|
|
182
|
+
}
|
|
183
|
+
#compilePixiNode(node, parent, actionsParameter) {
|
|
184
|
+
const variable = node.id || this.#anonymousName(node.type);
|
|
185
|
+
this.#lines.push(` const ${variable} = ${this.#constructPixiNode(node)};`);
|
|
186
|
+
this.#applyNodeId(variable, node.id);
|
|
187
|
+
this.#applyPixiProps(variable, node.props);
|
|
188
|
+
this.#applyParentSorting(parent, node.props);
|
|
189
|
+
this.#lines.push(` ${parent}.addChild(${variable});`);
|
|
190
|
+
for (const child of node.children) {
|
|
191
|
+
this.#compileNode(child, variable, actionsParameter);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
#compileSceneInstance(node, parent, actionsParameter) {
|
|
195
|
+
const variable = node.id || this.#anonymousName(node.type);
|
|
196
|
+
const constructorName = this.#sceneConstructorName(node);
|
|
197
|
+
this.#lines.push(` const ${variable} = new ${constructorName}();`);
|
|
198
|
+
this.#applyPixiProps(variable, node.props, true, node);
|
|
199
|
+
for (const [name, action] of Object.entries(node.events)) {
|
|
200
|
+
this.#runtimeImports.add('connectSceneEvent');
|
|
201
|
+
this.#lines.push(` connectSceneEvent(${variable}.${name}, ${JSON.stringify(action)}, root, ${actionsParameter});`);
|
|
202
|
+
}
|
|
203
|
+
this.#applyParentSorting(parent, node.props);
|
|
204
|
+
this.#lines.push(` ${parent}.addChild(${variable});`);
|
|
205
|
+
for (const [slot, children] of Object.entries(node.slots)) {
|
|
206
|
+
for (const child of children) {
|
|
207
|
+
const childVariable = this.#compileSlottedNode(child, variable, actionsParameter);
|
|
208
|
+
this.#runtimeImports.add('mount');
|
|
209
|
+
this.#lines.push(` mount(${variable}, ${childVariable}, ${JSON.stringify(slot)});`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
#compileSlottedNode(node, slotTarget, actionsParameter) {
|
|
214
|
+
if (node.kind === 'slotOutlet') {
|
|
215
|
+
throw new Error(`Cannot place <slot> inside ${slotTarget}.`);
|
|
216
|
+
}
|
|
217
|
+
if (node.kind === 'sceneInstance') {
|
|
218
|
+
const variable = node.id || this.#anonymousName(node.type);
|
|
219
|
+
const constructorName = this.#sceneConstructorName(node);
|
|
220
|
+
this.#lines.push(` const ${variable} = new ${constructorName}();`);
|
|
221
|
+
this.#applyPixiProps(variable, node.props, true, node);
|
|
222
|
+
for (const [name, action] of Object.entries(node.events)) {
|
|
223
|
+
this.#runtimeImports.add('connectSceneEvent');
|
|
224
|
+
this.#lines.push(` connectSceneEvent(${variable}.${name}, ${JSON.stringify(action)}, root, ${actionsParameter});`);
|
|
225
|
+
}
|
|
226
|
+
for (const [slot, children] of Object.entries(node.slots)) {
|
|
227
|
+
for (const child of children) {
|
|
228
|
+
const childVariable = this.#compileSlottedNode(child, variable, actionsParameter);
|
|
229
|
+
this.#runtimeImports.add('mount');
|
|
230
|
+
this.#lines.push(` mount(${variable}, ${childVariable}, ${JSON.stringify(slot)});`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return variable;
|
|
234
|
+
}
|
|
235
|
+
const variable = node.id || this.#anonymousName(node.type);
|
|
236
|
+
this.#lines.push(` const ${variable} = ${this.#constructPixiNode(node)};`);
|
|
237
|
+
this.#applyNodeId(variable, node.id);
|
|
238
|
+
this.#applyPixiProps(variable, node.props);
|
|
239
|
+
for (const child of node.children) {
|
|
240
|
+
this.#compileNode(child, variable, actionsParameter);
|
|
241
|
+
}
|
|
242
|
+
return variable;
|
|
243
|
+
}
|
|
244
|
+
#constructPixiNode(node) {
|
|
245
|
+
if (node.type === 'Text' || node.type === 'BitmapText' || node.type === 'HTMLText') {
|
|
246
|
+
const text = this.#stringValue(node.props.text ?? '');
|
|
247
|
+
const style = this.#styleObject(node.props);
|
|
248
|
+
return `new ${node.type}({ text: ${text}${style ? `, style: ${style}` : ''} })`;
|
|
249
|
+
}
|
|
250
|
+
if (node.type === 'Sprite') {
|
|
251
|
+
return node.props.texture === undefined
|
|
252
|
+
? 'new Sprite()'
|
|
253
|
+
: `new Sprite({ texture: ${this.#textureValue(node.props.texture)} })`;
|
|
254
|
+
}
|
|
255
|
+
if (node.type === 'NineSliceSprite') {
|
|
256
|
+
return `new NineSliceSprite(${this.#spriteTextureOptions(node.props)})`;
|
|
257
|
+
}
|
|
258
|
+
if (node.type === 'TilingSprite') {
|
|
259
|
+
return `new TilingSprite(${this.#spriteTextureOptions(node.props)})`;
|
|
260
|
+
}
|
|
261
|
+
if (node.type === 'Graphics') {
|
|
262
|
+
return 'new Graphics()';
|
|
263
|
+
}
|
|
264
|
+
return `new ${node.type}()`;
|
|
265
|
+
}
|
|
266
|
+
#sceneConstructorName(node) {
|
|
267
|
+
return this.options.sceneClassAliases?.[node.scene] ?? node.type;
|
|
268
|
+
}
|
|
269
|
+
#sceneStructImports(sceneImport) {
|
|
270
|
+
const sceneInterface = this.options.sceneInterfaces?.[Object.entries(this.options.sceneClassAliases ?? {})
|
|
271
|
+
.find(([, alias]) => alias === sceneImport.localName)?.[0] ?? ''];
|
|
272
|
+
if (!sceneInterface) {
|
|
273
|
+
return [];
|
|
274
|
+
}
|
|
275
|
+
return [...new Set(Object.values(sceneInterface.props)
|
|
276
|
+
.filter((contract) => contract.type === 'struct')
|
|
277
|
+
.map((contract) => contract.struct))]
|
|
278
|
+
.sort();
|
|
279
|
+
}
|
|
280
|
+
#applyPixiProps(variable, props, instance = false, sceneInstance) {
|
|
281
|
+
const x = props.x;
|
|
282
|
+
const y = props.y;
|
|
283
|
+
if (x !== undefined || y !== undefined) {
|
|
284
|
+
this.#lines.push(` ${variable}.position.set(${this.#value(x ?? 0)}, ${this.#value(y ?? 0)});`);
|
|
285
|
+
}
|
|
286
|
+
if (props.width !== undefined && props.shape === undefined) {
|
|
287
|
+
this.#lines.push(` ${variable}.width = ${this.#value(props.width)};`);
|
|
288
|
+
}
|
|
289
|
+
if (props.height !== undefined && props.shape === undefined) {
|
|
290
|
+
this.#lines.push(` ${variable}.height = ${this.#value(props.height)};`);
|
|
291
|
+
}
|
|
292
|
+
const scaleX = props.scaleX;
|
|
293
|
+
const scaleY = props.scaleY;
|
|
294
|
+
if (scaleX !== undefined || scaleY !== undefined) {
|
|
295
|
+
this.#lines.push(` ${variable}.scale.set(${this.#value(scaleX ?? 1)}, ${this.#value(scaleY ?? 1)});`);
|
|
296
|
+
}
|
|
297
|
+
if (props.rotation !== undefined) {
|
|
298
|
+
this.#lines.push(` ${variable}.rotation = ${this.#value(props.rotation)};`);
|
|
299
|
+
}
|
|
300
|
+
const pivotX = props.pivotX;
|
|
301
|
+
const pivotY = props.pivotY;
|
|
302
|
+
if (pivotX !== undefined || pivotY !== undefined) {
|
|
303
|
+
this.#lines.push(` ${variable}.pivot.set(${this.#value(pivotX ?? 0)}, ${this.#value(pivotY ?? 0)});`);
|
|
304
|
+
}
|
|
305
|
+
const skewX = props.skewX;
|
|
306
|
+
const skewY = props.skewY;
|
|
307
|
+
if (skewX !== undefined || skewY !== undefined) {
|
|
308
|
+
this.#lines.push(` ${variable}.skew.set(${this.#value(skewX ?? 0)}, ${this.#value(skewY ?? 0)});`);
|
|
309
|
+
}
|
|
310
|
+
this.#applySpriteProps(variable, props);
|
|
311
|
+
if (!instance && props.shape !== undefined) {
|
|
312
|
+
this.#drawGraphics(variable, props);
|
|
313
|
+
}
|
|
314
|
+
for (const [key, value] of Object.entries(props)) {
|
|
315
|
+
const nativePixiNodeProp = transformProps.has(key)
|
|
316
|
+
|| spriteProps.has(key)
|
|
317
|
+
|| graphicsProps.has(key)
|
|
318
|
+
|| key === 'text'
|
|
319
|
+
|| this.#isTextStyleProp(key);
|
|
320
|
+
if (!instance && nativePixiNodeProp) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (instance && transformProps.has(key)) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (instance && value && typeof value === 'object') {
|
|
327
|
+
this.#applyStructProp(variable, key, value, sceneInstance);
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (instance || pixiProps.has(key)) {
|
|
331
|
+
this.#lines.push(` ${variable}.${key} = ${this.#value(value)};`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
#applyStructProp(variable, key, value, sceneInstance) {
|
|
336
|
+
const contract = this.#sceneInstanceStructContract(sceneInstance, key);
|
|
337
|
+
if (!contract) {
|
|
338
|
+
this.#lines.push(` ${variable}.${key} = ${this.#value(value)};`);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const structVariable = `${variable}${key.charAt(0).toUpperCase()}${key.slice(1)}`;
|
|
342
|
+
this.#lines.push(` const ${structVariable} = new ${contract.struct}();`);
|
|
343
|
+
for (const [field, fieldValue] of Object.entries(value)) {
|
|
344
|
+
this.#lines.push(` ${structVariable}.${field} = ${this.#value(fieldValue)};`);
|
|
345
|
+
}
|
|
346
|
+
this.#lines.push(` ${variable}.${key} = ${structVariable};`);
|
|
347
|
+
}
|
|
348
|
+
#sceneInstanceStructContract(sceneInstance, key) {
|
|
349
|
+
const contract = sceneInstance ? this.options.sceneInterfaces?.[sceneInstance.scene]?.props[key] : undefined;
|
|
350
|
+
return contract?.type === 'struct' ? contract : undefined;
|
|
351
|
+
}
|
|
352
|
+
#applySpriteProps(variable, props) {
|
|
353
|
+
const anchorX = props.anchorX;
|
|
354
|
+
const anchorY = props.anchorY;
|
|
355
|
+
if (anchorX !== undefined || anchorY !== undefined) {
|
|
356
|
+
this.#lines.push(` ${variable}.anchor.set(${this.#value(anchorX ?? 0)}, ${this.#value(anchorY ?? 0)});`);
|
|
357
|
+
}
|
|
358
|
+
if (props.tint !== undefined) {
|
|
359
|
+
this.#lines.push(` ${variable}.tint = ${this.#value(props.tint)};`);
|
|
360
|
+
}
|
|
361
|
+
for (const key of ['leftWidth', 'rightWidth', 'topHeight', 'bottomHeight', 'tileRotation']) {
|
|
362
|
+
const value = props[key];
|
|
363
|
+
if (value !== undefined) {
|
|
364
|
+
this.#lines.push(` ${variable}.${key} = ${this.#value(value)};`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const tilePositionX = props.tilePositionX;
|
|
368
|
+
const tilePositionY = props.tilePositionY;
|
|
369
|
+
if (tilePositionX !== undefined || tilePositionY !== undefined) {
|
|
370
|
+
this.#lines.push(` ${variable}.tilePosition.set(${this.#value(tilePositionX ?? 0)}, ${this.#value(tilePositionY ?? 0)});`);
|
|
371
|
+
}
|
|
372
|
+
const tileScaleX = props.tileScaleX;
|
|
373
|
+
const tileScaleY = props.tileScaleY;
|
|
374
|
+
if (tileScaleX !== undefined || tileScaleY !== undefined) {
|
|
375
|
+
this.#lines.push(` ${variable}.tileScale.set(${this.#value(tileScaleX ?? 1)}, ${this.#value(tileScaleY ?? 1)});`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
#drawGraphics(variable, props) {
|
|
379
|
+
const shape = props.shape;
|
|
380
|
+
if (shape === 'roundRect') {
|
|
381
|
+
this.#lines.push(` ${variable}.roundRect(0, 0, ${this.#value(props.width ?? 0)}, ${this.#value(props.height ?? 0)}, ${this.#value(props.radius ?? 0)})${this.#graphicsStyles(props)};`);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (shape === 'rect') {
|
|
385
|
+
this.#lines.push(` ${variable}.rect(0, 0, ${this.#value(props.width ?? 0)}, ${this.#value(props.height ?? 0)})${this.#graphicsStyles(props)};`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
#graphicsStyles(props) {
|
|
389
|
+
const fill = props.fillAlpha === undefined
|
|
390
|
+
? `.fill(${this.#value(props.fill ?? 0xffffff)})`
|
|
391
|
+
: `.fill({ color: ${this.#value(props.fill ?? 0xffffff)}, alpha: ${this.#value(props.fillAlpha)} })`;
|
|
392
|
+
if (props.strokeWidth === undefined) {
|
|
393
|
+
return fill;
|
|
394
|
+
}
|
|
395
|
+
const stroke = props.strokeAlpha === undefined
|
|
396
|
+
? `.stroke({ width: ${this.#value(props.strokeWidth)}, color: ${this.#value(props.strokeColor ?? 0x000000)} })`
|
|
397
|
+
: `.stroke({ width: ${this.#value(props.strokeWidth)}, color: ${this.#value(props.strokeColor ?? 0x000000)}, alpha: ${this.#value(props.strokeAlpha)} })`;
|
|
398
|
+
return `${fill}${stroke}`;
|
|
399
|
+
}
|
|
400
|
+
#applyNodeId(variable, id) {
|
|
401
|
+
if (id) {
|
|
402
|
+
this.#lines.push(` ${variable}.label = ${JSON.stringify(id)};`);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
#applyParentSorting(parent, props) {
|
|
406
|
+
if (props.zIndex !== undefined) {
|
|
407
|
+
this.#lines.push(` ${parent}.sortableChildren = true;`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
#styleObject(props) {
|
|
411
|
+
const entries = [];
|
|
412
|
+
for (const key of pixiSceneTextStyleProps) {
|
|
413
|
+
const value = props[key];
|
|
414
|
+
if (value !== undefined) {
|
|
415
|
+
entries.push(`${key}: ${key === 'fontWeight' ? JSON.stringify(String(value)) : this.#value(value)}`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return entries.length > 0 ? `{ ${entries.join(', ')} }` : undefined;
|
|
419
|
+
}
|
|
420
|
+
#spriteTextureOptions(props) {
|
|
421
|
+
return props.texture === undefined
|
|
422
|
+
? '{}'
|
|
423
|
+
: `{ texture: ${this.#textureValue(props.texture)} }`;
|
|
424
|
+
}
|
|
425
|
+
#hasEvents() {
|
|
426
|
+
for (const child of this.template.children) {
|
|
427
|
+
if (this.#nodeHasEvents(child)) {
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
#nodeHasEvents(node) {
|
|
434
|
+
if (node.kind === 'slotOutlet') {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
if (node.kind === 'sceneInstance') {
|
|
438
|
+
if (Object.keys(node.events).length > 0) {
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
return Object.values(node.slots).some((children) => children.some((child) => this.#nodeHasEvents(child)));
|
|
442
|
+
}
|
|
443
|
+
return node.children.some((child) => this.#nodeHasEvents(child));
|
|
444
|
+
}
|
|
445
|
+
#isTextStyleProp(key) {
|
|
446
|
+
return textStyleProps.has(key);
|
|
447
|
+
}
|
|
448
|
+
#anonymousName(type) {
|
|
449
|
+
this.#nextId += 1;
|
|
450
|
+
return `${type.charAt(0).toLowerCase()}${type.slice(1)}${this.#nextId}`;
|
|
451
|
+
}
|
|
452
|
+
#textureVariable(texture) {
|
|
453
|
+
const existing = this.#textures.get(texture);
|
|
454
|
+
if (existing) {
|
|
455
|
+
return existing;
|
|
456
|
+
}
|
|
457
|
+
this.#nextTextureId += 1;
|
|
458
|
+
const variable = `__pixifactTexture${this.#nextTextureId}`;
|
|
459
|
+
this.#textures.set(texture, variable);
|
|
460
|
+
return variable;
|
|
461
|
+
}
|
|
462
|
+
#textureImportEntries() {
|
|
463
|
+
return [...this.#textures.keys()]
|
|
464
|
+
.filter((texture) => this.options.textureImports?.[texture])
|
|
465
|
+
.map((texture, index) => [texture, `__pixifactTextureUrl${index + 1}`]);
|
|
466
|
+
}
|
|
467
|
+
#textureImportVariable(texture) {
|
|
468
|
+
const index = [...this.#textures.keys()]
|
|
469
|
+
.filter((key) => this.options.textureImports?.[key])
|
|
470
|
+
.indexOf(texture);
|
|
471
|
+
return index >= 0 ? `__pixifactTextureUrl${index + 1}` : undefined;
|
|
472
|
+
}
|
|
473
|
+
#textureLoadSource(texture) {
|
|
474
|
+
return this.#textureImportVariable(texture) ?? this.#value(texture);
|
|
475
|
+
}
|
|
476
|
+
#textureValue(value) {
|
|
477
|
+
return typeof value === 'string' ? this.#textureVariable(value) : this.#value(value);
|
|
478
|
+
}
|
|
479
|
+
#value(value) {
|
|
480
|
+
if (typeof value === 'string') {
|
|
481
|
+
return JSON.stringify(value);
|
|
482
|
+
}
|
|
483
|
+
if (value && typeof value === 'object') {
|
|
484
|
+
return `{ ${Object.entries(value).map(([key, fieldValue]) => `${key}: ${this.#scalarValue(fieldValue)}`).join(', ')} }`;
|
|
485
|
+
}
|
|
486
|
+
return String(value);
|
|
487
|
+
}
|
|
488
|
+
#scalarValue(value) {
|
|
489
|
+
return typeof value === 'string' ? JSON.stringify(value) : String(value);
|
|
490
|
+
}
|
|
491
|
+
#stringValue(value) {
|
|
492
|
+
return JSON.stringify(String(value));
|
|
493
|
+
}
|
|
494
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
function projectRootPath(projectRoot) {
|
|
4
|
+
if (typeof projectRoot === 'string') {
|
|
5
|
+
return projectRoot;
|
|
6
|
+
}
|
|
7
|
+
if (projectRoot) {
|
|
8
|
+
return fileURLToPath(projectRoot);
|
|
9
|
+
}
|
|
10
|
+
return process.cwd();
|
|
11
|
+
}
|
|
12
|
+
export function pixifactScenesPlugin(options = {}) {
|
|
13
|
+
const generatedDir = path.resolve(projectRootPath(options.projectRoot), options.generatedDir ?? '.pixifact/generated');
|
|
14
|
+
const sceneRegistry = path.join(generatedDir, 'scenes.generated.ts');
|
|
15
|
+
return {
|
|
16
|
+
name: 'pixifact-scenes',
|
|
17
|
+
resolveId(id) {
|
|
18
|
+
return id === 'pixifact:scenes' ? sceneRegistry : undefined;
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
package/dist/index.d.ts
ADDED