s2cfgtojson 3.0.5 → 3.2.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/Struct.mts +82 -8
- package/Struct.test.mts +62 -4
- package/package.json +1 -1
- package/types.mts +12 -2
package/Struct.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from "./types.mts";
|
|
2
2
|
export * from "./enums.mts";
|
|
3
|
-
import { DefaultEntries } from "./types.mts";
|
|
3
|
+
import { DefaultEntries, Internal } from "./types.mts";
|
|
4
4
|
|
|
5
5
|
const TAB = " ";
|
|
6
6
|
const WILDCARD = "_wildcard";
|
|
@@ -11,6 +11,17 @@ const KEYWORDS = [
|
|
|
11
11
|
"bpatch", // allows patching only specific keys
|
|
12
12
|
];
|
|
13
13
|
const REMOVE_NODE = "removenode";
|
|
14
|
+
const INTERNAL_PROPS = new Set([
|
|
15
|
+
"__internal__",
|
|
16
|
+
"fork",
|
|
17
|
+
"removeNode",
|
|
18
|
+
"addNode",
|
|
19
|
+
"clone",
|
|
20
|
+
"forEach",
|
|
21
|
+
"filter",
|
|
22
|
+
"map",
|
|
23
|
+
"toString",
|
|
24
|
+
]);
|
|
14
25
|
|
|
15
26
|
/**
|
|
16
27
|
* This file is part of the Stalker 2 Modding Tools project.
|
|
@@ -62,31 +73,94 @@ export class Struct {
|
|
|
62
73
|
return this;
|
|
63
74
|
}
|
|
64
75
|
|
|
76
|
+
addNode(value: any, key?: string | number): this {
|
|
77
|
+
if (this.__internal__.isArray !== true) {
|
|
78
|
+
throw new Error("Cannot add node to non-array struct.");
|
|
79
|
+
}
|
|
80
|
+
if (key === undefined) {
|
|
81
|
+
const nextIndex = Object.keys(this)
|
|
82
|
+
.map((k) => parseInt(k))
|
|
83
|
+
.filter((k) => !isNaN(k))
|
|
84
|
+
.sort((a, b) => a - b)
|
|
85
|
+
.pop();
|
|
86
|
+
this[nextIndex !== undefined ? nextIndex + 1 : 0] = value;
|
|
87
|
+
} else {
|
|
88
|
+
this[key] = value;
|
|
89
|
+
}
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
65
93
|
clone() {
|
|
66
94
|
return Struct.fromString(this.toString())[0] as this;
|
|
67
95
|
}
|
|
68
96
|
|
|
97
|
+
entries(): [keyof this, (typeof this)[keyof this]][] {
|
|
98
|
+
return Object.entries(this).filter(([key]) => !INTERNAL_PROPS.has(key)) as [
|
|
99
|
+
keyof this,
|
|
100
|
+
(typeof this)[keyof this],
|
|
101
|
+
][];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
forEach<K extends Exclude<keyof this, Internal>, V extends (typeof this)[K]>(
|
|
105
|
+
callback: ([key, value]: [K, V]) => void,
|
|
106
|
+
): void {
|
|
107
|
+
this.entries().forEach(([key, value]) => callback([key as K, value as V]));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Filters the struct entries based on a callback function. Returns a copy.
|
|
112
|
+
* @param callback
|
|
113
|
+
*/
|
|
114
|
+
filter<K extends Exclude<keyof this, Internal>, V extends (typeof this)[K]>(
|
|
115
|
+
callback: ([key, value]: [K, V]) => boolean,
|
|
116
|
+
): Partial<this> & Struct {
|
|
117
|
+
const clone = this.clone();
|
|
118
|
+
clone.entries().forEach(([key, value]) => {
|
|
119
|
+
if (!callback([key as K, value as V])) {
|
|
120
|
+
delete clone[key];
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
return clone;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Maps the struct entries based on a callback function. Returns a copy.
|
|
128
|
+
* @param callback
|
|
129
|
+
*/
|
|
130
|
+
map<K extends Exclude<keyof this, Internal>, V extends (typeof this)[K]>(
|
|
131
|
+
callback: ([key, value]: [K, V]) => V,
|
|
132
|
+
): this {
|
|
133
|
+
const clone = this.clone();
|
|
134
|
+
clone.entries().forEach(([key, value]) => {
|
|
135
|
+
clone[key] = callback([key as K, value as V]);
|
|
136
|
+
});
|
|
137
|
+
return clone;
|
|
138
|
+
}
|
|
139
|
+
|
|
69
140
|
toString(): string {
|
|
70
141
|
if (!(this.__internal__ instanceof Refs)) {
|
|
71
142
|
this.__internal__ = new Refs(this.__internal__);
|
|
72
143
|
}
|
|
73
|
-
const { __internal__: internal, ...entries } = this;
|
|
74
144
|
|
|
75
|
-
let text: string =
|
|
145
|
+
let text: string = this.__internal__.rawName
|
|
146
|
+
? `${this.__internal__.rawName} : `
|
|
147
|
+
: "";
|
|
76
148
|
text += "struct.begin";
|
|
77
149
|
|
|
78
|
-
const refs =
|
|
150
|
+
const refs = this.__internal__.toString();
|
|
79
151
|
if (refs) {
|
|
80
152
|
text += ` {${refs}}`;
|
|
81
153
|
}
|
|
82
154
|
|
|
83
155
|
text += "\n";
|
|
84
156
|
// Add all keys
|
|
85
|
-
text += Object.entries(
|
|
157
|
+
text += Object.entries(this)
|
|
158
|
+
.filter(([key]) => !INTERNAL_PROPS.has(key))
|
|
86
159
|
.map(([key, value]) => {
|
|
87
160
|
const nameAlreadyRendered =
|
|
88
161
|
value instanceof Struct && value.__internal__.rawName;
|
|
89
|
-
const useAsterisk =
|
|
162
|
+
const useAsterisk =
|
|
163
|
+
this.__internal__.isArray && this.__internal__.useAsterisk;
|
|
90
164
|
let keyOrIndex = "";
|
|
91
165
|
let equalsOrColon = "";
|
|
92
166
|
let spaceOrNoSpace = "";
|
|
@@ -117,7 +191,7 @@ export class Refs implements DefaultEntries {
|
|
|
117
191
|
isArray?: boolean;
|
|
118
192
|
useAsterisk?: boolean;
|
|
119
193
|
|
|
120
|
-
constructor(ref?: string |
|
|
194
|
+
constructor(ref?: string | Refs) {
|
|
121
195
|
if (typeof ref === "string") {
|
|
122
196
|
ref
|
|
123
197
|
.split(";")
|
|
@@ -151,7 +225,7 @@ export class Refs implements DefaultEntries {
|
|
|
151
225
|
}
|
|
152
226
|
|
|
153
227
|
const structHeadRegex = new RegExp(
|
|
154
|
-
|
|
228
|
+
`^\s*(.*)\\s*:\\s*struct\\.begin\\s*({\\s*((${KEYWORDS.join("|")})\\s*(=.+)?)\\s*})?`,
|
|
155
229
|
);
|
|
156
230
|
|
|
157
231
|
function parseHead(line: string, index: number): Struct {
|
package/Struct.test.mts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
pad,
|
|
6
6
|
Struct,
|
|
7
7
|
ArmorPrototype,
|
|
8
|
+
Refs,
|
|
8
9
|
} from "./Struct.mjs";
|
|
9
10
|
import fs from "node:fs";
|
|
10
11
|
|
|
@@ -26,15 +27,23 @@ class TradePrototype extends Struct {
|
|
|
26
27
|
TradeGenerators = new TradeGenerators();
|
|
27
28
|
}
|
|
28
29
|
class TradeGenerators extends Struct {
|
|
29
|
-
|
|
30
|
+
__internal__ = new Refs({
|
|
31
|
+
isArray: true,
|
|
32
|
+
useAsterisk: true,
|
|
33
|
+
});
|
|
34
|
+
"0" = new TradeGenerator();
|
|
30
35
|
}
|
|
31
36
|
class TradeGenerator extends Struct {
|
|
32
37
|
BuyLimitations = new BuyLimitations();
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
class BuyLimitations extends Struct {
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
__internal__ = new Refs({
|
|
42
|
+
rawName: "BuyLimitations",
|
|
43
|
+
isArray: true,
|
|
44
|
+
});
|
|
45
|
+
"0" = "EItemType::Weapon";
|
|
46
|
+
"1" = "EItemType::Armor";
|
|
38
47
|
}
|
|
39
48
|
|
|
40
49
|
describe("Struct", () => {
|
|
@@ -236,6 +245,55 @@ struct.end`;
|
|
|
236
245
|
});
|
|
237
246
|
});
|
|
238
247
|
|
|
248
|
+
describe("addNode", () => {
|
|
249
|
+
test("1", () => {
|
|
250
|
+
const a = new TradePrototype().fork(true);
|
|
251
|
+
expect(a.TradeGenerators[0].BuyLimitations[0]).toBe("EItemType::Weapon");
|
|
252
|
+
expect(a.TradeGenerators[0].BuyLimitations[1]).toBe("EItemType::Armor");
|
|
253
|
+
a.TradeGenerators[0].BuyLimitations.addNode("EItemType::Artifact");
|
|
254
|
+
expect(a.TradeGenerators[0].BuyLimitations[2]).toBe(
|
|
255
|
+
"EItemType::Artifact",
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
describe("forEach", () => {
|
|
261
|
+
test("1", () => {
|
|
262
|
+
const a = new TradePrototype().fork(true);
|
|
263
|
+
expect(a.TradeGenerators[0].BuyLimitations[0]).toBe("EItemType::Weapon");
|
|
264
|
+
expect(a.TradeGenerators[0].BuyLimitations[1]).toBe("EItemType::Armor");
|
|
265
|
+
a.TradeGenerators[0].BuyLimitations.forEach(([k]) => {
|
|
266
|
+
a.TradeGenerators[0].BuyLimitations[k] = "forEach";
|
|
267
|
+
});
|
|
268
|
+
expect(a.TradeGenerators[0].BuyLimitations[0]).toBe("forEach");
|
|
269
|
+
expect(a.TradeGenerators[0].BuyLimitations[1]).toBe("forEach");
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe("filter", () => {
|
|
274
|
+
test("1", () => {
|
|
275
|
+
const a = new TradePrototype().fork(true);
|
|
276
|
+
expect(a.TradeGenerators[0].BuyLimitations[0]).toBe("EItemType::Weapon");
|
|
277
|
+
expect(a.TradeGenerators[0].BuyLimitations[1]).toBe("EItemType::Armor");
|
|
278
|
+
const b = a.TradeGenerators[0].BuyLimitations.filter(([k]) => k === "0");
|
|
279
|
+
expect(b[0]).toBe("EItemType::Weapon");
|
|
280
|
+
expect(b[1]).toBeUndefined();
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
describe("map", () => {
|
|
285
|
+
test("1", () => {
|
|
286
|
+
const a = new TradePrototype();
|
|
287
|
+
expect(a.TradeGenerators[0].BuyLimitations[0]).toBe("EItemType::Weapon");
|
|
288
|
+
expect(a.TradeGenerators[0].BuyLimitations[1]).toBe("EItemType::Armor");
|
|
289
|
+
const b = a.TradeGenerators[0].BuyLimitations.map(
|
|
290
|
+
([k, v]) => `${v}-mapped-${k}`,
|
|
291
|
+
);
|
|
292
|
+
expect(b[0]).toBe("EItemType::Weapon-mapped-0");
|
|
293
|
+
expect(b[1]).toBe("EItemType::Armor-mapped-1");
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
239
297
|
describe("clone", () => {
|
|
240
298
|
test("1", () => {
|
|
241
299
|
const a = new TradePrototype();
|
|
@@ -251,7 +309,7 @@ struct.end`;
|
|
|
251
309
|
const a = new TradePrototype().fork(true);
|
|
252
310
|
expect(a.TradeGenerators[0].BuyLimitations[0]).toBe("EItemType::Weapon");
|
|
253
311
|
expect(a.TradeGenerators[0].BuyLimitations[1]).toBe("EItemType::Armor");
|
|
254
|
-
a.TradeGenerators[0].BuyLimitations.removeNode(0);
|
|
312
|
+
a.TradeGenerators[0].BuyLimitations.removeNode("0");
|
|
255
313
|
expect(a.TradeGenerators[0].BuyLimitations[0]).toBe("removenode");
|
|
256
314
|
});
|
|
257
315
|
});
|
package/package.json
CHANGED
package/types.mts
CHANGED
|
@@ -68,7 +68,17 @@ import {
|
|
|
68
68
|
} from "./enums.mjs";
|
|
69
69
|
import { Struct } from "./Struct.mjs";
|
|
70
70
|
|
|
71
|
-
export type Internal =
|
|
71
|
+
export type Internal =
|
|
72
|
+
| "__internal__"
|
|
73
|
+
| "fork"
|
|
74
|
+
| "removeNode"
|
|
75
|
+
| "addNode"
|
|
76
|
+
| "clone"
|
|
77
|
+
| "forEach"
|
|
78
|
+
| "filter"
|
|
79
|
+
| "map"
|
|
80
|
+
| "entries"
|
|
81
|
+
| "toString";
|
|
72
82
|
|
|
73
83
|
export type DeeplyPartial<T> = {
|
|
74
84
|
[P in keyof T]?: T[P] extends Array<infer U>
|
|
@@ -81,7 +91,7 @@ export type DeeplyPartial<T> = {
|
|
|
81
91
|
};
|
|
82
92
|
|
|
83
93
|
export type DeeplyPartialStruct<T> = DeeplyPartial<
|
|
84
|
-
T extends Struct ?
|
|
94
|
+
T extends Struct ? Exclude<T, Internal> : T
|
|
85
95
|
>;
|
|
86
96
|
|
|
87
97
|
export interface DefaultEntries {
|