prisma-zod-generator
Version:
Prisma 2+ generator to emit Zod schemas from your Prisma schema
242 lines • 9.19 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigParseError = void 0;
exports.discoverConfigFile = discoverConfigFile;
exports.validateFileExists = validateFileExists;
exports.readConfigFile = readConfigFile;
exports.parseJsonConfig = parseJsonConfig;
exports.resolveConfigPath = resolveConfigPath;
exports.parseConfigFromFile = parseConfigFromFile;
exports.parseConfigFromDiscovery = parseConfigFromDiscovery;
exports.parseConfiguration = parseConfiguration;
exports.createConfigErrorMessage = createConfigErrorMessage;
const fs_1 = require("fs");
const path_1 = require("path");
const logger_1 = require("../utils/logger");
/**
* Configuration parsing error
*/
class ConfigParseError extends Error {
constructor(message, cause, filePath) {
super(message);
this.cause = cause;
this.filePath = filePath;
this.name = 'ConfigParseError';
}
}
exports.ConfigParseError = ConfigParseError;
/**
* Discover configuration file in standard locations
*/
async function discoverConfigFile(baseDir = process.cwd()) {
const configFiles = [
'zod-generator.config.json',
'zod-generator.config.js',
'.zod-generator.json',
'.zod-generator.js',
];
for (const filename of configFiles) {
const configPath = (0, path_1.resolve)(baseDir, filename);
try {
await fs_1.promises.access(configPath);
return configPath;
}
catch {
// File doesn't exist, continue searching
}
}
return null;
}
/**
* Validate that a file exists and is readable
*/
async function validateFileExists(filePath) {
try {
const stats = await fs_1.promises.stat(filePath);
if (!stats.isFile()) {
throw new ConfigParseError(`Configuration path exists but is not a file: ${filePath}`);
}
}
catch (error) {
if (error.code === 'ENOENT') {
throw new ConfigParseError(`Configuration file not found at resolved path: ${filePath}. ` +
`Please verify the file exists or remove the config attribute to use defaults.`);
}
throw new ConfigParseError(`Cannot access configuration file: ${filePath}`, error, filePath);
}
}
/**
* Read configuration file content from disk
*/
async function readConfigFile(filePath) {
try {
return await fs_1.promises.readFile(filePath, 'utf-8');
}
catch (error) {
throw new ConfigParseError(`Failed to read configuration file: ${filePath}`, error, filePath);
}
}
/**
* Parse JSON configuration content
*/
function parseJsonConfig(content, filePath) {
try {
const parsed = JSON.parse(content);
if (typeof parsed !== 'object' || parsed === null) {
throw new ConfigParseError('Configuration must be a JSON object', undefined, filePath);
}
// Transform legacy format to new format
const transformedConfig = transformLegacyConfig(parsed);
return transformedConfig;
}
catch (error) {
if (error instanceof ConfigParseError) {
throw error;
}
throw new ConfigParseError(`Invalid JSON in configuration file${filePath ? `: ${filePath}` : ''}`, error, filePath);
}
}
function transformLegacyConfig(config) {
const base = (typeof config === 'object' && config !== null ? config : {});
const transformed = {
...base,
};
// Handle legacy Include/Select options
if (base.addSelectType !== undefined) {
logger_1.logger.debug(`🔄 Preserving legacy addSelectType: ${String(base.addSelectType)}`);
transformed.addSelectType = Boolean(base.addSelectType);
}
if (base.addIncludeType !== undefined) {
logger_1.logger.debug(`🔄 Preserving legacy addIncludeType: ${String(base.addIncludeType)}`);
transformed.addIncludeType = Boolean(base.addIncludeType);
}
// Transform model-specific legacy fields.exclude to variants format
if (transformed.models) {
const models = transformed.models;
Object.keys(models).forEach((modelName) => {
var _a, _b;
const modelConfig = models[modelName];
if (((_a = modelConfig.fields) === null || _a === void 0 ? void 0 : _a.exclude) && modelConfig.fields.exclude.length > 0) {
logger_1.logger.debug(`🔄 Transforming legacy fields.exclude for model ${modelName}:`, modelConfig.fields.exclude);
// Ensure variants object exists and work with a local, typed reference
const variants = ((_b = modelConfig.variants) !== null && _b !== void 0 ? _b : {});
// ensure modelConfig keeps the same reference
modelConfig.variants = variants;
// Apply fields.exclude to all variants
['pure', 'input', 'result'].forEach((variant) => {
var _a, _b, _c;
const vKey = variant;
if (!variants[vKey]) {
variants[vKey] = {};
}
// Merge with existing excludeFields if any
const existingExcludes = ((_a = variants[vKey]) === null || _a === void 0 ? void 0 : _a.excludeFields) || [];
const legacyExcludes = (_c = (_b = modelConfig.fields) === null || _b === void 0 ? void 0 : _b.exclude) !== null && _c !== void 0 ? _c : [];
variants[vKey].excludeFields = [
...new Set([...existingExcludes, ...legacyExcludes]),
];
});
// Preserve legacy fields.exclude so it can apply to custom array-based variants too
// (Do not delete here; generator will honor it for all variants.)
logger_1.logger.debug(`✅ Transformed model ${modelName} variants (preserving fields.exclude for compatibility):`, modelConfig.variants);
}
});
}
return transformed;
}
/**
* Resolve configuration file path (handle relative paths)
*/
function resolveConfigPath(configPath, baseDir = process.cwd()) {
if ((0, path_1.isAbsolute)(configPath)) {
return configPath;
}
return (0, path_1.resolve)(baseDir, configPath);
}
/**
* Parse configuration from file path
*/
async function parseConfigFromFile(configPath, baseDir) {
const resolvedPath = resolveConfigPath(configPath, baseDir);
logger_1.logger.debug(`🔍 Attempting to load config from: ${resolvedPath}`);
// Validate file exists
try {
await validateFileExists(resolvedPath);
logger_1.logger.debug(`✅ Config file exists at: ${resolvedPath}`);
}
catch (error) {
logger_1.logger.debug(`❌ Config file validation failed for: ${resolvedPath}`);
throw error;
}
// Read file content
const content = await readConfigFile(resolvedPath);
logger_1.logger.debug(`📖 Successfully read config file: ${resolvedPath} (${content.length} characters)`);
// Parse JSON content
const config = parseJsonConfig(content, resolvedPath);
logger_1.logger.debug(`✅ Successfully parsed config from: ${resolvedPath}`);
return {
config,
configPath: resolvedPath,
isDefault: false,
};
}
/**
* Auto-discover and parse configuration file
*/
async function parseConfigFromDiscovery(baseDir) {
const discoveredPath = await discoverConfigFile(baseDir);
if (!discoveredPath) {
return {
config: {},
isDefault: true,
};
}
return parseConfigFromFile(discoveredPath, baseDir);
}
/**
* Main configuration parser function
*
* @param configPath - Optional path to configuration file
* @param baseDir - Base directory for resolving relative paths
* @returns Parsed configuration result
*/
async function parseConfiguration(configPath, baseDir) {
try {
if (configPath) {
// Explicit config path provided
return await parseConfigFromFile(configPath, baseDir);
}
else {
// Auto-discover configuration
return await parseConfigFromDiscovery(baseDir);
}
}
catch (error) {
if (error instanceof ConfigParseError) {
throw error;
}
throw new ConfigParseError('Unexpected error while parsing configuration', error, configPath);
}
}
/**
* Create a detailed error message for configuration parsing failures
*/
function createConfigErrorMessage(error) {
let message = `Configuration Error: ${error.message}`;
if (error.filePath) {
message += `\n File: ${error.filePath}`;
}
if (error.cause) {
message += `\n Cause: ${error.cause.message}`;
}
// Add helpful suggestions
message += '\n\nTroubleshooting:';
message += '\n - Ensure the configuration file exists and is readable';
message += '\n - Verify the JSON syntax is valid';
message += '\n - Check file permissions';
if (!error.filePath) {
message += '\n - Consider creating a zod-generator.config.json file';
}
return message;
}
//# sourceMappingURL=parser.js.map