Skip to content

Commit 4e026c0

Browse files
author
naman-contentstack
committed
Merge branch 'development' into enhancement/DX-3404
2 parents 7ea8b71 + 9807923 commit 4e026c0

File tree

9 files changed

+308
-178
lines changed

9 files changed

+308
-178
lines changed

.talismanrc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ fileignoreconfig:
33
ignore_detectors:
44
- filecontent
55
- filename: package-lock.json
6-
checksum: 935e3c4e56b12c01608ff169a0d025acee6a01e7464fa500f50c1c202c8af08b
6+
checksum: 8c3e07947c069ab2ea4e523b18157b600707eb43fe06612a05bcb23e55fe5fc6
77
- filename: .husky/pre-commit
88
checksum: 5baabd7d2c391648163f9371f0e5e9484f8fb90fa2284cfc378732ec3192c193
99
- filename: src/graphqlTS/index.ts
@@ -14,4 +14,8 @@ fileignoreconfig:
1414
checksum: a7febf9673f6bb759da48c1984cffbf813861a9e2695f1416741ca17aeeb71d7
1515
- filename: src/generateTS/factory.ts
1616
checksum: d6dd1ebc15493f9ed5e748f93d2f7c11b8dead5e0985482677270d450b94d270
17+
- filename: tests/integration/graphqlTS/graphqlTS.test.ts
18+
checksum: 7ffa82084fd0fc2f5ec9c8e8124cdf1bce08c7f75c5be2ede2eb5cd506812db1
19+
- filename: tests/integration/generateTS/generateTS.test.ts
20+
checksum: 7c1bc7d659ee2f9f52bf644b9e512984f89e0ff6aa4288b6e30b2c899bf80123
1721
version: "1.0"

package-lock.json

Lines changed: 122 additions & 122 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/generateTS/factory.ts

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export type TSGenOptions = {
1818
};
1919
systemFields?: boolean;
2020
isEditableTags?: boolean;
21+
includeReferencedEntry?: boolean;
2122
};
2223

2324
export type TSGenResult = {
@@ -74,6 +75,7 @@ const defaultOptions: TSGenOptions = {
7475
},
7576
systemFields: false,
7677
isEditableTags: false,
78+
includeReferencedEntry: false,
7779
};
7880

7981
export default function (userOptions: TSGenOptions) {
@@ -534,6 +536,33 @@ export default function (userOptions: TSGenOptions) {
534536
return name_type(field.reference_to);
535537
}
536538

539+
function buildReferenceArrayType(references: string[], options: any): string {
540+
// If no valid references remain, return a more specific fallback type
541+
if (references.length === 0) {
542+
return "Record<string, unknown>[]";
543+
}
544+
545+
// Handle reference types with or without ReferencedEntry interface
546+
if (options.includeReferencedEntry) {
547+
const referencedEntryType = `${options.naming?.prefix || ""}ReferencedEntry`;
548+
549+
const wrapWithReferencedEntry = (refType: string) =>
550+
`(${refType} | ${referencedEntryType})`;
551+
552+
const types =
553+
references.length === 1
554+
? wrapWithReferencedEntry(references[0])
555+
: references.map(wrapWithReferencedEntry).join(" | ");
556+
557+
return `${types}[]`;
558+
}
559+
560+
const baseType =
561+
references.length === 1 ? references[0] : references.join(" | ");
562+
563+
return `${baseType}[]`;
564+
}
565+
537566
function type_reference(field: ContentstackTypes.Field) {
538567
const references: string[] = [];
539568

@@ -561,25 +590,7 @@ export default function (userOptions: TSGenOptions) {
561590
}
562591
}
563592

564-
// If no valid references remain, return a more specific fallback type
565-
if (references.length === 0) {
566-
return "Record<string, unknown>[]";
567-
}
568-
569-
// Use the ReferencedEntry interface from builtins
570-
const referencedEntryType = `${options.naming?.prefix || ""}ReferencedEntry`;
571-
572-
// If there's only one reference type, create a simple union
573-
if (references.length === 1) {
574-
return `(${references[0]} | ${referencedEntryType})[]`;
575-
}
576-
577-
// If there are multiple reference types, create separate unions for each
578-
const unionTypes = references.map((refType) => {
579-
return `(${refType} | ${referencedEntryType})`;
580-
});
581-
582-
return `${unionTypes.join(" | ")}[]`;
593+
return buildReferenceArrayType(references, options);
583594
}
584595

585596
return function (

src/generateTS/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const generateTS = async ({
2424
includeDocumentation,
2525
systemFields,
2626
isEditableTags,
27+
includeReferencedEntry,
2728
host,
2829
}: GenerateTS) => {
2930
try {
@@ -84,6 +85,7 @@ export const generateTS = async ({
8485
includeDocumentation,
8586
systemFields,
8687
isEditableTags,
88+
includeReferencedEntry,
8789
});
8890
return generatedTS;
8991
}
@@ -134,6 +136,7 @@ export const generateTSFromContentTypes = async ({
134136
includeDocumentation = true,
135137
systemFields = false,
136138
isEditableTags = false,
139+
includeReferencedEntry = false,
137140
}: GenerateTSFromContentTypes) => {
138141
try {
139142
const docgen: DocumentationGenerator = includeDocumentation
@@ -147,6 +150,7 @@ export const generateTSFromContentTypes = async ({
147150
naming: { prefix },
148151
systemFields,
149152
isEditableTags,
153+
includeReferencedEntry,
150154
});
151155
for (const contentType of contentTypes) {
152156
const tsgenResult = tsgen(contentType);
@@ -173,7 +177,8 @@ export const generateTSFromContentTypes = async ({
173177
prefix,
174178
systemFields,
175179
isEditableTags,
176-
hasJsonField
180+
hasJsonField,
181+
includeReferencedEntry
177182
).join("\n\n"),
178183
[...globalFields].join("\n\n"),
179184
definitions.join("\n\n"),

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/integration/graphqlTS/graphqlTS.test.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ describe("graphqlTS function with errors", () => {
6666
});
6767

6868
it("check for if wrong apiKey, token and environment is provided", async () => {
69-
const token = "invalid-token";
70-
const apiKey = process.env.APIKEY as unknown as any;
71-
const environment = process.env.ENVIRONMENT as unknown as any;
72-
const region = process.env.REGION as unknown as any;
73-
const branch = process.env.BRANCH as unknown as any;
69+
const token = "";
70+
const apiKey = "";
71+
const environment = "";
72+
const region = "US";
73+
const branch = "main";
7474

7575
try {
7676
await graphqlTS({
@@ -81,8 +81,9 @@ describe("graphqlTS function with errors", () => {
8181
branch,
8282
});
8383
} catch (err: any) {
84-
expect(err.error_message).toMatch(
85-
/(unauthorized|invalid|not valid|error|failed)/i
84+
expect(err.error_message).toBeDefined();
85+
expect(err.error_message).toEqual(
86+
"Please provide all the required params (token, apiKey, environment, region)"
8687
);
8788
}
8889
});

0 commit comments

Comments
 (0)