UNPKG

@contentstack/types-generator

Version:

Contentstack type definition generation library

1,267 lines (1,248 loc) 42.7 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/web.ts var web_exports = {}; __export(web_exports, { generateTS: () => generateTS, generateTSFromContentTypes: () => generateTSFromContentTypes }); module.exports = __toCommonJS(web_exports); // src/generateTS/index.ts var import_async = __toESM(require("async")); var import_lodash = require("lodash"); // src/constants/messages.ts var ERROR_MESSAGES = { // Validation Errors MISSING_REQUIRED_PARAMS: "Missing required parameters", REQUIRED_PARAMS_LIST: "Required: token, apiKey, environment, region", UNSUPPORTED_REGION: (region) => `Unsupported region: ${region}`, SUPPORTED_REGIONS: "Supported regions: US, EU, AU, AZURE_NA, AZURE_EU, GCP_NA, GCP_EU", CUSTOM_HOST_OPTION: "Or provide a custom host", // Content Type Errors NO_CONTENT_TYPES: "No Content Types found in the Stack", CREATE_CONTENT_MODELS: "Please create Content Models to generate type definitions", NO_CONTENT_TYPES_DETAILED: "There are no Content Types in the Stack, please create Content Models to generate type definitions", // Authentication Errors UNAUTHORIZED: "Unauthorized: The apiKey, token or region is not valid.", INVALID_CREDENTIALS: "Invalid credentials. Please verify your apiKey, token, and region.", INVALID_CREDENTIALS_GRAPHQL: "Unauthorized: The apiKey, token or environment is not valid.", // API Errors API_ERROR_DEFAULT: "Something went wrong", API_ERROR_WITH_STATUS: (status, message) => `API error occurred. Status: ${status}${message ? `. ${message}` : ""}`, GRAPHQL_SCHEMA_ERROR: "An error occurred while processing GraphQL schema", // Field/Block Skip Messages SKIPPED_FIELD_UNKNOWN_TYPE: (uid, dataType, reason) => `Skipped field "${uid}" with unknown type "${dataType}": ${reason}`, SKIPPED_GLOBAL_FIELD_REFERENCE: (uid, referenceTo, reason) => `Skipped global field reference "${uid}" to "${referenceTo}": ${reason}`, SKIPPED_FIELD_AT_PATH: (uid, path, reason) => `Skipped field "${uid}" at path "${path}": ${reason}`, SKIPPED_BLOCK_AT_PATH: (uid, path, reason) => `Skipped block "${uid}" at path "${path}": ${reason}`, SKIPPED_GLOBAL_FIELD: (uid, reason) => `Skipped global field "${uid}": ${reason}`, SKIPPED_GLOBAL_FIELD_NO_SCHEMA: (uid, reason) => `Skipped global field "${uid}": ${reason}. Did you forget to include it?`, SKIPPED_REFERENCE: (reference, reason) => `Skipped reference to content type "${reference}": ${reason}`, // Summary Messages SUMMARY_HEADER: "Summary of Skipped Items:", TOTAL_SKIPPED_ITEMS: (count) => `Total skipped items: ${count}`, GENERATION_COMPLETED_PARTIAL: "Generation completed successfully with partial schema.", // GraphQL Errors GRAPHQL_API_UNAVAILABLE: (region) => `GraphQL content delivery api unavailable for '${region}' region and no custom host provided` }; // src/constants/index.ts var TOKEN_TYPE = { DELIVERY: "delivery" }; var REGIONS = { US: "US", EU: "EU", AU: "AU", AWS_NA: "AWS-NA", AWS_EU: "AWS-EU", AWS_AU: "AWS-AU", AZURE_NA: "AZURE_NA", AZURE_EU: "AZURE_EU", GCP_NA: "GCP_NA", GCP_EU: "GCP_EU", CUSTOM: "CUSTOM" }; // src/sdk/utils.ts var import_delivery_sdk = __toESM(require("@contentstack/delivery-sdk")); var initializeContentstackSdk = ({ apiKey, token, environment, region, branch, host }) => { try { let isCustomRegion = false; const regionVal = (function(regionValue) { switch (regionValue) { case REGIONS.US: return import_delivery_sdk.Region.US; case REGIONS.EU: return import_delivery_sdk.Region.EU; case REGIONS.AU: return import_delivery_sdk.Region.AU; case REGIONS.AWS_NA: return import_delivery_sdk.Region.US; case REGIONS.AWS_EU: return import_delivery_sdk.Region.EU; case REGIONS.AWS_AU: return import_delivery_sdk.Region.AU; case REGIONS.AZURE_NA: return import_delivery_sdk.Region.AZURE_NA; case REGIONS.AZURE_EU: return import_delivery_sdk.Region.AZURE_EU; case REGIONS.GCP_NA: return import_delivery_sdk.Region.GCP_NA; case REGIONS.GCP_EU: return import_delivery_sdk.Region.GCP_EU; default: isCustomRegion = true; break; } })(region); let Stack; if (isCustomRegion && host) { Stack = import_delivery_sdk.default.stack({ apiKey, deliveryToken: token, environment, host, branch }); } else if (regionVal) { Stack = import_delivery_sdk.default.stack({ apiKey, deliveryToken: token, environment, region: regionVal, branch }); } else { throw { type: "validation", error_message: `The region "${region}" is not supported and no host is provided for a custom region.` }; } return Stack; } catch (err) { throw { type: "validation", error_message: "Something went wrong while initializing Contentstack SDK." }; } }; // src/generateTS/docgen/jsdoc.ts var JSDocumentationGenerator = class { interface(description) { return description ? this.block(description) : null; } field(description) { return description ? this.block(description) : null; } versionComment() { return "/** Version */"; } block(description) { return ["/**", description, "*/"].join(" "); } }; // src/generateTS/docgen/nulldoc.ts var NullDocumentationGenerator = class { interface(__description) { return null; } field(__description) { return null; } versionComment() { return null; } }; // src/generateTS/factory.ts var _ = __toESM(require("lodash")); // src/generateTS/shared/cslp-helpers.ts var CSLP_HELPERS = { INTERFACE_DEFINITION: `export interface CSLPAttribute { "data-cslp"?: string; "data-cslp-parent-field"?: string; } export type CSLPFieldMapping = CSLPAttribute | string;`, createFieldMapping: (fieldUid) => `${JSON.stringify(fieldUid)}?: CSLPFieldMapping`, createMappingBlock: (dollarKeys) => `$?: { ${dollarKeys.join(";\n ")}; };` }; // src/generateTS/shared/utils.ts function isNumericIdentifier(identifier) { return /^\d/.test(identifier); } function createSkippedItemRecord(uid, path, reason) { return { uid, path, reason }; } var NUMERIC_IDENTIFIER_EXCLUSION_REASON = "TypeScript constraint: object keys cannot start with numbers"; function checkNumericIdentifierExclusion(uid, path) { if (isNumericIdentifier(uid)) { return { shouldExclude: true, record: createSkippedItemRecord( uid, path, NUMERIC_IDENTIFIER_EXCLUSION_REASON ) }; } return { shouldExclude: false }; } function createValidationError(errorMessage) { return { type: "validation", error_message: errorMessage }; } function createErrorDetails(err, context = "generateTSFromContentTypes") { if (err.type === "validation") { return { error_message: err.error_message || "Validation error occurred", // Keep for backwards compatibility error_code: err.error_code || "VALIDATION_ERROR", // New property details: err.details || {} }; } else { const errorMessage = err.message || "Unknown error occurred"; return { error_message: `Type generation failed: ${errorMessage}`, // Keep for backwards compatibility error_code: "TYPE_GENERATION_FAILED", // New property details: {} }; } } function formatErrorDetails(error, index, skipHeader = false) { if (skipHeader) { return `TypeScript constraint: Object keys cannot start with a number. Suggestion: Since UIDs cannot be changed, use the --prefix flag to add a valid prefix to all interface names (e.g., --prefix "ContentType"). `; } return `${index}. UID: "${error.uid}" TypeScript constraint: Object keys cannot start with a number. Suggestion: Since UIDs cannot be changed, use the --prefix flag to add a valid prefix to all interface names (e.g., --prefix "ContentType"). `; } function buildErrorHeader(totalErrors) { return `Type generation failed: ${totalErrors} items use numeric identifiers, which result in invalid TypeScript interface names. Use the --prefix flag to resolve this issue. `; } function buildContentTypeErrorsSection(contentTypeErrors) { if (contentTypeErrors.length === 0) return ""; let section = "Content Types and Global Fields with Numeric UIDs\n"; section += "Note: Global Fields are also Content Types. If their UID begins with a number, they are listed here.\n\n"; contentTypeErrors.forEach((error, index) => { section += formatErrorDetails(error, index + 1); }); return section; } function buildGlobalFieldErrorsSection(globalFieldErrors) { if (globalFieldErrors.length === 0) return ""; let section = "Global Fields Referencing Invalid Content Types:\n\n"; globalFieldErrors.forEach((error, index) => { section += `${index + 1}. Global Field: "${error.uid}" `; section += ` References: "${error.referenceTo || "Unknown"}" `; section += formatErrorDetails(error, index + 1, true); }); return section; } function buildResolutionInstructionsSection() { return 'To resolve these issues:\n\u2022 Use the --prefix flag to add a valid prefix to all interface names.\n\u2022 Example: --prefix "ContentType"\n'; } function buildNumericIdentifierErrorDetails(errors) { const contentTypeErrors = errors.filter((err) => err.type === "content_type"); const globalFieldErrors = errors.filter((err) => err.type === "global_field"); let errorDetails = buildErrorHeader(errors.length); errorDetails += buildContentTypeErrorsSection(contentTypeErrors); errorDetails += buildGlobalFieldErrorsSection(globalFieldErrors); errorDetails += buildResolutionInstructionsSection(); return errorDetails; } function throwNumericIdentifierValidationError(errors) { const errorDetails = buildNumericIdentifierErrorDetails(errors); throw { type: "validation", error_code: "VALIDATION_ERROR", error_message: errorDetails }; } // src/generateTS/factory.ts function composePrefixedInterfaceName(uid, prefix) { const trimmed = prefix.trim(); return trimmed + _.upperFirst(_.camelCase(uid)); } var defaultOptions = { docgen: new NullDocumentationGenerator(), naming: { prefix: "" }, systemFields: false, isEditableTags: false, includeReferencedEntry: false }; function factory_default(userOptions) { var _a; const options = Object.assign({}, defaultOptions, userOptions); const logger = options.logger; const visitedJSTypes = /* @__PURE__ */ new Set(); const visitedCSTypes = /* @__PURE__ */ new Set(); const visitedGlobalFields = /* @__PURE__ */ new Set(); const visitedContentTypes = /* @__PURE__ */ new Set(); const cachedGlobalFields = {}; const cachedModularBlocks = {}; const modularBlockInterfaces = /* @__PURE__ */ new Set(); const uniqueBlockInterfaces = /* @__PURE__ */ new Set(); const blockInterfacesKeyToName = {}; let counter = 1; const skippedFields = []; const skippedBlocks = []; const trimmedNamingPrefix = typeof ((_a = options.naming) == null ? void 0 : _a.prefix) === "string" ? options.naming.prefix.trim() : ""; const numericIdentifierErrors = []; const typeMap = { text: { func: type_text, track: true, flag: 1 /* BuiltinJS */ }, number: { func: type_number, track: true, flag: 1 /* BuiltinJS */ }, isodate: { func: type_text, track: true, flag: 1 /* BuiltinJS */ }, boolean: { func: type_boolean, track: true, flag: 1 /* BuiltinJS */ }, blocks: { func: type_modular_blocks, track: false, flag: 8 /* UserBlock */ }, global_field: { func: type_global_field, track: true, flag: 4 /* UserGlobalField */ }, group: { func: type_group, track: false, flag: 16 /* UserGroup */ }, link: { func: type_link, track: true, flag: 2 /* BuiltinCS */ }, file: { func: type_file, track: true, flag: 2 /* BuiltinCS */ }, reference: { func: type_reference, track: true, flag: 32 /* UserReference */ }, taxonomy: { func: type_taxonomy, track: true, flag: 2 /* BuiltinCS */ } }; function track_dependency(field, type, flag) { if (flag === 1 /* BuiltinJS */) { visitedJSTypes.add(type); } else if (flag === 4 /* UserGlobalField */) { const _type = name_type(field.reference_to); visitedGlobalFields.add(_type); if (!cachedGlobalFields[_type]) { cachedGlobalFields[_type] = { definition: visit_content_type(field) }; } } else if (flag === 2 /* BuiltinCS */) { visitedCSTypes.add(type); } else if (flag === 32 /* UserReference */) { if (Array.isArray(field.reference_to)) { field.reference_to.forEach((v) => { visitedContentTypes.add(name_type(v)); }); } } } function name_type(uid) { if (trimmedNamingPrefix) { return composePrefixedInterfaceName(uid, trimmedNamingPrefix); } if (isNumericIdentifier(uid)) { return `InvalidInterface_${uid}`; } return composePrefixedInterfaceName(uid, ""); } function define_interface(contentType, systemFields = false) { const isGlobalField = contentType.data_type === "global_field"; const nameSourceUid = isGlobalField && contentType.reference_to ? contentType.reference_to : contentType.uid; let interfaceName; if (!trimmedNamingPrefix && isNumericIdentifier(nameSourceUid)) { if (isGlobalField && contentType.reference_to) { numericIdentifierErrors.push({ uid: contentType.uid, type: "global_field", referenceTo: contentType.reference_to }); } else { numericIdentifierErrors.push({ uid: contentType.uid, type: "content_type" }); } interfaceName = `InvalidInterface_${nameSourceUid}`; } else { interfaceName = name_type(nameSourceUid); } const interface_declaration = ["export interface", interfaceName]; if (systemFields && contentType.schema_type !== "global_field") { interface_declaration.push("extends", name_type("SystemFields")); } return interface_declaration.join(" "); } function op_array(type, field) { let op = ""; if (field.multiple) { op = "[]"; if (field.max_instance) { return ["MaxTuple<", type, ", ", field.max_instance, ">"].join(""); } } return type + op; } function op_required(required) { return required ? "" : "?"; } function op_paren(block) { return `(${block})`; } function visit_field_choices(field) { const choices = field.enum.choices; const length = choices.length; if (!choices && !length) return ""; function get_value(choice) { if (field.data_type === "number") { return choice.value; } return `${JSON.stringify(choice.value)}`; } return op_paren(choices.map((v) => get_value(v)).join(" | ")); } function visit_field_type(field) { let type = "any"; if (field.enum) { type = visit_field_choices(field); } else { const match = typeMap[field.data_type]; if (match) { type = match.func(field); if (match.track) { track_dependency(field, type, match.flag); } } else { const reason = `Unknown field type: ${field.data_type}`; skippedFields.push({ uid: field.uid, path: field.uid, reason }); logger == null ? void 0 : logger.warn( ERROR_MESSAGES.SKIPPED_FIELD_UNKNOWN_TYPE( field.uid, field.data_type, reason ) ); type = "Record<string, unknown>"; } } return op_array(type, field); } const handleGlobalField = (field) => { if (!trimmedNamingPrefix && isNumericIdentifier(field.reference_to)) { const exclusionCheck = checkNumericIdentifierExclusion( field.reference_to, field.uid ); skippedFields.push(exclusionCheck.record); logger == null ? void 0 : logger.warn( ERROR_MESSAGES.SKIPPED_GLOBAL_FIELD_REFERENCE( field.uid, field.reference_to, NUMERIC_IDENTIFIER_EXCLUSION_REASON ) ); return "string"; } const referenceName = name_type(field.reference_to); return `${referenceName}${field.multiple ? "[]" : ""}`; }; function visit_field(field) { let fieldType = ""; if (field.data_type === "global_field") { fieldType = handleGlobalField(field); } else if (field.data_type === "blocks") { fieldType = type_modular_blocks(field); } else if (field.data_type === "json") { fieldType = type_json_rte(field); } else { fieldType = visit_field_type(field); } const requiredFlag = field.data_type === "boolean" ? "" : op_required(field.mandatory); const typeModifier = ["isodate", "file", "number"].includes(field.data_type) || ["radio", "dropdown"].includes(field.display_type) ? field.mandatory ? "" : " | null" : ""; return `${field.uid}${requiredFlag}: ${fieldType}${typeModifier};`; } function visit_fields(schema, path = "") { const fieldLines = []; const dollarKeys = []; for (const field of schema) { const fieldPath = path ? `${path}.${field.uid}` : field.uid; const exclusionCheck = checkNumericIdentifierExclusion( field.uid, fieldPath ); if (exclusionCheck.shouldExclude) { skippedFields.push(exclusionCheck.record); logger == null ? void 0 : logger.warn( ERROR_MESSAGES.SKIPPED_FIELD_AT_PATH( field.uid, fieldPath, NUMERIC_IDENTIFIER_EXCLUSION_REASON ) ); continue; } const line = [ options.docgen.field(field.display_name), visit_field(field) ].filter((v) => v).join("\n"); fieldLines.push(line); dollarKeys.push(CSLP_HELPERS.createFieldMapping(field.uid)); } if (options.isEditableTags) { const fieldComment = options.docgen.field( "CSLP mapping for editable fields" ); const lines = fieldComment ? [fieldComment, CSLP_HELPERS.createMappingBlock(dollarKeys)] : [CSLP_HELPERS.createMappingBlock(dollarKeys)]; fieldLines.push(...lines); } return fieldLines.join("\n"); } function visit_content_type(contentType) { modularBlockInterfaces.clear(); const contentTypeInterface = [ options.docgen.interface(contentType.description), define_interface(contentType, options.systemFields), "{", options.docgen.versionComment(), `_version?: number;`, visit_fields(contentType.schema), "}" ].filter((v) => v).join("\n"); return [...modularBlockInterfaces, contentTypeInterface].join("\n\n"); } function type_modular_blocks(field) { let modularBlockInterfaceName = name_type(field.uid); const modularBlockDefinitions = field.blocks.map((block) => { const blockPath = `${field.uid}.blocks.${block.uid}`; const exclusionCheck = checkNumericIdentifierExclusion( block.uid, blockPath ); if (exclusionCheck.shouldExclude) { skippedBlocks.push(exclusionCheck.record); logger == null ? void 0 : logger.warn( ERROR_MESSAGES.SKIPPED_BLOCK_AT_PATH( block.uid, blockPath, NUMERIC_IDENTIFIER_EXCLUSION_REASON ) ); return null; } const blockFieldType = block.reference_to ? name_type(block.reference_to) : visit_fields( block.schema || [], `${field.uid}.blocks.${block.uid}` ); const blockSchemaDefinition = block.reference_to ? `${blockFieldType};` : `{ ${blockFieldType} }`; return `${block.uid}: ${blockSchemaDefinition}`; }).filter(Boolean); if (modularBlockDefinitions.length === 0) { if (options.systemFields) { const modularBlocksType = `${trimmedNamingPrefix}ModularBlocksExtension`; return field.multiple ? `${modularBlocksType}<Record<string, unknown>>[]` : `${modularBlocksType}<Record<string, unknown>>`; } return field.multiple ? "Record<string, unknown>[]" : "Record<string, unknown>"; } const modularBlockSignature = JSON.stringify(modularBlockDefinitions); if (uniqueBlockInterfaces.has(modularBlockSignature)) { const existingInterfaceName = blockInterfacesKeyToName[modularBlockSignature]; if (existingInterfaceName) { if (options.systemFields) { const modularBlocksType = `${trimmedNamingPrefix}ModularBlocksExtension`; return field.multiple ? `${modularBlocksType}<${existingInterfaceName}>[]` : `${modularBlocksType}<${existingInterfaceName}>`; } return field.multiple ? `${existingInterfaceName}[]` : existingInterfaceName; } } uniqueBlockInterfaces.add(modularBlockSignature); while (cachedModularBlocks[modularBlockInterfaceName]) { modularBlockInterfaceName = `${modularBlockInterfaceName}${counter}`; counter++; } const modularBlockInterfaceDefinition = [ `export interface ${modularBlockInterfaceName}${options.systemFields ? ` extends ${trimmedNamingPrefix}SystemFields` : ""} {`, modularBlockDefinitions.join("\n"), "}" ].join("\n"); modularBlockInterfaces.add(modularBlockInterfaceDefinition); cachedModularBlocks[modularBlockInterfaceName] = modularBlockSignature; blockInterfacesKeyToName[modularBlockSignature] = modularBlockInterfaceName; if (options.systemFields) { const modularBlocksType = `${trimmedNamingPrefix}ModularBlocksExtension`; return field.multiple ? `${modularBlocksType}<${modularBlockInterfaceName}>[]` : `${modularBlocksType}<${modularBlockInterfaceName}>`; } return field.multiple ? `${modularBlockInterfaceName}[]` : modularBlockInterfaceName; } function type_group(field) { return ["{", visit_fields(field.schema, field.uid), "}"].filter((v) => v).join("\n"); } function type_text() { return "string"; } function type_number() { return "number"; } function type_boolean() { return "boolean"; } function type_link() { return `${trimmedNamingPrefix}Link`; } function type_file(field) { if (field.uid === "parent_uid") { return "string | null"; } return `${trimmedNamingPrefix}File`; } function type_global_field(field) { const exclusionCheck = checkNumericIdentifierExclusion( field.uid, field.uid ); if (exclusionCheck.shouldExclude) { skippedFields.push(exclusionCheck.record); logger == null ? void 0 : logger.warn( ERROR_MESSAGES.SKIPPED_GLOBAL_FIELD( field.uid, NUMERIC_IDENTIFIER_EXCLUSION_REASON ) ); return "string"; } if (!field.schema) { const reason = "Schema not found for global field"; skippedFields.push({ uid: field.uid, path: field.uid, reason }); logger == null ? void 0 : logger.warn( ERROR_MESSAGES.SKIPPED_GLOBAL_FIELD_NO_SCHEMA(field.uid, reason) ); return "string"; } return name_type(field.reference_to); } function buildReferenceArrayType(references, options2) { if (references.length === 0) { return "Record<string, unknown>[]"; } if (options2.includeReferencedEntry) { const referencedEntryType = `${trimmedNamingPrefix}ReferencedEntry`; const baseUnion2 = references.join(" | "); const types = `(${baseUnion2} | ${referencedEntryType})`; return `${types}[]`; } const baseUnion = references.join(" | "); return `(${baseUnion})[]`; } function type_reference(field) { const references = []; if (Array.isArray(field.reference_to)) { field.reference_to.forEach((v) => { if (trimmedNamingPrefix || !isNumericIdentifier(v)) { references.push(name_type(v)); } else { logger == null ? void 0 : logger.warn( ERROR_MESSAGES.SKIPPED_REFERENCE( v, NUMERIC_IDENTIFIER_EXCLUSION_REASON ) ); } }); } else { const v = field.reference_to; if (trimmedNamingPrefix || !isNumericIdentifier(v)) { references.push(name_type(v)); } else { logger == null ? void 0 : logger.warn( ERROR_MESSAGES.SKIPPED_REFERENCE( v, NUMERIC_IDENTIFIER_EXCLUSION_REASON ) ); } } return buildReferenceArrayType(references, options); } return function(contentType) { if (contentType.schema_type === "global_field") { const name = name_type(contentType.uid); if (!cachedGlobalFields[name]) { cachedGlobalFields[name] = { definition: visit_content_type(contentType) }; } return { definition: cachedGlobalFields[name].definition, isGlobalField: true }; } const definition = visit_content_type(contentType); if (numericIdentifierErrors.length > 0) { throwNumericIdentifierValidationError(numericIdentifierErrors); } if (logger && (skippedFields.length > 0 || skippedBlocks.length > 0)) { logger.info(""); logger.info(ERROR_MESSAGES.SUMMARY_HEADER); const allSkippedItems = [ ...skippedFields.map((field) => ({ Type: "Field", "Key Name": field.uid, "Schema Path": field.path, Reason: field.reason })), ...skippedBlocks.map((block) => ({ Type: "Block", "Key Name": block.uid, "Schema Path": block.path, Reason: block.reason })) ]; if (logger.table) { logger.table( [ { value: "Type" }, { value: "Key Name" }, { value: "Schema Path" }, { value: "Reason" } ], allSkippedItems ); } const totalSkipped = skippedFields.length + skippedBlocks.length; logger.info(""); logger.warn(ERROR_MESSAGES.TOTAL_SKIPPED_ITEMS(totalSkipped)); logger.success(ERROR_MESSAGES.GENERATION_COMPLETED_PARTIAL); } return { definition, metadata: { name: name_type(contentType.uid), types: { javascript: visitedJSTypes, contentstack: visitedCSTypes, globalFields: visitedGlobalFields }, dependencies: { globalFields: cachedGlobalFields, contentTypes: visitedContentTypes }, skippedFields: { fields: [...skippedFields], // Create a copy to avoid reference issues blocks: [...skippedBlocks] // Create a copy to avoid reference issues } } }; }; function type_taxonomy() { return `${trimmedNamingPrefix}Taxonomy | ${trimmedNamingPrefix}TaxonomyEntry`; } function type_json_rte(field) { var _a2; let json_rte; if (field.config && ((_a2 = field.field_metadata) == null ? void 0 : _a2.extension)) { json_rte = `unknown`; } else { json_rte = `{ type: string; uid: string; _version: number; attrs: Record<string, any>; children: JSONRTENode[]; }`; } return json_rte; } } // src/generateTS/stack/builtins.ts var defaultInterfaces = (prefix = "", systemFields = false, isEditableTags = false, hasJsonRte, includeReferencedEntry = false) => { const defaultInterfaces2 = [ `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>>` ]; if (includeReferencedEntry) { const extendsClause = systemFields ? ` extends ${prefix}SystemFields` : ""; defaultInterfaces2.push( `export interface ${prefix}ReferencedEntry${extendsClause} { uid: string; _content_type_uid: string; }` ); } defaultInterfaces2.push( `export interface ${prefix}PublishDetails { environment: string; locale: string; time: string; user: string; }` ); defaultInterfaces2.push( `export interface ${prefix}File { uid: string; created_at: string; updated_at: string; created_by: string; updated_by: string; content_type: string; file_size: string; tags: string[]; filename: string; url: string; ACL: any[] | object; is_dir: boolean; parent_uid: string; _version: number; title: string; _metadata?:object; description?:string; dimension?: { height: number; width: number; } publish_details: ${prefix}PublishDetails; }` ); defaultInterfaces2.push( `export interface ${prefix}Link { title: string; href: string; }` ); defaultInterfaces2.push( `export interface ${prefix}Taxonomy { taxonomy_uid: string; max_terms?: number; mandatory: boolean; non_localizable: boolean; }` ); defaultInterfaces2.push( `export type ${prefix}TaxonomyEntry = ${prefix}Taxonomy & { term_uid: string }` ); if (hasJsonRte) { defaultInterfaces2.push( `export interface JSONRTENode { type: string; uid: string; _version: number; attrs: Record<string, any>; children?: JSONRTENode[]; text?: string; bold?: boolean; italic?: boolean; underline?: boolean; src?: string; alt?: string; href?: string; target?: string; embed?: { type: string; uid: string; _version: number; attrs: Record<string, any>; }; };` ); } if (isEditableTags) { defaultInterfaces2.push(CSLP_HELPERS.INTERFACE_DEFINITION); } if (systemFields) { defaultInterfaces2.push( `export interface ${prefix}SystemFields { uid?: string; created_at?: string; updated_at?: string; created_by?: string; updated_by?: string; _content_type_uid?: string; tags?: string[]; ACL?: any[]; _version?: number; _in_progress?: boolean; locale?: string; publish_details?: ${prefix}PublishDetails; title?: string; }` ); defaultInterfaces2.push( `export type ${prefix}ModularBlocksExtension<T> = { [P in keyof T]?: T[P] & { _metadata?: { uid?: string } }; }` ); return defaultInterfaces2; } else { return defaultInterfaces2; } }; // src/format/index.ts var prettier = __toESM(require("prettier")); var tsParser = __toESM(require("prettier/plugins/typescript")); var estreeParser = __toESM(require("prettier/plugins/estree")); async function format2(definition) { const formatted = await prettier.format(definition, { parser: "typescript", plugins: [tsParser, estreeParser] }); return formatted; } // src/logger/index.ts var BasicLogger = class { constructor() { this.colorMap = { red: "\x1B[31m", green: "\x1B[32m", yellow: "\x1B[33m", blue: "\x1B[34m", magenta: "\x1B[35m", cyan: "\x1B[36m", white: "\x1B[37m", gray: "\x1B[90m", reset: "\x1B[0m", bold: "\x1B[1m" }; } print(message, options = {}) { let formattedMessage = message; if (options.color && this.colorMap[options.color]) { formattedMessage = `${this.colorMap[options.color]}${formattedMessage}${this.colorMap.reset}`; } if (options.bold) { formattedMessage = `${this.colorMap.bold}${formattedMessage}${this.colorMap.reset}`; } console.log(formattedMessage); } success(message) { this.print(message, { color: "green" }); } info(message) { if (message === "") { console.log(); } else { console.log(message); } } warn(message) { this.print(message, { color: "yellow" }); } error(message) { this.print(message, { color: "red" }); } table(headers, data) { if (data.length === 0) return; if (console.table) { console.table(data); } else { const columnWidths = {}; headers.forEach((header) => { columnWidths[header.value] = Math.max( header.value.length, ...data.map((row) => String(row[header.value] || "").length) ); }); const headerRow = headers.map((header) => header.value.padEnd(columnWidths[header.value])).join(" | "); this.print(headerRow, { bold: true }); this.print( headers.map((header) => "-".repeat(columnWidths[header.value])).join("-|-") ); data.forEach((row) => { const dataRow = headers.map( (header) => String(row[header.value] || "").padEnd(columnWidths[header.value]) ).join(" | "); this.print(dataRow); }); } } }; function createLogger(externalLogger) { if (externalLogger) { if (!externalLogger.table) { externalLogger.table = (headers, data) => { console.table(data); }; } return externalLogger; } return new BasicLogger(); } // src/generateTS/index.ts var generateTS = async ({ token, tokenType, apiKey, environment, region, branch, prefix, includeDocumentation, systemFields, isEditableTags, includeReferencedEntry, host, logger: loggerInstance }) => { var _a, _b; const logger = createLogger(loggerInstance); try { if (!token || !tokenType || !apiKey || !environment || !region) { throw createValidationError( "Please provide all the required params (token, tokenType, apiKey, environment, region)" ); } if (tokenType === TOKEN_TYPE.DELIVERY) { const Stack = initializeContentstackSdk({ apiKey, token, environment, region, branch, host }); const contentTypeQuery = Stack.contentType(); contentTypeQuery._queryParams["include_count"] = "true"; const globalFieldsQuery = Stack.globalField(); const contentTypes = await getContentTypes(contentTypeQuery); const globalFields = await globalFieldsQuery.find(); const { content_types } = contentTypes; if (!content_types.length) { logger.error(ERROR_MESSAGES.NO_CONTENT_TYPES); logger.warn(ERROR_MESSAGES.CREATE_CONTENT_MODELS); throw createValidationError(ERROR_MESSAGES.NO_CONTENT_TYPES_DETAILED); } let schemas = []; if (content_types == null ? void 0 : content_types.length) { if ((_a = globalFields == null ? void 0 : globalFields.global_fields) == null ? void 0 : _a.length) { schemas = schemas.concat( globalFields.global_fields ); schemas = schemas.map((schema) => __spreadProps(__spreadValues({}, schema), { schema_type: "global_field" })); } schemas = schemas.concat(content_types); const generatedTS = generateTSFromContentTypes({ contentTypes: schemas, prefix, includeDocumentation, systemFields, isEditableTags, includeReferencedEntry }); return generatedTS; } } } catch (error) { if (error.type === "validation") { throw { error_message: error.error_message, error_code: error.error_code || "VALIDATION_ERROR" }; } else { const errorObj = JSON.parse((_b = error == null ? void 0 : error.message) == null ? void 0 : _b.replace("Error: ", "")); let errorMessage = ERROR_MESSAGES.API_ERROR_DEFAULT; let errorCode = "API_ERROR"; if (errorObj.status) { switch (errorObj.status) { case 401: errorMessage = ERROR_MESSAGES.UNAUTHORIZED; errorCode = "AUTHENTICATION_FAILED"; break; case 412: errorMessage = ERROR_MESSAGES.INVALID_CREDENTIALS; errorCode = "INVALID_CREDENTIALS"; break; default: errorMessage = ERROR_MESSAGES.API_ERROR_WITH_STATUS(errorObj.status, errorObj.error_message); errorCode = `API_ERROR_${errorObj.status}`; } } if (errorObj.error_message && !errorObj.status) { errorMessage = `${errorMessage}, ${errorObj.error_message}`; } throw { error_message: errorMessage, error_code: errorCode }; } } }; var generateTSFromContentTypes = async ({ contentTypes, prefix = "", includeDocumentation = true, systemFields = false, isEditableTags = false, includeReferencedEntry = false, logger: loggerInstance }) => { const logger = createLogger(loggerInstance); try { const docgen = includeDocumentation ? new JSDocumentationGenerator() : new NullDocumentationGenerator(); const globalFields = /* @__PURE__ */ new Set(); const definitions = []; const tsgen = factory_default({ docgen, naming: { prefix }, systemFields, isEditableTags, includeReferencedEntry, logger }); for (const contentType of contentTypes) { const tsgenResult = tsgen(contentType); if (tsgenResult.isGlobalField) { globalFields.add(tsgenResult.definition); } else { definitions.push(tsgenResult.definition); tsgenResult.metadata.types.globalFields.forEach((field) => { globalFields.add( tsgenResult.metadata.dependencies.globalFields[field].definition ); }); } } const hasJsonField = contentTypes.some( (contentType) => checkJsonField(contentType.schema) ); const output = await format2( [ defaultInterfaces( prefix, systemFields, isEditableTags, hasJsonField, includeReferencedEntry ).join("\n\n"), [...globalFields].join("\n\n"), definitions.join("\n\n") ].join("\n\n") ); return output; } catch (err) { if (err.type === "validation" && err.error_code === "VALIDATION_ERROR" && err.error_message && err.error_message.includes("numeric identifiers")) { throw err; } const errorDetails = createErrorDetails(err, "generateTSFromContentTypes"); throw errorDetails; } }; var getContentTypes = async (contentTypeQuery) => { try { const limit = 100; const results = await contentTypeQuery.find(); if ((results == null ? void 0 : results.count) > limit) { const additionalQueries = Array.from( { length: Math.ceil(results.count / limit) - 1 }, (_2, i) => { return import_async.default.reflect(async () => { contentTypeQuery._queryParams["skip"] = (i + 1) * limit; contentTypeQuery._queryParams["limit"] = limit; return contentTypeQuery.find(); }); } ); const additionalResults = await import_async.default.parallel(additionalQueries); const flattenedResult = additionalResults.flatMap( (res) => { var _a; return (_a = res == null ? void 0 : res.value) == null ? void 0 : _a.content_types; } ); results.content_types = (0, import_lodash.flatten)([flattenedResult, results.content_types]); } return results; } catch (error) { throw error; } }; var checkJsonField = (schema) => { return schema.some((field) => { var _a; if (field.data_type === "json" && ((_a = field.field_metadata) == null ? void 0 : _a.allow_json_rte)) { return true; } if (field.data_type === "group" && Array.isArray(field.schema)) { return checkJsonField(field.schema); } if (field.data_type === "blocks" && Array.isArray(field.blocks)) { return field.blocks.some( (block) => checkJsonField(block.schema || []) ); } return false; }); }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { generateTS, generateTSFromContentTypes });