prisma-zod-generator
Version:
Prisma 2+ generator to emit Zod schemas from your Prisma schema
528 lines • 19.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultConfigurationManager = void 0;
exports.getDefaultConfiguration = getDefaultConfiguration;
exports.getMinimalConfiguration = getMinimalConfiguration;
exports.mergeWithDefaults = mergeWithDefaults;
exports.fillMissingModelConfigs = fillMissingModelConfigs;
exports.normalizeConfiguration = normalizeConfiguration;
exports.createPreset = createPreset;
exports.getAvailablePresets = getAvailablePresets;
exports.processConfiguration = processConfiguration;
const schema_1 = require("./schema");
/**
* Deep merge utility for configuration objects
*/
function deepMerge(target, source) {
const result = { ...target };
for (const key in source) {
if (source[key] !== undefined) {
if (typeof source[key] === 'object' &&
source[key] !== null &&
!Array.isArray(source[key]) &&
typeof target[key] === 'object' &&
target[key] !== null &&
!Array.isArray(target[key])) {
result[key] = deepMerge(target[key], source[key]);
}
else {
result[key] = source[key];
}
}
}
return result;
}
/**
* Default configuration factory
*/
class DefaultConfigurationManager {
/**
* Get complete default configuration
*/
static getDefaultConfiguration() {
return {
mode: schema_1.DEFAULT_CONFIG.mode,
output: schema_1.DEFAULT_CONFIG.output,
useMultipleFiles: true,
singleFileName: 'schemas.ts',
validateWhereUniqueAtLeastOne: false,
strictCreateInputs: true,
preserveRequiredScalarsOnCreate: true,
inferCreateArgsFromSchemas: false,
pureModels: false, // Default to false, can be overridden by user config
pureModelsLean: true,
pureModelsIncludeRelations: false,
pureModelsExcludeCircularRelations: false,
dateTimeStrategy: 'date',
dateTimeSplitStrategy: true,
jsonSchemaCompatible: false,
jsonSchemaOptions: {
dateTimeFormat: 'isoString',
bigIntFormat: 'string',
bytesFormat: 'base64String',
conversionOptions: {
unrepresentable: 'any',
cycles: 'throw',
reused: 'inline',
},
},
optionalFieldBehavior: 'nullish',
naming: {
preset: 'default',
// Intentionally leave pureModel overrides empty so presets can supply their own
// values without being clobbered by merged defaults. Resolver will apply
// fallback defaults when no preset/overrides are provided.
pureModel: {},
},
globalExclusions: {
input: [],
result: [],
pure: [],
},
variants: {
pure: {
enabled: schema_1.DEFAULT_CONFIG.variants.pure.enabled,
suffix: schema_1.DEFAULT_CONFIG.variants.pure.suffix,
excludeFields: [],
},
input: {
enabled: schema_1.DEFAULT_CONFIG.variants.input.enabled,
suffix: schema_1.DEFAULT_CONFIG.variants.input.suffix,
excludeFields: [],
},
result: {
enabled: schema_1.DEFAULT_CONFIG.variants.result.enabled,
suffix: schema_1.DEFAULT_CONFIG.variants.result.suffix,
excludeFields: [],
},
},
models: {},
};
}
/**
* Get default configuration for minimal mode
*/
static getMinimalConfiguration() {
const baseConfig = this.getDefaultConfiguration();
return {
...baseConfig,
mode: 'minimal',
pureModels: true, // Enable pure models by default in minimal mode
pureModelsLean: true,
pureModelsIncludeRelations: false,
naming: {
preset: 'default',
pureModel: {},
},
variants: {
pure: {
enabled: true,
suffix: '.model',
excludeFields: [],
},
input: {
enabled: true,
suffix: '.input',
excludeFields: ['id', 'createdAt', 'updatedAt'],
},
result: {
enabled: false, // Not typically needed in minimal mode
suffix: '.result',
excludeFields: [],
},
},
};
}
/**
* Get default configuration for custom mode
*/
static getCustomConfiguration() {
const baseConfig = this.getDefaultConfiguration();
return {
...baseConfig,
mode: 'custom',
};
}
/**
* Get default variant configuration
*/
static getDefaultVariantConfig(variantType, modelFields) {
const defaults = schema_1.DEFAULT_CONFIG.variants[variantType];
const baseConfig = {
enabled: defaults.enabled,
suffix: defaults.suffix,
excludeFields: [],
};
// Apply variant-specific defaults
switch (variantType) {
case 'input':
// Only exclude fields that actually exist in the model
const commonInputExclusions = ['id', 'createdAt', 'updatedAt'];
const actualExclusions = modelFields
? commonInputExclusions.filter((field) => modelFields.includes(field))
: commonInputExclusions;
return {
...baseConfig,
excludeFields: actualExclusions,
};
case 'result':
return {
...baseConfig,
excludeFields: [], // Usually include all fields in results
};
case 'pure':
return {
...baseConfig,
excludeFields: [], // Pure models typically include all fields
};
default:
return baseConfig;
}
}
/**
* Get default model configuration
*/
static getDefaultModelConfig(modelName, mode = 'full', modelFields) {
const operations = mode === 'minimal' ? [...schema_1.MINIMAL_OPERATIONS] : [...schema_1.PRISMA_OPERATIONS];
return {
enabled: true,
operations,
variants: {
pure: this.getDefaultVariantConfig('pure', modelFields),
input: this.getDefaultVariantConfig('input', modelFields),
result: this.getDefaultVariantConfig('result', modelFields),
},
};
}
/**
* Merge user configuration with defaults
*/
static mergeWithDefaults(userConfig) {
// Start with appropriate default based on mode
let defaultConfig;
switch (userConfig.mode) {
case 'minimal':
defaultConfig = this.getMinimalConfiguration();
break;
case 'custom':
defaultConfig = this.getCustomConfiguration();
break;
case 'full':
default:
defaultConfig = this.getDefaultConfiguration();
break;
}
// Deep merge user config with defaults
const mergedConfig = deepMerge(defaultConfig, userConfig);
// Apply mode-specific adjustments
return this.applyModeSpecificDefaults(mergedConfig);
}
/**
* Apply mode-specific default adjustments
*/
static applyModeSpecificDefaults(config) {
var _a;
const result = { ...config };
switch (config.mode) {
case 'minimal':
// Ensure minimal mode has appropriate defaults
if (((_a = result.variants) === null || _a === void 0 ? void 0 : _a.result) && result.variants.result.enabled === undefined) {
result.variants.result.enabled = false;
}
// Apply minimal operations to models that don't specify operations
if (result.models) {
Object.keys(result.models).forEach((modelName) => {
var _a;
const modelConfig = (_a = result.models) === null || _a === void 0 ? void 0 : _a[modelName];
if (modelConfig && !modelConfig.operations) {
modelConfig.operations = [...schema_1.MINIMAL_OPERATIONS];
}
});
}
break;
case 'full':
// Ensure all variants are enabled by default in full mode
if (result.variants) {
Object.keys(result.variants).forEach((variantName) => {
var _a;
const variant = (_a = result.variants) === null || _a === void 0 ? void 0 : _a[variantName];
if (variant && variant.enabled === undefined) {
variant.enabled = true;
}
});
}
break;
case 'custom':
// Custom mode uses explicit configuration, minimal adjustments
break;
}
return result;
}
/**
* Fill in missing model configurations with defaults
*/
static fillMissingModelConfigs(config, availableModels, modelFieldInfo) {
const result = { ...config };
if (!result.models) {
result.models = {};
}
// Only add default configuration for explicitly configured models
// Don't auto-add models that weren't specified by the user
Object.keys(result.models).forEach((modelName) => {
const models = result.models;
if (models === null || models === void 0 ? void 0 : models[modelName]) {
// Fill in missing properties for existing model configs
const modelConfig = models[modelName];
const defaultModelConfig = this.getDefaultModelConfig(modelName, result.mode, modelFieldInfo === null || modelFieldInfo === void 0 ? void 0 : modelFieldInfo[modelName]);
models[modelName] = deepMerge(defaultModelConfig, modelConfig);
}
});
return result;
}
/**
* Validate and normalize configuration
*/
static normalizeConfiguration(config) {
const result = { ...config };
// Support legacy/minimal boolean flag by mapping to mode
const legacy = result;
if (legacy.minimal === true && result.mode !== 'minimal') {
result.mode = 'minimal';
}
// Normalize mode
if (!result.mode || !schema_1.GENERATION_MODES.includes(result.mode)) {
result.mode = schema_1.DEFAULT_CONFIG.mode;
}
// Normalize output path
if (!result.output || typeof result.output !== 'string') {
result.output = schema_1.DEFAULT_CONFIG.output;
}
// Normalize global exclusions
if (!result.globalExclusions) {
result.globalExclusions = {};
}
const variantTypes = ['input', 'result', 'pure'];
variantTypes.forEach((variantType) => {
const globalExclusions = result.globalExclusions;
if (globalExclusions && !Array.isArray(globalExclusions[variantType])) {
globalExclusions[variantType] = [];
}
});
// Normalize operations in global exclusions
if (result.globalExclusions && !Array.isArray(result.globalExclusions.operations)) {
result.globalExclusions.operations = [];
}
// Normalize variants
if (!result.variants) {
result.variants = {};
}
const variants = ['pure', 'input', 'result'];
variants.forEach((variantName) => {
const variantsConfig = result.variants;
if (variantsConfig && !variantsConfig[variantName]) {
variantsConfig[variantName] = this.getDefaultVariantConfig(variantName);
}
else if (variantsConfig) {
const variant = variantsConfig[variantName];
if (variant) {
const defaultVariant = this.getDefaultVariantConfig(variantName);
variantsConfig[variantName] = deepMerge(defaultVariant, variant);
}
}
});
// Normalize models
if (!result.models) {
result.models = {};
}
// Normalize file options
if (typeof result.useMultipleFiles !== 'boolean') {
result.useMultipleFiles = true;
}
if (!result.singleFileName || typeof result.singleFileName !== 'string') {
result.singleFileName = 'schemas.ts';
}
return result;
}
/**
* Create configuration preset for common use cases
*/
static createPreset(presetName) {
switch (presetName) {
case 'minimal':
return this.getMinimalConfiguration();
case 'trpc':
return {
mode: 'custom',
output: './generated/zod',
globalExclusions: {
input: ['id', 'createdAt', 'updatedAt'],
result: [],
pure: ['password', 'hashedPassword'],
},
variants: {
pure: {
enabled: true,
suffix: '.model',
excludeFields: [],
},
input: {
enabled: true,
suffix: '.input',
excludeFields: [],
},
result: {
enabled: true,
suffix: '.output',
excludeFields: [],
},
},
models: {},
};
case 'api-validation':
return {
mode: 'custom',
output: './src/schemas',
globalExclusions: {
input: ['id', 'createdAt', 'updatedAt'],
result: [],
pure: [],
},
variants: {
pure: {
enabled: false,
suffix: '.model',
excludeFields: [],
},
input: {
enabled: true,
suffix: '.request',
excludeFields: [],
},
result: {
enabled: true,
suffix: '.response',
excludeFields: [],
},
},
models: {},
};
case 'full-featured':
return {
mode: 'full',
output: './generated/zod-schemas',
globalExclusions: {},
variants: {
pure: {
enabled: true,
suffix: '.model',
excludeFields: [],
},
input: {
enabled: true,
suffix: '.input',
excludeFields: ['id', 'createdAt', 'updatedAt'],
},
result: {
enabled: true,
suffix: '.result',
excludeFields: [],
},
},
models: {},
};
default:
return this.getDefaultConfiguration();
}
}
/**
* Get available configuration presets
*/
static getAvailablePresets() {
return [
{
name: 'minimal',
description: 'Basic CRUD operations only, minimal file output',
useCase: 'Simple applications that only need basic database operations',
},
{
name: 'trpc',
description: 'Optimized for tRPC usage with input/output variants',
useCase: 'Full-stack applications using tRPC for type-safe APIs',
},
{
name: 'api-validation',
description: 'Request/response validation for REST APIs',
useCase: 'REST API applications needing request/response validation',
},
{
name: 'full-featured',
description: 'Complete schema generation with all features enabled',
useCase: 'Complex applications needing comprehensive schema coverage',
},
];
}
}
exports.DefaultConfigurationManager = DefaultConfigurationManager;
/**
* Convenience functions
*/
/**
* Get default configuration
*/
function getDefaultConfiguration() {
return DefaultConfigurationManager.getDefaultConfiguration();
}
/**
* Get minimal configuration
*/
function getMinimalConfiguration() {
return DefaultConfigurationManager.getMinimalConfiguration();
}
/**
* Merge user config with defaults
*/
function mergeWithDefaults(userConfig) {
return DefaultConfigurationManager.mergeWithDefaults(userConfig);
}
/**
* Fill missing model configurations
*/
function fillMissingModelConfigs(config, availableModels, modelFieldInfo) {
return DefaultConfigurationManager.fillMissingModelConfigs(config, availableModels, modelFieldInfo);
}
/**
* Normalize configuration
*/
function normalizeConfiguration(config) {
return DefaultConfigurationManager.normalizeConfiguration(config);
}
/**
* Create preset configuration
*/
function createPreset(presetName) {
return DefaultConfigurationManager.createPreset(presetName);
}
/**
* Get available presets
*/
function getAvailablePresets() {
return DefaultConfigurationManager.getAvailablePresets();
}
/**
* Process and finalize configuration
*
* This is the main function that should be used to process configuration
* from parsing through defaults application and normalization.
*/
function processConfiguration(userConfig, availableModels, modelFieldInfo) {
// 1. Merge with defaults
let config = mergeWithDefaults(userConfig);
// 2. Normalize the configuration
config = normalizeConfiguration(config);
// 3. Fill in missing model configurations if models are provided
if (availableModels && availableModels.length > 0) {
config = fillMissingModelConfigs(config, availableModels, modelFieldInfo);
}
return config;
}
//# sourceMappingURL=defaults.js.map