Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ fileignoreconfig:
ignore_detectors:
- filecontent
- filename: package-lock.json
checksum: 935e3c4e56b12c01608ff169a0d025acee6a01e7464fa500f50c1c202c8af08b
checksum: 2e59256a4223df4fa670d9bedb571586daa21e59194400b8f9aa4725d378cc72
- filename: .husky/pre-commit
checksum: 5baabd7d2c391648163f9371f0e5e9484f8fb90fa2284cfc378732ec3192c193
- filename: src/graphqlTS/index.ts
Expand All @@ -14,4 +14,8 @@ fileignoreconfig:
checksum: a7febf9673f6bb759da48c1984cffbf813861a9e2695f1416741ca17aeeb71d7
- filename: src/generateTS/factory.ts
checksum: d6dd1ebc15493f9ed5e748f93d2f7c11b8dead5e0985482677270d450b94d270
- filename: tests/integration/graphqlTS/graphqlTS.test.ts
checksum: 7ffa82084fd0fc2f5ec9c8e8124cdf1bce08c7f75c5be2ede2eb5cd506812db1
- filename: tests/integration/generateTS/generateTS.test.ts
checksum: 7c1bc7d659ee2f9f52bf644b9e512984f89e0ff6aa4288b6e30b2c899bf80123
version: "1.0"
409 changes: 220 additions & 189 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/types-generator",
"version": "3.5.0",
"version": "3.6.0",
"description": "Contentstack type definition generation library",
"private": false,
"author": "Contentstack",
Expand Down
49 changes: 30 additions & 19 deletions src/generateTS/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type TSGenOptions = {
};
systemFields?: boolean;
isEditableTags?: boolean;
includeReferencedEntry?: boolean;
};

export type TSGenResult = {
Expand Down Expand Up @@ -73,6 +74,7 @@ const defaultOptions: TSGenOptions = {
},
systemFields: false,
isEditableTags: false,
includeReferencedEntry: false,
};

export default function (userOptions: TSGenOptions) {
Expand Down Expand Up @@ -493,6 +495,33 @@ export default function (userOptions: TSGenOptions) {
return name_type(field.reference_to);
}

function buildReferenceArrayType(references: string[], options: any): string {
// If no valid references remain, return a more specific fallback type
if (references.length === 0) {
return "Record<string, unknown>[]";
}

// Handle reference types with or without ReferencedEntry interface
if (options.includeReferencedEntry) {
const referencedEntryType = `${options.naming?.prefix || ""}ReferencedEntry`;

const wrapWithReferencedEntry = (refType: string) =>
`(${refType} | ${referencedEntryType})`;

const types =
references.length === 1
? wrapWithReferencedEntry(references[0])
: references.map(wrapWithReferencedEntry).join(" | ");

return `${types}[]`;
}

const baseType =
references.length === 1 ? references[0] : references.join(" | ");

return `${baseType}[]`;
}

function type_reference(field: ContentstackTypes.Field) {
const references: string[] = [];

Expand Down Expand Up @@ -520,25 +549,7 @@ export default function (userOptions: TSGenOptions) {
}
}

// If no valid references remain, return a more specific fallback type
if (references.length === 0) {
return "Record<string, unknown>[]";
}

// Use the ReferencedEntry interface from builtins
const referencedEntryType = `${options.naming?.prefix || ""}ReferencedEntry`;

// If there's only one reference type, create a simple union
if (references.length === 1) {
return `(${references[0]} | ${referencedEntryType})[]`;
}

// If there are multiple reference types, create separate unions for each
const unionTypes = references.map((refType) => {
return `(${refType} | ${referencedEntryType})`;
});

return `${unionTypes.join(" | ")}[]`;
return buildReferenceArrayType(references, options);
}

