Skip to content

Commit d88e393

Browse files
author
naman-contentstack
committed
flag based approach for referenced entries
1 parent 1a62a0b commit d88e393

6 files changed

Lines changed: 198 additions & 41 deletions

File tree

src/generateTS/factory.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type TSGenOptions = {
1717
};
1818
systemFields?: boolean;
1919
isEditableTags?: boolean;
20+
includeReferencedEntry?: boolean;
2021
};
2122

2223
export type TSGenResult = {
@@ -73,6 +74,7 @@ const defaultOptions: TSGenOptions = {
7374
},
7475
systemFields: false,
7576
isEditableTags: false,
77+
includeReferencedEntry: false,
7678
};
7779

7880
export default function (userOptions: TSGenOptions) {
@@ -525,20 +527,29 @@ export default function (userOptions: TSGenOptions) {
525527
return "Record<string, unknown>[]";
526528
}
527529

528-
// Use the ReferencedEntry interface from builtins
529-
const referencedEntryType = `${options.naming?.prefix || ""}ReferencedEntry`;
530+
// Conditionally use the ReferencedEntry interface from builtins
531+
if (options.includeReferencedEntry) {
532+
const referencedEntryType = `${options.naming?.prefix || ""}ReferencedEntry`;
530533

531-
// If there's only one reference type, create a simple union
532-
if (references.length === 1) {
533-
return `(${references[0]} | ${referencedEntryType})[]`;
534-
}
534+
// If there's only one reference type, create a simple union
535+
if (references.length === 1) {
536+
return `(${references[0]} | ${referencedEntryType})[]`;
537+
}
535538

536-
// If there are multiple reference types, create separate unions for each
537-
const unionTypes = references.map((refType) => {
538-
return `(${refType} | ${referencedEntryType})`;
539-
});
539+
// If there are multiple reference types, create separate unions for each
540+
const unionTypes = references.map((refType) => {
541+
return `(${refType} | ${referencedEntryType})`;
542+
});
540543

541-
return `${unionTypes.join(" | ")}[]`;
544+
return `${unionTypes.join(" | ")}[]`;
545+
} else {
546+
// If ReferencedEntry is disabled, just use the reference types directly
547+
if (references.length === 1) {
548+
return `${references[0]}[]`;
549+
}
550+
551+
return `${references.join(" | ")}[]`;
552+
}
542553
}
543554

