Skip to content

feat: add support for locales=* Contentful responses #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 16, 2020
Merged
10 changes: 9 additions & 1 deletion src/contentful-typescript-codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const cli = meow(
and present, and does not provide types for Sys,
Assets, or Rich Text. This is useful for ensuring raw
Contentful responses will be compatible with your code.
--localization -l Output fields with localized values

Examples
$ contentful-typescript-codegen -o src/@types/generated/contentful.d.ts
Expand All @@ -43,6 +44,11 @@ const cli = meow(
alias: "i",
required: false,
},
localization: {
type: "boolean",
alias: "l",
required: false,
},
},
},
)
Expand All @@ -59,7 +65,9 @@ async function runCodegen(outputFile: string) {
if (cli.flags.fieldsOnly) {
output = await renderFieldsOnly(contentTypes.items)
} else {
output = await render(contentTypes.items, locales.items)
output = await render(contentTypes.items, locales.items, {
localization: cli.flags.localization,
})
}

outputFileSync(outputPath, output)
Expand Down
8 changes: 4 additions & 4 deletions src/renderers/contentful/renderContentType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import renderObject from "./fields/renderObject"
import renderRichText from "./fields/renderRichText"
import renderSymbol from "./fields/renderSymbol"

export default function renderContentType(contentType: ContentType): string {
export default function renderContentType(contentType: ContentType, localization: boolean): string {
const name = renderContentTypeId(contentType.sys.id)
const fields = renderContentTypeFields(contentType.fields)
const fields = renderContentTypeFields(contentType.fields, localization)
const sys = renderSys(contentType.sys)

return `
Expand All @@ -34,7 +34,7 @@ function descriptionComment(description: string | undefined) {
return ""
}

function renderContentTypeFields(fields: Field[]): string {
function renderContentTypeFields(fields: Field[], localization: boolean): string {
return fields
.filter(field => !field.omitted)
.map<string>(field => {
Expand All @@ -52,7 +52,7 @@ function renderContentTypeFields(fields: Field[]): string {
Text: renderSymbol,
}

return renderField(field, functionMap[field.type](field))
return renderField(field, functionMap[field.type](field), localization)
})
.join("\n\n")
}
Expand Down
11 changes: 10 additions & 1 deletion src/renderers/contentful/renderContentfulImports.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
export default function renderContentfulImports(): string {
export default function renderContentfulImports(localization: boolean = false): string {
if (localization) {
return `
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT.

import { Entry } from 'contentful'
import { Document } from '@contentful/rich-text-types'
`
}

return `
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT.

Expand Down
8 changes: 6 additions & 2 deletions src/renderers/contentful/renderField.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { Field } from "contentful"
import renderInterfaceProperty from "../typescript/renderInterfaceProperty"

export default function renderField(field: Field, type: string): string {
return renderInterfaceProperty(field.id, type, field.required, field.name)
export default function renderField(
field: Field,
type: string,
localization: boolean = false,
): string {
return renderInterfaceProperty(field.id, type, field.required, localization, field.name)
}
30 changes: 30 additions & 0 deletions src/renderers/contentful/renderLocalizedTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/** renders helper types for --localization flag */
export default function renderLocalizedTypes(localization: boolean) {
if (!localization) return null

return `
export type LocalizedField<T> = Partial<Record<LOCALE_CODE, T>>

// We have to use our own localized version of Asset because of a bug in contentful https://github.com/contentful/contentful.js/issues/208
export interface Asset {
sys: Sys
fields: {
title: LocalizedField<string>
description: LocalizedField<string>
file: LocalizedField<{
url: string
details: {
size: number
image?: {
width: number
height: number
}
}
fileName: string
contentType: string
}>
}
toPlainObject(): object
}
`
}
20 changes: 15 additions & 5 deletions src/renderers/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,35 @@ import renderContentType from "./contentful/renderContentType"
import renderUnion from "./typescript/renderUnion"
import renderAllLocales from "./contentful/renderAllLocales"
import renderDefaultLocale from "./contentful/renderDefaultLocale"
import renderLocalizedTypes from "./contentful/renderLocalizedTypes"

export default async function render(contentTypes: ContentType[], locales: Locale[]) {
interface Options {
localization?: boolean
}

export default async function render(
contentTypes: ContentType[],
locales: Locale[],
{ 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 source = [
renderContentfulImports(),
renderAllContentTypes(sortedContentTypes),
renderContentfulImports(localization),
renderAllContentTypes(sortedContentTypes, localization),
renderAllContentTypeIds(sortedContentTypes),
renderAllLocales(sortedLocales),
renderDefaultLocale(sortedLocales),
renderLocalizedTypes(localization),
].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[], localization: boolean): string {
return contentTypes.map(contentType => renderContentType(contentType, localization)).join("\n\n")
}

function renderAllContentTypeIds(contentTypes: ContentType[]): string {
Expand Down
3 changes: 2 additions & 1 deletion src/renderers/typescript/renderInterfaceProperty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ export default function renderInterfaceProperty(
name: string,
type: string,
required: boolean,
localization: boolean,
description?: string,
): string {
return [
descriptionComment(description),
name,
required ? "" : "?",
": ",
type,
localization ? `LocalizedField<${type}>` : type,
required ? "" : " | undefined",
";",
].join("")
Expand Down
33 changes: 31 additions & 2 deletions test/renderers/contentful/renderContentType.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe("renderContentType()", () => {
}

it("works with miscellaneous field types", () => {
expect(format(renderContentType(contentType))).toMatchInlineSnapshot(`
expect(format(renderContentType(contentType, false))).toMatchInlineSnapshot(`
"export interface IMyContentTypeFields {
/** Symbol Field™ */
symbolField?: string | undefined;
Expand Down Expand Up @@ -84,7 +84,7 @@ describe("renderContentType()", () => {
})

it("supports descriptions", () => {
expect(format(renderContentType(contentTypeWithDescription))).toMatchInlineSnapshot(`
expect(format(renderContentType(contentTypeWithDescription, false))).toMatchInlineSnapshot(`
"export interface IMyContentTypeFields {}

/** This is a description */
Expand All @@ -107,4 +107,33 @@ describe("renderContentType()", () => {
}"
`)
})

it("works with localized fields", () => {
expect(format(renderContentType(contentType, true))).toMatchInlineSnapshot(`
"export interface IMyContentTypeFields {
/** Symbol Field™ */
symbolField?: LocalizedField<string> | undefined;

/** Array field */
arrayField: LocalizedField<(\\"one\\" | \\"of\\" | \\"the\\" | \\"above\\")[]>;
}

export interface IMyContentType extends Entry<IMyContentTypeFields> {
sys: {
id: string,
type: string,
createdAt: string,
updatedAt: string,
locale: string,
contentType: {
sys: {
id: \\"myContentType\\",
linkType: \\"ContentType\\",
type: \\"Link\\"
}
}
};
}"
`)
})
})
17 changes: 13 additions & 4 deletions test/renderers/contentful/renderContentfulImports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,19 @@ import format from "../../support/format"
describe("renderContentfulImports()", () => {
it("renders the top of the codegen file", () => {
expect(format(renderContentfulImports())).toMatchInlineSnapshot(`
"// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT.
"// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT.

import { Asset, Entry } from \\"contentful\\";
import { Document } from \\"@contentful/rich-text-types\\";"
`)
import { Asset, Entry } from \\"contentful\\";
import { Document } from \\"@contentful/rich-text-types\\";"
`)
})

it("renders the localized top of the codegen file", () => {
expect(format(renderContentfulImports(true))).toMatchInlineSnapshot(`
"// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT.

import { Entry } from \\"contentful\\";
import { Document } from \\"@contentful/rich-text-types\\";"
`)
})
})
Loading