@supalive/codegen 1.22.1 → 1.23.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/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as generateToDisk, r as readEntryConfig } from "./src-Ckf5xah9.js";
2
+ import { n as generateToDisk, r as readEntryConfig } from "./src-Cmwpfczg.js";
3
3
  import path from "node:path";
4
4
  //#region src/cli.ts
5
5
  function parseArgs(argv) {
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as emit, c as loadSchemaEnums, i as resolveConfig, n as generateToDisk, o as extractClient, r as readEntryConfig, s as SchemaEnumRegistry, t as generate } from "./src-Ckf5xah9.js";
1
+ import { a as emit, c as loadSchemaEnums, i as resolveConfig, n as generateToDisk, o as extractClient, r as readEntryConfig, s as SchemaEnumRegistry, t as generate } from "./src-Cmwpfczg.js";
2
2
  export { SchemaEnumRegistry, emit, extractClient, generate, generateToDisk, loadSchemaEnums, readEntryConfig, resolveConfig };
@@ -358,7 +358,7 @@ function scanObjectSchema(schema) {
358
358
  }
359
359
  return map;
360
360
  }
361
- function topLevelZodMethods(node) {
361
+ function topLevelZodMethods(node, depth = 0) {
362
362
  const methods = /* @__PURE__ */ new Set();
363
363
  let cur = node;
364
364
  while (cur && Node.isCallExpression(cur)) {
@@ -368,8 +368,14 @@ function topLevelZodMethods(node) {
368
368
  cur = expr.getExpression();
369
369
  } else break;
370
370
  }
371
+ if (cur && Node.isIdentifier(cur) && depth < MAX_ALIAS_DEPTH) {
372
+ const init = resolveIdentifierInit(cur);
373
+ if (init) for (const m of topLevelZodMethods(init, depth + 1)) methods.add(m);
374
+ }
371
375
  return methods;
372
376
  }
377
+ /** Guards against a cyclic alias (`const A = B; const B = A;`) looping forever. */
378
+ const MAX_ALIAS_DEPTH = 8;
373
379
  function topLevelZodDescription(node) {
374
380
  let cur = node;
375
381
  while (cur && Node.isCallExpression(cur)) {
@@ -711,10 +717,47 @@ function extractClient(config, schemaEnums = SchemaEnumRegistry.empty()) {
711
717
  return {
712
718
  clientClassName: config.clientClassName,
713
719
  procedures,
714
- models: extractor.models(),
720
+ models: pruneUnreachableModels(procedures, extractor.models()),
715
721
  enums: extractor.enums()
716
722
  };
717
723
  }
724
+ /**
725
+ * Drop models nothing refers to.
726
+ *
727
+ * A result model is first registered under a name derived from the handler's TS
728
+ * return type, then RENAMED when the procedure's `returns` schema declares
729
+ * `.modelName("X")`. When a second procedure shares that same schema, the name
730
+ * is already taken: the reference correctly points at the registered `X`, but
731
+ * the structural model allocated for this procedure is left behind — emitting a
732
+ * near-identical `MyTimetableItem` beside the `TimetableSlotRow` everything
733
+ * actually uses.
734
+ *
735
+ * Pruning by reachability fixes that without having to decide, at rename time,
736
+ * whether a model is safe to delete — which is not knowable then, because a
737
+ * later procedure may still refer to it.
738
+ */
739
+ function pruneUnreachableModels(procedures, models) {
740
+ const byName = new Map(models.map((m) => [m.name, m]));
741
+ const reachable = /* @__PURE__ */ new Set();
742
+ const visitType = (t) => {
743
+ if (!t) return;
744
+ switch (t.kind) {
745
+ case "model":
746
+ if (reachable.has(t.name)) return;
747
+ reachable.add(t.name);
748
+ for (const f of byName.get(t.name)?.fields ?? []) visitType(f.type);
749
+ return;
750
+ case "list": return visitType(t.element);
751
+ case "map": return visitType(t.value);
752
+ default: return;
753
+ }
754
+ };
755
+ for (const p of procedures) {
756
+ visitType(p.args);
757
+ visitType(p.result);
758
+ }
759
+ return models.filter((m) => reachable.has(m.name));
760
+ }
718
761
  var Extractor = class {
719
762
  locationNode;
720
763
  config;
@@ -1392,6 +1435,7 @@ function stripNull(expr) {
1392
1435
  function emitModels(ir) {
1393
1436
  const out = [HEADER];
1394
1437
  if (modelsUseBytes(ir)) out.push("import 'dart:typed_data';\n");
1438
+ if (ir.enums.length > 0) out.push("import 'package:intl/intl.dart';");
1395
1439
  out.push("import 'package:supalive_client/supalive_client.dart';\n");
1396
1440
  for (const e of ir.enums) out.push(emitEnum(e));
1397
1441
  for (const m of ir.models) out.push(emitModel(m));
@@ -1423,6 +1467,18 @@ ${values};
1423
1467
  );
1424
1468
 
1425
1469
  String toJson() => wire;
1470
+
1471
+ /// This value in the reader's language, from the ARB key
1472
+ /// \`${e.name}_<constant>\` — e.g. \`"${e.name}_${e.values[0]?.dartName ?? "value"}": "…"\`.
1473
+ ///
1474
+ /// Lives on the enum so no app has to write a \`switch\` over it. Add the keys
1475
+ /// to your ARB files and every screen showing this enum is translated at once;
1476
+ /// an enum the server adds later shows up as a missing key rather than as a
1477
+ /// non-exhaustive switch nobody notices.
1478
+ ///
1479
+ /// Falls back to the constant name when the key is absent, so an untranslated
1480
+ /// value reads \`${e.values[0]?.dartName ?? "value"}\` rather than rendering blank.
1481
+ String get localizedName => Intl.message(name, name: '${e.name}_\$name');
1426
1482
  }
1427
1483
  `;
1428
1484
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supalive/codegen",
3
- "version": "1.22.1",
3
+ "version": "1.23.0",
4
4
  "description": "Generate type-safe client code (Dart, and more) from a Supalive TypeScript router.",
5
5
  "author": "Rebaz Raouf",
6
6
  "license": "MIT",