@cmmv/core
Version:
CMMV core module for contract and application management
439 lines (438 loc) • 19.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Compile = void 0;
const fs = require("node:fs");
const path = require("node:path");
const logger_1 = require("./logger");
class Compile {
constructor() {
this.logger = new logger_1.Logger('Compile');
}
static getInstance() {
if (!Compile.instance)
Compile.instance = new Compile();
return Compile.instance;
}
compileSchema(schema, outputPath) {
try {
this.validateSchema(schema);
const contractCode = this.generateContractCode(schema);
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir))
fs.mkdirSync(outputDir, { recursive: true });
fs.writeFileSync(outputPath, contractCode);
this.logger.log(`Contract successfully compiled and saved to ${outputPath}`);
return outputPath;
}
catch (error) {
this.logger.error(`Error compiling contract: ${error.message}`);
throw error;
}
}
validateSchema(schema) {
if (!schema.contractName)
throw new Error('Schema must have a contractName property');
if (!schema.controllerName)
throw new Error('Schema must have a controllerName property');
if (!schema.fields || !Array.isArray(schema.fields))
throw new Error('Schema must have a fields array property');
}
generateContractCode(schema) {
let code = this.generateImports(schema);
code += this.generateContractDecorator(schema);
code += this.generateContractClass(schema);
return code;
}
generateImports(schema) {
let imports = 'import {\n';
imports += ' Application,\n';
imports += ' AbstractContract,\n';
imports += ' Contract,\n';
imports += ' ContractField,\n';
if (schema.messages && schema.messages.length > 0)
imports += ' ContractMessage,\n';
if (schema.services && schema.services.length > 0)
imports += ' ContractService,\n';
imports += "} from '@cmmv/core';\n\n";
if (schema.imports && schema.imports.length > 0) {
schema.imports.forEach((importItem) => {
if (importItem.includes('*')) {
const parts = importItem.split(' as ');
imports += `import * as ${parts[1]} from '${parts[0].trim()}';\n`;
}
else if (importItem.includes('{')) {
imports += `import ${importItem};\n`;
}
else {
imports += `import * as ${importItem} from '${importItem}';\n`;
}
});
imports += '\n';
}
const linkedContracts = new Set();
schema.fields?.forEach((field) => {
if (field.link && Array.isArray(field.link)) {
field.link.forEach((link) => {
if (link.contract) {
linkedContracts.add(link.contract.name || link.contract.toString());
}
});
}
});
/*if (linkedContracts.size > 0) {
linkedContracts.forEach((contract) => {
imports += `import { ${contract} } from './${contract.replace('Contract', '').toLowerCase()}.contract';\n`;
});
imports += '\n';
}*/
return imports;
}
/**
* Generate the Contract decorator
*/
generateContractDecorator(schema) {
let decorator = '@Contract({\n';
if (schema.namespace)
decorator += ` namespace: '${schema.namespace}',\n`;
if (schema.isPublic)
decorator += ` isPublic: true,\n`;
decorator += ` controllerName: '${schema.controllerName}',\n`;
if (schema.protoPackage)
decorator += ` protoPackage: '${schema.protoPackage}',\n`;
if (schema.subPath)
decorator += ` subPath: '${schema.subPath}',\n`;
if (schema.generateController !== undefined)
decorator += ` generateController: ${schema.generateController},\n`;
if (schema.generateEntities !== undefined)
decorator += ` generateEntities: ${schema.generateEntities},\n`;
if (schema.auth !== undefined)
decorator += ` auth: ${schema.auth},\n`;
if (schema.rootOnly !== undefined)
decorator += ` rootOnly: ${schema.rootOnly},\n`;
if (schema.controllerCustomPath)
decorator += ` controllerCustomPath: '${schema.controllerCustomPath}',\n`;
if (schema.imports && schema.imports.length > 0) {
decorator += ' imports: [';
schema.imports.forEach((importItem, index) => {
decorator += `'${importItem}'`;
if (index < schema.imports.length - 1)
decorator += ', ';
});
decorator += '],\n';
}
if (schema.indexs && schema.indexs.length > 0) {
decorator += ' index: [\n';
schema.indexs.forEach((index, i) => {
decorator += ' {\n';
decorator += ` name: '${index.name}',\n`;
if (index.fields && index.fields.length > 0) {
decorator += ' fields: [';
index.fields.forEach((field, j) => {
if (typeof field === 'string')
decorator += `'${field}'`;
else
decorator += `'${field.name}'`;
if (j < index.fields.length - 1)
decorator += ', ';
});
decorator += '],\n';
}
decorator += ' }';
if (i < schema.indexs.length - 1)
decorator += ',\n';
else
decorator += '\n';
});
decorator += ' ],\n';
}
if (schema.cache && schema.cache.key) {
decorator += ' cache: {\n';
if (schema.cache.key !== undefined &&
typeof schema.cache.key === 'string')
decorator += ` key: "${schema.cache.key}",\n`;
if (schema.cache.ttl !== undefined &&
typeof schema.cache.ttl === 'number')
decorator += ` ttl: ${schema.cache.ttl},\n`;
if (schema.cache.compress !== undefined &&
typeof schema.cache.compress === 'boolean')
decorator += ` compress: ${schema.cache.compress},\n`;
decorator += ' },\n';
}
if (schema.options) {
decorator += ' options: {\n';
if (schema.options.tags) {
if (Array.isArray(schema.options.tags)) {
decorator += ' tags: [';
schema.options.tags.forEach((tag, index) => {
decorator += `'${tag}'`;
if (index < schema.options.tags.length - 1)
decorator += ', ';
});
decorator += '],\n';
}
else {
decorator += ` tags: ['${schema.options.tags}'],\n`;
}
}
if (schema.options.moduleContract !== undefined)
decorator += ` moduleContract: ${schema.options.moduleContract},\n`;
if (schema.options.databaseSchemaName)
decorator += ` databaseSchemaName: '${schema.options.databaseSchemaName}',\n`;
if (schema.options.databaseTimestamps !== undefined)
decorator += ` databaseTimestamps: ${schema.options.databaseTimestamps},\n`;
if (schema.options.databaseUserAction !== undefined)
decorator += ` databaseUserAction: ${schema.options.databaseUserAction},\n`;
if (schema.options.databaseFakeDelete !== undefined)
decorator += ` databaseFakeDelete: ${schema.options.databaseFakeDelete},\n`;
if (schema.options.description)
decorator += ` description: '${schema.options.description}',\n`;
decorator += ' },\n';
}
decorator += '})\n';
return decorator;
}
generateContractClass(schema) {
const className = this.getContractClassName(schema.contractName);
let classCode = `export class ${className} extends AbstractContract {\n`;
if (schema.fields && schema.fields.length > 0) {
schema.fields.forEach((field) => {
classCode += this.generateContractField(field);
});
}
if (schema.messages && schema.messages.length > 0) {
classCode += '\n';
schema.messages.forEach((message) => {
classCode += this.generateContractMessage(message);
});
}
if (schema.services && schema.services.length > 0) {
classCode += '\n';
schema.services.forEach((service) => {
classCode += this.generateContractService(service);
});
}
classCode += '}\n';
return classCode;
}
generateContractField(field) {
let fieldCode = ' @ContractField({\n';
fieldCode += ` protoType: '${field.protoType}',\n`;
if (field.unique !== undefined)
fieldCode += ` unique: ${field.unique},\n`;
if (field.nullable !== undefined)
fieldCode += ` nullable: ${field.nullable},\n`;
if (field.defaultValue !== undefined) {
if (typeof field.defaultValue === 'string')
fieldCode += ` defaultValue: '${field.defaultValue}',\n`;
else
fieldCode += ` defaultValue: ${field.defaultValue},\n`;
}
if (field.readOnly !== undefined)
fieldCode += ` readOnly: ${field.readOnly},\n`;
if (field.index !== undefined)
fieldCode += ` index: ${field.index},\n`;
if (field.objectType)
fieldCode += ` objectType: '${field.objectType}',\n`;
if (field.entityType)
fieldCode += ` entityType: '${field.entityType}',\n`;
if (field.protoRepeated !== undefined)
fieldCode += ` protoRepeated: ${field.protoRepeated},\n`;
if (field.exclude !== undefined)
fieldCode += ` exclude: ${field.exclude},\n`;
if (field.toPlainOnly !== undefined)
fieldCode += ` toPlainOnly: ${field.toPlainOnly},\n`;
if (field.validations && field.validations.length > 0) {
fieldCode += ' validations: [\n';
field.validations.forEach((validation, index) => {
fieldCode += ' {\n';
if (validation.type)
fieldCode += ` type: '${validation.type}',\n`;
if (validation.value !== undefined && validation.value !== null)
fieldCode += ` value: ${validation.value},\n`;
if (validation.message)
fieldCode += ` message: '${validation.message}',\n`;
fieldCode += ' }';
if (index < field.validations.length - 1)
fieldCode += ',\n';
else
fieldCode += '\n';
});
fieldCode += ' ],\n';
}
if (field.transform)
fieldCode += ` transform: ${field.transform.toString()},\n`;
if (field.afterValidation)
fieldCode += ` afterValidation: ${field.afterValidation.toString()},\n`;
if (field.resolver)
fieldCode += ` resolver: '${field.resolver}',\n`;
if (field.link && field.link.length > 0) {
fieldCode += ' link: [\n';
field.link.forEach((link, index) => {
if (link.contract) {
fieldCode += ' {\n';
if (link.createRelationship !== undefined)
fieldCode += ` createRelationship: ${link.createRelationship},\n`;
if (link.contract)
fieldCode += ` contract: Application.getContract("${link.contract}"),\n`;
if (link.entityName)
fieldCode += ` entityName: '${link.entityName}',\n`;
if (link.field)
fieldCode += ` field: '${link.field}',\n`;
if (link.array !== undefined)
fieldCode += ` array: ${link.array},\n`;
fieldCode += ' }';
if (index < field.link.length - 1)
fieldCode += ',\n';
else
fieldCode += '\n';
}
});
fieldCode += ' ],\n';
}
fieldCode += ' })\n';
fieldCode += ` ${field.propertyKey}`;
if (field.nullable || field.defaultValue !== undefined)
fieldCode += '?';
if (field.protoType === 'string') {
fieldCode += ': string';
}
else if (field.protoType === 'bool') {
fieldCode += ': boolean';
}
else if (field.protoType === 'int32' ||
field.protoType === 'int64' ||
field.protoType === 'float' ||
field.protoType === 'double') {
fieldCode += ': number';
}
else if (field.protoRepeated) {
let elementType = 'any';
if (field.protoType === 'string')
elementType = 'string';
if (field.protoType === 'bool')
elementType = 'boolean';
if (field.protoType === 'int32' ||
field.protoType === 'int64' ||
field.protoType === 'float' ||
field.protoType === 'double') {
elementType = 'number';
}
fieldCode += `: Array<${elementType}>`;
}
else {
fieldCode += ': any';
}
fieldCode += ';\n\n';
return fieldCode;
}
generateContractMessage(message) {
let messageCode = ` @ContractMessage({\n`;
messageCode += ` name: '${message.name}',\n`;
if (message.properties) {
messageCode += ' properties: {\n';
Object.entries(message.properties).forEach(([propKey, prop], index, array) => {
messageCode += ` '${propKey}': {\n`;
if (prop.type)
messageCode += ` type: '${prop.type}',\n`;
if (prop.paramType)
messageCode += ` paramType: '${prop.paramType}',\n`;
if (prop.required !== undefined &&
typeof prop.required === 'boolean')
messageCode += ` required: ${prop.required},\n`;
if (prop.arrayType)
messageCode += ` arrayType: '${prop.arrayType}',\n`;
messageCode += ' }';
if (index < Object.entries(array).length - 1)
messageCode += ',\n';
else
messageCode += '\n';
});
messageCode += ' },\n';
}
messageCode += ' })\n';
messageCode += ` ${message.propertyKey || message.name}: {\n`;
if (message.properties) {
Object.entries(message.properties).forEach(([propKey, prop]) => {
let typeAnnotation = 'any';
if (prop.type === 'string') {
typeAnnotation = 'string';
}
else if (prop.type === 'bool') {
typeAnnotation = 'boolean';
}
else if (prop.type === 'int' ||
prop.type === 'int32' ||
prop.type === 'int64' ||
prop.type === 'float' ||
prop.type === 'double') {
typeAnnotation = 'number';
}
else if (prop.type === 'simpleArray') {
let elementType = 'any';
if (prop.arrayType === 'string')
elementType = 'string';
if (prop.arrayType === 'boolean')
elementType = 'boolean';
if (prop.arrayType === 'number')
elementType = 'number';
typeAnnotation = `${elementType}[]`;
}
messageCode += ` ${propKey}${prop.required === false ? '?' : ''}: ${typeAnnotation};\n`;
});
}
messageCode += ' };\n\n';
return messageCode;
}
generateContractService(service) {
let serviceCode = ` @ContractService({\n`;
serviceCode += ` name: '${service.name}',\n`;
serviceCode += ` path: '${service.path}',\n`;
serviceCode += ` method: '${service.method}',\n`;
if (service.auth !== undefined && typeof service.auth === 'boolean')
serviceCode += ` auth: ${service.auth},\n`;
if (service.rootOnly !== undefined &&
typeof service.rootOnly === 'boolean')
serviceCode += ` rootOnly: ${service.rootOnly},\n`;
if (service.functionName)
serviceCode += ` functionName: '${service.functionName}',\n`;
if (service.request !== undefined)
serviceCode += ` request: '${service.request}',\n`;
if (service.response)
serviceCode += ` response: '${service.response}',\n`;
if (service.createBoilerplate !== undefined &&
typeof service.createBoilerplate === 'boolean')
serviceCode += ` createBoilerplate: ${service.createBoilerplate},\n`;
if (service.cache) {
serviceCode += ' cache: {\n';
if (service.cache.key !== undefined &&
typeof service.cache.key === 'string')
serviceCode += ` key: '${service.cache.key}',\n`;
if (service.cache.ttl !== undefined &&
typeof service.cache.ttl === 'number')
serviceCode += ` ttl: ${service.cache.ttl},\n`;
if (service.cache.compress !== undefined &&
typeof service.cache.compress === 'boolean')
serviceCode += ` compress: ${service.cache.compress},\n`;
serviceCode += ' },\n';
}
serviceCode += ' })\n';
serviceCode += ` ${service.propertyKey || service.name}: Function;\n\n`;
return serviceCode;
}
getContractClassName(contractName) {
if (contractName.endsWith('Contract')) {
return contractName;
}
return `${contractName}Contract`;
}
getContractName(contract) {
if (typeof contract === 'string') {
return contract;
}
if (contract.name) {
return contract.name;
}
return contract.toString();
}
}
exports.Compile = Compile;