diff --git a/.gitignore b/.gitignore index 111a198ac..4a5a878c4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ .baseDir.js .baseDir.ts yarn.lock +yarn-error.log /src/typings/typescript/typescript.js diff --git a/.vscode/launch.json b/.vscode/launch.json index 3d6f41e78..97157b0d0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,17 +7,19 @@ { "type": "node", "request": "launch", - "name": "Launch Program", + "name": "Debug Tests", "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", "cwd": "${workspaceRoot}", "args": [ - "--no-timeouts" + "--no-timeouts", + "dist/test/**/*.js" ], "outFiles": [ "${workspaceRoot}/lib/**/*.js", "${workspaceRoot}/test/**/*.js" ], + "preLaunchTask": "build", "sourceMaps": true } ] -} \ No newline at end of file +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index dbb12dd67..bc6030ff4 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,11 +1,23 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "0.1.0", - "command": "tsc", - "isShellCommand": true, - "args": ["-w", "-p", "."], - "showOutput": "silent", - "isWatching": true, - "problemMatcher": "$tsc-watch" -} \ No newline at end of file + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "identifier": "build", + "type": "grunt", + "task": "default", + "problemMatcher": [ + "$tsc" + ] + }, + { + "identifier": "build_and_test", + "type": "grunt", + "task": "build_and_test", + "problemMatcher": [ + "$tsc" + ] + } + ] +} diff --git a/package-lock.json b/package-lock.json index 2c9467fd9..b9191d4eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1064,7 +1064,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -1085,12 +1086,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1105,17 +1108,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -1232,7 +1238,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -1244,6 +1251,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1258,6 +1266,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1265,12 +1274,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -1289,6 +1300,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -1369,7 +1381,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -1381,6 +1394,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -1466,7 +1480,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -1502,6 +1517,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -1521,6 +1537,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -1564,12 +1581,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/package.json b/package.json index 37a0072b2..5194fc74f 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@types/shelljs": "^0.8.0", "fs-extra": "^7.0.0", "handlebars": "^4.0.6", - "highlight.js": "^9.0.0", + "highlight.js": "9.12.0", "lodash": "^4.17.10", "marked": "^0.4.0", "minimatch": "^3.0.0", diff --git a/src/lib/application.ts b/src/lib/application.ts index ce9ccc466..0fad877c7 100644 --- a/src/lib/application.ts +++ b/src/lib/application.ts @@ -17,7 +17,7 @@ import { Serializer } from './serialization'; import { ProjectReflection } from './models/index'; import { Logger, ConsoleLogger, CallbackLogger, PluginHost, writeFile } from './utils/index'; -import { AbstractComponent, ChildableComponent, Component, Option } from './utils/component'; +import { AbstractComponent, ChildableComponent, Component, Option, DUMMY_APPLICATION_OWNER } from './utils/component'; import { Options, OptionsReadMode, OptionsReadResult } from './utils/options/index'; import { ParameterType } from './utils/options/declaration'; @@ -67,21 +67,21 @@ export class Application extends ChildableComponent; + exclude!: Array; /** * The version number of TypeDoc. @@ -94,7 +94,7 @@ export class Application extends ChildableComponent('converter', Converter); @@ -153,9 +153,9 @@ export class Application extends ChildableComponent = this.exclude ? this.exclude.map(pattern => new Minimatch(pattern, {dot: true})) : []; diff --git a/src/lib/cli.ts b/src/lib/cli.ts index 061ee26b6..ee6e9f998 100644 --- a/src/lib/cli.ts +++ b/src/lib/cli.ts @@ -20,14 +20,14 @@ export class CliApplication extends Application { help: 'Specifies the location the documentation should be written to.', hint: ParameterHint.Directory }) - out: string; + out!: string; @Option({ name: 'json', help: 'Specifies the location and file name a json file describing the project is written to.', hint: ParameterHint.File }) - json: string; + json!: string; @Option({ name: 'version', @@ -35,7 +35,7 @@ export class CliApplication extends Application { help: 'Print the TypeDoc\'s version.', type: ParameterType.Boolean }) - version: boolean; + version!: boolean; @Option({ name: 'help', @@ -43,7 +43,7 @@ export class CliApplication extends Application { help: 'Print this message.', type: ParameterType.Boolean }) - help: boolean; + help!: boolean; /** * Run TypeDoc from the command line. @@ -51,8 +51,7 @@ export class CliApplication extends Application { protected bootstrap(options?: Object): OptionsReadResult { const result = super.bootstrap(options); if (result.hasErrors) { - process.exit(ExitCode.OptionError); - return; + return process.exit(ExitCode.OptionError); } if (this.version) { diff --git a/src/lib/converter/components.ts b/src/lib/converter/components.ts index ef24666b9..0196d9949 100644 --- a/src/lib/converter/components.ts +++ b/src/lib/converter/components.ts @@ -14,9 +14,9 @@ export abstract class ConverterNodeComponent extends Converte /** * List of supported TypeScript syntax kinds. */ - supports: ts.SyntaxKind[]; + abstract supports: ts.SyntaxKind[]; - abstract convert(context: Context, node: T): Reflection; + abstract convert(context: Context, node: T): Reflection | undefined; } export abstract class ConverterTypeComponent extends ConverterComponent { @@ -39,17 +39,17 @@ export interface TypeTypeConverter extends ConverterTypeCompo /** * Convert the given type to its type reflection. */ - convertType(context: Context, type: T): Type; + convertType(context: Context, type: T): Type | undefined; } export interface TypeNodeConverter extends ConverterTypeComponent { /** * Test whether this converter can handle the given TypeScript node. */ - supportsNode(context: Context, node: N, type: T): boolean; + supportsNode(context: Context, node: N, type: T | undefined): boolean; /** * Convert the given type node to its type reflection. */ - convertNode(context: Context, node: N, type: T): Type; + convertNode(context: Context, node: N, type: T | undefined): Type | undefined; } diff --git a/src/lib/converter/context.ts b/src/lib/converter/context.ts index 6678c1776..78e085f31 100644 --- a/src/lib/converter/context.ts +++ b/src/lib/converter/context.ts @@ -43,42 +43,42 @@ export class Context { /** * Is the current source file marked as being external? */ - isExternal: boolean; + isExternal?: boolean; /** * Is the current source file a declaration file? */ - isDeclaration: boolean; + isDeclaration?: boolean; /** * The currently set type parameters. */ - typeParameters: ts.MapLike; + typeParameters?: ts.MapLike; /** * The currently set type arguments. */ - typeArguments: Type[]; + typeArguments?: Type[]; /** * Is the converter in inheritance mode? */ - isInherit: boolean; + isInherit?: boolean; /** * The node that has started the inheritance mode. */ - inheritParent: ts.Node; + inheritParent?: ts.Node; /** * List symbol ids of inherited children already visited while inheriting. */ - inheritedChildren: number[]; + inheritedChildren?: number[]; /** * The names of the children of the scope before inheritance has been started. */ - inherited: string[]; + inherited?: string[]; /** * A list of parent nodes that have been passed to the visit function. @@ -93,7 +93,7 @@ export class Context { /** * The pattern that should be used to flag external source files. */ - private externalPattern: IMinimatch; + private externalPattern?: IMinimatch; /** * Create a new Context instance. @@ -131,8 +131,8 @@ export class Context { * @param node The TypeScript node whose type should be resolved. * @returns The type declaration of the given node. */ - getTypeAtLocation(node: ts.Node): ts.Type { - let nodeType: ts.Type; + getTypeAtLocation(node: ts.Node): ts.Type | undefined { + let nodeType: ts.Type | undefined; try { nodeType = this.checker.getTypeAtLocation(node); } catch (error) { @@ -165,11 +165,11 @@ export class Context { * It will assign negative ids if they are not set. * * @param symbol The symbol whose id should be returned. - * @returns The id of the given symbol. + * @returns The id of the given symbol or undefined if no symbol is provided. */ - getSymbolID(symbol: ts.Symbol): number { + getSymbolID(symbol: ts.Symbol | undefined): number | undefined { if (!symbol) { - return null; + return; } if (!symbol.id) { symbol.id = this.symbolID--; @@ -187,10 +187,10 @@ export class Context { * @param node The node the given reflection was resolved from. * @param symbol The symbol the given reflection was resolved from. */ - registerReflection(reflection: Reflection, node: ts.Node, symbol?: ts.Symbol) { + registerReflection(reflection: Reflection, node?: ts.Node, symbol?: ts.Symbol) { this.project.reflections[reflection.id] = reflection; - const id = this.getSymbolID(symbol ? symbol : (node ? node.symbol : null)); + const id = this.getSymbolID(symbol ? symbol : (node ? node.symbol : undefined)); if (!this.isInherit && id && !this.project.symbolMapping[id]) { this.project.symbolMapping[id] = reflection.id; } @@ -248,20 +248,27 @@ export class Context { /** * @param callback The callback function that should be executed with the changed context. */ - public withScope(scope: Reflection, callback: Function): void; + public withScope(scope: Reflection | undefined, callback: () => void): void; /** * @param parameters An array of type parameters that should be set on the context while the callback is invoked. * @param callback The callback function that should be executed with the changed context. */ - public withScope(scope: Reflection, parameters: ts.NodeArray, callback: Function): void; + public withScope( + scope: Reflection | undefined, + parameters: ts.NodeArray | undefined, + callback: () => void): void; /** * @param parameters An array of type parameters that should be set on the context while the callback is invoked. * @param preserve Should the currently set type parameters of the context be preserved? * @param callback The callback function that should be executed with the changed context. */ - public withScope(scope: Reflection, parameters: ts.NodeArray, preserve: boolean, callback: Function): void; + public withScope( + scope: Reflection | undefined, + parameters: ts.NodeArray | undefined, + preserve: boolean, + callback: () => void): void; /** * Run the given callback with the scope of the context set to the given reflection. @@ -281,7 +288,7 @@ export class Context { this.scope = scope; this.typeParameters = parameters ? this.extractTypeParameters(parameters, args.length > 0) : this.typeParameters; - this.typeArguments = null; + this.typeArguments = undefined; callback(); @@ -313,7 +320,7 @@ export class Context { } if (baseNode.symbol) { - const id = this.getSymbolID(baseNode.symbol); + const id = this.getSymbolID(baseNode.symbol)!; if (this.inheritedChildren && this.inheritedChildren.indexOf(id) !== -1) { return target; } else { @@ -329,9 +336,9 @@ export class Context { } if (typeArguments) { - this.typeArguments = typeArguments.map((t) => this.converter.convertType(this, t)); + this.typeArguments = this.converter.convertTypes(this, typeArguments); } else { - this.typeArguments = null; + this.typeArguments = undefined; } this.converter.convertNode(this, baseNode); @@ -359,20 +366,23 @@ export class Context { const typeParameters: ts.MapLike = {}; if (preserve) { - for (let key in this.typeParameters) { - if (!this.typeParameters.hasOwnProperty(key)) { - continue; - } - typeParameters[key] = this.typeParameters[key]; - } + Object.keys(this.typeParameters || {}).forEach(key => { + typeParameters[key] = this.typeParameters![key]; + }); } parameters.forEach((declaration: ts.TypeParameterDeclaration, index: number) => { + if (!declaration.symbol) { + return; + } const name = declaration.symbol.name; if (this.typeArguments && this.typeArguments[index]) { typeParameters[name] = this.typeArguments[index]; } else { - typeParameters[name] = createTypeParameter(this, declaration); + const param = createTypeParameter(this, declaration); + if (param) { + typeParameters[name] = param; + } } }); diff --git a/src/lib/converter/convert-expression.ts b/src/lib/converter/convert-expression.ts index 2fe94cd26..4a7e61a24 100644 --- a/src/lib/converter/convert-expression.ts +++ b/src/lib/converter/convert-expression.ts @@ -8,11 +8,11 @@ import * as _ts from '../ts-internal'; * @returns The default value as a string. */ -export function convertDefaultValue(node: ts.VariableDeclaration|ts.ParameterDeclaration|ts.EnumMember): string { +export function convertDefaultValue(node: ts.VariableDeclaration|ts.ParameterDeclaration|ts.EnumMember): string | undefined { if (node.initializer) { return convertExpression(node.initializer); } else { - return null; + return undefined; } } diff --git a/src/lib/converter/converter.ts b/src/lib/converter/converter.ts index 5a02111e6..9b65778ef 100644 --- a/src/lib/converter/converter.ts +++ b/src/lib/converter/converter.ts @@ -38,56 +38,68 @@ export class Converter extends ChildableComponent}; + /** + * Defined in the initialize method + */ + private nodeConverters!: {[syntaxKind: number]: ConverterNodeComponent}; - private typeNodeConverters: TypeNodeConverter[]; + /** + * Defined in the initialize method + */ + private typeNodeConverters!: TypeNodeConverter[]; - private typeTypeConverters: TypeTypeConverter[]; + /** + * Defined in the initialize method + */ + private typeTypeConverters!: TypeTypeConverter[]; /** * General events @@ -220,7 +232,7 @@ export class Converter extends ChildableComponent = [], types: ReadonlyArray = []): Type[] { + const result: Type[] = []; + _.zip(nodes, types).forEach(([node, type]) => { + const converted = this.convertType(context, node, type); + if (converted) { + result.push(converted); + } + }); + return result; + } + /** * Compile the files within the given context and convert the compiler symbols to reflections. * diff --git a/src/lib/converter/factories/comment.ts b/src/lib/converter/factories/comment.ts index eabde9786..c6d5443a5 100644 --- a/src/lib/converter/factories/comment.ts +++ b/src/lib/converter/factories/comment.ts @@ -7,13 +7,12 @@ import { Comment, CommentTag } from '../../models/comments/index'; * Return the parsed comment of the given TypeScript node. * * @param node The node whose comment should be returned. - * @return The parsed comment as a [[Comment]] instance or NULL if - * no comment is present. + * @return The parsed comment as a [[Comment]] instance or undefined if no comment is present. */ -export function createComment(node: ts.Node): Comment { +export function createComment(node: ts.Node): Comment | undefined { const comment = getRawComment(node); - if (comment == null) { - return null; + if (!comment) { + return; } return parseComment(comment); @@ -22,7 +21,7 @@ export function createComment(node: ts.Node): Comment { /** * Check whether the given module declaration is the topmost. * - * This funtion returns TRUE if there is no trailing module defined, in + * This function returns TRUE if there is no trailing module defined, in * the following example this would be the case only for module C. * * ``` @@ -70,14 +69,14 @@ function getRootModuleDeclaration(node: ts.ModuleDeclaration): ts.Node { * Return the raw comment string for the given node. * * @param node The node whose comment should be resolved. - * @returns The raw comment string or NULL if no comment could be found. + * @returns The raw comment string or undefined if no comment could be found. */ -export function getRawComment(node: ts.Node): string { +export function getRawComment(node: ts.Node): string | undefined { if (node.parent && node.parent.kind === ts.SyntaxKind.VariableDeclarationList) { node = node.parent.parent; } else if (node.kind === ts.SyntaxKind.ModuleDeclaration) { if (!isTopmostModuleDeclaration( node)) { - return null; + return; } else { node = getRootModuleDeclaration( node); } @@ -89,7 +88,7 @@ export function getRawComment(node: ts.Node): string { let comment: ts.CommentRange; if (node.kind === ts.SyntaxKind.SourceFile) { if (comments.length === 1) { - return null; + return; } comment = comments[0]; } else { @@ -98,7 +97,7 @@ export function getRawComment(node: ts.Node): string { return sourceFile.text.substring(comment.pos, comment.end); } else { - return null; + return; } } @@ -138,7 +137,7 @@ export function parseComment(text: string, comment: Comment = new Comment()): Co function readTagLine(line: string, tag: RegExpExecArray) { let tagName = tag[1].toLowerCase(); - let paramName: string; + let paramName: string | undefined; line = line.substr(tagName.length + 1).trim(); if (tagName === 'return') { tagName = 'returns'; } @@ -179,8 +178,6 @@ export function parseComment(text: string, comment: Comment = new Comment()): Co readBareLine(line); } - // text = text.replace(/^\s*\/\*+\s*(\r\n?|\n)/, ''); - // text = text.replace(/(\r\n?|\n)\s*\*+\/\s*$/, ''); text = text.replace(/^\s*\/\*+/, ''); text = text.replace(/\*+\/\s*$/, ''); text.split(/\r\n?|\n/).forEach(readLine); diff --git a/src/lib/converter/factories/declaration.ts b/src/lib/converter/factories/declaration.ts index 371e9997a..777bd4ca7 100644 --- a/src/lib/converter/factories/declaration.ts +++ b/src/lib/converter/factories/declaration.ts @@ -30,13 +30,13 @@ const nonStaticMergeKinds = [ * @param node The TypeScript node that should be converted to a reflection. * @param kind The desired kind of the reflection. * @param name The desired name of the reflection. - * @returns The resulting reflection. + * @returns The resulting reflection or undefined if an error is encountered. */ -export function createDeclaration(context: Context, node: ts.Declaration, kind: ReflectionKind, name?: string): DeclarationReflection { - const container = context.scope; - if (!(container instanceof ContainerReflection)) { +export function createDeclaration(context: Context, node: ts.Declaration, kind: ReflectionKind, name?: string): DeclarationReflection | undefined { + if (!(context.scope instanceof ContainerReflection)) { throw new Error('Expected container reflection.'); } + const container = context.scope; // Ensure we have a name for the reflection if (!name) { @@ -45,7 +45,7 @@ export function createDeclaration(context: Context, node: ts.Declaration, kind: } else if (node.symbol) { name = node.symbol.name; } else { - return null; + return; } } @@ -69,13 +69,13 @@ export function createDeclaration(context: Context, node: ts.Declaration, kind: } if (!isExported && context.converter.excludeNotExported) { - return null; + return; } // Test whether the node is private, when inheriting ignore private members const isPrivate = !!(modifiers & ts.ModifierFlags.Private); if (context.isInherit && isPrivate) { - return null; + return; } // Test whether the node is static, when merging a module to a class make the node static @@ -93,7 +93,7 @@ export function createDeclaration(context: Context, node: ts.Declaration, kind: } // Check if we already have a child with the same name and static flag - let child: DeclarationReflection; + let child: DeclarationReflection | undefined; const children = container.children = container.children || []; children.forEach((n: DeclarationReflection) => { if (n.name === name && n.flags.isStatic === isStatic) { @@ -103,7 +103,7 @@ export function createDeclaration(context: Context, node: ts.Declaration, kind: if (!child) { // Child does not exist, create a new reflection - child = new DeclarationReflection(container, name, kind); + child = new DeclarationReflection(name, kind, container); child.setFlag(ReflectionFlag.Static, isStatic); child.setFlag(ReflectionFlag.Private, isPrivate); child.setFlag(ReflectionFlag.ConstructorProperty, isConstructorProperty); @@ -179,7 +179,7 @@ function mergeDeclarations(context: Context, reflection: DeclarationReflection, if ( context.isInherit && - context.inherited.indexOf(reflection.name) !== -1 && + (context.inherited || []).indexOf(reflection.name) !== -1 && (node.parent === context.inheritParent || reflection.flags.isConstructorProperty) ) { if (!reflection.overwrites) { @@ -188,7 +188,7 @@ function mergeDeclarations(context: Context, reflection: DeclarationReflection, signature.overwrites = createReferenceType(context, node.symbol, true); }); } - return null; + return; } return reflection; diff --git a/src/lib/converter/factories/parameter.ts b/src/lib/converter/factories/parameter.ts index bf82b654a..c5a95e8f6 100644 --- a/src/lib/converter/factories/parameter.ts +++ b/src/lib/converter/factories/parameter.ts @@ -13,13 +13,17 @@ import { convertDefaultValue } from '../convert-expression'; * @param node The parameter node that should be reflected. * @returns The newly created parameter reflection. */ -export function createParameter(context: Context, node: ts.ParameterDeclaration): ParameterReflection { - const signature = context.scope; - if (!(signature instanceof SignatureReflection)) { +export function createParameter(context: Context, node: ts.ParameterDeclaration): ParameterReflection | undefined { + if (!(context.scope instanceof SignatureReflection)) { throw new Error('Expected signature reflection.'); } + const signature = context.scope; - const parameter = new ParameterReflection(signature, node.symbol.name, ReflectionKind.Parameter); + if (!node.symbol) { + return; + } + + const parameter = new ParameterReflection(node.symbol.name, ReflectionKind.Parameter, signature); context.registerReflection(parameter, node); context.withScope(parameter, () => { if (_ts.isBindingPattern(node.name)) { diff --git a/src/lib/converter/factories/reference.ts b/src/lib/converter/factories/reference.ts index 76f907ae9..c1b1b3c53 100644 --- a/src/lib/converter/factories/reference.ts +++ b/src/lib/converter/factories/reference.ts @@ -11,10 +11,14 @@ import { Context } from '../context'; * @param includeParent Should the name of the parent be provided within the fallback name? * @returns A new reference type instance pointing to the given symbol. */ -export function createReferenceType(context: Context, symbol: ts.Symbol, includeParent?: boolean): ReferenceType { +export function createReferenceType(context: Context, symbol: ts.Symbol | undefined, includeParent?: boolean): ReferenceType | undefined { + if (!symbol) { + return; + } + const checker = context.checker; - const id = context.getSymbolID(symbol); - let name = checker.symbolToString(symbol); + const id = context.getSymbolID(symbol)!; + let name = checker.symbolToString(symbol); if (includeParent && symbol.parent) { name = checker.symbolToString(symbol.parent) + '.' + name; diff --git a/src/lib/converter/factories/signature.ts b/src/lib/converter/factories/signature.ts index dd8f74eff..d7059ce5a 100644 --- a/src/lib/converter/factories/signature.ts +++ b/src/lib/converter/factories/signature.ts @@ -21,7 +21,7 @@ export function createSignature(context: Context, node: ts.SignatureDeclaration, throw new Error('Expected container reflection.'); } - const signature = new SignatureReflection(container, name, kind); + const signature = new SignatureReflection(name, kind, container); context.registerReflection(signature, node); context.withScope(signature, node.typeParameters, true, () => { node.parameters.forEach((parameter: ts.ParameterDeclaration) => { @@ -46,18 +46,18 @@ export function createSignature(context: Context, node: ts.SignatureDeclaration, * @param node The signature declaration whose return type should be determined. * @returns The return type reflection of the given signature. */ -function extractSignatureType(context: Context, node: ts.SignatureDeclaration): Type { +function extractSignatureType(context: Context, node: ts.SignatureDeclaration): Type | undefined { const checker = context.checker; if (node.kind & ts.SyntaxKind.CallSignature || node.kind & ts.SyntaxKind.CallExpression) { try { const signature = checker.getSignatureFromDeclaration(node); + // This is essentially what checker.getReturnTypeOfSignature will do, but doing it ourselves avoids type errors. + if (!signature) { + throw new Error('Failed to retrieve signature for node.'); + } return context.converter.convertType(context, node.type, checker.getReturnTypeOfSignature(signature)); } catch (error) {} } - if (node.type) { - return context.converter.convertType(context, node.type); - } else { - return context.converter.convertType(context, node); - } + return context.converter.convertType(context, node.type || node); } diff --git a/src/lib/converter/factories/type-parameter.ts b/src/lib/converter/factories/type-parameter.ts index 073c4f8f3..468089059 100644 --- a/src/lib/converter/factories/type-parameter.ts +++ b/src/lib/converter/factories/type-parameter.ts @@ -11,15 +11,18 @@ import { Converter } from '../converter'; * @param node The type parameter node that should be reflected. * @returns The newly created type parameter reflection. */ -export function createTypeParameter(context: Context, node: ts.TypeParameterDeclaration): TypeParameterType { - const typeParameter = new TypeParameterType(); - typeParameter.name = node.symbol.name; +export function createTypeParameter(context: Context, node: ts.TypeParameterDeclaration): TypeParameterType | undefined { + if (!node.symbol) { + return; + } + + const typeParameter = new TypeParameterType(node.symbol.name); if (node.constraint) { typeParameter.constraint = context.converter.convertType(context, node.constraint); } const reflection = context.scope; - const typeParameterReflection = new TypeParameterReflection(reflection, typeParameter); + const typeParameterReflection = new TypeParameterReflection(typeParameter, reflection); if (!reflection.typeParameters) { reflection.typeParameters = []; diff --git a/src/lib/converter/nodes/accessor.ts b/src/lib/converter/nodes/accessor.ts index 2b44d57ef..8509f6c46 100644 --- a/src/lib/converter/nodes/accessor.ts +++ b/src/lib/converter/nodes/accessor.ts @@ -22,14 +22,14 @@ export class AccessorConverter extends ConverterNodeComponent { if (node.kind === ts.SyntaxKind.GetAccessor) { - accessor.getSignature = createSignature(context, node, '__get', ReflectionKind.GetSignature); + accessor!.getSignature = createSignature(context, node, '__get', ReflectionKind.GetSignature); } else { - accessor.setSignature = createSignature(context, node, '__set', ReflectionKind.SetSignature); + accessor!.setSignature = createSignature(context, node, '__set', ReflectionKind.SetSignature); } }); diff --git a/src/lib/converter/nodes/alias.ts b/src/lib/converter/nodes/alias.ts index b00796442..5bfc56c32 100644 --- a/src/lib/converter/nodes/alias.ts +++ b/src/lib/converter/nodes/alias.ts @@ -21,11 +21,11 @@ export class AliasConverter extends ConverterNodeComponent { - alias.type = this.owner.convertType(context, node.type, context.getTypeAtLocation(node.type)); + alias!.type = this.owner.convertType(context, node.type, context.getTypeAtLocation(node.type)); }); return alias; diff --git a/src/lib/converter/nodes/block.ts b/src/lib/converter/nodes/block.ts index aa3d6a365..dbee4a65f 100644 --- a/src/lib/converter/nodes/block.ts +++ b/src/lib/converter/nodes/block.ts @@ -7,7 +7,7 @@ import { Component, ConverterNodeComponent } from '../components'; import { Option } from '../../utils/component'; import { ParameterType } from '../../utils/options/declaration'; -const prefered: ts.SyntaxKind[] = [ +const preferred: ts.SyntaxKind[] = [ ts.SyntaxKind.ClassDeclaration, ts.SyntaxKind.InterfaceDeclaration, ts.SyntaxKind.EnumDeclaration @@ -29,7 +29,7 @@ export class BlockConverter extends ConverterNodeComponent { if (this.mode === SourceFileMode.Modules) { result = createDeclaration(context, node, ReflectionKind.ExternalModule, node.fileName); context.withScope(result, () => { this.convertStatements(context, node); - result.setFlag(ReflectionFlag.Exported); + result!.setFlag(ReflectionFlag.Exported); }); } else { this.convertStatements(context, node); @@ -87,7 +87,7 @@ export class BlockConverter extends ConverterNodeComponent { - if (prefered.indexOf(statement.kind) !== -1) { + if (preferred.indexOf(statement.kind) !== -1) { this.owner.convertNode(context, statement); } else { statements.push(statement); diff --git a/src/lib/converter/nodes/class.ts b/src/lib/converter/nodes/class.ts index b30f30d4a..5d84cc8e8 100644 --- a/src/lib/converter/nodes/class.ts +++ b/src/lib/converter/nodes/class.ts @@ -23,8 +23,8 @@ export class ClassConverter extends ConverterNodeComponent * @param node The class declaration node that should be analyzed. * @return The resulting reflection or NULL. */ - convert(context: Context, node: ts.ClassDeclaration): Reflection { - let reflection: DeclarationReflection; + convert(context: Context, node: ts.ClassDeclaration): Reflection | undefined { + let reflection: DeclarationReflection | undefined; if (context.isInherit && context.inheritParent === node) { reflection = context.scope; } else { @@ -54,10 +54,13 @@ export class ClassConverter extends ConverterNodeComponent if (baseType) { const type = context.getTypeAtLocation(baseType); if (!context.isInherit) { - if (!reflection.extendedTypes) { - reflection.extendedTypes = []; + if (!reflection!.extendedTypes) { + reflection!.extendedTypes = []; + } + const convertedType = this.owner.convertType(context, baseType, type); + if (convertedType) { + reflection!.extendedTypes!.push(convertedType); } - reflection.extendedTypes.push(this.owner.convertType(context, baseType, type)); } if (type && type.symbol) { @@ -68,14 +71,9 @@ export class ClassConverter extends ConverterNodeComponent } const implementedTypes = _ts.getClassImplementsHeritageClauseElements(node); - if (implementedTypes) { - implementedTypes.forEach((implementedType) => { - if (!reflection.implementedTypes) { - reflection.implementedTypes = []; - } - - reflection.implementedTypes.push(this.owner.convertType(context, implementedType)); - }); + if (implementedTypes && implementedTypes.length) { + const implemented = this.owner.convertTypes(context, implementedTypes); + reflection!.implementedTypes = (reflection!.implementedTypes || []).concat(implemented); } }); diff --git a/src/lib/converter/nodes/constructor.ts b/src/lib/converter/nodes/constructor.ts index 341926b6f..01bd771ef 100644 --- a/src/lib/converter/nodes/constructor.ts +++ b/src/lib/converter/nodes/constructor.ts @@ -23,7 +23,7 @@ export class ConstructorConverter extends ConverterNodeComponent { - if (!hasBody || !method.signatures) { + if (!hasBody || !method!.signatures) { const name = 'new ' + parent.name; const signature = createSignature(context, node, name, ReflectionKind.ConstructorSignature); // If no return type defined, use the parent one. if (!node.type) { signature.type = new ReferenceType(parent.name, ReferenceType.SYMBOL_ID_RESOLVED, parent); } - method.signatures = method.signatures || []; - method.signatures.push(signature); + method!.signatures = method!.signatures || []; + method!.signatures!.push(signature); } else { - context.trigger(Converter.EVENT_FUNCTION_IMPLEMENTATION, method, node); + context.trigger(Converter.EVENT_FUNCTION_IMPLEMENTATION, method!, node); } }); @@ -60,7 +60,7 @@ export class ConstructorConverter extends ConverterNodeComponent { * @param node The enumeration declaration node that should be analyzed. * @return The resulting reflection or NULL. */ - convert(context: Context, node: ts.EnumDeclaration): Reflection { + convert(context: Context, node: ts.EnumDeclaration): Reflection | undefined { const enumeration = createDeclaration(context, node, ReflectionKind.Enum); context.withScope(enumeration, () => { @@ -43,7 +43,7 @@ export class EnumConverter extends ConverterNodeComponent { * @param node The enumeration member node that should be analyzed. * @return The resulting reflection or NULL. */ - private convertMember(context: Context, node: ts.EnumMember): Reflection { + private convertMember(context: Context, node: ts.EnumMember): Reflection | undefined { const member = createDeclaration(context, node, ReflectionKind.EnumMember); if (member) { member.defaultValue = convertDefaultValue(node); diff --git a/src/lib/converter/nodes/export.ts b/src/lib/converter/nodes/export.ts index 66ca60426..dab11eac2 100644 --- a/src/lib/converter/nodes/export.ts +++ b/src/lib/converter/nodes/export.ts @@ -14,7 +14,7 @@ export class ExportConverter extends ConverterNodeComponent ]; convert(context: Context, node: ts.ExportAssignment): Reflection { - let symbol: ts.Symbol = undefined; + let symbol: ts.Symbol | undefined; // default export if (node.symbol && (node.symbol.flags & ts.SymbolFlags.Alias) === ts.SymbolFlags.Alias) { @@ -29,7 +29,7 @@ export class ExportConverter extends ConverterNodeComponent if (!declaration.symbol) { return; } - const id = project.symbolMapping[context.getSymbolID(declaration.symbol)]; + const id = project.symbolMapping[context.getSymbolID(declaration.symbol)!]; if (!id) { return; } diff --git a/src/lib/converter/nodes/function.ts b/src/lib/converter/nodes/function.ts index 8e4706d74..45e8b56be 100644 --- a/src/lib/converter/nodes/function.ts +++ b/src/lib/converter/nodes/function.ts @@ -24,7 +24,7 @@ export class FunctionConverter extends ConverterNodeComponent { - if (!hasBody || !method.signatures) { - const signature = createSignature(context, node, method.name, ReflectionKind.CallSignature); - if (!method.signatures) { - method.signatures = []; + if (!hasBody || !method!.signatures) { + const signature = createSignature(context, node, method!.name, ReflectionKind.CallSignature); + if (!method!.signatures) { + method!.signatures = []; } - method.signatures.push(signature); + method!.signatures!.push(signature); } else { - context.trigger(Converter.EVENT_FUNCTION_IMPLEMENTATION, method, node); + context.trigger(Converter.EVENT_FUNCTION_IMPLEMENTATION, method!, node); } }); diff --git a/src/lib/converter/nodes/interface.ts b/src/lib/converter/nodes/interface.ts index 2ea440080..e03560435 100644 --- a/src/lib/converter/nodes/interface.ts +++ b/src/lib/converter/nodes/interface.ts @@ -22,8 +22,8 @@ export class InterfaceConverter extends ConverterNodeComponent context.scope; } else { @@ -42,10 +42,13 @@ export class InterfaceConverter extends ConverterNodeComponent { const type = context.getTypeAtLocation(baseType); if (!context.isInherit) { - if (!reflection.extendedTypes) { - reflection.extendedTypes = []; + if (!reflection!.extendedTypes) { + reflection!.extendedTypes = []; + } + const convertedType = this.owner.convertType(context, baseType, type); + if (convertedType) { + reflection!.extendedTypes!.push(convertedType); } - reflection.extendedTypes.push(this.owner.convertType(context, baseType, type)); } if (type && type.symbol) { diff --git a/src/lib/converter/nodes/module.ts b/src/lib/converter/nodes/module.ts index 8d3aee7b0..0b55c944d 100644 --- a/src/lib/converter/nodes/module.ts +++ b/src/lib/converter/nodes/module.ts @@ -21,14 +21,14 @@ export class ModuleConverter extends ConverterNodeComponent { if (parent instanceof ProjectReflection && !context.isDeclaration && (!module || module.valueOf() === ts.ModuleKind.None.valueOf())) { - reflection.setFlag(ReflectionFlag.Exported); + reflection!.setFlag(ReflectionFlag.Exported); } if (node.body) { diff --git a/src/lib/converter/nodes/variable.ts b/src/lib/converter/nodes/variable.ts index 4816e1ed8..74795f366 100644 --- a/src/lib/converter/nodes/variable.ts +++ b/src/lib/converter/nodes/variable.ts @@ -35,26 +35,27 @@ export class VariableConverter extends ConverterNodeComponent node.initializer)) { - variable.kind = ReflectionKind.ObjectLiteral; - variable.type = new IntrinsicType('object'); + variable!.kind = ReflectionKind.ObjectLiteral; + variable!.type = new IntrinsicType('object'); this.owner.convertNode(context, node.initializer); } break; default: - variable.defaultValue = convertDefaultValue(node); + variable!.defaultValue = convertDefaultValue(node); } } - if (variable.kind === kind || variable.kind === ReflectionKind.Event) { + if (variable!.kind === kind || variable!.kind === ReflectionKind.Event) { if (isBindingPattern) { - variable.type = this.owner.convertType(context, node.name); + variable!.type = this.owner.convertType(context, node.name); } else { - variable.type = this.owner.convertType(context, node.type, context.getTypeAtLocation(node)); + variable!.type = this.owner.convertType(context, node.type, context.getTypeAtLocation(node)); } } }); diff --git a/src/lib/converter/plugins/CategoryPlugin.ts b/src/lib/converter/plugins/CategoryPlugin.ts index 3c4dea33d..77f18fe8e 100644 --- a/src/lib/converter/plugins/CategoryPlugin.ts +++ b/src/lib/converter/plugins/CategoryPlugin.ts @@ -16,7 +16,7 @@ export class CategoryPlugin extends ConverterComponent { /** * Define the sort order of categories. By default, sort alphabetically. */ - static WEIGHTS = []; + static WEIGHTS: string[] = []; /** * Create a new CategoryPlugin instance. diff --git a/src/lib/converter/plugins/CommentPlugin.ts b/src/lib/converter/plugins/CommentPlugin.ts index 4082b0479..1e4d7d527 100644 --- a/src/lib/converter/plugins/CommentPlugin.ts +++ b/src/lib/converter/plugins/CommentPlugin.ts @@ -38,13 +38,14 @@ interface ModuleComment { export class CommentPlugin extends ConverterComponent { /** * List of discovered module comments. + * Defined in this.onBegin */ - private comments: {[id: number]: ModuleComment}; + private comments!: {[id: number]: ModuleComment}; /** * List of hidden reflections. */ - private hidden: Reflection[]; + private hidden?: Reflection[]; /** * Create a new CommentPlugin instance. @@ -134,7 +135,7 @@ export class CommentPlugin extends ConverterComponent { * @param node The node that is currently processed if available. */ private onCreateTypeParameter(context: Context, reflection: TypeParameterReflection, node?: ts.Node) { - const comment = reflection.parent.comment; + const comment = reflection.parent && reflection.parent.comment; if (comment) { let tag = comment.getTag('typeparam', reflection.name); if (!tag) { @@ -146,7 +147,8 @@ export class CommentPlugin extends ConverterComponent { if (tag) { reflection.comment = new Comment(tag.text); - comment.tags.splice(comment.tags.indexOf(tag), 1); + // comment.tags must be set if we found a tag. + comment.tags!.splice(comment.tags!.indexOf(tag), 1); } } } @@ -247,14 +249,14 @@ export class CommentPlugin extends ConverterComponent { if (signatures.length) { const comment = reflection.comment; if (comment && comment.hasTag('returns')) { - comment.returns = comment.getTag('returns').text; + comment.returns = comment.getTag('returns')!.text; CommentPlugin.removeTags(comment, 'returns'); } signatures.forEach((signature) => { let childComment = signature.comment; if (childComment && childComment.hasTag('returns')) { - childComment.returns = childComment.getTag('returns').text; + childComment.returns = childComment.getTag('returns')!.text; CommentPlugin.removeTags(childComment, 'returns'); } @@ -270,7 +272,7 @@ export class CommentPlugin extends ConverterComponent { if (signature.parameters) { signature.parameters.forEach((parameter) => { - let tag: CommentTag; + let tag: CommentTag | undefined; if (childComment) { tag = childComment.getTag('param', parameter.name); } @@ -296,7 +298,7 @@ export class CommentPlugin extends ConverterComponent { * @param comment The comment that should be modified. * @param tagName The name of the that that should be removed. */ - static removeTags(comment: Comment, tagName: string) { + static removeTags(comment: Comment | undefined, tagName: string) { if (!comment || !comment.tags) { return; } @@ -338,9 +340,9 @@ export class CommentPlugin extends ConverterComponent { break; case TraverseProperty.Parameters: if (( reflection.parent).parameters) { - const index = ( reflection.parent).parameters.indexOf( reflection); + const index = ( reflection.parent).parameters!.indexOf( reflection); if (index !== -1) { - ( reflection.parent).parameters.splice(index, 1); + ( reflection.parent).parameters!.splice(index, 1); } } break; diff --git a/src/lib/converter/plugins/DecoratorPlugin.ts b/src/lib/converter/plugins/DecoratorPlugin.ts index 09fd8bfb9..526da154e 100644 --- a/src/lib/converter/plugins/DecoratorPlugin.ts +++ b/src/lib/converter/plugins/DecoratorPlugin.ts @@ -12,7 +12,10 @@ import { Context } from '../context'; */ @Component({name: 'decorator'}) export class DecoratorPlugin extends ConverterComponent { - private usages: {[symbolID: number]: ReferenceType[]}; + /** + * Defined in this.onBegin + */ + private usages!: {[symbolID: number]: ReferenceType[]}; /** * Create a new ImplementsPlugin instance. @@ -33,7 +36,7 @@ export class DecoratorPlugin extends ConverterComponent { * @param signature The signature definition of the decorator being used. * @returns An object describing the decorator parameters, */ - private extractArguments(args: ts.NodeArray, signature: ts.Signature): any { + private extractArguments(args: ts.NodeArray, signature: ts.Signature): { [name: string]: string | string[] } { const result = {}; args.forEach((arg: ts.Expression, index: number) => { if (index < signature.parameters.length) { @@ -71,7 +74,7 @@ export class DecoratorPlugin extends ConverterComponent { return; } node.decorators.forEach((decorator: ts.Decorator) => { - let callExpression: ts.CallExpression; + let callExpression: ts.CallExpression | undefined; let identifier: ts.Expression; switch (decorator.expression.kind) { @@ -92,7 +95,7 @@ export class DecoratorPlugin extends ConverterComponent { const type = context.checker.getTypeAtLocation(identifier); if (type && type.symbol) { - const symbolID = context.getSymbolID(type.symbol); + const symbolID = context.getSymbolID(type.symbol)!; info.type = new ReferenceType(info.name, symbolID); if (callExpression && callExpression.arguments) { diff --git a/src/lib/converter/plugins/DeepCommentPlugin.ts b/src/lib/converter/plugins/DeepCommentPlugin.ts index ed7bd5d45..34c398666 100644 --- a/src/lib/converter/plugins/DeepCommentPlugin.ts +++ b/src/lib/converter/plugins/DeepCommentPlugin.ts @@ -50,7 +50,7 @@ export class DeepCommentPlugin extends ConverterComponent { while (target && !(target instanceof ProjectReflection)) { push(target); if (target.comment) { - let tag: CommentTag; + let tag: CommentTag | undefined; if (reflection instanceof TypeParameterReflection) { tag = target.comment.getTag('typeparam', reflection.name); if (!tag) { @@ -63,7 +63,8 @@ export class DeepCommentPlugin extends ConverterComponent { } if (tag) { - target.comment.tags.splice(target.comment.tags.indexOf(tag), 1); + // If we found a tag, comment.tags mus be set. + target.comment.tags!.splice(target.comment.tags!.indexOf(tag), 1); reflection.comment = new Comment('', tag.text); break; } diff --git a/src/lib/converter/plugins/DynamicModulePlugin.ts b/src/lib/converter/plugins/DynamicModulePlugin.ts index b251c9e45..12c1b68c3 100644 --- a/src/lib/converter/plugins/DynamicModulePlugin.ts +++ b/src/lib/converter/plugins/DynamicModulePlugin.ts @@ -21,7 +21,7 @@ export class DynamicModulePlugin extends ConverterComponent { /** * List of reflections whose name must be trimmed. */ - private reflections: Reflection[]; + private reflections!: Reflection[]; /** * Create a new DynamicModuleHandler instance. diff --git a/src/lib/converter/plugins/GitHubPlugin.ts b/src/lib/converter/plugins/GitHubPlugin.ts index 846ed3123..99bfae67f 100644 --- a/src/lib/converter/plugins/GitHubPlugin.ts +++ b/src/lib/converter/plugins/GitHubPlugin.ts @@ -31,12 +31,12 @@ class Repository { /** * The user/organisation name of this repository on GitHub. */ - gitHubUser: string; + gitHubUser?: string; /** * The project name of this repository on GitHub. */ - gitHubProject: string; + gitHubProject?: string; /** * Create a new Repository instance. @@ -50,7 +50,7 @@ class Repository { let out = ShellJS.exec('git ls-remote --get-url', {silent: true}); if (out.code === 0) { - let url: RegExpExecArray; + let url: RegExpExecArray | null; const remotes = out.stdout.split('\n'); for (let i = 0, c = remotes.length; i < c; i++) { url = /github\.com[:\/]([^\/]+)\/(.*)/.exec(remotes[i]); @@ -100,9 +100,9 @@ class Repository { * @param fileName The file whose GitHub URL should be determined. * @returns An url pointing to the web preview of the given file or NULL. */ - getGitHubURL(fileName: string): string { + getGitHubURL(fileName: string): string | undefined { if (!this.gitHubUser || !this.gitHubProject || !this.contains(fileName)) { - return null; + return; } return [ @@ -122,15 +122,15 @@ class Repository { * creates a new instance of [[Repository]]. * * @param path The potential repository root. - * @returns A new instance of [[Repository]] or NULL. + * @returns A new instance of [[Repository]] or undefined. */ - static tryCreateRepository(path: string, gitRevision: string): Repository { + static tryCreateRepository(path: string, gitRevision: string): Repository | undefined { ShellJS.pushd(path); const out = ShellJS.exec('git rev-parse --show-toplevel', {silent: true}); ShellJS.popd(); if (!out || out.code !== 0) { - return null; + return; } return new Repository(BasePath.normalize(out.stdout.replace('\n', '')), gitRevision); } @@ -157,7 +157,7 @@ export class GitHubPlugin extends ConverterComponent { help: 'Use specified revision instead of the last revision for linking to GitHub source files.', type: ParameterType.String }) - gitRevision: string; + gitRevision!: string; /** * Create a new GitHubHandler instance. @@ -175,14 +175,14 @@ export class GitHubPlugin extends ConverterComponent { * Check whether the given file is placed inside a repository. * * @param fileName The name of the file a repository should be looked for. - * @returns The found repository info or NULL. + * @returns The found repository info or undefined. */ - private getRepository(fileName: string): Repository { + private getRepository(fileName: string): Repository | undefined { // Check for known non-repositories const dirName = Path.dirname(fileName); for (let i = 0, c = this.ignoredPaths.length; i < c; i++) { if (this.ignoredPaths[i] === dirName) { - return null; + return; } } @@ -208,8 +208,6 @@ export class GitHubPlugin extends ConverterComponent { for (let i = segments.length; i > 0; i--) { this.ignoredPaths.push(segments.slice(0, i).join('/')); } - - return null; } /** diff --git a/src/lib/converter/plugins/GroupPlugin.ts b/src/lib/converter/plugins/GroupPlugin.ts index 8222d814d..3bb1c032c 100644 --- a/src/lib/converter/plugins/GroupPlugin.ts +++ b/src/lib/converter/plugins/GroupPlugin.ts @@ -158,7 +158,7 @@ export class GroupPlugin extends ConverterComponent { allExternal = child.flags.isExternal && allExternal; if (child instanceof DeclarationReflection) { - allInherited = child.inheritedFrom && allInherited; + allInherited = !!child.inheritedFrom && allInherited; } else { allInherited = false; } diff --git a/src/lib/converter/plugins/ImplementsPlugin.ts b/src/lib/converter/plugins/ImplementsPlugin.ts index d1afab481..baaf45c2b 100644 --- a/src/lib/converter/plugins/ImplementsPlugin.ts +++ b/src/lib/converter/plugins/ImplementsPlugin.ts @@ -34,7 +34,7 @@ export class ImplementsPlugin extends ConverterComponent { return; } - let classMember: DeclarationReflection; + let classMember: DeclarationReflection | undefined; if (!classReflection.children) { return; @@ -64,7 +64,7 @@ export class ImplementsPlugin extends ConverterComponent { if (interfaceMember.kindOf(ReflectionKind.FunctionOrMethod) && interfaceMember.signatures && classMember.signatures) { interfaceMember.signatures.forEach((interfaceSignature: SignatureReflection) => { const interfaceParameters = interfaceSignature.getParameterTypes(); - classMember.signatures.forEach((classSignature: SignatureReflection) => { + (classMember!.signatures || []).forEach((classSignature: SignatureReflection) => { if (Type.isTypeListEqual(interfaceParameters, classSignature.getParameterTypes())) { classSignature.implementationOf = new ReferenceType(interfaceMemberName, ReferenceType.SYMBOL_ID_RESOLVED, interfaceSignature); this.copyComment(classSignature, interfaceSignature); @@ -89,7 +89,7 @@ export class ImplementsPlugin extends ConverterComponent { source instanceof SignatureReflection && source.parameters) { for (let index = 0, count = target.parameters.length; index < count; index++) { if (target.parameters[index].comment) { - target.parameters[index].comment.copyFrom(source.parameters[index].comment); + target.parameters[index].comment!.copyFrom(source.parameters[index].comment!); } } } diff --git a/src/lib/converter/plugins/PackagePlugin.ts b/src/lib/converter/plugins/PackagePlugin.ts index eff18320c..f48fbb6df 100644 --- a/src/lib/converter/plugins/PackagePlugin.ts +++ b/src/lib/converter/plugins/PackagePlugin.ts @@ -22,27 +22,27 @@ export class PackagePlugin extends ConverterComponent { name: 'readme', help: 'Path to the readme file that should be displayed on the index page. Pass `none` to disable the index page and start the documentation on the globals page.' }) - readme: string; + readme!: string; /** * The file name of the found readme.md file. */ - private readmeFile: string; + private readmeFile?: string; /** * The file name of the found package.json file. */ - private packageFile: string; + private packageFile?: string; /** * List of directories the handler already inspected. */ - private visited: string[]; + private visited!: string[]; /** * Should the readme file be ignored? */ - private noReadmeFile: boolean; + private noReadmeFile?: boolean; /** * Create a new PackageHandler instance. @@ -61,9 +61,9 @@ export class PackagePlugin extends ConverterComponent { * @param context The context object describing the current state the converter is in. */ private onBegin(context: Context) { - this.readmeFile = null; - this.packageFile = null; - this.visited = []; + this.readmeFile = undefined; + this.packageFile = undefined; + this.visited = []; let readme = this.readme; this.noReadmeFile = (readme === 'none'); diff --git a/src/lib/converter/plugins/SourcePlugin.ts b/src/lib/converter/plugins/SourcePlugin.ts index 4217c2312..2f1270acb 100644 --- a/src/lib/converter/plugins/SourcePlugin.ts +++ b/src/lib/converter/plugins/SourcePlugin.ts @@ -108,7 +108,7 @@ export class SourcePlugin extends ConverterComponent { file.reflections.push(reflection); } - reflection.sources.push({ + reflection.sources!.push({ file: file, fileName: fileName, line: position.line + 1, diff --git a/src/lib/converter/plugins/TypePlugin.ts b/src/lib/converter/plugins/TypePlugin.ts index 5a1b29512..943e4d1c6 100644 --- a/src/lib/converter/plugins/TypePlugin.ts +++ b/src/lib/converter/plugins/TypePlugin.ts @@ -65,7 +65,7 @@ export class TypePlugin extends ConverterComponent { }); } - function walk(types: Type[], callback: {(declaration: DeclarationReflection): void}) { + function walk(types: Type[] | undefined, callback: {(declaration: DeclarationReflection): void}) { if (!types) { return; } @@ -80,7 +80,7 @@ export class TypePlugin extends ConverterComponent { }); } - function resolveTypes(reflection: Reflection, types: Type[]) { + function resolveTypes(reflection: Reflection, types?: Type[]) { if (!types) { return; } @@ -132,8 +132,8 @@ export class TypePlugin extends ConverterComponent { }); } - let root: DeclarationHierarchy; - let hierarchy: DeclarationHierarchy; + let root!: DeclarationHierarchy; + let hierarchy!: DeclarationHierarchy; function push(types: Type[]) { const level: DeclarationHierarchy = {types: types}; if (hierarchy) { diff --git a/src/lib/converter/types/alias.ts b/src/lib/converter/types/alias.ts index e7466bf8b..bb143f1eb 100644 --- a/src/lib/converter/types/alias.ts +++ b/src/lib/converter/types/alias.ts @@ -75,7 +75,7 @@ export class AliasConverter extends ConverterTypeComponent implements TypeNodeCo const result = new ReferenceType(name, ReferenceType.SYMBOL_ID_RESOLVE_BY_NAME); if (node.typeArguments) { - result.typeArguments = node.typeArguments.map(n => this.owner.convertType(context, n)); + result.typeArguments = this.owner.convertTypes(context, node.typeArguments); } return result; diff --git a/src/lib/converter/types/array.ts b/src/lib/converter/types/array.ts index 81db9c9ce..e56dcb0de 100644 --- a/src/lib/converter/types/array.ts +++ b/src/lib/converter/types/array.ts @@ -39,10 +39,11 @@ export class ArrayConverter extends ConverterTypeComponent implements TypeConver * @param node The array type node that should be converted. * @returns The type reflection representing the given array type node. */ - convertNode(context: Context, node: ts.ArrayTypeNode): Type { + convertNode(context: Context, node: ts.ArrayTypeNode): Type | undefined { const result = this.owner.convertType(context, node.elementType); - - return new ArrayType(result); + if (result) { + return new ArrayType(result); + } } /** @@ -59,9 +60,10 @@ export class ArrayConverter extends ConverterTypeComponent implements TypeConver * @param type The type reference that should be converted. * @returns The type reflection representing the given type reference. */ - convertType(context: Context, type: ts.TypeReference): Type { - const result = this.owner.convertType(context, null, type.typeArguments[0]); - - return new ArrayType(result); + convertType(context: Context, type: ts.TypeReference): Type | undefined { + const result = this.owner.convertType(context, undefined, type.typeArguments && type.typeArguments[0]); + if (result) { + return new ArrayType(result); + } } } diff --git a/src/lib/converter/types/binding-array.ts b/src/lib/converter/types/binding-array.ts index ffec609a1..d323d88a9 100644 --- a/src/lib/converter/types/binding-array.ts +++ b/src/lib/converter/types/binding-array.ts @@ -21,12 +21,7 @@ export class BindingArrayConverter extends ConverterTypeComponent implements Typ * @returns The type reflection representing the given binding pattern. */ convertNode(context: Context, node: ts.BindingPattern): Type { - const types: Type[] = []; - - (node.elements as ts.NodeArray).forEach((element) => { - types.push(this.owner.convertType(context, element)); - }); - + const types = this.owner.convertTypes(context, node.elements); return new TupleType(types); } } diff --git a/src/lib/converter/types/binding-object.ts b/src/lib/converter/types/binding-object.ts index a9dc66505..56732dd58 100644 --- a/src/lib/converter/types/binding-object.ts +++ b/src/lib/converter/types/binding-object.ts @@ -22,12 +22,9 @@ export class BindingObjectConverter extends ConverterTypeComponent implements Ty * @returns The type reflection representing the given binding pattern. */ convertNode(context: Context, node: ts.BindingPattern): Type { - const declaration = new DeclarationReflection(); - declaration.kind = ReflectionKind.TypeLiteral; - declaration.name = '__type'; - declaration.parent = context.scope; + const declaration = new DeclarationReflection('__type', ReflectionKind.TypeLiteral, context.scope); - context.registerReflection(declaration, null); + context.registerReflection(declaration); context.trigger(Converter.EVENT_CREATE_DECLARATION, declaration, node); context.withScope(declaration, () => { (node.elements as ts.NodeArray).forEach((element) => { diff --git a/src/lib/converter/types/enum.ts b/src/lib/converter/types/enum.ts index f2d79c513..80ed322c7 100644 --- a/src/lib/converter/types/enum.ts +++ b/src/lib/converter/types/enum.ts @@ -28,7 +28,7 @@ export class EnumConverter extends ConverterTypeComponent implements TypeTypeCon * @param type The enumeration type that should be converted. * @returns The type reflection representing the given enumeration type. */ - convertType(context: Context, type: ts.Type): Type { + convertType(context: Context, type: ts.Type): Type | undefined { return createReferenceType(context, type.symbol); } } diff --git a/src/lib/converter/types/reference.ts b/src/lib/converter/types/reference.ts index b6e637c61..f10fc4b2a 100644 --- a/src/lib/converter/types/reference.ts +++ b/src/lib/converter/types/reference.ts @@ -44,7 +44,7 @@ export class ReferenceConverter extends ConverterTypeComponent implements TypeNo * @param type The type of the type reference node. * @returns The type reflection representing the given reference node. */ - convertNode(context: Context, node: ts.TypeReferenceNode, type: ts.TypeReference): Type { + convertNode(context: Context, node: ts.TypeReferenceNode, type: ts.TypeReference): Type | undefined { if (!type.symbol) { return new IntrinsicType('Object'); } else if (type.symbol.declarations && (type.symbol.flags & ts.SymbolFlags.TypeLiteral || type.symbol.flags & ts.SymbolFlags.ObjectLiteral)) { @@ -52,8 +52,8 @@ export class ReferenceConverter extends ConverterTypeComponent implements TypeNo } const result = createReferenceType(context, type.symbol); - if (node.typeArguments) { - result.typeArguments = node.typeArguments.map((n) => this.owner.convertType(context, n)); + if (result && node.typeArguments) { + result.typeArguments = this.owner.convertTypes(context, node.typeArguments); } return result; @@ -73,7 +73,7 @@ export class ReferenceConverter extends ConverterTypeComponent implements TypeNo * @param type The type reference that should be converted. * @returns The type reflection representing the given type reference. */ - convertType(context: Context, type: ts.TypeReference): Type { + convertType(context: Context, type: ts.TypeReference): Type | undefined { if (!type.symbol) { return new IntrinsicType('Object'); } else if (type.symbol.declarations && (type.symbol.flags & ts.SymbolFlags.TypeLiteral || type.symbol.flags & ts.SymbolFlags.ObjectLiteral)) { @@ -81,8 +81,8 @@ export class ReferenceConverter extends ConverterTypeComponent implements TypeNo } const result = createReferenceType(context, type.symbol); - if (type.typeArguments) { - result.typeArguments = type.typeArguments.map((t) => this.owner.convertType(context, null, t)); + if (result && type.typeArguments) { + result.typeArguments = this.owner.convertTypes(context, undefined, type.typeArguments); } return result; @@ -110,24 +110,23 @@ export class ReferenceConverter extends ConverterTypeComponent implements TypeNo * implicitly generated by TypeScript won't have a corresponding node. * @returns A type reflection representing the given type literal. */ - private convertLiteral(context: Context, symbol: ts.Symbol, node?: ts.Node): Type { + private convertLiteral(context: Context, symbol: ts.Symbol, node?: ts.Node): Type | undefined { for (let declaration of symbol.declarations) { if (context.visitStack.indexOf(declaration) !== -1) { if (declaration.kind === ts.SyntaxKind.TypeLiteral || declaration.kind === ts.SyntaxKind.ObjectLiteralExpression) { - return createReferenceType(context, declaration.parent.symbol); + // TODO: Check if this type assertion is safe and document. + return createReferenceType(context, declaration.parent.symbol!); } else { - return createReferenceType(context, declaration.symbol); + // TODO: Check if this type assertion is safe and document. + return createReferenceType(context, declaration.symbol!); } } } - const declaration = new DeclarationReflection(); - declaration.kind = ReflectionKind.TypeLiteral; - declaration.name = '__type'; - declaration.parent = context.scope; + const declaration = new DeclarationReflection('__type', ReflectionKind.TypeLiteral, context.scope); - context.registerReflection(declaration, null, symbol); + context.registerReflection(declaration, undefined, symbol); context.trigger(Converter.EVENT_CREATE_DECLARATION, declaration, node); context.withScope(declaration, () => { symbol.declarations.forEach((node) => { diff --git a/src/lib/converter/types/tuple.ts b/src/lib/converter/types/tuple.ts index 1fc2152c6..dd343f882 100644 --- a/src/lib/converter/types/tuple.ts +++ b/src/lib/converter/types/tuple.ts @@ -34,13 +34,7 @@ export class TupleConverter extends ConverterTypeComponent implements TypeConver * @returns The type reflection representing the given tuple type node. */ convertNode(context: Context, node: ts.TupleTypeNode): TupleType { - let elements: Type[]; - if (node.elementTypes) { - elements = node.elementTypes.map((n) => this.owner.convertType(context, n)); - } else { - elements = []; - } - + const elements: Type[] = this.owner.convertTypes(context, node.elementTypes); return new TupleType(elements); } @@ -58,13 +52,7 @@ export class TupleConverter extends ConverterTypeComponent implements TypeConver * @returns The type reflection representing the given tuple type. */ convertType(context: Context, type: ts.TypeReference): TupleType { - let elements: Type[]; - if (type.typeArguments) { - elements = type.typeArguments.map((t) => this.owner.convertType(context, null, t)); - } else { - elements = []; - } - + const elements: Type[] = this.owner.convertTypes(context, undefined, type.typeArguments); return new TupleType(elements); } } diff --git a/src/lib/converter/types/type-operator.ts b/src/lib/converter/types/type-operator.ts index fa9bac6da..872b3551b 100644 --- a/src/lib/converter/types/type-operator.ts +++ b/src/lib/converter/types/type-operator.ts @@ -25,8 +25,10 @@ export class TypeOperatorConverter extends ConverterTypeComponent implements Typ * @param node The type operator node representing keys of a type. * @returns The type representing keys of a type. */ - convertNode(context: Context, node: ts.TypeOperatorNode): TypeOperatorType { + convertNode(context: Context, node: ts.TypeOperatorNode): TypeOperatorType | undefined { const target = this.owner.convertType(context, node.type); - return new TypeOperatorType(target); + if (target) { + return new TypeOperatorType(target); + } } } diff --git a/src/lib/converter/types/type-parameter.ts b/src/lib/converter/types/type-parameter.ts index 9180c7c01..04fd73f50 100644 --- a/src/lib/converter/types/type-parameter.ts +++ b/src/lib/converter/types/type-parameter.ts @@ -35,16 +35,14 @@ export class TypeParameterConverter extends ConverterTypeComponent implements Ty * @param node The type reference node representing a type parameter. * @returns The type reflection representing the given type parameter. */ - convertNode(context: Context, node: ts.TypeReferenceNode): Type { + convertNode(context: Context, node: ts.TypeReferenceNode): Type | undefined { if (node.typeName) { const name = _ts.getTextOfNode(node.typeName); if (context.typeParameters && context.typeParameters[name]) { return context.typeParameters[name].clone(); } - const result = new TypeParameterType(); - result.name = name; - return result; + return new TypeParameterType(name); } } } diff --git a/src/lib/converter/types/union-or-intersection.ts b/src/lib/converter/types/union-or-intersection.ts index e9e256b7d..05f059ad9 100644 --- a/src/lib/converter/types/union-or-intersection.ts +++ b/src/lib/converter/types/union-or-intersection.ts @@ -34,14 +34,9 @@ export class UnionOrIntersectionConverter extends ConverterTypeComponent impleme * @returns The type reflection representing the given union type node. */ convertNode(context: Context, node: ts.UnionOrIntersectionTypeNode): UnionType | IntersectionType { - let types: Type[] = []; - if (node.types) { - types = node.types.map((n) => this.owner.convertType(context, n)); - } else { - types = []; - } + const types: Type[] = this.owner.convertTypes(context, node.types); - return node.kind === ts.SyntaxKind.IntersectionType ? new IntersectionType(types) : new UnionType(types); + return ts.isIntersectionTypeNode(node) ? new IntersectionType(types) : new UnionType(types); } /** @@ -58,13 +53,7 @@ export class UnionOrIntersectionConverter extends ConverterTypeComponent impleme * @returns The type reflection representing the given union type. */ convertType(context: Context, type: ts.UnionOrIntersectionType): UnionType | IntersectionType { - let types: Type[]; - if (type && type.types) { - types = type.types.map((t) => this.owner.convertType(context, null, t)); - } else { - types = []; - } - - return !!(type.flags & ts.TypeFlags.Intersection) ? new IntersectionType(types) : new UnionType(types); + const types: Type[] = this.owner.convertTypes(context, undefined, type.types); + return type.flags & ts.TypeFlags.Intersection ? new IntersectionType(types) : new UnionType(types); } } diff --git a/src/lib/converter/types/unknown.ts b/src/lib/converter/types/unknown.ts index 25a9a507a..48ac4ec70 100644 --- a/src/lib/converter/types/unknown.ts +++ b/src/lib/converter/types/unknown.ts @@ -25,7 +25,7 @@ export class UnknownConverter extends ConverterTypeComponent implements TypeType * This is a type based converter with no node based equivalent. * * If no other converter is able to reflect a type, this converter will produce a - * reflection by utilising ts.typeToString() to generate a string representation of the + * reflection by utilizing ts.typeToString() to generate a string representation of the * given type. * * @param context The context object describing the current state the converter is in. diff --git a/src/lib/converter/utils/compiler-host.ts b/src/lib/converter/utils/compiler-host.ts index fd945b69c..90e9edd29 100644 --- a/src/lib/converter/utils/compiler-host.ts +++ b/src/lib/converter/utils/compiler-host.ts @@ -16,7 +16,7 @@ export class CompilerHost extends ConverterComponent implements ts.CompilerHost /** * The full path of the current directory. Result cache of [[getCurrentDirectory]]. */ - private currentDirectory: string; + private currentDirectory?: string; /** * Return an instance of ts.SourceFile representing the given file. @@ -28,8 +28,8 @@ export class CompilerHost extends ConverterComponent implements ts.CompilerHost * @param onError A callback that will be invoked if an error occurs. * @returns An instance of ts.SourceFile representing the given file. */ - getSourceFile(filename: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void): ts.SourceFile { - let text: string; + getSourceFile(filename: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void): ts.SourceFile | undefined { + let text: string | undefined; try { text = ts.sys.readFile(filename, this.application.options.getCompilerOptions().charset); } catch (e) { @@ -112,7 +112,7 @@ export class CompilerHost extends ConverterComponent implements ts.CompilerHost * @param fileName * @returns {string} */ - readFile(fileName: string): string { + readFile(fileName: string): string | undefined { return ts.sys.readFile(fileName); } diff --git a/src/lib/models/ReflectionCategory.ts b/src/lib/models/ReflectionCategory.ts index c40a35d40..1b26bf182 100644 --- a/src/lib/models/ReflectionCategory.ts +++ b/src/lib/models/ReflectionCategory.ts @@ -43,7 +43,7 @@ export class ReflectionCategory { private getAllChildrenHaveOwnDocument(): boolean { let onlyOwnDocuments = true; this.children.forEach((child) => { - onlyOwnDocuments = onlyOwnDocuments && child.hasOwnDocument; + onlyOwnDocuments = onlyOwnDocuments && !!child.hasOwnDocument; }); return onlyOwnDocuments; diff --git a/src/lib/models/ReflectionGroup.ts b/src/lib/models/ReflectionGroup.ts index 72e3b809b..88dbc9d26 100644 --- a/src/lib/models/ReflectionGroup.ts +++ b/src/lib/models/ReflectionGroup.ts @@ -27,7 +27,7 @@ export class ReflectionGroup { * A list of generated css classes that should be applied to representations of this * group in the generated markup. */ - cssClasses: string; + cssClasses?: string; /** * Do all children of this group have a separate document? @@ -40,27 +40,27 @@ export class ReflectionGroup { /** * Are all children inherited members? */ - allChildrenAreInherited: boolean; + allChildrenAreInherited?: boolean; /** * Are all children private members? */ - allChildrenArePrivate: boolean; + allChildrenArePrivate?: boolean; /** * Are all children private or protected members? */ - allChildrenAreProtectedOrPrivate: boolean; + allChildrenAreProtectedOrPrivate?: boolean; /** * Are all children external members? */ - allChildrenAreExternal: boolean; + allChildrenAreExternal?: boolean; /** * Are any children exported declarations? */ - someChildrenAreExported: boolean; + someChildrenAreExported?: boolean; /** * Create a new ReflectionGroup instance. @@ -81,7 +81,7 @@ export class ReflectionGroup { private getAllChildrenHaveOwnDocument(): boolean { let onlyOwnDocuments = true; this.children.forEach((child) => { - onlyOwnDocuments = onlyOwnDocuments && child.hasOwnDocument; + onlyOwnDocuments = onlyOwnDocuments && !!child.hasOwnDocument; }); return onlyOwnDocuments; diff --git a/src/lib/models/comments/comment.ts b/src/lib/models/comments/comment.ts index 3d50de841..6c0a78bd5 100644 --- a/src/lib/models/comments/comment.ts +++ b/src/lib/models/comments/comment.ts @@ -21,12 +21,12 @@ export class Comment { /** * The text of the ```@returns``` tag if present. */ - returns: string; + returns?: string; /** * All associated javadoc tags. */ - tags: CommentTag[]; + tags?: CommentTag[]; /** * Creates a new Comment instance. @@ -70,19 +70,12 @@ export class Comment { * * @param tagName The name of the tag to look for. * @param paramName An optional parameter name to look for. - * @returns The found tag or NULL. + * @returns The found tag or undefined. */ - getTag(tagName: string, paramName?: string): CommentTag { - if (!this.tags) { - return null; - } - for (let i = 0, c = this.tags.length; i < c; i++) { - const tag = this.tags[i]; - if (tag.tagName === tagName && (paramName === void 0 || tag.paramName === paramName)) { - return this.tags[i]; - } - } - return null; + getTag(tagName: string, paramName?: string): CommentTag | undefined { + return (this.tags || []).find(tag => { + return tag.tagName === tagName && (paramName === void 0 || tag.paramName === paramName); + }); } /** @@ -92,9 +85,9 @@ export class Comment { */ copyFrom(comment: Comment) { this.shortText = comment.shortText; - this.text = comment.text; - this.returns = comment.returns; - this.tags = comment.tags ? comment.tags.map((tag) => new CommentTag(tag.tagName, tag.paramName, tag.text)) : null; + this.text = comment.text; + this.returns = comment.returns; + this.tags = comment.tags ? comment.tags.map((tag) => new CommentTag(tag.tagName, tag.paramName, tag.text)) : undefined; } /** diff --git a/src/lib/models/reflections/abstract.ts b/src/lib/models/reflections/abstract.ts index f08defcf4..ebd331711 100644 --- a/src/lib/models/reflections/abstract.ts +++ b/src/lib/models/reflections/abstract.ts @@ -60,12 +60,13 @@ export enum ReflectionKind { ClassOrInterface = Class | Interface, VariableOrProperty = Variable | Property, - FunctionOrMethod = Function | Method, + FunctionOrMethod = ReflectionKind.Function | Method, SomeSignature = CallSignature | IndexSignature | ConstructorSignature | GetSignature | SetSignature, SomeModule = Module | ExternalModule } export enum ReflectionFlag { + None = 0, Private = 1, Protected = 2, Public = 4, @@ -95,75 +96,152 @@ const relevantFlags: ReflectionFlag[] = [ ReflectionFlag.Const ]; -export interface ReflectionFlags extends Array { - flags?: ReflectionFlag; +/** + * This must extend Array in order to work with Handlebar's each helper. + */ +export class ReflectionFlags extends Array { + private flags = ReflectionFlag.None; + + hasFlag(flag: ReflectionFlag) { + return (flag & this.flags) !== 0; + } /** * Is this a private member? */ - isPrivate?: boolean; + get isPrivate(): boolean { + return this.hasFlag(ReflectionFlag.Private); + } /** * Is this a protected member? */ - isProtected?: boolean; + get isProtected(): boolean { + return this.hasFlag(ReflectionFlag.Protected); + } /** * Is this a public member? */ - isPublic?: boolean; + get isPublic(): boolean { + return this.hasFlag(ReflectionFlag.Public); + } /** * Is this a static member? */ - isStatic?: boolean; + get isStatic(): boolean { + return this.hasFlag(ReflectionFlag.Static); + } /** * Is this member exported? */ - isExported?: boolean; + get isExported(): boolean { + return this.hasFlag(ReflectionFlag.Exported); + } /** * Is this a declaration from an external document? */ - isExternal?: boolean; + get isExternal(): boolean { + return this.hasFlag(ReflectionFlag.External); + } /** * Whether this reflection is an optional component or not. * * Applies to function parameters and object members. */ - isOptional?: boolean; + get isOptional(): boolean { + return this.hasFlag(ReflectionFlag.Optional); + } /** * Whether it's a rest parameter, like `foo(...params);`. */ - isRest?: boolean; + get isRest(): boolean { + return this.hasFlag(ReflectionFlag.Rest); + } - /** - * - */ - hasExportAssignment?: boolean; + get hasExportAssignment(): boolean { + return this.hasFlag(ReflectionFlag.ExportAssignment); + } - isConstructorProperty?: boolean; + get isConstructorProperty(): boolean { + return this.hasFlag(ReflectionFlag.ConstructorProperty); + } - isAbstract?: boolean; + get isAbstract(): boolean { + return this.hasFlag(ReflectionFlag.Abstract); + } + + get isConst() { + return this.hasFlag(ReflectionFlag.Const); + } - isConst?: boolean; + get isLet() { + return this.hasFlag(ReflectionFlag.Let); + } + + setFlag(flag: ReflectionFlag, set: boolean) { + switch (flag) { + case ReflectionFlag.Private: + this.setSingleFlag(ReflectionFlag.Private, set); + if (set) { + this.setFlag(ReflectionFlag.Protected, false); + this.setFlag(ReflectionFlag.Public, false); + } + break; + case ReflectionFlag.Protected: + this.setSingleFlag(ReflectionFlag.Protected, set); + if (set) { + this.setFlag(ReflectionFlag.Private, false); + this.setFlag(ReflectionFlag.Public, false); + } + break; + case ReflectionFlag.Public: + this.setSingleFlag(ReflectionFlag.Public, set); + if (set) { + this.setFlag(ReflectionFlag.Private, false); + this.setFlag(ReflectionFlag.Protected, false); + } + break; + case ReflectionFlag.Const: + case ReflectionFlag.Let: + this.setSingleFlag(flag, set); + this.setSingleFlag((ReflectionFlag.Let | ReflectionFlag.Const) ^ flag, !set); + default: + this.setSingleFlag(flag, set); + } + } - isLet?: boolean; + private setSingleFlag(flag: ReflectionFlag, set: boolean) { + const name = ReflectionFlag[flag].replace(/(.)([A-Z])/g, (m, a, b) => a + ' ' + b.toLowerCase()); + if (!set && this.hasFlag(flag)) { + if (relevantFlags.indexOf(flag) !== -1) { + this.splice(this.indexOf(name), 1); + } + this.flags ^= flag; + } else if (set && !this.hasFlag(flag)) { + if (relevantFlags.indexOf(flag) !== -1) { + this.push(name); + } + this.flags |= flag; + } + } } export interface DefaultValueContainer extends Reflection { - defaultValue: string; + defaultValue?: string; } export interface TypeContainer extends Reflection { - type: Type; + type?: Type; } export interface TypeParameterContainer extends Reflection { - typeParameters: TypeParameterReflection[]; + typeParameters?: TypeParameterReflection[]; } export enum TraverseProperty { @@ -237,71 +315,75 @@ export abstract class Reflection { /** * The human readable string representation of the kind of this reflection. */ - kindString: string; + kindString?: string; - flags: ReflectionFlags = []; + flags: ReflectionFlags = new ReflectionFlags(); /** * The reflection this reflection is a child of. */ - parent: Reflection; + parent?: Reflection; /** * The parsed documentation comment attached to this reflection. */ - comment: Comment; + comment?: Comment; /** * A list of all source files that contributed to this reflection. */ - sources: SourceReference[]; + sources?: SourceReference[]; /** * A list of all decorators attached to this reflection. */ - decorators: Decorator[]; + decorators?: Decorator[]; /** * A list of all types that are decorated by this reflection. */ - decorates: Type[]; + decorates?: Type[]; /** * The url of this reflection in the generated documentation. + * TODO: Reflections shouldn't know urls exist. Move this to a serializer. */ - url: string; + url?: string; /** * The name of the anchor of this child. + * TODO: Reflections shouldn't know anchors exist. Move this to a serializer. */ - anchor: string; + 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. + * TODO: Reflections shouldn't know how they are rendered. Move this to the correct serializer. */ - hasOwnDocument: boolean; + hasOwnDocument?: boolean; /** * A list of generated css classes that should be applied to representations of this * reflection in the generated markup. + * TODO: Reflections shouldn't know about CSS. Move this property to the correct serializer. */ - cssClasses: string; + cssClasses?: string; /** * Url safe alias for this reflection. * * @see [[BaseReflection.getAlias]] */ - private _alias: string; + private _alias?: string; - private _aliases: string[]; + private _aliases?: string[]; /** * Create a new BaseReflection instance. */ - constructor(parent?: Reflection, name?: string, kind?: ReflectionKind) { + constructor(name: string, kind: ReflectionKind, parent?: Reflection) { this.id = REFLECTION_ID++; this.parent = parent; this.name = name; @@ -309,30 +391,12 @@ export abstract class Reflection { 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; - } + kindOf(kind: ReflectionKind | ReflectionKind[]): boolean { + const kindArray = Array.isArray(kind) ? kind : [kind]; + return kindArray.some(kind => (this.kind & kind) !== 0); } /** @@ -355,78 +419,7 @@ export abstract class Reflection { * 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; - } + this.flags.setFlag(flag, value); } /** @@ -466,7 +459,7 @@ export abstract class Reflection { * @returns TRUE when this reflection has a visible comment. */ hasComment(): boolean { - return this.comment && this.comment.hasVisibleComponent(); + return this.comment ? this.comment.hasVisibleComponent() : false; } hasGetterOrSetter(): boolean { @@ -488,10 +481,10 @@ export abstract class Reflection { * * @returns The found child or NULL. */ - getChildByName(arg: any): Reflection { + getChildByName(arg: string | string[]): Reflection | undefined { const names: string[] = Array.isArray(arg) ? arg : arg.split('.'); const name = names[0]; - let result: Reflection = null; + let result: Reflection | undefined; this.traverse((child) => { if (child.name === name) { @@ -513,28 +506,18 @@ export abstract class Reflection { 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 { + findReflectionByName(arg: string | string[]): Reflection | undefined { const names: string[] = Array.isArray(arg) ? arg : arg.split('.'); const reflection = this.getChildByName(names); if (reflection) { return reflection; - } else { + } else if (this.parent) { return this.parent.findReflectionByName(names); } } @@ -570,15 +553,12 @@ export abstract class Reflection { result.comment = this.comment.toObject(); } - for (let key in this.flags) { - // tslint:disable-next-line:triple-equals - if (parseInt(key, 10) == key || key === 'flags') { - continue; + Object.getOwnPropertyNames(ReflectionFlags.prototype).forEach(name => { + const descriptor = Object.getOwnPropertyDescriptor(ReflectionFlags.prototype, name)!; + if (typeof descriptor.get === 'function' && this.flags[name] === true) { + result.flags[name] = true; } - if (this.flags[key]) { - result.flags[key] = true; - } - } + }); if (this.decorates) { result.decorates = this.decorates.map((type) => type.toObject()); diff --git a/src/lib/models/reflections/container.ts b/src/lib/models/reflections/container.ts index 2194c5bf7..777291006 100644 --- a/src/lib/models/reflections/container.ts +++ b/src/lib/models/reflections/container.ts @@ -7,17 +7,17 @@ export class ContainerReflection extends Reflection { /** * The children of this reflection. */ - children: DeclarationReflection[]; + children?: DeclarationReflection[]; /** * All children grouped by their kind. */ - groups: ReflectionGroup[]; + groups?: ReflectionGroup[]; /** * All children grouped by their category. */ - categories: ReflectionCategory[]; + categories?: ReflectionCategory[]; /** * Return a list of all children of a certain kind. @@ -26,14 +26,7 @@ export class ContainerReflection extends Reflection { * @returns An array containing all children with the desired kind. */ getChildrenByKind(kind: ReflectionKind): DeclarationReflection[] { - const values: DeclarationReflection[] = []; - for (let key in this.children) { - const child = this.children[key]; - if (child.kindOf(kind)) { - values.push(child); - } - } - return values; + return (this.children || []).filter(child => child.kindOf(kind)); } /** diff --git a/src/lib/models/reflections/declaration.ts b/src/lib/models/reflections/declaration.ts index 657547833..45290eeb9 100644 --- a/src/lib/models/reflections/declaration.ts +++ b/src/lib/models/reflections/declaration.ts @@ -39,9 +39,9 @@ export class DeclarationReflection extends ContainerReflection implements Defaul * If the reflection represents a variable or a property, this is the value type.
* If the reflection represents a signature, this is the return type. */ - type: Type; + type?: Type; - typeParameters: TypeParameterReflection[]; + typeParameters?: TypeParameterReflection[]; /** * A list of call signatures attached to this declaration. @@ -49,76 +49,76 @@ export class DeclarationReflection extends ContainerReflection implements Defaul * TypeDoc creates one declaration per function that may contain ore or more * signature reflections. */ - signatures: SignatureReflection[]; + signatures?: SignatureReflection[]; /** * The index signature of this declaration. */ - indexSignature: SignatureReflection; + indexSignature?: SignatureReflection; /** * The get signature of this declaration. */ - getSignature: SignatureReflection; + getSignature?: SignatureReflection; /** * The set signature of this declaration. */ - setSignature: SignatureReflection; + setSignature?: SignatureReflection; /** * The default value of this reflection. * * Applies to function parameters. */ - defaultValue: string; + defaultValue?: string; /** * A type that points to the reflection that has been overwritten by this reflection. * * Applies to interface and class members. */ - overwrites: Type; + overwrites?: Type; /** * A type that points to the reflection this reflection has been inherited from. * * Applies to interface and class members. */ - inheritedFrom: Type; + inheritedFrom?: Type; /** * A type that points to the reflection this reflection is the implementation of. * * Applies to class members. */ - implementationOf: Type; + implementationOf?: Type; /** * A list of all types this reflection extends (e.g. the parent classes). */ - extendedTypes: Type[]; + extendedTypes?: Type[]; /** * A list of all types that extend this reflection (e.g. the subclasses). */ - extendedBy: Type[]; + extendedBy?: Type[]; /** * A list of all types this reflection implements. */ - implementedTypes: Type[]; + implementedTypes?: Type[]; /** * A list of all types that implement this reflection. */ - implementedBy: Type[]; + implementedBy?: Type[]; /** * Contains a simplified representation of the type hierarchy suitable for being * rendered in templates. */ - typeHierarchy: DeclarationHierarchy; + typeHierarchy?: DeclarationHierarchy; hasGetterOrSetter(): boolean { return !!this.getSignature || !!this.setSignature; diff --git a/src/lib/models/reflections/parameter.ts b/src/lib/models/reflections/parameter.ts index e0e53119c..5a493737c 100644 --- a/src/lib/models/reflections/parameter.ts +++ b/src/lib/models/reflections/parameter.ts @@ -3,11 +3,11 @@ import { Reflection, DefaultValueContainer, TypeContainer, TraverseCallback, Tra import { SignatureReflection } from './signature'; export class ParameterReflection extends Reflection implements DefaultValueContainer, TypeContainer { - parent: SignatureReflection; + parent?: SignatureReflection; - defaultValue: string; + defaultValue?: string; - type: Type; + type?: Type; /** * Traverse all potential child reflections of this reflection. diff --git a/src/lib/models/reflections/project.ts b/src/lib/models/reflections/project.ts index 6d7881587..17394207f 100644 --- a/src/lib/models/reflections/project.ts +++ b/src/lib/models/reflections/project.ts @@ -30,19 +30,20 @@ export class ProjectReflection extends ContainerReflection { /** * All reflections categorized. */ - categories: ReflectionCategory[]; + categories?: ReflectionCategory[]; /** * The name of the project. * - * The name can be passed as a commandline argument or it is read from the package info. + * The name can be passed as a command line argument or it is read from the package info. + * this.name is assigned in the Reflection class. */ - name: string; + name!: string; /** * The contents of the readme.md file of the project when found. */ - readme: string; + readme?: string; /** * The parsed data of the package.json file of the project when found. @@ -55,7 +56,7 @@ export class ProjectReflection extends ContainerReflection { * @param name The name of the project. */ constructor(name: string) { - super(null, name, ReflectionKind.Global); + super(name, ReflectionKind.Global); } /** @@ -96,9 +97,9 @@ export class ProjectReflection extends ContainerReflection { /** * Try to find a reflection by its name. * - * @return The found reflection or null. + * @return The found reflection or undefined. */ - findReflectionByName(arg: any): Reflection { + findReflectionByName(arg: any): Reflection | undefined { const names: string[] = Array.isArray(arg) ? arg : arg.split('.'); const name = names.pop(); @@ -109,9 +110,8 @@ export class ProjectReflection extends ContainerReflection { } let depth = names.length - 1; - let target = reflection; - while (target && depth >= 0) { - target = target.parent; + let target: Reflection | undefined = reflection; + while ((target = target.parent) && depth >= 0) { if (target.name !== names[depth]) { continue search; } @@ -121,7 +121,7 @@ export class ProjectReflection extends ContainerReflection { return reflection; } - return null; + return undefined; } /** diff --git a/src/lib/models/reflections/signature.ts b/src/lib/models/reflections/signature.ts index b21df09b5..f1cb0420f 100644 --- a/src/lib/models/reflections/signature.ts +++ b/src/lib/models/reflections/signature.ts @@ -5,34 +5,34 @@ import { ParameterReflection } from './parameter'; import { TypeParameterReflection } from './type-parameter'; export class SignatureReflection extends Reflection implements TypeContainer, TypeParameterContainer { - parent: ContainerReflection; + parent?: ContainerReflection; - parameters: ParameterReflection[]; + parameters?: ParameterReflection[]; - typeParameters: TypeParameterReflection[]; + typeParameters?: TypeParameterReflection[]; - type: Type; + type?: Type; /** * A type that points to the reflection that has been overwritten by this reflection. * * Applies to interface and class members. */ - overwrites: Type; + overwrites?: Type; /** * A type that points to the reflection this reflection has been inherited from. * * Applies to interface and class members. */ - inheritedFrom: Type; + inheritedFrom?: Type; /** * A type that points to the reflection this reflection is the implementation of. * * Applies to class members. */ - implementationOf: Type; + implementationOf?: Type; /** * Return an array of the parameter types. @@ -41,7 +41,10 @@ export class SignatureReflection extends Reflection implements TypeContainer, Ty if (!this.parameters) { return []; } - return this.parameters.map((parameter: ParameterReflection) => parameter.type); + function notUndefined(t: T | undefined): t is T { + return !!t; + } + return this.parameters.map(parameter => parameter.type).filter(notUndefined); } /** diff --git a/src/lib/models/reflections/type-parameter.ts b/src/lib/models/reflections/type-parameter.ts index 893023d65..c7e55502f 100644 --- a/src/lib/models/reflections/type-parameter.ts +++ b/src/lib/models/reflections/type-parameter.ts @@ -3,15 +3,15 @@ import { Reflection, ReflectionKind, TypeContainer } from './abstract'; import { DeclarationReflection } from './declaration'; export class TypeParameterReflection extends Reflection implements TypeContainer { - parent: DeclarationReflection; + parent?: DeclarationReflection; - type: Type; + type?: Type; /** * Create a new TypeParameterReflection instance. */ - constructor(parent?: Reflection, type?: TypeParameterType) { - super(parent, type.name, ReflectionKind.TypeParameter); + constructor(type: TypeParameterType, parent?: Reflection) { + super(type.name, ReflectionKind.TypeParameter, parent); this.type = type.constraint; } diff --git a/src/lib/models/sources/directory.ts b/src/lib/models/sources/directory.ts index 0e6bd83dc..8b7152996 100644 --- a/src/lib/models/sources/directory.ts +++ b/src/lib/models/sources/directory.ts @@ -12,18 +12,18 @@ import { SourceFile } from './file'; */ export class SourceDirectory { /** - * The parent directory or NULL if this is a root directory. + * The parent directory or undefined if this is a root directory. */ - parent: SourceDirectory = null; + parent?: SourceDirectory; /** * A list of all subdirectories. */ directories: {[name: string]: SourceDirectory} = {}; - groups: ReflectionGroup[]; + groups?: ReflectionGroup[]; - categories: ReflectionCategory[]; + categories?: ReflectionCategory[]; /** * A list of all files in this directory. @@ -33,17 +33,17 @@ export class SourceDirectory { /** * The name of this directory. */ - name: string = null; + name?: string; /** * The relative path from the root directory to this directory. */ - dirName: string = null; + dirName?: string; /** * The url of the page displaying the directory contents. */ - url: string; + url?: string; /** * Create a new SourceDirectory instance. diff --git a/src/lib/models/sources/file.ts b/src/lib/models/sources/file.ts index 58d47ebe6..1cb131c10 100644 --- a/src/lib/models/sources/file.ts +++ b/src/lib/models/sources/file.ts @@ -64,12 +64,12 @@ export class SourceFile { /** * A url pointing to a page displaying the contents of this file. */ - url: string; + url?: string; /** * The representation of the parent directory of this source file. */ - parent: SourceDirectory; + parent?: SourceDirectory; /** * A list of all reflections that are declared in this file. @@ -79,12 +79,12 @@ export class SourceFile { /** * A grouped list of the reflections declared in this file. */ - groups: ReflectionGroup[]; + groups?: ReflectionGroup[]; /** * A categorized list of the reflections declared in this file. */ - categories: ReflectionCategory[]; + categories?: ReflectionCategory[]; /** * Create a new SourceFile instance. diff --git a/src/lib/models/types/abstract.ts b/src/lib/models/types/abstract.ts index 2d10da24f..56ef22929 100644 --- a/src/lib/models/types/abstract.ts +++ b/src/lib/models/types/abstract.ts @@ -51,7 +51,7 @@ export abstract class Type { * @param a * @param b */ - static isTypeListSimiliar(a: Type[], b: Type[]): boolean { + static isTypeListSimilar(a: Type[], b: Type[]): boolean { if (a.length !== b.length) { return false; } diff --git a/src/lib/models/types/intersection.ts b/src/lib/models/types/intersection.ts index 53bbd8e1a..da5f7962e 100644 --- a/src/lib/models/types/intersection.ts +++ b/src/lib/models/types/intersection.ts @@ -47,7 +47,7 @@ export class IntersectionType extends Type { if (!(type instanceof IntersectionType)) { return false; } - return Type.isTypeListSimiliar(type.types, this.types); + return Type.isTypeListSimilar(type.types, this.types); } /** diff --git a/src/lib/models/types/reference.ts b/src/lib/models/types/reference.ts index 8c16e5d56..686fbea7e 100644 --- a/src/lib/models/types/reference.ts +++ b/src/lib/models/types/reference.ts @@ -25,7 +25,7 @@ export class ReferenceType extends Type { /** * The type arguments of this reference. */ - typeArguments: Type[]; + typeArguments?: Type[]; /** * The symbol id of the referenced type as returned from the TypeScript compiler. @@ -40,7 +40,7 @@ export class ReferenceType extends Type { * * The [[TypePlugin]] will try to set this property in the resolving phase. */ - reflection: Reflection; + reflection?: Reflection; /** * Special symbol ID noting that the reference of a ReferenceType was known when creating the type. @@ -100,7 +100,7 @@ export class ReferenceType extends Type { result.id = this.reflection.id; } - if (this.typeArguments) { + if (this.typeArguments && this.typeArguments.length) { result.typeArguments = this.typeArguments.map((t) => t.toObject()); } diff --git a/src/lib/models/types/type-operator.ts b/src/lib/models/types/type-operator.ts index 03576b177..2e280ff7c 100644 --- a/src/lib/models/types/type-operator.ts +++ b/src/lib/models/types/type-operator.ts @@ -18,7 +18,7 @@ export class TypeOperatorType extends Type { // currently, there is only one type operator, this is always "keyof" // but, if more types will be added in the future we are ready. - operator: 'keyof' = 'keyof'; + readonly operator = 'keyof'; constructor(target: Type) { super(); @@ -62,6 +62,6 @@ export class TypeOperatorType extends Type { * Return a string representation of this type. */ toString() { - return `keyof ${this.target.toString()}`; + return `${this.operator} ${this.target.toString()}`; } } diff --git a/src/lib/models/types/type-parameter.ts b/src/lib/models/types/type-parameter.ts index aef221a8c..f34d222ea 100644 --- a/src/lib/models/types/type-parameter.ts +++ b/src/lib/models/types/type-parameter.ts @@ -11,23 +11,27 @@ export class TypeParameterType extends Type { /** * */ - name: string; + readonly name: string; - constraint: Type; + constraint?: Type; /** * The type name identifier. */ readonly type: string = 'typeParameter'; + constructor(name: string) { + super(); + this.name = name; + } + /** * Clone this type. * * @return A clone of this type. */ clone(): Type { - const clone = new TypeParameterType(); - clone.name = this.name; + const clone = new TypeParameterType(this.name); clone.constraint = this.constraint; return clone; } diff --git a/src/lib/models/types/union.ts b/src/lib/models/types/union.ts index 2da698025..bb3ce3a36 100644 --- a/src/lib/models/types/union.ts +++ b/src/lib/models/types/union.ts @@ -47,7 +47,7 @@ export class UnionType extends Type { if (!(type instanceof UnionType)) { return false; } - return Type.isTypeListSimiliar(type.types, this.types); + return Type.isTypeListSimilar(type.types, this.types); } /** diff --git a/src/lib/output/components.ts b/src/lib/output/components.ts index 8111c23c9..436209d82 100644 --- a/src/lib/output/components.ts +++ b/src/lib/output/components.ts @@ -16,17 +16,18 @@ export abstract class ContextAwareRendererComponent extends RendererComponent { /** * The project that is currently processed. */ - protected project: ProjectReflection; + protected project?: ProjectReflection; /** * The reflection that is currently processed. */ - protected reflection: DeclarationReflection; + protected reflection?: DeclarationReflection; /** * The url of the document that is being currently generated. + * Set when a page begins rendering. */ - private location: string; + private location!: string; /** * Regular expression to test if a string looks like an external url. @@ -76,6 +77,6 @@ export abstract class ContextAwareRendererComponent extends RendererComponent { */ protected onBeginPage(page: PageEvent) { this.location = page.url; - this.reflection = page.model instanceof DeclarationReflection ? page.model : null; + this.reflection = page.model instanceof DeclarationReflection ? page.model : undefined; } } diff --git a/src/lib/output/events.ts b/src/lib/output/events.ts index 81bb41af1..855befb26 100644 --- a/src/lib/output/events.ts +++ b/src/lib/output/events.ts @@ -16,7 +16,7 @@ export class RendererEvent extends Event { /** * The project the renderer is currently processing. */ - project: ProjectReflection; + readonly project: ProjectReflection; /** * The settings that have been passed to TypeDoc. @@ -26,14 +26,14 @@ export class RendererEvent extends Event { /** * The path of the directory the documentation should be written to. */ - outputDirectory: string; + readonly outputDirectory: string; /** * A list of all pages that should be generated. * * This list can be altered during the [[Renderer.EVENT_BEGIN]] event. */ - urls: UrlMapping[]; + urls?: UrlMapping[]; /** * Triggered before the renderer starts rendering a project. @@ -47,6 +47,12 @@ export class RendererEvent extends Event { */ static END = 'endRender'; + constructor(name: string, outputDirectory: string, project: ProjectReflection) { + super(name); + this.outputDirectory = outputDirectory; + this.project = project; + } + /** * Create an [[PageEvent]] event based on this event and the given url mapping. * @@ -79,7 +85,7 @@ export class PageEvent extends Event { /** * The project the renderer is currently processing. */ - project: ProjectReflection; + project!: ProjectReflection; /** * The settings that have been passed to TypeDoc. @@ -89,12 +95,12 @@ export class PageEvent extends Event { /** * The filename the page will be written to. */ - filename: string; + filename!: string; /** * The url this page will be located at. */ - url: string; + url!: string; /** * The model that should be rendered on this page. @@ -104,29 +110,29 @@ export class PageEvent extends Event { /** * The template that should be used to render this page. */ - template: HandlebarsTemplateDelegate; + template?: HandlebarsTemplateDelegate; /** * The name of the template that should be used to render this page. */ - templateName: string; + templateName!: string; /** * The primary navigation structure of this page. */ - navigation: NavigationItem; + navigation?: NavigationItem; /** * The table of contents structure of this page. */ - toc: NavigationItem; + toc?: NavigationItem; /** * The final html content of this page. * * Should be rendered by layout templates and can be modifies by plugins. */ - contents: string; + contents?: string; /** * Triggered before a document will be rendered. @@ -151,7 +157,7 @@ export class MarkdownEvent extends Event { /** * The unparsed original text. */ - originalText: string; + readonly originalText: string; /** * The parsed output. @@ -163,4 +169,10 @@ export class MarkdownEvent extends Event { * @event */ static PARSE = 'parseMarkdown'; + + constructor(name: string, originalText: string, parsedText: string) { + super(name); + this.originalText = originalText; + this.parsedText = parsedText; + } } diff --git a/src/lib/output/models/NavigationItem.ts b/src/lib/output/models/NavigationItem.ts index c7d49abe0..d32f4e4de 100644 --- a/src/lib/output/models/NavigationItem.ts +++ b/src/lib/output/models/NavigationItem.ts @@ -21,17 +21,17 @@ export class NavigationItem { /** * A list of urls that should be seen as sub-pages of this node. */ - dedicatedUrls: string[]; + dedicatedUrls?: string[]; /** * The parent navigation node. */ - parent: NavigationItem; + parent?: NavigationItem; /** * An array containing all child navigation nodes. */ - children: NavigationItem[]; + children?: NavigationItem[]; /** * A string containing the css classes of this node. @@ -41,32 +41,32 @@ export class NavigationItem { /** * Is this item a simple label without a link? */ - isLabel: boolean; + isLabel?: boolean; /** * Is this item visible? */ - isVisible: boolean; + isVisible?: boolean; /** * Does this navigation node represent the current page? */ - isCurrent: boolean; + isCurrent?: boolean; /** * Is this the navigation node for the globals page? */ - isGlobals: boolean; + isGlobals?: boolean; /** * Is this navigation node one of the parents of the current page? */ - isInPath: boolean; + isInPath?: boolean; /** * The source [Reflection] this item is built from */ - reflection: Reflection; + reflection?: Reflection; /** * Create a new NavigationItem instance. @@ -78,9 +78,9 @@ export class NavigationItem { * @param reflection The source [Reflection] for this [NavigationItem] */ constructor(title?: string, url?: string, parent?: NavigationItem, cssClasses?: string, reflection?: Reflection) { - this.title = title || ''; - this.url = url || ''; - this.parent = parent || null; + this.title = title || ''; + this.url = url || ''; + this.parent = parent; this.cssClasses = cssClasses || ''; this.reflection = reflection; diff --git a/src/lib/output/plugins/AssetsPlugin.ts b/src/lib/output/plugins/AssetsPlugin.ts index d888faed2..d4a1e0e7e 100644 --- a/src/lib/output/plugins/AssetsPlugin.ts +++ b/src/lib/output/plugins/AssetsPlugin.ts @@ -31,16 +31,16 @@ export class AssetsPlugin extends RendererComponent { * @param event An event object describing the current render operation. */ private onRendererBegin(event: RendererEvent) { - let fromDefault = Path.join(Renderer.getDefaultTheme(), 'assets'); + let fromDefault: string | undefined = Path.join(Renderer.getDefaultTheme(), 'assets'); const to = Path.join(event.outputDirectory, 'assets'); if (this.copyDefaultAssets) { FS.copySync(fromDefault, to); } else { - fromDefault = null; + fromDefault = undefined; } - const from = Path.join(this.owner.theme.basePath, 'assets'); + const from = Path.join(this.owner.theme!.basePath, 'assets'); if (from !== fromDefault && FS.existsSync(from)) { FS.copySync(from, to); } diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 36bb799f2..05032ed7f 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -44,7 +44,7 @@ export class JavascriptIndexPlugin extends RendererComponent { let parent = reflection.parent; if (parent instanceof ProjectReflection) { - parent = null; + parent = undefined; } const row: any = { diff --git a/src/lib/output/plugins/LayoutPlugin.ts b/src/lib/output/plugins/LayoutPlugin.ts index 5a732bb1e..c2a241246 100644 --- a/src/lib/output/plugins/LayoutPlugin.ts +++ b/src/lib/output/plugins/LayoutPlugin.ts @@ -22,7 +22,7 @@ export class LayoutPlugin extends RendererComponent { * @param page An event object describing the current render operation. */ private onRendererEndPage(page: PageEvent) { - const layout = this.owner.theme.resources.layouts.getResource('default').getTemplate(); + const layout = this.owner.theme!.resources.layouts.getResource('default')!.getTemplate(); page.contents = layout(page); } } diff --git a/src/lib/output/plugins/MarkedLinksPlugin.ts b/src/lib/output/plugins/MarkedLinksPlugin.ts index eee61bd78..d44fc6027 100644 --- a/src/lib/output/plugins/MarkedLinksPlugin.ts +++ b/src/lib/output/plugins/MarkedLinksPlugin.ts @@ -26,7 +26,7 @@ export class MarkedLinksPlugin extends ContextAwareRendererComponent { help: 'Emits a list of broken symbol [[navigation]] links after documentation generation', type: ParameterType.Boolean }) - listInvalidSymbolLinks: boolean; + listInvalidSymbolLinks!: boolean; private warnings: string[] = []; @@ -38,7 +38,7 @@ export class MarkedLinksPlugin extends ContextAwareRendererComponent { this.listenTo(this.owner, { [MarkdownEvent.PARSE]: this.onParseMarkdown, [RendererEvent.END]: this.onEndRenderer - }, null, 100); + }, undefined, 100); } /** @@ -70,14 +70,7 @@ export class MarkedLinksPlugin extends ContextAwareRendererComponent { const split = MarkedLinksPlugin.splitLinkText(content); const target = split.target; const caption = leading || split.caption; - - let monospace: boolean; - if (tagName === 'linkcode') { - monospace = true; - } - if (tagName === 'linkplain') { - monospace = false; - } + const monospace = tagName === 'linkcode'; return this.buildLink(match, target, caption, monospace); }); @@ -97,7 +90,7 @@ export class MarkedLinksPlugin extends ContextAwareRendererComponent { if (this.urlPrefix.test(target)) { attributes = ' class="external"'; } else { - let reflection: Reflection; + let reflection: Reflection | undefined; if (this.reflection) { reflection = this.reflection.findReflectionByName(target); } else if (this.project) { @@ -112,8 +105,8 @@ export class MarkedLinksPlugin extends ContextAwareRendererComponent { target = this.getRelativeUrl(reflection.url); } } else { - reflection = this.reflection || this.project; - this.warnings.push(`In ${reflection.getFullName()}: ${original}`); + const fullName = (this.reflection || this.project)!.getFullName(); + this.warnings.push(`In ${fullName}: ${original}`); return original; } } diff --git a/src/lib/output/plugins/MarkedPlugin.ts b/src/lib/output/plugins/MarkedPlugin.ts index b0b504b69..4a97f93f8 100644 --- a/src/lib/output/plugins/MarkedPlugin.ts +++ b/src/lib/output/plugins/MarkedPlugin.ts @@ -46,24 +46,24 @@ export class MarkedPlugin extends ContextAwareRendererComponent { help: 'Specifies the location to look for included documents (use [[include:FILENAME]] in comments).', hint: ParameterHint.Directory }) - includeSource: string; + includeSource!: string; @Option({ name: 'media', help: 'Specifies the location with media files that should be copied to the output directory.', hint: ParameterHint.Directory }) - mediaSource: string; + mediaSource!: string; /** * The path referenced files are located in. */ - private includes: string; + private includes?: string; /** * Path to the output media directory. */ - private mediaDirectory: string; + private mediaDirectory?: string; /** * The pattern used to find references in markdown. @@ -121,7 +121,7 @@ export class MarkedPlugin extends ContextAwareRendererComponent { public parseMarkdown(text: string, context: any) { if (this.includes) { text = text.replace(this.includePattern, (match: string, path: string) => { - path = Path.join(this.includes, path.trim()); + path = Path.join(this.includes!, path.trim()); if (FS.existsSync(path) && FS.statSync(path).isFile()) { const contents = FS.readFileSync(path, 'utf-8'); if (path.substr(-4).toLocaleLowerCase() === '.hbs') { @@ -138,7 +138,7 @@ export class MarkedPlugin extends ContextAwareRendererComponent { if (this.mediaDirectory) { text = text.replace(this.mediaPattern, (match: string, path: string) => { - if (FS.existsSync(Path.join(this.mediaDirectory, path))) { + if (FS.existsSync(Path.join(this.mediaDirectory!, path))) { return this.getRelativeUrl('media') + '/' + path; } else { return match; @@ -146,9 +146,7 @@ export class MarkedPlugin extends ContextAwareRendererComponent { }); } - const event = new MarkdownEvent(MarkdownEvent.PARSE); - event.originalText = text; - event.parsedText = text; + const event = new MarkdownEvent(MarkdownEvent.PARSE, text, text); this.owner.trigger(event); return event.parsedText; @@ -178,7 +176,7 @@ export class MarkedPlugin extends ContextAwareRendererComponent { this.mediaDirectory = Path.join(event.outputDirectory, 'media'); FS.copySync(media, this.mediaDirectory); } else { - this.mediaDirectory = null; + this.mediaDirectory = undefined; this.application.logger.warn('Could not find provided media directory: ' + media); } } diff --git a/src/lib/output/plugins/NavigationPlugin.ts b/src/lib/output/plugins/NavigationPlugin.ts index 4745472f0..cff0a39a9 100644 --- a/src/lib/output/plugins/NavigationPlugin.ts +++ b/src/lib/output/plugins/NavigationPlugin.ts @@ -15,7 +15,7 @@ export class NavigationPlugin extends RendererComponent { /** * The navigation structure generated by the current theme. */ - navigation: NavigationItem; + navigation!: NavigationItem; /** * Create a new NavigationPlugin instance. @@ -33,7 +33,7 @@ export class NavigationPlugin extends RendererComponent { * @param event An event object describing the current render operation. */ private onBeginRenderer(event: RendererEvent) { - this.navigation = this.owner.theme.getNavigation(event.project); + this.navigation = this.owner.theme!.getNavigation(event.project); } /** @@ -57,10 +57,10 @@ export class NavigationPlugin extends RendererComponent { } })(this.navigation); - currentItems.forEach((item: NavigationItem) => { - item.isCurrent = true; + currentItems.forEach((item: NavigationItem | undefined) => { + item!.isCurrent = true; - let depth = item.isGlobals ? -1 : 0; + let depth = item!.isGlobals ? -1 : 0; let count = 1; while (item) { item.isInPath = true; diff --git a/src/lib/output/plugins/PrettyPrintPlugin.ts b/src/lib/output/plugins/PrettyPrintPlugin.ts index e1cac8298..c89ec7d9b 100644 --- a/src/lib/output/plugins/PrettyPrintPlugin.ts +++ b/src/lib/output/plugins/PrettyPrintPlugin.ts @@ -36,7 +36,7 @@ export class PrettyPrintPlugin extends RendererComponent { /** * Map of all tags that will be ignored. */ - static IGNORED_TAGS: any = { + static IGNORED_TAGS = { area: true, base: true, br: true, @@ -56,7 +56,7 @@ export class PrettyPrintPlugin extends RendererComponent { /** * Map of all tags that prevent this plugin form modifying the following code. */ - static PRE_TAGS: any = { + static PRE_TAGS = { pre: true, code: true, textarea: true, @@ -77,12 +77,12 @@ export class PrettyPrintPlugin extends RendererComponent { * @param event */ onRendererEndPage(event: PageEvent) { - let match: RegExpMatchArray; + let match: RegExpMatchArray | null; let line: string; let lineState: PrettyPrintState; let lineDepth: number; let tagName: string; - let preName: string; + let preName: string | undefined; let tagExp = /<\s*(\w+)[^>]*>|<\/\s*(\w+)[^>]*>|/g; let emptyLineExp = /^[\s]*$/; @@ -90,7 +90,7 @@ export class PrettyPrintPlugin extends RendererComponent { let state = PrettyPrintState.Default; const stack: string[] = []; - const lines = event.contents.split(/\r\n?|\n/); + const lines = (event.contents || '').split(/\r\n?|\n/); let index = 0; let count = lines.length; diff --git a/src/lib/output/renderer.ts b/src/lib/output/renderer.ts index a3b4cb05a..df6d5da29 100644 --- a/src/lib/output/renderer.ts +++ b/src/lib/output/renderer.ts @@ -59,7 +59,7 @@ export class Renderer extends ChildableComponent /** * The theme that is used to render the documentation. */ - theme: Theme; + theme?: Theme; @Option({ name: 'theme', @@ -67,48 +67,48 @@ export class Renderer extends ChildableComponent type: ParameterType.String, defaultValue: 'default' }) - themeName: string; + themeName!: string; @Option({ name: 'disableOutputCheck', help: 'Should TypeDoc disable the testing and cleaning of the output directory?', type: ParameterType.Boolean }) - disableOutputCheck: boolean; + disableOutputCheck!: boolean; @Option({ name: 'gaID', help: 'Set the Google Analytics tracking ID and activate tracking code.' }) - gaID: string; + gaID!: string; @Option({ name: 'gaSite', help: 'Set the site name for Google Analytics. Defaults to `auto`.', defaultValue: 'auto' }) - gaSite: string; + gaSite!: string; @Option({ name: 'hideGenerator', help: 'Do not print the TypeDoc link at the end of the page.', type: ParameterType.Boolean }) - hideGenerator: boolean; + hideGenerator!: boolean; @Option({ name: 'entryPoint', help: 'Specifies the fully qualified name of the root symbol. Defaults to global namespace.', type: ParameterType.String }) - entryPoint: string; + entryPoint!: string; @Option({ name: 'toc', help: 'Specifies the top level table of contents.', type: ParameterType.Array }) - toc: string[]; + toc!: string[]; /** * Create a new Renderer instance. @@ -129,11 +129,9 @@ export class Renderer extends ChildableComponent return; } - const output = new RendererEvent(RendererEvent.BEGIN); - output.outputDirectory = outputDirectory; - output.project = project; + const output = new RendererEvent(RendererEvent.BEGIN, outputDirectory, project); output.settings = this.application.options.getRawValues(); - output.urls = this.theme.getUrls(project); + output.urls = this.theme!.getUrls(project); const bar = new ProgressBar('Rendering [:bar] :percent', { total: output.urls.length, @@ -163,7 +161,8 @@ export class Renderer extends ChildableComponent return false; } - page.template = page.template || this.theme.resources.templates.getResource(page.templateName).getTemplate(); + // Theme must be set as this is only called in render, and render ensures theme is set. + page.template = page.template || this.theme!.resources.templates.getResource(page.templateName)!.getTemplate(); page.contents = page.template(page); this.trigger(PageEvent.END, page); @@ -218,7 +217,7 @@ export class Renderer extends ChildableComponent } } - this.theme.resources.activate(); + this.theme!.resources.activate(); return true; } @@ -246,7 +245,8 @@ export class Renderer extends ChildableComponent return true; } - if (!this.theme.isOutputDirectory(directory)) { + // Theme must be set as this is only called after the theme is created. + if (!this.theme!.isOutputDirectory(directory)) { this.application.logger.error( 'The output directory "%s" exists but does not seem to be a documentation generated by TypeDoc.\n' + 'Make sure this is the right target directory, delete the folder and rerun TypeDoc.', diff --git a/src/lib/output/theme.ts b/src/lib/output/theme.ts index 3d451640c..1539bacd8 100644 --- a/src/lib/output/theme.ts +++ b/src/lib/output/theme.ts @@ -50,7 +50,7 @@ import { Resources } from './utils/resources'; * this theme. */ @Component({name: 'theme', internal: true}) -export class Theme extends RendererComponent { +export abstract class Theme extends RendererComponent { /** * The base path of this theme. */ @@ -88,9 +88,7 @@ export class Theme extends RendererComponent { * * @see [[Renderer.prepareOutputDirectory]] */ - isOutputDirectory(path: string): boolean { - return false; - } + abstract isOutputDirectory(path: string): boolean; /** * Map the models of the given project to the desired output files. @@ -102,9 +100,7 @@ export class Theme extends RendererComponent { * @returns A list of [[UrlMapping]] instances defining which models * should be rendered to which files. */ - getUrls(project: ProjectReflection): UrlMapping[] { - return []; - } + abstract getUrls(project: ProjectReflection): UrlMapping[]; /** * Create a navigation structure for the given project. @@ -119,7 +115,5 @@ export class Theme extends RendererComponent { * @param project The project whose navigation should be generated. * @returns The root navigation item. */ - getNavigation(project: ProjectReflection): NavigationItem { - return null; - } + abstract getNavigation(project: ProjectReflection): NavigationItem; } diff --git a/src/lib/output/themes/DefaultTheme.ts b/src/lib/output/themes/DefaultTheme.ts index 904167a55..e3b5106f4 100644 --- a/src/lib/output/themes/DefaultTheme.ts +++ b/src/lib/output/themes/DefaultTheme.ts @@ -207,13 +207,12 @@ export class DefaultTheme extends Theme { */ function includeDedicatedUrls(reflection: DeclarationReflection, item: NavigationItem) { (function walk(reflection: DeclarationReflection) { - for (let key in reflection.children) { - const child = reflection.children[key]; + for (const child of reflection.children || []) { if (child.hasOwnDocument && !child.kindOf(ReflectionKind.SomeModule)) { if (!item.dedicatedUrls) { item.dedicatedUrls = []; } - item.dedicatedUrls.push(child.url); + item.dedicatedUrls.push(child.url!); walk(child); } } @@ -255,10 +254,10 @@ export class DefaultTheme extends Theme { reflections.forEach((reflection) => { if (hasExternals && !reflection.flags.isExternal && state !== 1) { - new NavigationItem('Internals', null, parent, 'tsd-is-external'); + new NavigationItem('Internals', undefined, parent, 'tsd-is-external'); state = 1; } else if (hasExternals && reflection.flags.isExternal && state !== 2) { - new NavigationItem('Externals', null, parent, 'tsd-is-external'); + new NavigationItem('Externals', undefined, parent, 'tsd-is-external'); state = 2; } @@ -332,8 +331,8 @@ export class DefaultTheme extends Theme { DefaultTheme.applyReflectionClasses(reflection); } - if (reflection instanceof ContainerReflection && reflection['groups']) { - reflection['groups'].forEach(DefaultTheme.applyGroupClasses); + if (reflection instanceof ContainerReflection && reflection.groups) { + reflection.groups.forEach(DefaultTheme.applyGroupClasses); } } } @@ -361,17 +360,10 @@ export class DefaultTheme extends Theme { * Return the template mapping fore the given reflection. * * @param reflection The reflection whose mapping should be resolved. - * @returns The found mapping or NULL if no mapping could be found. + * @returns The found mapping or undefined if no mapping could be found. */ - static getMapping(reflection: DeclarationReflection): TemplateMapping { - for (let i = 0, c = DefaultTheme.MAPPINGS.length; i < c; i++) { - const mapping = DefaultTheme.MAPPINGS[i]; - if (reflection.kindOf(mapping.kind)) { - return mapping; - } - } - - return null; + static getMapping(reflection: DeclarationReflection): TemplateMapping | undefined { + return DefaultTheme.MAPPINGS.find(mapping => reflection.kindOf(mapping.kind)); } /** @@ -392,15 +384,14 @@ export class DefaultTheme extends Theme { reflection.hasOwnDocument = true; } - for (let key in reflection.children) { - const child = reflection.children[key]; + for (const child of reflection.children || []) { if (mapping.isLeaf) { DefaultTheme.applyAnchorUrl(child, reflection); } else { DefaultTheme.buildUrls(child, urls); } } - } else { + } else if (reflection.parent) { DefaultTheme.applyAnchorUrl(reflection, reflection.parent); } diff --git a/src/lib/output/themes/MinimalTheme.ts b/src/lib/output/themes/MinimalTheme.ts index 1f840864d..62f35fdf2 100644 --- a/src/lib/output/themes/MinimalTheme.ts +++ b/src/lib/output/themes/MinimalTheme.ts @@ -52,10 +52,10 @@ export class MinimalTheme extends DefaultTheme { urls.push(new UrlMapping('index.html', project, 'index.hbs')); project.url = 'index.html'; - project.anchor = null; + project.anchor = undefined; project.hasOwnDocument = true; - project.children.forEach((child) => { + (project.children || []).forEach((child) => { DefaultTheme.applyAnchorUrl(child, project); }); diff --git a/src/lib/output/utils/resources.ts b/src/lib/output/utils/resources.ts index 81e171cb4..19c0eb832 100644 --- a/src/lib/output/utils/resources.ts +++ b/src/lib/output/utils/resources.ts @@ -18,7 +18,7 @@ export class Resources { private theme: Theme; - private isActive: boolean; + private isActive = false; constructor(theme: Theme) { this.theme = theme; diff --git a/src/lib/output/utils/resources/stack.ts b/src/lib/output/utils/resources/stack.ts index 4dd47f98c..1cac1c713 100644 --- a/src/lib/output/utils/resources/stack.ts +++ b/src/lib/output/utils/resources/stack.ts @@ -66,12 +66,8 @@ export class ResourceOrigin { return name in this.resources; } - getResource(name: string): T { - if (name in this.resources) { - return this.resources[name]; - } else { - return null; - } + getResource(name: string): T | undefined { + return this.resources[name]; } getName(): string { @@ -80,7 +76,7 @@ export class ResourceOrigin { private findResources(dir?: string) { const resourceClass = this.stack.getResourceClass(); - const ressourceRegExp = this.stack.getRessourceRegExp(); + const resourceRegExp = this.stack.getResourceRegExp(); let path = this.path; if (dir) { path = Path.join(path, dir); @@ -91,7 +87,7 @@ export class ResourceOrigin { if (FS.statSync(fullName).isDirectory()) { this.findResources(dir ? Path.join(dir, fileName) : fileName); - } else if (ressourceRegExp.test(fileName)) { + } else if (resourceRegExp.test(fileName)) { const name: string = normalizeName(dir ? Path.join(dir, fileName) : fileName); this.resources[name] = new resourceClass(this, name, fullName); } @@ -100,20 +96,20 @@ export class ResourceOrigin { } export abstract class ResourceStack { - private isActive: boolean; + private isActive = false; - private ressourceClass: ResourceClass; + private resourceClass: ResourceClass; - private ressourceRegExp: RegExp; + private resourceRegExp: RegExp; /** * A list of all source directories. */ private origins: ResourceOrigin[] = []; - constructor(ressourceClass: ResourceClass, ressourceRegExp?: RegExp) { - this.ressourceClass = ressourceClass; - this.ressourceRegExp = ressourceRegExp || /.*/; + constructor(resourceClass: ResourceClass, resourceRegExp?: RegExp) { + this.resourceClass = resourceClass; + this.resourceRegExp = resourceRegExp || /.*/; } activate(): boolean { @@ -135,7 +131,7 @@ export abstract class ResourceStack { /** * Return a resource by its name. */ - getResource(name: string): T { + getResource(name: string): T | undefined { const normalizedName = normalizeName(name); let index = this.origins.length - 1; @@ -161,25 +157,23 @@ export abstract class ResourceStack { } getResourceClass(): ResourceClass { - return this.ressourceClass; + return this.resourceClass; } - getRessourceRegExp(): RegExp { - return this.ressourceRegExp; + getResourceRegExp(): RegExp { + return this.resourceRegExp; } - getOrigin(name: string): ResourceOrigin { + getOrigin(name: string): ResourceOrigin | undefined { for (let origin of this.origins) { if (origin.getName() === name) { return origin; } } - - return null; } hasOrigin(name: string): boolean { - return this.getOrigin(name) !== null; + return !!this.getOrigin(name); } /** diff --git a/src/lib/output/utils/resources/templates.ts b/src/lib/output/utils/resources/templates.ts index 6f315c23a..5e7b50aaa 100644 --- a/src/lib/output/utils/resources/templates.ts +++ b/src/lib/output/utils/resources/templates.ts @@ -4,7 +4,7 @@ import { readFile } from '../../../utils/fs'; import { ResourceStack, Resource } from './stack'; export class Template extends Resource { - private template: HandlebarsTemplateDelegate; + private template?: HandlebarsTemplateDelegate; getTemplate(): HandlebarsTemplateDelegate { if (!this.template) { diff --git a/src/lib/serialization/components.ts b/src/lib/serialization/components.ts index 7eccbe2e9..8cf7b679b 100644 --- a/src/lib/serialization/components.ts +++ b/src/lib/serialization/components.ts @@ -14,12 +14,6 @@ import { Serializer } from './serializer'; * * Grouping serializers is required due to performance, we don't need to check all the reflection * serializers when we are looking for type (or any other) serializers. - * - * [[Serializer]] will compare the function referenced in `serializeGroup` for each serializer - * component instance, this is why, when extending [[SerializerComponent]], it is recommended to - * reference `serializeGroup` to an existing function instead of creating a new function on for - * every new plugin. This will ensure maximum performance. - * > It is also possible to set a get accessor in the prototype to return the same function. */ export abstract class SerializerComponent extends AbstractComponent { @@ -39,7 +33,7 @@ export abstract class SerializerComponent extends AbstractComponent instance instanceof Reflection ? Reflection : undefined; + * serializeGroup(instance) { return instance instanceof Reflection } * serializeGroupSymbol = Reflection; * } * ``` @@ -47,16 +41,16 @@ export abstract class SerializerComponent extends AbstractComponent instance instanceof Type ? Type : undefined; + * serializeGroup(instance) { return instance instanceof Type } * serializeGroupSymbol = Type; * } * ``` * * > When a serializer component extends a parent serializer component the SERIALIZE_GROUP - * and SERIALIZE_GROUP_SYMBOL are also inherited so child serializers of the same group does not + * and SERIALIZE_GROUP_SYMBOL are also inherited so child serializers of the same group do not * need to declare a predicate nor a group. */ - abstract serializeGroup: (instance: boolean) => boolean; + abstract serializeGroup(instance: unknown): boolean; /** * The symbol representing the group this serializer belongs to. */ @@ -70,7 +64,7 @@ export abstract class SerializerComponent extends AbstractComponent boolean; + abstract supports(item: unknown): boolean; abstract toObject(item: T, obj?: any): any; @@ -81,17 +75,11 @@ export abstract class ReflectionSerializerComponent extend /** * Filter for instances of [[Reflection]] */ - protected static serializeGroup(instance: any): boolean { + serializeGroup(instance: unknown): boolean { return instance instanceof Reflection; } - // use same fn for every instance - serializeGroup = ReflectionSerializerComponent.serializeGroup; serializeGroupSymbol = Reflection; - - supports: (reflection: T) => boolean; - - abstract toObject(reflection: T, obj?: any): any; } export abstract class TypeSerializerComponent extends SerializerComponent { @@ -99,15 +87,9 @@ export abstract class TypeSerializerComponent extends Serializer /** * Filter for instances of [[Type]] */ - protected static serializeGroup(instance: any): boolean { + serializeGroup(instance: unknown): boolean { return instance instanceof Type; } - // use same fn for every instance - serializeGroup = TypeSerializerComponent.serializeGroup; serializeGroupSymbol = Type; - - supports: (type: T) => boolean; - - abstract toObject(type: T, obj?: any): any; } diff --git a/src/lib/serialization/events.ts b/src/lib/serialization/events.ts index 6b3450a20..a8918eb60 100644 --- a/src/lib/serialization/events.ts +++ b/src/lib/serialization/events.ts @@ -9,20 +9,25 @@ import { ProjectReflection } from '../models'; * @see [[Serializer.EVENT_END]] */ export class SerializeEvent extends Event { - /** - * The project the renderer is currently processing. - */ - project: ProjectReflection; + /** + * The project the renderer is currently processing. + */ + readonly project: ProjectReflection; - /** - * The path of the directory the serialized JSON should be written to. - */ - outputDirectory?: string; + /** + * The path of the directory the serialized JSON should be written to. + */ + outputDirectory?: string; - /** - * The name of the main JSON file (base + ext) - */ - outputFile?: string; + /** + * The name of the main JSON file (base + ext) + */ + outputFile?: string; - output: any; + output: any; + + constructor(name: string, project: ProjectReflection) { + super(name); + this.project = project; + } } diff --git a/src/lib/serialization/serializer.ts b/src/lib/serialization/serializer.ts index 606b99a0c..c77ecdb6a 100644 --- a/src/lib/serialization/serializer.ts +++ b/src/lib/serialization/serializer.ts @@ -21,8 +21,8 @@ export class Serializer extends ChildableComponent[] }>; - private routes: any[]; + private router!: Map[] }>; + private routes!: any[]; initialize(): void { this.router = new Map[] }>(); @@ -43,16 +43,16 @@ export class Serializer extends ChildableComponent (b.priority || 0) - (a.priority || 0)); - - return component; } + + return component; } /** * Remove a child component from the registry. * @param name The name the component registered as */ - removeComponent(name: string): SerializerComponent { + removeComponent(name: string): SerializerComponent | undefined { const component = super.removeComponent(name); const symbol = component && component.serializeGroupSymbol; if (symbol) { @@ -87,22 +87,20 @@ export class Serializer extends ChildableComponent[] { - const routes = []; + const routes: SerializerComponent[] = []; for (let i = 0, len = this.routes.length; i < len; i++) { if (this.routes[i](value)) { - const serializers = this.router.get(this.routes[i]).group; + const serializers = this.router.get(this.routes[i])!.group; for (let serializer of serializers) { if (serializer.supports(value)) { routes.push(serializer); diff --git a/src/lib/serialization/serializers/comments/comment-tag.ts b/src/lib/serialization/serializers/comments/comment-tag.ts index 0024f7e7c..c4b101094 100644 --- a/src/lib/serialization/serializers/comments/comment-tag.ts +++ b/src/lib/serialization/serializers/comments/comment-tag.ts @@ -11,17 +11,14 @@ export class CommentTagSerializer extends SerializerComponent { /** * Filter for instances of [[CommentTag]] */ - protected static serializeGroup(instance: any): boolean { + serializeGroup(instance: unknown): boolean { return instance instanceof CommentTag; } - // use same fn for every instance - serializeGroup = CommentTagSerializer.serializeGroup; serializeGroupSymbol = CommentTag; - initialize(): void { - super.initialize(); - this.supports = (r: CommentTag) => true; + supports(t: unknown) { + return true; } toObject(tag: CommentTag, obj?: any): any { diff --git a/src/lib/serialization/serializers/comments/comment.ts b/src/lib/serialization/serializers/comments/comment.ts index b15bf93f4..d05dd3914 100644 --- a/src/lib/serialization/serializers/comments/comment.ts +++ b/src/lib/serialization/serializers/comments/comment.ts @@ -11,19 +11,16 @@ export class CommentSerializer extends SerializerComponent { /** * Filter for instances of [[Comment]] */ - protected static serializeGroup(instance: any): boolean { + serializeGroup(instance: unknown): boolean { return instance instanceof Comment; } - // use same fn for every instance - serializeGroup = CommentSerializer.serializeGroup; - serializeGroupSymbol = Comment; - - initialize(): void { - super.initialize(); - this.supports = (r: Comment) => true; + supports(t: unknown) { + return true; } + serializeGroupSymbol = Comment; + toObject(comment: Comment, obj?: any): any { obj = obj || {}; diff --git a/src/lib/serialization/serializers/decorator.ts b/src/lib/serialization/serializers/decorator.ts index 26094c145..eb66505b9 100644 --- a/src/lib/serialization/serializers/decorator.ts +++ b/src/lib/serialization/serializers/decorator.ts @@ -11,17 +11,18 @@ export class DecoratorContainerSerializer extends SerializerComponent s instanceof DecoratorWrapper; + } + + supports(s: unknown) { + return s instanceof DecoratorWrapper; } toObject(decoratorWrapper: DecoratorWrapper, obj?: any): any { diff --git a/src/lib/serialization/serializers/reflection-group.ts b/src/lib/serialization/serializers/reflection-group.ts index edfeedfc2..b9ab40a61 100644 --- a/src/lib/serialization/serializers/reflection-group.ts +++ b/src/lib/serialization/serializers/reflection-group.ts @@ -11,17 +11,18 @@ export class ReflectionGroupSerializer extends SerializerComponent r instanceof ReflectionGroup; + } + + supports(r: unknown) { + return r instanceof ReflectionGroup; } toObject(group: ReflectionGroup, obj?: any): any { diff --git a/src/lib/serialization/serializers/reflections/abstract.ts b/src/lib/serialization/serializers/reflections/abstract.ts index 5314456e2..f3b381bc4 100644 --- a/src/lib/serialization/serializers/reflections/abstract.ts +++ b/src/lib/serialization/serializers/reflections/abstract.ts @@ -9,9 +9,8 @@ export class ReflectionSerializer extends ReflectionSerializerComponent true; + supports(t: unknown) { + return t instanceof Reflection; } toObject(reflection: Reflection, obj?: any): any { diff --git a/src/lib/serialization/serializers/reflections/container.ts b/src/lib/serialization/serializers/reflections/container.ts index f5fe73abf..c7caec21a 100644 --- a/src/lib/serialization/serializers/reflections/container.ts +++ b/src/lib/serialization/serializers/reflections/container.ts @@ -7,9 +7,8 @@ import { SourceReferenceWrapper } from '../models'; @Component({name: 'serializer:container-reflection'}) export class ContainerReflectionSerializer extends ReflectionSerializerComponent { - initialize(): void { - super.initialize(); - this.supports = (r: ContainerReflection) => r instanceof ContainerReflection; + supports(t: unknown) { + return t instanceof ContainerReflection; } toObject(container: ContainerReflection, obj?: any): any { diff --git a/src/lib/serialization/serializers/reflections/declaration.ts b/src/lib/serialization/serializers/reflections/declaration.ts index 993f1c7d0..439419a83 100644 --- a/src/lib/serialization/serializers/reflections/declaration.ts +++ b/src/lib/serialization/serializers/reflections/declaration.ts @@ -9,9 +9,8 @@ export class DeclarationReflectionSerializer extends ReflectionSerializerCompone static PRIORITY = ContainerReflectionSerializer.PRIORITY - 1; // mimic inheritance, run after parent - initialize(): void { - super.initialize(); - this.supports = (r: DeclarationReflection) => r instanceof DeclarationReflection; + supports(t: unknown) { + return t instanceof DeclarationReflection; } toObject(declaration: DeclarationReflection, obj?: any): any { diff --git a/src/lib/serialization/serializers/reflections/parameter.ts b/src/lib/serialization/serializers/reflections/parameter.ts index 1f89d0d27..3ef5e9e6b 100644 --- a/src/lib/serialization/serializers/reflections/parameter.ts +++ b/src/lib/serialization/serializers/reflections/parameter.ts @@ -6,9 +6,8 @@ import { ReflectionSerializerComponent } from '../../components'; @Component({name: 'serializer:parameter-reflection'}) export class ParameterReflectionSerializer extends ReflectionSerializerComponent { - initialize(): void { - super.initialize(); - this.supports = (r: ParameterReflection) => r instanceof ParameterReflection; + supports(t: unknown) { + return t instanceof ParameterReflection; } toObject(parameter: ParameterReflection, obj?: any): any { diff --git a/src/lib/serialization/serializers/reflections/project.ts b/src/lib/serialization/serializers/reflections/project.ts index 062ae18c4..2db643a2d 100644 --- a/src/lib/serialization/serializers/reflections/project.ts +++ b/src/lib/serialization/serializers/reflections/project.ts @@ -9,9 +9,8 @@ export class ProjectReflectionSerializer extends ReflectionSerializerComponent

r instanceof ProjectReflection; + supports(t: unknown) { + return t instanceof ProjectReflection; } toObject(container: ProjectReflection, obj?: any): any { diff --git a/src/lib/serialization/serializers/reflections/signature.ts b/src/lib/serialization/serializers/reflections/signature.ts index b3c512ace..1591f3588 100644 --- a/src/lib/serialization/serializers/reflections/signature.ts +++ b/src/lib/serialization/serializers/reflections/signature.ts @@ -6,9 +6,8 @@ import { ReflectionSerializerComponent } from '../../components'; @Component({name: 'serializer:signature-reflection'}) export class SignatureReflectionSerializer extends ReflectionSerializerComponent { - initialize(): void { - super.initialize(); - this.supports = (r: SignatureReflection) => r instanceof SignatureReflection; + supports(t: unknown) { + return t instanceof SignatureReflection; } toObject(signature: SignatureReflection, obj?: any): any { diff --git a/src/lib/serialization/serializers/reflections/type-parameter.ts b/src/lib/serialization/serializers/reflections/type-parameter.ts index 8a91278bc..e86a4aa76 100644 --- a/src/lib/serialization/serializers/reflections/type-parameter.ts +++ b/src/lib/serialization/serializers/reflections/type-parameter.ts @@ -6,9 +6,8 @@ import { ReflectionSerializerComponent } from '../../components'; @Component({name: 'serializer:type-parameter-reflection'}) export class TypeParameterReflectionSerializer extends ReflectionSerializerComponent { - initialize(): void { - super.initialize(); - this.supports = (r: TypeParameterReflection) => r instanceof TypeParameterReflection; + supports(t: unknown) { + return t instanceof TypeParameterReflection; } toObject(typeParameter: TypeParameterReflection, obj?: any): any { diff --git a/src/lib/serialization/serializers/sources/source-reference.ts b/src/lib/serialization/serializers/sources/source-reference.ts index 307f1d3ca..8836dc054 100644 --- a/src/lib/serialization/serializers/sources/source-reference.ts +++ b/src/lib/serialization/serializers/sources/source-reference.ts @@ -8,12 +8,13 @@ export class SourceReferenceContainerSerializer extends SerializerComponent instance instanceof SourceReferenceWrapper; serializeGroupSymbol = SourceReferenceWrapper; + serializeGroup(instance: unknown) { + return instance instanceof SourceReferenceWrapper; + } - initialize(): void { - super.initialize(); - this.supports = (s: SourceReferenceWrapper) => s instanceof SourceReferenceWrapper; + supports(t: unknown) { + return t instanceof SourceReferenceWrapper; } toObject(sourceReferenceContainer: SourceReferenceWrapper, obj?: any): any { diff --git a/src/lib/serialization/serializers/types/abstract.ts b/src/lib/serialization/serializers/types/abstract.ts index 2f9fd197a..03991000a 100644 --- a/src/lib/serialization/serializers/types/abstract.ts +++ b/src/lib/serialization/serializers/types/abstract.ts @@ -8,9 +8,8 @@ export class TypeSerializer extends TypeSerializerComponent { static PRIORITY = 1000; - initialize(): void { - super.initialize(); - this.supports = (t: Type) => true; + supports(t: unknown) { + return t instanceof Type; } toObject(type: Type, obj?: any): any { diff --git a/src/lib/serialization/serializers/types/array.ts b/src/lib/serialization/serializers/types/array.ts index 7a1ccc1b0..f2ea7128d 100644 --- a/src/lib/serialization/serializers/types/array.ts +++ b/src/lib/serialization/serializers/types/array.ts @@ -6,9 +6,8 @@ import { TypeSerializerComponent } from '../../components'; @Component({name: 'serializer:array-type'}) export class ArrayTypeSerializer extends TypeSerializerComponent { - initialize(): void { - super.initialize(); - this.supports = (t: ArrayType) => t instanceof ArrayType; + supports(t: unknown) { + return t instanceof ArrayType; } toObject(arrayType: ArrayType, obj?: any): any { diff --git a/src/lib/serialization/serializers/types/intersection-union.ts b/src/lib/serialization/serializers/types/intersection-union.ts index e22cbde05..65b23f5b1 100644 --- a/src/lib/serialization/serializers/types/intersection-union.ts +++ b/src/lib/serialization/serializers/types/intersection-union.ts @@ -8,9 +8,8 @@ export type IntersectionUnion = IntersectionType | UnionType; @Component({name: 'serializer:intersection-type'}) export class IntersectionTypeSerializer extends TypeSerializerComponent { - initialize(): void { - super.initialize(); - this.supports = (t: IntersectionUnion) => t instanceof IntersectionType || t instanceof UnionType; + supports(t: unknown) { + return t instanceof IntersectionType || t instanceof UnionType; } toObject(intersectionUnion: IntersectionUnion, obj?: any): any { diff --git a/src/lib/serialization/serializers/types/intrinsic.ts b/src/lib/serialization/serializers/types/intrinsic.ts index 9e8e98d06..fc4b5d8ca 100644 --- a/src/lib/serialization/serializers/types/intrinsic.ts +++ b/src/lib/serialization/serializers/types/intrinsic.ts @@ -6,9 +6,8 @@ import { TypeSerializerComponent } from '../../components'; @Component({name: 'serializer:intrinsic-type'}) export class IntrinsicTypeSerializer extends TypeSerializerComponent { - initialize(): void { - super.initialize(); - this.supports = (t: IntrinsicType) => t instanceof IntrinsicType; + supports(t: unknown) { + return t instanceof IntrinsicType; } toObject(intrinsic: IntrinsicType, obj?: any): any { diff --git a/src/lib/serialization/serializers/types/reference.ts b/src/lib/serialization/serializers/types/reference.ts index 30db62863..559000750 100644 --- a/src/lib/serialization/serializers/types/reference.ts +++ b/src/lib/serialization/serializers/types/reference.ts @@ -6,9 +6,8 @@ import { TypeSerializerComponent } from '../../components'; @Component({name: 'serializer:reference-type'}) export class ReferenceTypeSerializer extends TypeSerializerComponent { - initialize(): void { - super.initialize(); - this.supports = (t: ReferenceType) => t instanceof ReferenceType; + supports(t: unknown) { + return t instanceof ReferenceType; } toObject(reference: ReferenceType, obj?: any): any { diff --git a/src/lib/serialization/serializers/types/reflection.ts b/src/lib/serialization/serializers/types/reflection.ts index e04742e87..feddf59ce 100644 --- a/src/lib/serialization/serializers/types/reflection.ts +++ b/src/lib/serialization/serializers/types/reflection.ts @@ -6,11 +6,10 @@ import { TypeSerializerComponent } from '../../components'; @Component({name: 'serializer:reflection-type'}) export class ReflectionTypeSerializer extends TypeSerializerComponent { - private declaration: DeclarationReflection; + private declaration?: DeclarationReflection; - initialize(): void { - super.initialize(); - this.supports = (t: ReflectionType) => t instanceof ReflectionType; + supports(t: unknown) { + return t instanceof ReflectionType; } toObject(reference: ReflectionType, obj?: any): any { diff --git a/src/lib/serialization/serializers/types/string-literal.ts b/src/lib/serialization/serializers/types/string-literal.ts index ebc900c30..0fb4906a5 100644 --- a/src/lib/serialization/serializers/types/string-literal.ts +++ b/src/lib/serialization/serializers/types/string-literal.ts @@ -6,9 +6,8 @@ import { TypeSerializerComponent } from '../../components'; @Component({name: 'serializer:string-literal-type'}) export class StringLiteralTypeSerializer extends TypeSerializerComponent { - initialize(): void { - super.initialize(); - this.supports = (t: StringLiteralType) => t instanceof StringLiteralType; + supports(t: unknown) { + return t instanceof StringLiteralType; } toObject(stringLiteral: StringLiteralType, obj?: any): any { diff --git a/src/lib/serialization/serializers/types/tuple.ts b/src/lib/serialization/serializers/types/tuple.ts index 9bcbe88f8..617016cf3 100644 --- a/src/lib/serialization/serializers/types/tuple.ts +++ b/src/lib/serialization/serializers/types/tuple.ts @@ -6,9 +6,8 @@ import { TypeSerializerComponent } from '../../components'; @Component({name: 'serializer:tuple-type'}) export class TupleTypeSerializer extends TypeSerializerComponent { - initialize(): void { - super.initialize(); - this.supports = (t: TupleType) => t instanceof TupleType; + supports(t: unknown) { + return t instanceof TupleType; } toObject(tuple: TupleType, obj?: any): any { diff --git a/src/lib/serialization/serializers/types/type-operator.ts b/src/lib/serialization/serializers/types/type-operator.ts index a9fe0266c..5f2235913 100644 --- a/src/lib/serialization/serializers/types/type-operator.ts +++ b/src/lib/serialization/serializers/types/type-operator.ts @@ -6,9 +6,8 @@ import { TypeSerializerComponent } from '../../components'; @Component({name: 'serializer:type-operator-type'}) export class TypeOperatorTypeSerializer extends TypeSerializerComponent { - initialize(): void { - super.initialize(); - this.supports = (t: TypeOperatorType) => t instanceof TypeOperatorType; + supports(t: unknown) { + return t instanceof TypeOperatorType; } toObject(typeOperator: TypeOperatorType, obj?: any): any { diff --git a/src/lib/serialization/serializers/types/type-parameter.ts b/src/lib/serialization/serializers/types/type-parameter.ts index 1404b5a18..92027ecb8 100644 --- a/src/lib/serialization/serializers/types/type-parameter.ts +++ b/src/lib/serialization/serializers/types/type-parameter.ts @@ -6,9 +6,8 @@ import { TypeSerializerComponent } from '../../components'; @Component({name: 'serializer:type-parameter-type'}) export class TypeParameterTypeSerializer extends TypeSerializerComponent { - initialize(): void { - super.initialize(); - this.supports = (t: TypeParameterType) => t instanceof TypeParameterType; + supports(t: unknown) { + return t instanceof TypeParameterType; } toObject(typeParameter: TypeParameterType, obj?: any): any { diff --git a/src/lib/serialization/serializers/types/unknown.ts b/src/lib/serialization/serializers/types/unknown.ts index 6ec176195..56784473a 100644 --- a/src/lib/serialization/serializers/types/unknown.ts +++ b/src/lib/serialization/serializers/types/unknown.ts @@ -6,9 +6,8 @@ import { TypeSerializerComponent } from '../../components'; @Component({name: 'serializer:unknown-type'}) export class UnknownTypeSerializer extends TypeSerializerComponent { - initialize(): void { - super.initialize(); - this.supports = (t: UnknownType) => t instanceof UnknownType; + supports(t: unknown) { + return t instanceof UnknownType; } toObject(unknown: UnknownType, obj?: any): any { diff --git a/src/lib/ts-internal.ts b/src/lib/ts-internal.ts index 39c33cab6..3e9964717 100644 --- a/src/lib/ts-internal.ts +++ b/src/lib/ts-internal.ts @@ -102,7 +102,7 @@ export function getEffectiveBaseTypeNode(node: ts.ClassLikeDeclaration | ts.Inte } // https://github.com/Microsoft/TypeScript/blob/v2.1.4/src/compiler/utilities.ts#L1734 -export function getClassImplementsHeritageClauseElements(node: ts.ClassLikeDeclaration) { +export function getClassImplementsHeritageClauseElements(node: ts.ClassLikeDeclaration): ts.NodeArray | undefined { return tsany.getClassImplementsHeritageClauseElements.apply(this, arguments); } @@ -130,15 +130,55 @@ export const optionDeclarations: CommandLineOption[] = tsany.optionDeclarations; /** * Command line options * - * https://github.com/Microsoft/TypeScript/blob/v2.1.4/src/compiler/types.ts#L3344 + * https://github.com/Microsoft/TypeScript/blob/v2.1.4/src/compiler/types.ts#L3310 */ -export interface CommandLineOption { +export interface CommandLineOptionBase { name: string; - type: string; - shortName: string; - description: DiagnosticsEnumValue; - paramType: DiagnosticsEnumValue; -} + /** + * a value of a primitive type, or an object literal mapping named values to actual values + */ + type: 'string' | 'number' | 'boolean' | 'object' | 'list' | Map; + /** + * True if option value is a path or fileName + */ + isFilePath?: boolean; + /** + * A short mnemonic for convenience - for instance, 'h' can be used in place of 'help' + */ + shortName?: string; + /** + * The message describing what the command line switch does + */ + description?: ts.DiagnosticMessage; + /** + * The name to be used for a non-boolean option's parameter + */ + paramType?: ts.DiagnosticMessage; + experimental?: boolean; + /** + * True if option can only be specified via tsconfig.json file + */ + isTSConfigOnly?: boolean; +} + +export interface CommandLineOptionOfPrimitiveType extends CommandLineOptionBase { + type: 'string' | 'number' | 'boolean'; +} + +export interface CommandLineOptionOfCustomType extends CommandLineOptionBase { + type: Map; // an object literal mapping named values to actual values +} + +export interface TsConfigOnlyOption extends CommandLineOptionBase { + type: 'object'; +} + +export interface CommandLineOptionOfListType extends CommandLineOptionBase { + type: 'list'; + element: CommandLineOptionOfCustomType | CommandLineOptionOfPrimitiveType; +} + +export type CommandLineOption = CommandLineOptionOfCustomType | CommandLineOptionOfPrimitiveType | TsConfigOnlyOption | CommandLineOptionOfListType; export const Diagnostics: { [key: string]: DiagnosticsEnumValue; diff --git a/src/lib/utils/component.ts b/src/lib/utils/component.ts index 6ee0b16ff..5a4e27abd 100644 --- a/src/lib/utils/component.ts +++ b/src/lib/utils/component.ts @@ -5,7 +5,7 @@ import { EventDispatcher, Event, EventMap } from './events'; import { DeclarationOption } from './options/declaration'; export interface ComponentHost { - application: Application; + readonly application: Application; } export interface Component extends AbstractComponent { @@ -22,7 +22,7 @@ export interface ComponentOptions { internal?: boolean; } -const childMappings: {host: any, child: Function}[] = []; +const childMappings: {host: ChildableComponent, child: Function}[] = []; export function Component(options: ComponentOptions): ClassDecorator { return (target: Function) => { @@ -49,14 +49,14 @@ export function Component(options: ComponentOptions): ClassDecorator { const internal = !!options.internal; if (name && !internal) { - for (let childMapping of childMappings) { + for (const childMapping of childMappings) { if (!(proto instanceof childMapping.child)) { continue; } const host = childMapping.host; - const defaults = host._defaultComponents || (host._defaultComponents = {}); - defaults[name] = target; + host['_defaultComponents'] = host['_defaultComponents'] || {}; + host['_defaultComponents'][name] = target as any; break; } } @@ -69,12 +69,12 @@ export function Option(options: DeclarationOption): PropertyDecorator { throw new Error('The `Option` decorator can only be used on properties within an `AbstractComponent` subclass.'); } - const list = target['_componentOptions'] || (target['_componentOptions'] = []); options.component = target['_componentName']; - list.push(options); + target['_componentOptions'] = target['_componentOptions'] || []; + target['_componentOptions'].push(options); Object.defineProperty(target, propertyKey, { - get: function () { + get: function (this: AbstractComponent) { return this.application.options.getValue(options.name); }, enumerable: true, @@ -99,6 +99,13 @@ export class ComponentEvent extends Event { } } +/** + * Dummy owner to be passed in to AbstractComponent / ChildableComponents if the class being constructed is + * the application. The application does not have an owner and will return itself for component.application + * and component.owner. + */ +export const DUMMY_APPLICATION_OWNER = Symbol(); + /** * Component base class. */ @@ -106,22 +113,22 @@ export abstract class AbstractComponent extends EventDi /** * The owner of this component instance. */ - private _componentOwner: O; + private _componentOwner: O | typeof DUMMY_APPLICATION_OWNER; /** * The name of this component as set by the @Component decorator. */ - public componentName: string; + public componentName!: string; /** * A list of options defined by this component. */ - private _componentOptions: DeclarationOption[]; + private _componentOptions?: DeclarationOption[]; /** * Create new Component instance. */ - constructor(owner: O) { + constructor(owner: O | typeof DUMMY_APPLICATION_OWNER) { super(); this._componentOwner = owner; this.initialize(); @@ -133,11 +140,10 @@ export abstract class AbstractComponent extends EventDi protected initialize() {} protected bubble(name: Event|EventMap|string, ...args: any[]) { - super.trigger.apply(this, arguments); + super.trigger(name, ...args); - const owner = this.owner; - if (owner instanceof AbstractComponent) { - owner.bubble.apply(this._componentOwner, arguments); + if (this.owner instanceof AbstractComponent && this._componentOwner !== DUMMY_APPLICATION_OWNER) { + this.owner.bubble(name, ...args); } return this; @@ -147,25 +153,25 @@ export abstract class AbstractComponent extends EventDi * Return all option declarations emitted by this component. */ getOptionDeclarations(): DeclarationOption[] { - return this._componentOptions ? this._componentOptions.slice() : []; + return (this._componentOptions || []).slice(); } /** * Return the application / root component instance. */ get application(): Application { - if (this._componentOwner) { - return this._componentOwner.application; - } else { - return null; - } + return this._componentOwner === DUMMY_APPLICATION_OWNER + ? this as any as Application + : this._componentOwner.application; } /** * Return the owner of this component. */ get owner(): O { - return this._componentOwner; + return this._componentOwner === DUMMY_APPLICATION_OWNER + ? this as any + : this._componentOwner; } } @@ -176,43 +182,39 @@ export abstract class ChildableComponent}; + private _defaultComponents?: {[name: string]: ComponentClass}; /** * Create new Component instance. */ - constructor(owner: O) { + constructor(owner: O | typeof DUMMY_APPLICATION_OWNER) { super(owner); - for (let name in this._defaultComponents) { - this.addComponent(name, this._defaultComponents[name]); - } + _.entries(this._defaultComponents || {}).forEach(([name, component]) => { + this.addComponent(name, component); + }); } /** * Retrieve a plugin instance. * - * @returns The instance of the plugin or NULL if no plugin with the given class is attached. + * @returns The instance of the plugin or undefined if no plugin with the given class is attached. */ - getComponent(name: string): C { - if (this._componentChildren && this._componentChildren[name]) { - return this._componentChildren[name]; - } else { - return null; - } + getComponent(name: string): C | undefined { + return (this._componentChildren || {})[name]; } getComponents(): C[] { - return _.values(this._componentChildren); + return _.values(this._componentChildren); } hasComponent(name: string): boolean { - return !!(this._componentChildren && this._componentChildren[name]); + return !!(this._componentChildren || {})[name]; } - addComponent(name: string, componentClass: T|ComponentClass): T { + addComponent(name: string, componentClass: T|ComponentClass): T { if (!this._componentChildren) { this._componentChildren = {}; } @@ -235,27 +237,19 @@ export abstract class ChildableComponent { - (events: U, name: string, callback: Function, options: T): U; + (events: U, name: string, callback: Function | undefined, options: T): U; } interface EventTriggerer { @@ -69,7 +69,11 @@ const eventSplitter = /\s+/; * space-separated events `"change blur", callback` and jQuery-style event * maps `{event: callback}`). */ -function eventsApi(iteratee: EventIteratee, events: U, name: EventMap|string, callback: EventCallback, options: T): U { +function eventsApi( + iteratee: EventIteratee, + events: U, name: EventMap | string | undefined, + callback: EventCallback | undefined, + options: T): U { let i = 0, names: string[]; if (name && typeof name === 'object') { @@ -81,6 +85,7 @@ function eventsApi(iteratee: EventIteratee, events: U, name: EventMa for (names = _.keys(name); i < names.length ; i++) { events = eventsApi(iteratee, events, names[i], name[names[i]], options); } + // tslint:disable-next-line:strict-type-predicates } else if (name && typeof name === 'string' && eventSplitter.test(name)) { // Handle space separated event names by delegating them individually. for (names = name.split(eventSplitter); i < names.length; i++) { @@ -97,7 +102,7 @@ function eventsApi(iteratee: EventIteratee, events: U, name: EventMa /** * The reducing API that adds a callback to the `events` object. */ -function onApi(events: EventHandlers, name: string, callback: EventCallback, options: OnApiOptions): EventHandlers { +function onApi(events: EventHandlers, name: string, callback: EventCallback | undefined, options: OnApiOptions): EventHandlers { if (callback) { const handlers = events[name] || (events[name] = []); const context = options.context, ctx = options.ctx, listening = options.listening, priority = options.priority; @@ -122,7 +127,7 @@ function onApi(events: EventHandlers, name: string, callback: EventCallback, opt /** * The reducing API that removes a callback from the `events` object. */ -function offApi(events: EventHandlers, name: string, callback: EventCallback, options: OffApiOptions): EventHandlers { +function offApi(events: EventHandlers | undefined, name: string, callback: EventCallback | undefined, options: OffApiOptions): EventHandlers | undefined { if (!events) { return; } @@ -187,7 +192,7 @@ function offApi(events: EventHandlers, name: string, callback: EventCallback, op * Reduces the event callbacks into a map of `{event: onceWrapper`.} * `offer` unbinds the `onceWrapper` after it has been called. */ -function onceMap(map: EventMap, name: string, callback: EventCallback, offer: Function): EventMap { +function onceMap(map: EventMap, name: string, callback: EventCallback | undefined, offer: Function): EventMap { if (callback) { const once = map[name] = _.once(function() { offer(name, once); @@ -203,7 +208,7 @@ function onceMap(map: EventMap, name: string, callback: EventCallback, offer: Fu /** * Handles triggering the appropriate event callbacks. */ -function triggerApi(objEvents: EventHandlers, name: string, callback: Function, args: any[], triggerer: EventTriggerer = triggerEvents): EventHandlers { +function triggerApi(objEvents: EventHandlers, name: string, callback: Function | undefined, args: any[], triggerer: EventTriggerer = triggerEvents): EventHandlers { if (objEvents) { const events = objEvents[name]; let allEvents = objEvents['all']; @@ -249,12 +254,12 @@ export class Event { /** * Has [[Event.stopPropagation]] been called? */ - private _isPropagationStopped: boolean; + private _isPropagationStopped = false; /** * Has [[Event.preventDefault]] been called? */ - private _isDefaultPrevented: boolean; + private _isDefaultPrevented = false; /** * Create a new Event instance. @@ -309,22 +314,25 @@ export class EventDispatcher { /** * Map of all handlers registered with the "on" function. */ - private _events: EventHandlers; + private _events?: EventHandlers; /** * Map of all objects this instance is listening to. */ - private _listeningTo: EventListeners; + private _listeningTo?: EventListeners; /** * Map of all objects that are listening to this instance. */ - private _listeners: EventListeners; + private _listeners?: EventListeners; /** * A unique id that identifies this instance. */ - private _listenId: string; + private get _listenId(): string { + return this._savedListenId || (this._savedListenId = _.uniqueId('l')); + } + private _savedListenId?: string; /** * Bind an event to a `callback` function. Passing `"all"` will bind @@ -341,7 +349,7 @@ export class EventDispatcher { /** * Guard the `listening` argument from the public API. */ - private internalOn(name: EventMap|string, callback: EventCallback, context?: any, priority: number = 0, listening?: EventListener) { + private internalOn(name: EventMap | string, callback: EventCallback | undefined, context?: any, priority: number = 0, listening?: EventListener) { this._events = eventsApi(onApi, this._events || {}, name, callback, { context: context, ctx: this, @@ -376,8 +384,8 @@ export class EventDispatcher { * callbacks for all events. */ off(); - off(eventMap: EventMap, context?: any); - off(name: string, callback?: EventCallback, context?: any); + off(eventMap: EventMap | undefined, context?: any); + off(name: string | undefined, callback?: EventCallback, context?: any); off(name?: EventMap|string, callback?: EventCallback, context?: any) { if (!this._events) { return this; @@ -400,14 +408,14 @@ export class EventDispatcher { if (!obj) { return this; } - const id = obj._listenId || (obj._listenId = _.uniqueId('l')); + const id = obj._listenId; const listeningTo = this._listeningTo || (this._listeningTo = {}); let listening = listeningTo[id]; // This object is not listening to any other events on `obj` yet. // Setup the necessary references to track the listening callbacks. if (!listening) { - const thisId = this._listenId || (this._listenId = _.uniqueId('l')); + const thisId = this._listenId; listening = listeningTo[id] = { obj: obj, objId: id, diff --git a/src/lib/utils/loggers.ts b/src/lib/utils/loggers.ts index 6e485bf1c..fe54436c5 100644 --- a/src/lib/utils/loggers.ts +++ b/src/lib/utils/loggers.ts @@ -131,7 +131,7 @@ export class Logger { let output: string; if (diagnostic.file) { output = diagnostic.file.fileName; - output += '(' + ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start).line + ')'; + output += '(' + ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start || 0).line + ')'; output += ts.sys.newLine + ' ' + ts.flattenDiagnosticMessageText(diagnostic.messageText, ts.sys.newLine); } else { output = ts.flattenDiagnosticMessageText(diagnostic.messageText, ts.sys.newLine); diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index c359c6f32..a55f8bb64 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -1,3 +1,5 @@ +import * as _ from 'lodash'; + export enum ParameterHint { File, Directory @@ -13,9 +15,13 @@ export enum ParameterType { } export enum ParameterScope { - TypeDoc, TypeScript + TypeDoc, + TypeScript } +/** + * TODO: This should be a union type of multiple possible option types. + */ export interface DeclarationOption { name: string; component?: string; @@ -24,32 +30,30 @@ export interface DeclarationOption { type?: ParameterType; hint?: ParameterHint; scope?: ParameterScope; - map?: {}; + map?: 'object' | Map | { [ key: string]: any }; mapError?: string; defaultValue?: any; convert?: (param: OptionDeclaration, value?: any) => any; } export class OptionDeclaration { - name: string; - - short: string; + name!: string; - component: string; + component?: string; - help: string; + short?: string; - type: ParameterType; + help!: string; - hint: ParameterHint; + type: ParameterType = ParameterType.String; - scope: ParameterScope; + hint?: ParameterHint; - protected map: Object | Map | 'object'; + scope: ParameterScope = ParameterScope.TypeDoc; - mapError: string; + protected map?: { [k: string]: any } | 'object'; - isArray: boolean; + mapError?: string; defaultValue: any; @@ -57,9 +61,6 @@ export class OptionDeclaration { for (let key in data) { this[key] = data[key]; } - - this.type = this.type || ParameterType.String; - this.scope = this.scope || ParameterScope.TypeDoc; } getNames(): string[] { @@ -72,29 +73,36 @@ export class OptionDeclaration { return result; } - convert(value: any, errorCallback?: Function): any { + /** + * + * @param value the value the user passed in + * @param errorCallback + */ + convert(value: unknown, errorCallback?: (format: string, ...args: string[]) => void): any { switch (this.type) { case ParameterType.Number: - value = parseInt(value, 10); + value = parseInt(value + '', 10); break; case ParameterType.Boolean: - value = (typeof value === void 0 ? true : !!value); + value = !!value; break; case ParameterType.String: - value = value || ''; + value = value ? value + '' : ''; break; case ParameterType.Array: if (!value) { value = []; + // TSLint *should* be correct here, but tslint doesn't know about user config files. + // tslint:disable-next-line:strict-type-predicates } else if (typeof value === 'string') { value = value.split(','); } break; case ParameterType.Map: - const map = this.map; + const map = this.map || {}; if (map !== 'object') { const key = value ? (value + '').toLowerCase() : ''; - const values = Object.keys(map).map(key => map[key]); + const values = _.values(map); if (map instanceof Map) { value = map.has(key) ? map.get(key) : value; diff --git a/src/lib/utils/options/options.ts b/src/lib/utils/options/options.ts index bc78341e9..f3cf56f57 100644 --- a/src/lib/utils/options/options.ts +++ b/src/lib/utils/options/options.ts @@ -20,9 +20,13 @@ export interface OptionsReadResult { } export class DiscoverEvent extends Event { + readonly mode: OptionsReadMode; data: any; - mode: OptionsReadMode; + constructor(name: string, mode: OptionsReadMode) { + super(name); + this.mode = mode; + } inputFiles: string[] = []; @@ -49,11 +53,11 @@ export class DiscoverEvent extends Event { @Component({name: 'options', internal: true, childClass: OptionsComponent}) export class Options extends ChildableComponent { - private declarations: {[name: string]: OptionDeclaration}; + private declarations!: {[name: string]: OptionDeclaration}; - private values: {[name: string]: any}; + private values!: {[name: string]: any}; - private compilerOptions: ts.CompilerOptions; + private compilerOptions!: ts.CompilerOptions; initialize() { this.declarations = {}; @@ -65,9 +69,8 @@ export class Options extends ChildableComponent { } read(data: any = {}, mode: OptionsReadMode = OptionsReadMode.Fetch): OptionsReadResult { - const event = new DiscoverEvent(DiscoverEvent.DISCOVER); + const event = new DiscoverEvent(DiscoverEvent.DISCOVER, mode); event.data = data; - event.mode = mode; this.trigger(event); this.setValues(event.data, '', event.addError.bind(event)); @@ -106,23 +109,13 @@ export class Options extends ChildableComponent { return _.clone(this.values); } - getDeclaration(name: string): OptionDeclaration { - name = name.toLowerCase(); - - if (name in this.declarations) { - return this.declarations[name]; - } + getDeclaration(name: string): OptionDeclaration | undefined { + return this.declarations[name.toLowerCase()]; } getDeclarationsByScope(scope: ParameterScope): OptionDeclaration[] { - const result: OptionDeclaration[] = []; - for (let name in this.declarations) { - const declaration = this.declarations[name]; - if (declaration.scope === scope) { - result.push(declaration); - } - } - + const result = _.values(this.declarations) + .filter(declaration => declaration.scope === scope); return _.uniq(result); } @@ -130,7 +123,7 @@ export class Options extends ChildableComponent { return this.compilerOptions; } - setValue(name: string|OptionDeclaration, value: any, errorCallback?: Function) { + setValue(name: string | OptionDeclaration, value: any, errorCallback?: (format: string, ...args: string[]) => void) { const declaration = name instanceof OptionDeclaration ? name : this.getDeclaration(name); if (!declaration) { return; @@ -144,7 +137,7 @@ export class Options extends ChildableComponent { } } - setValues(obj: Object, prefix: string = '', errorCallback?: Function) { + setValues(obj: Object, prefix: string = '', errorCallback?: (format: string, ...args: string[]) => void) { for (let key in obj) { const value = obj[key]; const declaration = this.getDeclaration(key); @@ -164,7 +157,9 @@ export class Options extends ChildableComponent { for (let name of decl.getNames()) { if (name in this.declarations) { - this.application.logger.error('The option "%s" has already been registered by the "%s" component.', name, this.declarations[name].component); + this.application.logger.error('The option "%s" has already been registered by the "%s" component.', + name, + this.declarations[name].component || '__unknown'); } else { this.declarations[name] = decl; } @@ -179,16 +174,14 @@ export class Options extends ChildableComponent { removeDeclaration(declaration: OptionDeclaration) { const names = _.keys(this.declarations); - let name: string; - for (name in names) { + for (const name of names) { if (this.declarations[name] === declaration) { delete this.declarations[name]; } } - name = declaration.name; - if (name in this.values) { - delete this.values[name]; + if (declaration.name in this.values) { + delete this.values[declaration.name]; } } diff --git a/src/lib/utils/options/readers/arguments.ts b/src/lib/utils/options/readers/arguments.ts index d904a2d7d..068b4210c 100644 --- a/src/lib/utils/options/readers/arguments.ts +++ b/src/lib/utils/options/readers/arguments.ts @@ -20,16 +20,16 @@ export class ArgumentsReader extends OptionsComponent { /** * Read and store the given list of arguments. * - * @param args The list of arguments that should be parsed. When omitted the + * @param passedArgs The list of arguments that should be parsed. When omitted the * current command line arguments will be used. * @param ignoreUnknownArgs Should unknown arguments be ignored? If so the parser * will simply skip all unknown arguments. * @returns TRUE on success, otherwise FALSE. */ - private parseArguments(event: DiscoverEvent, args?: string[]) { + private parseArguments(event: DiscoverEvent, passedArgs?: string[]) { let index = 0; const owner = this.owner; - args = args || process.argv.slice(2); + const args = passedArgs || process.argv.slice(2); function readArgument(arg: string) { const declaration = owner.getDeclaration(arg); @@ -46,7 +46,7 @@ export class ArgumentsReader extends OptionsComponent { } } - const files = []; + const files: string[] = []; while (index < args.length) { const arg = args[index++]; diff --git a/src/lib/utils/options/readers/tsconfig.ts b/src/lib/utils/options/readers/tsconfig.ts index d0430bb9e..02d44c75b 100644 --- a/src/lib/utils/options/readers/tsconfig.ts +++ b/src/lib/utils/options/readers/tsconfig.ts @@ -16,7 +16,7 @@ export class TSConfigReader extends OptionsComponent { type: ParameterType.String, hint: ParameterHint.File }) - options: string; + options!: string; /** * The name of the parameter that specifies the tsconfig file. @@ -44,13 +44,13 @@ export class TSConfigReader extends OptionsComponent { this.load(event, Path.resolve(event.data[TSConfigReader.OPTIONS_KEY])); } else if (TSConfigReader.PROJECT_KEY in event.data) { // The `project` option may be a directory or file, so use TS to find it - let file: string = ts.findConfigFile(event.data[TSConfigReader.PROJECT_KEY], ts.sys.fileExists); + const file = ts.findConfigFile(event.data[TSConfigReader.PROJECT_KEY], ts.sys.fileExists); // If file is undefined, we found no file to load. if (file) { this.load(event, file); } } else if (this.application.isCLI) { - let file: string = ts.findConfigFile('.', ts.sys.fileExists); + const file = ts.findConfigFile('.', ts.sys.fileExists); // If file is undefined, we found no file to load. if (file) { this.load(event, file); diff --git a/src/lib/utils/options/readers/typedoc.ts b/src/lib/utils/options/readers/typedoc.ts index a03e8df11..abc0b8ec7 100644 --- a/src/lib/utils/options/readers/typedoc.ts +++ b/src/lib/utils/options/readers/typedoc.ts @@ -14,7 +14,7 @@ export class TypedocReader extends OptionsComponent { type: ParameterType.String, hint: ParameterHint.File }) - options: string; + options!: string; /** * The name of the parameter that specifies the options file. diff --git a/src/lib/utils/options/sources/component.ts b/src/lib/utils/options/sources/component.ts index 5748311a2..3d7a17964 100644 --- a/src/lib/utils/options/sources/component.ts +++ b/src/lib/utils/options/sources/component.ts @@ -3,7 +3,7 @@ import { OptionsComponent } from '../options'; @Component({name: 'options:component'}) export class ComponentSource extends OptionsComponent { - private knownComponents: string[]; + private knownComponents!: string[]; protected initialize() { this.knownComponents = []; diff --git a/src/lib/utils/options/sources/typescript.ts b/src/lib/utils/options/sources/typescript.ts index c55baa7fd..5df578217 100644 --- a/src/lib/utils/options/sources/typescript.ts +++ b/src/lib/utils/options/sources/typescript.ts @@ -7,7 +7,7 @@ import { DeclarationOption, ParameterScope, ParameterType, ParameterHint } from @Component({name: 'options:typescript'}) export class TypeScriptSource extends OptionsComponent { - private declarations: DeclarationOption[]; + private declarations!: DeclarationOption[]; /** * A list of all TypeScript parameters that should be ignored. @@ -19,11 +19,10 @@ export class TypeScriptSource extends OptionsComponent { ]; initialize() { - const ignored = TypeScriptSource.IGNORED; this.declarations = []; for (let declaration of _ts.optionDeclarations) { - if (ignored.indexOf(declaration.name) === -1) { + if (TypeScriptSource.IGNORED.indexOf(declaration.name) === -1) { this.addTSOption(declaration); } } @@ -40,7 +39,7 @@ export class TypeScriptSource extends OptionsComponent { const param: DeclarationOption = { name: option.name, short: option.shortName, - help: option.description ? option.description.key : null, + help: option.description ? option.description.key : '', scope: ParameterScope.TypeScript, component: this.componentName }; diff --git a/src/lib/utils/plugins.ts b/src/lib/utils/plugins.ts index 0330c69b6..6cefbf854 100644 --- a/src/lib/utils/plugins.ts +++ b/src/lib/utils/plugins.ts @@ -1,6 +1,5 @@ import * as FS from 'fs'; import * as Path from 'path'; -import * as Util from 'util'; import { Application } from '../application'; import { AbstractComponent, Component, Option } from './component'; @@ -13,7 +12,7 @@ export class PluginHost extends AbstractComponent { help: 'Specify the npm plugins that should be loaded. Omit to load all installed plugins, set to \'none\' to load no plugins.', type: ParameterType.Array }) - plugins: string[]; + plugins!: string[]; /** * Load the given list of npm plugins. @@ -29,6 +28,8 @@ export class PluginHost extends AbstractComponent { let i: number, c: number = plugins.length; for (i = 0; i < c; i++) { const plugin = plugins[i]; + // TSLint would be correct here, but it doesn't take into account user config files. + // tslint:disable-next-line:strict-type-predicates if (typeof plugin !== 'string') { logger.error('Unknown plugin %s', plugin); return false; @@ -54,8 +55,10 @@ export class PluginHost extends AbstractComponent { } catch (error) { logger.error('The plugin %s could not be loaded.', plugin); logger.writeln(error.stack); + return false; } } + return true; } /** @@ -128,8 +131,8 @@ export class PluginHost extends AbstractComponent { * Test whether the given package info describes a TypeDoc plugin. */ function isPlugin(info: any): boolean { - const keywords: string[] = info.keywords; - if (!keywords || !Util.isArray(keywords)) { + const keywords: unknown[] = info.keywords; + if (!keywords || !Array.isArray(keywords)) { return false; } diff --git a/src/test/converter.ts b/src/test/converter.ts index 1a61430cd..a9c2ead65 100644 --- a/src/test/converter.ts +++ b/src/test/converter.ts @@ -79,7 +79,7 @@ describe('Converter', function() { } describe(directory, function() { - let result: ProjectReflection; + let result: ProjectReflection | undefined; it('converts fixtures', function() { resetReflectionID(); @@ -89,7 +89,7 @@ describe('Converter', function() { it('matches specs', function() { const specs = JSON.parse(FS.readFileSync(Path.join(path, 'specs.json')).toString()); - let data = JSON.stringify(result.toObject(), null, ' '); + let data = JSON.stringify(result!.toObject(), null, ' '); data = data.split(normalizePath(base)).join('%BASE%'); compareReflections(JSON.parse(data), specs); @@ -116,7 +116,7 @@ describe('Converter with excludeNotExported=true', function() { }); }); - let result: ProjectReflection; + let result: ProjectReflection | undefined; describe('export-with-local', () => { it('converts fixtures', function() { @@ -127,7 +127,7 @@ describe('Converter with excludeNotExported=true', function() { it('matches specs', function() { const specs = JSON.parse(FS.readFileSync(Path.join(exportWithLocalDir, 'specs-without-exported.json')).toString()); - let data = JSON.stringify(result.toObject(), null, ' '); + let data = JSON.stringify(result!.toObject(), null, ' '); data = data.split(normalizePath(base)).join('%BASE%'); compareReflections(JSON.parse(data), specs); @@ -143,7 +143,7 @@ describe('Converter with excludeNotExported=true', function() { it('matches specs', function() { const specs = JSON.parse(FS.readFileSync(Path.join(classDir, 'specs-without-exported.json')).toString()); - let data = JSON.stringify(result.toObject(), null, ' '); + let data = JSON.stringify(result!.toObject(), null, ' '); data = data.split(normalizePath(base)).join('%BASE%'); compareReflections(JSON.parse(data), specs); diff --git a/src/test/events.ts b/src/test/events.ts index e0da08457..f3b69631c 100644 --- a/src/test/events.ts +++ b/src/test/events.ts @@ -12,9 +12,9 @@ import * as _ from 'lodash'; import { EventDispatcher, Event } from '../lib/utils/events'; class Events extends EventDispatcher { - counter?: number; - counterA?: number; - counterB?: number; + counter = 0; + counterA = 0; + counterB = 0; } describe('Events', function () { @@ -178,11 +178,11 @@ describe('Events', function () { a.listenTo(b, 'event', cb); b.on('event', cb); a.listenTo(b, 'event2', cb); - a.stopListening(null, {event: cb}); + a.stopListening(undefined, {event: cb}); b.trigger('event event2'); b.off(); a.listenTo(b, 'event event2', cb); - a.stopListening(null, 'event'); + a.stopListening(undefined, 'event'); a.stopListening(); b.trigger('event2'); }); @@ -416,13 +416,14 @@ describe('Events', function () { it('listenTo with empty callback does not throw an error', function () { const e = new Events(); - e.listenTo(e, 'foo', null); + e.listenTo(e, 'foo', undefined); e.trigger('foo'); Assert(true); }); it('trigger all for each event', function () { - let a: boolean, b: boolean; + let a = false; + let b = false; const obj = new Events(); obj.counter = 0; obj.on('all', function (event) { @@ -586,7 +587,7 @@ describe('Events', function () { obj.on('x y all', function () { Assert(false); }, obj); - obj.off(null, null, obj); + obj.off(undefined, undefined, obj); obj.trigger('x y'); }); @@ -600,7 +601,7 @@ describe('Events', function () { }; obj.on('x y all', success); obj.on('x y all', fail); - obj.off(null, fail); + obj.off(undefined, fail); obj.trigger('x y'); }); @@ -612,7 +613,7 @@ describe('Events', function () { obj.on('event', function () { Assert(false); }, obj); - obj.off(null, null, obj); + obj.off(undefined, undefined, obj); obj.trigger('event'); }); @@ -711,7 +712,7 @@ describe('Events', function () { obj.once('event', function () { Assert(false); }, context); - obj.off(null, null, context); + obj.off(undefined, undefined, context); obj.trigger('event'); }); @@ -781,7 +782,7 @@ describe('Events', function () { Assert.equal(obj, obj.trigger('noeventssetyet')); Assert.equal(obj, obj.off('noeventssetyet')); - Assert.equal(obj, obj.stopListening(null, 'noeventssetyet')); + Assert.equal(obj, obj.stopListening(undefined, 'noeventssetyet')); Assert.equal(obj, obj.on('a', fn)); Assert.equal(obj, obj.once('c', fn)); Assert.equal(obj, obj.trigger('a')); diff --git a/src/test/renderer.ts b/src/test/renderer.ts index ebbf60a70..e160c896a 100644 --- a/src/test/renderer.ts +++ b/src/test/renderer.ts @@ -44,7 +44,7 @@ function compareDirectories(a, b) { describe('Renderer', function() { const src = Path.join(__dirname, '..', '..', 'examples', 'basic', 'src'); const out = Path.join(__dirname, '..', 'tmp', 'test'); - let app: Application, project: ProjectReflection; + let app: Application, project: ProjectReflection | undefined; before(function() { FS.removeSync(out); @@ -76,7 +76,7 @@ describe('Renderer', function() { it('renders basic example', function() { this.timeout(0); - const result = app.generateDocs(project, out); + const result = app.generateDocs(project!, out); Assert(result === true, 'Application.generateDocs returned errors'); FS.removeSync(Path.join(out, 'assets')); diff --git a/tsconfig.json b/tsconfig.json index 634cfd59a..2195896e8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,9 +8,12 @@ "es2015.collection", "es2015.iterable" ], - "target": "es5", + "target": "es2015", "noImplicitAny": false, + "downlevelIteration": true, "strictFunctionTypes": true, + "strictNullChecks": true, + "strictPropertyInitialization": true, "removeComments": true, "noUnusedLocals": true, "preserveConstEnums": true, diff --git a/tslint.json b/tslint.json index 220e561cc..1718dddfd 100644 --- a/tslint.json +++ b/tslint.json @@ -23,6 +23,7 @@ "no-duplicate-variable": true, "no-empty": false, "no-eval": true, + "no-for-in-array": true, "no-inferrable-types": [ true, "ignore-params" ], "no-shadowed-variable": false, "no-string-literal": false, @@ -33,6 +34,7 @@ "no-use-before-declare": false, "no-var-keyword": true, "no-var-requires": false, + "strict-type-predicates": true, "object-literal-sort-keys": false, "one-line": [ true, "check-open-brace", "check-whitespace", "check-else", "check-catch" ], "quotemark": [ true, "single", "avoid-escape" ],