@supalive/codegen 1.7.0 → 1.8.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-DM44BrYS.js";
2
+ import { n as generateToDisk, r as readEntryConfig } from "./src-BRGy8CeY.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-DM44BrYS.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-BRGy8CeY.js";
2
2
  export { SchemaEnumRegistry, emit, extractClient, generate, generateToDisk, loadSchemaEnums, readEntryConfig, resolveConfig };
@@ -92,12 +92,91 @@ function followToCall(node, depth) {
92
92
  if (!node || depth > 8) return void 0;
93
93
  if (Node.isCallExpression(node)) return node;
94
94
  if (Node.isIdentifier(node)) {
95
- for (const def of node.getDefinitionNodes()) if (Node.isVariableDeclaration(def)) {
96
- const found = followToCall(def.getInitializer(), depth + 1);
95
+ for (const def of node.getDefinitionNodes()) {
96
+ const init = definitionInitializer(def);
97
+ if (init) {
98
+ const found = followToCall(init, depth + 1);
99
+ if (found) return found;
100
+ }
101
+ }
102
+ const imported = followImport(node);
103
+ if (imported) {
104
+ const found = followToCall(imported, depth + 1);
97
105
  if (found) return found;
98
106
  }
99
107
  }
100
108
  }
109
+ /**
110
+ * Given an Identifier that may be an import binding, follow through the import
111
+ * chain to the actual exported declaration's initializer node.
112
+ *
113
+ * Handles: `import { listItems } from "./procs.js"` → the exported VariableDeclaration
114
+ * `import { x } from "./reexport.js"` where that file re-exports → recursive follow
115
+ */
116
+ function followImport(id) {
117
+ const sourceFile = id.getSourceFile();
118
+ const name = id.getText();
119
+ for (const imp of sourceFile.getImportDeclarations()) {
120
+ const specifiers = imp.getNamedImports();
121
+ for (const spec of specifiers) {
122
+ if (spec.getName() !== name) continue;
123
+ const resolved = imp.getModuleSpecifierSourceFile();
124
+ if (!resolved) continue;
125
+ const exportName = spec.getNameNode().getText();
126
+ const exported = resolved.getExportedDeclarations().get(exportName);
127
+ if (!exported || exported.length === 0) continue;
128
+ const decl = exported[0];
129
+ if (Node.isExportSpecifier(decl)) {
130
+ const targets = decl.getLocalTargetDeclarations();
131
+ if (targets.length > 0) return definitionInitializer(targets[0]);
132
+ continue;
133
+ }
134
+ return definitionInitializer(decl);
135
+ }
136
+ }
137
+ for (const exp of sourceFile.getExportDeclarations()) {
138
+ const specifiers = exp.getNamedExports();
139
+ for (const spec of specifiers) {
140
+ if (spec.getName() !== name) continue;
141
+ const resolved = exp.getModuleSpecifierSourceFile();
142
+ if (!resolved) continue;
143
+ const exportName = spec.getNameNode().getText();
144
+ const exported = resolved.getExportedDeclarations().get(exportName);
145
+ if (!exported || exported.length === 0) continue;
146
+ const decl = exported[0];
147
+ if (Node.isExportSpecifier(decl)) {
148
+ const targets = decl.getLocalTargetDeclarations();
149
+ if (targets.length > 0) return definitionInitializer(targets[0]);
150
+ continue;
151
+ }
152
+ return definitionInitializer(decl);
153
+ }
154
+ }
155
+ }
156
+ /** Extract the initializer expression from a definition node, unwrapping any
157
+ * container nodes (VariableStatement, ExportNamedDeclaration, etc.) that
158
+ * ts-morph may return for cross-file imports. */
159
+ function definitionInitializer(def) {
160
+ if (Node.isVariableDeclaration(def)) return def.getInitializer();
161
+ if (Node.isVariableStatement(def)) for (const d of def.getDeclarationList().getDeclarations()) {
162
+ const init = d.getInitializer();
163
+ if (init) return init;
164
+ }
165
+ for (const child of def.getChildren()) {
166
+ const init = definitionInitializer(child);
167
+ if (init) return init;
168
+ }
169
+ }
170
+ /** Resolve an Identifier to its initializer, following definitions across files. */
171
+ function resolveIdentifierInit(id) {
172
+ if (!Node.isIdentifier(id)) return void 0;
173
+ for (const def of id.getDefinitionNodes()) {
174
+ const init = definitionInitializer(def);
175
+ if (init) return init;
176
+ }
177
+ const imported = followImport(id);
178
+ if (imported) return imported;
179
+ }
101
180
  /** From a `query({ args: z.object({...}), handler })` call, get the schema. */
102
181
  function getArgsSchema(call) {
103
182
  if (!Node.isCallExpression(call)) return void 0;
@@ -110,7 +189,12 @@ function getArgsSchema(call) {
110
189
  /** Scan `z.object({ field: <expr>, ... })` for per-field metadata. */
111
190
  function scanObjectSchema(schema) {
112
191
  const map = /* @__PURE__ */ new Map();
113
- const objectLiteral = findZodObjectLiteral(schema);
192
+ let node = schema;
193
+ if (Node.isIdentifier(node)) {
194
+ const init = resolveIdentifierInit(node);
195
+ if (init) node = init;
196
+ }
197
+ const objectLiteral = findZodObjectLiteral(node);
114
198
  if (!objectLiteral || !Node.isObjectLiteralExpression(objectLiteral)) return map;
115
199
  for (const prop of objectLiteral.getProperties()) {
116
200
  if (!Node.isPropertyAssignment(prop)) continue;
@@ -428,6 +512,31 @@ var Extractor = class {
428
512
  return;
429
513
  }
430
514
  }
515
+ /**
516
+ * Read an `@type` JSDoc tag from a property and parse it into a DartType.
517
+ *
518
+ * Supported formats:
519
+ * - `/** @type StoreCardItem *​/` → model with that name
520
+ * - `/** @type List<StoreCardItem> *​/` → list with model element
521
+ * - `/** @type String *​/`, `/** @type int *​/`, etc. → primitive
522
+ */
523
+ readTypeOverride(prop) {
524
+ try {
525
+ const decls = prop.getDeclarations();
526
+ if (!decls || decls.length === 0) return null;
527
+ const node = decls[0];
528
+ if (!Node.isJSDocable(node)) return null;
529
+ const jsDocs = node.getJsDocs();
530
+ if (jsDocs.length === 0) return null;
531
+ const typeTag = jsDocs[jsDocs.length - 1].getTags().find((t) => t.getTagName() === "type");
532
+ if (!typeTag) return null;
533
+ const raw = typeTag.compilerNode.typeExpression?.getText?.()?.trim();
534
+ if (!raw) return null;
535
+ return parseTypeOverride(raw);
536
+ } catch {
537
+ return null;
538
+ }
539
+ }
431
540
  models() {
432
541
  this.finalizeEnums();
433
542
  return [...this.modelRegistry.values()];
@@ -544,11 +653,15 @@ var Extractor = class {
544
653
  const { core, hasNull, hasUndefined } = splitNullish(propType);
545
654
  const optional = declaredOptional || hasUndefined;
546
655
  const nullable = hasNull;
656
+ const typeOverride = this.readTypeOverride(prop);
547
657
  let dartType;
548
- if (core.length === 0) dartType = { kind: "dynamic" };
549
- else if (core.length === 1) dartType = this.walkSingle(core[0], name + pascal(jsonKey));
550
- else dartType = this.walkUnion(core, {
551
- hint: name + pascal(jsonKey),
658
+ if (typeOverride) dartType = typeOverride;
659
+ else if (core.length === 0) dartType = { kind: "dynamic" };
660
+ else if (core.length === 1) {
661
+ const fieldHint = name.replace(/Result$/, "") + pascal(jsonKey);
662
+ dartType = this.walkSingle(core[0], fieldHint);
663
+ } else dartType = this.walkUnion(core, {
664
+ hint: name.replace(/Result$/, "") + pascal(jsonKey),
552
665
  stem: cleanStem(name),
553
666
  field: jsonKey
554
667
  });
@@ -832,6 +945,40 @@ function cleanStem(modelName) {
832
945
  }
833
946
  return singularizeWord(s);
834
947
  }
948
+ const PRIMITIVE_MAP = {
949
+ string: "string",
950
+ int: "int",
951
+ double: "double",
952
+ bool: "bool",
953
+ bigint: "bigint",
954
+ bytes: "bytes",
955
+ datetime: "datetime",
956
+ dynamic: "dynamic"
957
+ };
958
+ /**
959
+ * Parse an `@type` override value into a DartType.
960
+ *
961
+ * - `"StoreCardItem"` → `{ kind: "model", name: "StoreCardItem" }`
962
+ * - `"List<StoreCardItem>"` → `{ kind: "list", element: { kind: "model", name: "StoreCardItem" } }`
963
+ * - `"String"` → `{ kind: "string" }`
964
+ * - `"int"` → `{ kind: "int" }`
965
+ */
966
+ function parseTypeOverride(raw) {
967
+ const listMatch = raw.match(/^List<(.+)>$/);
968
+ if (listMatch) {
969
+ const innerType = parseTypeOverride(listMatch[1].trim());
970
+ return innerType ? {
971
+ kind: "list",
972
+ element: innerType
973
+ } : null;
974
+ }
975
+ const prim = PRIMITIVE_MAP[raw.toLowerCase()];
976
+ if (prim) return { kind: prim };
977
+ return {
978
+ kind: "model",
979
+ name: raw
980
+ };
981
+ }
835
982
  /** The bare candidate name for a structural (non-schema) enum group. */
836
983
  function fallbackBase(g) {
837
984
  return pascal(g.field ?? g.hint);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supalive/codegen",
3
- "version": "1.7.0",
3
+ "version": "1.8.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",