parse-tmx 4.3.3 → 4.5.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/README.md CHANGED
@@ -5,13 +5,15 @@
5
5
  [![NPM downloads][badge-npm-downloads]][url-npm]
6
6
  [![NPM Unpacked Size (with version)][badge-npm-unpacked-size]][url-npm]
7
7
 
8
- Inspired by [tmx-map-parser](https://github.com/praghus/tmx-map-parser).
9
- A parser for [Tiled Map Editor](http://www.mapeditor.org/) \*.tmx files.
8
+ This package parses \*.tmx files written by [Tiled Map Editor](http://www.mapeditor.org/). It is inspired by [tmx-map-parser](https://github.com/praghus/tmx-map-parser).
9
+
10
+ Design notes live in [the specs](https://github.com/Esposter/Esposter/tree/main/openspec/specs/parse-tmx).
10
11
 
11
12
  ## Table of Contents
12
13
 
13
14
  - 🚀 [Getting Started](#getting-started)
14
- - 📖 [Documentation](#documentation)
15
+ - 🧩 [Key Exports](#key-exports)
16
+ - 📝 [Usage](#usage)
15
17
  - ⚖️ [License](#license)
16
18
 
17
19
  ---
@@ -22,33 +24,42 @@ A parser for [Tiled Map Editor](http://www.mapeditor.org/) \*.tmx files.
22
24
  pnpm i parse-tmx
23
25
  ```
24
26
 
25
- ## <a name="documentation">📖 Documentation</a>
27
+ ## <a name="key-exports">🧩 Key Exports</a>
26
28
 
27
- We highly recommend you take a look at the [documentation](https://github.com/Esposter/Esposter/tree/main/openspec/specs/parse-tmx) to level up.
29
+ | Export | Kind | Purpose |
30
+ | :----------------- | :-------- | :----------------------------------------------------------------------------------------------------------- |
31
+ | `parseTileset` | function | Reads an external tileset file, as Tiled writes one, into the embedded tileset it holds, without a map |
32
+ | `parseTmx` | function | Parses a `.tmx` string into a `TMXParsed`, matching Phaser's tilemap export and supporting external tilesets |
33
+ | `PropertyType` | enum | The types a Tiled custom property can have, such as `int` or `string` |
34
+ | `Shape` | enum | The shapes a Tiled object can take, such as `rectangle` or `polygon` |
35
+ | `TMXLayerParsed` | interface | A layer as parsed: its tile data, flips, objects and properties |
36
+ | `TMXMapParsed` | class | The map element's attributes, with the defaults a map that omits them reads as |
37
+ | `TMXParsed` | class | The parse result, holding the parsed map as `map` |
38
+ | `TMXTilesetParsed` | type | A tileset as parsed: an embedded one or an external one |
28
39
 
29
- ### Usage
40
+ ## <a name="usage">📝 Usage</a>
30
41
 
31
42
  ```ts
32
43
  import { parseTmx } from "parse-tmx";
33
- // The *.tmx file can be loaded as a string or URL encoded data
34
- import map from "map.tmx";
44
+
45
+ // The *.tmx file is read as text, here from a URL
46
+ const xmlString = await (await fetch("/maps/dungeon.tmx")).text();
35
47
 
36
48
  // Translates the tile flips in the layer data (default: false)
37
49
  const translateFlips = true;
38
50
 
39
- const loadMap = async () => {
40
- // parseTmx resolves to a neverthrow Result, so the map is taken from its Ok branch and an error is rethrown
41
- const data = (await parseTmx(map, translateFlips)).match(
42
- (tmxParsed) => tmxParsed.map,
43
- (error) => {
44
- throw error;
45
- },
46
- );
47
- ...
48
- };
51
+ // parseTmx resolves to a neverthrow Result, so the parsed map is taken from its Ok branch and an error is rethrown
52
+ const { map } = (await parseTmx(xmlString, translateFlips)).match(
53
+ (tmxParsed) => tmxParsed,
54
+ (error) => {
55
+ throw error;
56
+ },
57
+ );
58
+
59
+ console.log(map.layers.length);
49
60
  ```
50
61
 
51
- ### Example data
62
+ ### <a name="example-data">📄 Example data</a>
52
63
 
53
64
  [TMX Map Format documentation](https://doc.mapeditor.org/en/stable/reference/tmx-map-format/)
54
65
 
@@ -117,17 +128,6 @@ const loadMap = async () => {
117
128
  }
118
129
  ```
119
130
 
120
- ### Commands
121
-
122
- Run from `packages/parse-tmx/`:
123
-
124
- ```bash
125
- pnpm build # compile to dist/
126
- pnpm test # vitest watch mode (coverage is run from the repo root)
127
- pnpm lint:fix # auto-fix lint
128
- pnpm typecheck # type check
129
- ```
130
-
131
131
  ## <a name="license">⚖️ License</a>
132
132
 
133
133
  This project is licensed under the [Apache-2.0 license](https://github.com/Esposter/Esposter/blob/main/LICENSE).
package/dist/index.d.ts CHANGED
@@ -1,4 +1,15 @@
1
1
  import { Result, ResultAsync } from "neverthrow";
2
+ export declare enum PropertyType {
3
+ Bool = "bool",
4
+ Class = "class",
5
+ Color = "color",
6
+ Enum = "enum",
7
+ File = "file",
8
+ Float = "float",
9
+ Int = "int",
10
+ Object = "object",
11
+ String = "string"
12
+ }
2
13
  export declare enum Shape {
3
14
  Ellipse = "ellipse",
4
15
  Point = "point",
package/dist/index.js CHANGED
@@ -1,5 +1,18 @@
1
1
  import { InvalidOperationError, Operation, exhaustiveGuard, getResult, getResultAsync, normalizeString } from "@esposter/shared";
2
2
  import sax from "sax";
3
+ // src/models/PropertyType.ts
4
+ let PropertyType = /* @__PURE__ */ function(PropertyType) {
5
+ PropertyType["Bool"] = "bool";
6
+ PropertyType["Class"] = "class";
7
+ PropertyType["Color"] = "color";
8
+ PropertyType["Enum"] = "enum";
9
+ PropertyType["File"] = "file";
10
+ PropertyType["Float"] = "float";
11
+ PropertyType["Int"] = "int";
12
+ PropertyType["Object"] = "object";
13
+ PropertyType["String"] = "string";
14
+ return PropertyType;
15
+ }({});
3
16
  // src/models/Shape.ts
4
17
  let Shape = /* @__PURE__ */ function(Shape) {
5
18
  Shape["Ellipse"] = "ellipse";
@@ -72,21 +85,7 @@ let Encoding = /* @__PURE__ */ function(Encoding) {
72
85
  Encoding["Csv"] = "csv";
73
86
  return Encoding;
74
87
  }({});
75
- // src/services/getDecompressedBytes.ts
76
- const getDecompressedBytes = async (bytes, compression) => {
77
- const format = compression === "gzip" ? "gzip" : "deflate";
78
- const decompressedTileStream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream(format));
79
- return new Uint8Array(await new Response(decompressedTileStream).arrayBuffer());
80
- };
81
- // src/services/unpackTileBytes.ts
82
- const unpackTileBytes = (bytes) => {
83
- if (bytes.byteLength % Uint32Array.BYTES_PER_ELEMENT !== 0) throw new InvalidOperationError(Operation.Read, "TMXLayer", `expected whole ${Uint32Array.BYTES_PER_ELEMENT}-byte tiles, received ${bytes.byteLength} bytes`);
84
- const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
85
- const tiles = new Uint32Array(bytes.byteLength / Uint32Array.BYTES_PER_ELEMENT);
86
- for (let index = 0; index < tiles.length; index++) tiles[index] = dataView.getUint32(index * Uint32Array.BYTES_PER_ELEMENT, true);
87
- return [...tiles];
88
- };
89
- // src/services/decodeTileData.ts
88
+ // src/services/decodeCsv.ts
90
89
  const CSV_SEPARATOR_CODE_POINT = ",".codePointAt(0) ?? 0;
91
90
  const ZERO_CODE_POINT = "0".codePointAt(0) ?? 0;
92
91
  const NINE_CODE_POINT = "9".codePointAt(0) ?? 0;
@@ -97,8 +96,10 @@ const decodeCsv = (text) => {
97
96
  let hasDigit = false;
98
97
  let isEnded = false;
99
98
  for (let index = 0; index <= text.length; index++) {
100
- const codePoint = text.codePointAt(index) ?? CSV_SEPARATOR_CODE_POINT;
101
- if (codePoint === CSV_SEPARATOR_CODE_POINT) {
99
+ const codePoint = text.codePointAt(index);
100
+ if (codePoint === void 0) {
101
+ if (hasDigit || tiles.length > 0) tiles.push(tile);
102
+ } else if (codePoint === CSV_SEPARATOR_CODE_POINT) {
102
103
  tiles.push(tile);
103
104
  tile = 0;
104
105
  hasDigit = false;
@@ -112,6 +113,21 @@ const decodeCsv = (text) => {
112
113
  }
113
114
  return tiles;
114
115
  };
116
+ // src/services/getDecompressedBytes.ts
117
+ const getDecompressedBytes = async (bytes, compression) => {
118
+ const format = compression === "gzip" ? "gzip" : "deflate";
119
+ const decompressedTileStream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream(format));
120
+ return new Uint8Array(await new Response(decompressedTileStream).arrayBuffer());
121
+ };
122
+ // src/services/unpackTileBytes.ts
123
+ const unpackTileBytes = (bytes) => {
124
+ if (bytes.byteLength % Uint32Array.BYTES_PER_ELEMENT !== 0) throw new InvalidOperationError(Operation.Read, "TMXLayer", `expected whole ${Uint32Array.BYTES_PER_ELEMENT}-byte tiles, received ${bytes.byteLength} bytes`);
125
+ const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
126
+ const tiles = new Uint32Array(bytes.byteLength / Uint32Array.BYTES_PER_ELEMENT);
127
+ for (let index = 0; index < tiles.length; index++) tiles[index] = dataView.getUint32(index * Uint32Array.BYTES_PER_ELEMENT, true);
128
+ return [...tiles];
129
+ };
130
+ // src/services/decodeTileData.ts
115
131
  const decodeTileData = async (source) => {
116
132
  const { compression, encoding, gids, text } = source;
117
133
  if (encoding === void 0) return gids;
@@ -157,24 +173,13 @@ const assignTileData = async (pending, translateFlips) => {
157
173
  } else owner.data = tiles;
158
174
  }));
159
175
  };
160
- // src/models/PropertyType.ts
161
- let PropertyType = /* @__PURE__ */ function(PropertyType) {
162
- PropertyType["Bool"] = "bool";
163
- PropertyType["Class"] = "class";
164
- PropertyType["Color"] = "color";
165
- PropertyType["File"] = "file";
166
- PropertyType["Float"] = "float";
167
- PropertyType["Int"] = "int";
168
- PropertyType["Object"] = "object";
169
- PropertyType["String"] = "string";
170
- return PropertyType;
171
- }({});
172
- // src/services/closeTmxFrame.ts
176
+ // src/services/parsePropertyValue.ts
173
177
  const parsePropertyValue = (propertyType, value) => {
174
178
  switch (propertyType) {
175
179
  case "bool": return value === "true";
176
180
  case "class":
177
181
  case "color":
182
+ case "enum":
178
183
  case "file":
179
184
  case "object":
180
185
  case "string": return value;
@@ -183,6 +188,7 @@ const parsePropertyValue = (propertyType, value) => {
183
188
  default: return exhaustiveGuard(propertyType);
184
189
  }
185
190
  };
191
+ // src/services/closeTmxFrame.ts
186
192
  const closeTmxFrame = (frame) => {
187
193
  switch (frame.tag) {
188
194
  case "chunk":
@@ -223,6 +229,75 @@ const imageAttributeKindMap = {
223
229
  width: "requiredNumber"
224
230
  };
225
231
  const createTmxImage = (attributes) => readTmxAttributes(attributes, imageAttributeKindMap);
232
+ // src/services/createDataSource.ts
233
+ const createDataSource = (compression, encoding) => ({
234
+ chunked: false,
235
+ compression,
236
+ encoding,
237
+ gids: [],
238
+ text: ""
239
+ });
240
+ // src/services/parseEnumAttribute.ts
241
+ const parseEnumAttribute = (operation, label, enumObject, value) => {
242
+ if (value === void 0) return void 0;
243
+ const enumValue = Object.values(enumObject).find((member) => member === value);
244
+ if (enumValue !== void 0) return enumValue;
245
+ throw new InvalidOperationError(Operation.Read, operation, `${value} is not a supported ${label}`);
246
+ };
247
+ // src/services/parseCompression.ts
248
+ const parseCompression = (value) => parseEnumAttribute(parseCompression.name, "compression", Compression, value);
249
+ // src/services/parseEncoding.ts
250
+ const parseEncoding = (value) => parseEnumAttribute(parseEncoding.name, "encoding", Encoding, value);
251
+ // src/services/openTileData.ts
252
+ const openTileData = (layer, attributes, context) => {
253
+ const source = createDataSource(parseCompression(attributes.compression), parseEncoding(attributes.encoding));
254
+ context.pending.push({
255
+ expectedCount: context.tileCount,
256
+ owner: layer,
257
+ source
258
+ });
259
+ return {
260
+ owner: layer,
261
+ tag: "data",
262
+ target: source,
263
+ text: ""
264
+ };
265
+ };
266
+ // src/services/openTmxDataChild.ts
267
+ const gidAttributeKindMap = { gid: "requiredNumber" };
268
+ const chunkAttributeKindMap = {
269
+ height: "requiredNumber",
270
+ width: "requiredNumber",
271
+ x: "requiredNumber",
272
+ y: "requiredNumber"
273
+ };
274
+ const openTmxDataChild = (parent, tag, attributes, context) => {
275
+ const source = parent.target;
276
+ switch (tag) {
277
+ case "chunk": {
278
+ if (parent.tag !== "data") return void 0;
279
+ const chunk = readTmxAttributes(attributes, chunkAttributeKindMap);
280
+ parent.owner.chunks ??= [];
281
+ parent.owner.chunks.push(chunk);
282
+ source.chunked = true;
283
+ const chunkSource = createDataSource(source.compression, source.encoding);
284
+ context.pending.push({
285
+ expectedCount: chunk.width * chunk.height,
286
+ owner: chunk,
287
+ source: chunkSource
288
+ });
289
+ return {
290
+ tag,
291
+ target: chunkSource,
292
+ text: ""
293
+ };
294
+ }
295
+ case "tile":
296
+ source.gids.push(readTmxAttributes(attributes, gidAttributeKindMap).gid);
297
+ return;
298
+ default: return;
299
+ }
300
+ };
226
301
  // src/services/openTmxLayer.ts
227
302
  const groupAttributeKindMap = {
228
303
  class: "string",
@@ -280,96 +355,88 @@ const openTmxProperties = (target) => {
280
355
  text: ""
281
356
  };
282
357
  };
283
- // src/services/parseEnumAttribute.ts
284
- const parseEnumAttribute = (operation, label, enumObject, value) => {
285
- if (value === void 0) return void 0;
286
- const enumValue = Object.values(enumObject).find((member) => member === value);
287
- if (enumValue !== void 0) return enumValue;
288
- throw new InvalidOperationError(Operation.Read, operation, `${value} is not a supported ${label}`);
289
- };
290
358
  // src/services/openTmxLayerChild.ts
291
- const gidAttributeKindMap = { gid: "requiredNumber" };
292
- const chunkAttributeKindMap = {
293
- height: "requiredNumber",
294
- width: "requiredNumber",
295
- x: "requiredNumber",
296
- y: "requiredNumber"
297
- };
298
- const parseCompression = (value) => parseEnumAttribute(parseCompression.name, "compression", Compression, value);
299
- const parseEncoding = (value) => parseEnumAttribute(parseEncoding.name, "encoding", Encoding, value);
300
- const createDataSource = (compression, encoding) => ({
301
- chunked: false,
302
- compression,
303
- encoding,
304
- gids: [],
305
- text: ""
306
- });
307
- const openTileData = (layer, attributes, context) => {
308
- const source = createDataSource(parseCompression(attributes.compression), parseEncoding(attributes.encoding));
309
- context.pending.push({
310
- expectedCount: context.tileCount,
311
- owner: layer,
312
- source
313
- });
314
- return {
315
- owner: layer,
316
- tag: "data",
317
- target: source,
318
- text: ""
319
- };
320
- };
321
- const openTmxDataChild = (parent, tag, attributes, context) => {
322
- const source = parent.target;
359
+ const openTmxLayerChild = (parent, tag, attributes, context) => {
360
+ if (parent.tag === "chunk" || parent.tag === "data") return openTmxDataChild(parent, tag, attributes, context);
323
361
  switch (tag) {
324
- case "chunk": {
325
- if (parent.tag !== "data") return void 0;
326
- const chunk = readTmxAttributes(attributes, chunkAttributeKindMap);
327
- parent.owner.chunks ??= [];
328
- parent.owner.chunks.push(chunk);
329
- source.chunked = true;
330
- const chunkSource = createDataSource(source.compression, source.encoding);
331
- context.pending.push({
332
- owner: chunk,
333
- source: chunkSource
334
- });
362
+ case "data": if (parent.tag === "layer") return openTileData(parent.target, attributes, context);
363
+ else return void 0;
364
+ case "group":
365
+ case "imagelayer":
366
+ case "layer":
367
+ case "objectgroup": if (parent.tag === "group") return openTmxLayer(parent.target.layers, tag, attributes);
368
+ else return void 0;
369
+ case "image":
370
+ if (parent.tag !== "imagelayer") return void 0;
371
+ parent.target.image = createTmxImage(attributes);
335
372
  return {
336
373
  tag,
337
- target: chunkSource,
374
+ target: parent.target.image,
338
375
  text: ""
339
376
  };
340
- }
341
- case "tile":
342
- source.gids.push(readTmxAttributes(attributes, gidAttributeKindMap).gid);
343
- return;
377
+ case "properties": return openTmxProperties(parent.target);
344
378
  default: return;
345
379
  }
346
380
  };
347
- const openTmxLayerChild = (parent, tag, attributes, context) => {
348
- if (parent.tag === "chunk" || parent.tag === "data") return openTmxDataChild(parent, tag, attributes, context);
381
+ // src/services/createEmbeddedTileset.ts
382
+ const embeddedTilesetAttributeKindMap = {
383
+ columns: "requiredNumber",
384
+ firstgid: "requiredNumber",
385
+ margin: "number",
386
+ name: "requiredString",
387
+ spacing: "number",
388
+ tilecount: "requiredNumber",
389
+ tileheight: "requiredNumber",
390
+ tilewidth: "requiredNumber"
391
+ };
392
+ const createEmbeddedTileset = (attributes) => ({
393
+ ...readTmxAttributes(attributes, embeddedTilesetAttributeKindMap),
394
+ tiles: []
395
+ });
396
+ // src/services/openTmxMapChild.ts
397
+ const externalTilesetAttributeKindMap = {
398
+ firstgid: "requiredNumber",
399
+ source: "requiredString"
400
+ };
401
+ const openTmxMapChild = (parent, name, tag, attributes) => {
402
+ const map = parent.target;
349
403
  switch (tag) {
350
404
  case "data":
351
- if (parent.tag !== "layer") return void 0;
352
- return openTileData(parent.target, attributes, context);
405
+ case "export":
406
+ case "image":
407
+ case "object":
408
+ case "property": return;
409
+ case "editorsettings": {
410
+ const editorSettings = {};
411
+ map.editorsettings = editorSettings;
412
+ return {
413
+ tag,
414
+ target: editorSettings,
415
+ text: ""
416
+ };
417
+ }
353
418
  case "group":
354
419
  case "imagelayer":
355
420
  case "layer":
356
- case "objectgroup":
357
- if (parent.tag !== "group") return void 0;
358
- return openTmxLayer(parent.target.layers, tag, attributes);
359
- case "image":
360
- if (parent.tag !== "imagelayer") return void 0;
361
- parent.target.image = createTmxImage(attributes);
421
+ case "objectgroup": return openTmxLayer(map.layers, tag, attributes);
422
+ case "properties": return openTmxProperties(map);
423
+ case "tileset": {
424
+ if (attributes.source !== void 0) {
425
+ map.tilesets.push(readTmxAttributes(attributes, externalTilesetAttributeKindMap));
426
+ return;
427
+ }
428
+ const tileset = createEmbeddedTileset(attributes);
429
+ map.tilesets.push(tileset);
362
430
  return {
363
431
  tag,
364
- target: parent.target.image,
432
+ target: tileset,
365
433
  text: ""
366
434
  };
367
- case "properties": return openTmxProperties(parent.target);
368
- default: return;
435
+ }
436
+ default: throw new InvalidOperationError(Operation.Read, openTmxMapChild.name, `${name} is not a map child`);
369
437
  }
370
438
  };
371
- // src/services/openTmxObjectChild.ts
372
- const NUMBER_REGEX = /^[+-]?\d+(?:\.\d+)?$/u;
439
+ // src/services/createObject.ts
373
440
  const objectAttributeKindMap = {
374
441
  gid: "number",
375
442
  height: "requiredNumber",
@@ -390,7 +457,7 @@ const createObject = (attributes) => {
390
457
  shape: "rectangle"
391
458
  };
392
459
  if (gid === void 0) return shaped;
393
- return gid ? {
460
+ else return gid ? {
394
461
  ...shaped,
395
462
  flips: parseFlips(gid),
396
463
  gid: parseTileId(gid)
@@ -399,20 +466,25 @@ const createObject = (attributes) => {
399
466
  gid
400
467
  };
401
468
  };
402
- const parsePoints = (points) => points === "" ? [] : points.split(" ").map((point) => {
403
- const [x = "", y = ""] = point.split(",");
404
- return [Number(x), Number(y)];
405
- });
469
+ // src/services/parseUntypedValue.ts
470
+ const NUMBER_REGEX = /^[+-]?\d+(?:\.\d+)?$/u;
406
471
  const parseUntypedValue = (value) => {
407
472
  if (value === "true") return true;
408
473
  else if (value === "false") return false;
409
474
  else if (NUMBER_REGEX.test(value)) return Number(value);
410
475
  else return value;
411
476
  };
477
+ // src/services/createTextProperties.ts
412
478
  const createTextProperties = (attributes) => Object.entries(attributes).map(([name, value]) => ({
413
479
  name,
414
480
  value: parseUntypedValue(value)
415
481
  }));
482
+ // src/services/parsePoints.ts
483
+ const parsePoints = (points) => points === "" ? [] : points.split(" ").map((point) => {
484
+ const [x = "", y = ""] = point.split(",");
485
+ return [Number(x), Number(y)];
486
+ });
487
+ // src/services/openTmxObjectChild.ts
416
488
  const openTmxObjectChild = (parent, tag, attributes) => {
417
489
  if (parent.tag === "objectgroup") {
418
490
  if (tag === "properties") return openTmxProperties(parent.target);
@@ -456,7 +528,28 @@ const openTmxObjectChild = (parent, tag, attributes) => {
456
528
  default: return;
457
529
  }
458
530
  };
459
- // src/services/openTmxFrame.ts
531
+ // src/services/parsePropertyType.ts
532
+ const parsePropertyType = (value) => parseEnumAttribute(parsePropertyType.name, "property type", PropertyType, value) ?? "string";
533
+ // src/services/openTmxPropertyChild.ts
534
+ const propertyAttributeKindMap = { name: "requiredString" };
535
+ const openTmxPropertyChild = (parent, tag, attributes) => {
536
+ if (tag !== "property") return void 0;
537
+ const target = {
538
+ ...readTmxAttributes(attributes, propertyAttributeKindMap),
539
+ value: ""
540
+ };
541
+ parent.target.push(target);
542
+ return {
543
+ propertyType: parsePropertyType(attributes.type),
544
+ tag,
545
+ target,
546
+ text: "",
547
+ valueAttribute: attributes.value
548
+ };
549
+ };
550
+ // src/services/parseTmxTagName.ts
551
+ const parseTmxTagName = (name) => Object.values(TMXTagName).find((tagName) => tagName === name);
552
+ // src/services/openTmxRoot.ts
460
553
  const mapAttributeKindMap = {
461
554
  backgroundcolor: "string",
462
555
  compressionlevel: "number",
@@ -477,41 +570,6 @@ const mapAttributeKindMap = {
477
570
  version: "requiredNumber",
478
571
  width: "requiredNumber"
479
572
  };
480
- const embeddedTilesetAttributeKindMap = {
481
- columns: "requiredNumber",
482
- firstgid: "requiredNumber",
483
- margin: "number",
484
- name: "requiredString",
485
- spacing: "number",
486
- tilecount: "requiredNumber",
487
- tileheight: "requiredNumber",
488
- tilewidth: "requiredNumber"
489
- };
490
- const externalTilesetAttributeKindMap = {
491
- firstgid: "requiredNumber",
492
- source: "requiredString"
493
- };
494
- const tileAttributeKindMap = {
495
- id: "requiredNumber",
496
- probability: "number",
497
- terrain: "string",
498
- type: "string"
499
- };
500
- const animationFrameAttributeKindMap = {
501
- duration: "requiredNumber",
502
- tileid: "requiredNumber"
503
- };
504
- const exportAttributeKindMap = {
505
- format: "requiredString",
506
- target: "requiredString"
507
- };
508
- const propertyAttributeKindMap = { name: "requiredString" };
509
- const createEmbeddedTileset = (attributes) => ({
510
- ...readTmxAttributes(attributes, embeddedTilesetAttributeKindMap),
511
- tiles: []
512
- });
513
- const parseTmxTagName = (name) => Object.values(TMXTagName).find((tagName) => tagName === name);
514
- const parsePropertyType = (value) => parseEnumAttribute(parsePropertyType.name, "property type", PropertyType, value) ?? "string";
515
573
  const openTmxRoot = (name, attributes, context) => {
516
574
  const tag = parseTmxTagName(name);
517
575
  switch (tag) {
@@ -524,54 +582,16 @@ const openTmxRoot = (name, attributes, context) => {
524
582
  text: ""
525
583
  };
526
584
  }
527
- case "tileset":
528
- if (attributes.source !== void 0) return void 0;
529
- return {
530
- tag,
531
- target: createEmbeddedTileset(attributes),
532
- text: ""
533
- };
585
+ case "tileset": if (attributes.source === void 0) return {
586
+ tag,
587
+ target: createEmbeddedTileset(attributes),
588
+ text: ""
589
+ };
590
+ else return void 0;
534
591
  default: throw new InvalidOperationError(Operation.Read, openTmxRoot.name, `${name} is not a map or a tileset`);
535
592
  }
536
593
  };
537
- const openTmxMapChild = (parent, name, tag, attributes) => {
538
- const map = parent.target;
539
- switch (tag) {
540
- case "data":
541
- case "export":
542
- case "image":
543
- case "object":
544
- case "property": return;
545
- case "editorsettings": {
546
- const editorSettings = {};
547
- map.editorsettings = editorSettings;
548
- return {
549
- tag,
550
- target: editorSettings,
551
- text: ""
552
- };
553
- }
554
- case "group":
555
- case "imagelayer":
556
- case "layer":
557
- case "objectgroup": return openTmxLayer(map.layers, tag, attributes);
558
- case "properties": return openTmxProperties(map);
559
- case "tileset": {
560
- if (attributes.source !== void 0) {
561
- map.tilesets.push(readTmxAttributes(attributes, externalTilesetAttributeKindMap));
562
- return;
563
- }
564
- const tileset = createEmbeddedTileset(attributes);
565
- map.tilesets.push(tileset);
566
- return {
567
- tag,
568
- target: tileset,
569
- text: ""
570
- };
571
- }
572
- default: throw new InvalidOperationError(Operation.Read, openTmxMapChild.name, `${name} is not a map child`);
573
- }
574
- };
594
+ // src/services/openTmxTileChild.ts
575
595
  const openTmxTileChild = (parent, tag, attributes) => {
576
596
  const tile = parent.target;
577
597
  switch (tag) {
@@ -602,6 +622,13 @@ const openTmxTileChild = (parent, tag, attributes) => {
602
622
  default: return;
603
623
  }
604
624
  };
625
+ // src/services/openTmxTilesetChild.ts
626
+ const tileAttributeKindMap = {
627
+ id: "requiredNumber",
628
+ probability: "number",
629
+ terrain: "string",
630
+ type: "string"
631
+ };
605
632
  const openTmxTilesetChild = (parent, tag, attributes) => {
606
633
  const tileset = parent.target;
607
634
  switch (tag) {
@@ -626,20 +653,14 @@ const openTmxTilesetChild = (parent, tag, attributes) => {
626
653
  default: return;
627
654
  }
628
655
  };
629
- const openTmxPropertyChild = (parent, tag, attributes) => {
630
- if (tag !== "property") return void 0;
631
- const target = {
632
- ...readTmxAttributes(attributes, propertyAttributeKindMap),
633
- value: ""
634
- };
635
- parent.target.push(target);
636
- return {
637
- propertyType: parsePropertyType(attributes.type),
638
- tag,
639
- target,
640
- text: "",
641
- valueAttribute: attributes.value
642
- };
656
+ // src/services/openTmxFrame.ts
657
+ const animationFrameAttributeKindMap = {
658
+ duration: "requiredNumber",
659
+ tileid: "requiredNumber"
660
+ };
661
+ const exportAttributeKindMap = {
662
+ format: "requiredString",
663
+ target: "requiredString"
643
664
  };
644
665
  const openTmxFrame = (parent, name, attributes, context) => {
645
666
  if (parent === void 0) return openTmxRoot(name, attributes, context);
@@ -678,9 +699,10 @@ const openTmxFrame = (parent, name, attributes, context) => {
678
699
  default: return;
679
700
  }
680
701
  };
681
- // src/services/readTmxDocument.ts
682
- const BYTE_ORDER_MARK_REGEX = /^\uFEFF/u;
702
+ // src/services/toTmxAttributes.ts
683
703
  const toTmxAttributes = (attributes) => Object.fromEntries(Object.entries(attributes).map(([name, attribute]) => [name, typeof attribute === "string" ? attribute : attribute.value]));
704
+ // src/services/readTmxDocument.ts
705
+ const BYTE_ORDER_MARK_REGEX = /^/u;
684
706
  const readTmxDocument = (xmlString, context) => {
685
707
  const parser = sax.parser(true, {
686
708
  normalize: false,
@@ -733,4 +755,4 @@ const parseTileset = (xmlString) => getResult(() => readTmxDocument(xmlString, {
733
755
  if (root?.tag !== "tileset") throw new InvalidOperationError(Operation.Read, parseTileset.name, "has no embedded tileset root element");
734
756
  return root.target;
735
757
  }));
736
- export { Shape, TMXMapParsed, TMXParsed, parseTileset, parseTmx };
758
+ export { PropertyType, Shape, TMXMapParsed, TMXParsed, parseTileset, parseTmx };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "parse-tmx",
3
- "version": "4.3.3",
3
+ "version": "4.5.0",
4
4
  "description": "A parser for tmx.",
5
5
  "keywords": [
6
6
  "game-development",
@@ -47,18 +47,18 @@
47
47
  "lint": "eslint .",
48
48
  "lint:fix": "eslint --fix .",
49
49
  "test": "vitest",
50
- "typecheck": "tsc"
50
+ "typecheck": "tsrs --fast"
51
51
  },
52
52
  "dependencies": {
53
- "@esposter/shared": "4.3.3",
53
+ "@esposter/shared": "4.5.0",
54
54
  "neverthrow": "^8.2.0",
55
55
  "sax": "^1.6.1"
56
56
  },
57
57
  "devDependencies": {
58
- "@esposter/configuration": "4.3.3",
59
- "@esposter/shared-node": "4.3.3",
58
+ "@esposter/configuration": "4.5.0",
59
+ "@esposter/shared-node": "4.5.0",
60
60
  "@types/sax": "^1.2.7",
61
- "tsdown": "^0.23.1"
61
+ "tsdown": "^0.23.5"
62
62
  },
63
- "gitHead": "955bbc130ad9b28877fd4ae8efc73a045141ce01"
63
+ "gitHead": "e30c001f3b4b15693e75fb53092d62a97ade4d2b"
64
64
  }