@contractkit/plugin-typescript 0.18.0 → 0.19.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/index.js CHANGED
@@ -3,6 +3,7 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
3
3
 
4
4
  // src/index.ts
5
5
  import { resolve as resolve2, join as join2, relative as relative6, dirname as dirname6, basename as basename3 } from "path";
6
+ import { existsSync, readFileSync, rmSync, readdirSync, rmdirSync } from "fs";
6
7
 
7
8
  // src/codegen-contract.ts
8
9
  import { relative, dirname } from "path";
@@ -1734,6 +1735,9 @@ function deriveTypeImportPath(file, template) {
1734
1735
  }
1735
1736
  __name(deriveTypeImportPath, "deriveTypeImportPath");
1736
1737
 
1738
+ // src/index.ts
1739
+ import { runIncrementalCodegen, parseIncrementalManifest, emptyIncrementalManifest, hashFingerprint, collectTransitiveModelRefs } from "@contractkit/core";
1740
+
1737
1741
  // src/codegen-sdk.ts
1738
1742
  import { resolveModifiers as resolveModifiers2, isJsonMime, classifyContentType as classifyContentType2 } from "@contractkit/core";
1739
1743
  import { basename as basename2, dirname as dirname3, relative as relative3 } from "path";
@@ -3103,19 +3107,149 @@ function computePubliclyReachableTypes(opAsts, contractAsts, modelsWithInput, mo
3103
3107
  __name(computePubliclyReachableTypes, "computePubliclyReachableTypes");
3104
3108
 
3105
3109
  // src/index.ts
3106
- function runServerGeneration(config, rootDir, inputs, emitFile) {
3110
+ var TYPESCRIPT_CODEGEN_VERSION = "1";
3111
+ var MANIFEST_FILENAME = ".contractkit-typescript-manifest.json";
3112
+ var plugin = {
3113
+ name: "typescript",
3114
+ async generateTargets(inputs, ctx) {
3115
+ const config = ctx.options;
3116
+ await runTypescriptCodegen(inputs, ctx, config, ctx.rootDir);
3117
+ }
3118
+ };
3119
+ var index_default = plugin;
3120
+ function createTypescriptPlugin(config, rootDir) {
3121
+ return {
3122
+ name: "typescript",
3123
+ async generateTargets(inputs, ctx) {
3124
+ await runTypescriptCodegen(inputs, ctx, config, rootDir);
3125
+ }
3126
+ };
3127
+ }
3128
+ __name(createTypescriptPlugin, "createTypescriptPlugin");
3129
+ async function runTypescriptCodegen(inputs, ctx, config, rootDir) {
3130
+ const manifestPath = resolve2(rootDir, MANIFEST_FILENAME);
3131
+ const prevManifest = ctx.cacheEnabled ? readManifest(manifestPath) : emptyIncrementalManifest(TYPESCRIPT_CODEGEN_VERSION);
3132
+ const units = [];
3133
+ const globalFiles = [];
3134
+ if (config.server) collectServerOutput(config.server, rootDir, inputs, units);
3135
+ if (config.sdk) collectSdkOutput(config.sdk, rootDir, inputs, units, globalFiles);
3136
+ if (config.zod) collectZodOutput(config.zod, rootDir, inputs, units);
3137
+ if (config.types) collectTypesOutput(config.types, rootDir, inputs, units);
3138
+ const result = runIncrementalCodegen({
3139
+ codegenVersion: TYPESCRIPT_CODEGEN_VERSION,
3140
+ manifestFilename: manifestPath,
3141
+ prevManifest,
3142
+ globalFiles,
3143
+ units,
3144
+ // Paths are absolute, so existsSync works directly.
3145
+ fileExists: existsSync
3146
+ });
3147
+ deleteStalePaths(result.deletedPaths);
3148
+ for (const { relativePath, content } of result.filesToWrite) {
3149
+ ctx.emitFile(relativePath, content);
3150
+ }
3151
+ }
3152
+ __name(runTypescriptCodegen, "runTypescriptCodegen");
3153
+ function buildModelMap(contractRoots) {
3154
+ const map = /* @__PURE__ */ new Map();
3155
+ for (const root of contractRoots) {
3156
+ for (const model of root.models) map.set(model.name, model);
3157
+ }
3158
+ return map;
3159
+ }
3160
+ __name(buildModelMap, "buildModelMap");
3161
+ function collectContractRootRefs(root, modelMap) {
3162
+ const seeds = [];
3163
+ for (const m of root.models) {
3164
+ if (m.type) seeds.push(m.type);
3165
+ for (const f of m.fields) seeds.push(f.type);
3166
+ if (m.bases) {
3167
+ for (const b of m.bases) seeds.push({
3168
+ kind: "ref",
3169
+ name: b
3170
+ });
3171
+ }
3172
+ }
3173
+ return collectTransitiveModelRefs(seeds, modelMap);
3174
+ }
3175
+ __name(collectContractRootRefs, "collectContractRootRefs");
3176
+ function collectOpRootRefs(root, modelMap) {
3177
+ const seeds = [];
3178
+ for (const route of root.routes) {
3179
+ if (route.params) seeds.push(...paramSourceTypes(route.params));
3180
+ for (const op of route.operations) {
3181
+ if (op.query) seeds.push(...paramSourceTypes(op.query));
3182
+ if (op.headers) seeds.push(...paramSourceTypes(op.headers));
3183
+ if (op.request) {
3184
+ for (const body of op.request.bodies) seeds.push(body.bodyType);
3185
+ }
3186
+ for (const resp of op.responses) {
3187
+ if (resp.bodyType) seeds.push(resp.bodyType);
3188
+ if (resp.headers) {
3189
+ for (const h of resp.headers) seeds.push(h.type);
3190
+ }
3191
+ }
3192
+ }
3193
+ }
3194
+ return collectTransitiveModelRefs(seeds, modelMap);
3195
+ }
3196
+ __name(collectOpRootRefs, "collectOpRootRefs");
3197
+ function paramSourceTypes(src) {
3198
+ const out = [];
3199
+ if (src.kind === "params") {
3200
+ for (const n of src.nodes) out.push(n.type);
3201
+ } else if (src.kind === "ref") {
3202
+ out.push({
3203
+ kind: "ref",
3204
+ name: src.name
3205
+ });
3206
+ } else if (src.kind === "type") {
3207
+ out.push(src.node);
3208
+ }
3209
+ return out;
3210
+ }
3211
+ __name(paramSourceTypes, "paramSourceTypes");
3212
+ function sliceOutPathMap(refs, modelOutPaths, modelsWithInput, modelsWithOutput) {
3213
+ const slice = {};
3214
+ for (const ref of [
3215
+ ...refs
3216
+ ].sort()) {
3217
+ const p = modelOutPaths.get(ref);
3218
+ if (p) slice[ref] = p;
3219
+ if (modelsWithInput.has(ref)) {
3220
+ const ip = modelOutPaths.get(`${ref}Input`);
3221
+ if (ip) slice[`${ref}Input`] = ip;
3222
+ }
3223
+ if (modelsWithOutput.has(ref)) {
3224
+ const op = modelOutPaths.get(`${ref}Output`);
3225
+ if (op) slice[`${ref}Output`] = op;
3226
+ }
3227
+ }
3228
+ return slice;
3229
+ }
3230
+ __name(sliceOutPathMap, "sliceOutPathMap");
3231
+ function sliceModelSet(refs, ownNames, set) {
3232
+ const result = [];
3233
+ for (const name of set) {
3234
+ if (refs.has(name) || ownNames.has(name)) result.push(name);
3235
+ }
3236
+ return result.sort();
3237
+ }
3238
+ __name(sliceModelSet, "sliceModelSet");
3239
+ function collectServerOutput(config, rootDir, inputs, units) {
3107
3240
  const serverBase = resolve2(rootDir, config.baseDir ?? ".");
3108
3241
  const modelsWithInput = inputs.modelsWithInput;
3109
3242
  const modelsWithOutput = inputs.modelsWithOutput;
3243
+ const modelMap = buildModelMap(inputs.contractRoots);
3110
3244
  const allFiles = [
3111
3245
  ...inputs.contractRoots.map((r) => r.file),
3112
3246
  ...inputs.opRoots.map((r) => r.file)
3113
3247
  ];
3114
3248
  const commonRoot = commonDir(allFiles, rootDir);
3115
- let serverModelOutPaths = /* @__PURE__ */ new Map();
3249
+ const subConfigKey = stableSubConfig(config);
3250
+ const serverModelOutPaths = /* @__PURE__ */ new Map();
3251
+ const typeEntries = [];
3116
3252
  if (config.output?.types) {
3117
- serverModelOutPaths = /* @__PURE__ */ new Map();
3118
- const typeEntries = [];
3119
3253
  for (const ast of inputs.contractRoots) {
3120
3254
  const typeOutPath = computeContractOutPath(ast.file, serverBase, config.output.types, ".ts", commonRoot, ast.meta);
3121
3255
  typeEntries.push({
@@ -3124,40 +3258,81 @@ function runServerGeneration(config, rootDir, inputs, emitFile) {
3124
3258
  });
3125
3259
  for (const model of ast.models) {
3126
3260
  serverModelOutPaths.set(model.name, typeOutPath);
3127
- if (modelsWithInput.has(model.name)) {
3128
- serverModelOutPaths.set(`${model.name}Input`, typeOutPath);
3129
- }
3130
- if (modelsWithOutput.has(model.name)) {
3131
- serverModelOutPaths.set(`${model.name}Output`, typeOutPath);
3132
- }
3261
+ if (modelsWithInput.has(model.name)) serverModelOutPaths.set(`${model.name}Input`, typeOutPath);
3262
+ if (modelsWithOutput.has(model.name)) serverModelOutPaths.set(`${model.name}Output`, typeOutPath);
3133
3263
  }
3134
3264
  }
3135
- for (const { ast, typeOutPath } of typeEntries) {
3136
- const ctx = {
3137
- modelOutPaths: serverModelOutPaths,
3138
- currentOutPath: typeOutPath,
3139
- modelsWithInput,
3140
- modelsWithOutput
3141
- };
3142
- const content = config.zod ? generateContract(ast, ctx) : generatePlainTypes(ast, ctx);
3143
- emitFile(typeOutPath, content);
3144
- }
3265
+ }
3266
+ for (const { ast, typeOutPath } of typeEntries) {
3267
+ const refs = collectContractRootRefs(ast, modelMap);
3268
+ const ownNames = new Set(ast.models.map((m) => m.name));
3269
+ const fingerprint = hashFingerprint({
3270
+ kind: "server-types",
3271
+ v: TYPESCRIPT_CODEGEN_VERSION,
3272
+ outPath: typeOutPath,
3273
+ root: ast,
3274
+ outPathSlice: sliceOutPathMap(refs, serverModelOutPaths, modelsWithInput, modelsWithOutput),
3275
+ modelsWithInput: sliceModelSet(refs, ownNames, modelsWithInput),
3276
+ modelsWithOutput: sliceModelSet(refs, ownNames, modelsWithOutput),
3277
+ sub: subConfigKey
3278
+ });
3279
+ units.push({
3280
+ key: `server-types::${typeOutPath}`,
3281
+ fingerprint,
3282
+ render: /* @__PURE__ */ __name(() => {
3283
+ const renderCtx = {
3284
+ modelOutPaths: serverModelOutPaths,
3285
+ currentOutPath: typeOutPath,
3286
+ modelsWithInput,
3287
+ modelsWithOutput
3288
+ };
3289
+ const content = config.zod ? generateContract(ast, renderCtx) : generatePlainTypes(ast, renderCtx);
3290
+ return [
3291
+ {
3292
+ relativePath: typeOutPath,
3293
+ content
3294
+ }
3295
+ ];
3296
+ }, "render")
3297
+ });
3145
3298
  }
3146
3299
  for (const ast of inputs.opRoots) {
3147
3300
  const outPath = computeOpOutPath(ast.file, serverBase, config.output?.routes, ".router.ts", commonRoot, ast.meta);
3148
- const content = generateOp(ast, {
3149
- servicePathTemplate: config.servicePathTemplate,
3301
+ const refs = collectOpRootRefs(ast, modelMap);
3302
+ const fingerprint = hashFingerprint({
3303
+ kind: "server-router",
3304
+ v: TYPESCRIPT_CODEGEN_VERSION,
3150
3305
  outPath,
3151
- modelOutPaths: serverModelOutPaths,
3152
- modelsWithInput,
3153
- modelsWithOutput,
3154
- includeInternal: config.includeInternal
3306
+ root: ast,
3307
+ // The router imports types from each contract root's type file; the slice covers exactly that.
3308
+ outPathSlice: sliceOutPathMap(refs, serverModelOutPaths, modelsWithInput, modelsWithOutput),
3309
+ modelsWithInput: sliceModelSet(refs, /* @__PURE__ */ new Set(), modelsWithInput),
3310
+ modelsWithOutput: sliceModelSet(refs, /* @__PURE__ */ new Set(), modelsWithOutput),
3311
+ servicePathTemplate: config.servicePathTemplate ?? null,
3312
+ includeInternal: config.includeInternal ?? true,
3313
+ sub: subConfigKey
3314
+ });
3315
+ units.push({
3316
+ key: `server-router::${outPath}`,
3317
+ fingerprint,
3318
+ render: /* @__PURE__ */ __name(() => [
3319
+ {
3320
+ relativePath: outPath,
3321
+ content: generateOp(ast, {
3322
+ servicePathTemplate: config.servicePathTemplate,
3323
+ outPath,
3324
+ modelOutPaths: serverModelOutPaths,
3325
+ modelsWithInput,
3326
+ modelsWithOutput,
3327
+ includeInternal: config.includeInternal
3328
+ })
3329
+ }
3330
+ ], "render")
3155
3331
  });
3156
- emitFile(outPath, content);
3157
3332
  }
3158
3333
  }
3159
- __name(runServerGeneration, "runServerGeneration");
3160
- function runSdkGeneration(config, rootDir, inputs, emitFile) {
3334
+ __name(collectServerOutput, "collectServerOutput");
3335
+ function collectSdkOutput(config, rootDir, inputs, units, globalFiles) {
3161
3336
  const sdkBase = config.baseDir ? resolve2(rootDir, config.baseDir) : rootDir;
3162
3337
  const sdkName = config.name;
3163
3338
  const sdkOutput = config.output?.sdk;
@@ -3165,20 +3340,21 @@ function runSdkGeneration(config, rootDir, inputs, emitFile) {
3165
3340
  name: sdkName ?? "sdk"
3166
3341
  }) : sdkOutput) : join2(sdkBase, "sdk.ts");
3167
3342
  const sdkOptionsPath = join2(dirname6(sdkEntryPath), "sdk-options.ts");
3343
+ const subConfigKey = stableSubConfig(config);
3168
3344
  const modelsWithInput = inputs.modelsWithInput;
3169
3345
  const modelsWithOutput = inputs.modelsWithOutput;
3346
+ const modelMap = buildModelMap(inputs.contractRoots);
3170
3347
  const allFiles = [
3171
3348
  ...inputs.contractRoots.map((r) => r.file),
3172
3349
  ...inputs.opRoots.map((r) => r.file)
3173
3350
  ];
3174
3351
  const ckCommonRoot = commonDir(allFiles, rootDir);
3175
- let sdkModelOutPaths = /* @__PURE__ */ new Map();
3352
+ const sdkModelOutPaths = /* @__PURE__ */ new Map();
3176
3353
  const sdkTypePaths = [];
3177
3354
  const sdkClientInfos = [];
3355
+ const sdkContractEntries = [];
3178
3356
  if (config.output?.types) {
3179
- sdkModelOutPaths = /* @__PURE__ */ new Map();
3180
3357
  const publicTypes = computePubliclyReachableTypes(inputs.opRoots, inputs.contractRoots, modelsWithInput, modelsWithOutput);
3181
- const sdkContractEntries = [];
3182
3358
  for (const ast of inputs.contractRoots) {
3183
3359
  const typeOutPath = computeSdkTypeOutPath(ast.file, sdkBase, config.output.types, ckCommonRoot, ast.meta);
3184
3360
  if (!typeOutPath) continue;
@@ -3194,28 +3370,52 @@ function runSdkGeneration(config, rootDir, inputs, emitFile) {
3194
3370
  if (modelsWithOutput.has(model.name)) sdkModelOutPaths.set(`${model.name}Output`, typeOutPath);
3195
3371
  }
3196
3372
  }
3197
- for (const { ast, typeOutPath } of sdkContractEntries) {
3198
- let content;
3199
- if (config.zod) {
3200
- content = generateContract(ast, {
3201
- modelOutPaths: sdkModelOutPaths,
3202
- currentOutPath: typeOutPath,
3203
- modelsWithInput,
3204
- modelsWithOutput
3205
- });
3206
- } else {
3207
- let rel = relative6(dirname6(typeOutPath), sdkOptionsPath).replace(/\.ts$/, ".js");
3208
- if (!rel.startsWith(".")) rel = "./" + rel;
3209
- content = generatePlainTypes(ast, {
3210
- modelOutPaths: sdkModelOutPaths,
3211
- currentOutPath: typeOutPath,
3212
- modelsWithInput,
3213
- modelsWithOutput,
3214
- jsonValueImportPath: rel
3215
- });
3216
- }
3217
- emitFile(typeOutPath, content);
3218
- }
3373
+ }
3374
+ for (const { ast, typeOutPath } of sdkContractEntries) {
3375
+ const refs = collectContractRootRefs(ast, modelMap);
3376
+ const ownNames = new Set(ast.models.map((m) => m.name));
3377
+ const fingerprint = hashFingerprint({
3378
+ kind: "sdk-types",
3379
+ v: TYPESCRIPT_CODEGEN_VERSION,
3380
+ outPath: typeOutPath,
3381
+ root: ast,
3382
+ outPathSlice: sliceOutPathMap(refs, sdkModelOutPaths, modelsWithInput, modelsWithOutput),
3383
+ modelsWithInput: sliceModelSet(refs, ownNames, modelsWithInput),
3384
+ modelsWithOutput: sliceModelSet(refs, ownNames, modelsWithOutput),
3385
+ sdkOptionsPath,
3386
+ sub: subConfigKey
3387
+ });
3388
+ units.push({
3389
+ key: `sdk-types::${typeOutPath}`,
3390
+ fingerprint,
3391
+ render: /* @__PURE__ */ __name(() => {
3392
+ let content;
3393
+ if (config.zod) {
3394
+ content = generateContract(ast, {
3395
+ modelOutPaths: sdkModelOutPaths,
3396
+ currentOutPath: typeOutPath,
3397
+ modelsWithInput,
3398
+ modelsWithOutput
3399
+ });
3400
+ } else {
3401
+ let rel = relative6(dirname6(typeOutPath), sdkOptionsPath).replace(/\.ts$/, ".js");
3402
+ if (!rel.startsWith(".")) rel = "./" + rel;
3403
+ content = generatePlainTypes(ast, {
3404
+ modelOutPaths: sdkModelOutPaths,
3405
+ currentOutPath: typeOutPath,
3406
+ modelsWithInput,
3407
+ modelsWithOutput,
3408
+ jsonValueImportPath: rel
3409
+ });
3410
+ }
3411
+ return [
3412
+ {
3413
+ relativePath: typeOutPath,
3414
+ content
3415
+ }
3416
+ ];
3417
+ }, "render")
3418
+ });
3219
3419
  }
3220
3420
  const areaBuckets = /* @__PURE__ */ new Map();
3221
3421
  const topLevelEntries = [];
@@ -3249,28 +3449,48 @@ function runSdkGeneration(config, rootDir, inputs, emitFile) {
3249
3449
  });
3250
3450
  }
3251
3451
  }
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
- }));
3452
+ for (const [area, bucket] of areaBuckets.entries()) {
3453
+ for (const leaf of bucket.leaves) {
3454
+ const className = deriveSubareaClientClassName(area, leaf.subarea);
3455
+ sdkClientInfos.push({
3456
+ outPath: leaf.outPath,
3457
+ className,
3458
+ propertyName: deriveSubareaPropertyName(leaf.subarea)
3459
+ });
3460
+ const refs = collectOpRootRefs(leaf.ast, modelMap);
3461
+ const fingerprint = hashFingerprint({
3462
+ kind: "sdk-leaf-client",
3463
+ v: TYPESCRIPT_CODEGEN_VERSION,
3464
+ outPath: leaf.outPath,
3465
+ root: leaf.ast,
3466
+ outPathSlice: sliceOutPathMap(refs, sdkModelOutPaths, modelsWithInput, modelsWithOutput),
3467
+ modelsWithInput: sliceModelSet(refs, /* @__PURE__ */ new Set(), modelsWithInput),
3468
+ modelsWithOutput: sliceModelSet(refs, /* @__PURE__ */ new Set(), modelsWithOutput),
3469
+ sdkOptionsPath,
3470
+ className,
3471
+ includeInternal: config.includeInternal ?? false,
3472
+ sub: subConfigKey
3473
+ });
3474
+ units.push({
3475
+ key: `sdk-leaf-client::${leaf.outPath}`,
3476
+ fingerprint,
3477
+ render: /* @__PURE__ */ __name(() => [
3478
+ {
3479
+ relativePath: leaf.outPath,
3480
+ content: generateSdk(leaf.ast, {
3481
+ typeImportPathTemplate: void 0,
3482
+ outPath: leaf.outPath,
3483
+ modelOutPaths: sdkModelOutPaths,
3484
+ sdkOptionsPath,
3485
+ modelsWithInput,
3486
+ modelsWithOutput,
3487
+ includeInternal: config.includeInternal,
3488
+ clientClassName: className
3489
+ })
3490
+ }
3491
+ ], "render")
3492
+ });
3493
+ }
3274
3494
  }
