chore(dart): drop api and cheatsheet scripts and gulp tasks (#2970)
* chore(dart): drop the tools/dart-api-builder * chore(gulp): remove Dart cheatsheet and api tasks * chore(tools): remove tools/api-builder/dart-package * chore(gulp): drop pub task
This commit is contained in:
parent
0bdda1d358
commit
5ea668c1da
119
gulpfile.js
119
gulpfile.js
@ -77,15 +77,6 @@ var _apiShredOptions = {
|
|||||||
logLevel: _dgeniLogLevel
|
logLevel: _dgeniLogLevel
|
||||||
};
|
};
|
||||||
|
|
||||||
const relDartDocApiDir = path.join('doc', 'api');
|
|
||||||
var _apiShredOptionsForDart = {
|
|
||||||
lang: 'dart',
|
|
||||||
examplesDir: path.resolve(ANGULAR_PROJECT_PATH + '2_api_examples'),
|
|
||||||
fragmentsDir: path.join(DOCS_PATH, '_fragments/_api'),
|
|
||||||
zipDir: path.join(RESOURCES_PATH, 'zips/api'),
|
|
||||||
logLevel: _dgeniLogLevel
|
|
||||||
};
|
|
||||||
|
|
||||||
var _excludePatterns = ['**/node_modules/**', '**/packages/**'];
|
var _excludePatterns = ['**/node_modules/**', '**/packages/**'];
|
||||||
|
|
||||||
var _excludeMatchers = _excludePatterns.map(function(excludePattern){
|
var _excludeMatchers = _excludePatterns.map(function(excludePattern){
|
||||||
@ -624,8 +615,7 @@ gulp.task('build-docs', ['build-devguide-docs', 'build-api-docs', 'build-plunker
|
|||||||
// Stop zipping examples Feb 28, 2016
|
// Stop zipping examples Feb 28, 2016
|
||||||
//gulp.task('build-docs', ['build-devguide-docs', 'build-api-docs', 'build-plunkers', '_zip-examples']);
|
//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']
|
gulp.task('build-api-docs', ['build-js-api-docs', 'build-ts-api-docs']);
|
||||||
.concat(buildDartApiDocs ? ['build-dart-api-docs', 'build-dart-cheatsheet'] : []));
|
|
||||||
|
|
||||||
gulp.task('build-devguide-docs', ['_shred-devguide-examples', '_shred-devguide-shared-jade'], function() {
|
gulp.task('build-devguide-docs', ['_shred-devguide-examples', '_shred-devguide-shared-jade'], function() {
|
||||||
return buildShredMaps(true);
|
return buildShredMaps(true);
|
||||||
@ -639,50 +629,12 @@ gulp.task('build-js-api-docs', ['_shred-api-examples'], function() {
|
|||||||
return buildApiDocs('js');
|
return buildApiDocs('js');
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('build-dart-api-docs', ['_shred-api-examples', 'dartdoc'], function() {
|
|
||||||
return buildApiDocsForDart();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Using the --build flag will use systemjs.config.web.build.js (for preview builds)
|
// Using the --build flag will use systemjs.config.web.build.js (for preview builds)
|
||||||
gulp.task('build-plunkers', ['_copy-example-boilerplate'], function() {
|
gulp.task('build-plunkers', ['_copy-example-boilerplate'], function() {
|
||||||
regularPlunker.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log, build: argv.build });
|
regularPlunker.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log, build: argv.build });
|
||||||
return embeddedPlunker.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log, build: argv.build, targetSelf: argv.targetSelf });
|
return embeddedPlunker.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log, build: argv.build, targetSelf: argv.targetSelf });
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('build-dart-cheatsheet', [], function() {
|
|
||||||
return buildDartCheatsheet();
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('dartdoc', ['pub upgrade'], function() {
|
|
||||||
const ngRepoPath = ngPathFor('dart');
|
|
||||||
if (argv.fast && fs.existsSync(path.resolve(ngRepoPath, relDartDocApiDir))) {
|
|
||||||
gutil.log(`Skipping dartdoc: --fast flag enabled and api dir exists (${relDartDocApiDir})`);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
checkAngularProjectPath(ngRepoPath);
|
|
||||||
const topLevelLibFilePath = path.resolve(ngRepoPath, 'lib', 'angular2.dart');
|
|
||||||
const tmpPath = topLevelLibFilePath + '.disabled';
|
|
||||||
renameIfExistsSync(topLevelLibFilePath, tmpPath);
|
|
||||||
gutil.log(`Hiding top-level angular2 library: ${topLevelLibFilePath}`);
|
|
||||||
// Remove dartdoc '--add-crossdart' flag while we are fixing links to API pages.
|
|
||||||
const dartdoc = spawnExt('dartdoc', ['--output', relDartDocApiDir], { cwd: ngRepoPath});
|
|
||||||
return dartdoc.promise.finally(() => {
|
|
||||||
gutil.log(`Restoring top-level angular2 library: ${topLevelLibFilePath}`);
|
|
||||||
renameIfExistsSync(tmpPath, topLevelLibFilePath);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
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(){
|
gulp.task('git-changed-examples', ['_shred-devguide-examples'], function(){
|
||||||
var after, sha, messageSuffix;
|
var after, sha, messageSuffix;
|
||||||
if (argv.after) {
|
if (argv.after) {
|
||||||
@ -863,8 +815,7 @@ gulp.task('_shred-api-examples', ['_shred-clean-api'], function() {
|
|||||||
langs.forEach(lang => {
|
langs.forEach(lang => {
|
||||||
if (lang === 'js') return; // JS is handled via TS.
|
if (lang === 'js') return; // JS is handled via TS.
|
||||||
checkAngularProjectPath(ngPathFor(lang));
|
checkAngularProjectPath(ngPathFor(lang));
|
||||||
const options = lang == 'dart' ? _apiShredOptionsForDart : _apiShredOptions;
|
promises.push(docShredder.shred(_apiShredOptions));
|
||||||
promises.push(docShredder.shred(options));
|
|
||||||
});
|
});
|
||||||
return Q.all(promises);
|
return Q.all(promises);
|
||||||
});
|
});
|
||||||
@ -1347,72 +1298,6 @@ function buildApiDocs(targetLanguage) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function buildDartCheatsheet() {
|
|
||||||
'use strict';
|
|
||||||
const ALLOWED_LANGUAGES = ['ts', 'js', 'dart'];
|
|
||||||
const lang = 'dart';
|
|
||||||
const vers = 'latest';
|
|
||||||
checkAngularProjectPath(ngPathFor(lang));
|
|
||||||
try {
|
|
||||||
const pkg = new Package('dartApiDocs', [require(path.resolve(TOOLS_PATH, 'dart-api-builder'))]);
|
|
||||||
pkg.config(function(log, targetEnvironments, writeFilesProcessor) {
|
|
||||||
log.level = _dgeniLogLevel;
|
|
||||||
ALLOWED_LANGUAGES.forEach(function(target) { targetEnvironments.addAllowed(target); });
|
|
||||||
targetEnvironments.activate(lang);
|
|
||||||
const outputPath = path.join(lang, vers, 'can-be-any-name-read-comment-below');
|
|
||||||
// Note: cheatsheet data gets written to: outputPath + '/../guide';
|
|
||||||
writeFilesProcessor.outputFolder = outputPath;
|
|
||||||
});
|
|
||||||
var dgeni = new Dgeni([pkg]);
|
|
||||||
return dgeni.generate();
|
|
||||||
} catch(err) {
|
|
||||||
console.error(err);
|
|
||||||
console.error(err.stack);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function buildApiDocsForDart() {
|
|
||||||
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, 'api');
|
|
||||||
dabInfo.ngDartDocPath = path.join(ngPathFor('dart'), relDartDocApiDir);
|
|
||||||
// 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(ngPathFor('dart'));
|
|
||||||
var destPath = dabInfo.ngIoDartApiDocPath;
|
|
||||||
var sourceDirs = fs.readdirSync(dabInfo.ngDartDocPath)
|
|
||||||
.filter(name => !name.match(/^index|^(?!angular2)|testing|codegen/))
|
|
||||||
.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.error(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch(err) {
|
|
||||||
console.error(err);
|
|
||||||
console.error(err.stack);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildShredMaps(shouldWrite) {
|
function buildShredMaps(shouldWrite) {
|
||||||
var options = {
|
var options = {
|
||||||
devguideExamplesDir: _devguideShredOptions.examplesDir,
|
devguideExamplesDir: _devguideShredOptions.examplesDir,
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
'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'))
|
|
||||||
;
|
|
@ -1,21 +0,0 @@
|
|||||||
'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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,45 +0,0 @@
|
|||||||
'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();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,76 +0,0 @@
|
|||||||
'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);
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = function arrayFromIterable(iterable) {
|
|
||||||
const arr = [];
|
|
||||||
for (let e of iterable) arr.push(e);
|
|
||||||
return arr;
|
|
||||||
};
|
|
@ -1,13 +0,0 @@
|
|||||||
'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;
|
|
||||||
};
|
|
@ -1,8 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = function logFactory() {
|
|
||||||
var winston = require('winston');
|
|
||||||
winston.cli();
|
|
||||||
winston.level = 'info';
|
|
||||||
return winston;
|
|
||||||
};
|
|
@ -1,81 +0,0 @@
|
|||||||
'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;
|
|
||||||
};
|
|
@ -1,26 +0,0 @@
|
|||||||
'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, '../angular-dart/doc/api');
|
|
||||||
});
|
|
||||||
|
|
||||||
const dgeni = new Dgeni([dartPkg]);
|
|
||||||
|
|
||||||
dgeni.generate().catch(function (err) {
|
|
||||||
console.log(err);
|
|
||||||
console.log(err.stack);
|
|
||||||
throw err;
|
|
||||||
});
|
|
@ -1,247 +0,0 @@
|
|||||||
'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 _adjustDocsRelativeLinks($, div) {
|
|
||||||
// Omit leading https://angular.io so links work for local test sites.
|
|
||||||
const urlToDocs = '/docs/dart/latest/';
|
|
||||||
const urlToExamples = 'http://angular-examples.github.io/';
|
|
||||||
const docsLinkList = div.find('a[href^="docs/"],a[href^="examples/"]');
|
|
||||||
docsLinkList.each((i, elt) => {
|
|
||||||
const href = $(elt).attr('href');
|
|
||||||
const matches = href.match(/(\w+)\/(.*)$/);
|
|
||||||
// TODO: support links to chapters of other languages, e.g., 'docs/ts/latest/...'.
|
|
||||||
const urlStart = matches[1] === 'docs' ? urlToDocs : urlToExamples;
|
|
||||||
const absHref = urlStart + matches[2];
|
|
||||||
log.info(`Found angular.io relative link: ${href} --> ${absHref}`);
|
|
||||||
$(elt).attr('href', absHref);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _insertExampleFragments(enclosedByName, eltId, $, div) {
|
|
||||||
const fragDirBase = path.join(dartPkgConfigInfo.ngIoDartApiDocPath, '../../../_fragments/');
|
|
||||||
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(/^\s*{@example\s+([^\s]+)(\s+region=[\'\"]?([-\w]+)[\'\"]?)?\s*}([\s\S]*)$/);
|
|
||||||
if (!matches) {
|
|
||||||
log.warn(enclosedByName, eltId, 'has an invalidly formed @example tag:', text);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// const [, exRelPath, /*regionTagAndValue*/, region, rest] = matches;
|
|
||||||
const rest = matches[4].trim();
|
|
||||||
if (rest) log.warn(enclosedByName, eltId, '@example must be the only element in a paragraph, but found:', text);
|
|
||||||
const exRelPath = matches[1];
|
|
||||||
const region = matches[3];
|
|
||||||
|
|
||||||
let exRelPathParts = path.dirname(exRelPath).split(path.sep);
|
|
||||||
let fragDir;
|
|
||||||
if (exRelPathParts[0] === 'docs') {
|
|
||||||
// Path is to a docs example, not an API example.
|
|
||||||
const exampleName = exRelPathParts[1];
|
|
||||||
fragDir = path.join(fragDirBase, exampleName, 'dart');
|
|
||||||
exRelPathParts = exRelPathParts.slice(2);
|
|
||||||
} else {
|
|
||||||
fragDir = path.join(fragDirBase, '_api');
|
|
||||||
}
|
|
||||||
const extn = path.extname(exRelPath);
|
|
||||||
const baseName = path.basename(exRelPath, extn);
|
|
||||||
const fileNameNoExt = baseName + (region ? `-${region}` : '')
|
|
||||||
const exFragPath = path.resolve(fragDir, ...exRelPathParts, `${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();
|
|
||||||
while (lines && lines.pop().trim() !== '```') {}
|
|
||||||
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');
|
|
||||||
_adjustDocsRelativeLinks($, div);
|
|
||||||
_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 _adjustAnchorHref($, $elt, hrefPathPrefix) {
|
|
||||||
if (!hrefPathPrefix) return;
|
|
||||||
$elt.find('a[href]').each((i, e) => {
|
|
||||||
let href = $(e).attr('href')
|
|
||||||
// Do nothing to absolute or external links
|
|
||||||
if (href.match(/^\/|^[a-z]+:/)) return;
|
|
||||||
$(e).attr('href', `${hrefPathPrefix}/${href}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
_adjustAnchorHref($, $breadcrumbs, baseHref);
|
|
||||||
const breadcrumbs = _indentedEltHtml($breadcrumbs, 6, (line) => !line.match(/^\s*$/));
|
|
||||||
_adjustAnchorHref($, $mainDiv, baseHref);
|
|
||||||
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 head-extra
|
|
||||||
// generated Dart API page template: head-extra
|
|
||||||
//- <base> is no longer required
|
|
||||||
//- base(href="${baseHref}")
|
|
||||||
|
|
||||||
block breadcrumbs
|
|
||||||
// generated Dart API page template: breadcrumbs
|
|
||||||
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;
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
var fs = require('fs');
|
|
||||||
var path = require('canonical-path');
|
|
||||||
var Package = require('dgeni').Package;
|
|
||||||
var basePackage = require('../api-builder/docs-package');
|
|
||||||
var targetPackage = require('../api-builder/target-package');
|
|
||||||
var cheatsheetPackage = require('../api-builder/cheatsheet-package');
|
|
||||||
|
|
||||||
var PROJECT_PATH = path.resolve(__dirname, "../..");
|
|
||||||
var PUBLIC_PATH = path.resolve(PROJECT_PATH, 'public');
|
|
||||||
var DOCS_PATH = path.resolve(PUBLIC_PATH, 'docs');
|
|
||||||
var ANGULAR_REPO_PATH = path.resolve(__dirname, '../../../angular-dart');
|
|
||||||
// The 'docs' folder is actually named 'doc' for angular2 Dart.
|
|
||||||
var ANGULAR_DOCS_PATH = path.resolve(ANGULAR_REPO_PATH, 'doc');
|
|
||||||
var NG_IO_PKG_PATH = path.resolve(__dirname, "../api-builder/angular.io-package");
|
|
||||||
|
|
||||||
function requireNgIoPkg(_path) { return require(path.resolve(NG_IO_PKG_PATH, _path)); }
|
|
||||||
|
|
||||||
module.exports = new Package('dart-api-and-cheatsheet-builder', [basePackage, targetPackage, cheatsheetPackage])
|
|
||||||
|
|
||||||
// overrides base packageInfo and returns the one for the Angular repo.
|
|
||||||
.factory(require('./services/packageInfo'))
|
|
||||||
|
|
||||||
// Configure rendering
|
|
||||||
.config(function (templateFinder, renderDocsProcessor) {
|
|
||||||
|
|
||||||
templateFinder.templateFolders
|
|
||||||
.unshift(path.resolve(NG_IO_PKG_PATH, 'templates'));
|
|
||||||
|
|
||||||
// helpers are made available to the nunjucks templates
|
|
||||||
renderDocsProcessor.helpers.relativePath = function (from, to) {
|
|
||||||
return path.relative(from, to);
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
||||||
.config(function (parseTagsProcessor, getInjectables) {
|
|
||||||
const tagDefs = requireNgIoPkg('./tag-defs');
|
|
||||||
parseTagsProcessor.tagDefinitions =
|
|
||||||
parseTagsProcessor.tagDefinitions.concat(getInjectables(tagDefs));
|
|
||||||
})
|
|
||||||
|
|
||||||
.config(function (readFilesProcessor) {
|
|
||||||
// confirm that the angular repo is actually there.
|
|
||||||
if (!fs.existsSync(ANGULAR_REPO_PATH)) {
|
|
||||||
throw new Error('dart-api-and-cheatsheet-builder task requires the angular2 repo to be at ' + ANGULAR_REPO_PATH);
|
|
||||||
}
|
|
||||||
readFilesProcessor.basePath = DOCS_PATH;
|
|
||||||
readFilesProcessor.sourceFiles = [{
|
|
||||||
basePath: ANGULAR_DOCS_PATH,
|
|
||||||
include: path.resolve(ANGULAR_DOCS_PATH, 'cheatsheet/*.md')
|
|
||||||
}];
|
|
||||||
})
|
|
||||||
|
|
||||||
.config(function (convertPrivateClassesToInterfacesProcessor,
|
|
||||||
createOverviewDump,
|
|
||||||
extractDecoratedClassesProcessor,
|
|
||||||
extractJSDocCommentsProcessor,
|
|
||||||
extractTitleFromGuides,
|
|
||||||
generateNavigationDoc,
|
|
||||||
mergeDecoratorDocs,
|
|
||||||
readTypeScriptModules
|
|
||||||
) {
|
|
||||||
// Clear out unwanted processors
|
|
||||||
createOverviewDump.$enabled = false;
|
|
||||||
convertPrivateClassesToInterfacesProcessor.$enabled = false;
|
|
||||||
extractDecoratedClassesProcessor.$enabled = false;
|
|
||||||
extractJSDocCommentsProcessor.$enabled = false;
|
|
||||||
extractTitleFromGuides.$enabled = false;
|
|
||||||
generateNavigationDoc.$enabled = false;
|
|
||||||
mergeDecoratorDocs.$enabled = false;
|
|
||||||
readTypeScriptModules.$enabled = false;
|
|
||||||
})
|
|
||||||
|
|
||||||
.config(function (computePathsProcessor) {
|
|
||||||
computePathsProcessor.pathTemplates.push({
|
|
||||||
docTypes: ['cheatsheet-data'],
|
|
||||||
pathTemplate: '../guide/cheatsheet.json',
|
|
||||||
outputPathTemplate: '${path}'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.config(function (getLinkInfo) {
|
|
||||||
getLinkInfo.relativeLinks = true;
|
|
||||||
})
|
|
||||||
|
|
||||||
.config(function (templateEngine, getInjectables) {
|
|
||||||
templateEngine.filters = templateEngine.filters.concat(getInjectables([
|
|
||||||
requireNgIoPkg('./rendering/trimBlankLines'),
|
|
||||||
requireNgIoPkg('./rendering/toId'),
|
|
||||||
requireNgIoPkg('./rendering/indentForMarkdown')
|
|
||||||
]));
|
|
||||||
})
|
|
||||||
|
|
||||||
;
|
|
@ -1,38 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('canonical-path');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load information about this project from the pubspec.yaml
|
|
||||||
* @return {Object} The package information
|
|
||||||
*/
|
|
||||||
module.exports = function packageInfo() {
|
|
||||||
const ngPath = '../angular-dart';
|
|
||||||
const angularPubspec = path.join(ngPath, 'pubspec.yaml');
|
|
||||||
const pubspec = fs.readFileSync(angularPubspec, 'UTF-8').split('\n');
|
|
||||||
|
|
||||||
const info = {
|
|
||||||
version: _get(pubspec, 'version'),
|
|
||||||
repository: {
|
|
||||||
type: 'git', //? 'pub' @ 'https://pub.dartlang.org/packages/angular2'
|
|
||||||
// Not sure `url has a user visible impact on the generated cheatsheet.
|
|
||||||
url: 'https://github.com/angular/angular.git',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Array.prototype.find doesn't seem to be working.
|
|
||||||
// console.error([1, 'a', 2].find((x) => x === 'a')); // --> -1
|
|
||||||
function _find(arr, test) {
|
|
||||||
for (let x of arr) {
|
|
||||||
// console.error(`Looking at: ${x}`);
|
|
||||||
if (test(x)) return x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _get(lines, tag) {
|
|
||||||
const line = _find(lines, (line) => line.startsWith(tag));
|
|
||||||
return line.match(/^\w+: (.*)/)[1] || 'unknown';
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user