Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 22 additions & 18 deletions packages/openapi-generator/src/codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,10 @@ function parseObjectExpression(
if (E.isLeft(initE)) {
return initE;
}
const [newSourceFile, init] = initE.right;
let [newSourceFile, init] = initE.right;
if (init.type === 'TsAsExpression' || init.type === 'TsConstAssertion') {
init = init.expression;
}
if (init.type !== 'ObjectExpression') {
return E.left('Spread element must be object');
}
Expand Down Expand Up @@ -209,6 +212,8 @@ export function parsePlainInitializer(
return E.right({ type: 'literal', kind: 'null', value: null });
} else if (init.type === 'Identifier' && init.value === 'undefined') {
return E.right({ type: 'undefined' });
} else if (init.type === 'TsConstAssertion' || init.type === 'TsAsExpression') {
return parsePlainInitializer(project, source, init.expression);
} else if (
init.type === 'Identifier' ||
init.type === 'MemberExpression' ||
Expand Down Expand Up @@ -253,23 +258,21 @@ export function parseCodecInitializer(
return E.right(identifier);
}

function deref(source: SourceFile): DerefFn {
return (schema, fn) => {
if (schema.type !== 'ref') {
return fn(deref(source), schema);
} else {
const initE = findSymbolInitializer(project, source, schema.name);
if (E.isLeft(initE)) {
return initE;
}
const [newSourceFile, init] = initE.right;
const newSchemaE = parsePlainInitializer(project, newSourceFile, init);
if (E.isLeft(newSchemaE)) {
return newSchemaE;
}
return fn(deref(newSourceFile), newSchemaE.right);
function deref(schema: Schema): E.Either<string, Schema> {
if (schema.type !== 'ref') {
return E.right(schema);
} else {
const refSource = project.get(schema.location);
if (refSource === undefined) {
return E.left(`Unknown source ${schema.location}`);
}
};
const initE = findSymbolInitializer(project, refSource, schema.name);
if (E.isLeft(initE)) {
return initE;
}
const [newSourceFile, init] = initE.right;
return parsePlainInitializer(project, newSourceFile, init);
}
}
const args = init.arguments.map<E.Either<string, Schema>>(({ expression }) => {
return parsePlainInitializer(project, source, expression);
Expand All @@ -278,7 +281,8 @@ export function parseCodecInitializer(
return pipe(
args,
E.sequenceArray,
E.chain((args) => identifier.schema(deref(source), ...args)),
E.chain((args) => pipe(args.map(deref), E.sequenceArray)),
E.chain((args) => identifier.schema(deref, ...args)),
);
} else {
return E.left(`Unimplemented initializer type ${init.type}`);
Expand Down
160 changes: 70 additions & 90 deletions packages/openapi-generator/src/knownImports.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import * as E from 'fp-ts/Either';
import { pipe } from 'fp-ts/lib/function';

import type { Schema } from './ir';

export type DerefFn = (ref: Schema, fn: KnownCodec) => E.Either<string, Schema>;
export type DerefFn = (ref: Schema) => E.Either<string, Schema>;
export type KnownCodec = (
deref: DerefFn,
...schemas: Schema[]
Expand Down Expand Up @@ -32,31 +31,27 @@ export const KNOWN_IMPORTS: KnownImports = {
boolean: () => E.right({ type: 'primitive', value: 'boolean' }),
null: () => E.right({ type: 'primitive', value: 'null' }),
array: (_, innerSchema) => E.right({ type: 'array', items: innerSchema }),
type: (deref, schema) => {
return deref(schema, (_, schema) => {
if (schema.type !== 'object') {
return E.left('typeC parameter must be object');
}
const props = Object.entries(schema.properties).reduce((acc, [key, prop]) => {
return { ...acc, [key]: prop };
}, {});
return E.right({
type: 'object',
properties: props,
required: Object.keys(props),
});
type: (_, schema) => {
if (schema.type !== 'object') {
return E.left('typeC parameter must be object');
}
const props = Object.entries(schema.properties).reduce((acc, [key, prop]) => {
return { ...acc, [key]: prop };
}, {});
return E.right({
type: 'object',
properties: props,
required: Object.keys(props),
});
},
partial: (deref, schema) => {
return deref(schema, (_, schema) => {
if (schema.type !== 'object') {
return E.left('typeC parameter must be object');
}
const props = Object.entries(schema.properties).reduce((acc, [key, prop]) => {
return { ...acc, [key]: prop };
}, {});
return E.right({ type: 'object', properties: props, required: [] });
});
partial: (_, schema) => {
if (schema.type !== 'object') {
return E.left('typeC parameter must be object');
}
const props = Object.entries(schema.properties).reduce((acc, [key, prop]) => {
return { ...acc, [key]: prop };
}, {});
return E.right({ type: 'object', properties: props, required: [] });
},
record: (_, _domain, codomain) => {
if (!codomain) {
Expand All @@ -65,46 +60,37 @@ export const KNOWN_IMPORTS: KnownImports = {
return E.right({ type: 'record', codomain });
}
},
union: (deref, schema) => {
return deref(schema, (_, schema) => {
if (schema.type !== 'tuple') {
return E.left('unionC parameter must be array');
}
return E.right({ type: 'union', schemas: schema.schemas });
});
union: (_, schema) => {
if (schema.type !== 'tuple') {
return E.left('unionC parameter must be array');
}
return E.right({ type: 'union', schemas: schema.schemas });
},
intersection: (deref, schema) => {
return deref(schema, (_, schema) => {
if (schema.type !== 'tuple') {
return E.left('unionC parameter must be array');
}
return E.right({ type: 'intersection', schemas: schema.schemas });
});
intersection: (_, schema) => {
if (schema.type !== 'tuple') {
return E.left('unionC parameter must be array');
}
return E.right({ type: 'intersection', schemas: schema.schemas });
},
literal: (deref, arg) => {
return deref(arg, (_, arg) => {
if (arg.type !== 'literal') {
return E.left(`Unimplemented literal type ${arg.type}`);
} else {
return E.right(arg);
}
});
literal: (_, arg) => {
if (arg.type !== 'literal') {
return E.left(`Unimplemented literal type ${arg.type}`);
} else {
return E.right(arg);
}
},
keyof: (deref, arg) => {
return deref(arg, (_, arg) => {
if (arg.type !== 'object') {
return E.left(`Unimplemented keyof type ${arg.type}`);
}
const schemas: E.Either<string, Schema>[] = Object.keys(arg.properties).map(
(prop) => {
return E.right({ type: 'literal', kind: 'string', value: prop });
},
);
return pipe(
schemas,
E.sequenceArray,
E.map((schemas) => ({ type: 'union', schemas: [...schemas] })),
);
keyof: (_, arg) => {
if (arg.type !== 'object') {
return E.left(`Unimplemented keyof type ${arg.type}`);
}
const schemas: Schema[] = Object.keys(arg.properties).map((prop) => ({
type: 'literal',
kind: 'string',
value: prop,
}));
return E.right({
type: 'union',
schemas,
});
},
brand: (_, arg) => E.right(arg),
Expand All @@ -120,26 +106,22 @@ export const KNOWN_IMPORTS: KnownImports = {
'@api-ts/io-ts-http': {
optional: (_, innerSchema) =>
E.right({ type: 'union', schemas: [innerSchema, { type: 'undefined' }] }),
optionalized: (deref, props) => {
return deref(props, (_, props) => {
if (props.type !== 'object') {
return E.left('optionalized parameter must be object');
}
const required = Object.keys(props.properties).filter(
(key) => !isOptional(props.properties[key]!),
);
return E.right({ type: 'object', properties: props.properties, required });
});
optionalized: (_, props) => {
if (props.type !== 'object') {
return E.left('optionalized parameter must be object');
}
const required = Object.keys(props.properties).filter(
(key) => !isOptional(props.properties[key]!),
);
return E.right({ type: 'object', properties: props.properties, required });
},
httpRequest: (deref, arg) => {
if (arg.type !== 'object') {
return E.left(`Unimplemented httpRequest type ${arg.type}`);
}
const properties: Record<string, Schema> = {};
for (const [outerKey, outerValue] of Object.entries(arg.properties)) {
const innerPropsE = deref(outerValue, (_, innerProps) => {
return E.right(innerProps);
});
const innerPropsE = deref(outerValue);

if (E.isLeft(innerPropsE)) {
return innerPropsE;
Expand All @@ -164,22 +146,20 @@ export const KNOWN_IMPORTS: KnownImports = {
});
},
httpRoute: (deref, schema) => {
return deref(schema, (_, schema) => {
if (schema.type !== 'object') {
return E.left('httpRoute parameter must be object');
if (schema.type !== 'object') {
return E.left('httpRoute parameter must be object');
}
const props = Object.entries(schema.properties).reduce((acc, [key, prop]) => {
const derefedE = deref(prop);
if (E.isLeft(derefedE)) {
return acc;
}
const props = Object.entries(schema.properties).reduce((acc, [key, prop]) => {
const derefedE = deref(prop, (_, derefed) => E.right(derefed));
if (E.isLeft(derefedE)) {
return acc;
}
return { ...acc, [key]: derefedE.right };
}, {});
return E.right({
type: 'object',
properties: props,
required: Object.keys(props),
});
return { ...acc, [key]: derefedE.right };
}, {});
return E.right({
type: 'object',
properties: props,
required: Object.keys(props),
});
},
},
Expand Down
42 changes: 41 additions & 1 deletion packages/openapi-generator/src/symbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,45 @@ function parseDeclaration(
}
}
});
} else if (subDeclaration.type === 'TsEnumDeclaration') {
const comment =
prev !== undefined
? leadingComment(src, srcSpanStart, prev.span.end, decl.span.start)[0]
: undefined;
// Construct a synthetic object declaration
const properties: swc.KeyValueProperty[] = subDeclaration.members.map((member) => {
return {
type: 'KeyValueProperty',
key: {
type: 'Identifier',
value: member.id.value,
span: member.id.span,
optional: false,
},
value: member.init ?? {
type: 'StringLiteral',
value: member.id.value,
span: member.id.span,
},
};
});
const syntheticObject: swc.ObjectExpression = {
type: 'ObjectExpression',
span: subDeclaration.span,
properties,
};
result.declarations.push({
name: subDeclaration.id.value,
init: syntheticObject,
comment,
});
if (decl.type === 'ExportDeclaration') {
result.exports.push({
type: 'named',
exportedName: subDeclaration.id.value,
localName: subDeclaration.id.value,
});
}
}
return result;
}
Expand Down Expand Up @@ -179,7 +218,8 @@ export function parseTopLevelSymbols(
symbols.imports.push(...newSyms);
} else if (
item.type === 'VariableDeclaration' ||
item.type === 'ExportDeclaration'
item.type === 'ExportDeclaration' ||
item.type === 'TsEnumDeclaration'
) {
const newSyms = parseDeclaration(src, srcSpanStart, items[idx - 1], item);
addTable(newSyms);
Expand Down
Loading