Initial Commit ...
This commit is contained in:
@@ -0,0 +1,622 @@
|
||||
//
|
||||
//#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,
|
||||
}
|
||||
Reference in New Issue
Block a user