@flighthq/spritesheet-formats 0.1.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/asepriteParse.d.ts +18 -0
- package/dist/asepriteParse.d.ts.map +1 -0
- package/dist/asepriteParse.js +87 -0
- package/dist/asepriteParse.js.map +1 -0
- package/dist/asepriteSchema.d.ts +64 -0
- package/dist/asepriteSchema.d.ts.map +1 -0
- package/dist/asepriteSchema.js +6 -0
- package/dist/asepriteSchema.js.map +1 -0
- package/dist/asepriteSerialize.d.ts +14 -0
- package/dist/asepriteSerialize.d.ts.map +1 -0
- package/dist/asepriteSerialize.js +86 -0
- package/dist/asepriteSerialize.js.map +1 -0
- package/dist/cocosPlistParse.d.ts +15 -0
- package/dist/cocosPlistParse.d.ts.map +1 -0
- package/dist/cocosPlistParse.js +157 -0
- package/dist/cocosPlistParse.js.map +1 -0
- package/dist/cocosPlistSchema.d.ts +31 -0
- package/dist/cocosPlistSchema.d.ts.map +1 -0
- package/dist/cocosPlistSchema.js +6 -0
- package/dist/cocosPlistSchema.js.map +1 -0
- package/dist/cocosPlistSerialize.d.ts +8 -0
- package/dist/cocosPlistSerialize.d.ts.map +1 -0
- package/dist/cocosPlistSerialize.js +86 -0
- package/dist/cocosPlistSerialize.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/libgdxAtlasParse.d.ts +13 -0
- package/dist/libgdxAtlasParse.d.ts.map +1 -0
- package/dist/libgdxAtlasParse.js +209 -0
- package/dist/libgdxAtlasParse.js.map +1 -0
- package/dist/libgdxAtlasSchema.d.ts +38 -0
- package/dist/libgdxAtlasSchema.d.ts.map +1 -0
- package/dist/libgdxAtlasSchema.js +6 -0
- package/dist/libgdxAtlasSchema.js.map +1 -0
- package/dist/spritesheetDetect.d.ts +41 -0
- package/dist/spritesheetDetect.d.ts.map +1 -0
- package/dist/spritesheetDetect.js +101 -0
- package/dist/spritesheetDetect.js.map +1 -0
- package/dist/starlingParse.d.ts +20 -0
- package/dist/starlingParse.d.ts.map +1 -0
- package/dist/starlingParse.js +138 -0
- package/dist/starlingParse.js.map +1 -0
- package/dist/starlingSchema.d.ts +32 -0
- package/dist/starlingSchema.d.ts.map +1 -0
- package/dist/starlingSchema.js +6 -0
- package/dist/starlingSchema.js.map +1 -0
- package/dist/starlingSerialize.d.ts +8 -0
- package/dist/starlingSerialize.d.ts.map +1 -0
- package/dist/starlingSerialize.js +67 -0
- package/dist/starlingSerialize.js.map +1 -0
- package/dist/texturePackerParse.d.ts +15 -0
- package/dist/texturePackerParse.d.ts.map +1 -0
- package/dist/texturePackerParse.js +70 -0
- package/dist/texturePackerParse.js.map +1 -0
- package/dist/texturePackerSchema.d.ts +52 -0
- package/dist/texturePackerSchema.d.ts.map +1 -0
- package/dist/texturePackerSchema.js +5 -0
- package/dist/texturePackerSchema.js.map +1 -0
- package/dist/texturePackerSerialize.d.ts +14 -0
- package/dist/texturePackerSerialize.d.ts.map +1 -0
- package/dist/texturePackerSerialize.js +74 -0
- package/dist/texturePackerSerialize.js.map +1 -0
- package/package.json +41 -0
- package/src/asepriteParse.test.ts +235 -0
- package/src/asepriteSerialize.test.ts +218 -0
- package/src/cocosPlistParse.test.ts +131 -0
- package/src/cocosPlistSerialize.test.ts +56 -0
- package/src/libgdxAtlasParse.test.ts +100 -0
- package/src/spritesheetDetect.test.ts +217 -0
- package/src/starlingParse.test.ts +176 -0
- package/src/starlingSerialize.test.ts +117 -0
- package/src/texturePackerParse.test.ts +237 -0
- package/src/texturePackerSerialize.test.ts +200 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { parseTexturePackerSpritesheet, parseTexturePackerSpritesheetDocument } from './texturePackerParse';
|
|
2
|
+
|
|
3
|
+
// ─── Fixtures ────────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
const HASH_JSON = JSON.stringify({
|
|
6
|
+
frames: {
|
|
7
|
+
'hero/idle_0.png': {
|
|
8
|
+
frame: { x: 0, y: 0, w: 60, h: 56 },
|
|
9
|
+
rotated: false,
|
|
10
|
+
trimmed: true,
|
|
11
|
+
spriteSourceSize: { x: 2, y: 4, w: 60, h: 56 },
|
|
12
|
+
sourceSize: { w: 64, h: 64 },
|
|
13
|
+
pivot: { x: 0.5, y: 1.0 },
|
|
14
|
+
},
|
|
15
|
+
'hero/idle_1.png': {
|
|
16
|
+
frame: { x: 60, y: 0, w: 60, h: 56 },
|
|
17
|
+
rotated: false,
|
|
18
|
+
trimmed: true,
|
|
19
|
+
spriteSourceSize: { x: 2, y: 4, w: 60, h: 56 },
|
|
20
|
+
sourceSize: { w: 64, h: 64 },
|
|
21
|
+
pivot: { x: 0.5, y: 1.0 },
|
|
22
|
+
},
|
|
23
|
+
'fx/spark.png': {
|
|
24
|
+
frame: { x: 0, y: 56, w: 16, h: 16 },
|
|
25
|
+
rotated: false,
|
|
26
|
+
trimmed: false,
|
|
27
|
+
spriteSourceSize: { x: 0, y: 0, w: 16, h: 16 },
|
|
28
|
+
sourceSize: { w: 16, h: 16 },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
meta: {
|
|
32
|
+
app: 'https://www.codeandweb.com/texturepacker',
|
|
33
|
+
version: '1.0',
|
|
34
|
+
image: 'atlas.png',
|
|
35
|
+
format: 'RGBA8888',
|
|
36
|
+
size: { w: 256, h: 128 },
|
|
37
|
+
scale: '2',
|
|
38
|
+
frameTags: [{ name: 'idle', from: 0, to: 1, direction: 'forward' }],
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const ARRAY_JSON = JSON.stringify({
|
|
43
|
+
frames: [
|
|
44
|
+
{
|
|
45
|
+
filename: 'hero/idle_0.png',
|
|
46
|
+
frame: { x: 0, y: 0, w: 60, h: 56 },
|
|
47
|
+
rotated: false,
|
|
48
|
+
trimmed: true,
|
|
49
|
+
spriteSourceSize: { x: 2, y: 4, w: 60, h: 56 },
|
|
50
|
+
sourceSize: { w: 64, h: 64 },
|
|
51
|
+
pivot: { x: 0.5, y: 1.0 },
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
filename: 'hero/run_0.png',
|
|
55
|
+
frame: { x: 60, y: 0, w: 32, h: 32 },
|
|
56
|
+
rotated: true,
|
|
57
|
+
trimmed: false,
|
|
58
|
+
spriteSourceSize: { x: 0, y: 0, w: 32, h: 32 },
|
|
59
|
+
sourceSize: { w: 32, h: 32 },
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
meta: {
|
|
63
|
+
app: 'https://www.codeandweb.com/texturepacker',
|
|
64
|
+
version: '1.0',
|
|
65
|
+
image: 'sprites.png',
|
|
66
|
+
format: 'RGBA8888',
|
|
67
|
+
size: { w: 128, h: 64 },
|
|
68
|
+
scale: 1,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const MINIMAL_HASH_JSON = JSON.stringify({
|
|
73
|
+
frames: {
|
|
74
|
+
'a.png': {
|
|
75
|
+
frame: { x: 0, y: 0, w: 10, h: 10 },
|
|
76
|
+
rotated: false,
|
|
77
|
+
trimmed: false,
|
|
78
|
+
spriteSourceSize: { x: 0, y: 0, w: 10, h: 10 },
|
|
79
|
+
sourceSize: { w: 10, h: 10 },
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
meta: {
|
|
83
|
+
app: 'tp',
|
|
84
|
+
version: '1.0',
|
|
85
|
+
image: 'a.png',
|
|
86
|
+
format: 'RGBA8888',
|
|
87
|
+
size: { w: 64, h: 64 },
|
|
88
|
+
scale: '1',
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('parseTexturePackerSpritesheet', () => {
|
|
93
|
+
it('returns a SpritesheetData (not a Parsed object)', () => {
|
|
94
|
+
const result = parseTexturePackerSpritesheet(HASH_JSON);
|
|
95
|
+
expect(typeof result.frames).toBe('object');
|
|
96
|
+
expect((result as unknown as Record<string, unknown>).document).toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('parses frame count from Hash variant', () => {
|
|
100
|
+
expect(parseTexturePackerSpritesheet(HASH_JSON).frames).toHaveLength(3);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('parses frame count from Array variant', () => {
|
|
104
|
+
expect(parseTexturePackerSpritesheet(ARRAY_JSON).frames).toHaveLength(2);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('maps atlas position and size', () => {
|
|
108
|
+
const data = parseTexturePackerSpritesheet(HASH_JSON);
|
|
109
|
+
const frame = data.frames.find((f) => f.name === 'hero/idle_0.png')!;
|
|
110
|
+
expect(frame.x).toBe(0);
|
|
111
|
+
expect(frame.y).toBe(0);
|
|
112
|
+
expect(frame.width).toBe(60);
|
|
113
|
+
expect(frame.height).toBe(56);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('maps frame name from Hash key', () => {
|
|
117
|
+
const data = parseTexturePackerSpritesheet(HASH_JSON);
|
|
118
|
+
expect(data.frames.map((f) => f.name)).toContain('hero/idle_0.png');
|
|
119
|
+
expect(data.frames.map((f) => f.name)).toContain('fx/spark.png');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('maps frame name from Array filename field', () => {
|
|
123
|
+
const data = parseTexturePackerSpritesheet(ARRAY_JSON);
|
|
124
|
+
expect(data.frames[0].name).toBe('hero/idle_0.png');
|
|
125
|
+
expect(data.frames[1].name).toBe('hero/run_0.png');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('preserves trim offset', () => {
|
|
129
|
+
const data = parseTexturePackerSpritesheet(HASH_JSON);
|
|
130
|
+
const frame = data.frames.find((f) => f.name === 'hero/idle_0.png')!;
|
|
131
|
+
expect(frame.offsetX).toBe(2);
|
|
132
|
+
expect(frame.offsetY).toBe(4);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('preserves source size', () => {
|
|
136
|
+
const data = parseTexturePackerSpritesheet(HASH_JSON);
|
|
137
|
+
const frame = data.frames.find((f) => f.name === 'hero/idle_0.png')!;
|
|
138
|
+
expect(frame.sourceWidth).toBe(64);
|
|
139
|
+
expect(frame.sourceHeight).toBe(64);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('maps pivot when present', () => {
|
|
143
|
+
const data = parseTexturePackerSpritesheet(HASH_JSON);
|
|
144
|
+
const frame = data.frames.find((f) => f.name === 'hero/idle_0.png')!;
|
|
145
|
+
expect(frame.pivotX).toBe(0.5);
|
|
146
|
+
expect(frame.pivotY).toBe(1.0);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('sets pivot to null when absent', () => {
|
|
150
|
+
const data = parseTexturePackerSpritesheet(HASH_JSON);
|
|
151
|
+
const frame = data.frames.find((f) => f.name === 'fx/spark.png')!;
|
|
152
|
+
expect(frame.pivotX).toBeNull();
|
|
153
|
+
expect(frame.pivotY).toBeNull();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('maps rotated flag', () => {
|
|
157
|
+
const data = parseTexturePackerSpritesheet(ARRAY_JSON);
|
|
158
|
+
expect(data.frames[0].rotated).toBe(false);
|
|
159
|
+
expect(data.frames[1].rotated).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('maps image file from meta', () => {
|
|
163
|
+
expect(parseTexturePackerSpritesheet(HASH_JSON).imageFile).toBe('atlas.png');
|
|
164
|
+
expect(parseTexturePackerSpritesheet(ARRAY_JSON).imageFile).toBe('sprites.png');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('maps atlas size from meta', () => {
|
|
168
|
+
const data = parseTexturePackerSpritesheet(HASH_JSON);
|
|
169
|
+
expect(data.imageWidth).toBe(256);
|
|
170
|
+
expect(data.imageHeight).toBe(128);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('parses numeric string scale', () => {
|
|
174
|
+
expect(parseTexturePackerSpritesheet(HASH_JSON).scale).toBe(2);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('parses numeric scale directly', () => {
|
|
178
|
+
expect(parseTexturePackerSpritesheet(ARRAY_JSON).scale).toBe(1);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('parses animations from frameTags', () => {
|
|
182
|
+
const data = parseTexturePackerSpritesheet(HASH_JSON);
|
|
183
|
+
expect(data.animations).toHaveLength(1);
|
|
184
|
+
expect(data.animations[0].name).toBe('idle');
|
|
185
|
+
expect(data.animations[0].direction).toBe('forward');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('maps animation frameNames to the correct frames', () => {
|
|
189
|
+
const data = parseTexturePackerSpritesheet(HASH_JSON);
|
|
190
|
+
const anim = data.animations[0];
|
|
191
|
+
expect(anim.frameNames).toHaveLength(2);
|
|
192
|
+
expect(anim.frameNames[0]).toBe('hero/idle_0.png');
|
|
193
|
+
expect(anim.frameNames[1]).toBe('hero/idle_1.png');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('produces empty animations when no frameTags present', () => {
|
|
197
|
+
expect(parseTexturePackerSpritesheet(ARRAY_JSON).animations).toHaveLength(0);
|
|
198
|
+
expect(parseTexturePackerSpritesheet(MINIMAL_HASH_JSON).animations).toHaveLength(0);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('handles frames with no trim (offsetX/Y = 0)', () => {
|
|
202
|
+
const data = parseTexturePackerSpritesheet(HASH_JSON);
|
|
203
|
+
const frame = data.frames.find((f) => f.name === 'fx/spark.png')!;
|
|
204
|
+
expect(frame.offsetX).toBe(0);
|
|
205
|
+
expect(frame.offsetY).toBe(0);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe('parseTexturePackerSpritesheetDocument', () => {
|
|
210
|
+
it('returns the same data as parseTexturePackerSpritesheet', () => {
|
|
211
|
+
const parsed = parseTexturePackerSpritesheet(HASH_JSON);
|
|
212
|
+
const { data } = parseTexturePackerSpritesheetDocument(HASH_JSON);
|
|
213
|
+
expect(data.frames.length).toBe(parsed.frames.length);
|
|
214
|
+
expect(data.imageFile).toBe(parsed.imageFile);
|
|
215
|
+
expect(data.imageWidth).toBe(parsed.imageWidth);
|
|
216
|
+
expect(data.imageHeight).toBe(parsed.imageHeight);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('preserves the original document for Hash variant', () => {
|
|
220
|
+
const { document } = parseTexturePackerSpritesheetDocument(HASH_JSON);
|
|
221
|
+
expect(Array.isArray(document.frames)).toBe(false);
|
|
222
|
+
expect((document.frames as Record<string, unknown>)['hero/idle_0.png']).toBeDefined();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('preserves the original document for Array variant', () => {
|
|
226
|
+
const { document } = parseTexturePackerSpritesheetDocument(ARRAY_JSON);
|
|
227
|
+
expect(Array.isArray(document.frames)).toBe(true);
|
|
228
|
+
expect((document.frames as unknown[])[0]).toBeDefined();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('preserves meta fields in document', () => {
|
|
232
|
+
const { document } = parseTexturePackerSpritesheetDocument(HASH_JSON);
|
|
233
|
+
expect(document.meta.app).toBe('https://www.codeandweb.com/texturepacker');
|
|
234
|
+
expect(document.meta.version).toBe('1.0');
|
|
235
|
+
expect(document.meta.format).toBe('RGBA8888');
|
|
236
|
+
});
|
|
237
|
+
});
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { parseTexturePackerSpritesheet, parseTexturePackerSpritesheetDocument } from './texturePackerParse';
|
|
2
|
+
import { serializeTexturePackerSpritesheet } from './texturePackerSerialize';
|
|
3
|
+
|
|
4
|
+
const HASH_JSON = JSON.stringify({
|
|
5
|
+
frames: {
|
|
6
|
+
'hero.png': {
|
|
7
|
+
frame: { h: 64, w: 64, x: 0, y: 0 },
|
|
8
|
+
rotated: false,
|
|
9
|
+
sourceSize: { h: 64, w: 64 },
|
|
10
|
+
spriteSourceSize: { h: 64, w: 64, x: 0, y: 0 },
|
|
11
|
+
trimmed: false,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
meta: {
|
|
15
|
+
app: 'https://www.codeandweb.com/texturepacker',
|
|
16
|
+
format: 'RGBA8888',
|
|
17
|
+
image: 'atlas.png',
|
|
18
|
+
scale: 1,
|
|
19
|
+
size: { h: 128, w: 128 },
|
|
20
|
+
version: '1.0',
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const ROUNDTRIP_HASH_JSON = JSON.stringify({
|
|
25
|
+
frames: {
|
|
26
|
+
'hero/idle_0.png': {
|
|
27
|
+
frame: { x: 0, y: 0, w: 60, h: 56 },
|
|
28
|
+
rotated: false,
|
|
29
|
+
trimmed: true,
|
|
30
|
+
spriteSourceSize: { x: 2, y: 4, w: 60, h: 56 },
|
|
31
|
+
sourceSize: { w: 64, h: 64 },
|
|
32
|
+
pivot: { x: 0.5, y: 1.0 },
|
|
33
|
+
},
|
|
34
|
+
'hero/idle_1.png': {
|
|
35
|
+
frame: { x: 60, y: 0, w: 60, h: 56 },
|
|
36
|
+
rotated: false,
|
|
37
|
+
trimmed: true,
|
|
38
|
+
spriteSourceSize: { x: 2, y: 4, w: 60, h: 56 },
|
|
39
|
+
sourceSize: { w: 64, h: 64 },
|
|
40
|
+
pivot: { x: 0.5, y: 1.0 },
|
|
41
|
+
},
|
|
42
|
+
'fx/spark.png': {
|
|
43
|
+
frame: { x: 0, y: 56, w: 16, h: 16 },
|
|
44
|
+
rotated: false,
|
|
45
|
+
trimmed: false,
|
|
46
|
+
spriteSourceSize: { x: 0, y: 0, w: 16, h: 16 },
|
|
47
|
+
sourceSize: { w: 16, h: 16 },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
meta: {
|
|
51
|
+
app: 'https://www.codeandweb.com/texturepacker',
|
|
52
|
+
version: '1.0',
|
|
53
|
+
image: 'atlas.png',
|
|
54
|
+
format: 'RGBA8888',
|
|
55
|
+
size: { w: 256, h: 128 },
|
|
56
|
+
scale: '2',
|
|
57
|
+
frameTags: [{ name: 'idle', from: 0, to: 1, direction: 'forward' }],
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const ROUNDTRIP_ARRAY_JSON = JSON.stringify({
|
|
62
|
+
frames: [
|
|
63
|
+
{
|
|
64
|
+
filename: 'hero/idle_0.png',
|
|
65
|
+
frame: { x: 0, y: 0, w: 60, h: 56 },
|
|
66
|
+
rotated: false,
|
|
67
|
+
trimmed: true,
|
|
68
|
+
spriteSourceSize: { x: 2, y: 4, w: 60, h: 56 },
|
|
69
|
+
sourceSize: { w: 64, h: 64 },
|
|
70
|
+
pivot: { x: 0.5, y: 1.0 },
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
filename: 'hero/run_0.png',
|
|
74
|
+
frame: { x: 60, y: 0, w: 32, h: 32 },
|
|
75
|
+
rotated: true,
|
|
76
|
+
trimmed: false,
|
|
77
|
+
spriteSourceSize: { x: 0, y: 0, w: 32, h: 32 },
|
|
78
|
+
sourceSize: { w: 32, h: 32 },
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
meta: {
|
|
82
|
+
app: 'https://www.codeandweb.com/texturepacker',
|
|
83
|
+
version: '1.0',
|
|
84
|
+
image: 'sprites.png',
|
|
85
|
+
format: 'RGBA8888',
|
|
86
|
+
size: { w: 128, h: 64 },
|
|
87
|
+
scale: 1,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const ROUNDTRIP_MINIMAL_JSON = JSON.stringify({
|
|
92
|
+
frames: {
|
|
93
|
+
'a.png': {
|
|
94
|
+
frame: { x: 0, y: 0, w: 10, h: 10 },
|
|
95
|
+
rotated: false,
|
|
96
|
+
trimmed: false,
|
|
97
|
+
spriteSourceSize: { x: 0, y: 0, w: 10, h: 10 },
|
|
98
|
+
sourceSize: { w: 10, h: 10 },
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
meta: {
|
|
102
|
+
app: 'tp',
|
|
103
|
+
version: '1.0',
|
|
104
|
+
image: 'a.png',
|
|
105
|
+
format: 'RGBA8888',
|
|
106
|
+
size: { w: 64, h: 64 },
|
|
107
|
+
scale: '1',
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('serializeTexturePackerSpritesheet', () => {
|
|
112
|
+
it('produces valid JSON with frames and meta', () => {
|
|
113
|
+
const data = parseTexturePackerSpritesheet(HASH_JSON);
|
|
114
|
+
const serialized = serializeTexturePackerSpritesheet(data);
|
|
115
|
+
const reparsed = JSON.parse(serialized) as Record<string, unknown>;
|
|
116
|
+
expect(reparsed).toHaveProperty('frames');
|
|
117
|
+
expect(reparsed).toHaveProperty('meta');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('preserves frame count on round-trip', () => {
|
|
121
|
+
const data = parseTexturePackerSpritesheet(HASH_JSON);
|
|
122
|
+
const serialized = serializeTexturePackerSpritesheet(data);
|
|
123
|
+
const reparsed = parseTexturePackerSpritesheet(serialized);
|
|
124
|
+
expect(reparsed.frames).toHaveLength(data.frames.length);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('serializes array variant when option is set', () => {
|
|
128
|
+
const data = parseTexturePackerSpritesheet(HASH_JSON);
|
|
129
|
+
const serialized = serializeTexturePackerSpritesheet(data, undefined, { variant: 'array' });
|
|
130
|
+
const parsed = JSON.parse(serialized) as { frames: unknown[] };
|
|
131
|
+
expect(Array.isArray(parsed.frames)).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('round-trips frame positions (Hash variant)', () => {
|
|
135
|
+
const { data, document } = parseTexturePackerSpritesheetDocument(ROUNDTRIP_HASH_JSON);
|
|
136
|
+
const json2 = serializeTexturePackerSpritesheet(data, document);
|
|
137
|
+
const data2 = parseTexturePackerSpritesheet(json2);
|
|
138
|
+
expect(data2.frames.map((f) => f.name).sort()).toEqual(data.frames.map((f) => f.name).sort());
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('round-trips image file and size', () => {
|
|
142
|
+
const { data, document } = parseTexturePackerSpritesheetDocument(ROUNDTRIP_HASH_JSON);
|
|
143
|
+
const data2 = parseTexturePackerSpritesheet(serializeTexturePackerSpritesheet(data, document));
|
|
144
|
+
expect(data2.imageFile).toBe(data.imageFile);
|
|
145
|
+
expect(data2.imageWidth).toBe(data.imageWidth);
|
|
146
|
+
expect(data2.imageHeight).toBe(data.imageHeight);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('round-trips frame x/y/width/height', () => {
|
|
150
|
+
const { data, document } = parseTexturePackerSpritesheetDocument(ROUNDTRIP_HASH_JSON);
|
|
151
|
+
const data2 = parseTexturePackerSpritesheet(serializeTexturePackerSpritesheet(data, document));
|
|
152
|
+
const orig = data.frames.find((f) => f.name === 'hero/idle_0.png')!;
|
|
153
|
+
const rt = data2.frames.find((f) => f.name === 'hero/idle_0.png')!;
|
|
154
|
+
expect(rt.x).toBe(orig.x);
|
|
155
|
+
expect(rt.y).toBe(orig.y);
|
|
156
|
+
expect(rt.width).toBe(orig.width);
|
|
157
|
+
expect(rt.height).toBe(orig.height);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('round-trips pivot values', () => {
|
|
161
|
+
const { data, document } = parseTexturePackerSpritesheetDocument(ROUNDTRIP_HASH_JSON);
|
|
162
|
+
const data2 = parseTexturePackerSpritesheet(serializeTexturePackerSpritesheet(data, document));
|
|
163
|
+
const rt = data2.frames.find((f) => f.name === 'hero/idle_0.png')!;
|
|
164
|
+
expect(rt.pivotX).toBe(0.5);
|
|
165
|
+
expect(rt.pivotY).toBe(1.0);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('round-trips animations via frameTags', () => {
|
|
169
|
+
const { data, document } = parseTexturePackerSpritesheetDocument(ROUNDTRIP_HASH_JSON);
|
|
170
|
+
const data2 = parseTexturePackerSpritesheet(serializeTexturePackerSpritesheet(data, document));
|
|
171
|
+
expect(data2.animations).toHaveLength(data.animations.length);
|
|
172
|
+
expect(data2.animations[0].name).toBe(data.animations[0].name);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('emits array variant when existing document is array variant', () => {
|
|
176
|
+
const { data, document } = parseTexturePackerSpritesheetDocument(ROUNDTRIP_ARRAY_JSON);
|
|
177
|
+
const json2 = serializeTexturePackerSpritesheet(data, document);
|
|
178
|
+
const parsed = JSON.parse(json2);
|
|
179
|
+
expect(Array.isArray(parsed.frames)).toBe(true);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('emits hash variant by default', () => {
|
|
183
|
+
const { data, document } = parseTexturePackerSpritesheetDocument(ROUNDTRIP_HASH_JSON);
|
|
184
|
+
const json2 = serializeTexturePackerSpritesheet(data, document);
|
|
185
|
+
const parsed = JSON.parse(json2);
|
|
186
|
+
expect(Array.isArray(parsed.frames)).toBe(false);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('respects variant override option', () => {
|
|
190
|
+
const { data, document } = parseTexturePackerSpritesheetDocument(ROUNDTRIP_HASH_JSON);
|
|
191
|
+
const json2 = serializeTexturePackerSpritesheet(data, document, { variant: 'array' });
|
|
192
|
+
const parsed = JSON.parse(json2);
|
|
193
|
+
expect(Array.isArray(parsed.frames)).toBe(true);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('produces valid JSON', () => {
|
|
197
|
+
const data = parseTexturePackerSpritesheet(ROUNDTRIP_MINIMAL_JSON);
|
|
198
|
+
expect(() => JSON.parse(serializeTexturePackerSpritesheet(data))).not.toThrow();
|
|
199
|
+
});
|
|
200
|
+
});
|