@contractkit/plugin-typescript 0.29.0 → 0.30.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.
@@ -5,9 +5,9 @@ $ tsup src/index.ts --format esm --sourcemap --dts && tsc --emitDeclarationOnly
5
5
  CLI tsup v8.5.1
6
6
  CLI Target: esnext
7
7
  ESM Build start
8
- ESM dist/index.js 178.19 KB
9
- ESM dist/index.js.map 395.78 KB
10
- ESM ⚡️ Build success in 396ms
8
+ ESM dist/index.js 176.34 KB
9
+ ESM dist/index.js.map 392.55 KB
10
+ ESM ⚡️ Build success in 283ms
11
11
  DTS Build start
12
- DTS ⚡️ Build success in 6065ms
12
+ DTS ⚡️ Build success in 7018ms
13
13
  DTS dist/index.d.ts 4.55 KB
@@ -3,29 +3,29 @@ $ vitest run --coverage
3
3
   RUN  v4.1.5 /home/runner/work/ContractKit/ContractKit/packages/plugin-typescript
4
4
  Coverage enabled with v8
5
5
 
6
- ✓ tests/codegen-contract.test.ts (128 tests) 221ms
7
- ✓ tests/codegen-sdk.test.ts (140 tests) 252ms
8
- ✓ tests/codegen-operation.test.ts (113 tests) 255ms
9
- ✓ tests/codegen-server.test.ts (25 tests) 110ms
10
- ✓ tests/codegen-plain-types.test.ts (66 tests) 35ms
11
- ✓ tests/pipeline.test.ts (28 tests) 217ms
12
- ✓ tests/escaping-security.test.ts (18 tests) 27ms
13
- ✓ tests/codegen-mcp.test.ts (24 tests) 67ms
6
+ ✓ tests/codegen-contract.test.ts (128 tests) 300ms
7
+ ✓ tests/codegen-sdk.test.ts (140 tests) 255ms
8
+ ✓ tests/codegen-operation.test.ts (117 tests) 165ms
9
+ ✓ tests/codegen-plain-types.test.ts (66 tests) 41ms
10
+ ✓ tests/codegen-server.test.ts (25 tests) 90ms
11
+ ✓ tests/pipeline.test.ts (28 tests) 234ms
12
+ ✓ tests/codegen-mcp.test.ts (24 tests) 70ms
13
+ ✓ tests/escaping-security.test.ts (18 tests) 52ms
14
14
 
15
15
   Test Files  8 passed (8)
16
-  Tests  542 passed (542)
17
-  Start at  12:07:42
18
-  Duration  9.60s (transform 5.83s, setup 0ms, import 18.92s, tests 1.18s, environment 1ms)
16
+  Tests  546 passed (546)
17
+  Start at  15:07:03
18
+  Duration  10.03s (transform 6.46s, setup 0ms, import 19.18s, tests 1.21s, environment 1ms)
19
19
 
20
20
   % Coverage report from v8
21
21
  -------------------|---------|----------|---------|---------|-------------------
22
22
  File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
23
23
  -------------------|---------|----------|---------|---------|-------------------
24
- All files | 82.66 | 77.13 | 85.59 | 85.15 |
25
- src | 82.52 | 76.89 | 85.35 | 85 |
24
+ All files | 82.42 | 76.7 | 85.01 | 85.02 |
25
+ src | 82.27 | 76.44 | 84.72 | 84.86 |
26
26
  ...n-contract.ts | 88.1 | 82.91 | 90.08 | 89.26 | ...1095,1100-1101
27
27
  codegen-mcp.ts | 82.67 | 75.47 | 84.61 | 85.9 | ...85,290-291,374
28
- ...-operation.ts | 80.12 | 77.35 | 79.22 | 81.36 | ...59-770,775-776
28
+ ...-operation.ts | 78.36 | 75 | 72.88 | 80.34 | ...65-776,781-782
29
29
  ...lain-types.ts | 91.57 | 81.98 | 96.87 | 94.44 | ...31,157,269,275
30
30
  codegen-sdk.ts | 88.37 | 83.2 | 87.83 | 91.48 | ...1187-1188,1191
31
31
  index.ts | 66.25 | 53.64 | 72.72 | 70.29 | ...1022,1038-1057
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @contractkit/contractkit-plugin-typescript
2
2
 
3
+ ## 0.30.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 23e4beb: Stop emitting unused imports in generated Koa routers. `bodyParserMiddleware` was imported unconditionally even when no operation declared a request body, and `MultipartBody` was imported whenever any body was multipart — including when the multipart body is structurally equal to its sibling MIME types and the handler collapses to a single `parseAndValidate` call that never references it. `requirePolicy`, `requireSignature` and `parseAndValidate` could also go unused when `includeInternal: false` skipped the only handlers that needed them. Each unused import trips `noUnusedLocals` and lint in the consuming project.
8
+
9
+ Imports are now derived from the generated body — a symbol is emitted only if it actually appears in the output — rather than from predicates over the AST that had to be kept in step by hand.
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies [23e4beb]
14
+ - @contractkit/core@0.24.0
15
+
3
16
  ## 0.29.0
