Skip to content

Commit 0a13f48

Browse files
authored
feat: add support for locales=* Contentful responses (#24)
Fixes #20 ## Overview Add a new `--localization` flag. When enabled, generated fields will wrapped in `LocalizedField` helper , for example: ```ts export interface IMyContentTypeFields { /** Array field */ arrayField: LocalizedField<(\"one\" | \"of\" | \"the\" | \"above\")[]> } ``` This flag is very useful when entries are being fetched with `locale="*"`. ## Notes * `field.required` is ignored, because contentful api ignores it as well. * we have to use a custom localized version of `Asset`
1 parent 2359ab8 commit 0a13f48

11 files changed

+247
-47
lines changed

src/contentful-typescript-codegen.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const cli = meow(
1818
and present, and does not provide types for Sys,
1919
Assets, or Rich Text. This is useful for ensuring raw
2020
Contentful responses will be compatible with your code.
21+
--localization -l Output fields with localized values
2122
2223
Examples
2324
$ contentful-typescript-codegen -o src/@types/generated/contentful.d.ts
@@ -43,6 +44,11 @@ const cli = meow(
4344
alias: "i",
4445
required: false,
4546
},
47+
localization: {
48+
type: "boolean",
49+
alias: "l",
50+
required: false,
51+
},
4652
},
4753
},
4854
)
@@ -59,7 +65,9 @@ async function runCodegen(outputFile: string) {
5965
if (cli.flags.fieldsOnly) {
6066
output = await renderFieldsOnly(contentTypes.items)
6167
} else {
62-
output = await render(contentTypes.items, locales.items)
68+
output = await render(contentTypes.items, locales.items, {
69+
localization: cli.flags.localization,
70+
})
6371
}
6472

6573
outputFileSync(outputPath, output)

src/renderers/contentful/renderContentType.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import renderObject from "./fields/renderObject"
1313
import renderRichText from "./fields/renderRichText"
1414
import renderSymbol from "./fields/renderSymbol"
1515

16-
export default function renderContentType(contentType: ContentType): string {
16+
export default function renderContentType(contentType: ContentType, localization: boolean): string {
1717
const name = renderContentTypeId(contentType.sys.id)
18-
const fields = renderContentTypeFields(contentType.fields)
18+
const fields = renderContentTypeFields(contentType.fields, localization)
1919
const sys = renderSys(contentType.sys)
2020

2121
return `
@@ -34,7 +34,7 @@ function descriptionComment(description: string | undefined) {
3434
return ""
3535
}
3636

37-
function renderContentTypeFields(fields: Field[]): string {
37+
function renderContentTypeFields(fields: Field[], localization: boolean): string {
3838
return fields
3939
.filter(field => !field.omitted)
4040
.map<string>(field => {
@@ -52,7 +52,7 @@ function renderContentTypeFields(fields: Field[]): string {
5252
Text: renderSymbol,
5353
}
5454

55-
return renderField(field, functionMap[field.type](field))
55+
return renderField(field, functionMap[field.type](field), localization)
5656
})
5757
.join("\n\n")
5858
}

src/renderers/contentful/renderContentfulImports.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
export default function renderContentfulImports(): string {
1+
export default function renderContentfulImports(localization: boolean = false): string {
2+
if (localization) {
3+
return `
4+
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT.
5+
6+
import { Entry } from 'contentful'
7+
import { Document } from '@contentful/rich-text-types'
8+
`
9+
}
10+
211
return `
312
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT.
413
+6-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { Field } from "contentful"
22
import renderInterfaceProperty from "../typescript/renderInterfaceProperty"
33

4-
export default function renderField(field: Field, type: string): string {
5-
return renderInterfaceProperty(field.id, type, field.required, field.name)
4+
export default function renderField(
5+
field: Field,
6+
type: string,
7+
localization: boolean = false,
8+
): string {
9+
return renderInterfaceProperty(field.id, type, field.required, localization, field.name)
610
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/** renders helper types for --localization flag */
2+
export default function renderLocalizedTypes(localization: boolean) {
3+
if (!localization) return null
4+
5+
return `
6+
export type LocalizedField<T> = Partial<Record<LOCALE_CODE, T>>
7+
8+
// We have to use our own localized version of Asset because of a bug in contentful https://github.com/contentful/contentful.js/issues/208
9+
export interface Asset {
10+
sys: Sys
11+
fields: {
12+
title: LocalizedField<string>
13+
description: LocalizedField<string>
14+
file: LocalizedField<{
15+
url: string
16+
details: {
17+
size: number
18+
image?: {
19+
width: number
20+
height: number
21+
}
22+
}
23+
fileName: string
24+
contentType: string
25+
}>
26+
}
27+
toPlainObject(): object
28+
}
29+
`
30+
}

src/renderers/render.ts

+15-5
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,35 @@ import renderContentType from "./contentful/renderContentType"
77
import renderUnion from "./typescript/renderUnion"
88
import renderAllLocales from "./contentful/renderAllLocales"
99
import renderDefaultLocale from "./contentful/renderDefaultLocale"
10+
import renderLocalizedTypes from "./contentful/renderLocalizedTypes"
1011

11-
export default async function render(contentTypes: ContentType[], locales: Locale[]) {
12+
interface Options {
13+
localization?: boolean
14+
}
15+
16+
export default async function render(
17+
contentTypes: ContentType[],
18+
locales: Locale[],
19+
{ localization = false }: Options = {},
20+
) {
1221
const sortedContentTypes = contentTypes.sort((a, b) => a.sys.id.localeCompare(b.sys.id))
1322
const sortedLocales = locales.sort((a, b) => a.code.localeCompare(b.code))
1423

1524
const source = [
16-
renderContentfulImports(),
17-
renderAllContentTypes(sortedContentTypes),
25+
renderContentfulImports(localization),
26+
renderAllContentTypes(sortedContentTypes, localization),
1827
renderAllContentTypeIds(sortedContentTypes),
1928
renderAllLocales(sortedLocales),
2029
renderDefaultLocale(sortedLocales),
30+
renderLocalizedTypes(localization),
2131
].join("\n\n")
2232

2333
const prettierConfig = await resolveConfig(process.cwd())
2434
return format(source, { ...prettierConfig, parser: "typescript" })
2535
}
2636

27-
function renderAllContentTypes(contentTypes: ContentType[]): string {
28-
return contentTypes.map(contentType => renderContentType(contentType)).join("\n\n")
37+
function renderAllContentTypes(contentTypes: ContentType[], localization: boolean): string {
38+
return contentTypes.map(contentType => renderContentType(contentType, localization)).join("\n\n")
2939
}
3040

3141
function renderAllContentTypeIds(contentTypes: ContentType[]): string {

src/renderers/typescript/renderInterfaceProperty.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ export default function renderInterfaceProperty(
22
name: string,
33
type: string,
44
required: boolean,
5+
localization: boolean,
56
description?: string,
67
): string {
78
return [
89
descriptionComment(description),
910
name,
1011
required ? "" : "?",
1112
": ",
12-
type,
13+
localization ? `LocalizedField<${type}>` : type,
1314
required ? "" : " | undefined",
1415
";",
1516
].join("")

test/renderers/contentful/renderContentType.test.ts

+31-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe("renderContentType()", () => {
5555
}
5656

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

8686
it("supports descriptions", () => {
87-
expect(format(renderContentType(contentTypeWithDescription))).toMatchInlineSnapshot(`
87+
expect(format(renderContentType(contentTypeWithDescription, false))).toMatchInlineSnapshot(`
8888
"export interface IMyContentTypeFields {}
8989
9090
/** This is a description */
@@ -107,4 +107,33 @@ describe("renderContentType()", () => {
107107
}"
108108
`)
109109
})
110+
111+
it("works with localized fields", () => {
112+
expect(format(renderContentType(contentType, true))).toMatchInlineSnapshot(`
113+
"export interface IMyContentTypeFields {
114+
/** Symbol Field™ */
115+
symbolField?: LocalizedField<string> | undefined;
116+
117+
/** Array field */
118+
arrayField: LocalizedField<(\\"one\\" | \\"of\\" | \\"the\\" | \\"above\\")[]>;
119+
}
120+
121+
export interface IMyContentType extends Entry<IMyContentTypeFields> {
122+
sys: {
123+
id: string,
124+
type: string,
125+
createdAt: string,
126+
updatedAt: string,
127+
locale: string,
128+
contentType: {
129+
sys: {
130+
id: \\"myContentType\\",
131+
linkType: \\"ContentType\\",
132+
type: \\"Link\\"
133+
}
134+
}
135+
};
136+
}"
137+
`)
138+
})
110139
})

test/renderers/contentful/renderContentfulImports.test.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,19 @@ import format from "../../support/format"
44
describe("renderContentfulImports()", () => {
55
it("renders the top of the codegen file", () => {
66
expect(format(renderContentfulImports())).toMatchInlineSnapshot(`
7-
"// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT.
7+
"// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT.
88
9-
import { Asset, Entry } from \\"contentful\\";
10-
import { Document } from \\"@contentful/rich-text-types\\";"
11-
`)
9+
import { Asset, Entry } from \\"contentful\\";
10+
import { Document } from \\"@contentful/rich-text-types\\";"
11+
`)
12+
})
13+
14+
it("renders the localized top of the codegen file", () => {
15+
expect(format(renderContentfulImports(true))).toMatchInlineSnapshot(`
16+
"// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT.
17+
18+
import { Entry } from \\"contentful\\";
19+
import { Document } from \\"@contentful/rich-text-types\\";"
20+
`)
1221
})
1322
})

0 commit comments

Comments
 (0)