3275
3495
  for (const { ast, outPath } of topLevelEntries) {
3276
3496
  const className = deriveClientClassName(ast.file);
@@ -3279,18 +3499,43 @@ function runSdkGeneration(config, rootDir, inputs, emitFile) {
3279
3499
  className,
3280
3500
  propertyName: deriveClientPropertyName(ast.file)
3281
3501
  });
3282
- emitFile(outPath, generateSdk(ast, {
3283
- typeImportPathTemplate: void 0,
3502
+ const refs = collectOpRootRefs(ast, modelMap);
3503
+ const fingerprint = hashFingerprint({
3504
+ kind: "sdk-top-client",
3505
+ v: TYPESCRIPT_CODEGEN_VERSION,
3284
3506
  outPath,
3285
- modelOutPaths: sdkModelOutPaths,
3507
+ root: ast,
3508
+ outPathSlice: sliceOutPathMap(refs, sdkModelOutPaths, modelsWithInput, modelsWithOutput),
3509
+ modelsWithInput: sliceModelSet(refs, /* @__PURE__ */ new Set(), modelsWithInput),
3510
+ modelsWithOutput: sliceModelSet(refs, /* @__PURE__ */ new Set(), modelsWithOutput),
3286
3511
  sdkOptionsPath,
3287
- modelsWithInput,
3288
- modelsWithOutput,
3289
- includeInternal: config.includeInternal
3290
- }));
3512
+ includeInternal: config.includeInternal ?? false,
3513
+ sub: subConfigKey
3514
+ });
3515
+ units.push({
3516
+ key: `sdk-top-client::${outPath}`,
3517
+ fingerprint,
3518
+ render: /* @__PURE__ */ __name(() => [
3519
+ {
3520
+ relativePath: outPath,
3521
+ content: generateSdk(ast, {
3522
+ typeImportPathTemplate: void 0,
3523
+ outPath,
3524
+ modelOutPaths: sdkModelOutPaths,
3525
+ sdkOptionsPath,
3526
+ modelsWithInput,
3527
+ modelsWithOutput,
3528
+ includeInternal: config.includeInternal
3529
+ })
3530
+ }
3531
+ ], "render")
3532
+ });
3291
3533
  }
