ts-procedures 6.0.2 → 6.2.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/agent_config/bin/setup.mjs +2 -2
- package/agent_config/claude-code/skills/ts-procedures/SKILL.md +2 -0
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +2 -0
- package/agent_config/claude-code/skills/ts-procedures-kotlin/SKILL.md +106 -0
- package/agent_config/claude-code/skills/ts-procedures-swift/SKILL.md +119 -0
- package/agent_config/copilot/copilot-instructions.md +3 -0
- package/agent_config/cursor/cursorrules +3 -0
- package/agent_config/lib/install-claude.mjs +1 -1
- package/build/codegen/bin/cli.d.ts +39 -0
- package/build/codegen/bin/cli.js +164 -0
- package/build/codegen/bin/cli.js.map +1 -1
- package/build/codegen/bin/cli.test.js +180 -1
- package/build/codegen/bin/cli.test.js.map +1 -1
- package/build/codegen/index.d.ts +36 -0
- package/build/codegen/index.js +8 -0
- package/build/codegen/index.js.map +1 -1
- package/build/codegen/pipeline.d.ts +22 -4
- package/build/codegen/pipeline.js +44 -86
- package/build/codegen/pipeline.js.map +1 -1
- package/build/codegen/pipeline.test.js +162 -0
- package/build/codegen/pipeline.test.js.map +1 -1
- package/build/codegen/targets/_shared/error-schemas.d.ts +10 -0
- package/build/codegen/targets/_shared/error-schemas.js +17 -0
- package/build/codegen/targets/_shared/error-schemas.js.map +1 -0
- package/build/codegen/targets/_shared/error-schemas.test.d.ts +1 -0
- package/build/codegen/targets/_shared/error-schemas.test.js +38 -0
- package/build/codegen/targets/_shared/error-schemas.test.js.map +1 -0
- package/build/codegen/targets/_shared/indent.d.ts +6 -0
- package/build/codegen/targets/_shared/indent.js +13 -0
- package/build/codegen/targets/_shared/indent.js.map +1 -0
- package/build/codegen/targets/_shared/indent.test.d.ts +1 -0
- package/build/codegen/targets/_shared/indent.test.js +21 -0
- package/build/codegen/targets/_shared/indent.test.js.map +1 -0
- package/build/codegen/targets/_shared/pascal-case.d.ts +6 -0
- package/build/codegen/targets/_shared/pascal-case.js +13 -0
- package/build/codegen/targets/_shared/pascal-case.js.map +1 -0
- package/build/codegen/targets/_shared/pascal-case.test.d.ts +1 -0
- package/build/codegen/targets/_shared/pascal-case.test.js +25 -0
- package/build/codegen/targets/_shared/pascal-case.test.js.map +1 -0
- package/build/codegen/targets/_shared/path-utils.d.ts +12 -0
- package/build/codegen/targets/_shared/path-utils.js +20 -0
- package/build/codegen/targets/_shared/path-utils.js.map +1 -0
- package/build/codegen/targets/_shared/path-utils.test.d.ts +1 -0
- package/build/codegen/targets/_shared/path-utils.test.js +42 -0
- package/build/codegen/targets/_shared/path-utils.test.js.map +1 -0
- package/build/codegen/targets/_shared/pick-defined.d.ts +11 -0
- package/build/codegen/targets/_shared/pick-defined.js +21 -0
- package/build/codegen/targets/_shared/pick-defined.js.map +1 -0
- package/build/codegen/targets/_shared/pick-defined.test.d.ts +1 -0
- package/build/codegen/targets/_shared/pick-defined.test.js +25 -0
- package/build/codegen/targets/_shared/pick-defined.test.js.map +1 -0
- package/build/codegen/targets/_shared/route-slots.d.ts +17 -0
- package/build/codegen/targets/_shared/route-slots.js +17 -0
- package/build/codegen/targets/_shared/route-slots.js.map +1 -0
- package/build/codegen/targets/_shared/route-slots.test.d.ts +1 -0
- package/build/codegen/targets/_shared/route-slots.test.js +43 -0
- package/build/codegen/targets/_shared/route-slots.test.js.map +1 -0
- package/build/codegen/targets/_shared/target-run.d.ts +27 -0
- package/build/codegen/targets/_shared/target-run.js +2 -0
- package/build/codegen/targets/_shared/target-run.js.map +1 -0
- package/build/codegen/targets/_shared/write-files.d.ts +24 -0
- package/build/codegen/targets/_shared/write-files.js +35 -0
- package/build/codegen/targets/_shared/write-files.js.map +1 -0
- package/build/codegen/targets/_shared/write-files.test.d.ts +1 -0
- package/build/codegen/targets/_shared/write-files.test.js +79 -0
- package/build/codegen/targets/_shared/write-files.test.js.map +1 -0
- package/build/codegen/targets/kotlin/ajsc-adapter.d.ts +6 -4
- package/build/codegen/targets/kotlin/ajsc-adapter.js +12 -7
- package/build/codegen/targets/kotlin/ajsc-adapter.js.map +1 -1
- package/build/codegen/targets/kotlin/ajsc-adapter.test.js +20 -2
- package/build/codegen/targets/kotlin/ajsc-adapter.test.js.map +1 -1
- package/build/codegen/targets/kotlin/e2e-compile.test.js +41 -9
- package/build/codegen/targets/kotlin/e2e-compile.test.js.map +1 -1
- package/build/codegen/targets/kotlin/emit-route-kotlin.d.ts +6 -2
- package/build/codegen/targets/kotlin/emit-route-kotlin.js +18 -28
- package/build/codegen/targets/kotlin/emit-route-kotlin.js.map +1 -1
- package/build/codegen/targets/kotlin/emit-route-kotlin.test.js +120 -1
- package/build/codegen/targets/kotlin/emit-route-kotlin.test.js.map +1 -1
- package/build/codegen/targets/kotlin/emit-scope-kotlin.d.ts +4 -1
- package/build/codegen/targets/kotlin/emit-scope-kotlin.js +12 -11
- package/build/codegen/targets/kotlin/emit-scope-kotlin.js.map +1 -1
- package/build/codegen/targets/kotlin/emit-scope-kotlin.test.js +39 -0
- package/build/codegen/targets/kotlin/emit-scope-kotlin.test.js.map +1 -1
- package/build/codegen/targets/kotlin/format-kotlin.d.ts +0 -1
- package/build/codegen/targets/kotlin/format-kotlin.js +0 -7
- package/build/codegen/targets/kotlin/format-kotlin.js.map +1 -1
- package/build/codegen/targets/kotlin/format-kotlin.test.js +1 -8
- package/build/codegen/targets/kotlin/format-kotlin.test.js.map +1 -1
- package/build/codegen/targets/kotlin/integration.test.js +27 -10
- package/build/codegen/targets/kotlin/integration.test.js.map +1 -1
- package/build/codegen/targets/kotlin/probe-unsupported-unions.test.d.ts +1 -0
- package/build/codegen/targets/kotlin/probe-unsupported-unions.test.js +50 -0
- package/build/codegen/targets/kotlin/probe-unsupported-unions.test.js.map +1 -0
- package/build/codegen/targets/kotlin/run.d.ts +11 -0
- package/build/codegen/targets/kotlin/run.js +51 -0
- package/build/codegen/targets/kotlin/run.js.map +1 -0
- package/build/codegen/targets/swift/access-level.test.d.ts +1 -0
- package/build/codegen/targets/swift/access-level.test.js +98 -0
- package/build/codegen/targets/swift/access-level.test.js.map +1 -0
- package/build/codegen/targets/swift/ajsc-adapter.d.ts +27 -0
- package/build/codegen/targets/swift/ajsc-adapter.js +38 -0
- package/build/codegen/targets/swift/ajsc-adapter.js.map +1 -0
- package/build/codegen/targets/swift/ajsc-adapter.test.d.ts +1 -0
- package/build/codegen/targets/swift/ajsc-adapter.test.js +37 -0
- package/build/codegen/targets/swift/ajsc-adapter.test.js.map +1 -0
- package/build/codegen/targets/swift/e2e-compile.test.d.ts +1 -0
- package/build/codegen/targets/swift/e2e-compile.test.js +57 -0
- package/build/codegen/targets/swift/e2e-compile.test.js.map +1 -0
- package/build/codegen/targets/swift/emit-route-swift.d.ts +15 -0
- package/build/codegen/targets/swift/emit-route-swift.js +64 -0
- package/build/codegen/targets/swift/emit-route-swift.js.map +1 -0
- package/build/codegen/targets/swift/emit-route-swift.test.d.ts +1 -0
- package/build/codegen/targets/swift/emit-route-swift.test.js +258 -0
- package/build/codegen/targets/swift/emit-route-swift.test.js.map +1 -0
- package/build/codegen/targets/swift/emit-scope-swift.d.ts +13 -0
- package/build/codegen/targets/swift/emit-scope-swift.js +36 -0
- package/build/codegen/targets/swift/emit-scope-swift.js.map +1 -0
- package/build/codegen/targets/swift/emit-scope-swift.test.d.ts +1 -0
- package/build/codegen/targets/swift/emit-scope-swift.test.js +136 -0
- package/build/codegen/targets/swift/emit-scope-swift.test.js.map +1 -0
- package/build/codegen/targets/swift/format-swift.d.ts +2 -0
- package/build/codegen/targets/swift/format-swift.js +10 -0
- package/build/codegen/targets/swift/format-swift.js.map +1 -0
- package/build/codegen/targets/swift/format-swift.test.d.ts +1 -0
- package/build/codegen/targets/swift/format-swift.test.js +14 -0
- package/build/codegen/targets/swift/format-swift.test.js.map +1 -0
- package/build/codegen/targets/swift/integration.test.d.ts +1 -0
- package/build/codegen/targets/swift/integration.test.js +53 -0
- package/build/codegen/targets/swift/integration.test.js.map +1 -0
- package/build/codegen/targets/swift/run.d.ts +11 -0
- package/build/codegen/targets/swift/run.js +47 -0
- package/build/codegen/targets/swift/run.js.map +1 -0
- package/build/codegen/targets/ts/run.d.ts +4 -0
- package/build/codegen/targets/ts/run.js +86 -0
- package/build/codegen/targets/ts/run.js.map +1 -0
- package/build/codegen/test-helpers/golden.d.ts +15 -0
- package/build/codegen/test-helpers/golden.js +30 -0
- package/build/codegen/test-helpers/golden.js.map +1 -0
- package/build/codegen/test-helpers/golden.test.d.ts +1 -0
- package/build/codegen/test-helpers/golden.test.js +76 -0
- package/build/codegen/test-helpers/golden.test.js.map +1 -0
- package/docs/codegen-kotlin.md +176 -0
- package/docs/codegen-swift.md +314 -0
- package/docs/superpowers/plans/2026-04-25-ajsc-v7-kotlin-polish.md +1993 -0
- package/docs/superpowers/specs/2026-04-24-kotlin-swift-codegen-design.md +1 -1
- package/docs/superpowers/specs/2026-04-25-ajsc-v7-kotlin-polish-design.md +314 -0
- package/docs/superpowers/specs/2026-04-25-swift-codegen-design.md +264 -0
- package/package.json +2 -2
- package/src/codegen/__fixtures__/users-envelope.json +144 -0
- package/src/codegen/bin/cli.test.ts +200 -1
- package/src/codegen/bin/cli.ts +187 -0
- package/src/codegen/index.ts +50 -0
- package/src/codegen/pipeline.test.ts +175 -0
- package/src/codegen/pipeline.ts +58 -101
- package/src/codegen/targets/_shared/error-schemas.test.ts +42 -0
- package/src/codegen/targets/_shared/error-schemas.ts +17 -0
- package/src/codegen/targets/_shared/indent.test.ts +25 -0
- package/src/codegen/targets/_shared/indent.ts +12 -0
- package/src/codegen/targets/_shared/pascal-case.test.ts +30 -0
- package/src/codegen/targets/_shared/pascal-case.ts +12 -0
- package/src/codegen/targets/_shared/path-utils.test.ts +51 -0
- package/src/codegen/targets/_shared/path-utils.ts +21 -0
- package/src/codegen/targets/_shared/pick-defined.test.ts +48 -0
- package/src/codegen/targets/_shared/pick-defined.ts +23 -0
- package/src/codegen/targets/_shared/route-slots.test.ts +55 -0
- package/src/codegen/targets/_shared/route-slots.ts +32 -0
- package/src/codegen/targets/_shared/target-run.ts +28 -0
- package/src/codegen/targets/_shared/write-files.test.ts +110 -0
- package/src/codegen/targets/_shared/write-files.ts +53 -0
- package/src/codegen/targets/kotlin/__fixtures__/users-golden.kt +121 -0
- package/src/codegen/targets/kotlin/__snapshots__/probe-unsupported-unions.test.ts.snap +27 -0
- package/src/codegen/targets/kotlin/ajsc-adapter.test.ts +47 -0
- package/src/codegen/targets/kotlin/ajsc-adapter.ts +66 -0
- package/src/codegen/targets/kotlin/e2e-compile.test.ts +86 -0
- package/src/codegen/targets/kotlin/emit-route-kotlin.test.ts +239 -0
- package/src/codegen/targets/kotlin/emit-route-kotlin.ts +89 -0
- package/src/codegen/targets/kotlin/emit-scope-kotlin.test.ts +112 -0
- package/src/codegen/targets/kotlin/emit-scope-kotlin.ts +60 -0
- package/src/codegen/targets/kotlin/format-kotlin.test.ts +26 -0
- package/src/codegen/targets/kotlin/format-kotlin.ts +13 -0
- package/src/codegen/targets/kotlin/integration.test.ts +77 -0
- package/src/codegen/targets/kotlin/probe-unsupported-unions.test.ts +64 -0
- package/src/codegen/targets/kotlin/run.ts +78 -0
- package/src/codegen/targets/swift/__fixtures__/users-golden.swift +123 -0
- package/src/codegen/targets/swift/access-level.test.ts +108 -0
- package/src/codegen/targets/swift/ajsc-adapter.test.ts +47 -0
- package/src/codegen/targets/swift/ajsc-adapter.ts +67 -0
- package/src/codegen/targets/swift/e2e-compile.test.ts +66 -0
- package/src/codegen/targets/swift/emit-route-swift.test.ts +300 -0
- package/src/codegen/targets/swift/emit-route-swift.ts +90 -0
- package/src/codegen/targets/swift/emit-scope-swift.test.ts +164 -0
- package/src/codegen/targets/swift/emit-scope-swift.ts +59 -0
- package/src/codegen/targets/swift/format-swift.test.ts +23 -0
- package/src/codegen/targets/swift/format-swift.ts +9 -0
- package/src/codegen/targets/swift/integration.test.ts +80 -0
- package/src/codegen/targets/swift/run.ts +74 -0
- package/src/codegen/targets/ts/run.ts +117 -0
- package/src/codegen/test-helpers/golden.test.ts +80 -0
- package/src/codegen/test-helpers/golden.ts +34 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AnyHttpRouteDoc } from '../../../implementations/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A "slot" is one of the deterministic schema sources a route exposes:
|
|
4
|
+
* `pathParams`, `query`, `body` (under `schema.input`) and the response
|
|
5
|
+
* (`schema.returnType`). Targets emit one type per non-null slot.
|
|
6
|
+
*/
|
|
7
|
+
export interface RouteSlot {
|
|
8
|
+
/** Stable identifier used as the emitted type's `rootTypeName`. */
|
|
9
|
+
rootName: string;
|
|
10
|
+
/** The raw JSON Schema source for this slot, or `null`/`undefined` when absent. */
|
|
11
|
+
source: unknown;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Returns the deterministic ordered slot list for a route, filtered to slots
|
|
15
|
+
* with non-null sources. Order is fixed at the module level for stable output.
|
|
16
|
+
*/
|
|
17
|
+
export declare function extractRouteSlots(route: AnyHttpRouteDoc): RouteSlot[];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the deterministic ordered slot list for a route, filtered to slots
|
|
3
|
+
* with non-null sources. Order is fixed at the module level for stable output.
|
|
4
|
+
*/
|
|
5
|
+
export function extractRouteSlots(route) {
|
|
6
|
+
const schema = route.schema ?? {};
|
|
7
|
+
const input = (schema.input ?? {});
|
|
8
|
+
// Order is load-bearing: targets emit slots in this sequence.
|
|
9
|
+
const slots = [
|
|
10
|
+
{ rootName: 'PathParams', source: input.pathParams },
|
|
11
|
+
{ rootName: 'Query', source: input.query },
|
|
12
|
+
{ rootName: 'Body', source: input.body },
|
|
13
|
+
{ rootName: 'Response', source: schema.returnType },
|
|
14
|
+
];
|
|
15
|
+
return slots.filter((s) => s.source != null);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=route-slots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-slots.js","sourceRoot":"","sources":["../../../../src/codegen/targets/_shared/route-slots.ts"],"names":[],"mappings":"AAcA;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAsB;IACtD,MAAM,MAAM,GAAI,KAA8C,CAAC,MAAM,IAAI,EAAE,CAAA;IAC3E,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAA4B,CAAA;IAE7D,8DAA8D;IAC9D,MAAM,KAAK,GAAgB;QACzB,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE;QACpD,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE;QAC1C,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE;QACxC,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE;KACpD,CAAA;IAED,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,CAAA;AAC9C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { extractRouteSlots } from './route-slots.js';
|
|
3
|
+
const r = (schema) => ({ name: 'X', kind: 'api', method: 'GET', fullPath: '/x', schema });
|
|
4
|
+
describe('extractRouteSlots', () => {
|
|
5
|
+
it('returns no slots when schema is undefined', () => {
|
|
6
|
+
expect(extractRouteSlots(r(undefined))).toEqual([]);
|
|
7
|
+
});
|
|
8
|
+
it('returns no slots when schema is empty', () => {
|
|
9
|
+
expect(extractRouteSlots(r({}))).toEqual([]);
|
|
10
|
+
});
|
|
11
|
+
it('returns the deterministic slot order: PathParams, Query, Body, Response', () => {
|
|
12
|
+
const slots = extractRouteSlots(r({
|
|
13
|
+
input: {
|
|
14
|
+
body: { type: 'object', x: 'body' },
|
|
15
|
+
query: { type: 'object', x: 'query' },
|
|
16
|
+
pathParams: { type: 'object', x: 'path' },
|
|
17
|
+
},
|
|
18
|
+
returnType: { type: 'object', x: 'response' },
|
|
19
|
+
}));
|
|
20
|
+
expect(slots.map((s) => s.rootName)).toEqual(['PathParams', 'Query', 'Body', 'Response']);
|
|
21
|
+
});
|
|
22
|
+
it('omits slots whose source is null or undefined', () => {
|
|
23
|
+
const slots = extractRouteSlots(r({
|
|
24
|
+
input: { pathParams: { type: 'object' }, query: null, body: undefined },
|
|
25
|
+
// returnType missing
|
|
26
|
+
}));
|
|
27
|
+
expect(slots.map((s) => s.rootName)).toEqual(['PathParams']);
|
|
28
|
+
});
|
|
29
|
+
it('attaches the source schema verbatim to each slot', () => {
|
|
30
|
+
const path = { type: 'object', tag: 'p' };
|
|
31
|
+
const ret = { type: 'object', tag: 'r' };
|
|
32
|
+
const slots = extractRouteSlots(r({ input: { pathParams: path }, returnType: ret }));
|
|
33
|
+
expect(slots).toEqual([
|
|
34
|
+
{ rootName: 'PathParams', source: path },
|
|
35
|
+
{ rootName: 'Response', source: ret },
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
it('returns no slots for stream/no-schema routes', () => {
|
|
39
|
+
const stream = { name: 'S', kind: 'stream', method: 'GET', path: '/s' };
|
|
40
|
+
expect(extractRouteSlots(stream)).toEqual([]);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=route-slots.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-slots.test.js","sourceRoot":"","sources":["../../../../src/codegen/targets/_shared/route-slots.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { DocEnvelope } from '../../../implementations/types.js';
|
|
2
|
+
import type { ScopeGroup } from '../../group-routes.js';
|
|
3
|
+
import type { AjscOptions } from '../../emit-types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Inputs the dispatcher prepares once and forwards to every per-target
|
|
6
|
+
* `run*Pipeline` module. Each target module reads only the fields it needs.
|
|
7
|
+
*
|
|
8
|
+
* The fields here are deliberately narrow: target-specific knobs (e.g.
|
|
9
|
+
* `kotlinPackage`, `swiftSerializer`) are passed as additional options on
|
|
10
|
+
* each per-target input shape, not on this base.
|
|
11
|
+
*/
|
|
12
|
+
export interface TargetRunInput {
|
|
13
|
+
envelope: DocEnvelope;
|
|
14
|
+
outDir: string;
|
|
15
|
+
/** MD5 hash of the envelope JSON, prefix-stamped into every emitted file. */
|
|
16
|
+
hash: string;
|
|
17
|
+
/** Pre-grouped routes (one entry per scope key). */
|
|
18
|
+
groups: ScopeGroup[];
|
|
19
|
+
/** Validated service name (defaults to `Api` upstream). */
|
|
20
|
+
serviceName: string;
|
|
21
|
+
ajsc?: AjscOptions;
|
|
22
|
+
clientImportPath?: string;
|
|
23
|
+
dryRun?: boolean;
|
|
24
|
+
namespaceTypes?: boolean;
|
|
25
|
+
selfContained?: boolean;
|
|
26
|
+
cleanOutDir?: boolean;
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"target-run.js","sourceRoot":"","sources":["../../../../src/codegen/targets/_shared/target-run.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface GeneratedFile {
|
|
2
|
+
path: string;
|
|
3
|
+
code: string;
|
|
4
|
+
}
|
|
5
|
+
export interface WriteOptions {
|
|
6
|
+
/** When true, log what would happen instead of touching the filesystem. */
|
|
7
|
+
dryRun?: boolean;
|
|
8
|
+
/** When true, recursively delete `outDir` before writing. */
|
|
9
|
+
cleanOutDir?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Persists the generated files to disk (or simulates the writes under
|
|
13
|
+
* `dryRun`). Centralises the dryRun / cleanOutDir / mkdir / writeFile
|
|
14
|
+
* sequence shared by every codegen target so per-target run modules don't
|
|
15
|
+
* duplicate it.
|
|
16
|
+
*
|
|
17
|
+
* Behaviour:
|
|
18
|
+
* - `dryRun: true` — logs `[dry-run] Would write: <path> (<bytes> bytes)` per
|
|
19
|
+
* file, plus a leading `[dry-run] Would clean outDir: <outDir>` when
|
|
20
|
+
* `cleanOutDir: true`. No filesystem mutation.
|
|
21
|
+
* - `dryRun: false` (default) — when `cleanOutDir`, recursively removes
|
|
22
|
+
* `outDir`; then ensures it exists; then writes each file as utf-8.
|
|
23
|
+
*/
|
|
24
|
+
export declare function writeGeneratedFiles(files: readonly GeneratedFile[], outDir: string, opts?: WriteOptions): Promise<void>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { mkdir, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
/**
|
|
3
|
+
* Persists the generated files to disk (or simulates the writes under
|
|
4
|
+
* `dryRun`). Centralises the dryRun / cleanOutDir / mkdir / writeFile
|
|
5
|
+
* sequence shared by every codegen target so per-target run modules don't
|
|
6
|
+
* duplicate it.
|
|
7
|
+
*
|
|
8
|
+
* Behaviour:
|
|
9
|
+
* - `dryRun: true` — logs `[dry-run] Would write: <path> (<bytes> bytes)` per
|
|
10
|
+
* file, plus a leading `[dry-run] Would clean outDir: <outDir>` when
|
|
11
|
+
* `cleanOutDir: true`. No filesystem mutation.
|
|
12
|
+
* - `dryRun: false` (default) — when `cleanOutDir`, recursively removes
|
|
13
|
+
* `outDir`; then ensures it exists; then writes each file as utf-8.
|
|
14
|
+
*/
|
|
15
|
+
export async function writeGeneratedFiles(files, outDir, opts = {}) {
|
|
16
|
+
const { dryRun = false, cleanOutDir = false } = opts;
|
|
17
|
+
if (dryRun) {
|
|
18
|
+
if (cleanOutDir) {
|
|
19
|
+
console.log(`[dry-run] Would clean outDir: ${outDir}`);
|
|
20
|
+
}
|
|
21
|
+
for (const f of files) {
|
|
22
|
+
const bytes = Buffer.byteLength(f.code, 'utf-8');
|
|
23
|
+
console.log(`[dry-run] Would write: ${f.path} (${bytes} bytes)`);
|
|
24
|
+
}
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (cleanOutDir) {
|
|
28
|
+
await rm(outDir, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
await mkdir(outDir, { recursive: true });
|
|
31
|
+
for (const f of files) {
|
|
32
|
+
await writeFile(f.path, f.code, 'utf-8');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=write-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-files.js","sourceRoot":"","sources":["../../../../src/codegen/targets/_shared/write-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAcvD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAA+B,EAC/B,MAAc,EACd,OAAqB,EAAE;IAEvB,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,WAAW,GAAG,KAAK,EAAE,GAAG,IAAI,CAAA;IAEpD,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,iCAAiC,MAAM,EAAE,CAAC,CAAA;QACxD,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YAChD,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC,IAAI,KAAK,KAAK,SAAS,CAAC,CAAA;QAClE,CAAC;QACD,OAAM;IACR,CAAC;IAED,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IACpD,CAAC;IACD,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACxC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC1C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { mkdir, readFile, rm, writeFile, stat, mkdtemp } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { writeGeneratedFiles } from './write-files.js';
|
|
6
|
+
describe('writeGeneratedFiles', () => {
|
|
7
|
+
describe('dryRun mode', () => {
|
|
8
|
+
let logSpy;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
logSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
logSpy.mockRestore();
|
|
14
|
+
});
|
|
15
|
+
it('logs each file with its byte length and does not touch the filesystem', async () => {
|
|
16
|
+
const dir = '/nonexistent/should-not-be-created';
|
|
17
|
+
await writeGeneratedFiles([
|
|
18
|
+
{ path: `${dir}/a.txt`, code: 'hello' },
|
|
19
|
+
{ path: `${dir}/b.txt`, code: 'hi' },
|
|
20
|
+
], dir, { dryRun: true });
|
|
21
|
+
expect(logSpy).toHaveBeenCalledWith(`[dry-run] Would write: ${dir}/a.txt (5 bytes)`);
|
|
22
|
+
expect(logSpy).toHaveBeenCalledWith(`[dry-run] Would write: ${dir}/b.txt (2 bytes)`);
|
|
23
|
+
// Sanity: the dir was not created.
|
|
24
|
+
await expect(stat(dir)).rejects.toBeInstanceOf(Error);
|
|
25
|
+
});
|
|
26
|
+
it('logs a clean-outDir notice when cleanOutDir is true', async () => {
|
|
27
|
+
await writeGeneratedFiles([], '/out', { dryRun: true, cleanOutDir: true });
|
|
28
|
+
expect(logSpy).toHaveBeenCalledWith('[dry-run] Would clean outDir: /out');
|
|
29
|
+
});
|
|
30
|
+
it('does not log clean-outDir when cleanOutDir is false', async () => {
|
|
31
|
+
await writeGeneratedFiles([{ path: '/out/x', code: 'x' }], '/out', { dryRun: true });
|
|
32
|
+
const messages = logSpy.mock.calls.map((c) => c[0]);
|
|
33
|
+
expect(messages.find((m) => String(m).includes('Would clean'))).toBeUndefined();
|
|
34
|
+
});
|
|
35
|
+
it('counts bytes as utf-8', async () => {
|
|
36
|
+
// "é" is 2 bytes in utf-8.
|
|
37
|
+
await writeGeneratedFiles([{ path: '/x/e.txt', code: 'é' }], '/x', { dryRun: true });
|
|
38
|
+
expect(logSpy).toHaveBeenCalledWith('[dry-run] Would write: /x/e.txt (2 bytes)');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('real write mode', () => {
|
|
42
|
+
let outDir;
|
|
43
|
+
beforeEach(async () => {
|
|
44
|
+
outDir = await mkdtemp(join(tmpdir(), 'write-files-test-'));
|
|
45
|
+
});
|
|
46
|
+
afterEach(async () => {
|
|
47
|
+
await rm(outDir, { recursive: true, force: true });
|
|
48
|
+
});
|
|
49
|
+
it('writes each file with utf-8 contents', async () => {
|
|
50
|
+
await writeGeneratedFiles([
|
|
51
|
+
{ path: join(outDir, 'a.txt'), code: 'alpha' },
|
|
52
|
+
{ path: join(outDir, 'b.txt'), code: 'beta' },
|
|
53
|
+
], outDir);
|
|
54
|
+
expect(await readFile(join(outDir, 'a.txt'), 'utf-8')).toBe('alpha');
|
|
55
|
+
expect(await readFile(join(outDir, 'b.txt'), 'utf-8')).toBe('beta');
|
|
56
|
+
});
|
|
57
|
+
it('creates outDir recursively if missing', async () => {
|
|
58
|
+
const nested = join(outDir, 'a', 'b', 'c');
|
|
59
|
+
await writeGeneratedFiles([{ path: join(nested, 'x.txt'), code: 'x' }], nested);
|
|
60
|
+
expect(await readFile(join(nested, 'x.txt'), 'utf-8')).toBe('x');
|
|
61
|
+
});
|
|
62
|
+
it('removes outDir when cleanOutDir is true before writing', async () => {
|
|
63
|
+
// Pre-populate a stale file.
|
|
64
|
+
await mkdir(outDir, { recursive: true });
|
|
65
|
+
await writeFile(join(outDir, 'stale.txt'), 'stale', 'utf-8');
|
|
66
|
+
await writeGeneratedFiles([{ path: join(outDir, 'fresh.txt'), code: 'fresh' }], outDir, { cleanOutDir: true });
|
|
67
|
+
await expect(readFile(join(outDir, 'stale.txt'), 'utf-8')).rejects.toBeInstanceOf(Error);
|
|
68
|
+
expect(await readFile(join(outDir, 'fresh.txt'), 'utf-8')).toBe('fresh');
|
|
69
|
+
});
|
|
70
|
+
it('does not remove outDir when cleanOutDir is false', async () => {
|
|
71
|
+
await mkdir(outDir, { recursive: true });
|
|
72
|
+
await writeFile(join(outDir, 'keep.txt'), 'keep', 'utf-8');
|
|
73
|
+
await writeGeneratedFiles([{ path: join(outDir, 'new.txt'), code: 'new' }], outDir);
|
|
74
|
+
expect(await readFile(join(outDir, 'keep.txt'), 'utf-8')).toBe('keep');
|
|
75
|
+
expect(await readFile(join(outDir, 'new.txt'), 'utf-8')).toBe('new');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=write-files.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-files.test.js","sourceRoot":"","sources":["../../../../src/codegen/targets/_shared/write-files.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,11 +6,12 @@ export interface KotlinEmitResult {
|
|
|
6
6
|
}
|
|
7
7
|
export interface KotlinEmitOptions {
|
|
8
8
|
rootTypeName: string;
|
|
9
|
+
/** Always set true at our call sites; v7.2 default is false. */
|
|
9
10
|
inlineTypes?: boolean;
|
|
10
|
-
serializer?: 'kotlinx';
|
|
11
|
-
|
|
11
|
+
serializer?: 'kotlinx' | 'none';
|
|
12
|
+
unsupportedUnions?: 'throw' | 'fallback';
|
|
13
|
+
arrayItemNaming?: string | false;
|
|
12
14
|
depluralize?: boolean;
|
|
13
|
-
arrayItemNaming?: string;
|
|
14
15
|
uncountableWords?: string[];
|
|
15
16
|
}
|
|
16
17
|
export interface KotlinEmitter {
|
|
@@ -19,6 +20,7 @@ export interface KotlinEmitter {
|
|
|
19
20
|
export declare function createStubKotlinEmitter(results: Record<string, KotlinEmitResult>): KotlinEmitter;
|
|
20
21
|
/**
|
|
21
22
|
* Resolves the production Kotlin emitter from `ajsc`. Throws a clear error
|
|
22
|
-
* if
|
|
23
|
+
* if ajsc is not installed or does not expose `emitKotlin` (e.g. consumer
|
|
24
|
+
* ran `npm install --omit=optional` since ajsc is in optionalDependencies).
|
|
23
25
|
*/
|
|
24
26
|
export declare function resolveProductionKotlinEmitter(): Promise<KotlinEmitter>;
|
|
@@ -12,21 +12,26 @@ export function createStubKotlinEmitter(results) {
|
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Resolves the production Kotlin emitter from `ajsc`. Throws a clear error
|
|
15
|
-
* if
|
|
15
|
+
* if ajsc is not installed or does not expose `emitKotlin` (e.g. consumer
|
|
16
|
+
* ran `npm install --omit=optional` since ajsc is in optionalDependencies).
|
|
16
17
|
*/
|
|
17
18
|
export async function resolveProductionKotlinEmitter() {
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
let ajsc = null;
|
|
20
|
+
let importError;
|
|
21
|
+
try {
|
|
22
|
+
ajsc = (await import('ajsc'));
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
importError = err;
|
|
26
|
+
}
|
|
20
27
|
const emitKotlin = ajsc?.emitKotlin;
|
|
21
28
|
if (typeof emitKotlin !== 'function') {
|
|
22
29
|
throw new Error('[ts-procedures-codegen] ajsc.emitKotlin is not available. ' +
|
|
23
|
-
'
|
|
30
|
+
'Install ajsc (`npm install ajsc`) — it is an optional dependency.', importError !== undefined ? { cause: importError } : undefined);
|
|
24
31
|
}
|
|
25
32
|
return {
|
|
26
33
|
emit(schema, opts) {
|
|
27
|
-
|
|
28
|
-
const r = emitKotlin(schema, opts);
|
|
29
|
-
return r;
|
|
34
|
+
return emitKotlin(schema, opts);
|
|
30
35
|
},
|
|
31
36
|
};
|
|
32
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ajsc-adapter.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/ajsc-adapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ajsc-adapter.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/ajsc-adapter.ts"],"names":[],"mappings":"AAsBA,MAAM,UAAU,uBAAuB,CACrC,OAAyC;IAEzC,OAAO;QACL,IAAI,CAAC,OAAO,EAAE,IAAI;YAChB,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;YACzC,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;gBACnB,MAAM,IAAI,KAAK,CACb,6DAA6D,IAAI,CAAC,YAAY,KAAK;oBACjF,iCAAiC,CACpC,CAAA;YACH,CAAC;YACD,OAAO,MAAM,CAAA;QACf,CAAC;KACF,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B;IAClD,IAAI,IAAI,GAAoC,IAAI,CAAA;IAChD,IAAI,WAAoB,CAAA;IACxB,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,CAA6B,CAAA;IAC3D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,WAAW,GAAG,GAAG,CAAA;IACnB,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,EAAE,UAAU,CAAA;IACnC,IAAI,OAAO,UAAU,KAAK,UAAU,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,4DAA4D;YAC5D,mEAAmE,EACnE,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAC/D,CAAA;IACH,CAAC;IACD,OAAO;QACL,IAAI,CAAC,MAAM,EAAE,IAAI;YACf,OAAQ,UAA2D,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QACnF,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { createStubKotlinEmitter } from './ajsc-adapter.js';
|
|
2
|
+
import { createStubKotlinEmitter, resolveProductionKotlinEmitter } from './ajsc-adapter.js';
|
|
3
3
|
describe('createStubKotlinEmitter', () => {
|
|
4
4
|
it('returns the configured EmitResult for the matching root name', () => {
|
|
5
5
|
const expected = {
|
|
@@ -9,11 +9,29 @@ describe('createStubKotlinEmitter', () => {
|
|
|
9
9
|
imports: ['kotlinx.serialization.Serializable'],
|
|
10
10
|
};
|
|
11
11
|
const emitter = createStubKotlinEmitter({ User: expected });
|
|
12
|
-
expect(emitter.emit({ type: 'object' }, {
|
|
12
|
+
expect(emitter.emit({ type: 'object' }, {
|
|
13
|
+
rootTypeName: 'User',
|
|
14
|
+
inlineTypes: true,
|
|
15
|
+
serializer: 'kotlinx',
|
|
16
|
+
unsupportedUnions: 'throw',
|
|
17
|
+
})).toEqual(expected);
|
|
13
18
|
});
|
|
14
19
|
it('throws when asked to emit a name not in the stub map', () => {
|
|
15
20
|
const emitter = createStubKotlinEmitter({});
|
|
16
21
|
expect(() => emitter.emit({}, { rootTypeName: 'Missing' })).toThrow(/Missing/);
|
|
17
22
|
});
|
|
18
23
|
});
|
|
24
|
+
describe('resolveProductionKotlinEmitter', () => {
|
|
25
|
+
it('returns a working emitter that invokes ajsc.emitKotlin when ajsc is installed', async () => {
|
|
26
|
+
const emitter = await resolveProductionKotlinEmitter();
|
|
27
|
+
const result = emitter.emit({ type: 'object', properties: { id: { type: 'string' } }, required: ['id'] }, { rootTypeName: 'Probe' });
|
|
28
|
+
expect(typeof result.code).toBe('string');
|
|
29
|
+
expect(result.code.length).toBeGreaterThan(0);
|
|
30
|
+
expect(result.rootTypeName).toBe('Probe');
|
|
31
|
+
expect(Array.isArray(result.imports)).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
// Note: testing the failure path (ajsc unavailable) requires module mocking;
|
|
34
|
+
// we leave that as a manual-verification path. The error message is pinned
|
|
35
|
+
// by the message text below so any change requires updating both call sites.
|
|
36
|
+
});
|
|
19
37
|
//# sourceMappingURL=ajsc-adapter.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ajsc-adapter.test.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/ajsc-adapter.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,uBAAuB,EAAyB,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"ajsc-adapter.test.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/ajsc-adapter.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAyB,MAAM,mBAAmB,CAAA;AAElH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,QAAQ,GAAqB;YACjC,IAAI,EAAE,+CAA+C;YACrD,YAAY,EAAE,MAAM;YACpB,kBAAkB,EAAE,EAAE;YACtB,OAAO,EAAE,CAAC,oCAAoC,CAAC;SAChD,CAAA;QACD,MAAM,OAAO,GAAG,uBAAuB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;QAC3D,MAAM,CACJ,OAAO,CAAC,IAAI,CACV,EAAE,IAAI,EAAE,QAAQ,EAAE,EAClB;YACE,YAAY,EAAE,MAAM;YACpB,WAAW,EAAE,IAAI;YACjB,UAAU,EAAE,SAAS;YACrB,iBAAiB,EAAE,OAAO;SAC3B,CACF,CACF,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IACrB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,OAAO,GAAG,uBAAuB,CAAC,EAAE,CAAC,CAAA;QAC3C,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAChF,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC9C,EAAE,CAAC,+EAA+E,EAAE,KAAK,IAAI,EAAE;QAC7F,MAAM,OAAO,GAAG,MAAM,8BAA8B,EAAE,CAAA;QACtD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CACzB,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,EAC5E,EAAE,YAAY,EAAE,OAAO,EAAE,CAC1B,CAAA;QACD,MAAM,CAAC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACzC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;QAC7C,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACzC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAClD,CAAC,CAAC,CAAA;IACF,6EAA6E;IAC7E,2EAA2E;IAC3E,6EAA6E;AAC/E,CAAC,CAAC,CAAA"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { execSync } from 'node:child_process';
|
|
3
|
-
import { mkdtempSync, writeFileSync } from 'node:fs';
|
|
4
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { execFileSync, execSync } from 'node:child_process';
|
|
3
|
+
import { mkdtempSync, writeFileSync, readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
5
4
|
import { tmpdir } from 'node:os';
|
|
6
5
|
import { dirname, join } from 'node:path';
|
|
7
6
|
import { fileURLToPath } from 'node:url';
|
|
@@ -18,10 +17,31 @@ function kotlincAvailable() {
|
|
|
18
17
|
return false;
|
|
19
18
|
}
|
|
20
19
|
}
|
|
20
|
+
const RUN = process.env.TS_PROCEDURES_KOTLIN_E2E === '1';
|
|
21
|
+
/**
|
|
22
|
+
* E2E: real ajsc → real .kt output → real kotlinc compile.
|
|
23
|
+
*
|
|
24
|
+
* Gated on (a) `kotlinc` on PATH and (b) opt-in via env var so default
|
|
25
|
+
* `npm test` runs stay green for contributors without the toolchain.
|
|
26
|
+
*
|
|
27
|
+
* **Classpath setup (one-time, local):**
|
|
28
|
+
*
|
|
29
|
+
* Download kotlinx-serialization jars (matching versions, e.g. 1.6.3) from
|
|
30
|
+
* Maven Central or a local Gradle/Maven cache. The classpath value is a
|
|
31
|
+
* `:`-separated list (`;` on Windows). Concrete example:
|
|
32
|
+
*
|
|
33
|
+
* export TS_PROCEDURES_KOTLIN_E2E_CLASSPATH="\
|
|
34
|
+
* $HOME/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-serialization-json-jvm/1.6.3/<hash>/kotlinx-serialization-json-jvm-1.6.3.jar:\
|
|
35
|
+
* $HOME/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-serialization-core-jvm/1.6.3/<hash>/kotlinx-serialization-core-jvm-1.6.3.jar"
|
|
36
|
+
*
|
|
37
|
+
* If the env var is unset the compile uses kotlinc's default classpath which
|
|
38
|
+
* lacks the kotlinx jars and fails — that's the expected mode when checking
|
|
39
|
+
* gating works.
|
|
40
|
+
*/
|
|
21
41
|
describe('kotlin codegen — kotlinc compile (gated)', () => {
|
|
22
|
-
it.skipIf(!kotlincAvailable() ||
|
|
42
|
+
it.skipIf(!kotlincAvailable() || !RUN)('compiles generated output without errors', async () => {
|
|
23
43
|
const emitter = await resolveProductionKotlinEmitter();
|
|
24
|
-
const envelope = JSON.parse(
|
|
44
|
+
const envelope = JSON.parse(readFileSync(join(__dirname, '../../__fixtures__/users-envelope.json'), 'utf8'));
|
|
25
45
|
const files = await runPipeline({
|
|
26
46
|
envelope,
|
|
27
47
|
outDir: 'out',
|
|
@@ -34,10 +54,22 @@ describe('kotlin codegen — kotlinc compile (gated)', () => {
|
|
|
34
54
|
for (const f of files) {
|
|
35
55
|
writeFileSync(join(dir, f.path.split('/').pop()), f.code);
|
|
36
56
|
}
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
57
|
+
// Expand the source glob in Node and pass kotlinc args as an array so
|
|
58
|
+
// the shell never sees user-controlled input. The classpath env var is
|
|
59
|
+
// only forwarded as a single -classpath argument; even a value with
|
|
60
|
+
// shell metachars cannot escape into command parsing.
|
|
61
|
+
const ktFiles = readdirSync(dir)
|
|
62
|
+
.filter((name) => name.endsWith('.kt'))
|
|
63
|
+
.map((name) => join(dir, name));
|
|
64
|
+
const outJar = join(dir, 'out.jar');
|
|
65
|
+
const cp = process.env.TS_PROCEDURES_KOTLIN_E2E_CLASSPATH;
|
|
66
|
+
const args = [];
|
|
67
|
+
if (cp != null && cp.length > 0) {
|
|
68
|
+
args.push('-classpath', cp);
|
|
69
|
+
}
|
|
70
|
+
args.push(...ktFiles, '-d', outJar);
|
|
71
|
+
execFileSync('kotlinc', args, { stdio: 'inherit' });
|
|
72
|
+
expect(existsSync(outJar)).toBe(true);
|
|
41
73
|
});
|
|
42
74
|
});
|
|
43
75
|
//# sourceMappingURL=e2e-compile.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"e2e-compile.test.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/e2e-compile.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"e2e-compile.test.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/e2e-compile.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AnyHttpRouteDoc } from '../../../implementations/types.js';
|
|
2
|
-
import type { KotlinEmitter } from './ajsc-adapter.js';
|
|
2
|
+
import type { KotlinEmitter, KotlinEmitOptions } from './ajsc-adapter.js';
|
|
3
3
|
export interface EmitRouteResult {
|
|
4
4
|
/** Inner body of the `object RouteName { ... }` block — already indented one level. */
|
|
5
5
|
code: string;
|
|
@@ -7,5 +7,9 @@ export interface EmitRouteResult {
|
|
|
7
7
|
imports: string[];
|
|
8
8
|
/** Outer route name used as the `object RouteName` identifier. */
|
|
9
9
|
routeName: string;
|
|
10
|
+
/** True when the route was a stream (out-of-scope). Caller logs once. */
|
|
11
|
+
skipped?: boolean;
|
|
10
12
|
}
|
|
11
|
-
|
|
13
|
+
/** Subset of KotlinEmitOptions threaded by the pipeline; per-call rootTypeName is set inside. */
|
|
14
|
+
export type EmitRouteOpts = Omit<KotlinEmitOptions, 'rootTypeName' | 'inlineTypes'>;
|
|
15
|
+
export declare function emitKotlinRoute(route: AnyHttpRouteDoc, emitter: KotlinEmitter, errorSchemas: Map<string, unknown>, routeOpts?: EmitRouteOpts): EmitRouteResult;
|
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
import { indent } from '
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
function pathParamNames(template) {
|
|
7
|
-
const names = [];
|
|
8
|
-
for (const match of template.matchAll(COLON_PARAM_RE))
|
|
9
|
-
names.push(match[1]);
|
|
10
|
-
return names;
|
|
11
|
-
}
|
|
1
|
+
import { indent } from '../_shared/indent.js';
|
|
2
|
+
import { pickDefined } from '../_shared/pick-defined.js';
|
|
3
|
+
import { toBracePath, pathParamNames } from '../_shared/path-utils.js';
|
|
4
|
+
import { extractRouteSlots } from '../_shared/route-slots.js';
|
|
12
5
|
function buildPathFn(bracePath, params) {
|
|
13
6
|
if (params.length === 0)
|
|
14
7
|
return `const val path = "${bracePath}"`;
|
|
@@ -17,11 +10,18 @@ function buildPathFn(bracePath, params) {
|
|
|
17
10
|
body = body.replace(`{${name}}`, `\${p.${name}}`);
|
|
18
11
|
return `fun path(p: PathParams): String = "${body}"`;
|
|
19
12
|
}
|
|
20
|
-
|
|
13
|
+
function emitOptsFor(rootTypeName, routeOpts) {
|
|
14
|
+
const PASSTHROUGH_KEYS = ['serializer', 'unsupportedUnions', 'arrayItemNaming', 'depluralize', 'uncountableWords'];
|
|
15
|
+
return {
|
|
16
|
+
rootTypeName,
|
|
17
|
+
inlineTypes: true,
|
|
18
|
+
...pickDefined(routeOpts, PASSTHROUGH_KEYS),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function emitKotlinRoute(route, emitter, errorSchemas, routeOpts = {}) {
|
|
21
22
|
const kind = route.kind;
|
|
22
23
|
if (kind === 'stream') {
|
|
23
|
-
|
|
24
|
-
return { code: '', imports: [], routeName: route.name };
|
|
24
|
+
return { code: '', imports: [], routeName: route.name, skipped: true };
|
|
25
25
|
}
|
|
26
26
|
const isApi = kind === 'api' || 'fullPath' in route;
|
|
27
27
|
const rawPath = isApi ? route.fullPath : route.path;
|
|
@@ -34,19 +34,9 @@ export function emitKotlinRoute(route, emitter, errorSchemas) {
|
|
|
34
34
|
buildPathFn(bracePath, params),
|
|
35
35
|
];
|
|
36
36
|
const imports = [];
|
|
37
|
-
const schema = route.schema ?? {};
|
|
38
|
-
const input = (schema.input ?? {});
|
|
39
37
|
// Per-slot emission. Order is fixed for deterministic output.
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
{ key: 'query', rootName: 'Query', source: input.query },
|
|
43
|
-
{ key: 'body', rootName: 'Body', source: input.body },
|
|
44
|
-
{ key: 'response', rootName: 'Response', source: schema.returnType },
|
|
45
|
-
];
|
|
46
|
-
for (const slot of slots) {
|
|
47
|
-
if (slot.source == null)
|
|
48
|
-
continue;
|
|
49
|
-
const result = emitter.emit(slot.source, { rootTypeName: slot.rootName });
|
|
38
|
+
for (const slot of extractRouteSlots(route)) {
|
|
39
|
+
const result = emitter.emit(slot.source, emitOptsFor(slot.rootName, routeOpts));
|
|
50
40
|
lines.push('');
|
|
51
41
|
lines.push(result.code);
|
|
52
42
|
imports.push(...result.imports);
|
|
@@ -59,7 +49,7 @@ export function emitKotlinRoute(route, emitter, errorSchemas) {
|
|
|
59
49
|
if (routeErrorKeys.length > 0) {
|
|
60
50
|
const inner = [];
|
|
61
51
|
for (const key of routeErrorKeys) {
|
|
62
|
-
const r = emitter.emit(errorSchemas.get(key),
|
|
52
|
+
const r = emitter.emit(errorSchemas.get(key), emitOptsFor(key, routeOpts));
|
|
63
53
|
inner.push(r.code);
|
|
64
54
|
imports.push(...r.imports);
|
|
65
55
|
}
|
|
@@ -68,6 +58,6 @@ export function emitKotlinRoute(route, emitter, errorSchemas) {
|
|
|
68
58
|
lines.push(indent(inner.join('\n\n'), 1));
|
|
69
59
|
lines.push('}');
|
|
70
60
|
}
|
|
71
|
-
return { code: lines.join('\n'), imports, routeName: route.name };
|
|
61
|
+
return { code: lines.join('\n'), imports, routeName: route.name, skipped: false };
|
|
72
62
|
}
|
|
73
63
|
//# sourceMappingURL=emit-route-kotlin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emit-route-kotlin.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/emit-route-kotlin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"emit-route-kotlin.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/emit-route-kotlin.ts"],"names":[],"mappings":"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"}
|