diff --git a/docs/angular.io-package/templates/class.template.html b/docs/angular.io-package/templates/class.template.html
index d8faf84985..d266afeaf2 100644
--- a/docs/angular.io-package/templates/class.template.html
+++ b/docs/angular.io-package/templates/class.template.html
@@ -3,8 +3,8 @@
{% block body %}
p.location-badge.
- exported from {$ doc.moduleDoc.id $}
- defined in {$ doc.location.start.source.name $}.js (line {$ doc.location.start.line $})
+ exported from {@link {$ doc.moduleDoc.id $} {$doc.moduleDoc.id $} }
+ defined in {$ doc.fileInfo.relativePath $} (line {$ doc.location.start.line+1 $})
:markdown
{$ doc.description | indent(2, true) $}
@@ -17,10 +17,10 @@ p.location-badge.
.l-sub-section
h3 {$ doc.constructorDoc.name $}
- {% if doc.constructorDoc.params %}
+ {% if doc.constructorDoc.parameters %}
pre.prettyprint
code.
- {$ doc.constructorDoc.name $}{$ paramList(doc.constructorDoc.params) | indent(4, true) | trim $}
+ {$ doc.constructorDoc.name $}{$ paramList(doc.constructorDoc.parameters) | indent(4, true) | trim $}
{% endif %}
:markdown
{$ doc.constructorDoc.description | indent(6, true) | replace('## Example', '') | replace('# Example', '') $}
@@ -32,10 +32,10 @@ p.location-badge.
.l-sub-section
h3 {$ member.name $}
- {% if member.params %}
+ {% if member.parameters %}
pre.prettyprint
code.
- {$ member.name $}{$ paramList(member.params) | indent(4, true) | trim $}
+ {$ member.name $}{$ paramList(member.parameters) | indent(4, true) | trim $}{$ returnType(doc.returnType) $}
{% endif %}
:markdown
diff --git a/docs/angular.io-package/templates/function.template.html b/docs/angular.io-package/templates/function.template.html
index bd198ba0fe..8a23300ea1 100644
--- a/docs/angular.io-package/templates/function.template.html
+++ b/docs/angular.io-package/templates/function.template.html
@@ -5,10 +5,15 @@
.l-main-section
h2(class="function export") {$ doc.name $}
- p exported from {$ doc.moduleDoc.id $}{$ paramList(doc.parameters) $}
+ {% if doc.parameters %}
+ pre.prettyprint
+ code.
+ {$ doc.name $}{$ paramList(doc.parameters) | indent(4, true) | trim $}{$ returnType(doc.returnType) $}
+ {% endif %}
p.location-badge.
- exported from {$ doc.moduleDoc.id $}
+ exported from {@link {$ doc.moduleDoc.id $} {$doc.moduleDoc.id $} }
+ defined in {$ doc.fileInfo.relativePath $} (line {$ doc.location.start.line+1 $})
:markdown
{$ doc.description | indent(4, true) $}
diff --git a/docs/angular.io-package/templates/lib/paramList.html b/docs/angular.io-package/templates/lib/paramList.html
index b84a8d7162..24ba12c080 100644
--- a/docs/angular.io-package/templates/lib/paramList.html
+++ b/docs/angular.io-package/templates/lib/paramList.html
@@ -4,4 +4,9 @@
{$ param | escape $}{% if not loop.last %}, {% endif %}
{%- endfor %})
{%- endif %}
-{%- endmacro -%}
\ No newline at end of file
+{%- endmacro -%}
+
+
+{% macro returnType(returnType) -%}
+ {%- if returnType %} : {$ returnType | escape $}{% endif -%}
+{%- endmacro -%}
diff --git a/docs/angular.io-package/templates/module.template.html b/docs/angular.io-package/templates/module.template.html
index 497b3ca956..96ba98ccd6 100644
--- a/docs/angular.io-package/templates/module.template.html
+++ b/docs/angular.io-package/templates/module.template.html
@@ -1,5 +1,8 @@
{% extends 'layout/base.template.html' -%}
{% block body -%}
+p.location-badge.
+ defined in {$ doc.fileInfo.relativePath $} (line {$ doc.location.start.line+1 $})
+
ul
for page, slug in public.docs[current.path[1]][current.path[2]][current.path[3]][current.path[4]]._data
if slug != 'index'
diff --git a/docs/dgeni-package/index.js b/docs/dgeni-package/index.js
index de52461a2f..e858075946 100644
--- a/docs/dgeni-package/index.js
+++ b/docs/dgeni-package/index.js
@@ -15,6 +15,11 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage, linksPac
// Register the services and file readers
.factory(require('./services/modules'))
+.factory(require('./services/tsParser'))
+.factory(require('./services/tsParser/createCompilerHost'))
+.factory(require('./services/tsParser/getFileInfo'))
+.factory(require('./services/tsParser/getExportDocType'))
+.factory(require('./services/tsParser/getContent'))
.factory(require('./readers/ngdoc'))
.factory('EXPORT_DOC_TYPES', function() {
@@ -28,6 +33,7 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage, linksPac
// Register the processors
+.processor(require('./processors/readTypeScriptModules'))
.processor(require('./processors/generateNavigationDoc'))
.processor(require('./processors/extractTitleFromGuides'))
.processor(require('./processors/createOverviewDump'))
@@ -40,13 +46,23 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage, linksPac
// Configure file reading
-.config(function(readFilesProcessor, ngdocFileReader) {
+.config(function(readFilesProcessor, ngdocFileReader, readTypeScriptModules) {
readFilesProcessor.fileReaders = [ngdocFileReader];
readFilesProcessor.basePath = path.resolve(__dirname, '../..');
readFilesProcessor.sourceFiles = [
{ include: 'modules/*/docs/**/*.md', basePath: 'modules' },
{ include: 'docs/content/**/*.md', basePath: 'docs/content' }
];
+
+ readTypeScriptModules.sourceFiles = [
+ '*/*.js',
+ '*/src/**/*.js',
+ '*/*.es6',
+ '*/src/**/*.es6',
+ '*/*.ts',
+ '*/src/**/*.ts'
+ ];
+ readTypeScriptModules.basePath = 'modules';
})
@@ -54,6 +70,14 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage, linksPac
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/public'));
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/private'));
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/exportedAs'));
+
+ // We actually don't want to parse param docs in this package as we are getting the data out using TS
+ parseTagsProcessor.tagDefinitions.forEach(function(tagDef) {
+ if (tagDef.name === 'param') {
+ tagDef.ignore = true;
+ }
+ });
+
})
@@ -88,12 +112,6 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage, linksPac
// Configure ids and paths
.config(function(computeIdsProcessor, computePathsProcessor, EXPORT_DOC_TYPES) {
- computeIdsProcessor.idTemplates.push({
- docTypes: EXPORT_DOC_TYPES,
- idTemplate: '${moduleDoc.id}.${name}',
- getAliases: function(doc) { return [doc.id, doc.name]; }
- });
-
computeIdsProcessor.idTemplates.push({
docTypes: ['member'],
idTemplate: '${classDoc.id}.${name}',
@@ -117,7 +135,7 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage, linksPac
computePathsProcessor.pathTemplates.push({
docTypes: ['module'],
- pathTemplate: '${id}',
+ pathTemplate: '/${id}',
outputPathTemplate: MODULES_DOCS_PATH + '/${id}/index.html'
});
@@ -136,7 +154,7 @@ module.exports = new Package('angular', [jsdocPackage, nunjucksPackage, linksPac
computePathsProcessor.pathTemplates.push({
docTypes: ['guide'],
- pathTemplate: '${id}',
+ pathTemplate: '/${id}',
outputPathTemplate: GUIDES_PATH + '/${id}.html'
});
});
diff --git a/docs/dgeni-package/mocks/importedSrc.ts b/docs/dgeni-package/mocks/importedSrc.ts
new file mode 100644
index 0000000000..74a115e5fe
--- /dev/null
+++ b/docs/dgeni-package/mocks/importedSrc.ts
@@ -0,0 +1 @@
+export var x = 100;
\ No newline at end of file
diff --git a/docs/dgeni-package/mocks/testSrc.ts b/docs/dgeni-package/mocks/testSrc.ts
new file mode 100644
index 0000000000..159985a9e6
--- /dev/null
+++ b/docs/dgeni-package/mocks/testSrc.ts
@@ -0,0 +1,38 @@
+/**
+ * @module
+ * @description
+ * This is the module description
+ */
+
+export * from 'importedSrc';
+
+/**
+ * This is some random other comment
+ */
+
+/**
+ * This is MyClass
+ */
+export class MyClass {
+ message: String;
+
+ /**
+ * Create a new MyClass
+ * @param {String} name The name to say hello to
+ */
+ constructor(name) {
+ this.message = 'hello ' + name;
+ }
+
+ /**
+ * Return a greeting message
+ */
+ greet() {
+ return this.message;
+ }
+}
+
+/**
+ * An exported function
+ */
+export var myFn = (val:number) => { return val*2; }
\ No newline at end of file
diff --git a/docs/dgeni-package/processors/readTypeScriptModules.js b/docs/dgeni-package/processors/readTypeScriptModules.js
new file mode 100644
index 0000000000..700252fcf0
--- /dev/null
+++ b/docs/dgeni-package/processors/readTypeScriptModules.js
@@ -0,0 +1,201 @@
+var glob = require('glob');
+var path = require('canonical-path');
+var _ = require('lodash');
+var ts = require('typescript');
+
+module.exports = function readTypeScriptModules(tsParser, readFilesProcessor, modules, getFileInfo, getExportDocType, getContent, log) {
+
+ return {
+ $runAfter: ['files-read'],
+ $runBefore: ['parsing-tags'],
+
+ $validate: {
+ sourceFiles: {presence: true},
+ basePath: {presence: true},
+ hidePrivateMembers: { inclusion: [true, false] },
+ hideSpecialExports: { inclusion: [true, false] }
+ },
+
+ sourceFiles: [],
+ basePath: '.',
+ hidePrivateMembers: false,
+ hideSpecialExports: true,
+
+ $process: function(docs) {
+
+ var hideSpecialExports = this.hideSpecialExports;
+ var hidePrivateMembers = this.hidePrivateMembers;
+
+ var basePath = path.resolve(readFilesProcessor.basePath, this.basePath);
+ var filesPaths = expandSourceFiles(this.sourceFiles, basePath);
+ var parseInfo = tsParser.parse(filesPaths, this.basePath);
+ var moduleSymbols = parseInfo.moduleSymbols;
+
+ // Iterate through each of the modules that were parsed and generate a module doc
+ // as well as docs for each module's exports.
+ moduleSymbols.forEach(function(moduleSymbol) {
+
+ var moduleDoc = createModuleDoc(moduleSymbol, basePath);
+
+ // Add this module doc to the module lookup collection and the docs collection
+ modules[moduleDoc.id] = moduleDoc;
+ docs.push(moduleDoc);
+
+ // Iterate through this module's exports and generate a doc for each
+ moduleSymbol.exportArray.forEach(function(exportSymbol) {
+
+ // Ignore exports starting with an underscore
+ if (hideSpecialExports && exportSymbol.name.charAt(0) === '_') return;
+
+ // If the symbol is an Alias then for most things we want the original resolved symbol
+ var resolvedExport = exportSymbol.resolvedSymbol || exportSymbol;
+ var exportDoc = createExportDoc(exportSymbol.name, resolvedExport, moduleDoc, basePath, parseInfo.typeChecker);
+ log.debug('>>>> EXPORT: ' + exportDoc.name + ' (' + exportDoc.docType + ') from ' + moduleDoc.id);
+
+ // Generate docs for each of the export's members
+ if (resolvedExport.flags & ts.SymbolFlags.HasMembers) {
+
+ exportDoc.members = [];
+ for(var memberName in resolvedExport.members) {
+ log.silly('>>>>>> member: ' + memberName + ' from ' + exportDoc.id + ' in ' + moduleDoc.id);
+ var memberSymbol = resolvedExport.members[memberName];
+ var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker);
+
+ // We special case the constructor and sort the other members alphabetically
+ if (memberSymbol.flags & ts.SymbolFlags.Constructor) {
+ exportDoc.constructorDoc = memberDoc;
+ docs.push(memberDoc);
+ } else if (!hidePrivateMembers || memberSymbol.name.charAt(0) !== '_') {
+ docs.push(memberDoc);
+ insertSorted(exportDoc.members, memberDoc, 'name');
+ }
+ }
+ }
+
+ // Add this export doc to its module doc
+ moduleDoc.exports.push(exportDoc);
+ docs.push(exportDoc);
+ });
+ });
+ }
+ };
+
+
+ function createModuleDoc(moduleSymbol, basePath) {
+ var id = moduleSymbol.name.replace(/^"|"$/g, '');
+ var moduleDoc = {
+ docType: 'module',
+ id: id,
+ aliases: [id],
+ moduleTree: moduleSymbol,
+ content: getContent(moduleSymbol),
+ exports: [],
+ fileInfo: getFileInfo(moduleSymbol, basePath),
+ location: getLocation(moduleSymbol)
+ };
+ return moduleDoc;
+ }
+
+ function createExportDoc(name, exportSymbol, moduleDoc, basePath, typeChecker) {
+ var exportDoc = {
+ docType: getExportDocType(exportSymbol),
+ name: name,
+ id: name,
+ aliases: [name],
+ moduleDoc: moduleDoc,
+ content: getContent(exportSymbol),
+ fileInfo: getFileInfo(exportSymbol, basePath),
+ location: getLocation(exportSymbol)
+ };
+ if(exportSymbol.flags & ts.SymbolFlags.Function) {
+ exportDoc.parameters = getParameters(typeChecker, exportSymbol);
+ exportDoc.returnType = getReturnType(typeChecker, exportSymbol);
+ }
+ return exportDoc;
+ }
+
+ function createMemberDoc(memberSymbol, classDoc, basePath, typeChecker) {
+ var memberDoc = {
+ docType: 'member',
+ classDoc: classDoc,
+ name: memberSymbol.name,
+ id: memberSymbol.name,
+ content: getContent(memberSymbol),
+ fileInfo: getFileInfo(memberSymbol, basePath),
+ location: getLocation(memberSymbol)
+ };
+
+ if (memberSymbol.flags & ts.SymbolFlags.Method) {
+ // NOTE: we use the property name `parameters` here so we don't conflict
+ // with the `params` property that will be updated by dgeni reading the
+ // `@param` tags from the docs
+ memberDoc.parameters = getParameters(typeChecker, memberSymbol);
+ memberDoc.returnType = getReturnType(typeChecker, memberSymbol);
+ }
+
+ if (memberSymbol.flags & ts.SymbolFlags.Constructor) {
+ memberDoc.parameters = getParameters(typeChecker, memberSymbol);
+ memberDoc.name = 'constructor';
+ }
+
+ return memberDoc;
+ }
+
+
+ function getParameters(typeChecker, symbol) {
+ var declaration = symbol.valueDeclaration || symbol.declarations[0];
+ var sourceFile = ts.getSourceFileOfNode(declaration);
+ if(!declaration.parameters) {
+ console.log(declaration);
+ throw 'missing declaration parameters';
+ }
+ var signature = typeChecker.getSignatureFromDeclaration(declaration);
+ return declaration.parameters.map(function(parameter) {
+ return getText(sourceFile, parameter).trim();
+ });
+ }
+
+ function getReturnType(typeChecker, symbol) {
+ var declaration = symbol.valueDeclaration || symbol.declarations[0];
+ var sourceFile = ts.getSourceFileOfNode(declaration);
+ if(declaration.type) {
+ var signature = typeChecker.getSignatureFromDeclaration(declaration);
+ return getText(sourceFile, declaration.type).trim();
+ }
+ }
+
+
+ function expandSourceFiles(sourceFiles, basePath) {
+ var filePaths = [];
+ sourceFiles.forEach(function(sourcePattern) {
+ filePaths = filePaths.concat(glob.sync(sourcePattern, { cwd: basePath }));
+ });
+ return filePaths;
+ }
+
+
+ function getText(sourceFile, node) {
+ return sourceFile.text.substring(node.pos, node.end);
+ }
+
+
+ function getLocation(symbol) {
+ var node = symbol.valueDeclaration || symbol.declarations[0];
+ var sourceFile = ts.getSourceFileOfNode(node);
+ var location = {
+ start: ts.getLineAndCharacterOfPosition(sourceFile, node.pos),
+ end: ts.getLineAndCharacterOfPosition(sourceFile, node.end)
+ };
+ return location;
+ }
+
+};
+
+function insertSorted(collection, item, property) {
+ var index = collection.length;
+ while(index>0) {
+ if(collection[index-1][property] < item[property]) break;
+ index -= 1;
+ }
+ collection.splice(index, 0, item);
+}
diff --git a/docs/dgeni-package/services/tsParser.js b/docs/dgeni-package/services/tsParser.js
new file mode 100644
index 0000000000..e21d2947f8
--- /dev/null
+++ b/docs/dgeni-package/services/tsParser.js
@@ -0,0 +1,68 @@
+var ts = require('typescript');
+
+module.exports = function tsParser(createCompilerHost, log) {
+
+ return {
+
+ options: {
+ allowNonTsExtensions: true
+ },
+
+ parse: function(fileNames, baseDir) {
+ // This is the easiest way I could find to ensure that we loaded
+ // modules with paths relative to the baseDir
+ process.chdir(baseDir);
+
+ // "Compile" a program from the given module filenames, to get hold of a
+ // typeChecker that can be used to interrogate the modules, exports and so on.
+ var host = createCompilerHost(this.options);
+ var program = ts.createProgram(fileNames, this.options, host);
+ var typeChecker = program.getTypeChecker();
+
+ // Create an array of module symbols for each file we were given
+ var moduleSymbols = [];
+ fileNames.forEach(function(fileName) {
+ var sourceFile = program.getSourceFile(fileName);
+
+ if (!sourceFile) {
+ throw new Error('Invalid source file: ' + fileName);
+ } else if (!sourceFile.symbol) {
+ // Some files contain only a comment and no actual module code
+ log.warn('No module code found in ' + fileName);
+ } else {
+ moduleSymbols.push(sourceFile.symbol);
+ }
+ });
+
+
+ moduleSymbols.forEach(function(tsModule) {
+
+ // The type checker has a nice helper function that returns an array of Symbols
+ // representing the exports for a given module
+ tsModule.exportArray = typeChecker.getExportsOfModule(tsModule);
+
+ // Although 'star' imports (e.g. `export * from 'some/module';) get resolved automatically
+ // by the compiler/binder, it seems that explicit imports (e.g. `export {SomeClass} from 'some/module'`)
+ // do not so we have to do a little work.
+ tsModule.exportArray.forEach(function(moduleExport) {
+ if (moduleExport.flags & 8388608 /* Alias */) {
+ // To maintain the alias information (particularly the alias name)
+ // we just attach the original "resolved" symbol to the alias symbol
+ moduleExport.resolvedSymbol = typeChecker.getAliasedSymbol(moduleExport);
+ }
+ });
+ });
+
+ moduleSymbols.typeChecker = typeChecker;
+
+ return {
+ moduleSymbols: moduleSymbols,
+ typeChecker: typeChecker,
+ program: program,
+ host: host
+ };
+ }
+ };
+
+
+};
diff --git a/docs/dgeni-package/services/tsParser.spec.js b/docs/dgeni-package/services/tsParser.spec.js
new file mode 100644
index 0000000000..a7819276ca
--- /dev/null
+++ b/docs/dgeni-package/services/tsParser.spec.js
@@ -0,0 +1,21 @@
+var mockPackage = require('../mocks/mockPackage');
+var Dgeni = require('dgeni');
+var path = require('canonical-path');
+
+describe('tsParser', function() {
+ var dgeni, injector, parser;
+
+ beforeEach(function() {
+ dgeni = new Dgeni([mockPackage()]);
+ injector = dgeni.configureInjector();
+ parser = injector.get('tsParser');
+ });
+
+ it("should parse a TS file", function() {
+ var parseInfo = parser.parse(['testSrc.ts'], path.resolve(__dirname, '../mocks/'));
+ var tsModules = parseInfo.moduleSymbols;
+ expect(tsModules.length).toEqual(1);
+ expect(tsModules[0].exportArray.length).toEqual(3);
+ expect(tsModules[0].exportArray.map(function(i) { return i.name; })).toEqual(['MyClass', 'myFn', 'x']);
+ });
+});
\ No newline at end of file
diff --git a/docs/dgeni-package/services/tsParser/createCompilerHost.js b/docs/dgeni-package/services/tsParser/createCompilerHost.js
new file mode 100644
index 0000000000..61a19d2804
--- /dev/null
+++ b/docs/dgeni-package/services/tsParser/createCompilerHost.js
@@ -0,0 +1,32 @@
+var ts = require('typescript');
+
+// These are the extension that we should consider when trying to load a module
+var extensions = ['.ts', '.js', '.es6']
+
+// We need to provide our own version of CompilerHost because, at the moment, there is
+// a mix of `.ts`, `.es6` and `.js` (atScript) files in the project and the TypeScript
+// compiler only looks for `.ts` files when trying to load imports.
+module.exports = function createCompilerHost(log) {
+ return function createCompilerHost(options) {
+
+ var host = ts.createCompilerHost(options);
+
+ // Override the `getSourceFile` implementation to also look for js and es6 files
+ var getSourceFile = host.getSourceFile;
+ host.getSourceFile = function(filename, languageVersion, onError) {
+ // Iterate through each possible extension and return the first source file that is actually found
+ for(var i=0; i
-defined in {$ doc.location.start.source.name $}.js (line {$ doc.location.start.line $})
{$ doc.description | marked $}
{%- if doc.constructorDoc or doc.members.length -%} diff --git a/docs/public-docs-package/index.js b/docs/public-docs-package/index.js index 311335df50..484022d33f 100644 --- a/docs/public-docs-package/index.js +++ b/docs/public-docs-package/index.js @@ -3,6 +3,25 @@ var basePackage = require('../dgeni-package'); module.exports = new Package('angular-public', [basePackage]) +.config(function(readTypeScriptModules) { + readTypeScriptModules.sourceFiles = [ + 'angular2/annotations.js', + 'angular2/change_detection.ts', + 'angular2/core.js', + 'angular2/di.ts', + 'angular2/directives.js', + 'angular2/forms.js', + 'angular2/router.js', + 'angular2/test.js', + 'angular2/pipes.js' + ]; + readTypeScriptModules.hidePrivateMembers = true; +}) + +.config(function(getLinkInfo) { + getLinkInfo.useFirstAmbiguousLink = false; +}) + // Configure file writing .config(function(writeFilesProcessor) { writeFilesProcessor.outputFolder = 'dist/public_docs';