4
17
 
5
18
  ### Minor Changes
@@ -22,7 +22,15 @@ export interface OpCodegenOptions {
22
22
  */
23
23
  includeInternal?: boolean;
24
24
  }
25
- /** Generate a Koa router module for every operation in `root`, including the imports, type aliases, and handler list. */
25
+ /**
26
+ * Generate a Koa router module for every operation in `root`, including the imports, type
27
+ * aliases, and handler list.
28
+ *
29
+ * Imports are derived from the generated body — each candidate symbol is emitted only if it
30
+ * actually appears in the output. Deciding them from predicates over the AST instead means any
31
+ * drift between predicate and codegen leaves an unused import in every generated file, which
32
+ * trips `noUnusedLocals` and lint downstream.
33
+ */
26
34
  export declare function generateOp(root: OpRootNode, options?: OpCodegenOptions): string;
27
35
  /**
28
36
  * Resolve the service class and method a handler should delegate to.
@@ -1 +1 @@
1
- {"version":3,"file":"codegen-operation.d.ts","sourceRoot":"","sources":["../src/codegen-operation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAA2C,MAAM,mBAAmB,CAAC;AAmC7I;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAkE5F;AAID,8EAA8E;AAC9E,MAAM,WAAW,gBAAgB;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,qFAAqF;IACrF,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,yHAAyH;AACzH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAuFnF;AAyKD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAY7H;AAoBD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,eAAe,GAAG,MAAM,CAqBzE;AAiaD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWnD;AAMD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ/E"}
1
+ {"version":3,"file":"codegen-operation.d.ts","sourceRoot":"","sources":["../src/codegen-operation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAA2C,MAAM,mBAAmB,CAAC;AAkC7I;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAkE5F;AAID,8EAA8E;AAC9E,MAAM,WAAW,gBAAgB;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,qFAAqF;IACrF,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAsFnF;AAyKD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAY7H;AAoBD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,eAAe,GAAG,MAAM,CAqBzE;AAwXD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWnD;AAMD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ/E"}
package/dist/index.js CHANGED
@@ -1182,37 +1182,6 @@ function generateOp(root, options = {}) {
1182
1182
  const types = collectTypes(root, options.modelsWithInput, options.modelsWithOutput);
1183
1183
  const services = collectServices(root);
1184
1184
  const routerName = deriveRouterName(root.file);
1185
- const needsParseAndValidate = routeNeedsValidation(root);
1186
- const body = [];
1187
- const needsSignature = fileNeedsSignature(root);
1188
- const needsPolicy = fileNeedsPolicy(root);
1189
- const koaImports = [
1190
- "ServerKitRouter",
1191
- "bodyParserMiddleware"
1192
- ];
1193
- if (needsPolicy) koaImports.push("requirePolicy");
1194
- if (needsSignature) koaImports.push("requireSignature");
1195
- body.push(`import { ${koaImports.join(", ")} } from '@maroonedsoftware/koa';`);
1196
- for (const svc of services) {
1197
- const modulePath = root.services?.[svc] ?? root.meta[svc] ?? deriveModulePath(svc, options.servicePathTemplate);
1198
- body.push(`import { ${svc} } from '${modulePath}';`);
1199
- }
1200
- if (types.length > 0) {
1201
- body.push(...generateTypeImports(types, root.file, options));
1202
- }
1203
- const luxonImports = [];
1204
- if (opNeedsDateTime(root)) luxonImports.push("DateTime");
1205
- if (opNeedsScalar(root, "duration")) luxonImports.push("Duration");
1206
- if (opNeedsScalar(root, "interval")) luxonImports.push("Interval");
1207
- if (luxonImports.length > 0) {
1208
- body.push(`import { ${luxonImports.join(", ")} } from 'luxon';`);
1209
- }
1210
- if (needsParseAndValidate) {
1211
- body.push(`import { parseAndValidate } from '@maroonedsoftware/zod';`);
1212
- }
1213
- if (fileUsesMultipart(root)) {
1214
- body.push(`import { MultipartBody } from '@maroonedsoftware/multipart';`);
1215
- }
1216
1185
  const helpers = [];
1217
1186
  if (opNeedsScalar(root, "binary")) {
1218
1187
  helpers.push(`const _ZodBinary = z.custom<Buffer>((val) => Buffer.isBuffer(val), { error: 'Must be binary data' });`);
@@ -1243,6 +1212,45 @@ function generateOp(root, options = {}) {
1243
1212
  lines.push("");
1244
1213
  }
1245
1214
  }
1215
+ const generated = [
1216
+ ...helpers.length ? [
1217
+ "",
1218
+ ...helpers
1219
+ ] : [],
1220
+ ...lines
1221
+ ].join("\n");
1222
+ const uses = /* @__PURE__ */ __name((symbol) => new RegExp(`\\b${symbol}\\b`).test(generated), "uses");
1223
+ const body = [];
1224
+ const koaImports = [
1225
+ "ServerKitRouter",
1226
+ "bodyParserMiddleware",
1227
+ "requirePolicy",
1228
+ "requireSignature"
1229
+ ].filter(uses);
1230
+ if (koaImports.length > 0) {
1231
+ body.push(`import { ${koaImports.join(", ")} } from '@maroonedsoftware/koa';`);
1232
+ }
1233
+ for (const svc of services) {
1234
+ const modulePath = root.services?.[svc] ?? root.meta[svc] ?? deriveModulePath(svc, options.servicePathTemplate);
1235
+ body.push(`import { ${svc} } from '${modulePath}';`);
1236
+ }
1237
+ if (types.length > 0) {
1238
+ body.push(...generateTypeImports(types, root.file, options));
1239
+ }
1240
+ const luxonImports = [
1241
+ "DateTime",
1242
+ "Duration",
1243
+ "Interval"
1244
+ ].filter(uses);
1245
+ if (luxonImports.length > 0) {
1246
+ body.push(`import { ${luxonImports.join(", ")} } from 'luxon';`);
1247
+ }
1248
+ if (uses("parseAndValidate")) {
1249
+ body.push(`import { parseAndValidate } from '@maroonedsoftware/zod';`);
1250
+ }
1251
+ if (uses("MultipartBody")) {
1252
+ body.push(`import { MultipartBody } from '@maroonedsoftware/multipart';`);
1253
+ }
1246
1254
  const allContent = [
1247
1255
  ...body,
1248
1256
  ...helpers.length ? [
@@ -1716,17 +1724,6 @@ function collectTypeNodeRefs(type, out) {
1716
1724
  }
1717
1725
  }
1718
1726
  __name(collectTypeNodeRefs, "collectTypeNodeRefs");
1719
- function paramSourceNeedsDateTime(source) {
1720
- if (!source) return false;
1721
- if (source.kind === "ref") return false;
1722
- if (source.kind === "params") return source.nodes.some((p) => typeNeedsDateTime(p.type));
1723
- return typeNeedsDateTime(source.node);
1724
- }
1725
- __name(paramSourceNeedsDateTime, "paramSourceNeedsDateTime");
1726
- function opNeedsDateTime(root) {
1727
- return root.routes.some((route) => paramSourceNeedsDateTime(route.params) || route.operations.some((op) => !!op.request?.bodies.some((b) => typeNeedsDateTime(b.bodyType)) || op.responses.some((r) => r.bodyType && typeNeedsDateTime(r.bodyType)) || paramSourceNeedsDateTime(op.query) || paramSourceNeedsDateTime(op.headers)));
1728
- }
1729
- __name(opNeedsDateTime, "opNeedsDateTime");
1730
1727
  function paramSourceNeedsScalar(source, name) {
1731
1728
  if (!source) return false;
1732
1729
  if (source.kind === "ref") return false;
@@ -1755,29 +1752,6 @@ function collectServices(root) {
1755
1752
  ].sort();
1756
1753
  }
1757
1754
  __name(collectServices, "collectServices");
1758
- function hasParamSource(source) {
1759
- if (!source) return false;
1760
- if (source.kind === "ref") return true;
1761
- if (source.kind === "params") return source.nodes.length > 0;
1762
- return true;
1763
- }
1764
- __name(hasParamSource, "hasParamSource");
1765
- function routeNeedsValidation(root) {
1766
- return root.routes.some((r) => hasParamSource(r.params) || r.operations.some((op) => !!op.request || hasParamSource(op.query) || hasParamSource(op.headers)));
1767
- }
1768
- __name(routeNeedsValidation, "routeNeedsValidation");
1769
- function fileNeedsPolicy(root) {
1770
- return root.routes.some((route) => route.operations.some((op) => resolveSecurity(route, op, root) !== SECURITY_NONE));
1771
- }
1772
- __name(fileNeedsPolicy, "fileNeedsPolicy");
1773
- function fileNeedsSignature(root) {
1774
- return root.routes.some((route) => route.operations.some((op) => !!op.signature));
1775
- }
1776
- __name(fileNeedsSignature, "fileNeedsSignature");
1777
- function fileUsesMultipart(root) {
1778
- return root.routes.some((route) => route.operations.some((op) => (op.request?.bodies ?? []).some((b) => b.contentType === "multipart/form-data")));
1779
- }
1780
- __name(fileUsesMultipart, "fileUsesMultipart");
1781
1755
  function isValidIdentifier2(name) {
1782
1756
  return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
1783
1757
  }