prisma-zod-generator
Version:
Prisma 2+ generator to emit Zod schemas from your Prisma schema
359 lines • 12.9 kB
JavaScript
;
/**
* Variant Naming Convention System
* Provides utilities for generating consistent file names and schema names for different variants
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.NamingUtils = exports.VariantNamingSystem = exports.CollisionStrategy = void 0;
const variants_1 = require("../types/variants");
/**
* Name collision resolution strategies
*/
var CollisionStrategy;
(function (CollisionStrategy) {
CollisionStrategy["SUFFIX_INCREMENT"] = "suffix_increment";
CollisionStrategy["PREFIX_VARIANT"] = "prefix_variant";
CollisionStrategy["THROW_ERROR"] = "throw_error";
})(CollisionStrategy || (exports.CollisionStrategy = CollisionStrategy = {}));
/**
* Utility class for variant naming conventions
*/
class VariantNamingSystem {
constructor(globalOptions = {}, collisionConfig = {
strategy: CollisionStrategy.SUFFIX_INCREMENT,
maxAttempts: 10,
}) {
this.usedFileNames = new Set();
this.usedSchemaNames = new Set();
this.globalOptions = {
useModelPrefix: true,
usePascalCase: true,
customSeparator: '.',
baseDirectory: './generated/schemas',
...globalOptions,
};
this.collisionConfig = collisionConfig;
}
/**
* Generate complete naming for a model variant
*/
generateNaming(modelName, variantType, customConfig) {
const config = this.mergeNamingConfig(variantType, customConfig);
const normalizedModelName = this.normalizeModelName(modelName);
// Generate base names
const baseFileName = this.generateFileName(normalizedModelName, config);
const baseSchemaName = this.generateSchemaName(normalizedModelName, config);
const baseTypeName = this.generateTypeName(normalizedModelName, config);
// Resolve collisions
const resolvedNames = this.resolveNamingCollisions({
fileName: baseFileName,
schemaName: baseSchemaName,
typeName: baseTypeName,
});
// Build full paths
const directoryPath = this.buildDirectoryPath(variantType);
const filePath = this.buildFilePath(directoryPath, resolvedNames.fileName);
// Track used names
this.usedFileNames.add(resolvedNames.fileName);
this.usedSchemaNames.add(resolvedNames.schemaName);
return {
fileName: resolvedNames.fileName,
filePath,
directoryPath,
schemaName: resolvedNames.schemaName,
typeName: resolvedNames.typeName,
schemaExportName: resolvedNames.schemaName,
typeExportName: resolvedNames.typeName,
variantType,
modelName,
isCollisionResolved: resolvedNames.isCollisionResolved,
originalNames: resolvedNames.isCollisionResolved
? {
fileName: baseFileName,
schemaName: baseSchemaName,
typeName: baseTypeName,
}
: undefined,
};
}
/**
* Generate file name for a variant
*/
generateFileName(modelName, config) {
const baseName = this.globalOptions.usePascalCase
? this.toPascalCase(modelName)
: this.toCamelCase(modelName);
return `${baseName}${config.suffix}`;
}
/**
* Generate schema name for a variant
*/
generateSchemaName(modelName, config) {
const baseName = this.globalOptions.usePascalCase
? this.toPascalCase(modelName)
: this.toCamelCase(modelName);
return `${baseName}${config.schemaNameSuffix}`;
}
/**
* Generate type name for a variant
*/
generateTypeName(modelName, config) {
const baseName = this.globalOptions.usePascalCase
? this.toPascalCase(modelName)
: this.toCamelCase(modelName);
return `${baseName}${config.typeNameSuffix}`;
}
/**
* Build directory path for variant
*/
buildDirectoryPath(variantType) {
const baseDir = this.globalOptions.baseDirectory || './generated/schemas';
const separator = this.globalOptions.customSeparator || '/';
return `${baseDir}${separator}${variantType}`;
}
/**
* Build full file path
*/
buildFilePath(directoryPath, fileName) {
const separator = this.globalOptions.customSeparator === '.' ? '/' : this.globalOptions.customSeparator || '/';
return `${directoryPath}${separator}${fileName}`;
}
/**
* Resolve naming collisions
*/
resolveNamingCollisions(names) {
let { fileName, schemaName, typeName } = names;
let isCollisionResolved = false;
let attempts = 0;
// Check for file name collisions
while (this.usedFileNames.has(fileName) &&
attempts < (this.collisionConfig.maxAttempts || 10)) {
fileName = this.resolveFileNameCollision(names.fileName, attempts + 1);
isCollisionResolved = true;
attempts++;
}
// Check for schema name collisions
attempts = 0;
while (this.usedSchemaNames.has(schemaName) &&
attempts < (this.collisionConfig.maxAttempts || 10)) {
schemaName = this.resolveSchemaNameCollision(names.schemaName, attempts + 1);
isCollisionResolved = true;
attempts++;
}
// Type names typically follow schema names
if (isCollisionResolved) {
typeName = typeName.replace(names.schemaName.replace('Schema', ''), schemaName.replace('Schema', ''));
}
if (attempts >= (this.collisionConfig.maxAttempts || 10)) {
if (this.collisionConfig.strategy === CollisionStrategy.THROW_ERROR) {
throw new Error(`Unable to resolve naming collision after ${attempts} attempts`);
}
}
return {
fileName,
schemaName,
typeName,
isCollisionResolved,
};
}
/**
* Resolve file name collision
*/
resolveFileNameCollision(originalName, attempt) {
const { strategy } = this.collisionConfig;
switch (strategy) {
case CollisionStrategy.SUFFIX_INCREMENT:
const extension = originalName.substring(originalName.lastIndexOf('.'));
const baseName = originalName.substring(0, originalName.lastIndexOf('.'));
return `${baseName}_${attempt}${extension}`;
case CollisionStrategy.PREFIX_VARIANT:
const prefix = this.collisionConfig.customPrefix || 'var';
return `${prefix}_${attempt}_${originalName}`;
default:
return `${originalName}_${attempt}`;
}
}
/**
* Resolve schema name collision
*/
resolveSchemaNameCollision(originalName, attempt) {
const { strategy } = this.collisionConfig;
switch (strategy) {
case CollisionStrategy.SUFFIX_INCREMENT:
return `${originalName}_${attempt}`;
case CollisionStrategy.PREFIX_VARIANT:
const prefix = this.collisionConfig.customPrefix || 'Var';
return `${prefix}${attempt}${originalName}`;
default:
return `${originalName}_${attempt}`;
}
}
/**
* Merge naming configuration with defaults
*/
mergeNamingConfig(variantType, customConfig) {
const defaultConfig = variants_1.DEFAULT_NAMING_CONFIGS[variantType];
return {
...defaultConfig,
...customConfig,
};
}
/**
* Normalize model name for consistent naming
*/
normalizeModelName(modelName) {
// Remove special characters and ensure proper casing
return modelName.replace(/[^a-zA-Z0-9]/g, '').replace(/^\d+/, ''); // Remove leading numbers
}
/**
* Convert string to PascalCase
*/
toPascalCase(str) {
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => word.toUpperCase()).replace(/\s+/g, '');
}
/**
* Convert string to camelCase
*/
toCamelCase(str) {
const pascalCase = this.toPascalCase(str);
return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
}
/**
* Validate naming conventions
*/
validateNaming(naming) {
const errors = [];
const warnings = [];
const suggestions = [];
// Check file name validity
if (!this.isValidFileName(naming.fileName)) {
errors.push(`Invalid file name: ${naming.fileName}`);
}
// Check schema name validity
if (!this.isValidSchemaName(naming.schemaName)) {
errors.push(`Invalid schema name: ${naming.schemaName}`);
}
// Check type name validity
if (!this.isValidTypeName(naming.typeName)) {
errors.push(`Invalid type name: ${naming.typeName}`);
}
// Check for naming consistency
if (!naming.schemaName.includes(naming.modelName) &&
!naming.schemaName.toLowerCase().includes(naming.modelName.toLowerCase())) {
warnings.push(`Schema name '${naming.schemaName}' doesn't clearly reference model '${naming.modelName}'`);
}
// Suggest improvements
if (naming.fileName.length > 50) {
suggestions.push('Consider shorter file names for better readability');
}
if (naming.isCollisionResolved) {
suggestions.push('Review naming conventions to avoid collisions');
}
return {
isValid: errors.length === 0,
errors,
warnings,
suggestions,
};
}
/**
* Check if file name is valid for file system
*/
isValidFileName(fileName) {
// Basic file system compatibility check
const invalidChars = /[<>:"/\\|?*]/;
return (!invalidChars.test(fileName) &&
fileName.length > 0 &&
fileName.length <= 255 &&
fileName.endsWith('.ts'));
}
/**
* Check if schema name follows TypeScript naming conventions
*/
isValidSchemaName(schemaName) {
// Must be valid TypeScript identifier
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
return validIdentifier.test(schemaName) && schemaName.length > 0 && /^[A-Z]/.test(schemaName); // Should start with uppercase
}
/**
* Check if type name follows TypeScript naming conventions
*/
isValidTypeName(typeName) {
// Must be valid TypeScript identifier
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
return validIdentifier.test(typeName) && typeName.length > 0 && /^[A-Z]/.test(typeName); // Should start with uppercase
}
/**
* Clear tracked names (useful for batch operations)
*/
clearTrackedNames() {
this.usedFileNames.clear();
this.usedSchemaNames.clear();
}
/**
* Get all tracked file names
*/
getTrackedFileNames() {
return Array.from(this.usedFileNames);
}
/**
* Get all tracked schema names
*/
getTrackedSchemaNames() {
return Array.from(this.usedSchemaNames);
}
}
exports.VariantNamingSystem = VariantNamingSystem;
/**
* Utility functions for common naming operations
*/
class NamingUtils {
/**
* Generate barrel export file name for variant type
*/
static generateBarrelFileName(variantType) {
return `${variantType}.ts`;
}
/**
* Generate index file name for all variants
*/
static generateIndexFileName() {
return 'index.ts';
}
/**
* Generate variant directory name
*/
static generateVariantDirectoryName(variantType) {
return variantType.toLowerCase();
}
/**
* Extract model name from file name
*/
static extractModelNameFromFileName(fileName, variantType) {
const config = variants_1.DEFAULT_NAMING_CONFIGS[variantType];
const suffix = config.suffix;
if (fileName.endsWith(suffix)) {
return fileName.substring(0, fileName.length - suffix.length);
}
return fileName.replace(/\.ts$/, '');
}
/**
* Check if two names would conflict
*/
static wouldConflict(name1, name2) {
return name1.toLowerCase() === name2.toLowerCase();
}
/**
* Generate safe identifier from string
*/
static toSafeIdentifier(input) {
return input
.replace(/[^a-zA-Z0-9]/g, '_')
.replace(/^(\d)/, '_$1')
.replace(/_+/g, '_')
.replace(/^_|_$/g, '');
}
}
exports.NamingUtils = NamingUtils;
exports.default = VariantNamingSystem;
//# sourceMappingURL=naming.js.map