Skip to content

Commit 3e7dcb3

Browse files
Merge pull request #65 from contentstack/development
DX | 24-03-2025 | Release
2 parents cbc810b + b8ad91f commit 3e7dcb3

File tree

10 files changed

+445
-358
lines changed

10 files changed

+445
-358
lines changed

package-lock.json

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

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentstack/types-generator",
3-
"version": "2.2.0",
3+
"version": "2.3.0",
44
"description": "Contentstack type definition generation library",
55
"private": false,
66
"author": "Contentstack",
@@ -29,6 +29,7 @@
2929
},
3030
"homepage": "https://github.com/contentstack/types-generator",
3131
"devDependencies": {
32+
"@types/async": "^3.2.24",
3233
"@types/jest": "^29.5.14",
3334
"@types/lodash": "^4.17.15",
3435
"@types/node": "^20.17.17",
@@ -45,7 +46,8 @@
4546
"dependencies": {
4647
"@contentstack/delivery-sdk": "^4.5.0",
4748
"@gql2ts/from-schema": "^2.0.0-4",
48-
"axios": "^1.7.9",
49+
"async": "^3.2.6",
50+
"axios": "^1.8.4",
4951
"lodash": "^4.17.21",
5052
"prettier": "^3.4.2"
5153
},

src/generateTS/factory.ts

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@ export default function (userOptions: TSGenOptions) {
162162
op = "[]";
163163

164164
if (field.max_instance) {
165-
const elements = new Array(field.max_instance).fill(type as any);
166-
return ["[", elements.join(", "), "]"].join("");
165+
return ["MaxTuple<", type, ", ", field.max_instance, ">"].join("");
167166
}
168167
}
169168

@@ -215,35 +214,18 @@ export default function (userOptions: TSGenOptions) {
215214
return op_array(type, field);
216215
}
217216

217+
const handleGlobalField = (field: ContentstackTypes.Field): string => {
218+
const referenceName = name_type(field.reference_to);
219+
// Return the reference name with array brackets if the field is multiple
220+
return `${referenceName}${field.multiple ? "[]" : ""}`;
221+
};
222+
223+
218224
function visit_field(field: ContentstackTypes.Field) {
219225
let fieldType = "";
220226
// Check if the field is a global field
221227
if (field.data_type === "global_field") {
222-
// Check if the field is cached
223-
const isCached = cachedGlobalFields[name_type(field.reference_to)];
224-
225-
// Generate the referred_content_types array
226-
const referredContentTypes = [
227-
{
228-
title: name_type(field.reference_to),
229-
uid: field.reference_to,
230-
},
231-
];
232-
233-
// Assign the new structure for the global field
234-
fieldType = `referred_content_types: ${JSON.stringify(
235-
referredContentTypes
236-
)}`;
237-
238-
// If it's a multiple field, append '[]' to the fieldType
239-
if (field.multiple) {
240-
fieldType += "[]";
241-
}
242-
243-
// If the field is not cached and there is a reference, update fieldType accordingly
244-
if (!isCached && field.reference_to) {
245-
fieldType = type_reference(field);
246-
}
228+
fieldType = handleGlobalField(field);
247229
} else if (field.data_type === "blocks") {
248230
// Handle blocks type (unchanged)
249231
fieldType = type_modular_blocks(field);
@@ -264,18 +246,17 @@ export default function (userOptions: TSGenOptions) {
264246
: " | null"
265247
: "";
266248

267-
if (fieldType.startsWith("referred_content_types")) {
268-
// For global_field or referred_content_types, omit field.uid in output
269-
return `${fieldType}`;
270-
}
271249
// Ensure the formatting is correct, and avoid concatenating field.uid directly to a string
272250
return `${field.uid}${requiredFlag}: ${fieldType}${typeModifier};`;
273251
}
274252

275253
function visit_fields(schema: ContentstackTypes.Schema) {
276254
return schema
277255
.map((v) => {
278-
return [options.docgen.field(v.display_name), visit_field(v)]
256+
return [
257+
options.docgen.field(v.display_name),
258+
visit_field(v),
259+
]
279260
.filter((v) => v)
280261
.join("\n");
281262
})

src/generateTS/index.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import async from "async";
2+
import {flatMap, flatten} from 'lodash';
13
import { TOKEN_TYPE } from "../constants";
24
import { initializeContentstackSdk } from "../sdk/utils";
35
import { GenerateTS, GenerateTSFromContentTypes } from "../types";
@@ -41,8 +43,9 @@ export const generateTS = async ({
4143
});
4244

4345
const contentTypeQuery = Stack.contentType();
46+
contentTypeQuery._queryParams['include_count'] = 'true';
4447
const globalFieldsQuery = Stack.globalField();
45-
const contentTypes = await contentTypeQuery.find();
48+
const contentTypes = await getContentTypes(contentTypeQuery);
4649
const globalFields = await globalFieldsQuery.find();
4750

4851
const { content_types }: any = contentTypes;
@@ -163,3 +166,31 @@ export const generateTSFromContentTypes = async ({
163166
};
164167
}
165168
};
169+
170+
const getContentTypes = async (contentTypeQuery: any) => {
171+
try {
172+
const limit = 100;
173+
174+
const results: any = await contentTypeQuery.find();
175+
176+
if (results?.count > limit) {
177+
const additionalQueries = Array.from(
178+
{ length: Math.ceil(results.count / limit) - 1 },
179+
(_, i) => {
180+
return async.reflect(async () => {
181+
contentTypeQuery._queryParams['skip'] = (i + 1) * limit;
182+
contentTypeQuery._queryParams['limit'] = limit;
183+
return contentTypeQuery.find();
184+
});
185+
}
186+
);
187+
const additionalResults: any = (await async.parallel(additionalQueries));
188+
const flattenedResult = additionalResults.flatMap((res: any) => res?.value?.content_types);
189+
results.content_types = flatten([flattenedResult, results.content_types]);
190+
}
191+
192+
return results;
193+
} catch (error) {
194+
throw error;
195+
}
196+
};

src/generateTS/stack/builtins.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ export const defaultInterfaces = (
44
hasJsonRte?: boolean
55
) => {
66
const defaultInterfaces = [
7+
`type BuildTuple<T, N extends number, R extends T[] = []> =
8+
R['length'] extends N ? R : BuildTuple<T, N, [...R, T]>`,
9+
`type TuplePrefixes<T extends any[]> =
10+
T extends [any, ...infer Rest] ? T | TuplePrefixes<Rest extends any[] ? Rest : []> : []`,
11+
`type MaxTuple<T, N extends number> = TuplePrefixes<BuildTuple<T, N>>`,
712
`export interface ${prefix}PublishDetails {
813
environment: string;
914
locale: string;

src/graphqlTS/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,14 @@ export async function graphqlTS({
8080
"Unauthorized: The apiKey, token or environment is not valid.",
8181
};
8282
} else {
83+
let details = '';
84+
if (error.response.data.errors[0]?.extensions?.errors?.[0]?.code === 'SCHEMA_BUILD_ERROR') {
85+
error.response.data.errors[0].extensions.errors[0].details.forEach((element: {error: string}) => {
86+
details += element.error + '\n'
87+
});
88+
}
8389
throw {
84-
error_message:
85-
error.response.data.errors[0]?.extensions?.errors[0].message,
90+
error_message: details ? details : error.response.data.errors[0]?.extensions?.errors[0].message,
8691
};
8792
}
8893
}

tests/unit/tsgen/group.test.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,9 @@ describe("group", () => {
2626
/** Version */
2727
_version: number;
2828
title: string;
29-
multiple_group_max_limit?: [{
29+
multiple_group_max_limit?: MaxTuple<{
3030
number?: number | null;
31-
}, {
32-
number?: number | null;
33-
}, {
34-
number?: number | null;
35-
}, {
36-
number?: number | null;
37-
}, {
38-
number?: number | null;
39-
}];
31+
}, 5>;
4032
multiple_group?: {
4133
single_line?: string;
4234
}[];

tests/unit/tsgen/isodate.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ describe("builtin isodate field", () => {
3030
date?: string | null;
3131
date_required: string;
3232
date_multiple?: string[] | null;
33-
date_multiple_maxlength?: [string, string, string, string, string] | null;
34-
date_required_multiple_maxlength: [string, string, string, string, string, string, string, string];
33+
date_multiple_maxlength?: MaxTuple<string, 5> | null;
34+
date_required_multiple_maxlength: MaxTuple<string, 8>;
3535
}"
3636
`);
3737
});

tests/unit/tsgen/number.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ describe("builtin number field", () => {
2828
number?: number | null;
2929
number_required: number;
3030
number_multiple?: number[] | null;
31-
number_multiple_max_limit?: [number, number, number, number, number, number, number, number, number, number] | null;
32-
number_required_multiple_max_limit: [number, number, number];
31+
number_multiple_max_limit?: MaxTuple<number, 10> | null;
32+
number_required_multiple_max_limit: MaxTuple<number, 3>;
3333
}"
3434
`);
3535
});

tests/unit/tsgen/options.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe("all options", () => {
2121
single_line_textbox_not_required?: string;
2222
single_line_textbox_required: string;
2323
single_line_textbox_multiple?: string[];
24-
single_line_textbox_multiple_max_limit?: [string, string, string, string, string];
24+
single_line_textbox_multiple_max_limit?: MaxTuple<string, 5>;
2525
}"
2626
`);
2727
});

0 commit comments

Comments
 (0)