@contractkit/plugin-typescript 0.17.4 → 0.18.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/index.js CHANGED
@@ -1798,7 +1798,7 @@ function generateSdk(root, options = {}) {
1798
1798
  const lines = [];
1799
1799
  const includeInternal = options.includeInternal ?? false;
1800
1800
  const types = collectTypes2(root, options.modelsWithInput, options.modelsWithOutput, includeInternal);
1801
- const clientClassName = deriveClientClassName(root.file);
1801
+ const clientClassName = options.clientClassName ?? deriveClientClassName(root.file);
1802
1802
  if (types.length > 0) {
1803
1803
  lines.push(...generateTypeImports2(types, root.file, options));
1804
1804
  }
@@ -1899,6 +1899,26 @@ function generateSdk(root, options = {}) {
1899
1899
  return lines.join("\n");
1900
1900
  }
1901
1901
  __name(generateSdk, "generateSdk");
1902
+ function generateClientMethods(root, options) {
1903
+ const lines = [];
1904
+ const methodNames = [];
1905
+ const includeInternal = options.includeInternal ?? false;
1906
+ for (const route of root.routes) {
1907
+ for (const op of route.operations) {
1908
+ const mods = resolveModifiers2(route, op);
1909
+ if (!includeInternal && mods.includes("internal")) continue;
1910
+ lines.push("");
1911
+ if (mods.includes("deprecated")) lines.push(" /** @deprecated */");
1912
+ lines.push(...generateMethod(route, op, root.file, options));
1913
+ methodNames.push(deriveMethodName(op, route));
1914
+ }
1915
+ }
1916
+ return {
1917
+ lines,
1918
+ methodNames
1919
+ };
1920
+ }
1921
+ __name(generateClientMethods, "generateClientMethods");
1902
1922
  function generateMethod(route, op, file, options) {
1903
1923
  const lines = [];
1904
1924
  const methodName = deriveMethodName(op, route);
@@ -2202,6 +2222,38 @@ function deriveClientPropertyName(file) {
2202
2222
  return base.charAt(0).toLowerCase() + base.slice(1);
2203
2223
  }
2204
2224
  __name(deriveClientPropertyName, "deriveClientPropertyName");
2225
+ function getAreaSubarea(root) {
2226
+ return {
2227
+ area: root.meta?.area,
2228
+ subarea: root.meta?.subarea
2229
+ };
2230
+ }
2231
+ __name(getAreaSubarea, "getAreaSubarea");
2232
+ function pascal(value) {
2233
+ return value.split(/[-_\s]+/).filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
2234
+ }
2235
+ __name(pascal, "pascal");
2236
+ function camel(value) {
2237
+ const p = pascal(value);
2238
+ return p.charAt(0).toLowerCase() + p.slice(1);
2239
+ }
2240
+ __name(camel, "camel");
2241
+ function deriveAreaClientClassName(area) {
2242
+ return `${pascal(area)}Client`;
2243
+ }
2244
+ __name(deriveAreaClientClassName, "deriveAreaClientClassName");
2245
+ function deriveAreaPropertyName(area) {
2246
+ return camel(area);
2247
+ }
2248
+ __name(deriveAreaPropertyName, "deriveAreaPropertyName");
2249
+ function deriveSubareaClientClassName(area, subarea) {
2250
+ return `${pascal(area)}${pascal(subarea)}Client`;
2251
+ }
2252
+ __name(deriveSubareaClientClassName, "deriveSubareaClientClassName");
2253
+ function deriveSubareaPropertyName(subarea) {
2254
+ return camel(subarea);
2255
+ }
2256
+ __name(deriveSubareaPropertyName, "deriveSubareaPropertyName");
2205
2257
  function collectTypes2(root, modelsWithInput, modelsWithOutput, includeInternal = false) {
2206
2258
  const types = /* @__PURE__ */ new Set();
2207
2259
  for (const route of root.routes) {
@@ -2516,22 +2568,128 @@ function generateSdkOptions() {
2516
2568
  ].join("\n");
2517
2569
  }
2518
2570
  __name(generateSdkOptions, "generateSdkOptions");
2519
- function generateSdkAggregator(clients, sdkOptionsImportPath = "./sdk-options.js", sdkClassName = "Sdk") {
2571
+ function generateSdkAggregator(input) {
2572
+ const sdkOptionsImportPath = input.sdkOptionsImportPath ?? "./sdk-options.js";
2573
+ const sdkClassName = input.sdkClassName ?? "Sdk";
2574
+ const inlinedByArea = /* @__PURE__ */ new Map();
2575
+ const typesByImportPath = /* @__PURE__ */ new Map();
2576
+ const unresolvedTypes = /* @__PURE__ */ new Set();
2577
+ let needsJson = false;
2578
+ let needsBigIntReplacer = false;
2579
+ let needsBigIntReviver = false;
2580
+ let needsQueryString = false;
2581
+ for (const area of input.areas) {
2582
+ const collected = [];
2583
+ const seenMethods = /* @__PURE__ */ new Set();
2584
+ for (const inline of area.inlineFiles) {
2585
+ const includeInternal = inline.codegenOptions.includeInternal ?? false;
2586
+ const { lines: methodLines, methodNames } = generateClientMethods(inline.root, inline.codegenOptions);
2587
+ for (const name of methodNames) {
2588
+ if (seenMethods.has(name)) {
2589
+ throw new Error(`[sdk] duplicate method '${name}' in area '${area.area}': two area-level files contribute the same method. Disambiguate via 'sdk:' or move one into a subarea.`);
2590
+ }
2591
+ seenMethods.add(name);
2592
+ }
2593
+ collected.push(...methodLines);
2594
+ if (sdkNeedsJson(inline.root, includeInternal)) needsJson = true;
2595
+ if (sdkNeedsBigIntReplacer(inline.root, includeInternal)) needsBigIntReplacer = true;
2596
+ if (sdkNeedsBigIntReviver(inline.root, includeInternal)) needsBigIntReviver = true;
2597
+ if (sdkNeedsQueryString(inline.root, includeInternal)) needsQueryString = true;
2598
+ const typesForFile = collectTypes2(inline.root, inline.codegenOptions.modelsWithInput, inline.codegenOptions.modelsWithOutput, includeInternal);
2599
+ const { modelOutPaths, outPath } = inline.codegenOptions;
2600
+ if (modelOutPaths && outPath) {
2601
+ const fromDir = dirname3(outPath);
2602
+ for (const t of typesForFile) {
2603
+ const typeOutPath = modelOutPaths.get(t);
2604
+ if (typeOutPath) {
2605
+ let rel = relative3(fromDir, typeOutPath).replace(/\.ts$/, ".js");
2606
+ if (!rel.startsWith(".")) rel = "./" + rel;
2607
+ const set = typesByImportPath.get(rel) ?? /* @__PURE__ */ new Set();
2608
+ set.add(t);
2609
+ typesByImportPath.set(rel, set);
2610
+ } else {
2611
+ unresolvedTypes.add(t);
2612
+ }
2613
+ }
2614
+ }
2615
+ }
2616
+ inlinedByArea.set(area.area, {
2617
+ lines: collected,
2618
+ methodNames: seenMethods
2619
+ });
2620
+ }
2520
2621
  const lines = [];
2622
+ const jsonImport = needsJson ? ", JsonValue" : "";
2623
+ lines.push(`import type { SdkFetch${jsonImport} } from '${sdkOptionsImportPath}';`);
2624
+ const valueImports = [];
2625
+ if (needsBigIntReplacer) valueImports.push("bigIntReplacer");
2626
+ if (needsBigIntReviver) valueImports.push("parseJson");
2627
+ if (needsQueryString) valueImports.push("buildQueryString");
2628
+ if (valueImports.length > 0) {
2629
+ lines.push(`import { ${valueImports.join(", ")} } from '${sdkOptionsImportPath}';`);
2630
+ }
2521
2631
  lines.push(`import type { SdkOptions } from '${sdkOptionsImportPath}';`);
2522
2632
  lines.push(`import { createSdkFetch } from '${sdkOptionsImportPath}';`);
2523
- for (const c of clients) {
2633
+ const typeImportPaths = [
2634
+ ...typesByImportPath.keys()
2635
+ ].sort();
2636
+ for (const path of typeImportPaths) {
2637
+ const names = [
2638
+ ...typesByImportPath.get(path)
2639
+ ].sort();
2640
+ lines.push(`import type { ${names.join(", ")} } from '${path}';`);
2641
+ }
2642
+ for (const t of [
2643
+ ...unresolvedTypes
2644
+ ].sort()) {
2645
+ lines.push(`import type { ${t} } from './${pascalToDotCase(t)}.js';`);
2646
+ }
2647
+ const importedClients = /* @__PURE__ */ new Set();
2648
+ const pushClientImport = /* @__PURE__ */ __name((c) => {
2649
+ const key = `${c.className}|${c.importPath}`;
2650
+ if (importedClients.has(key)) return;
2651
+ importedClients.add(key);
2524
2652
  lines.push(`import { ${c.className} } from '${c.importPath}';`);
2653
+ }, "pushClientImport");
2654
+ for (const c of input.topLevelClients) pushClientImport(c);
2655
+ for (const area of input.areas) {
2656
+ for (const sc of area.subareaClients) pushClientImport(sc.client);
2525
2657
  }
2526
2658
  lines.push("");
2659
+ for (const area of input.areas) {
2660
+ const areaClassName = deriveAreaClientClassName(area.area);
2661
+ const inlined = inlinedByArea.get(area.area);
2662
+ lines.push(`class ${areaClassName} {`);
2663
+ for (const sc of area.subareaClients) {
2664
+ lines.push(` readonly ${sc.propertyName}: ${sc.client.className};`);
2665
+ }
2666
+ if (area.subareaClients.length > 0) lines.push("");
2667
+ if (inlined.lines.length > 0 || area.subareaClients.length > 0) {
2668
+ const fetchModifier = inlined.lines.length > 0 ? "private " : "";
2669
+ lines.push(` constructor(${fetchModifier}fetch: SdkFetch) {`);
2670
+ for (const sc of area.subareaClients) {
2671
+ lines.push(` this.${sc.propertyName} = new ${sc.client.className}(fetch);`);
2672
+ }
2673
+ lines.push(" }");
2674
+ }
2675
+ for (const ln of inlined.lines) lines.push(ln);
2676
+ lines.push("}");
2677
+ lines.push("");
2678
+ }
2527
2679
  lines.push(`export class ${sdkClassName} {`);
2528
- for (const c of clients) {
2680
+ for (const area of input.areas) {
2681
+ lines.push(` readonly ${deriveAreaPropertyName(area.area)}: ${deriveAreaClientClassName(area.area)};`);
2682
+ }
2683
+ for (const c of input.topLevelClients) {
2529
2684
  lines.push(` readonly ${c.propertyName}: ${c.className};`);
2530
2685
  }
2531
2686
  lines.push("");
2532
2687
  lines.push(" constructor(options: SdkOptions) {");
2533
2688
  lines.push(" const sdkFetch = options.fetch ?? createSdkFetch(options);");
2534
- for (const c of clients) {
2689
+ for (const area of input.areas) {
2690
+ lines.push(` this.${deriveAreaPropertyName(area.area)} = new ${deriveAreaClientClassName(area.area)}(sdkFetch);`);
2691
+ }
2692
+ for (const c of input.topLevelClients) {
2535
2693
  lines.push(` this.${c.propertyName} = new ${c.className}(sdkFetch);`);
2536
2694
  }
2537
2695
  lines.push(" }");
@@ -3059,18 +3217,71 @@ function runSdkGeneration(config, rootDir, inputs, emitFile) {
3059
3217
  emitFile(typeOutPath, content);
3060
3218
  }
3061
3219
  }
3220
+ const areaBuckets = /* @__PURE__ */ new Map();
3221
+ const topLevelEntries = [];
3062
3222
  if (config.output?.clients) {
3063
3223
  for (const ast of inputs.opRoots) {
3064
3224
  const sdkOutPath = computeSdkOutPath(ast.file, sdkBase, config.output.clients, ckCommonRoot, ast.meta);
3065
3225
  if (!sdkOutPath || !hasPublicOperations(ast, config.includeInternal)) continue;
3226
+ const { area, subarea } = getAreaSubarea(ast);
3227
+ if (area && subarea) {
3228
+ const bucket = areaBuckets.get(area) ?? {
3229
+ leaves: [],
3230
+ inlineRoots: []
3231
+ };
3232
+ bucket.leaves.push({
3233
+ ast,
3234
+ outPath: sdkOutPath,
3235
+ subarea
3236
+ });
3237
+ areaBuckets.set(area, bucket);
3238
+ } else if (area) {
3239
+ const bucket = areaBuckets.get(area) ?? {
3240
+ leaves: [],
3241
+ inlineRoots: []
3242
+ };
3243
+ bucket.inlineRoots.push(ast);
3244
+ areaBuckets.set(area, bucket);
3245
+ } else {
3246
+ topLevelEntries.push({
3247
+ ast,
3248
+ outPath: sdkOutPath
3249
+ });
3250
+ }
3251
+ }
3252
+ for (const { ast, outPath, subarea } of [
3253
+ ...areaBuckets.entries()
3254
+ ].flatMap(([area, b]) => b.leaves.map((l) => ({
3255
+ ...l,
3256
+ area
3257
+ })))) {
3258
+ const className = deriveSubareaClientClassName(ast.meta?.area ?? "", subarea);
3259
+ sdkClientInfos.push({
3260
+ outPath,
3261
+ className,
3262
+ propertyName: deriveSubareaPropertyName(subarea)
3263
+ });
3264
+ emitFile(outPath, generateSdk(ast, {
3265
+ typeImportPathTemplate: void 0,
3266
+ outPath,
3267
+ modelOutPaths: sdkModelOutPaths,
3268
+ sdkOptionsPath,
3269
+ modelsWithInput,
3270
+ modelsWithOutput,
3271
+ includeInternal: config.includeInternal,
3272
+ clientClassName: className
3273
+ }));
3274
+ }
3275
+ for (const { ast, outPath } of topLevelEntries) {
3276
+ const className = deriveClientClassName(ast.file);
3066
3277
  sdkClientInfos.push({
3067
- outPath: sdkOutPath,
3068
- className: deriveClientClassName(ast.file),
3278
+ outPath,
3279
+ className,
3069
3280
  propertyName: deriveClientPropertyName(ast.file)
3070
3281
  });
3071
- emitFile(sdkOutPath, generateSdk(ast, {
3282
+ emitFile(outPath, generateSdk(ast, {
3072
3283
  typeImportPathTemplate: void 0,
3073
- outPath: sdkOutPath,
3284
+ outPath,
3074
3285
  modelOutPaths: sdkModelOutPaths,
3075
3286
  sdkOptionsPath,
3076
3287
  modelsWithInput,
@@ -3080,20 +3291,60 @@ function runSdkGeneration(config, rootDir, inputs, emitFile) {
3080
3291
  }
3081
3292
  }
3082
3293
  emitFile(sdkOptionsPath, generateSdkOptions());
3083
- if (sdkClientInfos.length > 0) {
3294
+ const hasAnything = sdkClientInfos.length > 0 || areaBuckets.size > 0;
3295
+ if (hasAnything) {
3084
3296
  const sdkEntryDir = dirname6(sdkEntryPath);
3085
- const clients = sdkClientInfos.map((c) => {
3086
- let rel = relative6(sdkEntryDir, c.outPath).replace(/\.ts$/, ".js");
3297
+ const sdkOptionsRel = relative6(sdkEntryDir, sdkOptionsPath).replace(/\.ts$/, ".js");
3298
+ const sdkOptionsImportPath = sdkOptionsRel.startsWith(".") ? sdkOptionsRel : "./" + sdkOptionsRel;
3299
+ const sdkClassName = sdkName ? sdkName.split(/[-._\s]+/).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("") + "Sdk" : "Sdk";
3300
+ const toClientImport = /* @__PURE__ */ __name((info) => {
3301
+ let rel = relative6(sdkEntryDir, info.outPath).replace(/\.ts$/, ".js");
3087
3302
  if (!rel.startsWith(".")) rel = "./" + rel;
3088
3303
  return {
3089
- className: c.className,
3090
- propertyName: c.propertyName,
3304
+ className: info.className,
3305
+ propertyName: info.propertyName,
3091
3306
  importPath: rel
3092
3307
  };
3093
- });
3094
- const sdkOptionsRel = relative6(sdkEntryDir, sdkOptionsPath).replace(/\.ts$/, ".js");
3095
- const sdkClassName = sdkName ? sdkName.split(/[-._\s]+/).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("") + "Sdk" : "Sdk";
3096
- emitFile(sdkEntryPath, generateSdkAggregator(clients, sdkOptionsRel.startsWith(".") ? sdkOptionsRel : "./" + sdkOptionsRel, sdkClassName));
3308
+ }, "toClientImport");
3309
+ const topLevelClients = topLevelEntries.map((e) => ({
3310
+ className: deriveClientClassName(e.ast.file),
3311
+ propertyName: deriveClientPropertyName(e.ast.file),
3312
+ importPath: (() => {
3313
+ const rel = relative6(sdkEntryDir, e.outPath).replace(/\.ts$/, ".js");
3314
+ return rel.startsWith(".") ? rel : "./" + rel;
3315
+ })()
3316
+ }));
3317
+ const areas = [
3318
+ ...areaBuckets.entries()
3319
+ ].sort(([a], [b]) => a.localeCompare(b)).map(([area, bucket]) => ({
3320
+ area,
3321
+ inlineFiles: bucket.inlineRoots.map((root) => ({
3322
+ root,
3323
+ codegenOptions: {
3324
+ typeImportPathTemplate: void 0,
3325
+ outPath: sdkEntryPath,
3326
+ modelOutPaths: sdkModelOutPaths,
3327
+ sdkOptionsPath,
3328
+ modelsWithInput,
3329
+ modelsWithOutput,
3330
+ includeInternal: config.includeInternal
3331
+ }
3332
+ })),
3333
+ subareaClients: bucket.leaves.sort((a, b) => a.subarea.localeCompare(b.subarea)).map((l) => ({
3334
+ propertyName: deriveSubareaPropertyName(l.subarea),
3335
+ client: toClientImport({
3336
+ outPath: l.outPath,
3337
+ className: deriveSubareaClientClassName(area, l.subarea),
3338
+ propertyName: deriveSubareaPropertyName(l.subarea)
3339
+ })
3340
+ }))
3341
+ }));
3342
+ emitFile(sdkEntryPath, generateSdkAggregator({
3343
+ topLevelClients,
3344
+ areas,
3345
+ sdkOptionsImportPath,
3346
+ sdkClassName
3347
+ }));
3097
3348
  }
3098
3349
  const sdkSrcDir = dirname6(sdkEntryPath);
3099
3350
  const sdkTypeBarrels = generateBarrelFiles(sdkTypePaths);
@@ -3101,7 +3352,7 @@ function runSdkGeneration(config, rootDir, inputs, emitFile) {
3101
3352
  const rootExports = [
3102
3353
  `export * from './${basename3(sdkOptionsPath).replace(/\.ts$/, ".js")}';`
3103
3354
  ];
3104
- if (sdkClientInfos.length > 0) {
3355
+ if (hasAnything) {
3105
3356
  rootExports.push(`export * from './${basename3(sdkEntryPath).replace(/\.ts$/, ".js")}';`);
3106
3357
  }
3107
3358
  for (const c of sdkClientInfos) {