diff --git a/scripts/docs/templates/common.template.html b/scripts/docs/templates/common.template.html
index 98f1656914..a0d049f313 100644
--- a/scripts/docs/templates/common.template.html
+++ b/scripts/docs/templates/common.template.html
@@ -179,10 +179,40 @@ Improve this doc
<@ endif @>
-
+
+<@- if doc.statics.length -@>
+
Static Methods
+<@- for method in doc.statics @><@ if not method.internal @>
+
+<$ functionSyntax(method) $>
+
+<$ method.description $>
+
+<@ if method.params @>
+<$ paramTable(method.params) $>
+<@ endif @>
+
+<@ if method.this @>
+ Method's `this`
+<$ method.this $>
+
+<@ endif @>
+
+<@ if method.returns @>
+
+
+Returns: <$ typeInfo(method.returns) $>
+
+<@ endif @>
+<@ endif @>
+<@ endfor -@>
+<@ endif @>
+
+
+
<@- if doc.members and doc.members.length @>
-Methods
+Instance Methods
<@- for method in doc.members @>
diff --git a/scripts/docs/typescript-package/index.js b/scripts/docs/typescript-package/index.js
old mode 100644
new mode 100755
index d31dc77440..876c080855
--- a/scripts/docs/typescript-package/index.js
+++ b/scripts/docs/typescript-package/index.js
@@ -1,5 +1,3 @@
-//require('../../tools/transpiler/index.js').init();
-
var basePackage = require('dgeni-packages/base');
var Package = require('dgeni').Package;
var path = require('canonical-path');
@@ -16,6 +14,8 @@ module.exports = new Package('typescript-parsing', [basePackage])
.factory(require('./services/tsParser/getContent'))
.factory(require('./services/tsParser/getDirectiveInfo'))
+.factory(require('./services/convertPrivateClassesToInterfaces'))
+
.factory('EXPORT_DOC_TYPES', function() {
return [
'class',
@@ -23,6 +23,7 @@ module.exports = new Package('typescript-parsing', [basePackage])
'function',
'var',
'const',
+ 'let',
'enum',
'type-alias'
];
@@ -45,16 +46,18 @@ module.exports = new Package('typescript-parsing', [basePackage])
computeIdsProcessor.idTemplates.push({
docTypes: ['member'],
idTemplate: '${classDoc.id}.${name}',
- getAliases: function(doc) { return [doc.id]; }
+ getAliases: function(doc) {
+ return doc.classDoc.aliases.map(function(alias) { return alias + '.' + doc.name; });
+ }
});
computePathsProcessor.pathTemplates.push({
docTypes: ['member'],
- pathTemplate: '${classDoc.path}/${name}',
+ pathTemplate: '${classDoc.path}#${name}',
getOutputPath: function() {} // These docs are not written to their own file, instead they are part of their class doc
});
- var MODULES_DOCS_PATH = 'docs';
+ var MODULES_DOCS_PATH = 'partials/modules';
computePathsProcessor.pathTemplates.push({
docTypes: ['module'],
diff --git a/scripts/docs/typescript-package/mocks/mockPackage.js b/scripts/docs/typescript-package/mocks/mockPackage.js
old mode 100644
new mode 100755
diff --git a/scripts/docs/typescript-package/mocks/readTypeScriptModules/ignoreExportsMatching.ts b/scripts/docs/typescript-package/mocks/readTypeScriptModules/ignoreExportsMatching.ts
old mode 100644
new mode 100755
diff --git a/scripts/docs/typescript-package/mocks/readTypeScriptModules/interfaces.ts b/scripts/docs/typescript-package/mocks/readTypeScriptModules/interfaces.ts
old mode 100644
new mode 100755
diff --git a/scripts/docs/typescript-package/mocks/readTypeScriptModules/orderingOfMembers.ts b/scripts/docs/typescript-package/mocks/readTypeScriptModules/orderingOfMembers.ts
old mode 100644
new mode 100755
diff --git a/scripts/docs/typescript-package/mocks/readTypeScriptModules/privateModule.ts b/scripts/docs/typescript-package/mocks/readTypeScriptModules/privateModule.ts
new file mode 100755
index 0000000000..d4c6ef610a
--- /dev/null
+++ b/scripts/docs/typescript-package/mocks/readTypeScriptModules/privateModule.ts
@@ -0,0 +1 @@
+export var x = 10;
\ No newline at end of file
diff --git a/scripts/docs/typescript-package/mocks/readTypeScriptModules/publicModule.ts b/scripts/docs/typescript-package/mocks/readTypeScriptModules/publicModule.ts
new file mode 100755
index 0000000000..0fbae4ad2f
--- /dev/null
+++ b/scripts/docs/typescript-package/mocks/readTypeScriptModules/publicModule.ts
@@ -0,0 +1,3 @@
+export { x as y} from './privateModule';
+
+export abstract class AbstractClass {}
\ No newline at end of file
diff --git a/scripts/docs/typescript-package/mocks/tsParser/importedSrc.ts b/scripts/docs/typescript-package/mocks/tsParser/importedSrc.ts
old mode 100644
new mode 100755
diff --git a/scripts/docs/typescript-package/mocks/tsParser/testSrc.ts b/scripts/docs/typescript-package/mocks/tsParser/testSrc.ts
old mode 100644
new mode 100755
diff --git a/scripts/docs/typescript-package/processors/readTypeScriptModules.js b/scripts/docs/typescript-package/processors/readTypeScriptModules.js
old mode 100644
new mode 100755
index b8243b9825..aa80c7a15c
--- a/scripts/docs/typescript-package/processors/readTypeScriptModules.js
+++ b/scripts/docs/typescript-package/processors/readTypeScriptModules.js
@@ -4,8 +4,9 @@ var _ = require('lodash');
var ts = require('typescript');
module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
+ getDirectiveInfo,
getExportDocType, getContent,
- getDirectiveInfo, log) {
+ createDocMessage, log) {
return {
$runAfter: ['files-read'],
@@ -61,13 +62,26 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
// If the symbol is an Alias then for most things we want the original resolved symbol
var resolvedExport = exportSymbol.resolvedSymbol || exportSymbol;
+
+ // If the resolved symbol contains no declarations then it is invalid
+ // (probably an abstract class)
+ // For the moment we are just going to ignore such exports
+ // TODO: find a way of generating docs for them
+ if (!resolvedExport.declarations) return;
+
var exportDoc = createExportDoc(exportSymbol.name, resolvedExport, moduleDoc, basePath, parseInfo.typeChecker);
log.debug('>>>> EXPORT: ' + exportDoc.name + ' (' + exportDoc.docType + ') from ' + moduleDoc.id);
+ // Add this export doc to its module doc
+ moduleDoc.exports.push(exportDoc);
+ docs.push(exportDoc);
+
+ exportDoc.members = [];
+ exportDoc.statics = [];
+
// Generate docs for each of the export's members
if (resolvedExport.flags & ts.SymbolFlags.HasMembers) {
- exportDoc.members = [];
for(var memberName in resolvedExport.members) {
// FIXME(alexeagle): why do generic type params appear in members?
if (memberName === 'T') {
@@ -92,26 +106,40 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
exportDoc.newMember = memberDoc;
}
}
+ }
- if (sortClassMembers) {
- exportDoc.members.sort(function(a, b) {
- if (a.name > b.name) return 1;
- if (a.name < b.name) return -1;
- return 0;
- });
+ if (exportDoc.docType === 'enum') {
+ for(var memberName in resolvedExport.exports) {
+ log.silly('>>>>>> member: ' + memberName + ' from ' + exportDoc.id + ' in ' + moduleDoc.id);
+ var memberSymbol = resolvedExport.exports[memberName];
+ var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker);
+ docs.push(memberDoc);
+ exportDoc.members.push(memberDoc);
+ }
+ } else if (resolvedExport.flags & ts.SymbolFlags.HasExports) {
+ for (var exported in resolvedExport.exports) {
+ if (exported === 'prototype') continue;
+ if (hidePrivateMembers && exported.charAt(0) === '_') continue;
+ var memberSymbol = resolvedExport.exports[exported];
+ var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker);
+ memberDoc.isStatic = true;
+ docs.push(memberDoc);
+ exportDoc.statics.push(memberDoc);
}
}
- if (exportDoc.docType == 'enum') {
- exportDoc.members = [];
- for (var etype in resolvedExport.exports) {
- exportDoc.members.push(etype);
- }
+ if (sortClassMembers) {
+ exportDoc.members.sort(function(a, b) {
+ if (a.name > b.name) return 1;
+ if (a.name < b.name) return -1;
+ return 0;
+ });
+ exportDoc.statics.sort(function(a, b) {
+ if (a.name > b.name) return 1;
+ if (a.name < b.name) return -1;
+ return 0;
+ });
}
-
- // Add this export doc to its module doc
- moduleDoc.exports.push(exportDoc);
- docs.push(exportDoc);
});
});
}
@@ -120,10 +148,12 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
function createModuleDoc(moduleSymbol, basePath) {
var id = moduleSymbol.name.replace(/^"|"$/g, '');
+ var name = id.split('/').pop();
var moduleDoc = {
docType: 'module',
+ name: name,
id: id,
- aliases: [id],
+ aliases: [id, name],
moduleTree: moduleSymbol,
content: getContent(moduleSymbol),
exports: [],
@@ -136,6 +166,7 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
function createExportDoc(name, exportSymbol, moduleDoc, basePath, typeChecker) {
var typeParamString = '';
var heritageString = '';
+ var typeDefinition = '';
exportSymbol.declarations.forEach(function(decl) {
var sourceFile = ts.getSourceFileOfNode(decl);
@@ -144,6 +175,10 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
typeParamString = '<' + getText(sourceFile, decl.typeParameters) + '>';
}
+ if (decl.symbol.flags & ts.SymbolFlags.TypeAlias) {
+ typeDefinition = getText(sourceFile, decl.type);
+ }
+
if (decl.heritageClauses) {
decl.heritageClauses.forEach(function(heritage) {
@@ -173,10 +208,12 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
var exportDoc = {
docType: getExportDocType(exportSymbol),
+ exportSymbol: exportSymbol,
name: name,
id: moduleDoc.id + '/' + name,
typeParams: typeParamString,
heritage: heritageString,
+ decorators: getDecorators(exportSymbol),
aliases: aliasNames,
moduleDoc: moduleDoc,
content: getContent(exportSymbol),
@@ -185,12 +222,30 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
directiveInfo: getDirectiveInfo(exportSymbol)
};
+ if (exportDoc.docType === 'var' || exportDoc.docType === 'const' || exportDoc.docType === 'let') {
+ exportDoc.symbolTypeName = exportSymbol.valueDeclaration.type &&
+ exportSymbol.valueDeclaration.type.typeName &&
+ exportSymbol.valueDeclaration.type.typeName.text;
+ }
+
+ if (exportDoc.docType === 'type-alias') {
+ exportDoc.returnType = getReturnType(typeChecker, exportSymbol);
+ }
+
if(exportSymbol.flags & ts.SymbolFlags.Function) {
exportDoc.parameters = getParameters(typeChecker, exportSymbol);
}
if(exportSymbol.flags & ts.SymbolFlags.Value) {
exportDoc.returnType = getReturnType(typeChecker, exportSymbol);
}
+ if (exportSymbol.flags & ts.SymbolFlags.TypeAlias) {
+ exportDoc.typeDefinition = typeDefinition;
+ }
+
+ // Compute the original module name from the relative file path
+ exportDoc.originalModule = exportDoc.fileInfo.relativePath
+ .replace(new RegExp('\.' + exportDoc.fileInfo.extension + '$'), '');
+
return exportDoc;
}
@@ -199,6 +254,7 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
docType: 'member',
classDoc: classDoc,
name: memberSymbol.name,
+ decorators: getDecorators(memberSymbol),
content: getContent(memberSymbol),
fileInfo: getFileInfo(memberSymbol, basePath),
location: getLocation(memberSymbol)
@@ -242,6 +298,45 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
return memberDoc;
}
+
+ function getDecorators(symbol) {
+
+ var declaration = symbol.valueDeclaration || symbol.declarations[0];
+ var sourceFile = ts.getSourceFileOfNode(declaration);
+
+ var decorators = declaration.decorators && declaration.decorators.map(function(decorator) {
+ decorator = decorator.expression;
+ return {
+ name: decorator.expression ? decorator.expression.text : decorator.text,
+ arguments: decorator.arguments && decorator.arguments.map(function(argument) {
+ return getText(sourceFile, argument).trim();
+ }),
+ argumentInfo: decorator.arguments && decorator.arguments.map(function(argument) {
+ return parseArgument(argument);
+ }),
+ expression: decorator
+ };
+ });
+ return decorators;
+ }
+
+ function parseProperties(properties) {
+ var result = {};
+ _.forEach(properties, function(property) {
+ result[property.name.text] = parseArgument(property.initializer);
+ });
+ return result;
+ }
+
+ function parseArgument(argument) {
+ if (argument.text) return argument.text;
+ if (argument.properties) return parseProperties(argument.properties);
+ if (argument.elements) return argument.elements.map(function(element) { return element.text; });
+ var sourceFile = ts.getSourceFileOfNode(argument);
+ var text = getText(sourceFile, argument).trim();
+ return text;
+ }
+
function getParameters(typeChecker, symbol) {
var declaration = symbol.valueDeclaration || symbol.declarations[0];
var sourceFile = ts.getSourceFileOfNode(declaration);
@@ -252,7 +347,11 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
' at line ' + location.start.line);
}
return declaration.parameters.map(function(parameter) {
- var paramText = getText(sourceFile, parameter.name);
+ var paramText = '';
+ if (parameter.dotDotDotToken) {
+ paramText += '...';
+ }
+ paramText += getText(sourceFile, parameter.name);
if (parameter.questionToken || parameter.initializer) {
paramText += '?';
}
@@ -260,6 +359,9 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
paramText += ':' + getType(sourceFile, parameter.type);
} else {
paramText += ': any';
+ if (parameter.dotDotDotToken) {
+ paramText += '[]';
+ }
}
return paramText.trim();
});
@@ -280,6 +382,14 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
var sourceFile = ts.getSourceFileOfNode(declaration);
if (declaration.type) {
return getType(sourceFile, declaration.type).trim();
+ } else if (declaration.initializer) {
+ // The symbol does not have a "type" but it is being initialized
+ // so we can deduce the type of from the initializer (mostly).
+ if (declaration.initializer.expression) {
+ return declaration.initializer.expression.text.trim();
+ } else {
+ return getType(sourceFile, declaration.initializer).trim();
+ }
}
}
@@ -302,8 +412,8 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
function getType(sourceFile, type) {
var text = getText(sourceFile, type);
while (text.indexOf(".") >= 0) {
- // Keep namespaced symbols in Rx
- if (text.match(/^\s*Rx\./)) break;
+ // Keep namespaced symbols in RxNext
+ if (text.match(/^\s*RxNext\./)) break;
// handle the case List -> List
text = text.replace(/([^.<]*)\.([^>]*)/, "$2");
}
diff --git a/scripts/docs/typescript-package/processors/readTypeScriptModules.spec.js b/scripts/docs/typescript-package/processors/readTypeScriptModules.spec.js
old mode 100644
new mode 100755
index 53d959c70c..1224acc58d
--- a/scripts/docs/typescript-package/processors/readTypeScriptModules.spec.js
+++ b/scripts/docs/typescript-package/processors/readTypeScriptModules.spec.js
@@ -13,6 +13,27 @@ describe('readTypeScriptModules', function() {
processor.basePath = path.resolve(__dirname, '../mocks/readTypeScriptModules');
});
+ describe('exportDocs', function() {
+ it('should provide the original module if the export is re-exported', function() {
+ processor.sourceFiles = [ 'publicModule.ts' ];
+ var docs = [];
+ processor.$process(docs);
+
+ var exportedDoc = docs[1];
+ expect(exportedDoc.originalModule).toEqual('privateModule');
+ });
+
+ it('should include exported abstract classes', function() {
+ processor.sourceFiles = [ 'publicModule.ts' ];
+ var docs = [];
+ processor.$process(docs);
+
+ var exportedDoc = docs[2];
+ expect(exportedDoc.name).toEqual('AbstractClass');
+ });
+
+ });
+
describe('ignoreExportsMatching', function() {
it('should ignore exports that match items in the `ignoreExportsMatching` property', function() {
diff --git a/scripts/docs/typescript-package/services/convertPrivateClassesToInterfaces.js b/scripts/docs/typescript-package/services/convertPrivateClassesToInterfaces.js
new file mode 100755
index 0000000000..69afdea89e
--- /dev/null
+++ b/scripts/docs/typescript-package/services/convertPrivateClassesToInterfaces.js
@@ -0,0 +1,31 @@
+var _ = require('lodash');
+
+module.exports = function convertPrivateClassesToInterfaces() {
+ return function(exportDocs, addInjectableReference) {
+ _.forEach(exportDocs, function(exportDoc) {
+
+ // Search for classes with a constructor marked as `@internal`
+ if (exportDoc.docType === 'class' && exportDoc.constructorDoc && exportDoc.constructorDoc.internal) {
+
+ // Convert this class to an interface with no constructor
+ exportDoc.docType = 'interface';
+ exportDoc.constructorDoc = null;
+
+ if (exportDoc.heritage) {
+ // convert the heritage since interfaces use `extends` not `implements`
+ exportDoc.heritage = exportDoc.heritage.replace('implements', 'extends');
+ }
+
+ if (addInjectableReference) {
+ // Add the `declare var SomeClass extends InjectableReference` construct
+ exportDocs.push({
+ docType: 'var',
+ name: exportDoc.name,
+ id: exportDoc.id,
+ returnType: 'InjectableReference'
+ });
+ }
+ }
+ });
+ };
+};
diff --git a/scripts/docs/typescript-package/services/convertPrivateClassesToInterfaces.spec.js b/scripts/docs/typescript-package/services/convertPrivateClassesToInterfaces.spec.js
new file mode 100755
index 0000000000..bc4ed7411c
--- /dev/null
+++ b/scripts/docs/typescript-package/services/convertPrivateClassesToInterfaces.spec.js
@@ -0,0 +1,76 @@
+var mockPackage = require('../mocks/mockPackage');
+var Dgeni = require('dgeni');
+var _ = require('lodash');
+
+describe('readTypeScriptModules', function() {
+ var dgeni, injector, convertPrivateClassesToInterfaces;
+
+ beforeEach(function() {
+ dgeni = new Dgeni([mockPackage()]);
+ injector = dgeni.configureInjector();
+ convertPrivateClassesToInterfaces = injector.get('convertPrivateClassesToInterfaces');
+ });
+
+ it('should convert private class docs to interface docs', function() {
+ var docs = [
+ {
+ docType: 'class',
+ name: 'privateClass',
+ id: 'privateClass',
+ constructorDoc: { internal: true }
+ }
+ ];
+ convertPrivateClassesToInterfaces(docs, false);
+ expect(docs[0].docType).toEqual('interface');
+ });
+
+
+ it('should not touch non-private class docs', function() {
+ var docs = [
+ {
+ docType: 'class',
+ name: 'privateClass',
+ id: 'privateClass',
+ constructorDoc: { }
+ }
+ ];
+ convertPrivateClassesToInterfaces(docs, false);
+ expect(docs[0].docType).toEqual('class');
+ });
+
+
+ it('should convert the heritage since interfaces use `extends` not `implements`', function() {
+ var docs = [
+ {
+ docType: 'class',
+ name: 'privateClass',
+ id: 'privateClass',
+ constructorDoc: { internal: true },
+ heritage: 'implements parentInterface'
+ }
+ ];
+ convertPrivateClassesToInterfaces(docs, false);
+ expect(docs[0].heritage).toEqual('extends parentInterface');
+ });
+
+
+ it('should add new injectable reference types, if specified, to the passed in collection', function() {
+ var docs = [
+ {
+ docType: 'class',
+ name: 'privateClass',
+ id: 'privateClass',
+ constructorDoc: { internal: true },
+ heritage: 'implements parentInterface'
+ }
+ ];
+ convertPrivateClassesToInterfaces(docs, true);
+ expect(docs[1]).toEqual({
+ docType : 'var',
+ name : 'privateClass',
+ id : 'privateClass',
+ returnType : 'InjectableReference'
+ });
+ });
+
+});
diff --git a/scripts/docs/typescript-package/services/modules.js b/scripts/docs/typescript-package/services/modules.js
old mode 100644
new mode 100755
diff --git a/scripts/docs/typescript-package/services/tsParser/createCompilerHost.js b/scripts/docs/typescript-package/services/tsParser/createCompilerHost.js
old mode 100644
new mode 100755
index 0dc488b0b8..c9d3d368b4
--- a/scripts/docs/typescript-package/services/tsParser/createCompilerHost.js
+++ b/scripts/docs/typescript-package/services/tsParser/createCompilerHost.js
@@ -58,17 +58,22 @@ module.exports = function createCompilerHost(log) {
return ts.sys.newLine;
},
fileExists: function(fileName) {
- var resolvedPath = path.resolve(baseDir, fileName);
- try {
- fs.statSync(resolvedPath);
- return true;
- } catch (e) {
- return false;
+ var text, resolvedPath, resolvedPathWithExt;
+
+ // Strip off the extension and resolve relative to the baseDir
+ baseFilePath = fileName.replace(/\.[^.]+$/, '');
+ resolvedPath = path.resolve(baseDir, baseFilePath);
+
+ // Iterate through each possible extension and return the first source file that is actually found
+ for(var i=0; i