3292
3534
  }
3293
- emitFile(sdkOptionsPath, generateSdkOptions());
3535
+ globalFiles.push({
3536
+ relativePath: sdkOptionsPath,
3537
+ content: generateSdkOptions()
3538
+ });
3294
3539
  const hasAnything = sdkClientInfos.length > 0 || areaBuckets.size > 0;
3295
3540
  if (hasAnything) {
3296
3541
  const sdkEntryDir = dirname6(sdkEntryPath);
@@ -3339,22 +3584,26 @@ function runSdkGeneration(config, rootDir, inputs, emitFile) {
3339
3584
  })
3340
3585
  }))
3341
3586
  }));
3342
- emitFile(sdkEntryPath, generateSdkAggregator({
3343
- topLevelClients,
3344
- areas,
3345
- sdkOptionsImportPath,
3346
- sdkClassName
3347
- }));
3587
+ globalFiles.push({
3588
+ relativePath: sdkEntryPath,
3589
+ content: generateSdkAggregator({
3590
+ topLevelClients,
3591
+ areas,
3592
+ sdkOptionsImportPath,
3593
+ sdkClassName
3594
+ })
3595
+ });
3348
3596
  }
3349
3597
  const sdkSrcDir = dirname6(sdkEntryPath);
3350
3598
  const sdkTypeBarrels = generateBarrelFiles(sdkTypePaths);