return function (
Expand Down
8 changes: 6 additions & 2 deletions src/generateTS/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { flatMap, flatten } from "lodash";
import { TOKEN_TYPE } from "../constants";
import { initializeContentstackSdk } from "../sdk/utils";
import { GenerateTS, GenerateTSFromContentTypes } from "../types";
import * as fs from "fs";
import { DocumentationGenerator } from "./docgen/doc";
import JSDocumentationGenerator from "./docgen/jsdoc";
import NullDocumentationGenerator from "./docgen/nulldoc";
Expand All @@ -24,6 +23,7 @@ export const generateTS = async ({
includeDocumentation,
systemFields,
isEditableTags,
includeReferencedEntry,
host,
}: GenerateTS) => {
try {
Expand Down Expand Up @@ -88,6 +88,7 @@ export const generateTS = async ({
includeDocumentation,
systemFields,
isEditableTags,
includeReferencedEntry,
});
return generatedTS;
}
Expand Down Expand Up @@ -141,6 +142,7 @@ export const generateTSFromContentTypes = async ({
includeDocumentation = true,
systemFields = false,
isEditableTags = false,
includeReferencedEntry = false,
}: GenerateTSFromContentTypes) => {
try {
const docgen: DocumentationGenerator = includeDocumentation
Expand All @@ -154,6 +156,7 @@ export const generateTSFromContentTypes = async ({
naming: { prefix },
systemFields,
isEditableTags,
includeReferencedEntry,
});
for (const contentType of contentTypes) {
const tsgenResult = tsgen(contentType);
Expand All @@ -180,7 +183,8 @@ export const generateTSFromContentTypes = async ({
prefix,
systemFields,
isEditableTags,
hasJsonField
hasJsonField,
includeReferencedEntry
).join("\n\n"),
[...globalFields].join("\n\n"),
definitions.join("\n\n"),
Expand Down
36 changes: 27 additions & 9 deletions src/generateTS/stack/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,36 @@ export const defaultInterfaces = (
prefix = "",
systemFields = false,
isEditableTags = false,
hasJsonRte?: boolean
hasJsonRte?: boolean,
includeReferencedEntry = false
) => {
const defaultInterfaces = [
`type BuildTuple<T, N extends number, R extends T[] = []> =
R['length'] extends N ? R : BuildTuple<T, N, [...R, T]>`,
`type TuplePrefixes<T extends any[]> =
T extends [any, ...infer Rest] ? T | TuplePrefixes<Rest extends any[] ? Rest : []> : []`,
`type MaxTuple<T, N extends number> = TuplePrefixes<BuildTuple<T, N>>`,
`export interface ${prefix}ReferencedEntry {
];

// Conditionally include ReferencedEntry interface
if (includeReferencedEntry) {
defaultInterfaces.push(
`export interface ${prefix}ReferencedEntry {
uid: string;
_content_type_uid: string;
}`,
}`
);
}

defaultInterfaces.push(
`export interface ${prefix}PublishDetails {
environment: string;
locale: string;
time: string;
user: string;
}`,
}`
);
defaultInterfaces.push(
`export interface ${prefix}File {
uid: string;
created_at: string;
Expand All @@ -45,19 +57,25 @@ export const defaultInterfaces = (
width: number;
}
publish_details: ${prefix}PublishDetails;
}`,
}`
);
defaultInterfaces.push(
`export interface ${prefix}Link {
title: string;
href: string;
}`,
}`
);
defaultInterfaces.push(
`export interface ${prefix}Taxonomy {
taxonomy_uid: string;
max_terms?: number;
mandatory: boolean;
non_localizable: boolean;
}`,
`export type ${prefix}TaxonomyEntry = ${prefix}Taxonomy & { term_uid: string }`,
];
}`
);
defaultInterfaces.push(
`export type ${prefix}TaxonomyEntry = ${prefix}Taxonomy & { term_uid: string }`
);
if (hasJsonRte) {
defaultInterfaces.push(
`export interface JSONRTENode {
Expand Down
2 changes: 2 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface GenerateTSBase extends StackConnectionConfig {
includeDocumentation?: boolean;
systemFields?: boolean;
isEditableTags?: boolean;
includeReferencedEntry?: boolean;
}

export type GenerateTS = GenerateTSBase;
Expand All @@ -38,4 +39,5 @@ export interface GenerateTSFromContentTypes {
includeDocumentation?: boolean;
systemFields?: boolean;
isEditableTags?: boolean;
includeReferencedEntry?: boolean;
}
50 changes: 48 additions & 2 deletions tests/integration/generateTS/generateTS.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,58 @@ describe("generateTS function", () => {
});

expect(generatedTS).toEqual(expect.stringContaining("interface")); // Check for Output is not undefined
expect(generatedTS).toMatch(/export interface CSLPAttribute/); // Check for CSLP attribute interface is created
expect(generatedTS).toMatch(/export type CSLPFieldMapping/); // Check for CSLP field mapping type is created
expect(generatedTS).toMatch(/Dishes/); // Check for whether typeDef of Content type is included
expect(generatedTS).toMatch(/export interface CSLPAttribute/); // Check for whether CSLP attribute interface is created
expect(generatedTS).toMatch(/export type CSLPFieldMapping/); // Check for whether CSLP field mapping type is created
expect(generatedTS).toMatch(/\$\?\:/); // Check for editable field mappings with $ property
expect(generatedTS).toMatch(/\?\: CSLPFieldMapping/); // Check for individual field CSLP mappings
expect(generatedTS).toMatch(/\/\*\*.*\*\/\n\s*(export)/); // Check for Documentation is generated
});

it("generates type definitions with ReferencedEntry enabled", async () => {
const token = process.env.TOKEN as unknown as any;
const apiKey = process.env.APIKEY as unknown as any;
const environment = process.env.ENVIRONMENT as unknown as any;
const region = process.env.REGION as unknown as any;
const tokenType = process.env.TOKENTYPE as unknown as any;
const includeReferencedEntry = true;

const generatedTS = await generateTS({
token,
apiKey,
environment,
region,
tokenType,
includeReferencedEntry,
});

expect(generatedTS).toEqual(expect.stringContaining("interface")); // Check for Output is not undefined
expect(generatedTS).toEqual(expect.stringContaining("Dishes")); // Check for whether typeDef of Content type is included
expect(generatedTS).toMatch(/ReferencedEntry/); // Check that ReferencedEntry interface is included
expect(generatedTS).toMatch(/\/\*\*.*\*\/\n\s*(export)/); // Check for Documentation is generated
});

it("generates type definitions without ReferencedEntry (default)", async () => {
const token = process.env.TOKEN as unknown as any;
const apiKey = process.env.APIKEY as unknown as any;
const environment = process.env.ENVIRONMENT as unknown as any;
const region = process.env.REGION as unknown as any;
const tokenType = process.env.TOKENTYPE as unknown as any;
// Don't pass includeReferencedEntry, should default to false

const generatedTS = await generateTS({
token,
apiKey,
environment,
region,
tokenType,
});

expect(generatedTS).toEqual(expect.stringContaining("interface")); // Check for Output is not undefined
expect(generatedTS).toEqual(expect.stringContaining("Dishes")); // Check for whether typeDef of Content type is included
expect(generatedTS).not.toMatch(/ReferencedEntry/); // Check that ReferencedEntry interface is not included
expect(generatedTS).toMatch(/\/\*\*.*\*\/\n\s*(export)/); // Check for Documentation is generated
});
});

describe("generateTS function with errors", () => {
Expand Down
13 changes: 7 additions & 6 deletions tests/integration/graphqlTS/graphqlTS.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ describe("graphqlTS function with errors", () => {
});

it("check for if wrong apiKey, token and environment is provided", async () => {
const token = process.env.TOKEN as unknown as any;
const apiKey = "process.env.APIKEY" as unknown as any;
const environment = process.env.ENVIRONMENT as unknown as any;
const region = process.env.REGION as unknown as any;
const branch = process.env.BRANCH as unknown as any;
const token = "";
const apiKey = "";
const environment = "";
const region = "US";
const branch = "main";

try {
await graphqlTS({
Expand All @@ -81,8 +81,9 @@ describe("graphqlTS function with errors", () => {
branch,
});
} catch (err: any) {
expect(err.error_message).toBeDefined();
expect(err.error_message).toEqual(
"Unauthorized: The apiKey, token or environment is not valid."
"Please provide all the required params (token, apiKey, environment, region)"
);
}
});
Expand Down
Loading