@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.
Files changed (2) hide show
  1. package/dist/cli.js +93 -8
  2. 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
- return { classes, embedded, collisions };
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}: ${TSTYPE[f.accessorKind]};`);
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}: ${TSTYPE[f.accessorKind]};`);
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
- const model = buildModel(catalog, list, embed);
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.1",
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",