// //#region Imports ... const XFileTools = require('../tools/x-file.tools'); const XValueTools = require('../tools/x-value.tools'); const XBaseDescriptor = require('./x-base.descriptor'); const XDecoratorDescriptor = require('./x-decorator.descriptor'); //#endregion /** * check an object instance is ClassDescriptor or not ... * * @param {any} object proposed object to check ... * @returns */ function isDescriptor(object) { // if (!object) { return false; } // let result = false; result = object.hasOwnProperty('raw') && Array.isArray(object.extends) && object.hasOwnProperty('name') && Array.isArray(object.implements) && object.hasOwnProperty('extends') && object.hasOwnProperty('isPublic') && object.hasOwnProperty('decorator') && object.hasOwnProperty('implements') && object.hasOwnProperty('isExported') && object.hasOwnProperty('isExtended') && object.hasOwnProperty('isAbstract') && object.hasOwnProperty('hasDecorator') && object.hasOwnProperty('isImplemented'); // return result; } /** * retrieve base or filled ClassDescriptor object instance ... * * @param {any} value a value provider object ... * @returns an instance of ClassDescriptor object ... */ function createDescriptor(value) { // const result = { raw: '', name: '', extends: [], implements: [], isPublic: true, isExported: false, isExtended: false, isAbstract: false, hasDecorator: false, isImplemented: false, decorator: { ...XDecoratorDescriptor.createDescriptor() }, }; // //#region Apply Props ... if (value) { // //#region name ... if (XValueTools.isValidArg(value.name)) { result.name = value.name; } //#endregion // //#region extends ... if (XValueTools.hasChildArray(value.extends)) { result.extends = [...value.extends]; } //#endregion // //#region implements ... if (XValueTools.hasChildArray(value.implements)) { result.implements = [...value.implements]; } //#endregion // //#region raw ... if (XValueTools.isValidArg(value.raw)) { result.raw = value.raw; } //#endregion // //#region isPublic ... if (!!value.isPublic) { result.isPublic = true; } else if (value.isPublic === false) { result.isPublic = false; } //#endregion // //#region isExported ... if (!!value.isExported) { result.isExported = true; } //#endregion // //#region isExtended ... if (!!value.isExtended) { result.isExtended = true; } //#endregion // //#region isAbstract ... if (!!value.isAbstract) { result.isAbstract = true; } //#endregion // //#region hasDecorator ... if (!!value.hasDecorator) { result.hasDecorator = true; } //#endregion // //#region decorator ... if (!!result.hasDecorator && value.decorator) { result.decorator = { ...XDecoratorDescriptor.createDescriptor(value.decorator) }; } else { result.decorator = undefined; } //#endregion // //#region isImplemented ... if (!!value.isImplemented) { result.isImplemented = true; } //#endregion } //#endregion // return result; } /** * converts an instance of Descriptor to Expression ... * * @param descriptor an instance of ClassDescriptor ... * @returns creator string ... */ function toExpression( descriptor = createDescriptor(), forceClearContent = false, ) { // let result = ''; // // Validate Arg ... if (!isDescriptor(descriptor)) { return result; } // let classContent = ''; let classContentIndexers = XValueTools.findClosedContent('{', '}', descriptor.raw); if (XValueTools.hasChildArray(classContentIndexers)) { // classContent = XValueTools .clearObjectSurround(classContentIndexers[0].content); // if (!!forceClearContent) { classContent = XBaseDescriptor.clearContent(classContent); } } // // Handle Class Decorator ... if ( !!descriptor.hasDecorator && XValueTools.isValidArgs(descriptor.decorator.type) && XBaseDescriptor.isValidDecoratorType(descriptor.decorator.type) ) { // result += XDecoratorDescriptor .toExpression(descriptor.decorator) + '\n'; } // // Handle Class ... result += // // Export ... ( descriptor.isExported ? XBaseDescriptor.ExpressionKeys.Export + ' ' : '' ) + // // Public or Private ... ( !descriptor.isPublic ? XBaseDescriptor.ExpressionKeys.Private + ' ' : '' ) + // // Abstract ... ( descriptor.isAbstract ? XBaseDescriptor.ExpressionKeys.Abstract + ' ' : '' ) + // // Class Definition ... XBaseDescriptor.ExpressionKeys.Class + ' ' + descriptor.name + ' ' + // // Extends ... ( descriptor.isExtended ? XBaseDescriptor.ExpressionKeys.Extends + ' ' + descriptor.extends.join(', ') + ' ' : '' ) + // // Implements ... ( descriptor.isImplemented ? XBaseDescriptor.ExpressionKeys.Implements + ' ' + descriptor.implements.join(', ') + ' ' : '' ) + // // Open Class ... '{\n' + // // Class Content ... ( XValueTools.isValidArg(classContent) ? classContent : '' ) + // // Close Class ... '\n}'; // return result; } /** * extract all exists ClassDescriptors ... * * @param {string} file a file path ... * @returns a collection of ClassDescriptors ... */ async function extractDescriptors(file = '') { // if (!XFileTools.isFileExists(file)) { return []; } // const fileContent = await XFileTools.readFile(file); if (!XValueTools.isValidArg(fileContent)) { return []; } // let result = [ createDescriptor() ]; result.pop(); // result = extractDescriptorsFromContent(fileContent); // return result; } /** * extract all exists ClassDescriptors ... * * @param {string} content a file content ... * @returns a collection of ClassDescriptors ... */ function extractDescriptorsFromContent(content = '') { // // Prepare result ... let result = [ createDescriptor() ]; result.pop(); // // Validate Args ... if ( !XValueTools.isValidArg(content) || !content.includes(XBaseDescriptor.ExpressionKeys.Class) ) { return result; } // // Extract all Key Indexes ... const keyIndexes = XBaseDescriptor. extractValidIdentifierIndexes(XBaseDescriptor.ExpressionKeys.Class, content); // // Validate Keys Exists on Content ... if (!XValueTools.hasChildArray(keyIndexes)) { return result; } // // Loop through indexes ... for (let index of keyIndexes) { // // create an empty instance of descriptor ... let descriptor = { ...createDescriptor() } // let startIndex = index; let endIndex = content.indexOf('{', startIndex); let rawIndexContents = XValueTools.findClosedContent('{', '}', content, endIndex); if (!XValueTools.hasChildArray(rawIndexContents)) { continue; } // let identifier = content.substring( index + XBaseDescriptor.ExpressionKeys.Class.length, endIndex ).trim(); // //#region HasExtends, Extends, HasImplements, Implements ... // const hasExtends = identifier.includes(XBaseDescriptor.ExpressionKeys.Extends); const hasImplements = identifier.includes(XBaseDescriptor.ExpressionKeys.Implements); // descriptor.isExtended = hasExtends; descriptor.isImplemented = hasImplements; // let extendsStartIndex = -1; let implementsStartIndex = -1; // // Extends ... if (!!hasExtends) { // extendsStartIndex = identifier .indexOf(XBaseDescriptor.ExpressionKeys.Extends) + XBaseDescriptor.ExpressionKeys.Extends.length; // let extendsIdentifier = identifier .substring(extendsStartIndex) .trim(); if (extendsIdentifier .includes(XBaseDescriptor.ExpressionKeys.Implements)) { // let extendsEndIndex = extendsIdentifier .indexOf(XBaseDescriptor.ExpressionKeys.Implements); extendsIdentifier = extendsIdentifier .substring(0, extendsEndIndex) .trim(); } // // Continue if it's not Valid ... if (!XValueTools.isValidArg(extendsIdentifier)) { continue; } // // Check if extends is Array or not ... if (extendsIdentifier.includes(',')) { descriptor.extends.push( ...extendsIdentifier .split(',') .map(e => e.trim()) .filter(e => XValueTools.isValidArg(e)) ) } else { descriptor.extends.push(extendsIdentifier); } } // // Implements ... if (!!hasImplements) { // implementsStartIndex = identifier .indexOf(XBaseDescriptor.ExpressionKeys.Implements) + XBaseDescriptor.ExpressionKeys.Implements.length; // let implementsIdentifier = identifier .substring(implementsStartIndex) .trim(); if (implementsIdentifier .includes(XBaseDescriptor.ExpressionKeys.Extends)) { // let implementsEndIndex = implementsIdentifier .indexOf(XBaseDescriptor.ExpressionKeys.Extends); implementsIdentifier = implementsIdentifier .substring(0, implementsEndIndex) .trim(); } // // Continue if it's not Valid ... if (!XValueTools.isValidArg(implementsIdentifier)) { continue; } // // Check if extends is Array or not ... if (implementsIdentifier.includes(',')) { descriptor.implements.push( ...implementsIdentifier .split(',') .map(i => i.trim()) .filter(i => XValueTools.isValidArg(i)) ) } else { descriptor.implements.push(implementsIdentifier); } } //#endregion // //#region Class Name ... let nameEndIndex = Math.min( ...[ extendsStartIndex, implementsStartIndex, identifier.length ] .filter(i => i >= 0) ); identifier = identifier .substring(0, nameEndIndex) .replace(XBaseDescriptor.ExpressionKeys.Extends, '') .replace(XBaseDescriptor.ExpressionKeys.Implements, '') .trim(); // // Continue if class name is not valid ... if (!XValueTools.isValidArg(identifier)) { continue; } // // Set Descriptor name ... descriptor.name = identifier; //#endregion // //#region Exported, Public, Abstract ... // const lengthForComeBack = XBaseDescriptor.ExpressionKeys.Export.length + XBaseDescriptor.ExpressionKeys.Public.length + XBaseDescriptor.ExpressionKeys.Private.length + XBaseDescriptor.ExpressionKeys.Abstract.length; // startIndex = index - lengthForComeBack; endIndex = index; identifier = content .substring(startIndex, endIndex) .trim(); // // Exported ... descriptor.isExported = identifier.includes(XBaseDescriptor.ExpressionKeys.Export); // // Public ... descriptor.isPublic = identifier.includes(XBaseDescriptor.ExpressionKeys.Public) || !identifier.includes(XBaseDescriptor.ExpressionKeys.Private); // // Abstract ... descriptor.isAbstract = identifier.includes(XBaseDescriptor.ExpressionKeys.Abstract); //#endregion // //#region Raw ... let classStartIndex = Math.max( ...[ content.indexOf(XBaseDescriptor.ExpressionKeys.Export, startIndex), content.indexOf(XBaseDescriptor.ExpressionKeys.Public, startIndex), content.indexOf(XBaseDescriptor.ExpressionKeys.Private, startIndex), content.indexOf(XBaseDescriptor.ExpressionKeys.Abstract, startIndex), ] .filter(i => i >= 0 && i <= rawIndexContents[0].start) ); let classEndIndex = rawIndexContents[0].end + 1; descriptor.raw = content.substring(classStartIndex, classEndIndex); //#endregion // //#region Decorator ... // const forExtractDecoratorContentEndIndex = content.indexOf(descriptor.raw); let forExtractDecoratorContent = content.substring(0, forExtractDecoratorContentEndIndex); // let decoratorIndexes = XValueTools .findAllIndexes('@(.*)\\({', forExtractDecoratorContent) .map(index => { return { startIndex: index, decoratorContentIndexer: XValueTools .findClosedContent('({', '})', forExtractDecoratorContent, index)[0] } }); let decoratorIndex = decoratorIndexes .find(i => i.decoratorContentIndexer.end + 1 === classStartIndex - 1); if ( !decoratorIndex && decoratorIndexes.length === 1 && XValueTools.hasChildArray(decoratorIndexes) ) { // decoratorIndex = { // ...decoratorIndexes[0], // decoratorContentIndexer: { // ...decoratorIndexes[0] .decoratorContentIndexer, // end: classStartIndex - 1, // content: content.substring( decoratorIndexes[0] .decoratorContentIndexer .start, classStartIndex - 1 ), } }; } // if (decoratorIndex) { // identifier = forExtractDecoratorContent .substring( decoratorIndex.startIndex, decoratorIndex.decoratorContentIndexer.end + 1 ); // const decorator = XDecoratorDescriptor.extractDescriptorFromContent(identifier); if (!decorator) { continue; } // descriptor.hasDecorator = true; descriptor.decorator = { ...decorator }; } //#endregion // result.push(descriptor); } // return result; } // // Module Exports ... module.exports = { // isDescriptor, toExpression, createDescriptor, extractDescriptors, extractDescriptorsFromContent, }