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
2 changes: 2 additions & 0 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ fileignoreconfig:
checksum: 7ffa82084fd0fc2f5ec9c8e8124cdf1bce08c7f75c5be2ede2eb5cd506812db1
- filename: tests/integration/generateTS/generateTS.test.ts
checksum: 7c1bc7d659ee2f9f52bf644b9e512984f89e0ff6aa4288b6e30b2c899bf80123
- filename: src/generateTS/shared/utils.ts
checksum: da69dab1717422e12f3b3865604667151d46c96bde5faba13ae862c41d856fba
version: "1.0"
49 changes: 2 additions & 47 deletions src/generateTS/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
isNumericIdentifier,
NUMERIC_IDENTIFIER_EXCLUSION_REASON,
checkNumericIdentifierExclusion,
throwUIDValidationError,
throwNumericIdentifierValidationError,
} from "./shared/utils";

export type TSGenOptions = {
Expand Down Expand Up @@ -613,52 +613,7 @@ export default function (userOptions: TSGenOptions) {

// Check for numeric identifier errors and throw them immediately
if (numericIdentifierErrors.length > 0) {
// Group errors by type for better organization
const contentTypeErrors = numericIdentifierErrors.filter(
(err) => err.type === "content_type"
);
const globalFieldErrors = numericIdentifierErrors.filter(
(err) => err.type === "global_field"
);

// Build the detailed error message
let errorDetails = "";
errorDetails += `Type generation failed: ${numericIdentifierErrors.length} items use numeric identifiers, which result in invalid TypeScript interface names. Use the --prefix flag to resolve this issue.\n\n`;

if (contentTypeErrors.length > 0) {
errorDetails += "Content Types and Global Fields with Numeric UIDs\n";
errorDetails +=
"Note: Global Fields are also Content Types. If their UID begins with a number, they are listed here.\n\n";

contentTypeErrors.forEach((error, index) => {
errorDetails += `${index + 1}. UID: "${error.uid}"\n`;
errorDetails += `TypeScript constraint: Object keys cannot start with a number.\n`;
errorDetails += `Suggestion: Since UIDs cannot be changed, use the --prefix flag to add a valid prefix to all interface names (e.g., --prefix "ContentType").\n\n`;
});
}

if (globalFieldErrors.length > 0) {
errorDetails += "Global Fields Referencing Invalid Content Types:\n\n";

globalFieldErrors.forEach((error, index) => {
errorDetails += `${index + 1}. Global Field: "${error.uid}"\n`;
errorDetails += ` References: "${error.referenceTo || "Unknown"}"\n`;
errorDetails += `TypeScript constraint: Object keys cannot start with a number.\n`;
errorDetails += `Suggestion: Since UIDs cannot be changed, use the --prefix flag to add a valid prefix to all interface names (e.g., --prefix "ContentType").\n\n`;
});
}

errorDetails += "To resolve these issues:\n";
errorDetails +=
"• Use the --prefix flag to add a valid prefix to all interface names.\n";
errorDetails += '• Example: --prefix "ContentType"\n';

// Throw a comprehensive error with all the details
throw {
type: "validation",
error_code: "VALIDATION_ERROR",
error_message: errorDetails,
};
throwNumericIdentifierValidationError(numericIdentifierErrors);
}

// Log summary table of skipped fields and blocks
Expand Down
118 changes: 118 additions & 0 deletions src/generateTS/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,121 @@ export function createErrorDetails(
};
}
}

/**
* Helper function to format error details consistently
* @param error - The error object containing uid and other details
* @param index - The index number for the error
* @param skipHeader - Whether to skip the header (for global fields)
* @returns Formatted error details string
*/
export function formatErrorDetails(
error: any,
index: number,
skipHeader: boolean = false
): string {
if (skipHeader) {
// For global fields, skip the header since it's already added above
return `TypeScript constraint: Object keys cannot start with a number.\nSuggestion: Since UIDs cannot be changed, use the --prefix flag to add a valid prefix to all interface names (e.g., --prefix "ContentType").\n\n`;
}

// For content types, include the full header
return `${index}. UID: "${error.uid}"\nTypeScript constraint: Object keys cannot start with a number.\nSuggestion: Since UIDs cannot be changed, use the --prefix flag to add a valid prefix to all interface names (e.g., --prefix "ContentType").\n\n`;
}

/**
* Build the main error header for numeric identifier errors
* @param totalErrors - Total number of errors found
* @returns Error header string
*/
export function buildErrorHeader(totalErrors: number): string {
return `Type generation failed: ${totalErrors} items use numeric identifiers, which result in invalid TypeScript interface names. Use the --prefix flag to resolve this issue.\n\n`;
}

/**
* Build content type errors section
* @param contentTypeErrors - Array of content type errors
* @returns Formatted content type errors section string
*/
export function buildContentTypeErrorsSection(
contentTypeErrors: any[]
): string {
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;
}

/**
* Build global field errors section
* @param globalFieldErrors - Array of global field errors
* @returns Formatted global field errors section string
*/
export function buildGlobalFieldErrorsSection(
globalFieldErrors: any[]
): string {
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}"\n`;
section += ` References: "${error.referenceTo || "Unknown"}"\n`;
section += formatErrorDetails(error, index + 1, true);
});

return section;
}

/**
* Build resolution instructions section
* @returns Resolution instructions string
*/
export function buildResolutionInstructionsSection(): string {
return (
"To resolve these issues:\n" +
"• Use the --prefix flag to add a valid prefix to all interface names.\n" +
'• Example: --prefix "ContentType"\n'
);
}

/**
* Parent method that orchestrates the error building process
* @param errors - Array of numeric identifier errors
* @returns Complete error message string
*/
export function buildNumericIdentifierErrorDetails(errors: any[]): string {
// Group errors by type for better organization
const contentTypeErrors = errors.filter((err) => err.type === "content_type");
const globalFieldErrors = errors.filter((err) => err.type === "global_field");

// Build the complete error message by calling each section builder
let errorDetails = buildErrorHeader(errors.length);
errorDetails += buildContentTypeErrorsSection(contentTypeErrors);
errorDetails += buildGlobalFieldErrorsSection(globalFieldErrors);
errorDetails += buildResolutionInstructionsSection();

return errorDetails;
}

/**
* Create and throw validation error for numeric identifiers
* @param errors - Array of numeric identifier errors
* @throws A validation error object
*/
export function throwNumericIdentifierValidationError(errors: any[]): never {
const errorDetails = buildNumericIdentifierErrorDetails(errors);

throw {
type: "validation",
error_code: "VALIDATION_ERROR",
error_message: errorDetails,
};
}