Skip to content

Commit 7680884

Browse files
committed
feat: Generate SeamHttpEndpoints
1 parent eb8e0b3 commit 7680884

6 files changed

Lines changed: 120 additions & 20 deletions

File tree

codegen/layouts/endpoints.hbs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Automatically generated by codegen/smith.ts.
3+
* Do not edit this file or add other files to this directory.
4+
*/
5+
6+
{{> route-imports }}
7+
8+
{{#each routes}}
9+
import {
10+
{{className}},
11+
{{#each typeNames}}
12+
type {{.}},
13+
{{/each}}
14+
} from './{{fileName}}'
15+
{{/each}}
16+
17+
export class SeamHttpEndpoints {
18+
{{> route-class-methods }}
19+
20+
{{#each endpoints}}
21+
get['{{path}}'](): {{className}}['{{methodName}}']
22+
{
23+
const { client, defaults } = this
24+
return function {{> route-class-endpoint-sig methodName=functionName}}
25+
{
26+
const seam = {{className}}.fromClient(client, defaults)
27+
return seam.{{methodName}}({{methodParamName}}{{#if hasOptions}},options{{/if}})
28+
}
29+
}
30+
31+
{{/each}}
32+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{{methodName}}(
2+
{{methodParamName}}{{#if isOptionalParamsOk}}?{{/if}}: {{requestTypeName}},
3+
{{#if hasOptions}}options: {{optionsTypeName}} = {},{{/if}}
4+
): SeamHttpRequest<{{#if returnsVoid}}void, undefined{{else}}{{responseTypeName}}, '{{responseKey}}'{{/if}}>

codegen/layouts/partials/route-class-endpoint.hbs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
{{methodName}}(
2-
{{methodParamName}}{{#if isOptionalParamsOk}}?{{/if}}: {{requestTypeName}},
3-
{{#if hasOptions}}options: {{optionsTypeName}} = {},{{/if}}
4-
): SeamHttpRequest<{{#if returnsVoid}}void, undefined{{else}}{{responseTypeName}}, '{{responseKey}}'{{/if}}>
1+
{{> route-class-endpoint-sig className=functionName}}
52
{
63
return new SeamHttpRequest(this, {
74
pathname: '{{path}}',

codegen/lib/connect.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,24 @@ import type { Blueprint } from '@seamapi/blueprint'
22
import { kebabCase } from 'change-case'
33
import type Metalsmith from 'metalsmith'
44

5+
import {
6+
type EndpointsLayoutContext,
7+
setEndpointsLayoutContext,
8+
} from './layouts/endpoints.js'
59
import {
610
type RouteIndexLayoutContext,
711
type RouteLayoutContext,
812
setRouteLayoutContext,
13+
toFilePath,
914
} from './layouts/route.js'
1015

1116
interface Metadata {
1217
blueprint: Blueprint
1318
}
1419

15-
type File = RouteLayoutContext & RouteIndexLayoutContext & { layout: string }
20+
type File = RouteLayoutContext &
21+
RouteIndexLayoutContext &
22+
EndpointsLayoutContext & { layout: string }
1623

1724
const rootPath = 'src/lib/seam/connect/routes'
1825

@@ -34,15 +41,22 @@ export const connect = (
3441

3542
const routeIndexes: Record<string, Set<string>> = {}
3643

37-
const rootRouteKey = `${rootPath}/seam-http.ts`
38-
files[rootRouteKey] = { contents: Buffer.from('\n') }
39-
const file = files[rootRouteKey] as unknown as File
44+
const k = `${rootPath}/seam-http.ts`
45+
files[k] = { contents: Buffer.from('\n') }
46+
const file = files[k] as unknown as File
4047
file.layout = 'route.hbs'
4148
setRouteLayoutContext(file, null, nodes)
4249

4350
routeIndexes[''] ??= new Set()
4451
routeIndexes['']?.add('seam-http.js')
4552

53+
const endpointsKey = `${rootPath}/seam-http-endpoints.ts`
54+
files[endpointsKey] = { contents: Buffer.from('\n') }
55+
const endpointFile = files[endpointsKey] as unknown as File
56+
endpointFile.layout = 'endpoints.hbs'
57+
setEndpointsLayoutContext(endpointFile, routes)
58+
routeIndexes['']?.add('seam-http-endpoints.js')
59+
4660
for (const node of nodes) {
4761
const path = toFilePath(node.path)
4862
const name = kebabCase(node.name)
@@ -75,10 +89,3 @@ export const connect = (
7589
file.routes = [...routes]
7690
}
7791
}
78-
79-
const toFilePath = (path: string): string =>
80-
path
81-
.slice(1)
82-
.split('/')
83-
.map((p) => kebabCase(p))
84-
.join('/')

codegen/lib/layouts/endpoints.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type { Route } from '@seamapi/blueprint'
2+
3+
import {
4+
type EndpointLayoutContext,
5+
getClassName,
6+
getEndpointLayoutContext,
7+
toFilePath,
8+
} from './route.js'
9+
10+
export interface EndpointsLayoutContext {
11+
className: string
12+
endpoints: EndpointLayoutContext[]
13+
routes: Array<{
14+
fileName: string
15+
className: string
16+
typeNames: string[]
17+
}>
18+
skipClientSessionImport: boolean
19+
}
20+
21+
export const setEndpointsLayoutContext = (
22+
file: Partial<EndpointsLayoutContext>,
23+
routes: Route[],
24+
): void => {
25+
file.className = getClassName('Endpoints')
26+
file.skipClientSessionImport = true
27+
file.endpoints = routes.flatMap((route) =>
28+
route.endpoints
29+
.filter(({ isUndocumented }) => !isUndocumented)
30+
.map((endpoint) => getEndpointLayoutContext(endpoint, route)),
31+
)
32+
file.routes = routes.map((route) => {
33+
const typeNames = route.endpoints
34+
.filter(({ isUndocumented }) => !isUndocumented)
35+
.map((e) => getEndpointLayoutContext(e, route))
36+
.flatMap((e) => [
37+
e.optionsTypeName,
38+
e.requestTypeName,
39+
e.responseTypeName,
40+
])
41+
return {
42+
className: getClassName(route.path),
43+
typeNames,
44+
fileName: `${toFilePath(route.path)}/index.js`,
45+
}
46+
})
47+
}

codegen/lib/layouts/route.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ export interface RouteIndexLayoutContext {
1313
routes: string[]
1414
}
1515

16-
interface EndpointLayoutContext {
16+
export interface EndpointLayoutContext {
1717
path: string
1818
methodName: string
19+
functionName: string
20+
className: string
1921
method: Method
2022
hasOptions: boolean
2123
responseKey: string
@@ -71,7 +73,7 @@ const getSubrouteLayoutContext = (
7173
}
7274
}
7375

74-
const getEndpointLayoutContext = (
76+
export const getEndpointLayoutContext = (
7577
endpoint: Endpoint,
7678
route: Pick<Route, 'path' | 'name'>,
7779
): EndpointLayoutContext => {
@@ -95,11 +97,15 @@ const getEndpointLayoutContext = (
9597
endpoint.response.responseType === 'resource' &&
9698
endpoint.response.resourceType === 'action_attempt'
9799

100+
const methodName = camelCase(endpoint.name)
101+
98102
return {
99103
path: endpoint.path,
100-
methodName: camelCase(endpoint.name),
104+
methodName,
105+
functionName: camelCase(prefix),
101106
method: endpoint.request.preferredMethod,
102107
hasOptions: returnsActionAttempt,
108+
className: getClassName(route.path),
103109
methodParamName,
104110
requestFormat,
105111
requestFormatSuffix,
@@ -129,5 +135,12 @@ const getResponseContext = (
129135
}
130136
}
131137

132-
const getClassName = (name: string | null): string =>
133-
`SeamHttp${pascalCase(name ?? '')}`
138+
export const getClassName = (path: string | null): string =>
139+
`SeamHttp${pascalCase(path ?? '')}`
140+
141+
export const toFilePath = (path: string): string =>
142+
path
143+
.slice(1)
144+
.split('/')
145+
.map((p) => kebabCase(p))
146+
.join('/')

0 commit comments

Comments
 (0)