Skip to content

Commit

Permalink
Add formatCode support
Browse files Browse the repository at this point in the history
  • Loading branch information
razor-x committed Aug 29, 2024
1 parent c209c1e commit 6341664
Show file tree
Hide file tree
Showing 7 changed files with 572 additions and 57 deletions.
18 changes: 11 additions & 7 deletions src/lib/blueprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import {
CodeSampleDefinitionSchema,
createCodeSample,
} from './code-sample/index.js'
import type { CodeSampleDefinition } from './code-sample/schema.js'
import type {
CodeSampleDefinition,
CodeSampleSyntax,
} from './code-sample/schema.js'
import type {
Openapi,
OpenapiOperation,
Expand Down Expand Up @@ -221,7 +224,7 @@ export type TypesModuleInput = z.input<typeof TypesModuleSchema>
export type TypesModule = z.output<typeof TypesModuleSchema>

export interface BlueprintOptions {
formatCode?: (content: string) => Promise<string>
formatCode?: (content: string, syntax: CodeSampleSyntax) => Promise<string>
}

export const createBlueprint = async (
Expand Down Expand Up @@ -362,11 +365,12 @@ const createEndpoint = async (
codeSamples: await Promise.all(
context.codeSampleDefinitions
.filter(({ request }) => request.path === endpointPath)
.map(async (codeSampleDefinition) =>
await createCodeSample(codeSampleDefinition, {
endpoint,
formatCode: context.formatCode,
}),
.map(
async (codeSampleDefinition) =>
await createCodeSample(codeSampleDefinition, {
endpoint,
formatCode: context.formatCode,
}),
),
),
}
Expand Down
43 changes: 43 additions & 0 deletions src/lib/code-sample/format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { Code, Context } from './schema.js'

type CodeEntries = Entries<Code>
type CodeEntry = NonNullable<CodeEntries[number]>

export const formatCodeRecords = async (
code: Code,
context: Context,
): Promise<Code> => {
const entries = Object.entries(code) as unknown as CodeEntries
const formattedEntries = await Promise.all(
entries.map(async (entry): Promise<CodeEntry> => {
if (entry == null) throw new Error('Unexpected null code entry')
return await formatCodeEntry(entry, context)
}),
)
return Object.fromEntries(formattedEntries)
}

const formatCodeEntry = async (
[key, code]: CodeEntry,
{ formatCode }: Context,
): Promise<CodeEntry> => {
if (code == null) throw new Error(`Unexpected null in code object for ${key}`)
const [request, response] = await Promise.all([
await formatCode(code.request, code.request_syntax),
await formatCode(code.response, code.response_syntax),
])
return [
key,
{
...code,
request,
response,
},
]
}

type Entries<T> = Array<
{
[K in keyof T]: [K, T[K]]
}[keyof T]
>
1 change: 1 addition & 0 deletions src/lib/code-sample/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export {
type CodeSample,
type CodeSampleDefinitionInput,
CodeSampleDefinitionSchema,
type CodeSampleSyntax,
createCodeSample,
} from './schema.js'
114 changes: 64 additions & 50 deletions src/lib/code-sample/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from 'lib/code-sample/seam-cli.js'
import { JsonSchema } from 'lib/json.js'

import { formatCodeRecords } from './format.js'
import {
createJavascriptRequest,
createJavascriptResponse,
Expand Down Expand Up @@ -39,72 +40,85 @@ export type CodeSampleDefinitionInput = z.input<

export type CodeSampleDefinition = z.output<typeof CodeSampleDefinitionSchema>

const syntax = z.enum(['javascript', 'json', 'python', 'php', 'ruby', 'bash'])
const CodeSampleSyntaxSchema = z.enum([
'javascript',
'json',
'python',
'php',
'ruby',
'bash',
])

export type Syntax = z.infer<typeof syntax>
export type CodeSampleSyntax = z.infer<typeof CodeSampleSyntaxSchema>

const CodeSchema = z.record(
z.enum(['javascript', 'python', 'php', 'ruby', 'seam_cli']),
z.object({
title: z.string().min(1),
request: z.string(),
response: z.string(),
request_syntax: CodeSampleSyntaxSchema,
response_syntax: CodeSampleSyntaxSchema,
}),
)

export type Code = z.infer<typeof CodeSchema>

const CodeSampleSchema = CodeSampleDefinitionSchema.extend({
code: z.record(
z.enum(['javascript', 'python', 'php', 'ruby', 'seam_cli']),
z.object({
title: z.string().min(1),
request: z.string(),
response: z.string(),
request_syntax: syntax,
response_syntax: syntax,
}),
),
code: CodeSchema,
})

export type CodeSample = z.output<typeof CodeSampleSchema>

export interface Context {
endpoint: Omit<Endpoint, 'codeSamples'>
formatCode: (content: string, syntax: Syntax) => Promise<string>
formatCode: (content: string, syntax: CodeSampleSyntax) => Promise<string>
}

export const createCodeSample = async (
codeSampleDefinition: CodeSampleDefinition,
context: Context,
): Promise<CodeSample> => {
const code: Code = {
javascript: {
title: 'JavaScript',
request: createJavascriptRequest(codeSampleDefinition, context),
response: createJavascriptResponse(codeSampleDefinition, context),
request_syntax: 'javascript',
response_syntax: 'javascript',
},
python: {
title: 'Python',
request: createPythonRequest(codeSampleDefinition, context),
response: createPythonResponse(codeSampleDefinition, context),
request_syntax: 'python',
response_syntax: 'python',
},
ruby: {
title: 'Ruby',
request: createRubyRequest(codeSampleDefinition, context),
response: createRubyResponse(codeSampleDefinition, context),
request_syntax: 'ruby',
response_syntax: 'ruby',
},
php: {
title: 'PHP',
request: createPhpRequest(codeSampleDefinition, context),
response: createPhpResponse(codeSampleDefinition, context),
request_syntax: 'php',
response_syntax: 'json',
},
seam_cli: {
title: 'Seam CLI',
request: createSeamCliRequest(codeSampleDefinition, context),
response: createSeamCliResponse(codeSampleDefinition, context),
request_syntax: 'bash',
response_syntax: 'json',
},
}

return {
...codeSampleDefinition,
code: {
javascript: {
title: 'JavaScript',
request: createJavascriptRequest(codeSampleDefinition, context),
response: createJavascriptResponse(codeSampleDefinition, context),
request_syntax: 'javascript',
response_syntax: 'javascript',
},
python: {
title: 'Python',
request: createPythonRequest(codeSampleDefinition, context),
response: createPythonResponse(codeSampleDefinition, context),
request_syntax: 'python',
response_syntax: 'python',
},
ruby: {
title: 'Ruby',
request: createRubyRequest(codeSampleDefinition, context),
response: createRubyResponse(codeSampleDefinition, context),
request_syntax: 'ruby',
response_syntax: 'ruby',
},
php: {
title: 'PHP',
request: createPhpRequest(codeSampleDefinition, context),
response: createPhpResponse(codeSampleDefinition, context),
request_syntax: 'php',
response_syntax: 'json',
},
seam_cli: {
title: 'Seam CLI',
request: createSeamCliRequest(codeSampleDefinition, context),
response: createSeamCliResponse(codeSampleDefinition, context),
request_syntax: 'bash',
response_syntax: 'json',
},
},
code: await formatCodeRecords(code, context),
}
}
1 change: 1 addition & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export {
type CodeSample,
type CodeSampleDefinitionInput,
CodeSampleDefinitionSchema,
type CodeSampleSyntax,
} from './code-sample/index.js'
8 changes: 8 additions & 0 deletions test/blueprint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,11 @@ test('createBlueprint', async (t) => {
const blueprint = await createBlueprint(typesModule)
t.snapshot(blueprint, 'blueprint')
})

test('createBlueprint: with formatCode', async (t) => {
const typesModule = TypesModuleSchema.parse(types)
const blueprint = await createBlueprint(typesModule, {
formatCode: async (content, syntax) => [`// ${syntax}`, content].join('\n'),
})
t.snapshot(blueprint, 'blueprint')
})
Loading

0 comments on commit 6341664

Please sign in to comment.