3351
- for (const barrel of sdkTypeBarrels) emitFile(barrel.outPath, barrel.content);
3599
+ for (const barrel of sdkTypeBarrels) globalFiles.push({
3600
+ relativePath: barrel.outPath,
3601
+ content: barrel.content
3602
+ });
3352
3603
  const rootExports = [
3353
3604
  `export * from './${basename3(sdkOptionsPath).replace(/\.ts$/, ".js")}';`
3354
3605
  ];
3355
- if (hasAnything) {
3356
- rootExports.push(`export * from './${basename3(sdkEntryPath).replace(/\.ts$/, ".js")}';`);
3357
- }
3606
+ if (hasAnything) rootExports.push(`export * from './${basename3(sdkEntryPath).replace(/\.ts$/, ".js")}';`);
3358
3607
  for (const c of sdkClientInfos) {
3359
3608
  let rel = relative6(sdkSrcDir, c.outPath).replace(/\.ts$/, ".js");
3360
3609
  if (!rel.startsWith(".")) rel = "./" + rel;
@@ -3365,12 +3614,15 @@ function runSdkGeneration(config, rootDir, inputs, emitFile) {
3365
3614
  if (!rel.startsWith(".")) rel = "./" + rel;
3366
3615
  rootExports.push(`export * from '${rel}';`);
3367
3616
  }
3368
- emitFile(join2(sdkSrcDir, "index.ts"), `// Auto-generated barrel file
3617
+ globalFiles.push({
3618
+ relativePath: join2(sdkSrcDir, "index.ts"),
3619
+ content: `// Auto-generated barrel file
3369
3620
  ${rootExports.sort().join("\n")}
3370
- `);
3621
+ `
3622
+ });
3371
3623
  }
