diff --git a/src/__tests__/main.test.ts b/src/__tests__/main.test.ts index a27d8fe..5257bfa 100644 --- a/src/__tests__/main.test.ts +++ b/src/__tests__/main.test.ts @@ -625,4 +625,67 @@ describe('runtype generation', () => { " `); }); + + it('comments', () => { + const source = generateRuntypes( + { + name: 'Event', + comment: 'An event object', + type: { + kind: 'record', + fields: [ + { + name: 'name', + type: { kind: 'string' }, + comment: 'Single line comment', + }, + { + name: 'age', + type: { kind: 'string' }, + comment: 'Single line comment.\nWith newlines', + }, + { + name: 'id', + type: { kind: 'string' }, + comment: ['Multi line comment', 'As array'], + }, + { + name: 'noComment1', + type: { kind: 'string' }, + comment: [], + }, + { + name: 'noComment2', + type: { kind: 'string' }, + comment: '', + }, + ], + }, + }, + { includeTypes: false }, + ); + + expect(source).toMatchInlineSnapshot(` + "import * as rt from \\"runtypes\\"; + + // An event object + const event = rt.Record({ + // Single line comment + name: rt.String + /** + * Single line comment. + * With newlines + */, + age: rt.String + /** + * Multi line comment + * As array + */, + id: rt.String, + noComment1: rt.String, + noComment2: rt.String, + }); + " + `); + }); }); diff --git a/src/main.ts b/src/main.ts index 53cf8ed..90e8a5e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -97,6 +97,9 @@ function writeRootType( const { formatRuntypeName, formatTypeName, includeTypes } = options; const runtypeName = formatRuntypeName(node.name); const typeName = formatTypeName(node.name); + if (node.comment) { + writeComment(w, node.comment); + } w.conditionalWrite(Boolean(node.export), 'export '); w.write(`const ${runtypeName}=`); writeAnyType(options, w, node.type); @@ -219,6 +222,25 @@ function writeIntersectionType( w.write(')'); } +function writeComment(w: CodeWriter, comment: string | string[]) { + const lines = (Array.isArray(comment) ? comment : [comment]) + .map((e) => e.trim()) + .map((e) => e.split('\n')) + .reduce((prev, cur) => [...prev, ...cur], []); + + if (lines.length === 0) { + return; + } else if (lines.length === 1) { + w.write(`// ${lines[0]}\n`); + } else { + w.write(`/**\n`); + for (const line of lines) { + w.write(`* ${line}\n`); + } + w.write(`*/\n`); + } +} + /** * public for testing * @@ -273,6 +295,9 @@ function writeRecordType( for (const fieldKind of fieldKinds) { w.write('rt.Record({'); for (const field of fieldKind.fields) { + if (field.comment) { + writeComment(w, field.comment); + } w.write(field.name); w.write(':'); writeAnyType(options, w, field.type); diff --git a/src/types.ts b/src/types.ts index 5825f4a..c3e58fa 100644 --- a/src/types.ts +++ b/src/types.ts @@ -50,6 +50,7 @@ export type RecordField = { type: AnyType; readonly?: boolean; nullable?: boolean; + comment?: string | string[]; }; export type RecordType = { @@ -162,7 +163,12 @@ export type AnyType = rt.Static; export const rootTypeRt = rt.Intersect( rt.Record({ name: rt.String, type: anyTypeRt }), - rt.Record({ export: rt.Boolean }).asPartial(), + rt + .Record({ + export: rt.Boolean, + comment: rt.Union(rt.String, rt.Array(rt.String)), + }) + .asPartial(), ); export type RootType = rt.Static;