diff --git a/.gitignore b/.gitignore
index 42e9772f40..54f9e418ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,6 @@ packages
pubspec.lock
.c9
.idea/
+
+
+docs/bower_components/
\ No newline at end of file
diff --git a/docs/app/css/app.css b/docs/app/css/app.css
new file mode 100644
index 0000000000..3f34fde18d
--- /dev/null
+++ b/docs/app/css/app.css
@@ -0,0 +1,405 @@
+
+.hide { display: none !important; }
+
+body {
+ overflow: hidden;
+ max-width: 100%;
+ max-height: 100%;
+ font-size: 14px;
+}
+
+a {
+ color: #3f51b5;
+}
+
+table {
+ margin-bottom: 20px;
+ max-width: 100%;
+ width: 100%;
+ border-spacing: 0;
+ border-collapse: collapse;
+ background-color: transparent;
+}
+
+td,
+th {
+ padding: $baseline-grid ($baseline-grid * 2);
+ border-top: 1px solid #ddd;
+ vertical-align: top;
+}
+
+th {
+ border-bottom: 2px solid #ddd;
+ vertical-align: bottom;
+}
+
+pre {
+ white-space: pre;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+pre > code.highlight {
+ padding: 10px;
+ font-weight: 400;
+ -webkit-user-select: initial;
+ -moz-user-select: initial;
+ -ms-user-select: initial;
+ user-select: initial;
+}
+
+pre, code {
+
+ margin: 0;
+ padding: 0;
+ overflow-wrap: break-word;
+ font-family: monospace, serif;
+}
+
+
+pre > code.highlight {
+ padding: 10px;
+ font-weight: 400;
+}
+
+code {
+ font-size: 14px;
+ background: #f6f6f6;
+}
+
+code.highlight {
+ display: block;
+ overflow-wrap: break-word;
+}
+
+code:not(.highlight) {
+ color: #4285f4;
+ margin-left: 1px;
+ margin-right: 1px;
+}
+
+.md-sidenav-inner {
+ background: #fff;
+}
+
+.layout-content,
+.doc-content {
+ max-width: 864px;
+ margin: auto;
+}
+.layout-label {
+ width: 120px;
+}
+.layout-content code.highlight {
+ margin-bottom: 15px;
+}
+
+.menu-item {
+ background: none;
+ border-width: 0;
+ cursor: pointer;
+ display: block;
+ color: #333;
+ font-size: inherit;
+ line-height: 40px;
+ max-height: 40px;
+ opacity: 1;
+ margin: 0;
+ outline: none;
+ padding: 0px 28px;
+ position: relative;
+ text-align: left;
+ text-decoration: none;
+ width: 100%;
+ z-index: 1;
+ -webkit-transition: 0.45s cubic-bezier(0.35, 0, 0.25, 1);
+ -webkit-transition-property: max-height, background-color, opacity;
+ -moz-transition: 0.45s cubic-bezier(0.35, 0, 0.25, 1);
+ -moz-transition-property: max-height, background-color, opacity;
+ transition: 0.45s cubic-bezier(0.35, 0, 0.25, 1);
+ transition-property: max-height, background-color, opacity;
+}
+.menu-item.ng-hide {
+ max-height: 0;
+ opacity: 0;
+}
+.menu-item:hover {
+ color: #999;
+}
+.menu-item:focus {
+ font-weight: bold;
+}
+.menu-item.menu-title {
+ color: #888;
+ font-size: 14px;
+ padding-left: 16px;
+ text-align: left;
+ text-transform: uppercase;
+ transition: color 0.35s cubic-bezier(0.35, 0, 0.25, 1);
+}
+.menu-item.menu-title:hover,
+.menu-item.menu-title.active {
+ color: #1976d2;
+}
+
+.menu-icon {
+ background: none;
+ border: none;
+}
+.app-toolbar .md-toolbar-tools h3 {
+ -webkit-margin-before: 0;
+ -webkit-margin-after: 0;
+}
+
+.demo-container {
+ border-radius: 4px;
+ margin-top: 16px;
+ -webkit-transition: 0.02s padding cubic-bezier(0.35, 0, 0.25, 1);
+ transition: 0.02s padding cubic-bezier(0.35, 0, 0.25, 1);
+ position: relative;
+ padding-bottom: 0;
+}
+.demo-source-tabs {
+ z-index: 1;
+ -webkit-transition: all 0.45s cubic-bezier(0.35, 0, 0.25, 1);
+ transition: all 0.45s cubic-bezier(0.35, 0, 0.25, 1);
+ max-height: 448px;
+ min-height: 448px;
+ background: #fff;
+ overflow: hidden;
+}
+
+md-tabs.demo-source-tabs md-tab,
+md-tabs.demo-source-tabs .md-header {
+ background-color: #444444 !important;
+}
+
+
+md-tabs.demo-source-tabs md-tab-label {
+ color: #ccc !important;
+}
+
+md-tabs.demo-source-tabs .active md-tab-label {
+ color: #fff !important;
+}
+
+.demo-source-tabs.ng-hide {
+ max-height: 0px;
+ min-height: 0px;
+}
+.demo-source-tabs {
+ position: relative;
+ width: 100%;
+ z-index: 0;
+}
+.demo-content {
+ position: relative;
+ overflow:hidden;
+ min-height: 448px;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -moz-box;
+ display: -moz-flex;
+ display: -ms-flexbox;
+ display: flex;
+}
+.small-demo .demo-source-tabs:not(.ng-hide) {
+ min-height: 224px;
+ max-height: 224px;
+}
+.small-demo .demo-content {
+ min-height: 128px;
+}
+.demo-content > * {
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ -moz-box-flex: 1;
+ -moz-flex: 1;
+ -ms-flex: 1;
+ flex: 1;
+}
+
+.demo-content > div[layout-fill] {
+ min-height: 448px;
+}
+.small-demo .demo-content > div[layout-fill] {
+ min-height: 224px;
+}
+.small-demo .demo-toolbar,
+.small-demo .md-toolbar-tools {
+ min-height: 48px;
+ max-height: 48px;
+ font-size: 20px;
+}
+
+.show-source md-toolbar.demo-toolbar {
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.36);
+}
+.demo-toolbar .md-button {
+ color: #616161;
+}
+
+md-toolbar.demo-toolbar,
+.demo-source-tabs md-tab,
+.demo-source-tabs .tabs-header {
+ background: #E0E0E0 !important;
+ color: #616161;
+}
+md-toolbar.demo-toolbar md-tab-label {
+ color: #99E4EE
+}
+md-toolbar.demo-toolbar .md-button:hover,
+md-toolbar.demo-toolbar .md-button:focus,
+md-toolbar.demo-toolbar .md-button.active {
+ background: rgba(0,0,0,0.1);
+}
+
+md-toolbar.demo-toolbar .md-button {
+ -webkit-transition: all 0.3s linear;
+ -moz-transition: all 0.3s linear;
+ transition: all 0.3s linear;
+}
+.demo-source-container {
+ display: block;
+ border: 1px solid #ddd;
+ background-color: #f6f6f6;
+ height: 400px;
+}
+
+.demo-source-content {
+ height: 400px;
+}
+.demo-source-content,
+.demo-source-content pre,
+.demo-source-content code {
+ background: #f6f6f6;
+ font-family: monospace;
+}
+.demo-source-content pre {
+ max-width: 100%;
+ overflow-wrap: break-word;
+}
+
+.show-source div[demo-include] {
+ border-top: #ddd solid 2px;
+}
+
+
+.menu-separator-icon {
+ margin: 0;
+}
+.menu-module-name {
+ opacity: 0.6;
+ font-size: 18px;
+}
+
+/************
+ * DOCS
+ ************/
+.api-options-bar .md-button {
+ margin: 4px;
+ padding: 4px;
+}
+.api-options-bar .md-button:hover,
+.api-options-bar .md-button:focus {
+ background: rgba(0, 0, 0, 0.2);
+}
+.api-options-bar.with-icon md-icon {
+ position: absolute;
+ top: -3px;
+ left: 2px;
+}
+.api-options-bar.with-icon .md-button span {
+ margin-left: 22px;
+}
+
+.api-params-item {
+ min-height: 72px;
+ border-bottom: 1px solid #ddd;
+}
+.api-params-label {
+ margin-right: 8px;
+ text-align: center;
+ margin-top: 14px;
+ -webkit-align-self: flex-start;
+ -moz-align-self: flex-start;
+ -ms-flex-item-align: start;
+ align-self: flex-start;
+}
+.api-params-title {
+ color: #888;
+}
+code.api-type {
+ font-weight: bold;
+}
+
+ul {
+ margin: 0;
+}
+ul li {
+ margin-top: 3px;
+ list-style-position: inside;
+}
+ul li:first-child {
+ margin-top: 0;
+}
+
+.layout-title {
+ color: #999999;
+ font-size: 14px;
+ font-weight: bold;
+ text-transform: uppercase;
+}
+
+.api-params-content ul {
+ padding-left: 4px;
+}
+ul.methods > li {
+ margin-bottom: 48px;
+}
+
+ul.methods .method-function-syntax {
+ font-weight: normal;
+ font-size: 20px;
+ margin: 0;
+ -webkit-margin-before: 0;
+ -webkit-margin-after: 0;
+}
+ul.methods li h3 {
+ /* border-bottom: 1px solid #eee; */
+}
+
+@media (max-width: 600px) {
+ ul.methods > li {
+ padding-left: 0;
+ border-left: none;
+ list-style: default;
+ }
+ ul.methods .method-function-syntax {
+ font-size: 14px;
+ }
+}
+
+.version {
+ padding-left: 10px;
+ text-decoration: underline;
+ font-size: 0.95em;
+}
+
+.demo-source-container pre,
+.demo-source-container code {
+ min-height: 100%;
+}
+
+md-content.demo-source-container > hljs > pre > code.highlight {
+ position : absolute;
+ top : 0px;
+ left: 0px;
+ right: 0px;
+}
+
+
+.extraPad {
+ padding-left:32px !important;
+ padding-right:32px !important;
+}
diff --git a/docs/app/index.html b/docs/app/index.html
new file mode 100644
index 0000000000..0e1aa7e9df
--- /dev/null
+++ b/docs/app/index.html
@@ -0,0 +1,51 @@
+
+
+
+ Angular 2 Docs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Navigation
+
+ {{ area.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/app/js/app.js b/docs/app/js/app.js
new file mode 100644
index 0000000000..c5dd7d3115
--- /dev/null
+++ b/docs/app/js/app.js
@@ -0,0 +1,47 @@
+angular.module('app', ['ngMaterial', 'navigation-modules', 'navigation-guides'])
+
+.config(function($locationProvider) {
+ $locationProvider.html5Mode(true);
+})
+
+.controller('NavController', ['$scope', '$location', 'MODULES', 'GUIDES',
+ function($scope, $location, MODULES, GUIDES) {
+ var that = this;
+
+ this.areas = [
+ { name: 'Guides', sections: [ { pages: GUIDES.pages } ] },
+ { name: 'Modules', sections: MODULES.sections }
+ ];
+
+ this.updateCurrentPage = function(path) {
+ path = path.replace(/^\//, '');
+ console.log('path', path);
+ this.currentPage = null;
+
+ this.areas.forEach(function(area) {
+ area.sections.forEach(function(section) {
+
+ // Short-circuit out if the page has been found
+ if ( that.currentPage ) {
+ return;
+ }
+
+ if (section.path === path) {
+ console.log('found!');
+ that.currentPage = section;
+ } else {
+ section.pages.forEach(function(page) {
+ if (page.path === path) {
+ that.currentPage = page;
+ }
+ });
+ }
+ });
+ });
+ };
+
+ $scope.$watch(
+ function getLocationPath() { return $location.path(); },
+ function handleLocationPathChange(path) { that.updateCurrentPage(path); }
+ );
+}]);
diff --git a/docs/bower.json b/docs/bower.json
new file mode 100644
index 0000000000..0fc69be89f
--- /dev/null
+++ b/docs/bower.json
@@ -0,0 +1,19 @@
+{
+ "name": "angular-docs",
+ "main": "index.js",
+ "version": "0.0.0",
+ "homepage": "https://github.com/angular/angular",
+ "authors": [],
+ "license": "Apache-2.0",
+ "private": true,
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ],
+ "dependencies": {
+ "angular-material": "~0.6.0"
+ }
+}
diff --git a/docs/dgeni-package/index.js b/docs/dgeni-package/index.js
new file mode 100644
index 0000000000..c3afc48861
--- /dev/null
+++ b/docs/dgeni-package/index.js
@@ -0,0 +1,138 @@
+var Package = require('dgeni').Package;
+var jsdocPackage = require('dgeni-packages/jsdoc');
+var nunjucksPackage = require('dgeni-packages/nunjucks');
+var path = require('canonical-path');
+
+var PARTIAL_PATH = 'partials';
+var MODULES_DOCS_PATH = PARTIAL_PATH + '/modules';
+var GUIDES_PATH = PARTIAL_PATH + '/guides';
+
+// Define the dgeni package for generating the docs
+module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
+
+// Register the services and file readers
+.factory(require('./services/atParser'))
+.factory(require('./services/getJSDocComment'))
+.factory(require('./services/ExportTreeVisitor'))
+.factory(require('./readers/atScript'))
+.factory(require('./readers/ngdoc'))
+
+
+// Register the processors
+.processor(require('./processors/generateDocsFromComments'))
+.processor(require('./processors/processModuleDocs'))
+.processor(require('./processors/processClassDocs'))
+.processor(require('./processors/generateNavigationDoc'))
+
+
+// Configure the log service
+.config(function(log) {
+ log.level = 'info';
+})
+
+
+// Configure file reading
+.config(function(readFilesProcessor, atScriptFileReader, ngdocFileReader) {
+ readFilesProcessor.fileReaders = [atScriptFileReader, ngdocFileReader];
+ readFilesProcessor.basePath = path.resolve(__dirname, '../..');
+ readFilesProcessor.sourceFiles = [
+ { include: 'modules/*/src/**/*.js', basePath: 'modules' },
+ { include: 'modules/*/docs/**/*.md', basePath: 'modules' },
+ { include: 'docs/content/**/*.md', basePath: 'docs/content' }
+ ];
+})
+
+
+// Configure file writing
+.config(function(writeFilesProcessor) {
+ writeFilesProcessor.outputFolder = 'build/docs';
+})
+
+
+// Configure rendering
+.config(function(templateFinder, templateEngine) {
+
+ // Nunjucks and Angular conflict in their template bindings so change Nunjucks
+ templateEngine.config.tags = {
+ variableStart: '{$',
+ variableEnd: '$}'
+ };
+
+ templateFinder.templateFolders
+ .unshift(path.resolve(__dirname, 'templates'));
+
+ templateFinder.templatePatterns = [
+ '${ doc.template }',
+ '${ doc.id }.${ doc.docType }.template.html',
+ '${ doc.id }.template.html',
+ '${ doc.docType }.template.html',
+ 'common.template.html'
+ ];
+})
+
+
+// Configure ids and paths
+.config(function(computeIdsProcessor, computePathsProcessor) {
+
+ computeIdsProcessor.idTemplates.push({
+ docTypes: [
+ 'class',
+ 'function',
+ 'NAMED_EXPORT',
+ 'VARIABLE_STATEMENT'
+ ],
+ idTemplate: '${moduleDoc.id}.${name}',
+ getAliases: function(doc) { return [doc.id]; }
+ });
+
+ computeIdsProcessor.idTemplates.push({
+ docTypes: ['member'],
+ idTemplate: '${classDoc.id}.${name}',
+ getAliases: function(doc) { return [doc.id]; }
+ });
+
+ computeIdsProcessor.idTemplates.push({
+ docTypes: ['guide'],
+ getId: function(doc) {
+ return doc.fileInfo.relativePath
+ // path should be relative to `modules` folder
+ .replace(/.*\/?modules\//, '')
+ // path should not include `/docs/`
+ .replace(/\/docs\//, '/')
+ // path should not have a suffix
+ .replace(/\.\w*$/, '');
+ },
+ getAliases: function(doc) { return [doc.id]; }
+ });
+
+
+ computePathsProcessor.pathTemplates.push({
+ docTypes: ['module'],
+ pathTemplate: '${id}',
+ outputPathTemplate: MODULES_DOCS_PATH + '/${id}/index.html'
+ });
+
+ computePathsProcessor.pathTemplates.push({
+ docTypes: [
+ 'class',
+ 'function',
+ 'NAMED_EXPORT',
+ 'VARIABLE_STATEMENT'
+ ],
+ pathTemplate: '${moduleDoc.path}/${name}',
+ outputPathTemplate: MODULES_DOCS_PATH + '/${path}/index.html'
+ });
+
+ computePathsProcessor.pathTemplates.push({
+ docTypes: ['member'],
+ pathTemplate: '${classDoc.path}/${name}',
+ getOutputPath: function() {} // These docs are not written to their own file, instead they are part of their class doc
+ });
+
+
+ computePathsProcessor.pathTemplates.push({
+ docTypes: ['guide'],
+ pathTemplate: '${id}',
+ outputPathTemplate: GUIDES_PATH + '/${id}.html'
+ });
+});
\ No newline at end of file
diff --git a/docs/dgeni-package/mocks/mockPackage.js b/docs/dgeni-package/mocks/mockPackage.js
new file mode 100644
index 0000000000..61d0df228a
--- /dev/null
+++ b/docs/dgeni-package/mocks/mockPackage.js
@@ -0,0 +1,10 @@
+var Package = require('dgeni').Package;
+
+module.exports = function mockPackage() {
+
+ return new Package('mockPackage', [require('../')])
+
+ // provide a mock log service
+ .factory('log', function() { return require('dgeni/lib/mocks/log')(false); });
+
+};
diff --git a/docs/dgeni-package/processors/generateDocsFromComments.js b/docs/dgeni-package/processors/generateDocsFromComments.js
new file mode 100644
index 0000000000..5d8b682c66
--- /dev/null
+++ b/docs/dgeni-package/processors/generateDocsFromComments.js
@@ -0,0 +1,34 @@
+var _ = require('lodash');
+
+module.exports = function generateDocsFromComments(log) {
+ return {
+ $runAfter: ['files-read'],
+ $runBefore: ['parsing-tags'],
+ $process: function(docs) {
+ var commentDocs = [];
+ docs = _.filter(docs, function(doc) {
+ if (doc.docType !== 'atScriptFile') {
+ return true;
+ } else {
+ _.forEach(doc.fileInfo.comments, function(comment) {
+
+ // we need to check for `/**` at the start of the comment to find all the jsdoc style comments
+ comment.range.toString().replace(/^\/\*\*([\w\W]*)\*\/$/g, function(match, commentBody) {
+
+ // Create a doc from this comment
+ commentDocs.push({
+ fileInfo: doc.fileInfo,
+ startingLine: comment.range.start.line,
+ endingLine: comment.range.end.line,
+ content: commentBody,
+ codeTree: comment.treeAfter,
+ docType: 'atScriptDoc'
+ });
+ });
+ });
+ }
+ });
+ return docs.concat(commentDocs);
+ }
+ };
+};
\ No newline at end of file
diff --git a/docs/dgeni-package/processors/generateNavigationDoc.js b/docs/dgeni-package/processors/generateNavigationDoc.js
new file mode 100644
index 0000000000..5cc8a7bdbe
--- /dev/null
+++ b/docs/dgeni-package/processors/generateNavigationDoc.js
@@ -0,0 +1,66 @@
+var _ = require('lodash');
+
+module.exports = function generateNavigationDoc() {
+
+ return {
+ $runAfter: ['docs-processed'],
+ $runBefore: ['rendering-docs'],
+ $process: function(docs) {
+ var modulesDoc = {
+ value: { sections: [] },
+ moduleName: 'navigation-modules',
+ serviceName: 'MODULES',
+ template: 'data-module.template.js',
+ outputPath: 'js/navigation-modules.js'
+ };
+
+ _.forEach(docs, function(doc) {
+ if ( doc.docType === 'module' ) {
+ var moduleNavItem = {
+ path: doc.path,
+ partial: doc.outputPath,
+ name: doc.id,
+ type: 'module',
+ pages: []
+ };
+
+ modulesDoc.value.sections.push(moduleNavItem);
+
+ _.forEach(doc.exports, function(exportDoc) {
+ var exportNavItem = {
+ path: exportDoc.path,
+ partial: exportDoc.outputPath,
+ name: exportDoc.name,
+ type: exportDoc.docType
+ };
+ moduleNavItem.pages.push(exportNavItem);
+ });
+ }
+ });
+
+ docs.push(modulesDoc);
+
+
+ var guidesDoc = {
+ value: { pages: [] },
+ moduleName: 'navigation-guides',
+ serviceName: 'GUIDES',
+ template: 'data-module.template.js',
+ outputPath: 'js/navigation-guides.js'
+ };
+
+ _.forEach(docs, function(doc) {
+ if ( doc.docType === 'guide' ) {
+ var guideDoc = {
+ path: doc.path,
+ partial: doc.outputPath,
+ name: doc.id,
+ type: 'guide'
+ };
+ guidesDoc.value.pages.push(guideDoc)
+ }
+ });
+ docs.push(guidesDoc);
+ }
+ };
+};
\ No newline at end of file
diff --git a/docs/dgeni-package/processors/processClassDocs.js b/docs/dgeni-package/processors/processClassDocs.js
new file mode 100644
index 0000000000..fd0f17f1ab
--- /dev/null
+++ b/docs/dgeni-package/processors/processClassDocs.js
@@ -0,0 +1,42 @@
+var _ = require('lodash');
+
+module.exports = function processClassDocs(log, getJSDocComment) {
+
+ return {
+ $runAfter: ['processModuleDocs'],
+ $runBefore: ['parsing-tags', 'generateDocsFromComments'],
+ $process: function(docs) {
+ var memberDocs = [];
+ _.forEach(docs, function(classDoc) {
+ if ( classDoc.docType === 'class' ) {
+
+ classDoc.members = [];
+
+ // Create a new doc for each member of the class
+ _.forEach(classDoc.elements, function(memberDoc) {
+
+ classDoc.members.push(memberDoc);
+ memberDocs.push(memberDoc);
+
+ memberDoc.docType = 'member';
+ memberDoc.classDoc = classDoc;
+ memberDoc.name = memberDoc.name.literalToken.value;
+
+ if (memberDoc.commentBefore ) {
+ // If this export has a comment, remove it from the list of
+ // comments collected in the module
+ var index = classDoc.moduleDoc.comments.indexOf(memberDoc.commentBefore);
+ if ( index !== -1 ) {
+ classDoc.moduleDoc.comments.splice(index, 1);
+ }
+
+ _.assign(memberDoc, getJSDocComment(memberDoc.commentBefore));
+ }
+ });
+ }
+ });
+
+ return docs.concat(memberDocs);
+ }
+ };
+};
diff --git a/docs/dgeni-package/processors/processModuleDocs.js b/docs/dgeni-package/processors/processModuleDocs.js
new file mode 100644
index 0000000000..52db81b056
--- /dev/null
+++ b/docs/dgeni-package/processors/processModuleDocs.js
@@ -0,0 +1,46 @@
+var _ = require('lodash');
+
+module.exports = function processModuleDocs(log, ExportTreeVisitor, getJSDocComment) {
+
+ return {
+ $runAfter: ['files-read'],
+ $runBefore: ['parsing-tags', 'generateDocsFromComments'],
+ $process: function(docs) {
+ var exportDocs = [];
+ _.forEach(docs, function(doc) {
+ if ( doc.docType === 'module' ) {
+
+ log.debug('processing', doc.moduleTree.moduleName);
+
+ doc.exports = [];
+
+ if ( doc.moduleTree.visit ) {
+ var visitor = new ExportTreeVisitor();
+ visitor.visit(doc.moduleTree);
+
+ _.forEach(visitor.exports, function(exportDoc) {
+
+ doc.exports.push(exportDoc);
+ exportDocs.push(exportDoc);
+ exportDoc.moduleDoc = doc;
+
+ if (exportDoc.comment) {
+ // If this export has a comment, remove it from the list of
+ // comments collected in the module
+ var index = doc.comments.indexOf(exportDoc.comment);
+ if ( index !== -1 ) {
+ doc.comments.splice(index, 1);
+ }
+
+ _.assign(exportDoc, getJSDocComment(exportDoc.comment));
+ }
+
+ });
+ }
+ }
+ });
+
+ return docs.concat(exportDocs);
+ }
+ };
+};
\ No newline at end of file
diff --git a/docs/dgeni-package/readers/atScript.js b/docs/dgeni-package/readers/atScript.js
new file mode 100644
index 0000000000..b6e3f1cef6
--- /dev/null
+++ b/docs/dgeni-package/readers/atScript.js
@@ -0,0 +1,30 @@
+var path = require('canonical-path');
+
+
+/**
+ * @dgService atScriptFileReader
+ * @description
+ * This file reader will create a simple doc for each
+ * file including a code AST of the AtScript in the file.
+ */
+module.exports = function atScriptFileReader(log, atParser) {
+ var reader = {
+ name: 'atScriptFileReader',
+ defaultPattern: /\.js$/,
+ getDocs: function(fileInfo) {
+
+ var moduleDoc = atParser.parseModule(fileInfo);
+ moduleDoc.docType = 'module';
+ moduleDoc.id = moduleDoc.moduleTree.moduleName;
+ moduleDoc.aliases = [moduleDoc.id];
+
+ // Readers return a collection of docs read from the file
+ // but in this read there is only one document (module) to return
+ return [moduleDoc];
+ }
+ };
+
+ return reader;
+
+
+};
diff --git a/docs/dgeni-package/readers/atScript.spec.js b/docs/dgeni-package/readers/atScript.spec.js
new file mode 100644
index 0000000000..b524fc01f1
--- /dev/null
+++ b/docs/dgeni-package/readers/atScript.spec.js
@@ -0,0 +1,55 @@
+var mockPackage = require('../mocks/mockPackage');
+var Dgeni = require('dgeni');
+
+describe('atScript file reader', function() {
+
+ var dgeni, injector, reader;
+
+ var fileContent =
+ 'import {CONST} from "facade/lang";\n' +
+ '\n' +
+ '/**\n' +
+ '* A parameter annotation that creates a synchronous eager dependency.\n' +
+ '*\n' +
+ '* class AComponent {\n' +
+ '* constructor(@Inject("aServiceToken") aService) {}\n' +
+ '* }\n' +
+ '*\n' +
+ '*/\n' +
+ 'export class Inject {\n' +
+ 'token;\n' +
+ '@CONST()\n' +
+ 'constructor(token) {\n' +
+ 'this.token = token;\n' +
+ '}\n' +
+ '}';
+
+
+ beforeEach(function() {
+ dgeni = new Dgeni([mockPackage()]);
+ injector = dgeni.configureInjector();
+ reader = injector.get('atScriptFileReader');
+ });
+
+
+ it('should provide a default pattern', function() {
+ expect(reader.defaultPattern).toEqual(/\.js$/);
+ });
+
+
+ it('should parse the file using the atParser and return a single doc', function() {
+
+ var atParser = injector.get('atParser');
+ spyOn(atParser, 'parseModule').and.callThrough();
+
+ var docs = reader.getDocs({
+ content: fileContent,
+ relativePath: 'di/src/annotations.js'
+ });
+
+ expect(atParser.parseModule).toHaveBeenCalled();
+ expect(docs.length).toEqual(1);
+ expect(docs[0].docType).toEqual('module');
+ });
+
+});
\ No newline at end of file
diff --git a/docs/dgeni-package/readers/ngdoc.js b/docs/dgeni-package/readers/ngdoc.js
new file mode 100644
index 0000000000..a1e6a7e4d5
--- /dev/null
+++ b/docs/dgeni-package/readers/ngdoc.js
@@ -0,0 +1,32 @@
+var path = require('canonical-path');
+
+/**
+ * @dgService ngdocFileReader
+ * @description
+ * This file reader will pull the contents from a text file (by default .ngdoc)
+ *
+ * The doc will initially have the form:
+ * ```
+ * {
+ * content: 'the content of the file',
+ * startingLine: 1
+ * }
+ * ```
+ */
+module.exports = function ngdocFileReader() {
+ var reader = {
+ name: 'ngdocFileReader',
+ defaultPattern: /\.md$/,
+ getDocs: function(fileInfo) {
+
+ // We return a single element array because ngdoc files only contain one document
+ return [{
+ docType: 'guide',
+ content: fileInfo.content,
+ startingLine: 1
+ }];
+ }
+ };
+
+ return reader;
+};
\ No newline at end of file
diff --git a/docs/dgeni-package/readers/ngdoc.spec.js b/docs/dgeni-package/readers/ngdoc.spec.js
new file mode 100644
index 0000000000..658663c9fb
--- /dev/null
+++ b/docs/dgeni-package/readers/ngdoc.spec.js
@@ -0,0 +1,45 @@
+var ngdocFileReaderFactory = require('./ngdoc');
+var path = require('canonical-path');
+
+describe('ngdocFileReader', function() {
+
+ var fileReader;
+
+ var createFileInfo = function(file, content, basePath) {
+ return {
+ fileReader: fileReader.name,
+ filePath: file,
+ baseName: path.basename(file, path.extname(file)),
+ extension: path.extname(file).replace(/^\./, ''),
+ basePath: basePath,
+ relativePath: path.relative(basePath, file),
+ content: content
+ };
+ };
+
+
+ beforeEach(function() {
+ fileReader = ngdocFileReaderFactory();
+ });
+
+
+ describe('defaultPattern', function() {
+ it('should match .md files', function() {
+ expect(fileReader.defaultPattern.test('abc.md')).toBeTruthy();
+ expect(fileReader.defaultPattern.test('abc.js')).toBeFalsy();
+ });
+ });
+
+
+ describe('getDocs', function() {
+ it('should return an object containing info about the file and its contents', function() {
+ var fileInfo = createFileInfo('project/path/modules/someModule/foo/docs/subfolder/bar.ngdoc', 'A load of content', 'project/path');
+ expect(fileReader.getDocs(fileInfo)).toEqual([{
+ docType: 'guide',
+ content: 'A load of content',
+ startingLine: 1
+ }]);
+ });
+ });
+});
+
diff --git a/docs/dgeni-package/services/ExportTreeVisitor.js b/docs/dgeni-package/services/ExportTreeVisitor.js
new file mode 100644
index 0000000000..b8750ce8ca
--- /dev/null
+++ b/docs/dgeni-package/services/ExportTreeVisitor.js
@@ -0,0 +1,109 @@
+var traceur = require('traceur/src/node/traceur.js');
+var ParseTreeVisitor = System.get("traceur@0.0.74/src/syntax/ParseTreeVisitor").ParseTreeVisitor;
+
+module.exports = function ExportTreeVisitor(log) {
+
+ function ExportTreeVisitorImpl() {
+ ParseTreeVisitor.call(this);
+ }
+ ExportTreeVisitorImpl.prototype = {
+
+ __proto__: ParseTreeVisitor.prototype,
+
+ visitExportDeclaration: function(tree) {
+ // We are entering an export declaration - create an object to track it
+ this.currentExport = {
+ comment: tree.commentBefore,
+ location: tree.location
+ };
+ log.silly('enter', tree.type, tree.commentBefore ? 'has comment' : '');
+ ParseTreeVisitor.prototype.visitExportDeclaration.call(this, tree);
+ log.silly('exit', this.currentExport);
+
+ // We are exiting the export declaration - store the export object
+ this.exports.push(this.currentExport);
+ this.currentExport = null;
+ },
+
+ visitVariableStatement: function(tree) {
+ if ( this.currentExport ) {
+ this.updateExport(tree);
+ this.currentExport.name = "VARIABLE_STATEMENT";
+ }
+ },
+
+ visitVariableDeclaration: function(tree) {
+ if ( this.currentExport ) {
+ this.updateExport(tree);
+ this.currentExport.name = tree.lvalue;
+ this.currentExport.variableDeclaration = tree;
+ }
+ },
+
+ visitFunctionDeclaration: function(tree) {
+ if ( this.currentExport ) {
+ this.updateExport(tree);
+ this.currentExport.name = tree.name.identifierToken.value;
+ this.currentExport.functionKind = tree.functionKind;
+ this.currentExport.parameters = tree.parameterList.parameters;
+ this.currentExport.typeAnnotation = tree.typeAnnotation;
+ this.currentExport.annotations = tree.annotations;
+ this.currentExport.docType = 'function';
+
+ log.silly(tree.type, tree.commentBefore ? 'has comment' : '');
+ }
+ },
+ visitClassDeclaration: function(tree) {
+ if ( this.currentExport ) {
+ this.updateExport(tree);
+ this.currentExport.name = tree.name.identifierToken.value;
+ this.currentExport.superClass = tree.superClass;
+ this.currentExport.annotations = tree.annotations;
+ this.currentExport.elements = tree.elements;
+ this.currentExport.docType = 'class';
+ }
+ },
+ visitAsyncFunctionDeclaration: function(tree) {
+ if ( this.currentExport ) {
+ this.updateExport(tree);
+ }
+ },
+
+ visitExportDefault: function(tree) {
+ if ( this.currentExport ) {
+ this.updateExport(tree);
+ this.currentExport.name = 'DEFAULT';
+ this.currentExport.defaultExport = tree;
+ // Default exports are either classes, functions or expressions
+ // So we let the super class continue down...
+ ParseTreeVisitor.prototype.visitExportDefault.call(this, tree);
+ }
+ },
+
+ visitNamedExport: function(tree) {
+ if ( this.currentExport ) {
+ this.updateExport(tree);
+
+ this.currentExport.namedExport = tree;
+ this.currentExport.name = 'NAMED_EXPORT';
+ // TODO: work out this bit!!
+ // We need to cope with any export specifiers in the named export
+ }
+ },
+
+ // TODO - if the export is an expression, find the thing that is being
+ // exported and use it and its comments for docs
+
+ updateExport: function(tree) {
+ this.currentExport.comment = this.currentExport.comment || tree.commentBefore;
+ this.currentExport.docType = tree.type;
+ },
+
+ visit: function(tree) {
+ this.exports = [];
+ ParseTreeVisitor.prototype.visit.call(this, tree);
+ }
+ };
+
+ return ExportTreeVisitorImpl;
+};
\ No newline at end of file
diff --git a/docs/dgeni-package/services/atParser.js b/docs/dgeni-package/services/atParser.js
new file mode 100644
index 0000000000..31ece78ee6
--- /dev/null
+++ b/docs/dgeni-package/services/atParser.js
@@ -0,0 +1,97 @@
+var traceur = require('traceur/src/node/traceur.js');
+var ParseTreeVisitor = System.get(System.map.traceur + '/src/syntax/ParseTreeVisitor').ParseTreeVisitor;
+var file2modulename = require('../../../file2modulename');
+/**
+ * Wrapper around traceur that can parse the contents of a file
+ */
+module.exports = function atParser(log) {
+
+ var service = {
+ /**
+ * The options to pass to traceur
+ */
+ traceurOptions: {
+ annotations: true, // parse annotations
+ types: true, // parse types
+ memberVariables: true, // parse class fields
+ commentCallback: true // handle comments
+ },
+
+ /**
+ * Parse a module AST from the contents of a file.
+ * @param {Object} fileInfo information about the file to parse
+ * @return { { moduleTree: Object, comments: Array } } An object containing the parsed module
+ * AST and an array of all the comments found in the file
+ */
+ parseModule: parseModule
+ };
+
+ return service;
+
+
+ // Parse the contents of the file using traceur
+ function parseModule(fileInfo) {
+
+ var moduleName = file2modulename(fileInfo.relativePath);
+ var sourceFile = new traceur.syntax.SourceFile(moduleName, fileInfo.content);
+ var parser = new traceur.syntax.Parser(sourceFile);
+ var comments = [];
+ var moduleTree;
+
+ // Configure the parser
+ parser.handleComment = function(range) {
+ comments.push({ range: range });
+ };
+ traceur.options.setFromObject(service.traceurOptions);
+
+ try {
+ // Parse the file as a module, attaching the comments
+ moduleTree = parser.parseModule();
+ attachComments(moduleTree, comments);
+ } catch(ex) {
+ // HACK: sometime traceur crashes for various reasons including
+ // Not Yet Implemented (NYI)!
+ log.error(ex.stack);
+ moduleTree = {};
+ }
+ log.debug(moduleName);
+ moduleTree.moduleName = moduleName;
+
+ // We return the module AST but also a collection of all the comments
+ // since it can be helpful to iterate through them without having to
+ // traverse the AST again
+ return {
+ moduleTree: moduleTree,
+ comments: comments
+ };
+ }
+
+ // attach the comments to their nearest code tree
+ function attachComments(tree, comments) {
+
+ var visitor = new ParseTreeVisitor();
+ var index = 0;
+ var currentComment = comments[index];
+
+ if (currentComment) log.silly('comment: ' + currentComment.range.start.line + ' - ' + currentComment.range.end.line);
+
+ // Really we ought to subclass ParseTreeVisitor but this is fiddly in ES5 so
+ // it is easier to simply override the prototype's method on the instance
+ visitor.visitAny = function(tree) {
+ if (tree && tree.location && tree.location.start && currentComment) {
+ if (currentComment.range.end.offset < tree.location.start.offset) {
+ log.silly('tree: ' + tree.constructor.name + ' - ' + tree.location.start.line);
+ tree.commentBefore = currentComment;
+ currentComment.treeAfter = tree;
+ index++;
+ currentComment = comments[index];
+ if (currentComment) log.silly('comment: ' + currentComment.range.start.line + ' - ' + currentComment.range.end.line);
+ }
+ }
+ return ParseTreeVisitor.prototype.visitAny.call(this, tree);
+ };
+
+ // Visit every node of the tree using our custom method
+ visitor.visit(tree);
+ }
+};
\ No newline at end of file
diff --git a/docs/dgeni-package/services/atParser.spec.js b/docs/dgeni-package/services/atParser.spec.js
new file mode 100644
index 0000000000..7c4e1ad265
--- /dev/null
+++ b/docs/dgeni-package/services/atParser.spec.js
@@ -0,0 +1,80 @@
+var mockPackage = require('../mocks/mockPackage');
+var Dgeni = require('dgeni');
+
+describe('atParser service', function() {
+
+ var dgeni, injector, parser;
+
+ var fileContent =
+ 'import {CONST} from "facade/lang";\n' +
+ '\n' +
+ '/**\n' +
+ '* A parameter annotation that creates a synchronous eager dependency.\n' +
+ '*\n' +
+ '* class AComponent {\n' +
+ '* constructor(@Inject("aServiceToken") aService) {}\n' +
+ '* }\n' +
+ '*\n' +
+ '*/\n' +
+ 'export class Inject {\n' +
+ 'token;\n' +
+ '@CONST()\n' +
+ 'constructor(token) {\n' +
+ 'this.token = token;\n' +
+ '}\n' +
+ '}';
+
+ beforeEach(function() {
+ dgeni = new Dgeni([mockPackage()]);
+ injector = dgeni.configureInjector();
+ parser = injector.get('atParser');
+ });
+
+ it('should extract the comments from the file', function() {
+ var result = parser.parseModule({
+ content: fileContent,
+ relativePath: 'di/src/annotations.js'
+ });
+
+ expect(result.comments[0].range.toString()).toEqual(
+ '/**\n' +
+ '* A parameter annotation that creates a synchronous eager dependency.\n' +
+ '*\n' +
+ '* class AComponent {\n' +
+ '* constructor(@Inject("aServiceToken") aService) {}\n' +
+ '* }\n' +
+ '*\n' +
+ '*/'
+ );
+ });
+
+ it('should extract a module AST from the file', function() {
+ var result = parser.parseModule({
+ content: fileContent,
+ relativePath: 'di/src/annotations.js'
+ });
+
+ expect(result.moduleTree.moduleName).toEqual('di/annotations');
+ expect(result.moduleTree.scriptItemList[0].type).toEqual('IMPORT_DECLARATION');
+
+ expect(result.moduleTree.scriptItemList[1].type).toEqual('EXPORT_DECLARATION');
+ });
+
+ it('should attach comments to their following AST', function() {
+ var result = parser.parseModule({
+ content: fileContent,
+ relativePath: 'di/src/annotations.js'
+ });
+
+ expect(result.moduleTree.scriptItemList[1].commentBefore.range.toString()).toEqual(
+ '/**\n' +
+ '* A parameter annotation that creates a synchronous eager dependency.\n' +
+ '*\n' +
+ '* class AComponent {\n' +
+ '* constructor(@Inject("aServiceToken") aService) {}\n' +
+ '* }\n' +
+ '*\n' +
+ '*/'
+ );
+ });
+});
\ No newline at end of file
diff --git a/docs/dgeni-package/services/getJSDocComment.js b/docs/dgeni-package/services/getJSDocComment.js
new file mode 100644
index 0000000000..0ebc69d90a
--- /dev/null
+++ b/docs/dgeni-package/services/getJSDocComment.js
@@ -0,0 +1,28 @@
+var LEADING_STAR = /^[^\S\r\n]*\*[^\S\n\r]?/gm;
+
+/**
+ * Extact comment info from a comment object
+ * @param {Object} comment object to process
+ * @return { {startingLine, endingLine, content, codeTree}= } a comment info object
+ * or undefined if the comment is not a jsdoc style comment
+ */
+module.exports = function getJSDocComment() {
+ return function(comment) {
+
+ var commentInfo;
+
+ // we need to check for `/**` at the start of the comment to find all the jsdoc style comments
+ comment.range.toString().replace(/^\/\*\*([\w\W]*)\*\/$/g, function(match, commentBody) {
+ commentBody = commentBody.replace(LEADING_STAR, '').trim();
+
+ commentInfo = {
+ startingLine: comment.range.start.line,
+ endingLine: comment.range.end.line,
+ content: commentBody,
+ codeTree: comment.treeAfter
+ };
+ });
+
+ return commentInfo;
+ };
+};
\ No newline at end of file
diff --git a/docs/dgeni-package/services/getJSDocComment.spec.js b/docs/dgeni-package/services/getJSDocComment.spec.js
new file mode 100644
index 0000000000..210238cc67
--- /dev/null
+++ b/docs/dgeni-package/services/getJSDocComment.spec.js
@@ -0,0 +1,67 @@
+var mockPackage = require('../mocks/mockPackage');
+var Dgeni = require('dgeni');
+
+describe('getJSDocComment service', function() {
+
+ var dgeni, injector, getJSDocComment;
+
+ function createComment(commentString, start, end, codeTree) {
+ return {
+ range: {
+ toString: function() { return commentString; },
+ start: { line: start },
+ end: { line: end },
+ },
+ treeAfter: codeTree
+ };
+ }
+
+ beforeEach(function() {
+ dgeni = new Dgeni([mockPackage()]);
+ injector = dgeni.configureInjector();
+ getJSDocComment = injector.get('getJSDocComment');
+ });
+
+ it('should only return an object if the comment starts with /** and ends with */', function() {
+ var result = getJSDocComment(createComment('/** this is a jsdoc comment */'));
+ expect(result).toBeDefined();
+
+ result = getJSDocComment(createComment('/* this is a normal comment */'));
+ expect(result).toBeUndefined();
+
+ result = getJSDocComment(createComment('this is not a valid comment */'));
+ expect(result).toBeUndefined();
+
+ result = getJSDocComment(createComment('nor is this'));
+ expect(result).toBeUndefined();
+
+ result = getJSDocComment(createComment('/* or even this'));
+ expect(result).toBeUndefined();
+
+ result = getJSDocComment(createComment('/** and this'));
+ expect(result).toBeUndefined();
+ });
+
+
+ it('should return a result that contains info about the comment', function() {
+ var codeTree = {};
+ var result = getJSDocComment(createComment('/** this is a comment */', 10, 20, codeTree));
+ expect(result.startingLine).toEqual(10);
+ expect(result.endingLine).toEqual(20);
+ expect(result.codeTree).toBe(codeTree);
+ });
+
+ it('should strip off leading stars from each line', function() {
+ var result = getJSDocComment(createComment(
+ '/** this is a jsdoc comment */\n' +
+ ' *\n' +
+ ' * some content\n' +
+ ' */'
+ ));
+ expect(result.content).toEqual(
+ 'this is a jsdoc comment */\n' +
+ '\n' +
+ 'some content'
+ );
+ });
+});
\ No newline at end of file
diff --git a/docs/dgeni-package/templates/class.template.html b/docs/dgeni-package/templates/class.template.html
new file mode 100644
index 0000000000..8dace000b8
--- /dev/null
+++ b/docs/dgeni-package/templates/class.template.html
@@ -0,0 +1,14 @@
+{% extends 'layout/base.template.html' %}
+
+{% block body %}
+{$ doc.name $} class
+exported from {$ doc.moduleDoc.id $}
+{$ doc.description | marked $}
+
+Members
+{% for member in doc.members %}
+ {$ member.name $}
+ {$ member.description | marked $}
+{% endfor %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/docs/dgeni-package/templates/common.template.html b/docs/dgeni-package/templates/common.template.html
new file mode 100644
index 0000000000..dc10750a80
--- /dev/null
+++ b/docs/dgeni-package/templates/common.template.html
@@ -0,0 +1,9 @@
+{% extends 'layout/base.template.html' %}
+
+{% block body %}
+{$ doc.id $}
+({$ doc.docType $})
+
+{$ doc.description | marked $}
+
+{% endblock %}
\ No newline at end of file
diff --git a/docs/dgeni-package/templates/data-module.template.js b/docs/dgeni-package/templates/data-module.template.js
new file mode 100644
index 0000000000..498cdad8cf
--- /dev/null
+++ b/docs/dgeni-package/templates/data-module.template.js
@@ -0,0 +1,3 @@
+angular.module('{$ doc.moduleName $}', [])
+
+.value('{$ doc.serviceName $}', {$ doc.value | json $});
\ No newline at end of file
diff --git a/docs/dgeni-package/templates/guide.template.html b/docs/dgeni-package/templates/guide.template.html
new file mode 100644
index 0000000000..3ee289dbc8
--- /dev/null
+++ b/docs/dgeni-package/templates/guide.template.html
@@ -0,0 +1,5 @@
+{% extends 'layout/base.template.html' %}
+
+{% block body %}
+{$ doc.description | marked $}
+{% endblock %}
\ No newline at end of file
diff --git a/docs/dgeni-package/templates/layout/base.template.html b/docs/dgeni-package/templates/layout/base.template.html
new file mode 100644
index 0000000000..16a0d9dc96
--- /dev/null
+++ b/docs/dgeni-package/templates/layout/base.template.html
@@ -0,0 +1 @@
+{% block body %}{% endblock %}
\ No newline at end of file
diff --git a/docs/dgeni-package/templates/module.template.html b/docs/dgeni-package/templates/module.template.html
new file mode 100644
index 0000000000..2000a4ab7d
--- /dev/null
+++ b/docs/dgeni-package/templates/module.template.html
@@ -0,0 +1,16 @@
+{% extends 'layout/base.template.html' %}
+
+{% block body %}
+{$ doc.id $} module
+
+{$ doc.description | marked $}
+
+{% if doc.exports.length %}
+Exports
+
+{% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/gulpfile.js b/gulpfile.js
index 7c2cdc9bd9..8e55be5f3e 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -399,3 +399,58 @@ gulp.task('build', function(done) {
gulp.task('analyze', function(done) {
runSequence('analyze/analyzer.dart');
});
+
+
+// --------------
+// doc generation
+var Dgeni = require('dgeni');
+gulp.task('docs/dgeni', function() {
+ try {
+ var dgeni = new Dgeni([require('./docs/dgeni-package')]);
+ return dgeni.generate();
+ } catch(x) {
+ console.log(x.stack);
+ throw x;
+ }
+});
+
+var bower = require('bower');
+gulp.task('docs/bower', function() {
+ var bowerTask = bower.commands.install(undefined, undefined, { cwd: 'docs' });
+ bowerTask.on('log', function (result) {
+ console.log('bower:', result.id, result.data.endpoint.name);
+ });
+ bowerTask.on('error', function(error) {
+ console.log(error);
+ });
+ return bowerTask;
+});
+
+gulp.task('docs/assets', ['docs/bower'], function() {
+ return gulp.src('docs/bower_components/**/*')
+ .pipe(gulp.dest('build/docs/lib'));
+});
+
+gulp.task('docs/app', function() {
+ return gulp.src('docs/app/**/*')
+ .pipe(gulp.dest('build/docs'));
+});
+
+gulp.task('docs', ['docs/assets', 'docs/app', 'docs/dgeni']);
+gulp.task('docs-watch', function() {
+ return gulp.watch('docs/app/**/*', ['docs-app']);
+});
+
+var jasmine = require('gulp-jasmine');
+gulp.task('docs/test', function () {
+ return gulp.src('docs/**/*.spec.js')
+ .pipe(jasmine());
+});
+
+var webserver = require('gulp-webserver');
+gulp.task('docs/serve', function() {
+ gulp.src('build/docs/')
+ .pipe(webserver({
+ fallback: 'index.html'
+ }));
+});
diff --git a/package.json b/package.json
index 143a284783..9c29df5a8a 100644
--- a/package.json
+++ b/package.json
@@ -28,11 +28,18 @@
"which": "~1"
},
"devDependencies": {
+ "bower": "^1.3.12",
+ "canonical-path": "0.0.2",
+ "dgeni": "^0.4.1",
+ "dgeni-packages": "^0.10.7",
"gulp": "^3.8.8",
"gulp-changed": "^1.0.0",
"gulp-ejs": "^0.3.1",
+ "gulp-jasmine": "^1.0.1",
"gulp-load-plugins": "^0.7.1",
"gulp-rename": "^1.2.0",
- "gulp-shell": "^0.2.10"
+ "gulp-shell": "^0.2.10",
+ "gulp-webserver": "^0.8.7",
+ "lodash": "^2.4.1"
}
}