3372
- __name(runSdkGeneration, "runSdkGeneration");
3373
- function runZodGeneration(config, rootDir, inputs, emitFile) {
3624
+ __name(collectSdkOutput, "collectSdkOutput");
3625
+ function collectZodOutput(config, rootDir, inputs, units) {
3374
3626
  const zodBase = resolve2(rootDir, config.baseDir ?? ".");
3375
3627
  const allFiles = [
3376
3628
  ...inputs.contractRoots.map((r) => r.file),
@@ -3379,6 +3631,8 @@ function runZodGeneration(config, rootDir, inputs, emitFile) {
3379
3631
  const commonRoot = commonDir(allFiles, rootDir);
3380
3632
  const modelsWithInput = inputs.modelsWithInput;
3381
3633
  const modelsWithOutput = inputs.modelsWithOutput;
3634
+ const modelMap = buildModelMap(inputs.contractRoots);
3635
+ const subConfigKey = stableSubConfig(config);
3382
3636
  const modelOutPaths = /* @__PURE__ */ new Map();
3383
3637
  const entries = [];
3384
3638
  for (const ast of inputs.contractRoots) {
@@ -3394,17 +3648,37 @@ function runZodGeneration(config, rootDir, inputs, emitFile) {
3394
3648
  }
3395
3649
  }
3396
3650
  for (const { ast, outPath } of entries) {
3397
- const content = generateContract(ast, {
3398
- modelOutPaths,
3399
- currentOutPath: outPath,
3400
- modelsWithInput,
3401
- modelsWithOutput
3651
+ const refs = collectContractRootRefs(ast, modelMap);
3652
+ const ownNames = new Set(ast.models.map((m) => m.name));
3653
+ const fingerprint = hashFingerprint({
3654
+ kind: "zod",
3655
+ v: TYPESCRIPT_CODEGEN_VERSION,
3656
+ outPath,
3657
+ root: ast,
3658
+ outPathSlice: sliceOutPathMap(refs, modelOutPaths, modelsWithInput, modelsWithOutput),
3659
+ modelsWithInput: sliceModelSet(refs, ownNames, modelsWithInput),
3660
+ modelsWithOutput: sliceModelSet(refs, ownNames, modelsWithOutput),
3661
+ sub: subConfigKey
3662
+ });
3663
+ units.push({
3664
+ key: `zod::${outPath}`,
3665
+ fingerprint,
3666
+ render: /* @__PURE__ */ __name(() => [
3667
+ {
3668
+ relativePath: outPath,
3669
+ content: generateContract(ast, {
3670
+ modelOutPaths,
3671
+ currentOutPath: outPath,
3672
+ modelsWithInput,
3673
+ modelsWithOutput
3674
+ })
3675
+ }
3676
+ ], "render")
3402
3677
  });
3403
- emitFile(outPath, content);
3404
3678
  }
3405
3679
  }
3406
- __name(runZodGeneration, "runZodGeneration");
3407
- function runTypesGeneration(config, rootDir, inputs, emitFile) {
3680
+ __name(collectZodOutput, "collectZodOutput");
3681
+ function collectTypesOutput(config, rootDir, inputs, units) {
3408
3682
  const typesBase = resolve2(rootDir, config.baseDir ?? ".");
3409
3683
  const allFiles = [
3410
3684
  ...inputs.contractRoots.map((r) => r.file),
@@ -3413,6 +3687,8 @@ function runTypesGeneration(config, rootDir, inputs, emitFile) {
3413
3687
  const commonRoot = commonDir(allFiles, rootDir);
3414
3688
  const modelsWithInput = inputs.modelsWithInput;
3415
3689
  const modelsWithOutput = inputs.modelsWithOutput;
3690
+ const modelMap = buildModelMap(inputs.contractRoots);
3691
+ const subConfigKey = stableSubConfig(config);
3416
3692
  const modelOutPaths = /* @__PURE__ */ new Map();
3417
3693
  const entries = [];
3418
3694
  for (const ast of inputs.contractRoots) {
@@ -3428,58 +3704,79 @@ function runTypesGeneration(config, rootDir, inputs, emitFile) {
3428
3704
  }
3429
3705
  }
3430
3706
  for (const { ast, outPath } of entries) {
3431
- const content = generatePlainTypes(ast, {
3432
- modelOutPaths,
3433
- currentOutPath: outPath,
3434
- modelsWithInput,
3435
- modelsWithOutput
3707
+ const refs = collectContractRootRefs(ast, modelMap);
3708
+ const ownNames = new Set(ast.models.map((m) => m.name));
3709
+ const fingerprint = hashFingerprint({
3710
+ kind: "plain-types",
3711
+ v: TYPESCRIPT_CODEGEN_VERSION,
3712
+ outPath,
3713
+ root: ast,
3714
+ outPathSlice: sliceOutPathMap(refs, modelOutPaths, modelsWithInput, modelsWithOutput),
3715
+ modelsWithInput: sliceModelSet(refs, ownNames, modelsWithInput),
3716
+ modelsWithOutput: sliceModelSet(refs, ownNames, modelsWithOutput),
3717
+ sub: subConfigKey
3718
+ });
3719
+ units.push({
3720
+ key: `plain-types::${outPath}`,
3721
+ fingerprint,
3722
+ render: /* @__PURE__ */ __name(() => [
3723
+ {
3724
+ relativePath: outPath,
3725
+ content: generatePlainTypes(ast, {
3726
+ modelOutPaths,
3727
+ currentOutPath: outPath,
3728
+ modelsWithInput,
3729
+ modelsWithOutput
3730
+ })
3731
+ }
3732
+ ], "render")
3436
3733
  });
3437
- emitFile(outPath, content);
3438
3734
  }
3439
3735
  }
3440
- __name(runTypesGeneration, "runTypesGeneration");
3441
- var plugin = {
3442
- name: "typescript",
3443
- cacheKey: "typescript",
3444
- async generateTargets(inputs, ctx) {
3445
- const config = ctx.options;
3446
- if (config.server) {
3447
- runServerGeneration(config.server, ctx.rootDir, inputs, ctx.emitFile.bind(ctx));
3448
- }
3449
- if (config.sdk) {
3450
- runSdkGeneration(config.sdk, ctx.rootDir, inputs, ctx.emitFile.bind(ctx));
3451
- }
3452
- if (config.zod) {
3453
- runZodGeneration(config.zod, ctx.rootDir, inputs, ctx.emitFile.bind(ctx));
3454
- }
3455
- if (config.types) {
3456
- runTypesGeneration(config.types, ctx.rootDir, inputs, ctx.emitFile.bind(ctx));
3736
+ __name(collectTypesOutput, "collectTypesOutput");
3737
+ function readManifest(manifestPath) {
3738
+ if (!existsSync(manifestPath)) return emptyIncrementalManifest(TYPESCRIPT_CODEGEN_VERSION);
3739
+ try {
3740
+ return parseIncrementalManifest(readFileSync(manifestPath, "utf-8"));
3741
+ } catch {
3742
+ return emptyIncrementalManifest(TYPESCRIPT_CODEGEN_VERSION);
3743
+ }
3744
+ }
3745
+ __name(readManifest, "readManifest");
3746
+ function deleteStalePaths(absPaths) {
3747
+ if (absPaths.length === 0) return;
3748
+ const removedDirs = /* @__PURE__ */ new Set();
3749
+ for (const abs of absPaths) {
3750
+ if (existsSync(abs)) {
3751
+ rmSync(abs, {
3752
+ force: true
3753
+ });
3754
+ removedDirs.add(dirname6(abs));
3457
3755
  }
3458
3756
  }
3459
- };
3460
- var index_default = plugin;
3461
- function createTypescriptPlugin(config, rootDir) {
3462
- return {
3463
- name: "typescript",
3464
- cacheKey: `typescript:${JSON.stringify(config)}`,
3465
- async generateTargets(inputs, ctx) {
3466
- if (config.server) {
3467
- runServerGeneration(config.server, rootDir, inputs, ctx.emitFile.bind(ctx));
3468
- }
3469
- if (config.sdk) {
3470
- runSdkGeneration(config.sdk, rootDir, inputs, ctx.emitFile.bind(ctx));
3471
- }
3472
- if (config.zod) {
3473
- runZodGeneration(config.zod, rootDir, inputs, ctx.emitFile.bind(ctx));
3474
- }
3475
- if (config.types) {
3476
- runTypesGeneration(config.types, rootDir, inputs, ctx.emitFile.bind(ctx));
3757
+ for (const dir of removedDirs) {
3758
+ let current = dir;
3759
+ while (current.length > 1) {
3760
+ try {
3761
+ if (readdirSync(current).length === 0) {
3762
+ rmdirSync(current);
3763
+ current = dirname6(current);
3764
+ } else {
3765
+ break;
3766
+ }
3767
+ } catch {
3768
+ break;
3477
3769
  }
3478
3770
  }
3479
- };
3771
+ }
3480
3772
  }
3481
- __name(createTypescriptPlugin, "createTypescriptPlugin");
3773
+ __name(deleteStalePaths, "deleteStalePaths");
3774
+ function stableSubConfig(config) {
3775
+ return JSON.stringify(config ?? null);
3776
+ }
3777
+ __name(stableSubConfig, "stableSubConfig");
3482
3778
  export {
3779
+ TYPESCRIPT_CODEGEN_VERSION,
3483
3780
  createTypescriptPlugin,
3484
3781
  index_default as default
3485
3782
  };