@s2script/sdk 0.10.1 → 0.10.2
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/cli.js +93 -8
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4585,6 +4585,37 @@ import { readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from
|
|
|
4585
4585
|
import { join as join13 } from "node:path";
|
|
4586
4586
|
|
|
4587
4587
|
// src/schemagen/model.ts
|
|
4588
|
+
function enumMemberNames(enumName, members) {
|
|
4589
|
+
const raw = {};
|
|
4590
|
+
for (const m of members) raw[m] = m;
|
|
4591
|
+
if (members.length === 0) return raw;
|
|
4592
|
+
const stem = enumName.replace(/_t$/, "").replace(/[^A-Za-z0-9]/g, "").toUpperCase();
|
|
4593
|
+
if (stem === "") return raw;
|
|
4594
|
+
const stripped = {};
|
|
4595
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4596
|
+
for (const m of members) {
|
|
4597
|
+
const up = m.toUpperCase();
|
|
4598
|
+
let rest = null;
|
|
4599
|
+
for (const sep3 of ["_", ""]) {
|
|
4600
|
+
const pfx = stem + sep3;
|
|
4601
|
+
if (up.startsWith(pfx) && m.length > pfx.length) {
|
|
4602
|
+
rest = m.slice(pfx.length);
|
|
4603
|
+
break;
|
|
4604
|
+
}
|
|
4605
|
+
}
|
|
4606
|
+
if (rest === null && /^k[A-Z]/.test(m)) rest = m.slice(1);
|
|
4607
|
+
if (rest === null) return raw;
|
|
4608
|
+
const name = /^[0-9]/.test(rest) ? `_${rest}` : rest;
|
|
4609
|
+
if (seen.has(name)) return raw;
|
|
4610
|
+
seen.add(name);
|
|
4611
|
+
stripped[m] = name;
|
|
4612
|
+
}
|
|
4613
|
+
return stripped;
|
|
4614
|
+
}
|
|
4615
|
+
function enumTsName(schemaName) {
|
|
4616
|
+
const id = schemaName.replace(/[^A-Za-z0-9_$]/g, "_");
|
|
4617
|
+
return /^[0-9]/.test(id) ? `_${id}` : id;
|
|
4618
|
+
}
|
|
4588
4619
|
var READ = {
|
|
4589
4620
|
f32: "readFloat32",
|
|
4590
4621
|
bool: "readBool",
|
|
@@ -4683,7 +4714,7 @@ function classifyField(type, embeddable = /* @__PURE__ */ new Set()) {
|
|
|
4683
4714
|
}
|
|
4684
4715
|
if (type.kind === "enum") {
|
|
4685
4716
|
const w = ENUM_WIDTH[type.size ?? 0];
|
|
4686
|
-
if (w) return { accessorKind: w, writable: WRITE[w] !== void 0 };
|
|
4717
|
+
if (w) return { accessorKind: w, writable: WRITE[w] !== void 0, enumType: type.name };
|
|
4687
4718
|
return { skip: `enum '${type.name ?? ""}' byte width not stated by the schema` };
|
|
4688
4719
|
}
|
|
4689
4720
|
if (type.kind === "class") {
|
|
@@ -4705,7 +4736,7 @@ function flattenedFields(model, className) {
|
|
|
4705
4736
|
}
|
|
4706
4737
|
return chain.flatMap((c2) => c2.ownFields);
|
|
4707
4738
|
}
|
|
4708
|
-
function buildModel(catalog, requested, embeddable = []) {
|
|
4739
|
+
function buildModel(catalog, requested, embeddable = [], enumCatalog = {}) {
|
|
4709
4740
|
for (const n3 of embeddable) if (!catalog[n3]) throw new Error(`gen-schema: embedded class '${n3}' is not in the catalog`);
|
|
4710
4741
|
const restrict = embeddable.length > 0 ? new Set(embeddable) : null;
|
|
4711
4742
|
const canEmbed = (n3) => !!catalog[n3] && transparentValue(catalog, n3) === null && (restrict === null || restrict.has(n3));
|
|
@@ -4751,7 +4782,7 @@ function buildModel(catalog, requested, embeddable = []) {
|
|
|
4751
4782
|
emb.push({ propName: idiomaticName(f.name), rawName: f.name, declaringClass: owner, embeddedClass: c2.embedded });
|
|
4752
4783
|
continue;
|
|
4753
4784
|
}
|
|
4754
|
-
out.push({ propName: idiomaticName(f.name), rawName: f.name, declaringClass: owner, accessorKind: c2.accessorKind, writable: c2.writable, strLen: c2.strLen, addOffset: c2.addOffset });
|
|
4785
|
+
out.push({ propName: idiomaticName(f.name), rawName: f.name, declaringClass: owner, accessorKind: c2.accessorKind, writable: c2.writable, strLen: c2.strLen, addOffset: c2.addOffset, enumType: c2.enumType });
|
|
4755
4786
|
}
|
|
4756
4787
|
};
|
|
4757
4788
|
const classes = ordered.map((className) => {
|
|
@@ -4819,12 +4850,42 @@ function buildModel(catalog, requested, embeddable = []) {
|
|
|
4819
4850
|
collisions.push(`${prop} <- ${renamed.sort().join(", ")}${soleWinner ? ` (kept by ${winnerCls})` : ""}`);
|
|
4820
4851
|
}
|
|
4821
4852
|
collisions.sort();
|
|
4822
|
-
|
|
4853
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
4854
|
+
const noteEnum = (f) => {
|
|
4855
|
+
if (f.type.kind === "enum" && f.type.name && enumCatalog[f.type.name]) referenced.add(f.type.name);
|
|
4856
|
+
};
|
|
4857
|
+
for (const cls of inClosure) for (const f of catalog[cls].fields) noteEnum(f);
|
|
4858
|
+
for (const e of embedded) for (const owner of new Set(e.fields.map((f) => f.declaringClass))) {
|
|
4859
|
+
for (const f of catalog[owner]?.fields ?? []) noteEnum(f);
|
|
4860
|
+
}
|
|
4861
|
+
const byTsName = /* @__PURE__ */ new Map();
|
|
4862
|
+
for (const n3 of referenced) {
|
|
4863
|
+
const id = enumTsName(n3);
|
|
4864
|
+
(byTsName.get(id) ?? byTsName.set(id, []).get(id)).push(n3);
|
|
4865
|
+
}
|
|
4866
|
+
const ambiguous = /* @__PURE__ */ new Set();
|
|
4867
|
+
for (const [, group] of byTsName) if (group.length > 1) for (const n3 of group) ambiguous.add(n3);
|
|
4868
|
+
const enums = [...referenced].filter((n3) => !ambiguous.has(n3)).sort().map((className) => {
|
|
4869
|
+
const values = enumCatalog[className].values;
|
|
4870
|
+
const names = enumMemberNames(className, Object.keys(values));
|
|
4871
|
+
return {
|
|
4872
|
+
className,
|
|
4873
|
+
tsName: enumTsName(className),
|
|
4874
|
+
members: Object.keys(values).sort().map((raw) => ({ name: names[raw], raw, value: values[raw] }))
|
|
4875
|
+
};
|
|
4876
|
+
});
|
|
4877
|
+
for (const n3 of [...ambiguous].sort()) collisions.push(`enum ${n3} -> ${enumTsName(n3)} (ambiguous; emitted as number)`);
|
|
4878
|
+
return { classes, embedded, enums, collisions };
|
|
4823
4879
|
}
|
|
4824
4880
|
|
|
4825
4881
|
// src/schemagen/emit-dts.ts
|
|
4826
4882
|
var HEADER = "// GENERATED by `s2script gen-schema` from schema-catalog.json \u2014 DO NOT EDIT.";
|
|
4827
4883
|
function emitDts(model) {
|
|
4884
|
+
const enumTs = new Map(model.enums.map((e) => [e.className, e.tsName]));
|
|
4885
|
+
const tsType = (f) => {
|
|
4886
|
+
const id = f.enumType ? enumTs.get(f.enumType) : void 0;
|
|
4887
|
+
return id ? `${id} | null` : TSTYPE[f.accessorKind];
|
|
4888
|
+
};
|
|
4828
4889
|
const names = new Set(model.classes.map((c2) => c2.className));
|
|
4829
4890
|
const out = [HEADER, 'import type { EntityRef } from "@s2script/sdk/entity";'];
|
|
4830
4891
|
const vecClasses = /* @__PURE__ */ new Set();
|
|
@@ -4838,10 +4899,21 @@ function emitDts(model) {
|
|
|
4838
4899
|
}
|
|
4839
4900
|
if (vecClasses.size) out.push(`import type { ${[...vecClasses].sort().join(", ")} } from "@s2script/sdk/math";`);
|
|
4840
4901
|
out.push("");
|
|
4902
|
+
for (const e of model.enums) {
|
|
4903
|
+
out.push(`export declare const ${e.tsName}: {`);
|
|
4904
|
+
for (const m of e.members) {
|
|
4905
|
+
const doc = m.name === m.raw ? "" : ` /** \`${m.raw}\` */
|
|
4906
|
+
`;
|
|
4907
|
+
out.push(`${doc} readonly ${m.name}: ${m.value};`);
|
|
4908
|
+
}
|
|
4909
|
+
out.push("};");
|
|
4910
|
+
if (e.tsName !== e.className) out.push(`/** Schema enum \`${e.className}\`. */`);
|
|
4911
|
+
out.push(`export type ${e.tsName} = (typeof ${e.tsName})[keyof typeof ${e.tsName}];`, "");
|
|
4912
|
+
}
|
|
4841
4913
|
for (const e of model.embedded) {
|
|
4842
4914
|
out.push(`export interface ${e.className} {`);
|
|
4843
4915
|
for (const f of e.fields) {
|
|
4844
|
-
out.push(` ${f.writable ? "" : "readonly "}${f.propName}: ${
|
|
4916
|
+
out.push(` ${f.writable ? "" : "readonly "}${f.propName}: ${tsType(f)};`);
|
|
4845
4917
|
}
|
|
4846
4918
|
for (const f of e.embeddedFields) out.push(` readonly ${f.propName}: ${f.embeddedClass};`);
|
|
4847
4919
|
out.push("}", "");
|
|
@@ -4851,7 +4923,7 @@ function emitDts(model) {
|
|
|
4851
4923
|
out.push(`export interface ${c2.className}${ext} {`);
|
|
4852
4924
|
for (const f of c2.ownFields) {
|
|
4853
4925
|
const ro = f.writable ? "" : "readonly ";
|
|
4854
|
-
out.push(` ${ro}${f.propName}: ${
|
|
4926
|
+
out.push(` ${ro}${f.propName}: ${tsType(f)};`);
|
|
4855
4927
|
}
|
|
4856
4928
|
for (const f of c2.embeddedFields) out.push(` readonly ${f.propName}: ${f.embeddedClass};`);
|
|
4857
4929
|
out.push("}", "");
|
|
@@ -4947,6 +5019,13 @@ function emitJs(model) {
|
|
|
4947
5019
|
out.push(" var P = {");
|
|
4948
5020
|
for (const c2 of model.classes) if (c2.parent) out.push(` ${S(c2.className)}: ${S(c2.parent)},`);
|
|
4949
5021
|
out.push(" };");
|
|
5022
|
+
if (model.enums.length > 0) {
|
|
5023
|
+
out.push(" var E_CONST = {};");
|
|
5024
|
+
for (const e of model.enums) {
|
|
5025
|
+
const body = e.members.map((m) => `${S(m.name)}: ${m.value}`).join(", ");
|
|
5026
|
+
out.push(` E_CONST[${S(e.tsName)}] = Object.freeze({ ${body} });`);
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
4950
5029
|
out.push(
|
|
4951
5030
|
" // Walk the ancestor chain rather than storing every inherited accessor per class. Flattening",
|
|
4952
5031
|
" // into each table duplicated the whole chain 3.77x on the CS2 class set and grew as",
|
|
@@ -4972,7 +5051,7 @@ function emitJs(model) {
|
|
|
4972
5051
|
" }",
|
|
4973
5052
|
" globalThis.__s2pkg_cs2_schema = { applyAccessors: applyAccessors, wrap: wrap };",
|
|
4974
5053
|
" // Merge (not overwrite): this prelude runs FIRST, and pawn.js merges onto whatever it finds.",
|
|
4975
|
-
" globalThis.__s2pkg_cs2 = Object.assign({}, globalThis.__s2pkg_cs2, { wrapEntity: wrap });",
|
|
5054
|
+
" globalThis.__s2pkg_cs2 = Object.assign({}, globalThis.__s2pkg_cs2, { wrapEntity: wrap }" + (model.enums.length > 0 ? ", E_CONST);" : ");"),
|
|
4976
5055
|
"})();"
|
|
4977
5056
|
);
|
|
4978
5057
|
return out.join("\n");
|
|
@@ -4980,6 +5059,7 @@ function emitJs(model) {
|
|
|
4980
5059
|
|
|
4981
5060
|
// src/schemagen/gen.ts
|
|
4982
5061
|
var CATALOG_PATH = "games/cs2/gamedata/schema-catalog.json";
|
|
5062
|
+
var ENUMS_PATH = "games/cs2/gamedata/schema-enums.json";
|
|
4983
5063
|
var LIST_PATH = "games/cs2/codegen-classes.json";
|
|
4984
5064
|
var EMBED_PATH = "games/cs2/codegen-embedded.json";
|
|
4985
5065
|
var JS_OUT = "games/cs2/js/schema.generated.js";
|
|
@@ -4992,7 +5072,12 @@ function runGenSchema(repoRoot2, opts) {
|
|
|
4992
5072
|
embed = JSON.parse(readFileSync13(join13(repoRoot2, EMBED_PATH), "utf8"));
|
|
4993
5073
|
} catch {
|
|
4994
5074
|
}
|
|
4995
|
-
|
|
5075
|
+
let enumCatalog = {};
|
|
5076
|
+
try {
|
|
5077
|
+
enumCatalog = JSON.parse(readFileSync13(join13(repoRoot2, ENUMS_PATH), "utf8"));
|
|
5078
|
+
} catch {
|
|
5079
|
+
}
|
|
5080
|
+
const model = buildModel(catalog, list, embed, enumCatalog);
|
|
4996
5081
|
const dts = emitDts(model);
|
|
4997
5082
|
const js = emitJs(model);
|
|
4998
5083
|
const fields = model.classes.reduce((n3, c2) => n3 + c2.ownFields.length, 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@s2script/sdk",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"license": "MIT OR Apache-2.0",
|
|
5
5
|
"description": "s2script SDK — the builtin capability types (imported as @s2script/sdk/entity, …) plus the `s2s` CLI for building Source 2 / CS2 plugins.",
|
|
6
6
|
"type": "module",
|