@supalive/codegen 1.8.0 → 1.8.1
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 +1 -1
- package/dist/index.js +1 -1
- package/dist/{src-BRGy8CeY.js → src-CTW5fpJ-.js} +244 -10
- package/package.json +1 -1
package/dist/cli.js
CHANGED
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-
|
|
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-CTW5fpJ-.js";
|
|
2
2
|
export { SchemaEnumRegistry, emit, extractClient, generate, generateToDisk, loadSchemaEnums, readEntryConfig, resolveConfig };
|
|
@@ -39,6 +39,20 @@ function procedureDocText(prop) {
|
|
|
39
39
|
const doc = stmt ? jsDocText(stmt) : void 0;
|
|
40
40
|
if (doc) return doc;
|
|
41
41
|
}
|
|
42
|
+
for (const def of idNode.getDefinitionNodes()) if (Node.isImportSpecifier(def)) {
|
|
43
|
+
const sourceFile = def.getImportDeclaration().getModuleSpecifierSourceFile();
|
|
44
|
+
if (sourceFile) {
|
|
45
|
+
const exported = sourceFile.getExportedDeclarations().get(def.getName());
|
|
46
|
+
if (exported && exported.length > 0) {
|
|
47
|
+
const decl = exported[0];
|
|
48
|
+
if (Node.isVariableDeclaration(decl)) {
|
|
49
|
+
const stmt = decl.getVariableStatement();
|
|
50
|
+
const doc = stmt ? jsDocText(stmt) : void 0;
|
|
51
|
+
if (doc) return doc;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
42
56
|
}
|
|
43
57
|
}
|
|
44
58
|
/** The trimmed description of a node's last JSDoc block, if any. */
|
|
@@ -271,6 +285,79 @@ function findZodObjectLiteral(node) {
|
|
|
271
285
|
}
|
|
272
286
|
return cur && Node.isObjectLiteralExpression(cur) ? cur : void 0;
|
|
273
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Read `@genType` JSDoc tags from procedure declarations for return type overrides.
|
|
290
|
+
*
|
|
291
|
+
* Syntax:
|
|
292
|
+
* - Object shape: `/** @genType {{value: List<StoreCardItem>}} */` — overrides specific fields
|
|
293
|
+
* - Direct type: `/** @genType List<StoreCardItem> */` — overrides the whole return type
|
|
294
|
+
*
|
|
295
|
+
* Returns a map of procedure name → raw `@genType` value string.
|
|
296
|
+
*/
|
|
297
|
+
function scanProcedureTypeOverrides(routerDecl) {
|
|
298
|
+
const result = /* @__PURE__ */ new Map();
|
|
299
|
+
try {
|
|
300
|
+
const proceduresObj = getProceduresObjectLiteral(routerDecl);
|
|
301
|
+
if (!proceduresObj) return result;
|
|
302
|
+
for (const prop of proceduresObj.getProperties()) {
|
|
303
|
+
const key = getPropertyKey(prop);
|
|
304
|
+
if (!key) continue;
|
|
305
|
+
const typeValue = procedureTypeTag(prop);
|
|
306
|
+
if (typeValue) result.set(key, typeValue);
|
|
307
|
+
}
|
|
308
|
+
} catch {}
|
|
309
|
+
return result;
|
|
310
|
+
}
|
|
311
|
+
/** Read the `@genType` tag from a procedure declaration's JSDoc. */
|
|
312
|
+
function procedureTypeTag(prop) {
|
|
313
|
+
const tag = findTypeTagOnNode(prop);
|
|
314
|
+
if (tag) return tag;
|
|
315
|
+
let idNode;
|
|
316
|
+
if (Node.isShorthandPropertyAssignment(prop)) idNode = prop.getNameNode();
|
|
317
|
+
else if (Node.isPropertyAssignment(prop)) idNode = prop.getInitializer();
|
|
318
|
+
if (idNode && Node.isIdentifier(idNode)) {
|
|
319
|
+
for (const def of idNode.getDefinitionNodes()) if (Node.isVariableDeclaration(def)) {
|
|
320
|
+
const stmt = def.getVariableStatement();
|
|
321
|
+
if (stmt) {
|
|
322
|
+
const tag = findTypeTagOnNode(stmt);
|
|
323
|
+
if (tag) return tag;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
for (const def of idNode.getDefinitionNodes()) if (Node.isImportSpecifier(def)) {
|
|
327
|
+
const sourceFile = def.getImportDeclaration().getModuleSpecifierSourceFile();
|
|
328
|
+
if (sourceFile) {
|
|
329
|
+
const exported = sourceFile.getExportedDeclarations().get(def.getName());
|
|
330
|
+
if (exported && exported.length > 0) {
|
|
331
|
+
const decl = exported[0];
|
|
332
|
+
if (Node.isVariableDeclaration(decl)) {
|
|
333
|
+
const stmt = decl.getVariableStatement();
|
|
334
|
+
if (stmt) {
|
|
335
|
+
const tag = findTypeTagOnNode(stmt);
|
|
336
|
+
if (tag) return tag;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
/** Extract the `@genType` tag value from a JSDocable node, if present. */
|
|
345
|
+
function findTypeTagOnNode(node) {
|
|
346
|
+
if (!Node.isJSDocable(node)) return void 0;
|
|
347
|
+
const docs = node.getJsDocs();
|
|
348
|
+
if (docs.length === 0) return void 0;
|
|
349
|
+
const jd = docs[docs.length - 1];
|
|
350
|
+
for (const tag of jd.getTags()) if (tag.getTagName() === "genType") {
|
|
351
|
+
const text = tag.compilerNode.typeExpression?.getText?.()?.trim();
|
|
352
|
+
if (text) return text;
|
|
353
|
+
const comment = tag.getComment();
|
|
354
|
+
if (typeof comment === "string") return comment.trim();
|
|
355
|
+
if (Array.isArray(comment)) {
|
|
356
|
+
const joined = comment.map((c) => c.getText?.() ?? "").join("").trim();
|
|
357
|
+
if (joined) return joined;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
274
361
|
//#endregion
|
|
275
362
|
//#region src/names.ts
|
|
276
363
|
/** PascalCase an arbitrary string (splitting on any non-alphanumeric run). */
|
|
@@ -462,7 +549,7 @@ function extractClient(config, schemaEnums = SchemaEnumRegistry.empty()) {
|
|
|
462
549
|
const proceduresType = proceduresSym.getTypeAtLocation(routerDecl);
|
|
463
550
|
const argMeta = scanZodArgMeta(routerDecl);
|
|
464
551
|
const procDocs = scanProcedureDocs(routerDecl);
|
|
465
|
-
const extractor = new Extractor(routerDecl, config, argMeta, project.getTypeChecker(), schemaEnums);
|
|
552
|
+
const extractor = new Extractor(routerDecl, config, argMeta, scanProcedureTypeOverrides(routerDecl), project.getTypeChecker(), schemaEnums);
|
|
466
553
|
const procedures = [];
|
|
467
554
|
for (const procSym of proceduresType.getProperties()) {
|
|
468
555
|
const name = procSym.getName();
|
|
@@ -486,6 +573,7 @@ var Extractor = class {
|
|
|
486
573
|
locationNode;
|
|
487
574
|
config;
|
|
488
575
|
argMeta;
|
|
576
|
+
procTypeOverrides;
|
|
489
577
|
tc;
|
|
490
578
|
schemaEnums;
|
|
491
579
|
modelRegistry = /* @__PURE__ */ new Map();
|
|
@@ -495,10 +583,11 @@ var Extractor = class {
|
|
|
495
583
|
enumsFinalized = false;
|
|
496
584
|
nameCounts = /* @__PURE__ */ new Map();
|
|
497
585
|
visiting = /* @__PURE__ */ new Set();
|
|
498
|
-
constructor(locationNode, config, argMeta, tc, schemaEnums) {
|
|
586
|
+
constructor(locationNode, config, argMeta, procTypeOverrides, tc, schemaEnums) {
|
|
499
587
|
this.locationNode = locationNode;
|
|
500
588
|
this.config = config;
|
|
501
589
|
this.argMeta = argMeta;
|
|
590
|
+
this.procTypeOverrides = procTypeOverrides;
|
|
502
591
|
this.tc = tc;
|
|
503
592
|
this.schemaEnums = schemaEnums;
|
|
504
593
|
}
|
|
@@ -513,12 +602,12 @@ var Extractor = class {
|
|
|
513
602
|
}
|
|
514
603
|
}
|
|
515
604
|
/**
|
|
516
|
-
* Read an `@
|
|
605
|
+
* Read an `@genType` JSDoc tag from a property and parse it into a DartType.
|
|
517
606
|
*
|
|
518
607
|
* Supported formats:
|
|
519
|
-
* - `/** @
|
|
520
|
-
* - `/** @
|
|
521
|
-
* - `/** @
|
|
608
|
+
* - `/** @genType StoreCardItem */` → model with that name
|
|
609
|
+
* - `/** @genType List<StoreCardItem> */` → list with model element
|
|
610
|
+
* - `/** @genType String */`, `/** @genType int */`, etc. → primitive
|
|
522
611
|
*/
|
|
523
612
|
readTypeOverride(prop) {
|
|
524
613
|
try {
|
|
@@ -528,9 +617,14 @@ var Extractor = class {
|
|
|
528
617
|
if (!Node.isJSDocable(node)) return null;
|
|
529
618
|
const jsDocs = node.getJsDocs();
|
|
530
619
|
if (jsDocs.length === 0) return null;
|
|
531
|
-
const typeTag = jsDocs[jsDocs.length - 1].getTags().find((t) => t.getTagName() === "
|
|
620
|
+
const typeTag = jsDocs[jsDocs.length - 1].getTags().find((t) => t.getTagName() === "genType");
|
|
532
621
|
if (!typeTag) return null;
|
|
533
|
-
|
|
622
|
+
let raw = typeTag.compilerNode.typeExpression?.getText?.()?.trim();
|
|
623
|
+
if (!raw) {
|
|
624
|
+
const comment = typeTag.getComment();
|
|
625
|
+
if (typeof comment === "string") raw = comment.trim();
|
|
626
|
+
else if (Array.isArray(comment)) raw = comment.map((c) => c.getText?.() ?? "").join("").trim();
|
|
627
|
+
}
|
|
534
628
|
if (!raw) return null;
|
|
535
629
|
return parseTypeOverride(raw);
|
|
536
630
|
} catch {
|
|
@@ -569,7 +663,77 @@ var Extractor = class {
|
|
|
569
663
|
}
|
|
570
664
|
const ret = unwrapPromise(sig.getReturnType());
|
|
571
665
|
const { hasNull, hasUndefined } = splitNullish(ret);
|
|
572
|
-
const
|
|
666
|
+
const procOverrideRaw = this.procTypeOverrides.get(name);
|
|
667
|
+
let resultType;
|
|
668
|
+
if (procOverrideRaw) {
|
|
669
|
+
const parsed = parseProcTypeOverride(procOverrideRaw);
|
|
670
|
+
if (parsed) if (parsed.kind === "direct") {
|
|
671
|
+
const core = splitNullish(ret).core;
|
|
672
|
+
if (core.length > 0) {
|
|
673
|
+
if (parsed.dartType.kind === "model") this.walkSingle(core[0], parsed.dartType.name);
|
|
674
|
+
else if (parsed.dartType.kind === "list" && parsed.dartType.element.kind === "model") {
|
|
675
|
+
const actualEl = core[0].isArray() ? core[0].getArrayElementTypeOrThrow() : core[0];
|
|
676
|
+
this.walkSingle(actualEl, parsed.dartType.element.name);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
resultType = parsed.dartType;
|
|
680
|
+
} else {
|
|
681
|
+
const core = splitNullish(ret).core;
|
|
682
|
+
const actualType = core.length > 0 ? core[0] : void 0;
|
|
683
|
+
const modelName = pascal(name) + "Result";
|
|
684
|
+
const fields = [];
|
|
685
|
+
const overrideKeys = new Set(parsed.fields.keys());
|
|
686
|
+
for (const [fieldName, fieldType] of parsed.fields) {
|
|
687
|
+
if (actualType && actualType.isObject()) {
|
|
688
|
+
const actualProp = actualType.getProperty(fieldName);
|
|
689
|
+
if (actualProp) {
|
|
690
|
+
const actualPropType = actualProp.getTypeAtLocation(this.locationNode);
|
|
691
|
+
if (fieldType.kind === "model") this.walkSingle(actualPropType, fieldType.name);
|
|
692
|
+
else if (fieldType.kind === "list" && fieldType.element.kind === "model") {
|
|
693
|
+
const core2 = splitNullish(actualPropType).core;
|
|
694
|
+
if (core2.length > 0) {
|
|
695
|
+
const el = core2[0].isArray() ? core2[0].getArrayElementTypeOrThrow() : core2[0];
|
|
696
|
+
this.walkSingle(el, fieldType.element.name);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
fields.push({
|
|
702
|
+
name: camel(fieldName),
|
|
703
|
+
jsonKey: fieldName,
|
|
704
|
+
type: fieldType,
|
|
705
|
+
optional: false,
|
|
706
|
+
nullable: false
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
if (actualType && actualType.isObject()) for (const prop of actualType.getProperties()) {
|
|
710
|
+
const jsonKey = prop.getName();
|
|
711
|
+
if (overrideKeys.has(jsonKey)) continue;
|
|
712
|
+
if (prop.getFlags() & ts.SymbolFlags.Method) continue;
|
|
713
|
+
const { core: propCore, hasNull, hasUndefined } = splitNullish(prop.getTypeAtLocation(this.locationNode));
|
|
714
|
+
const optional = hasUndefined;
|
|
715
|
+
const nullable = hasNull;
|
|
716
|
+
const fieldHint = modelName.replace(/Result$/, "") + pascal(jsonKey);
|
|
717
|
+
const dartType = propCore.length > 0 ? propCore.length === 1 ? this.walkSingle(propCore[0], fieldHint) : this.walkUnion(propCore, { hint: fieldHint }) : { kind: "dynamic" };
|
|
718
|
+
fields.push({
|
|
719
|
+
name: camel(jsonKey),
|
|
720
|
+
jsonKey,
|
|
721
|
+
type: dartType,
|
|
722
|
+
optional,
|
|
723
|
+
nullable
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
this.modelRegistry.set(modelName, {
|
|
727
|
+
name: modelName,
|
|
728
|
+
fields
|
|
729
|
+
});
|
|
730
|
+
resultType = {
|
|
731
|
+
kind: "model",
|
|
732
|
+
name: modelName
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
else resultType = this.walk(ret, pascal(name) + "Result");
|
|
736
|
+
} else resultType = this.walk(ret, pascal(name) + "Result");
|
|
573
737
|
return {
|
|
574
738
|
name,
|
|
575
739
|
kind,
|
|
@@ -655,7 +819,16 @@ var Extractor = class {
|
|
|
655
819
|
const nullable = hasNull;
|
|
656
820
|
const typeOverride = this.readTypeOverride(prop);
|
|
657
821
|
let dartType;
|
|
658
|
-
if (typeOverride)
|
|
822
|
+
if (typeOverride) if (typeOverride.kind === "list" && typeOverride.element.kind === "model") if (core.length > 0) {
|
|
823
|
+
const actualEl = core[0].isArray() ? core[0].getArrayElementTypeOrThrow() : core[0];
|
|
824
|
+
dartType = {
|
|
825
|
+
kind: "list",
|
|
826
|
+
element: this.walkSingle(actualEl, typeOverride.element.name)
|
|
827
|
+
};
|
|
828
|
+
} else dartType = typeOverride;
|
|
829
|
+
else if (typeOverride.kind === "model") if (core.length > 0) dartType = this.walkSingle(core[0], typeOverride.name);
|
|
830
|
+
else dartType = typeOverride;
|
|
831
|
+
else dartType = typeOverride;
|
|
659
832
|
else if (core.length === 0) dartType = { kind: "dynamic" };
|
|
660
833
|
else if (core.length === 1) {
|
|
661
834
|
const fieldHint = name.replace(/Result$/, "") + pascal(jsonKey);
|
|
@@ -979,6 +1152,67 @@ function parseTypeOverride(raw) {
|
|
|
979
1152
|
name: raw
|
|
980
1153
|
};
|
|
981
1154
|
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Parse a procedure-level `@genType` value.
|
|
1157
|
+
*
|
|
1158
|
+
* - `{{value: List<StoreCardItem>}}` → object shape with field overrides
|
|
1159
|
+
* - `List<StoreCardItem>` → direct type override
|
|
1160
|
+
* - `StoreCardResult` → direct model name override
|
|
1161
|
+
*/
|
|
1162
|
+
function parseProcTypeOverride(raw) {
|
|
1163
|
+
const objectMatch = raw.match(/^\{([\s\S]+)\}$/);
|
|
1164
|
+
if (objectMatch) {
|
|
1165
|
+
let inner = objectMatch[1];
|
|
1166
|
+
if (inner.startsWith("{")) inner = inner.slice(1);
|
|
1167
|
+
if (inner.endsWith("}")) inner = inner.slice(0, -1);
|
|
1168
|
+
const fields = parseFieldOverrides(inner);
|
|
1169
|
+
return fields.size > 0 ? {
|
|
1170
|
+
kind: "object",
|
|
1171
|
+
fields
|
|
1172
|
+
} : null;
|
|
1173
|
+
}
|
|
1174
|
+
const dartType = parseTypeOverride(raw);
|
|
1175
|
+
return dartType ? {
|
|
1176
|
+
kind: "direct",
|
|
1177
|
+
dartType
|
|
1178
|
+
} : null;
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Parse comma-separated `field: Type` entries inside an object shape override.
|
|
1182
|
+
*
|
|
1183
|
+
* Handles nested `List<T>` types (the colon inside `List<...>` must not split
|
|
1184
|
+
* the field name). Examples:
|
|
1185
|
+
* - `"value: List<StoreCardItem>"` → `{ value: { kind: "list", ... } }`
|
|
1186
|
+
* - `"name: String, count: int"` → two entries
|
|
1187
|
+
*/
|
|
1188
|
+
function parseFieldOverrides(raw) {
|
|
1189
|
+
const fields = /* @__PURE__ */ new Map();
|
|
1190
|
+
const parts = splitOutsideAngles(raw);
|
|
1191
|
+
for (const part of parts) {
|
|
1192
|
+
const colonIdx = part.indexOf(":");
|
|
1193
|
+
if (colonIdx === -1) continue;
|
|
1194
|
+
const fieldName = part.slice(0, colonIdx).trim();
|
|
1195
|
+
const typeStr = part.slice(colonIdx + 1).trim();
|
|
1196
|
+
if (!fieldName || !typeStr) continue;
|
|
1197
|
+
const dartType = parseTypeOverride(typeStr);
|
|
1198
|
+
if (dartType) fields.set(fieldName, dartType);
|
|
1199
|
+
}
|
|
1200
|
+
return fields;
|
|
1201
|
+
}
|
|
1202
|
+
/** Split a string by commas, ignoring commas inside `<...>` angle brackets. */
|
|
1203
|
+
function splitOutsideAngles(raw) {
|
|
1204
|
+
const parts = [];
|
|
1205
|
+
let depth = 0;
|
|
1206
|
+
let start = 0;
|
|
1207
|
+
for (let i = 0; i < raw.length; i++) if (raw[i] === "<") depth++;
|
|
1208
|
+
else if (raw[i] === ">") depth = Math.max(0, depth - 1);
|
|
1209
|
+
else if (raw[i] === "," && depth === 0) {
|
|
1210
|
+
parts.push(raw.slice(start, i));
|
|
1211
|
+
start = i + 1;
|
|
1212
|
+
}
|
|
1213
|
+
parts.push(raw.slice(start));
|
|
1214
|
+
return parts;
|
|
1215
|
+
}
|
|
982
1216
|
/** The bare candidate name for a structural (non-schema) enum group. */
|
|
983
1217
|
function fallbackBase(g) {
|
|
984
1218
|
return pascal(g.field ?? g.hint);
|