Created
November 14, 2017 19:50
-
-
Save qfox/d57cec98f5dea9fd87a269c00f922e5d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let REFLECTION_ID = 0; | |
const ReflectionFlag = { | |
Private: 1, | |
Protected: 2, | |
Public: 4, | |
Static: 8, | |
Exported: 16, | |
ExportAssignment: 32, | |
External: 64, | |
Optional: 128, | |
DefaultValue: 256, | |
Rest: 512, | |
ConstructorProperty: 1024, | |
Abstract: 2048, | |
Const: 4096, | |
Let: 8192 | |
}; | |
const ReflectionKind = { | |
Global: 0, | |
ExternalModule: 1, | |
Module: 2, | |
Enum: 4, | |
EnumMember: 16, | |
Variable: 32, | |
Function: 64, | |
Class: 128, | |
Interface: 256, | |
Constructor: 512, | |
Property: 1024, | |
Method: 2048, | |
CallSignature: 4096, | |
IndexSignature: 8192, | |
ConstructorSignature: 16384, | |
Parameter: 32768, | |
TypeLiteral: 65536, | |
TypeParameter: 131072, | |
Accessor: 262144, | |
GetSignature: 524288, | |
SetSignature: 1048576, | |
ObjectLiteral: 2097152, | |
TypeAlias: 4194304, | |
Event: 8388608, | |
}; | |
const K = ReflectionKind; | |
K.ClassOrInterface = K.Class | K.Interface; | |
K.VariableOrProperty = K.Variable | K.Property; | |
K.FunctionOrMethod = K.Function | K.Method; | |
K.SomeSignature = K.CallSignature | K.IndexSignature | K.ConstructorSignature | K.GetSignature | K.SetSignature; | |
K.SomeModule = K.Module | K.ExternalModule | |
const TraverseProperty = { | |
Children: 1, | |
Parameters: 2, | |
TypeLiteral: 3, | |
TypeParameter: 4, | |
Signatures: 5, | |
IndexSignature: 6, | |
GetSignature: 7, | |
SetSignature: 8 | |
}; | |
class Reflection { | |
/** | |
* Unique id of this reflection. | |
*/ | |
// id: number; | |
/** | |
* The symbol name of this reflection. | |
*/ | |
// name = ''; | |
/** | |
* The original name of the TypeScript declaration. | |
*/ | |
// originalName: string; | |
/** | |
* The kind of this reflection. | |
*/ | |
// kind: ReflectionKind; | |
/** | |
* The human readable string representation of the kind of this reflection. | |
*/ | |
// kindString: string; | |
// flags: ReflectionFlags = []; | |
/** | |
* The reflection this reflection is a child of. | |
*/ | |
// parent: Reflection; | |
/** | |
* The parsed documentation comment attached to this reflection. | |
*/ | |
// comment: Comment; | |
/** | |
* A list of all source files that contributed to this reflection. | |
*/ | |
// sources: SourceReference[]; | |
/** | |
* A list of all decorators attached to this reflection. | |
*/ | |
// decorators: Decorator[]; | |
/** | |
* A list of all types that are decorated by this reflection. | |
*/ | |
// decorates: Type[]; | |
/** | |
* The url of this reflection in the generated documentation. | |
*/ | |
// url: string; | |
/** | |
* The name of the anchor of this child. | |
*/ | |
// anchor: string; | |
/** | |
* Is the url pointing to an individual document? | |
* | |
* When FALSE, the url points to an anchor tag on a page of a different reflection. | |
*/ | |
// hasOwnDocument: boolean; | |
/** | |
* A list of generated css classes that should be applied to representations of this | |
* reflection in the generated markup. | |
*/ | |
// cssClasses: string; | |
/** | |
* Url safe alias for this reflection. | |
* | |
* @see [[BaseReflection.getAlias]] | |
*/ | |
// private _alias: string; | |
// private _aliases: string[]; | |
/** | |
* Create a new BaseReflection instance. | |
*/ | |
constructor(parent/*?: Reflection*/, name/*?: string*/, kind/*?: ReflectionKind*/) { | |
this.id = REFLECTION_ID++; | |
this.parent = parent; | |
this.name = name; | |
this.originalName = name; | |
this.kind = kind; | |
} | |
/** | |
* @param kind The kind to test for. | |
*/ | |
// kindOf(kind: ReflectionKind): boolean; | |
/** | |
* @param kind An array of kinds to test for. | |
*/ | |
// kindOf(kind: ReflectionKind[]): boolean; | |
/** | |
* Test whether this reflection is of the given kind. | |
*/ | |
kindOf(kind/*: any*/)/*: boolean*/ { | |
if (Array.isArray(kind)) { | |
for (let i = 0, c = kind.length; i < c; i++) { | |
if ((this.kind & kind[i]) !== 0) { | |
return true; | |
} | |
} | |
return false; | |
} else { | |
return (this.kind & kind) !== 0; | |
} | |
} | |
/** | |
* Return the full name of this reflection. | |
* | |
* The full name contains the name of this reflection and the names of all parent reflections. | |
* | |
* @param separator Separator used to join the names of the reflections. | |
* @returns The full name of this reflection. | |
*/ | |
getFullName(separator/*: string*/ = '.')/*: string*/ { | |
if (this.parent && !this.parent.isProject()) { | |
return this.parent.getFullName(separator) + separator + this.name; | |
} else { | |
return this.name; | |
} | |
} | |
/** | |
* Set a flag on this reflection. | |
*/ | |
setFlag(flag/*: ReflectionFlag*/, value/*: boolean */= true) { | |
let name/*: string*/, index/*: number*/; | |
if (relevantFlags.indexOf(flag) !== -1) { | |
name = ReflectionFlag[flag]; | |
name = name.replace(/(.)([A-Z])/g, (m, a, b) => a + ' ' + b.toLowerCase()); | |
index = this.flags.indexOf(name); | |
} | |
if (value) { | |
this.flags.flags |= flag; | |
if (name && index === -1) { | |
this.flags.push(name); | |
} | |
} else { | |
this.flags.flags &= ~flag; | |
if (name && index !== -1) { | |
this.flags.splice(index, 1); | |
} | |
} | |
switch (flag) { | |
case ReflectionFlag.Private: | |
this.flags.isPrivate = value; | |
if (value) { | |
this.setFlag(ReflectionFlag.Protected, false); | |
this.setFlag(ReflectionFlag.Public, false); | |
} | |
break; | |
case ReflectionFlag.Protected: | |
this.flags.isProtected = value; | |
if (value) { | |
this.setFlag(ReflectionFlag.Private, false); | |
this.setFlag(ReflectionFlag.Public, false); | |
} | |
break; | |
case ReflectionFlag.Public: | |
this.flags.isPublic = value; | |
if (value) { | |
this.setFlag(ReflectionFlag.Private, false); | |
this.setFlag(ReflectionFlag.Protected, false); | |
} | |
break; | |
case ReflectionFlag.Static: | |
this.flags.isStatic = value; | |
break; | |
case ReflectionFlag.Exported: | |
this.flags.isExported = value; | |
break; | |
case ReflectionFlag.External: | |
this.flags.isExternal = value; | |
break; | |
case ReflectionFlag.Optional: | |
this.flags.isOptional = value; | |
break; | |
case ReflectionFlag.Rest: | |
this.flags.isRest = value; | |
break; | |
case ReflectionFlag.ExportAssignment: | |
this.flags.hasExportAssignment = value; | |
break; | |
case ReflectionFlag.ConstructorProperty: | |
this.flags.isConstructorProperty = value; | |
break; | |
case ReflectionFlag.Abstract: | |
this.flags.isAbstract = value; | |
break; | |
case ReflectionFlag.Let: | |
this.flags.isLet = value; | |
break; | |
case ReflectionFlag.Const: | |
this.flags.isConst = value; | |
break; | |
} | |
} | |
/** | |
* Return an url safe alias for this reflection. | |
*/ | |
getAlias()/*: string*/ { | |
if (!this._alias) { | |
let alias = this.name.replace(/[^a-z0-9]/gi, '_').toLowerCase(); | |
if (alias === '') { | |
alias = 'reflection-' + this.id; | |
} | |
let target = /*<Reflection>*/ this; | |
while (target.parent && !target.parent.isProject() && !target.hasOwnDocument) { | |
target = target.parent; | |
} | |
if (!target._aliases) { | |
target._aliases = []; | |
} | |
let suffix = '', index = 0; | |
while (target._aliases.indexOf(alias + suffix) !== -1) { | |
suffix = '-' + (++index).toString(); | |
} | |
alias += suffix; | |
target._aliases.push(alias); | |
this._alias = alias; | |
} | |
return this._alias; | |
} | |
/** | |
* Has this reflection a visible comment? | |
* | |
* @returns TRUE when this reflection has a visible comment. | |
*/ | |
hasComment()/*: boolean*/ { | |
return Boolean(this.comment && this.comment.hasVisibleComponent()); | |
} | |
hasGetterOrSetter()/*: boolean*/ { | |
return false; | |
} | |
/** | |
* @param name The name of the child to look for. Might contain a hierarchy. | |
*/ | |
// getChildByName(name: string): Reflection; | |
/** | |
* @param names The name hierarchy of the child to look for. | |
*/ | |
// getChildByName(names: string[]): Reflection; | |
/** | |
* Return a child by its name. | |
* | |
* @returns The found child or NULL. | |
*/ | |
getChildByName(arg/*: any*/)/*: Reflection*/ { | |
const names/*: string[]*/ = Array.isArray(arg) ? arg : arg.split('.'); | |
const name = names[0]; | |
let result/*: Reflection*/ = null; | |
this.traverse((child) => { | |
if (child.name === name) { | |
if (names.length <= 1) { | |
result = child; | |
} else if (child) { | |
result = child.getChildByName(names.slice(1)); | |
} | |
} | |
}); | |
return result; | |
} | |
/** | |
* Return whether this reflection is the root / project reflection. | |
*/ | |
isProject()/*: boolean*/ { // this is ProjectReflection | |
return false; | |
} | |
/** | |
* @param name The name to look for. Might contain a hierarchy. | |
*/ | |
// findReflectionByName(name: string): Reflection; | |
/** | |
* @param names The name hierarchy to look for. | |
*/ | |
// findReflectionByName(names: string[]): Reflection; | |
/** | |
* Try to find a reflection by its name. | |
* | |
* @return The found reflection or null. | |
*/ | |
findReflectionByName(arg/*: any*/)/*: Reflection*/ { | |
const names/*: string[]*/ = Array.isArray(arg) ? arg : arg.split('.'); | |
const reflection = this.getChildByName(names); | |
if (reflection) { | |
return reflection; | |
} else { | |
return this.parent.findReflectionByName(names); | |
} | |
} | |
/** | |
* Traverse all potential child reflections of this reflection. | |
* | |
* The given callback will be invoked for all children, signatures and type parameters | |
* attached to this reflection. | |
* | |
* @param callback The callback function that should be applied for each child reflection. | |
*/ | |
traverse(callback/*: TraverseCallback*/) { } | |
/** | |
* Return a raw object representation of this reflection. | |
* @deprecated Use serializers instead | |
*/ | |
toObject(): any { | |
const result: any = { | |
id: this.id, | |
name: this.name, | |
kind: this.kind, | |
kindString: this.kindString, | |
flags: {} | |
}; | |
if (this.originalName !== this.name) { | |
result.originalName = this.originalName; | |
} | |
if (this.comment) { | |
result.comment = this.comment.toObject(); | |
} | |
for (let key in this.flags) { | |
// tslint:disable-next-line:triple-equals | |
if (parseInt(key, 10) == <any> key || key === 'flags') { | |
continue; | |
} | |
if (this.flags[key]) { | |
result.flags[key] = true; | |
} | |
} | |
if (this.decorates) { | |
result.decorates = this.decorates.map((type) => type.toObject()); | |
} | |
if (this.decorators) { | |
result.decorators = this.decorators.map((decorator) => { | |
const result: any = { name: decorator.name }; | |
if (decorator.type) { | |
result.type = decorator.type.toObject(); | |
} | |
if (decorator.arguments) { | |
result.arguments = decorator.arguments; | |
} | |
return result; | |
}); | |
} | |
this.traverse((child, property) => { | |
if (property === TraverseProperty.TypeLiteral) { | |
return; | |
} | |
let name = TraverseProperty[property]; | |
name = name.substr(0, 1).toLowerCase() + name.substr(1); | |
if (!result[name]) { | |
result[name] = []; | |
} | |
result[name].push(child.toObject()); | |
}); | |
return result; | |
} | |
/** | |
* Return a string representation of this reflection. | |
*/ | |
toString(): string { | |
return ReflectionKind[this.kind] + ' ' + this.name; | |
} | |
/** | |
* Return a string representation of this reflection and all of its children. | |
* | |
* @param indent Used internally to indent child reflections. | |
*/ | |
toStringHierarchy(indent: string = '') { | |
const lines = [indent + this.toString()]; | |
indent += ' '; | |
this.traverse((child, property) => { | |
lines.push(child.toStringHierarchy(indent)); | |
}); | |
return lines.join('\n'); | |
} | |
}; | |
module.exports = { | |
Reflection, | |
ReflectionKind, | |
ReflectionFlag, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment