docs(api/dart): add support for generation and display (#1888)
Fixes #1880. Supersedes #1593.
This commit is contained in:
parent
08d051d2f6
commit
1bef20abc4
|
@ -15,13 +15,13 @@ env:
|
|||
- TASK=lint
|
||||
- TASK="run-e2e-tests --fast" SCRIPT=examples-install.sh
|
||||
- TASK="run-e2e-tests --fast" SCRIPT=examples-install-preview.sh
|
||||
- TASK=harp-compile SCRIPT=deploy-install.sh
|
||||
- TASK=harp-compile SCRIPT=deploy-install-preview.sh
|
||||
- TASK=build-compile SCRIPT=deploy-install.sh
|
||||
- TASK=build-compile SCRIPT=deploy-install-preview.sh
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- env: "TASK=\"run-e2e-tests --fast\" SCRIPT=examples-install-preview.sh"
|
||||
- env: "TASK=harp-compile SCRIPT=deploy-install-preview.sh"
|
||||
- env: "TASK=build-compile SCRIPT=deploy-install-preview.sh"
|
||||
before_install:
|
||||
- npm install -g gulp --no-optional
|
||||
install:
|
||||
|
|
150
gulpfile.js
150
gulpfile.js
|
@ -30,6 +30,7 @@ var tslint = require('gulp-tslint');
|
|||
// 2. Think about using spawn instead of exec in case of long error messages.
|
||||
|
||||
var TOOLS_PATH = './tools';
|
||||
var ANGULAR_IO_PROJECT_PATH = path.resolve('.');
|
||||
var ANGULAR_PROJECT_PATH = '../angular';
|
||||
var PUBLIC_PATH = './public';
|
||||
var TEMP_PATH = './_temp';
|
||||
|
@ -63,12 +64,21 @@ var _devguideShredJadeOptions = {
|
|||
};
|
||||
|
||||
var _apiShredOptions = {
|
||||
lang: 'ts',
|
||||
examplesDir: path.join(ANGULAR_PROJECT_PATH, 'modules/@angular/examples'),
|
||||
fragmentsDir: path.join(DOCS_PATH, '_fragments/_api'),
|
||||
zipDir: path.join(RESOURCES_PATH, 'zips/api'),
|
||||
logLevel: _dgeniLogLevel
|
||||
};
|
||||
|
||||
var _apiShredOptionsForDart = {
|
||||
lang: 'dart',
|
||||
examplesDir: path.resolve(ngPathFor('dart'), 'examples'),
|
||||
fragmentsDir: path.join(DOCS_PATH, '_fragments/_api'),
|
||||
zipDir: path.join(RESOURCES_PATH, 'zips/api'),
|
||||
logLevel: _dgeniLogLevel
|
||||
};
|
||||
|
||||
var _excludePatterns = ['**/node_modules/**', '**/typings/**', '**/packages/**'];
|
||||
|
||||
var _excludeMatchers = _excludePatterns.map(function(excludePattern){
|
||||
|
@ -96,6 +106,14 @@ var _exampleProtractorBoilerplateFiles = [
|
|||
|
||||
var _exampleConfigFilename = 'example-config.json';
|
||||
|
||||
var lang, langs;
|
||||
function configLangs(langOption) {
|
||||
lang = (langOption || 'all').toLowerCase();
|
||||
if (lang === 'all') { lang = '(ts|js|dart)'; }
|
||||
langs = lang.match(/\w+/g); // the languages in `lang` as an array
|
||||
}
|
||||
configLangs(argv.lang);
|
||||
|
||||
function isDartPath(path) {
|
||||
// Testing via indexOf() for now. If we need to match only paths with folders
|
||||
// named 'dart' vs 'dart*' then try: path.match('/dart(/|$)') != null;
|
||||
|
@ -131,6 +149,7 @@ gulp.task('run-e2e-tests', runE2e);
|
|||
* all means (ts|js|dart)
|
||||
*/
|
||||
function runE2e() {
|
||||
if (!argv.lang) configLangs('ts|js'); // Exclude dart by default
|
||||
var promise;
|
||||
if (argv.fast) {
|
||||
// fast; skip all setup
|
||||
|
@ -183,8 +202,6 @@ function runE2e() {
|
|||
// each app/spec collection sequentially.
|
||||
function findAndRunE2eTests(filter, outputFile) {
|
||||
// create an output file with header.
|
||||
var lang = (argv.lang || '(ts|js)').toLowerCase();
|
||||
if (lang === 'all') { lang = '(ts|js|dart)'; }
|
||||
var startTime = new Date().getTime();
|
||||
var header = `Doc Sample Protractor Results for ${lang} on ${new Date().toLocaleString()}\n`;
|
||||
header += argv.fast ?
|
||||
|
@ -528,7 +545,9 @@ gulp.task('build-docs', ['build-devguide-docs', 'build-api-docs', 'build-plunker
|
|||
// Stop zipping examples Feb 28, 2016
|
||||
//gulp.task('build-docs', ['build-devguide-docs', 'build-api-docs', 'build-plunkers', '_zip-examples']);
|
||||
|
||||
gulp.task('build-api-docs', ['build-js-api-docs', 'build-ts-api-docs', 'build-dart-cheatsheet']);
|
||||
gulp.task('build-api-docs', ['build-js-api-docs', 'build-ts-api-docs',
|
||||
// On TRAVIS? Skip building the Dart API docs for now.
|
||||
...(process.env.TRAVIS ? [] : ['build-dart-api-docs'])]);
|
||||
|
||||
gulp.task('build-devguide-docs', ['_shred-devguide-examples', '_shred-devguide-shared-jade'], function() {
|
||||
return buildShredMaps(true);
|
||||
|
@ -542,12 +561,40 @@ gulp.task('build-js-api-docs', ['_shred-api-examples'], function() {
|
|||
return buildApiDocs('js');
|
||||
});
|
||||
|
||||
gulp.task('build-dart-api-docs', ['_shred-api-examples', 'dartdoc'], function() {
|
||||
// TODO(chalin): also build build-dart-cheatsheet
|
||||
return buildApiDocsForDart();
|
||||
});
|
||||
|
||||
gulp.task('build-plunkers', ['_copy-example-boilerplate'], function() {
|
||||
return plunkerBuilder.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log });
|
||||
});
|
||||
|
||||
gulp.task('build-dart-cheatsheet', [], function() {
|
||||
return buildApiDocs('dart');
|
||||
gutil.log('build-dart-cheatsheet - NOT IMPLEMENTED YET');
|
||||
// return buildApiDocsForDart();
|
||||
});
|
||||
|
||||
gulp.task('dartdoc', ['pub upgrade'], function() {
|
||||
const ngRepoPath = ngPathFor('dart');
|
||||
if (argv.fast && fs.existsSync(path.resolve(ngRepoPath, 'doc'))) {
|
||||
gutil.log('Skipping dartdoc: --fast flag enabled and "doc" dir exists');
|
||||
return true;
|
||||
}
|
||||
checkAngularProjectPath(ngRepoPath);
|
||||
const dartdoc = spawnExt('dartdoc', ['--output', 'doc/api', '--add-crossdart'], { cwd: ngRepoPath});
|
||||
return dartdoc.promise;
|
||||
});
|
||||
|
||||
gulp.task('pub upgrade', [], function() {
|
||||
const ngRepoPath = ngPathFor('dart');
|
||||
if (argv.fast && fs.existsSync(path.resolve(ngRepoPath, 'packages'))) {
|
||||
gutil.log('Skipping pub upgrade: --fast flag enabled and "packages" dir exists');
|
||||
return true;
|
||||
}
|
||||
checkAngularProjectPath(ngRepoPath);
|
||||
const pubUpgrade = spawnExt('pub', ['upgrade'], { cwd: ngRepoPath});
|
||||
return pubUpgrade.promise;
|
||||
});
|
||||
|
||||
gulp.task('git-changed-examples', ['_shred-devguide-examples'], function(){
|
||||
|
@ -596,10 +643,35 @@ gulp.task('git-changed-examples', ['_shred-devguide-examples'], function(){
|
|||
});
|
||||
});
|
||||
|
||||
gulp.task('harp-compile', ['build-docs'], function() {
|
||||
gulp.task('harp-compile', [], function() {
|
||||
return harpCompile()
|
||||
});
|
||||
|
||||
gulp.task('serve', [], function() {
|
||||
// Harp will serve files from workspace.
|
||||
const cmd = 'npm run harp -- server .';
|
||||
gutil.log('Launching harp server (over project files)');
|
||||
gutil.log(` > ${cmd}`);
|
||||
gutil.log('Note: issuing this command directly from the command line will show harp comiple warnings.');
|
||||
return execPromise(cmd);
|
||||
});
|
||||
|
||||
gulp.task('serve-www', [], function() {
|
||||
// Serve generated site.
|
||||
return execPromise('npm run live-server ./www');
|
||||
});
|
||||
|
||||
gulp.task('build-compile', ['build-docs'], function() {
|
||||
return harpCompile();
|
||||
});
|
||||
|
||||
gulp.task('check-serve', ['build-docs'], function() {
|
||||
return harpCompile().then(function() {
|
||||
gutil.log('Launching live-server over ./www');
|
||||
return execPromise('npm run live-server ./www');
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('check-deploy', ['build-docs'], function() {
|
||||
return harpCompile().then(function() {
|
||||
gutil.log('compile ok');
|
||||
|
@ -693,8 +765,15 @@ gulp.task('_shred-clean-devguide', function(cb) {
|
|||
});
|
||||
|
||||
gulp.task('_shred-api-examples', ['_shred-clean-api'], function() {
|
||||
checkAngularProjectPath();
|
||||
return docShredder.shred(_apiShredOptions);
|
||||
const promises = [];
|
||||
gutil.log('Shredding API examples for languages: ' + langs.join(', '));
|
||||
langs.forEach((lang) => {
|
||||
if (lang === 'js') return; // JS is handled via TS.
|
||||
checkAngularProjectPath(ngPathFor(lang));
|
||||
const options = lang == 'dart' ? _apiShredOptionsForDart : _apiShredOptions;
|
||||
promises.push(docShredder.shred(options));
|
||||
});
|
||||
return Q.all(promises);
|
||||
});
|
||||
|
||||
gulp.task('_shred-clean-api', function(cb) {
|
||||
|
@ -1087,8 +1166,8 @@ function buildApiDocs(targetLanguage) {
|
|||
var dgeni = new Dgeni([package]);
|
||||
return dgeni.generate();
|
||||
} catch(err) {
|
||||
gutil.log(err);
|
||||
gutil.log(err.stack);
|
||||
console.error(err);
|
||||
console.error(err.stack);
|
||||
throw err;
|
||||
}
|
||||
|
||||
|
@ -1099,6 +1178,48 @@ function buildApiDocs(targetLanguage) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function buildApiDocsForDart() {
|
||||
const apiDir = 'api';
|
||||
const vers = 'latest';
|
||||
const dab = require('./tools/dart-api-builder/dab')(ANGULAR_IO_PROJECT_PATH);
|
||||
const log = dab.log;
|
||||
|
||||
log.level = _dgeniLogLevel;
|
||||
const dabInfo = dab.dartPkgConfigInfo;
|
||||
dabInfo.ngIoDartApiDocPath = path.join(DOCS_PATH, 'dart', vers, apiDir);
|
||||
dabInfo.ngDartDocPath = path.join(ngPathFor('dart'), 'doc', apiDir);
|
||||
// Exclude API entries for developer/internal libraries. Also exclude entries for
|
||||
// the top-level catch all "angular2" library (otherwise every entry appears twice).
|
||||
dabInfo.excludeLibRegExp = new RegExp(/^(?!angular2)|\.testing|_|codegen|^angular2$/);
|
||||
|
||||
try {
|
||||
checkAngularProjectPath('dart');
|
||||
var destPath = dabInfo.ngIoDartApiDocPath;
|
||||
var sourceDirs = fs.readdirSync(dabInfo.ngDartDocPath)
|
||||
.filter((name) => !name.match(/^index/))
|
||||
.map((name) => path.join(dabInfo.ngDartDocPath, name));
|
||||
log.info(`Building Dart API pages for ${sourceDirs.length} libraries`);
|
||||
|
||||
return copyFiles(sourceDirs, [destPath]).then(() => {
|
||||
log.debug('Finished copying', sourceDirs.length, 'directories from', dabInfo.ngDartDocPath, 'to', destPath);
|
||||
|
||||
const apiEntries = dab.loadApiDataAndSaveToApiListFile();
|
||||
const tmpDocsPath = path.resolve(path.join(process.env.HOME, 'tmp/docs.json'));
|
||||
if (argv.dumpDocsJson) fs.writeFileSync(tmpDocsPath, JSON.stringify(apiEntries, null, 2));
|
||||
dab.createApiDataAndJadeFiles(apiEntries);
|
||||
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
console.error(err.stack);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function buildShredMaps(shouldWrite) {
|
||||
var options = {
|
||||
devguideExamplesDir: _devguideShredOptions.examplesDir,
|
||||
|
@ -1270,8 +1391,13 @@ function execCommands(cmds, options, cb) {
|
|||
});
|
||||
}
|
||||
|
||||
function checkAngularProjectPath() {
|
||||
if (!fs.existsSync(ANGULAR_PROJECT_PATH)) {
|
||||
throw new Error('API related tasks require the angular2 repo to be at ' + path.resolve(ANGULAR_PROJECT_PATH));
|
||||
function ngPathFor(lang) {
|
||||
return ANGULAR_PROJECT_PATH + (lang === 'dart' ? '-dart' : '');
|
||||
}
|
||||
|
||||
function checkAngularProjectPath(lang) {
|
||||
var ngPath = path.resolve(ngPathFor(lang || 'ts'));
|
||||
if (!fs.existsSync(ngPath)) {
|
||||
throw new Error('API related tasks require the angular2 repo to be at ' + ngPath);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
"broken-link-checker": "0.7.1",
|
||||
"browser-sync": "^2.9.3",
|
||||
"canonical-path": "0.0.2",
|
||||
"cheerio": "^0.20.0",
|
||||
"cross-spawn": "^4.0.0",
|
||||
"codelyzer": "0.0.22",
|
||||
"del": "^2.2.0",
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Refer to jade.template.html and addJadeDataDocsProcessor to figure out where the context of this jade file originates
|
||||
// template: public/_includes/_hero
|
||||
//- Refer to jade.template.html and addJadeDataDocsProcessor to figure out where the context of this jade file originates
|
||||
- var textFormat = '';
|
||||
- var headerTitle = title + (typeof varType !== 'undefined' ? (': ' + varType) : '');
|
||||
- var capitalize = function capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); }
|
||||
- var useBadges = docType || stability;
|
||||
|
||||
// renamer :: String -> String
|
||||
// Renames `Let` and `Var` into `Const`
|
||||
//- renamer :: String -> String
|
||||
//- Renames `Let` and `Var` into `Const`
|
||||
- var renamer = function renamer(docType) {
|
||||
- return (docType === 'Let' || docType === 'Var') ? 'Const' : docType
|
||||
- }
|
||||
|
@ -13,7 +13,7 @@
|
|||
if current.path[4] && current.path[3] == 'api'
|
||||
- var textFormat = 'is-standard-case'
|
||||
|
||||
header(class="hero background-sky")
|
||||
header(class="hero background-sky", style=fixHeroCss ? "height:auto" : "")
|
||||
div(class="inner-header")
|
||||
h1(class="hero-title text-display-1 #{textFormat}") #{headerTitle}
|
||||
if useBadges
|
||||
|
@ -33,5 +33,7 @@ header(class="hero background-sky")
|
|||
if subtitle
|
||||
h2.hero-subtitle.text-subhead #{subtitle}
|
||||
|
||||
else if current.path[3] == 'api' && current.path[1] == 'dart'
|
||||
block breadcrumbs
|
||||
else if current.path[0] == "docs"
|
||||
!= partial("_version-dropdown")
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
//- WARNING: _layout.jade and _layout-dart-api.jade should match in terms of content
|
||||
//- except that one uses Harp partial/yield and the other uses Jade extends/include.
|
||||
doctype
|
||||
html(lang="en" ng-app="angularIOApp" itemscope itemtype="http://schema.org/Framework")
|
||||
// template: public/docs/_layout-dart-api
|
||||
head
|
||||
include ../_includes/_head-include
|
||||
block head-extra
|
||||
|
||||
block var-def
|
||||
body(class="l-offset-nav l-offset-side-nav" ng-controller="AppCtrl as appCtrl")
|
||||
include ../_includes/_main-nav
|
||||
if current.path[2]
|
||||
include _includes/_side-nav
|
||||
include ../_includes/_hero
|
||||
include ../_includes/_banner
|
||||
|
||||
if current.path[3] == 'api'
|
||||
if current.path[4] == 'index'
|
||||
block main-content
|
||||
else
|
||||
article(class="l-content-small grid-fluid docs-content")
|
||||
block main-content
|
||||
else if current.path.indexOf('cheatsheet') > 0
|
||||
block main-content
|
||||
else
|
||||
if current.path[3] == 'index' || current.path[3] == 'styleguide'
|
||||
article(class="l-content-small grid-fluid docs-content")
|
||||
block main-content
|
||||
else
|
||||
article(class="l-content-small grid-fluid docs-content")
|
||||
div(class="c10")
|
||||
.showcase
|
||||
.showcase-content
|
||||
block main-content
|
||||
if (current.path[3] == 'guide' || current.path[3] == 'tutorial') && current.path[4]
|
||||
include ../_includes/_next-item
|
||||
|
||||
include ../_includes/_footer
|
||||
include ../_includes/_scripts-include
|
|
@ -1,8 +1,13 @@
|
|||
//- WARNING: _layout.jade and _layout-dart-api.jade should match in terms of content
|
||||
//- except that one uses Harp partial/yield and the other uses Jade extends/include.
|
||||
doctype
|
||||
html(lang="en" ng-app="angularIOApp" itemscope itemtype="http://schema.org/Framework")
|
||||
// template: public/docs/_layout
|
||||
head
|
||||
!= partial("../_includes/_head-include")
|
||||
block head-extra
|
||||
|
||||
//-
|
||||
body(class="l-offset-nav l-offset-side-nav" ng-controller="AppCtrl as appCtrl")
|
||||
!= partial("../_includes/_main-nav")
|
||||
if current.path[2]
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
.l-main-section
|
||||
h2 Beta
|
||||
:marked
|
||||
> **WARNING:** API documentation is preliminary and subject to change.
|
||||
|
||||
p.
|
||||
The proposed Angular 2 API does not yet have Dart-specific documentation.
|
||||
However, because the Dart and JavaScript APIs are generated from the same source,
|
||||
you might find the JavaScript API docs helpful:
|
||||
> **Known issues:** Although this generated API reference displays Dart
|
||||
APIs, individual pages sometimes describe TypeScript APIs accompanied with
|
||||
TypeScript code. The angular.io issue tracker contains [all known
|
||||
issues][api-issues]; if you notice others, please [report
|
||||
them][new-issue]. Thanks!
|
||||
|
||||
p.text-center
|
||||
<b><a href="/docs/js/latest/api/">Angular 2 API Preview (JavaScript)</a></b>
|
||||
[new-issue]: https://github.com/angular/angular.io/issues/new?labels=dart,api&title=%5BDart%5D%5BAPI%5D%20
|
||||
[api-issues]: https://github.com/angular/angular.io/issues?q=label%3Aapi+label%3Adart
|
||||
|
||||
api-list(src="api-list.json" lang="dart")
|
||||
|
|
|
@ -26,6 +26,8 @@ angularIO.directive('apiList', function () {
|
|||
controller: function($scope, $attrs, $http, $location) {
|
||||
var $ctrl = this;
|
||||
|
||||
var isForDart = $attrs.lang === 'dart';
|
||||
|
||||
$ctrl.apiTypes = [
|
||||
{ cssClass: 'stable', title: 'Stable', matches: ['stable']},
|
||||
{ cssClass: 'directive', title: 'Directive', matches: ['directive'] },
|
||||
|
@ -37,6 +39,9 @@ angularIO.directive('apiList', function () {
|
|||
{ cssClass: 'const', title: 'Const', matches: ['var', 'let', 'const'] }
|
||||
];
|
||||
|
||||
if (isForDart) $ctrl.apiTypes = $ctrl.apiTypes.filter((t) =>
|
||||
!t.cssClass.match(/^(stable|directive|decorator|interface|enum)$/));
|
||||
|
||||
$ctrl.apiFilter = getApiFilterFromLocation();
|
||||
$ctrl.apiType = getApiTypeFromLocation();
|
||||
$ctrl.groupedSections = [];
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
TARGET=node_modules/terraform/lib/helpers/raw.js
|
||||
|
||||
# Around line 282 change from/to:
|
||||
# var namespace = sourcePath.split(".")[0].split("/")
|
||||
# var namespace = sourcePath.split('.').slice(0, -1).join('.').split('/')
|
||||
|
||||
if [ -e "$TARGET" ]; then
|
||||
perl -i.bak -pe 's/^(\s+var namespace.*split\("."\))\[0\]/\1.slice(0, -1).join(".")/' "$TARGET"
|
||||
echo "Patched '$TARGET'."
|
||||
else
|
||||
echo "Nothing to patch. Can't find file '$TARGET'."
|
||||
exit 1;
|
||||
fi
|
|
@ -0,0 +1,18 @@
|
|||
'use strict';
|
||||
|
||||
var Package = require('dgeni').Package;
|
||||
var path = require('canonical-path');
|
||||
|
||||
module.exports = new Package('dart', [])
|
||||
|
||||
.factory(require('./services/apiListDataFileService'))
|
||||
.factory(require('./services/arrayFromIterable'))
|
||||
.factory(require('./services/dartPkgConfigInfo'))
|
||||
.factory(require('./services/logFactory'))
|
||||
.factory(require('./services/preprocessDartDocData'))
|
||||
|
||||
// Register the processors
|
||||
.processor(require('./processors/loadDartDocData'))
|
||||
// .processor(require('./processors/createApiListData'))
|
||||
// .processor(require('./processors/loadDartDocHtml'))
|
||||
;
|
|
@ -0,0 +1,21 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('canonical-path');
|
||||
|
||||
module.exports = function loadDartDocDataProcessor(log, dartPkgConfigInfo, preprocessDartDocData) {
|
||||
return {
|
||||
// $runAfter: ['reading-docs'],
|
||||
// $runBefore: ['docs-read'],
|
||||
|
||||
$process: function (docs) {
|
||||
if (docs.length != 0) log.error('Expected docs array to be nonempty.');
|
||||
|
||||
const dataFilePath = path.resolve(dartPkgConfigInfo.ngDartDocPath, 'index.json');
|
||||
const dartDocData = require(dataFilePath);
|
||||
log.info('Loaded', dartDocData.length, 'dartdoc api entries from', dataFilePath);
|
||||
|
||||
preprocessDartDocData.preprocess(dartDocData);
|
||||
docs.push(...dartDocData);
|
||||
}
|
||||
};
|
||||
};
|
|
@ -0,0 +1,45 @@
|
|||
'use strict';
|
||||
|
||||
var path = require('canonical-path');
|
||||
var fs = require("q-io/fs");
|
||||
var q = require('q');
|
||||
var cheerio = require('cheerio');
|
||||
|
||||
// Original sample file by @petebacondarwin
|
||||
// Not currently used, but keeping it for now,
|
||||
// until we completely rule out use of dgeni.
|
||||
|
||||
module.exports = function loadDartDocHtmlProcessor(log, dartPkgConfigInfo) {
|
||||
return {
|
||||
$runAfter: ['loadDartDocDataProcessor'],
|
||||
// $runBefore: ['docs-read'],
|
||||
|
||||
$process: function (docs) {
|
||||
var ngIoDartApiDocPath = dartPkgConfigInfo.ngIoDartApiDocPath;
|
||||
|
||||
// Return a promise sync we are async in here
|
||||
return q.all(docs.map(function (doc) {
|
||||
if (doc.kind.match(/-dart-api$/)) return;
|
||||
|
||||
// Load up the HTML and extract the contents of the body
|
||||
var htmlPath = path.resolve(ngIoDartApiDocPath, doc.href);
|
||||
|
||||
return fs.exists(htmlPath).then(function (exists) {
|
||||
|
||||
if (!exists) {
|
||||
log.debug('missing html ' + htmlPath);
|
||||
return;
|
||||
}
|
||||
|
||||
return fs.read().then(function (html) {
|
||||
log.info('Reading ' + htmlPath)
|
||||
var $ = cheerio.load(html);
|
||||
doc.htmlContent = $('body').contents().html();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,76 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('assert-plus');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('canonical-path');
|
||||
const Array_from = require('./arrayFromIterable');
|
||||
|
||||
module.exports = function apiListDataFileService(log, dartPkgConfigInfo) {
|
||||
|
||||
const _self = {
|
||||
|
||||
mainDataFileName: 'api-list.json',
|
||||
mainDataFilePath: null,
|
||||
|
||||
libToEntryMap: null,
|
||||
containerToEntryMap: null,
|
||||
numExcludedEntries: 0,
|
||||
|
||||
createDataAndSaveToFile: function (dartDocDataWithExtraProps) {
|
||||
const libToEntryMap = _self.libToEntryMap = new Map();
|
||||
const containerToEntryMap = _self.containerToEntryMap = new Map();
|
||||
const re = dartPkgConfigInfo.excludeLibRegExp;
|
||||
|
||||
// Populate the two maps from dartDocDataWithExtraProps.
|
||||
dartDocDataWithExtraProps.forEach((e) => {
|
||||
// Skip non-preprocessed entries.
|
||||
if (!e.kind) return true;
|
||||
|
||||
// Exclude non-public APIs.
|
||||
if (e.libName.match(re)) { _self.numExcludedEntries++; return true; }
|
||||
|
||||
let key;
|
||||
if (e.kind.startsWith('entry')) {
|
||||
// Store library entry info in lib map.
|
||||
key = e.libName;
|
||||
assert.equal(key, e.enclosedByQualifiedName, e);
|
||||
_set(libToEntryMap, key, e);
|
||||
} else if (e.enclosedBy) {
|
||||
assert.notEqual(e.type, 'library');
|
||||
key = e.enclosedByQualifiedName;
|
||||
} else {
|
||||
assert.equal(e.type, 'library');
|
||||
// Add library "index" page to the library's entries in the general container map,
|
||||
// but not the lib map which is used to create the main API page index.
|
||||
key = e.libName;
|
||||
_set(containerToEntryMap, key, e);
|
||||
// Add the library as an entry to the Angular2 package container:
|
||||
key = '';
|
||||
}
|
||||
_set(containerToEntryMap, key, e);
|
||||
});
|
||||
log.info('Excluded', _self.numExcludedEntries, 'library entries (regexp match).');
|
||||
|
||||
// Write the library map out as the top-level data file.
|
||||
_self.mainDataFilePath = path.resolve(path.join(dartPkgConfigInfo.ngIoDartApiDocPath, _self.mainDataFileName));
|
||||
|
||||
// The data file needs to be a map of lib names to an array of entries
|
||||
const fileData = Object.create(null);
|
||||
for (let name of Array_from(libToEntryMap.keys()).sort()) {
|
||||
fileData[name] = Array_from(libToEntryMap.get(name).values());
|
||||
}
|
||||
fs.writeFileSync(_self.mainDataFilePath, JSON.stringify(fileData, null, 2));
|
||||
log.info('Wrote', Object.keys(fileData).length, 'library entries to', _self.mainDataFilePath);
|
||||
return fileData;
|
||||
},
|
||||
|
||||
}
|
||||
return _self;
|
||||
};
|
||||
|
||||
// Adds e to the map of m[key].
|
||||
function _set(m, key, e) {
|
||||
if (!m.has(key)) m.set(key, new Map());
|
||||
const entryMap = m.get(key);
|
||||
entryMap.set(e.name, e);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function arrayFromIterable(iterable) {
|
||||
const arr = [];
|
||||
for (let e of iterable) arr.push(e);
|
||||
return arr;
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @return {Object} The Dart package config information
|
||||
*/
|
||||
module.exports = function dartPkgConfigInfo() {
|
||||
const _self = {
|
||||
ngIoDartApiDocPath: 'ngIoDartApiDocPath is uninitialized',
|
||||
ngDartDocPath: 'ngDartDocPath is uninitialized',
|
||||
excludeLibRegExp: null,
|
||||
};
|
||||
return _self;
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function logFactory() {
|
||||
var winston = require('winston');
|
||||
winston.cli();
|
||||
winston.level = 'info';
|
||||
return winston;
|
||||
};
|
|
@ -0,0 +1,81 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('assert-plus');
|
||||
const path = require('canonical-path');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
module.exports = function preprocessDartDocData(log, dartPkgConfigInfo) {
|
||||
|
||||
const _self = {
|
||||
|
||||
entryMap: null,
|
||||
|
||||
preprocess: function (dartDocData) {
|
||||
// List of API entities
|
||||
let entryMap = _self.entryMap = new Map(); // used to remove duplicates
|
||||
let numDuplicates = 0;
|
||||
|
||||
dartDocData
|
||||
.forEach((e) => {
|
||||
if (entryMap.has(e.href)) {
|
||||
log.debug('Dartdoc preprocessor: duplicate entry for', e.href);
|
||||
numDuplicates++;
|
||||
return true;
|
||||
}
|
||||
// Sample entry (note that enclosedBy is optional):
|
||||
// {
|
||||
// "name": "Pipe",
|
||||
// "qualifiedName": "angular2.core.Pipe",
|
||||
// "href": "angular2.core/Pipe-class.html",
|
||||
// "type": "class",
|
||||
// "enclosedBy": {
|
||||
// "name": "angular2.core",
|
||||
// "type": "library"
|
||||
// }
|
||||
// }
|
||||
|
||||
// Save original type property since it will be overridden.
|
||||
e.origDartDocType = e.type;
|
||||
const name = e.name;
|
||||
const qualifiedName = e.qualifiedName;
|
||||
const matches = e.href.match(/-([a-z]+)\.html/);
|
||||
let type = matches ? (e.typeFromHref = matches[1]) : e.type;
|
||||
// Conform to TS type names for now.
|
||||
if (type === 'constant') type = 'let';
|
||||
|
||||
let libName;
|
||||
e.enclosedByQualifiedName = path.dirname(e.href);
|
||||
if (e.enclosedBy && e.enclosedBy.type === 'library') {
|
||||
e.kind = 'entry-dart-api';
|
||||
libName = e.enclosedBy.name;
|
||||
assert.equal(libName, e.enclosedByQualifiedName, e.kind);
|
||||
} else if (e.origDartDocType === 'library') {
|
||||
e.kind = 'library-dart-api';
|
||||
libName = e.name;
|
||||
e.enclosedByQualifiedName = ''; // Dart libraries can only be at the top level.
|
||||
} else {
|
||||
e.kind = 'subentry-dart-api';
|
||||
libName = e.enclosedByQualifiedName.split('/')[0];
|
||||
assert.equal(path.join(libName, e.enclosedBy.name), e.enclosedByQualifiedName, e);
|
||||
}
|
||||
e.docType = type;
|
||||
e.libName = libName;
|
||||
e.path = e.href;
|
||||
e.title = name;
|
||||
e.layout = false; // To prevent public/docs/_layout.jade from be applied to Dart API pages
|
||||
// Also set above:
|
||||
// e.kind: one of {library,entry,subentry}-dart-api
|
||||
// e.enclosedByQualifiedName
|
||||
// e.origDartDocType
|
||||
// e.typeFromHref
|
||||
Object.freeze(e);
|
||||
entryMap.set(e.path, e);
|
||||
log.silly('Preproc API entity =', JSON.stringify(e, null, 2));
|
||||
});
|
||||
// There shouldn't be duplicates (hence the warning), but there are:
|
||||
// https://github.com/dart-lang/dartdoc/issues/1197
|
||||
if (numDuplicates) log.warn('Number of duplicate dartdoc entries', numDuplicates);
|
||||
},
|
||||
};
|
||||
return _self;
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
'use strict';
|
||||
|
||||
// This file is likely outdated.
|
||||
// To run, cd to this dir and
|
||||
// node test.js
|
||||
|
||||
const path = require('canonical-path');
|
||||
const Dgeni = require('dgeni');
|
||||
const dartPkg = require(path.resolve('.'));
|
||||
|
||||
const ANGULAR_IO_PROJECT_PATH = '../../..';
|
||||
const DOCS_PATH = path.join(ANGULAR_IO_PROJECT_PATH, 'public/docs');
|
||||
const apiDocPath = path.join(DOCS_PATH, 'dart/latest/api');
|
||||
|
||||
dartPkg.config(function (dartPkgConfigInfo) {
|
||||
dartPkgConfigInfo.ngIoDartApiDocPath = apiDocPath;
|
||||
dartPkgConfigInfo.ngDartDocPath = path.join(ANGULAR_IO_PROJECT_PATH, '../ngdart/doc/api');
|
||||
});
|
||||
|
||||
const dgeni = new Dgeni([dartPkg]);
|
||||
|
||||
dgeni.generate().catch(function (err) {
|
||||
console.log(err);
|
||||
console.log(err.stack);
|
||||
throw err;
|
||||
});
|
|
@ -0,0 +1,215 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('assert-plus');
|
||||
const cheerio = require('cheerio');
|
||||
const Encoder = require('node-html-encoder').Encoder;
|
||||
const fs = require('fs-extra');
|
||||
const path = require('canonical-path');
|
||||
|
||||
module.exports = function dabFactory(ngIoProjPath) {
|
||||
const encoder = new Encoder('entity');
|
||||
|
||||
// Get the functionality we need from the dgeni package by the same name.
|
||||
const dartApiBuilderDgeniProjPath = 'tools/api-builder/dart-package';
|
||||
const dab = require(path.resolve(ngIoProjPath, dartApiBuilderDgeniProjPath)).module;
|
||||
|
||||
const log = dab.logFactory[1]();
|
||||
const dartPkgConfigInfo = dab.dartPkgConfigInfo[1]();
|
||||
const preprocessDartDocData = dab.preprocessDartDocData[1](log, dartPkgConfigInfo);
|
||||
const loadDartDocDataProcessor = dab.loadDartDocDataProcessor[1](log, dartPkgConfigInfo, preprocessDartDocData);
|
||||
const apiListDataFileService = dab.apiListDataFileService[1](log, dartPkgConfigInfo);
|
||||
const Array_from = dab.arrayFromIterable[1];
|
||||
|
||||
// Load API data, then create and save 'api-list.json'.
|
||||
function loadApiDataAndSaveToApiListFile() {
|
||||
const docs = [];
|
||||
loadDartDocDataProcessor.$process(docs);
|
||||
log.debug('Number of Dart API entries loaded:', docs.length);
|
||||
var libMap = apiListDataFileService.createDataAndSaveToFile(docs);
|
||||
for (let name in libMap) {
|
||||
log.debug(' ', name, 'has', libMap[name].length, 'top-level entries');
|
||||
}
|
||||
return docs;
|
||||
}
|
||||
|
||||
// Create and save the container's '_data.json' file.
|
||||
function _createDirData(containerName, destDirPath, entries) {
|
||||
const entryNames = Array_from(entries.keys()).sort();
|
||||
const dataMap = Object.create(null);
|
||||
entryNames.map((n) => {
|
||||
const e = entries.get(n);
|
||||
assert.object(e, `entry named ${n}`);
|
||||
dataMap[path.basename(e.path, '.html')] = e;
|
||||
});
|
||||
const dataFilePath = path.resolve(destDirPath, '_data.json');
|
||||
fs.writeFile(dataFilePath, JSON.stringify(dataMap, null, 2));
|
||||
log.info(containerName, 'wrote', Object.keys(dataMap).length, 'entries to', dataFilePath);
|
||||
}
|
||||
|
||||
function _insertExampleFragments(enclosedByName, eltId, $, div) {
|
||||
const fragDir = path.join(dartPkgConfigInfo.ngIoDartApiDocPath, '../../../_fragments/_api');
|
||||
const exList = div.find('p:contains("{@example")');
|
||||
exList.each((i, elt) => {
|
||||
const text = $(elt).text();
|
||||
log.debug(`Found example: ${enclosedByName} ${eltId}`, text);
|
||||
const matches = text.match(/{@example\s+([^\s]+)(\s+region=[\'\"]?(\w+)[\'\"]?)?\s*}/);
|
||||
if (!matches) {
|
||||
log.warn(enclosedByName, eltId, 'has an invalidly formed @example tag:', text);
|
||||
return true;
|
||||
}
|
||||
const exRelPath = matches[1];
|
||||
const region = matches[3];
|
||||
|
||||
const dir = path.dirname(exRelPath)
|
||||
const extn = path.extname(exRelPath);
|
||||
const baseName = path.basename(exRelPath, extn);
|
||||
const fileNameNoExt = baseName + (region ? `-${region}` : '')
|
||||
const exFragPath = path.resolve(fragDir, dir, `${fileNameNoExt}${extn}.md`);
|
||||
if (!fs.existsSync(exFragPath)) {
|
||||
log.warn('Fragment not found:', exFragPath);
|
||||
return true;
|
||||
}
|
||||
$(elt).empty();
|
||||
const md = fs.readFileSync(exFragPath, 'utf8');
|
||||
const codeElt = _extractAndWrapInCodeTags(md);
|
||||
$(elt).html(codeElt);
|
||||
log.silly('Fragment code in html:', $(elt).html());
|
||||
});
|
||||
}
|
||||
|
||||
function _extractAndWrapInCodeTags(md) {
|
||||
const lines = md.split('\n');
|
||||
// Drop first and last lines that are the code markdown tripple ticks (and last \n):
|
||||
lines.shift(); lines.pop(); lines.pop();
|
||||
const code = lines.map((line) => encoder.htmlEncode(line)).join('\n');
|
||||
// TS uses format="linenums"; removing that for now.
|
||||
return `<code-example language="dart">${code}\n</code-example>`;
|
||||
}
|
||||
|
||||
function _createEntryJadeFile(e, destDirPath) {
|
||||
const htmlPagePath = path.resolve(dartPkgConfigInfo.ngDartDocPath, e.path);
|
||||
if (!fs.existsSync(htmlPagePath)) {
|
||||
log.warn('Entry', e.name, ': expected to find file but didn\'t', htmlPagePath);
|
||||
return;
|
||||
}
|
||||
const html = fs.readFileSync(htmlPagePath, 'utf8');
|
||||
log.debug('Reading (and then deleting)', html.length, 'chars from', htmlPagePath);
|
||||
const $ = cheerio.load(html);
|
||||
const div = $('div.body.container');
|
||||
$('div.sidebar-offcanvas-left').remove();
|
||||
const baseNameNoExtn = path.basename(e.path, '.html');
|
||||
_insertExampleFragments(e.enclosedByQualifiedName, baseNameNoExtn, $, div);
|
||||
|
||||
const outFileNoExtn = path.join(destDirPath, baseNameNoExtn);
|
||||
const depth = path.dirname(e.path).split('/').length;
|
||||
assert(depth === 1 || depth == 2, 'depth ' + depth);
|
||||
const jadeFilePath = path.resolve(outFileNoExtn + '.jade');
|
||||
const breadcrumbs = $('header > nav ol.breadcrumbs');
|
||||
fs.writeFileSync(jadeFilePath, apiEntryJadeTemplate(depth, breadcrumbs, div));
|
||||
// In case harp cached the .html version, remove it since it will be generated.
|
||||
try {
|
||||
fs.unlinkSync(path.resolve(outFileNoExtn + '.html'));
|
||||
} catch (err) {
|
||||
if (e.enclosedBy && e.enclosedBy.type === 'class' &&
|
||||
e.enclosedBy.name.toLowerCase() === e.name.toLowerCase()) {
|
||||
// Do nothing since this is a known bug with dartdoc:
|
||||
// https://github.com/dart-lang/dartdoc/issues/1196
|
||||
} else {
|
||||
console.error(err);
|
||||
console.error(`Output path: ${destDirPath}`);
|
||||
console.error(`Entity: ${e}`);
|
||||
console.error(err.stack);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
log.debug(' ', e.enclosedByQualifiedName, 'entry', e.name, 'wrote to ', jadeFilePath);
|
||||
}
|
||||
|
||||
function _createJadeFiles(containerName, destDirPath, entries) {
|
||||
let numApiPagesWritten = 0;
|
||||
for (let name of entries.keys()) {
|
||||
_createEntryJadeFile(entries.get(name), destDirPath);
|
||||
numApiPagesWritten++
|
||||
}
|
||||
log.info(containerName, 'created', numApiPagesWritten, 'Jade entry files.');
|
||||
return numApiPagesWritten;
|
||||
}
|
||||
|
||||
function createApiDataAndJadeFiles(docs) {
|
||||
let numApiPagesWritten = 0;
|
||||
let map = apiListDataFileService.containerToEntryMap;
|
||||
for (let name of map.keys()) {
|
||||
if (!name) continue; // skip package-level
|
||||
let destDirPath = path.resolve(dartPkgConfigInfo.ngIoDartApiDocPath, name);
|
||||
let entries;
|
||||
if (!fs.existsSync(destDirPath)) {
|
||||
log.error(`Dartdoc API folder not found:`, destDirPath);
|
||||
} else if ((entries = map.get(name)).size > 0) {
|
||||
_createDirData(name, destDirPath, entries);
|
||||
numApiPagesWritten += _createJadeFiles(name, destDirPath, entries);
|
||||
}
|
||||
}
|
||||
return numApiPagesWritten;
|
||||
}
|
||||
|
||||
const _self = {
|
||||
Array_from: Array_from,
|
||||
apiEntryJadeTemplate: apiEntryJadeTemplate,
|
||||
apiListDataFileService: apiListDataFileService,
|
||||
loadApiDataAndSaveToApiListFile: loadApiDataAndSaveToApiListFile,
|
||||
createApiDataAndJadeFiles: createApiDataAndJadeFiles,
|
||||
dartPkgConfigInfo: dartPkgConfigInfo,
|
||||
loadDartDocDataProcessor: loadDartDocDataProcessor,
|
||||
log: log,
|
||||
preprocessDartDocData: preprocessDartDocData,
|
||||
};
|
||||
Object.freeze(_self);
|
||||
return _self;
|
||||
};
|
||||
|
||||
function _indentedEltHtml($elt, i, filterFnOpt) {
|
||||
let lines = $elt.html().split('\n');
|
||||
if (filterFnOpt) lines = lines.filter(filterFnOpt);
|
||||
const indent = ' '.substring(0,i);
|
||||
return lines.map((line) => `${indent}| ${line}`).join('\n');
|
||||
}
|
||||
|
||||
function apiEntryJadeTemplate(baseHrefDepth, $breadcrumbs, $mainDiv) {
|
||||
const baseHref = path.join(...Array(baseHrefDepth).fill('..'));
|
||||
// TODO/investigate: for some reason $breadcrumbs.html() is missing the <ol></ol>. We add it back in the template below.
|
||||
const breadcrumbs = _indentedEltHtml($breadcrumbs, 6, (line) => !line.match(/^\s*$/));
|
||||
const mainDivHtml = _indentedEltHtml($mainDiv, 4);
|
||||
// WARNING: since the following is Jade, indentation is significant.
|
||||
const result = `
|
||||
extends ${baseHref}/../../../_layout-dart-api
|
||||
|
||||
include ${baseHref}/../_util-fns
|
||||
|
||||
block var-def
|
||||
//- FIXME: a CSS expert needs to figure out why the header CSS needs to be patched for Dart.
|
||||
//- This enables the patch:
|
||||
- var fixHeroCss = 1;
|
||||
|
||||
block head-extra
|
||||
// generated Dart API page template: head-extra
|
||||
//- <base> is required because all the links in dartdoc generated pages are "pseudo-absolute"
|
||||
base(href="${baseHref}")
|
||||
link(rel='stylesheet' href='https://fonts.googleapis.com/css?family=Source+Code+Pro|Roboto:500,400italic,300,400' type='text/css')
|
||||
link(rel="stylesheet" href="static-assets/prettify.css")
|
||||
link(rel="stylesheet" href="static-assets/css/bootstrap.min.css")
|
||||
link(rel="stylesheet" href="static-assets/styles.css")
|
||||
|
||||
block breadcrumbs
|
||||
// generated Dart API page template: breadcrumbs
|
||||
nav.dropdown
|
||||
ol.breadcrumbs.gt-separated.hidden-xs
|
||||
${breadcrumbs}
|
||||
|
||||
block main-content
|
||||
// generated Dart API page template: main-content: start
|
||||
div.dart-api-entry-main
|
||||
${mainDivHtml}
|
||||
// generated Dart API page template: main-content: end
|
||||
`;
|
||||
return result;
|
||||
}
|
Loading…
Reference in New Issue