From d9fbf56f289c0af1370d5d687167a67ca4d07751 Mon Sep 17 00:00:00 2001 From: Alexander Arvidsson Date: Thu, 25 Jun 2020 23:32:59 +0200 Subject: [PATCH 1/2] Added prefix and suffix support --- src/contentful-typescript-codegen.ts | 32 ++++++++++++++++++- .../fields/renderArray.ts | 5 +-- .../fields/renderLink.ts | 7 ++-- .../renderContentType.ts | 14 ++++---- .../contentful/fields/renderArray.ts | 5 +-- src/renderers/contentful/fields/renderLink.ts | 7 ++-- src/renderers/contentful/renderContentType.ts | 14 ++++---- .../contentful/renderContentTypeId.ts | 12 +++++-- src/renderers/contentful/renderField.ts | 9 ++---- src/renderers/render.ts | 13 +++++--- src/renderers/renderFieldsOnly.ts | 17 +++++----- .../typescript/renderInterfaceProperty.ts | 2 +- .../contentful/renderContentType.test.ts | 7 ++-- test/renderers/render.test.ts | 28 +++++++++------- test/renderers/renderFieldsOnly.test.ts | 23 +++++++++---- 15 files changed, 131 insertions(+), 64 deletions(-) diff --git a/src/contentful-typescript-codegen.ts b/src/contentful-typescript-codegen.ts index c779a52a..d5405488 100644 --- a/src/contentful-typescript-codegen.ts +++ b/src/contentful-typescript-codegen.ts @@ -13,6 +13,10 @@ const cli = meow( Options --output, -o Where to write to --poll, -p Continuously refresh types + --prefix STR, -P Define prefix STR for types, defaults to 'I' + --suffix STR, -S Define suffix STR for types, defaults to empty string + --no-prefix, -nP Disable prefix completely + --no-suffix, -nS Disable suffix completely --interval N, -i The interval in seconds at which to poll (defaults to 15) --namespace N, -n Wrap types in namespace N (disabled by default) --fields-only Output a tree that _only_ ensures fields are valid @@ -40,6 +44,26 @@ const cli = meow( alias: "p", required: false, }, + prefix: { + type: "string", + alias: "P", + required: false, + }, + suffix: { + type: "string", + alias: "S", + required: false, + }, + noPrefix: { + type: "boolean", + alias: "nP", + required: false, + }, + noSuffix: { + type: "boolean", + alias: "nS", + required: false, + }, interval: { type: "string", alias: "i", @@ -69,11 +93,17 @@ async function runCodegen(outputFile: string) { let output if (cli.flags.fieldsOnly) { - output = await renderFieldsOnly(contentTypes.items, { namespace: cli.flags.namespace }) + output = await renderFieldsOnly(contentTypes.items, { + namespace: cli.flags.namespace, + prefix: cli.flags.noPrefix ? "" : cli.flags.prefix, + suffix: cli.flags.noSuffix ? "" : cli.flags.suffix, + }) } else { output = await render(contentTypes.items, locales.items, { localization: cli.flags.localization, namespace: cli.flags.namespace, + prefix: cli.flags.noPrefix ? "" : cli.flags.prefix, + suffix: cli.flags.noSuffix ? "" : cli.flags.suffix, }) } diff --git a/src/renderers/contentful-fields-only/fields/renderArray.ts b/src/renderers/contentful-fields-only/fields/renderArray.ts index 03d57ba6..d0f15fd5 100644 --- a/src/renderers/contentful-fields-only/fields/renderArray.ts +++ b/src/renderers/contentful-fields-only/fields/renderArray.ts @@ -2,8 +2,9 @@ import { Field } from "contentful" import renderSymbol from "../../contentful/fields/renderSymbol" import renderLink from "../../contentful-fields-only/fields/renderLink" import renderArrayOf from "../../typescript/renderArrayOf" +import { Options } from "../../renderFieldsOnly" -export default function renderArray(field: Field): string { +export default function renderArray(field: Field, options: Options = {}): string { if (!field.items) { throw new Error(`Cannot render non-array field ${field.id} as an array`) } @@ -20,7 +21,7 @@ export default function renderArray(field: Field): string { } case "Link": { - return renderArrayOf(renderLink(fieldWithValidations)) + return renderArrayOf(renderLink(fieldWithValidations, options)) } } diff --git a/src/renderers/contentful-fields-only/fields/renderLink.ts b/src/renderers/contentful-fields-only/fields/renderLink.ts index 978d1fdb..d92c3267 100644 --- a/src/renderers/contentful-fields-only/fields/renderLink.ts +++ b/src/renderers/contentful-fields-only/fields/renderLink.ts @@ -1,8 +1,9 @@ import { Field } from "contentful" import renderContentTypeId from "../../contentful/renderContentTypeId" import { renderUnionValues } from "../../typescript/renderUnion" +import { Options } from "../../renderFieldsOnly" -export default function renderLink(field: Field): string { +export default function renderLink(field: Field, options: Options = {}): string { if (field.linkType === "Asset") { return "any" } @@ -11,7 +12,9 @@ export default function renderLink(field: Field): string { const contentTypeValidation = field.validations.find(validation => !!validation.linkContentType) if (contentTypeValidation) { - return renderUnionValues(contentTypeValidation.linkContentType!.map(renderContentTypeId)) + return renderUnionValues( + contentTypeValidation.linkContentType!.map(link => renderContentTypeId(link, options)), + ) } else { return "unknown" } diff --git a/src/renderers/contentful-fields-only/renderContentType.ts b/src/renderers/contentful-fields-only/renderContentType.ts index 2394bd04..438ef570 100644 --- a/src/renderers/contentful-fields-only/renderContentType.ts +++ b/src/renderers/contentful-fields-only/renderContentType.ts @@ -1,5 +1,7 @@ import { ContentType, Field, FieldType } from "contentful" +import { Options } from "../renderFieldsOnly" + import renderInterface from "../typescript/renderInterface" import renderField from "../contentful/renderField" import renderContentTypeId from "../contentful/renderContentTypeId" @@ -14,9 +16,9 @@ import renderNumber from "../contentful/fields/renderNumber" import renderObject from "../contentful/fields/renderObject" import renderSymbol from "../contentful/fields/renderSymbol" -export default function renderContentType(contentType: ContentType): string { - const name = renderContentTypeId(contentType.sys.id) - const fields = renderContentTypeFields(contentType.fields) +export default function renderContentType(contentType: ContentType, options: Options = {}): string { + const name = renderContentTypeId(contentType.sys.id, options) + const fields = renderContentTypeFields(contentType.fields, options) return renderInterface({ name, @@ -27,11 +29,11 @@ export default function renderContentType(contentType: ContentType): string { }) } -function renderContentTypeFields(fields: Field[]): string { +function renderContentTypeFields(fields: Field[], options: Options = {}): string { return fields .filter(field => !field.omitted) .map(field => { - const functionMap: Record string> = { + const functionMap: Record string> = { Array: renderArray, Boolean: renderBoolean, Date: renderSymbol, @@ -45,7 +47,7 @@ function renderContentTypeFields(fields: Field[]): string { Text: renderSymbol, } - return renderField(field, functionMap[field.type](field)) + return renderField(field, functionMap[field.type](field, options), options) }) .join("\n\n") } diff --git a/src/renderers/contentful/fields/renderArray.ts b/src/renderers/contentful/fields/renderArray.ts index 0cc2fc12..6a96a8a1 100644 --- a/src/renderers/contentful/fields/renderArray.ts +++ b/src/renderers/contentful/fields/renderArray.ts @@ -2,8 +2,9 @@ import { Field } from "contentful" import renderSymbol from "./renderSymbol" import renderLink from "./renderLink" import renderArrayOf from "../../typescript/renderArrayOf" +import { Options } from "../../render" -export default function renderArray(field: Field): string { +export default function renderArray(field: Field, options: Options = {}): string { if (!field.items) { throw new Error(`Cannot render non-array field ${field.id} as an array`) } @@ -20,7 +21,7 @@ export default function renderArray(field: Field): string { } case "Link": { - return renderArrayOf(renderLink(fieldWithValidations)) + return renderArrayOf(renderLink(fieldWithValidations, options)) } } diff --git a/src/renderers/contentful/fields/renderLink.ts b/src/renderers/contentful/fields/renderLink.ts index 5e911025..a1ec5a09 100644 --- a/src/renderers/contentful/fields/renderLink.ts +++ b/src/renderers/contentful/fields/renderLink.ts @@ -1,8 +1,9 @@ import { Field } from "contentful" import renderContentTypeId from "../renderContentTypeId" import { renderUnionValues } from "../../typescript/renderUnion" +import { Options } from "../../render" -export default function renderLink(field: Field): string { +export default function renderLink(field: Field, options: Options = {}): string { if (field.linkType === "Asset") { return "Asset" } @@ -11,7 +12,9 @@ export default function renderLink(field: Field): string { const contentTypeValidation = field.validations.find(validation => !!validation.linkContentType) if (contentTypeValidation) { - return renderUnionValues(contentTypeValidation.linkContentType!.map(renderContentTypeId)) + return renderUnionValues( + contentTypeValidation.linkContentType!.map(link => renderContentTypeId(link, options)), + ) } else { return "Entry<{ [fieldId: string]: unknown }>" } diff --git a/src/renderers/contentful/renderContentType.ts b/src/renderers/contentful/renderContentType.ts index 456cf8eb..b8fc4fdb 100644 --- a/src/renderers/contentful/renderContentType.ts +++ b/src/renderers/contentful/renderContentType.ts @@ -1,5 +1,7 @@ import { ContentType, Field, FieldType, Sys } from "contentful" +import { Options } from "../render" + import renderInterface from "../typescript/renderInterface" import renderField from "./renderField" import renderContentTypeId from "./renderContentTypeId" @@ -13,9 +15,9 @@ import renderObject from "./fields/renderObject" import renderRichText from "./fields/renderRichText" import renderSymbol from "./fields/renderSymbol" -export default function renderContentType(contentType: ContentType, localization: boolean): string { - const name = renderContentTypeId(contentType.sys.id) - const fields = renderContentTypeFields(contentType.fields, localization) +export default function renderContentType(contentType: ContentType, options: Options = {}): string { + const name = renderContentTypeId(contentType.sys.id, options) + const fields = renderContentTypeFields(contentType.fields, options) const sys = renderSys(contentType.sys) return ` @@ -34,11 +36,11 @@ function descriptionComment(description: string | undefined) { return "" } -function renderContentTypeFields(fields: Field[], localization: boolean): string { +function renderContentTypeFields(fields: Field[], options: Options = {}): string { return fields .filter(field => !field.omitted) .map(field => { - const functionMap: Record string> = { + const functionMap: Record string> = { Array: renderArray, Boolean: renderBoolean, Date: renderSymbol, @@ -52,7 +54,7 @@ function renderContentTypeFields(fields: Field[], localization: boolean): string Text: renderSymbol, } - return renderField(field, functionMap[field.type](field), localization) + return renderField(field, functionMap[field.type](field, options), options) }) .join("\n\n") } diff --git a/src/renderers/contentful/renderContentTypeId.ts b/src/renderers/contentful/renderContentTypeId.ts index f70c100b..afa01d3a 100644 --- a/src/renderers/contentful/renderContentTypeId.ts +++ b/src/renderers/contentful/renderContentTypeId.ts @@ -1,5 +1,13 @@ import { upperFirst, camelCase } from "lodash" +import { Options } from "../render" -export default function renderContentTypeId(contentTypeId: string): string { - return "I" + upperFirst(camelCase(contentTypeId)) +export default function renderContentTypeId(contentTypeId: string, options: Options = {}): string { + let str = upperFirst(camelCase(contentTypeId)) + + if (options.prefix === undefined) options.prefix = "I" + + if (options.prefix) str = options.prefix + str + if (options.suffix) str = str + options.suffix + + return str } diff --git a/src/renderers/contentful/renderField.ts b/src/renderers/contentful/renderField.ts index ae8bb1c8..53579cff 100644 --- a/src/renderers/contentful/renderField.ts +++ b/src/renderers/contentful/renderField.ts @@ -1,10 +1,7 @@ import { Field } from "contentful" import renderInterfaceProperty from "../typescript/renderInterfaceProperty" +import { Options } from "../render" -export default function renderField( - field: Field, - type: string, - localization: boolean = false, -): string { - return renderInterfaceProperty(field.id, type, field.required, localization, field.name) +export default function renderField(field: Field, type: string, options: Options = {}): string { + return renderInterfaceProperty(field.id, type, field.required, options.localization, field.name) } diff --git a/src/renderers/render.ts b/src/renderers/render.ts index 6b056eef..68aa54bc 100644 --- a/src/renderers/render.ts +++ b/src/renderers/render.ts @@ -10,21 +10,24 @@ import renderDefaultLocale from "./contentful/renderDefaultLocale" import renderNamespace from "./contentful/renderNamespace" import renderLocalizedTypes from "./contentful/renderLocalizedTypes" -interface Options { +export interface Options { localization?: boolean namespace?: string + prefix?: string + suffix?: string } export default async function render( contentTypes: ContentType[], locales: Locale[], - { namespace, localization = false }: Options = {}, + options: Options = {}, ) { + const { namespace, localization = false } = options const sortedContentTypes = contentTypes.sort((a, b) => a.sys.id.localeCompare(b.sys.id)) const sortedLocales = locales.sort((a, b) => a.code.localeCompare(b.code)) const typingsSource = [ - renderAllContentTypes(sortedContentTypes, localization), + renderAllContentTypes(sortedContentTypes, options), renderAllContentTypeIds(sortedContentTypes), renderAllLocales(sortedLocales), renderDefaultLocale(sortedLocales), @@ -40,8 +43,8 @@ export default async function render( return format(source, { ...prettierConfig, parser: "typescript" }) } -function renderAllContentTypes(contentTypes: ContentType[], localization: boolean): string { - return contentTypes.map(contentType => renderContentType(contentType, localization)).join("\n\n") +function renderAllContentTypes(contentTypes: ContentType[], options: Options = {}): string { + return contentTypes.map(contentType => renderContentType(contentType, options)).join("\n\n") } function renderAllContentTypeIds(contentTypes: ContentType[]): string { diff --git a/src/renderers/renderFieldsOnly.ts b/src/renderers/renderFieldsOnly.ts index 6cae24d3..415100c0 100644 --- a/src/renderers/renderFieldsOnly.ts +++ b/src/renderers/renderFieldsOnly.ts @@ -5,24 +5,23 @@ import { format, resolveConfig } from "prettier" import renderContentType from "./contentful-fields-only/renderContentType" import renderNamespace from "./contentful/renderNamespace" -interface Options { +export interface Options { namespace?: string + prefix?: string + suffix?: string } -export default async function renderFieldsOnly( - contentTypes: ContentType[], - { namespace }: Options = {}, -) { +export default async function renderFieldsOnly(contentTypes: ContentType[], options: Options = {}) { const sortedContentTypes = contentTypes.sort((a, b) => a.sys.id.localeCompare(b.sys.id)) - const typingsSource = renderAllContentTypes(sortedContentTypes) - const source = [renderNamespace(typingsSource, namespace)].join("\n\n") + const typingsSource = renderAllContentTypes(sortedContentTypes, options) + const source = [renderNamespace(typingsSource, options.namespace)].join("\n\n") const prettierConfig = await resolveConfig(process.cwd()) return format(source, { ...prettierConfig, parser: "typescript" }) } -function renderAllContentTypes(contentTypes: ContentType[]): string { - return contentTypes.map(contentType => renderContentType(contentType)).join("\n\n") +function renderAllContentTypes(contentTypes: ContentType[], options: Options = {}): string { + return contentTypes.map(contentType => renderContentType(contentType, options)).join("\n\n") } diff --git a/src/renderers/typescript/renderInterfaceProperty.ts b/src/renderers/typescript/renderInterfaceProperty.ts index 2f514049..0762d452 100644 --- a/src/renderers/typescript/renderInterfaceProperty.ts +++ b/src/renderers/typescript/renderInterfaceProperty.ts @@ -2,7 +2,7 @@ export default function renderInterfaceProperty( name: string, type: string, required: boolean, - localization: boolean, + localization?: boolean, description?: string, ): string { return [ diff --git a/test/renderers/contentful/renderContentType.test.ts b/test/renderers/contentful/renderContentType.test.ts index af9df218..3a27ab6d 100644 --- a/test/renderers/contentful/renderContentType.test.ts +++ b/test/renderers/contentful/renderContentType.test.ts @@ -55,7 +55,7 @@ describe("renderContentType()", () => { } it("works with miscellaneous field types", () => { - expect(format(renderContentType(contentType, false))).toMatchInlineSnapshot(` + expect(format(renderContentType(contentType, { localization: false }))).toMatchInlineSnapshot(` "export interface IMyContentTypeFields { /** Symbol Field™ */ symbolField?: string | undefined; @@ -84,7 +84,8 @@ describe("renderContentType()", () => { }) it("supports descriptions", () => { - expect(format(renderContentType(contentTypeWithDescription, false))).toMatchInlineSnapshot(` + expect(format(renderContentType(contentTypeWithDescription, { localization: false }))) + .toMatchInlineSnapshot(` "export interface IMyContentTypeFields {} /** This is a description */ @@ -109,7 +110,7 @@ describe("renderContentType()", () => { }) it("works with localized fields", () => { - expect(format(renderContentType(contentType, true))).toMatchInlineSnapshot(` + expect(format(renderContentType(contentType, { localization: true }))).toMatchInlineSnapshot(` "export interface IMyContentTypeFields { /** Symbol Field™ */ symbolField?: LocalizedField | undefined; diff --git a/test/renderers/render.test.ts b/test/renderers/render.test.ts index 384ef2d0..d02a045f 100644 --- a/test/renderers/render.test.ts +++ b/test/renderers/render.test.ts @@ -1,7 +1,7 @@ import render from "../../src/renderers/render" import { ContentType, Sys, Locale } from "contentful" -describe("render()", () => { +const test = ({ prefix = "I", suffix = "" }: { prefix?: string; suffix?: string } = {}) => () => { const contentTypes: ContentType[] = [ { sys: { @@ -52,18 +52,18 @@ describe("render()", () => { ] it("renders a given content type", async () => { - expect(await render(contentTypes, locales)).toMatchInlineSnapshot(` + expect(await render(contentTypes, locales, { prefix, suffix })).toMatchInlineSnapshot(` "// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT. import { Asset, Entry } from \\"contentful\\" import { Document } from \\"@contentful/rich-text-types\\" - export interface IMyContentTypeFields { + export interface ${prefix}MyContentType${suffix}Fields { /** Array field */ arrayField: (\\"one\\" | \\"of\\" | \\"the\\" | \\"above\\")[] } - export interface IMyContentType extends Entry { + export interface ${prefix}MyContentType${suffix} extends Entry<${prefix}MyContentType${suffix}Fields> { sys: { id: string type: string @@ -122,18 +122,19 @@ describe("render()", () => { }, ] - expect(await render(contentTypes, locales, { localization: true })).toMatchInlineSnapshot(` + expect(await render(contentTypes, locales, { prefix, suffix, localization: true })) + .toMatchInlineSnapshot(` "// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT. import { Entry } from \\"contentful\\" import { Document } from \\"@contentful/rich-text-types\\" - export interface IMyContentTypeFields { + export interface ${prefix}MyContentType${suffix}Fields { /** Array field */ arrayField: LocalizedField<(\\"one\\" | \\"of\\" | \\"the\\" | \\"above\\")[]> } - export interface IMyContentType extends Entry { + export interface ${prefix}MyContentType${suffix} extends Entry<${prefix}MyContentType${suffix}Fields> { sys: { id: string type: string @@ -184,19 +185,20 @@ describe("render()", () => { }) it("renders given a content type inside a namespace", async () => { - expect(await render(contentTypes, locales, { namespace: "Codegen" })).toMatchInlineSnapshot(` + expect(await render(contentTypes, locales, { prefix, suffix, namespace: "Codegen" })) + .toMatchInlineSnapshot(` "// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT. import { Asset, Entry } from \\"contentful\\" import { Document } from \\"@contentful/rich-text-types\\" declare namespace Codegen { - export interface IMyContentTypeFields { + export interface ${prefix}MyContentType${suffix}Fields { /** Array field */ arrayField: (\\"one\\" | \\"of\\" | \\"the\\" | \\"above\\")[] } - export interface IMyContentType extends Entry { + export interface ${prefix}MyContentType${suffix} extends Entry<${prefix}MyContentType${suffix}Fields> { sys: { id: string type: string @@ -225,4 +227,8 @@ describe("render()", () => { " `) }) -}) +} + +describe("render() with default 'I' prefix and no suffix", test()) +describe("render() with no prefix and 'Data' suffix", test({ prefix: "", suffix: "Data" })) +describe("render() with 'C' prefix and 'Data' suffix", test({ prefix: "C", suffix: "Data" })) diff --git a/test/renderers/renderFieldsOnly.test.ts b/test/renderers/renderFieldsOnly.test.ts index 2e299265..be7e7fe0 100644 --- a/test/renderers/renderFieldsOnly.test.ts +++ b/test/renderers/renderFieldsOnly.test.ts @@ -1,7 +1,7 @@ import renderFieldsOnly from "../../src/renderers/renderFieldsOnly" import { ContentType, Sys } from "contentful" -describe("renderFieldsOnly()", () => { +const test = ({ prefix = "I", suffix = "" }: { prefix?: string; suffix?: string } = {}) => () => { const contentTypes: ContentType[] = [ { sys: { @@ -35,8 +35,8 @@ describe("renderFieldsOnly()", () => { ] it("renders a given content type", async () => { - expect(await renderFieldsOnly(contentTypes)).toMatchInlineSnapshot(` - "export interface IMyContentType { + expect(await renderFieldsOnly(contentTypes, { prefix, suffix })).toMatchInlineSnapshot(` + "export interface ${prefix}MyContentType${suffix} { fields: { /** Array field */ arrayField: (\\"one\\" | \\"of\\" | \\"the\\" | \\"above\\")[] @@ -48,9 +48,10 @@ describe("renderFieldsOnly()", () => { }) it("renders a given content type inside a namespace", async () => { - expect(await renderFieldsOnly(contentTypes, { namespace: "Codegen" })).toMatchInlineSnapshot(` + expect(await renderFieldsOnly(contentTypes, { prefix, suffix, namespace: "Codegen" })) + .toMatchInlineSnapshot(` "declare namespace Codegen { - export interface IMyContentType { + export interface ${prefix}MyContentType${suffix} { fields: { /** Array field */ arrayField: (\\"one\\" | \\"of\\" | \\"the\\" | \\"above\\")[] @@ -64,4 +65,14 @@ describe("renderFieldsOnly()", () => { " `) }) -}) +} + +describe("renderFieldsOnly() with default 'I' prefix and no suffix", test()) +describe( + "renderFieldsOnly() with no prefix and 'Data' suffix", + test({ prefix: "", suffix: "Data" }), +) +describe( + "renderFieldsOnly() with 'C' prefix and 'Data' suffix", + test({ prefix: "C", suffix: "Data" }), +) From 3ec43206a3ef41681ed56b34338fcd86c52271cc Mon Sep 17 00:00:00 2001 From: Alexander Arvidsson Date: Fri, 2 Oct 2020 23:22:37 +0200 Subject: [PATCH 2/2] Fixed tests --- .../fields/renderArray.ts | 4 +- .../fields/renderLink.ts | 2 +- .../renderContentType.ts | 4 +- .../contentful/fields/renderArray.ts | 4 +- src/renderers/contentful/fields/renderLink.ts | 2 +- src/renderers/contentful/renderContentType.ts | 4 +- .../contentful/renderContentTypeId.ts | 2 +- src/renderers/contentful/renderField.ts | 2 +- src/renderers/render.ts | 6 +- src/renderers/renderFieldsOnly.ts | 4 +- .../fields/renderArray.test.ts | 59 +++++++++---------- .../fields/renderLink.test.ts | 11 ++-- .../renderContentType.test.ts | 3 +- .../contentful/fields/renderArray.test.ts | 59 +++++++++---------- .../contentful/fields/renderLink.test.ts | 11 ++-- 15 files changed, 90 insertions(+), 87 deletions(-) diff --git a/src/renderers/contentful-fields-only/fields/renderArray.ts b/src/renderers/contentful-fields-only/fields/renderArray.ts index d0f15fd5..04070250 100644 --- a/src/renderers/contentful-fields-only/fields/renderArray.ts +++ b/src/renderers/contentful-fields-only/fields/renderArray.ts @@ -4,7 +4,7 @@ import renderLink from "../../contentful-fields-only/fields/renderLink" import renderArrayOf from "../../typescript/renderArrayOf" import { Options } from "../../renderFieldsOnly" -export default function renderArray(field: Field, options: Options = {}): string { +export default function renderArray(field: Field, options: Options): string { if (!field.items) { throw new Error(`Cannot render non-array field ${field.id} as an array`) } @@ -24,6 +24,4 @@ export default function renderArray(field: Field, options: Options = {}): string return renderArrayOf(renderLink(fieldWithValidations, options)) } } - - return renderArrayOf("unknown") } diff --git a/src/renderers/contentful-fields-only/fields/renderLink.ts b/src/renderers/contentful-fields-only/fields/renderLink.ts index d92c3267..ce46158d 100644 --- a/src/renderers/contentful-fields-only/fields/renderLink.ts +++ b/src/renderers/contentful-fields-only/fields/renderLink.ts @@ -3,7 +3,7 @@ import renderContentTypeId from "../../contentful/renderContentTypeId" import { renderUnionValues } from "../../typescript/renderUnion" import { Options } from "../../renderFieldsOnly" -export default function renderLink(field: Field, options: Options = {}): string { +export default function renderLink(field: Field, options: Options): string { if (field.linkType === "Asset") { return "any" } diff --git a/src/renderers/contentful-fields-only/renderContentType.ts b/src/renderers/contentful-fields-only/renderContentType.ts index 438ef570..e4e50305 100644 --- a/src/renderers/contentful-fields-only/renderContentType.ts +++ b/src/renderers/contentful-fields-only/renderContentType.ts @@ -16,7 +16,7 @@ import renderNumber from "../contentful/fields/renderNumber" import renderObject from "../contentful/fields/renderObject" import renderSymbol from "../contentful/fields/renderSymbol" -export default function renderContentType(contentType: ContentType, options: Options = {}): string { +export default function renderContentType(contentType: ContentType, options: Options): string { const name = renderContentTypeId(contentType.sys.id, options) const fields = renderContentTypeFields(contentType.fields, options) @@ -29,7 +29,7 @@ export default function renderContentType(contentType: ContentType, options: Opt }) } -function renderContentTypeFields(fields: Field[], options: Options = {}): string { +function renderContentTypeFields(fields: Field[], options: Options): string { return fields .filter(field => !field.omitted) .map(field => { diff --git a/src/renderers/contentful/fields/renderArray.ts b/src/renderers/contentful/fields/renderArray.ts index 6a96a8a1..79547b7f 100644 --- a/src/renderers/contentful/fields/renderArray.ts +++ b/src/renderers/contentful/fields/renderArray.ts @@ -4,7 +4,7 @@ import renderLink from "./renderLink" import renderArrayOf from "../../typescript/renderArrayOf" import { Options } from "../../render" -export default function renderArray(field: Field, options: Options = {}): string { +export default function renderArray(field: Field, options: Options): string { if (!field.items) { throw new Error(`Cannot render non-array field ${field.id} as an array`) } @@ -24,6 +24,4 @@ export default function renderArray(field: Field, options: Options = {}): string return renderArrayOf(renderLink(fieldWithValidations, options)) } } - - return renderArrayOf("unknown") } diff --git a/src/renderers/contentful/fields/renderLink.ts b/src/renderers/contentful/fields/renderLink.ts index a1ec5a09..e150af6b 100644 --- a/src/renderers/contentful/fields/renderLink.ts +++ b/src/renderers/contentful/fields/renderLink.ts @@ -3,7 +3,7 @@ import renderContentTypeId from "../renderContentTypeId" import { renderUnionValues } from "../../typescript/renderUnion" import { Options } from "../../render" -export default function renderLink(field: Field, options: Options = {}): string { +export default function renderLink(field: Field, options: Options): string { if (field.linkType === "Asset") { return "Asset" } diff --git a/src/renderers/contentful/renderContentType.ts b/src/renderers/contentful/renderContentType.ts index b8fc4fdb..78107aa9 100644 --- a/src/renderers/contentful/renderContentType.ts +++ b/src/renderers/contentful/renderContentType.ts @@ -15,7 +15,7 @@ import renderObject from "./fields/renderObject" import renderRichText from "./fields/renderRichText" import renderSymbol from "./fields/renderSymbol" -export default function renderContentType(contentType: ContentType, options: Options = {}): string { +export default function renderContentType(contentType: ContentType, options: Options): string { const name = renderContentTypeId(contentType.sys.id, options) const fields = renderContentTypeFields(contentType.fields, options) const sys = renderSys(contentType.sys) @@ -36,7 +36,7 @@ function descriptionComment(description: string | undefined) { return "" } -function renderContentTypeFields(fields: Field[], options: Options = {}): string { +function renderContentTypeFields(fields: Field[], options: Options): string { return fields .filter(field => !field.omitted) .map(field => { diff --git a/src/renderers/contentful/renderContentTypeId.ts b/src/renderers/contentful/renderContentTypeId.ts index afa01d3a..7e80d75e 100644 --- a/src/renderers/contentful/renderContentTypeId.ts +++ b/src/renderers/contentful/renderContentTypeId.ts @@ -1,7 +1,7 @@ import { upperFirst, camelCase } from "lodash" import { Options } from "../render" -export default function renderContentTypeId(contentTypeId: string, options: Options = {}): string { +export default function renderContentTypeId(contentTypeId: string, options: Options): string { let str = upperFirst(camelCase(contentTypeId)) if (options.prefix === undefined) options.prefix = "I" diff --git a/src/renderers/contentful/renderField.ts b/src/renderers/contentful/renderField.ts index 53579cff..6e0fadd9 100644 --- a/src/renderers/contentful/renderField.ts +++ b/src/renderers/contentful/renderField.ts @@ -2,6 +2,6 @@ import { Field } from "contentful" import renderInterfaceProperty from "../typescript/renderInterfaceProperty" import { Options } from "../render" -export default function renderField(field: Field, type: string, options: Options = {}): string { +export default function renderField(field: Field, type: string, options: Options): string { return renderInterfaceProperty(field.id, type, field.required, options.localization, field.name) } diff --git a/src/renderers/render.ts b/src/renderers/render.ts index 68aa54bc..e1c17aec 100644 --- a/src/renderers/render.ts +++ b/src/renderers/render.ts @@ -17,10 +17,12 @@ export interface Options { suffix?: string } +export const defaultOptions: Options = {} + export default async function render( contentTypes: ContentType[], locales: Locale[], - options: Options = {}, + options: Options, ) { const { namespace, localization = false } = options const sortedContentTypes = contentTypes.sort((a, b) => a.sys.id.localeCompare(b.sys.id)) @@ -43,7 +45,7 @@ export default async function render( return format(source, { ...prettierConfig, parser: "typescript" }) } -function renderAllContentTypes(contentTypes: ContentType[], options: Options = {}): string { +function renderAllContentTypes(contentTypes: ContentType[], options: Options): string { return contentTypes.map(contentType => renderContentType(contentType, options)).join("\n\n") } diff --git a/src/renderers/renderFieldsOnly.ts b/src/renderers/renderFieldsOnly.ts index 415100c0..63369d78 100644 --- a/src/renderers/renderFieldsOnly.ts +++ b/src/renderers/renderFieldsOnly.ts @@ -11,7 +11,7 @@ export interface Options { suffix?: string } -export default async function renderFieldsOnly(contentTypes: ContentType[], options: Options = {}) { +export default async function renderFieldsOnly(contentTypes: ContentType[], options: Options) { const sortedContentTypes = contentTypes.sort((a, b) => a.sys.id.localeCompare(b.sys.id)) const typingsSource = renderAllContentTypes(sortedContentTypes, options) @@ -22,6 +22,6 @@ export default async function renderFieldsOnly(contentTypes: ContentType[], opti return format(source, { ...prettierConfig, parser: "typescript" }) } -function renderAllContentTypes(contentTypes: ContentType[], options: Options = {}): string { +function renderAllContentTypes(contentTypes: ContentType[], options: Options): string { return contentTypes.map(contentType => renderContentType(contentType, options)).join("\n\n") } diff --git a/test/renderers/contentful-fields-only/fields/renderArray.test.ts b/test/renderers/contentful-fields-only/fields/renderArray.test.ts index 1f849b06..9d777d6b 100644 --- a/test/renderers/contentful-fields-only/fields/renderArray.test.ts +++ b/test/renderers/contentful-fields-only/fields/renderArray.test.ts @@ -1,60 +1,49 @@ import renderArray from "../../../../src/renderers/contentful-fields-only/fields/renderArray" import { Field } from "contentful" +import { defaultOptions } from "../../../../src/renderers/render" + +const common: Field = { + type: "Array", + id: "fieldId", + name: "Field Name", + validations: [], + omitted: false, + required: true, + disabled: false, + linkType: undefined, + localized: false, +} describe("renderArray()", () => { it("renders an array of symbols", () => { const arrayOfSymbols: Field = { - type: "Array", - id: "fieldId", - name: "Field Name", - validations: [], - omitted: false, - required: true, - disabled: false, - linkType: undefined, - localized: false, + ...common, items: { type: "Symbol", validations: [], }, } - expect(renderArray(arrayOfSymbols)).toMatchInlineSnapshot(`"(string)[]"`) + expect(renderArray(arrayOfSymbols, defaultOptions)).toMatchInlineSnapshot(`"(string)[]"`) }) it("renders an array of symbols with validations", () => { const arrayOfValidatedSymbols: Field = { - type: "Array", - id: "fieldId", - name: "Field Name", - validations: [], - omitted: false, - required: true, - disabled: false, - linkType: undefined, - localized: false, + ...common, items: { type: "Symbol", validations: [{ in: ["one", "of", "these"] }], }, } - expect(renderArray(arrayOfValidatedSymbols)).toMatchInlineSnapshot( + expect(renderArray(arrayOfValidatedSymbols, defaultOptions)).toMatchInlineSnapshot( `"('one' | 'of' | 'these')[]"`, ) }) it("renders an array of links of a particular type", () => { const arrayOfValidatedSymbols: Field = { - type: "Array", - id: "fieldId", - name: "Field Name", - validations: [], - omitted: false, - required: true, - disabled: false, - linkType: undefined, - localized: false, + ...common, items: { type: "Link", linkType: "Entry", @@ -62,8 +51,18 @@ describe("renderArray()", () => { }, } - expect(renderArray(arrayOfValidatedSymbols)).toMatchInlineSnapshot( + expect(renderArray(arrayOfValidatedSymbols, defaultOptions)).toMatchInlineSnapshot( `"(IContentType1 | IContentType2)[]"`, ) }) + + it("renders an array without items should throw error", () => { + const arrayOfValidatedSymbols: Field = { + ...common, + } + + expect(() => renderArray(arrayOfValidatedSymbols, defaultOptions)).toThrowError( + "Cannot render non-array field fieldId as an array", + ) + }) }) diff --git a/test/renderers/contentful-fields-only/fields/renderLink.test.ts b/test/renderers/contentful-fields-only/fields/renderLink.test.ts index d4edd72d..92ce1d49 100644 --- a/test/renderers/contentful-fields-only/fields/renderLink.test.ts +++ b/test/renderers/contentful-fields-only/fields/renderLink.test.ts @@ -1,5 +1,6 @@ import renderLink from "../../../../src/renderers/contentful-fields-only/fields/renderLink" import { Field } from "contentful" +import { defaultOptions } from "../../../../src/renderers/render" describe("renderLink()", () => { it("renders a simple entry link", () => { @@ -15,7 +16,7 @@ describe("renderLink()", () => { linkType: "Entry", } - expect(renderLink(simpleEntryLink)).toMatchInlineSnapshot(`"unknown"`) + expect(renderLink(simpleEntryLink, defaultOptions)).toMatchInlineSnapshot(`"unknown"`) }) it("renders a link with validations", () => { @@ -31,7 +32,9 @@ describe("renderLink()", () => { linkType: "Entry", } - expect(renderLink(validatedEntryLink)).toMatchInlineSnapshot(`"ILinkToOtherThing"`) + expect(renderLink(validatedEntryLink, defaultOptions)).toMatchInlineSnapshot( + `"ILinkToOtherThing"`, + ) }) it("renders an asset link", () => { @@ -47,7 +50,7 @@ describe("renderLink()", () => { omitted: false, } - expect(renderLink(assetLink)).toMatchInlineSnapshot(`"any"`) + expect(renderLink(assetLink, defaultOptions)).toMatchInlineSnapshot(`"any"`) }) it("handles mysteries", () => { @@ -63,6 +66,6 @@ describe("renderLink()", () => { omitted: false, } - expect(renderLink(mysteryLink)).toMatchInlineSnapshot(`"unknown"`) + expect(renderLink(mysteryLink, defaultOptions)).toMatchInlineSnapshot(`"unknown"`) }) }) diff --git a/test/renderers/contentful-fields-only/renderContentType.test.ts b/test/renderers/contentful-fields-only/renderContentType.test.ts index fb73fba1..b7c92f68 100644 --- a/test/renderers/contentful-fields-only/renderContentType.test.ts +++ b/test/renderers/contentful-fields-only/renderContentType.test.ts @@ -1,6 +1,7 @@ import renderContentType from "../../../src/renderers/contentful-fields-only/renderContentType" import { ContentType, Sys } from "contentful" import format from "../../support/format" +import { defaultOptions } from "../../../src/renderers/render" describe("renderContentType()", () => { const contentType: ContentType = { @@ -44,7 +45,7 @@ describe("renderContentType()", () => { } it("works with miscellaneous field types", () => { - expect(format(renderContentType(contentType))).toMatchInlineSnapshot(` + expect(format(renderContentType(contentType, defaultOptions))).toMatchInlineSnapshot(` "export interface IMyContentType { fields: { /** Symbol Field™ */ diff --git a/test/renderers/contentful/fields/renderArray.test.ts b/test/renderers/contentful/fields/renderArray.test.ts index 80039aae..ebf78d19 100644 --- a/test/renderers/contentful/fields/renderArray.test.ts +++ b/test/renderers/contentful/fields/renderArray.test.ts @@ -1,60 +1,49 @@ import renderArray from "../../../../src/renderers/contentful/fields/renderArray" import { Field } from "contentful" +import { defaultOptions } from "../../../../src/renderers/render" + +const common: Field = { + type: "Array", + id: "fieldId", + name: "Field Name", + validations: [], + omitted: false, + required: true, + disabled: false, + linkType: undefined, + localized: false, +} describe("renderArray()", () => { it("renders an array of symbols", () => { const arrayOfSymbols: Field = { - type: "Array", - id: "fieldId", - name: "Field Name", - validations: [], - omitted: false, - required: true, - disabled: false, - linkType: undefined, - localized: false, + ...common, items: { type: "Symbol", validations: [], }, } - expect(renderArray(arrayOfSymbols)).toMatchInlineSnapshot(`"(string)[]"`) + expect(renderArray(arrayOfSymbols, defaultOptions)).toMatchInlineSnapshot(`"(string)[]"`) }) it("renders an array of symbols with validations", () => { const arrayOfValidatedSymbols: Field = { - type: "Array", - id: "fieldId", - name: "Field Name", - validations: [], - omitted: false, - required: true, - disabled: false, - linkType: undefined, - localized: false, + ...common, items: { type: "Symbol", validations: [{ in: ["one", "of", "these"] }], }, } - expect(renderArray(arrayOfValidatedSymbols)).toMatchInlineSnapshot( + expect(renderArray(arrayOfValidatedSymbols, defaultOptions)).toMatchInlineSnapshot( `"('one' | 'of' | 'these')[]"`, ) }) it("renders an array of links of a particular type", () => { const arrayOfValidatedSymbols: Field = { - type: "Array", - id: "fieldId", - name: "Field Name", - validations: [], - omitted: false, - required: true, - disabled: false, - linkType: undefined, - localized: false, + ...common, items: { type: "Link", linkType: "Entry", @@ -62,8 +51,18 @@ describe("renderArray()", () => { }, } - expect(renderArray(arrayOfValidatedSymbols)).toMatchInlineSnapshot( + expect(renderArray(arrayOfValidatedSymbols, defaultOptions)).toMatchInlineSnapshot( `"(IContentType1 | IContentType2)[]"`, ) }) + + it("renders an array without items should throw error", () => { + const arrayOfValidatedSymbols: Field = { + ...common, + } + + expect(() => renderArray(arrayOfValidatedSymbols, defaultOptions)).toThrowError( + "Cannot render non-array field fieldId as an array", + ) + }) }) diff --git a/test/renderers/contentful/fields/renderLink.test.ts b/test/renderers/contentful/fields/renderLink.test.ts index 883ae0ad..e9c39d32 100644 --- a/test/renderers/contentful/fields/renderLink.test.ts +++ b/test/renderers/contentful/fields/renderLink.test.ts @@ -1,5 +1,6 @@ import renderLink from "../../../../src/renderers/contentful/fields/renderLink" import { Field } from "contentful" +import { defaultOptions } from "../../../../src/renderers/render" describe("renderLink()", () => { it("renders a simple entry link", () => { @@ -15,7 +16,7 @@ describe("renderLink()", () => { linkType: "Entry", } - expect(renderLink(simpleEntryLink)).toMatchInlineSnapshot( + expect(renderLink(simpleEntryLink, defaultOptions)).toMatchInlineSnapshot( `"Entry<{ [fieldId: string]: unknown }>"`, ) }) @@ -33,7 +34,9 @@ describe("renderLink()", () => { linkType: "Entry", } - expect(renderLink(validatedEntryLink)).toMatchInlineSnapshot(`"ILinkToOtherThing"`) + expect(renderLink(validatedEntryLink, defaultOptions)).toMatchInlineSnapshot( + `"ILinkToOtherThing"`, + ) }) it("renders an asset link", () => { @@ -49,7 +52,7 @@ describe("renderLink()", () => { omitted: false, } - expect(renderLink(assetLink)).toMatchInlineSnapshot(`"Asset"`) + expect(renderLink(assetLink, defaultOptions)).toMatchInlineSnapshot(`"Asset"`) }) it("handles mysteries", () => { @@ -65,6 +68,6 @@ describe("renderLink()", () => { omitted: false, } - expect(renderLink(mysteryLink)).toMatchInlineSnapshot(`"unknown"`) + expect(renderLink(mysteryLink, defaultOptions)).toMatchInlineSnapshot(`"unknown"`) }) })