@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.
- package/.turbo/turbo-build$colon$ci.log +4 -4
- package/.turbo/turbo-test$colon$ci.log +14 -14
- package/CHANGELOG.md +13 -0
- package/dist/codegen-operation.d.ts +9 -1
- package/dist/codegen-operation.d.ts.map +1 -1
- package/dist/index.js +39 -65
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/codegen-operation.ts +46 -81
- package/tests/codegen-operation.test.ts +63 -3
|
@@ -5,9 +5,9 @@ $ tsup src/index.ts --format esm --sourcemap --dts && tsc --emitDeclarationOnly
|
|
|
5
5
|
[34mCLI[39m tsup v8.5.1
|
|
6
6
|
[34mCLI[39m Target: esnext
|
|
7
7
|
[34mESM[39m Build start
|
|
8
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
9
|
-
[32mESM[39m [1mdist/index.js.map [22m[
|
|
10
|
-
[32mESM[39m ⚡️ Build success in
|
|
8
|
+
[32mESM[39m [1mdist/index.js [22m[32m176.34 KB[39m
|
|
9
|
+
[32mESM[39m [1mdist/index.js.map [22m[32m392.55 KB[39m
|
|
10
|
+
[32mESM[39m ⚡️ Build success in 283ms
|
|
11
11
|
[34mDTS[39m Build start
|
|
12
|
-
[32mDTS[39m ⚡️ Build success in
|
|
12
|
+
[32mDTS[39m ⚡️ Build success in 7018ms
|
|
13
13
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m4.55 KB[39m
|
|
@@ -3,29 +3,29 @@ $ vitest run --coverage
|
|
|
3
3
|
[1m[30m[46m RUN [49m[39m[22m [36mv4.1.5 [39m[90m/home/runner/work/ContractKit/ContractKit/packages/plugin-typescript[39m
|
|
4
4
|
[2mCoverage enabled with [22m[33mv8[39m
|
|
5
5
|
|
|
6
|
-
[32m✓[39m tests/codegen-contract.test.ts [2m([22m[2m128 tests[22m[2m)[22m[32m
|
|
7
|
-
[32m✓[39m tests/codegen-sdk.test.ts [2m([22m[2m140 tests[22m[2m)[22m[32m
|
|
8
|
-
[32m✓[39m tests/codegen-operation.test.ts [2m([22m[
|
|
9
|
-
[32m✓[39m tests/codegen-
|
|
10
|
-
[32m✓[39m tests/codegen-
|
|
11
|
-
[32m✓[39m tests/pipeline.test.ts [2m([22m[2m28 tests[22m[2m)[22m[32m
|
|
12
|
-
[32m✓[39m tests/
|
|
13
|
-
[32m✓[39m tests/
|
|
6
|
+
[32m✓[39m tests/codegen-contract.test.ts [2m([22m[2m128 tests[22m[2m)[22m[32m 300[2mms[22m[39m
|
|
7
|
+
[32m✓[39m tests/codegen-sdk.test.ts [2m([22m[2m140 tests[22m[2m)[22m[32m 255[2mms[22m[39m
|
|
8
|
+
[32m✓[39m tests/codegen-operation.test.ts [2m([22m[2m117 tests[22m[2m)[22m[32m 165[2mms[22m[39m
|
|
9
|
+
[32m✓[39m tests/codegen-plain-types.test.ts [2m([22m[2m66 tests[22m[2m)[22m[32m 41[2mms[22m[39m
|
|
10
|
+
[32m✓[39m tests/codegen-server.test.ts [2m([22m[2m25 tests[22m[2m)[22m[32m 90[2mms[22m[39m
|
|
11
|
+
[32m✓[39m tests/pipeline.test.ts [2m([22m[2m28 tests[22m[2m)[22m[32m 234[2mms[22m[39m
|
|
12
|
+
[32m✓[39m tests/codegen-mcp.test.ts [2m([22m[2m24 tests[22m[2m)[22m[32m 70[2mms[22m[39m
|
|
13
|
+
[32m✓[39m tests/escaping-security.test.ts [2m([22m[2m18 tests[22m[2m)[22m[32m 52[2mms[22m[39m
|
|
14
14
|
|
|
15
15
|
[2m Test Files [22m [1m[32m8 passed[39m[22m[90m (8)[39m
|
|
16
|
-
[2m Tests [22m [1m[
|
|
17
|
-
[2m Start at [22m
|
|
18
|
-
[2m Duration [22m
|
|
16
|
+
[2m Tests [22m [1m[32m546 passed[39m[22m[90m (546)[39m
|
|
17
|
+
[2m Start at [22m 15:07:03
|
|
18
|
+
[2m Duration [22m 10.03s[2m (transform 6.46s, setup 0ms, import 19.18s, tests 1.21s, environment 1ms)[22m
|
|
19
19
|
|
|
20
20
|
[34m % [39m[2mCoverage report from [22m[33mv8[39m
|
|
21
21
|
-------------------|---------|----------|---------|---------|-------------------
|
|
22
22
|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
|
|
23
23
|
-------------------|---------|----------|---------|---------|-------------------
|
|
24
|
-
All files | 82.
|
|
25
|
-
src | 82.
|
|
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 |
|
|
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
|
-
/**
|
|
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;
|
|
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
|
}
|