544555
return function (

src/generateTS/index.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import async from "async";
22
import { flatMap, flatten } from "lodash";
33
import { TOKEN_TYPE } from "../constants";
44
import { initializeContentstackSdk } from "../sdk/utils";
5-
import { GenerateTS, GenerateTSFromContentTypes } from "../types";
5+
import {
6+
GenerateTS,
7+
GenerateTSBase,
8+
GenerateTSFromContentTypes,
9+
} from "../types";
610
import * as fs from "fs";
711
import { DocumentationGenerator } from "./docgen/doc";
812
import JSDocumentationGenerator from "./docgen/jsdoc";
@@ -24,6 +28,7 @@ export const generateTS = async ({
2428
includeDocumentation,
2529
systemFields,
2630
isEditableTags,
31+
includeReferencedEntry,
2732
host,
2833
}: GenerateTS) => {
2934
try {
@@ -88,6 +93,7 @@ export const generateTS = async ({
8893
includeDocumentation,
8994
systemFields,
9095
isEditableTags,
96+
includeReferencedEntry,
9197
});
9298
return generatedTS;
9399
}
@@ -141,6 +147,7 @@ export const generateTSFromContentTypes = async ({
141147
includeDocumentation = true,
142148
systemFields = false,
143149
isEditableTags = false,
150+
includeReferencedEntry = false,
144151
}: GenerateTSFromContentTypes) => {
145152
try {
146153
const docgen: DocumentationGenerator = includeDocumentation
@@ -154,6 +161,7 @@ export const generateTSFromContentTypes = async ({
154161
naming: { prefix },
155162
systemFields,
156163
isEditableTags,
164+
includeReferencedEntry,
157165
});
158166
for (const contentType of contentTypes) {
159167
const tsgenResult = tsgen(contentType);
@@ -180,7 +188,8 @@ export const generateTSFromContentTypes = async ({
180188
prefix,
181189
systemFields,
182190
isEditableTags,
183-
hasJsonField
191+
hasJsonField,
192+
includeReferencedEntry
184193
).join("\n\n"),
185194
[...globalFields].join("\n\n"),
186195
definitions.join("\n\n"),
@@ -257,3 +266,31 @@ const checkJsonField = (schema: any[]): boolean => {
257266
return false;
258267
});
259268
};
269+
270+
const fun = async () => {
271+
try {
272+
const config: GenerateTSBase = {
273+
apiKey: "***REMOVED***",
274+
token: "***REMOVED***",
275+
environment: "dev",
276+
tokenType: "delivery",
277+
region: "AWS-NA",
278+
includeDocumentation: false,
279+
systemFields: true,
280+
includeReferencedEntry: true,
281+
// isEditableTags: true,
282+
// host: "stag-cdn.csnonprod.com",
283+
branch: "main",
284+
};
285+
286+
const val = await generateTS(config);
287+
fs.writeFileSync(
288+
"/Users/naman.dembla/Documents/TS-GEN/types-generator/generated.ts",
289+
JSON.parse(JSON.stringify(val)),
290+
"utf-8"
291+
);
292+
} catch (err: any) {
293+
console.log("🚀 ~ fun ~ err:", err);
294+
}
295+
};
296+
fun();

src/generateTS/stack/builtins.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,36 @@ export const defaultInterfaces = (
44
prefix = "",
55
systemFields = false,
66
isEditableTags = false,
7-
hasJsonRte?: boolean
7+
hasJsonRte?: boolean,
8+
includeReferencedEntry = false
89
) => {
910
const defaultInterfaces = [
1011
`type BuildTuple<T, N extends number, R extends T[] = []> =
1112
R['length'] extends N ? R : BuildTuple<T, N, [...R, T]>`,
1213
`type TuplePrefixes<T extends any[]> =
1314
T extends [any, ...infer Rest] ? T | TuplePrefixes<Rest extends any[] ? Rest : []> : []`,
1415
`type MaxTuple<T, N extends number> = TuplePrefixes<BuildTuple<T, N>>`,
15-
`export interface ${prefix}ReferencedEntry {
16+
];
17+
18+
// Conditionally include ReferencedEntry interface
19+
if (includeReferencedEntry) {
20+
defaultInterfaces.push(
21+
`export interface ${prefix}ReferencedEntry {
1622
uid: string;
1723
_content_type_uid: string;
18-
}`,
24+
}`
25+
);
26+
}
27+
28+
defaultInterfaces.push(
1929
`export interface ${prefix}PublishDetails {
2030
environment: string;
2131
locale: string;
2232
time: string;
2333
user: string;
24-
}`,
34+
}`
35+
);
36+
defaultInterfaces.push(
2537
`export interface ${prefix}File {
2638
uid: string;
2739
created_at: string;
@@ -45,19 +57,25 @@ export const defaultInterfaces = (
4557
width: number;
4658
}
4759
publish_details: ${prefix}PublishDetails;
48-
}`,
60+
}`
61+
);
62+
defaultInterfaces.push(
4963
`export interface ${prefix}Link {
5064
title: string;
5165
href: string;
52-
}`,
66+
}`
67+
);
68+
defaultInterfaces.push(
5369
`export interface ${prefix}Taxonomy {
5470
taxonomy_uid: string;
5571
max_terms?: number;
5672
mandatory: boolean;
5773
non_localizable: boolean;
58-
}`,
59-
`export type ${prefix}TaxonomyEntry = ${prefix}Taxonomy & { term_uid: string }`,
60-
];
74+
}`
75+
);
76+
defaultInterfaces.push(
77+
`export type ${prefix}TaxonomyEntry = ${prefix}Taxonomy & { term_uid: string }`
78+
);
6179
if (hasJsonRte) {
6280
defaultInterfaces.push(
6381
`export interface JSONRTENode {

src/types/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface GenerateTSBase extends StackConnectionConfig {
2424
includeDocumentation?: boolean;
2525
systemFields?: boolean;
2626
isEditableTags?: boolean;
27+
includeReferencedEntry?: boolean;
2728
}
2829

2930
export type GenerateTS = GenerateTSBase;
@@ -38,4 +39,5 @@ export interface GenerateTSFromContentTypes {
3839
includeDocumentation?: boolean;
3940
systemFields?: boolean;
4041
isEditableTags?: boolean;
42+
includeReferencedEntry?: boolean;
4143
}

tests/integration/generateTS/generateTS.test.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,58 @@ describe("generateTS function", () => {
111111
});
112112

113113
expect(generatedTS).toEqual(expect.stringContaining("interface")); // Check for Output is not undefined
114-
expect(generatedTS).toMatch(/export interface CSLPAttribute/); // Check for CSLP attribute interface is created
115-
expect(generatedTS).toMatch(/export type CSLPFieldMapping/); // Check for CSLP field mapping type is created
114+
expect(generatedTS).toMatch(/Dishes/); // Check for whether typeDef of Content type is included
115+
expect(generatedTS).toMatch(/export interface CSLPAttribute/); // Check for whether CSLP attribute interface is created
116+
expect(generatedTS).toMatch(/export type CSLPFieldMapping/); // Check for whether CSLP field mapping type is created
116117
expect(generatedTS).toMatch(/\$\?\:/); // Check for editable field mappings with $ property
117118
expect(generatedTS).toMatch(/\?\: CSLPFieldMapping/); // Check for individual field CSLP mappings
118119
expect(generatedTS).toMatch(/\/\*\*.*\*\/\n\s*(export)/); // Check for Documentation is generated
119120
});
121+
122+
it("generates type definitions with ReferencedEntry enabled", async () => {
123+
const token = process.env.TOKEN as unknown as any;
124+
const apiKey = process.env.APIKEY as unknown as any;
125+
const environment = process.env.ENVIRONMENT as unknown as any;
126+
const region = process.env.REGION as unknown as any;
127+
const tokenType = process.env.TOKENTYPE as unknown as any;
128+
const includeReferencedEntry = true;
129+
130+
const generatedTS = await generateTS({
131+
token,
132+
apiKey,
133+
environment,
134+
region,
135+
tokenType,
136+
includeReferencedEntry,
137+
});
138+
139+
expect(generatedTS).toEqual(expect.stringContaining("interface")); // Check for Output is not undefined
140+
expect(generatedTS).toEqual(expect.stringContaining("Dishes")); // Check for whether typeDef of Content type is included
141+
expect(generatedTS).toMatch(/ReferencedEntry/); // Check that ReferencedEntry interface is included
142+
expect(generatedTS).toMatch(/\/\*\*.*\*\/\n\s*(export)/); // Check for Documentation is generated
143+
});
144+
145+
it("generates type definitions without ReferencedEntry (default)", async () => {
146+
const token = process.env.TOKEN as unknown as any;
147+
const apiKey = process.env.APIKEY as unknown as any;
148+
const environment = process.env.ENVIRONMENT as unknown as any;
149+
const region = process.env.REGION as unknown as any;
150+
const tokenType = process.env.TOKENTYPE as unknown as any;
151+
// Don't pass includeReferencedEntry, should default to false
152+
153+
const generatedTS = await generateTS({
154+
token,
155+
apiKey,
156+
environment,
157+
region,
158+
tokenType,
159+
});
160+
161+
expect(generatedTS).toEqual(expect.stringContaining("interface")); // Check for Output is not undefined
162+
expect(generatedTS).toEqual(expect.stringContaining("Dishes")); // Check for whether typeDef of Content type is included
163+
expect(generatedTS).not.toMatch(/ReferencedEntry/); // Check that ReferencedEntry interface is not included
164+
expect(generatedTS).toMatch(/\/\*\*.*\*\/\n\s*(export)/); // Check for Documentation is generated
165+
});
120166
});
121167

122168
describe("generateTS function with errors", () => {

tests/unit/tsgen/references.test.ts

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,70 @@ const tsgen = tsgenFactory({
1010
},
1111
});
1212

13+
const tsgenWithReferencedEntry = tsgenFactory({
14+
docgen: new NullDocumentationGenerator(),
15+
naming: {
16+
prefix: "I",
17+
},
18+
includeReferencedEntry: true,
19+
});
20+
1321
describe("references", () => {
14-
const result = tsgen(testData.references);
22+
describe("with ReferencedEntry disabled (default)", () => {
23+
const result = tsgen(testData.references);
24+
25+
test("metadata", () => {
26+
const contentTypes = [...result.metadata.dependencies.contentTypes];
1527

16-
test("metadata", () => {
17-
const contentTypes = [...result.metadata.dependencies.contentTypes];
28+
expect(contentTypes).toEqual(
29+
expect.arrayContaining([
30+
"IReferenceChild",
31+
"IBoolean",
32+
"IBuiltinExample",
33+
])
34+
);
35+
});
1836

19-
expect(contentTypes).toEqual(
20-
expect.arrayContaining(["IReferenceChild", "IBoolean", "IBuiltinExample"])
21-
);
37+
test("definition", () => {
38+
expect(result.definition).toMatchInlineSnapshot(`
39+
"export interface IReferenceParent
40+
{
41+
_version?: number;
42+
title: string;
43+
url: string;
44+
single_reference: IReferenceChild[];
45+
multiple_reference?: IReferenceChild | IBoolean | IBuiltinExample[];
46+
}"
47+
`);
48+
});
2249
});
2350

24-
test("definition", () => {
25-
expect(result.definition).toMatchInlineSnapshot(`
26-
"export interface IReferenceParent
27-
{
28-
_version?: number;
29-
title: string;
30-
url: string;
31-
single_reference: (IReferenceChild | IReferencedEntry)[];
32-
multiple_reference?: (IReferenceChild | IReferencedEntry) | (IBoolean | IReferencedEntry) | (IBuiltinExample | IReferencedEntry)[];
33-
}"
34-
`);
51+
describe("with ReferencedEntry enabled", () => {
52+
const result = tsgenWithReferencedEntry(testData.references);
53+
54+
test("metadata", () => {
55+
const contentTypes = [...result.metadata.dependencies.contentTypes];
56+
57+
expect(contentTypes).toEqual(
58+
expect.arrayContaining([
59+
"IReferenceChild",
60+
"IBoolean",
61+
"IBuiltinExample",
62+
])
63+
);
64+
});
65+
66+
test("definition", () => {
67+
expect(result.definition).toMatchInlineSnapshot(`
68+
"export interface IReferenceParent
69+
{
70+
_version?: number;
71+
title: string;
72+
url: string;
73+
single_reference: (IReferenceChild | IReferencedEntry)[];
74+
multiple_reference?: (IReferenceChild | IReferencedEntry) | (IBoolean | IReferencedEntry) | (IBuiltinExample | IReferencedEntry)[];
75+
}"
76+
`);
77+
});
3578
});
3679
});

0 commit comments

Comments
 (0)