Generate module info

This commit is contained in:
Tim Schaub
2018-05-13 16:49:23 -05:00
parent c5f2787284
commit fb7d8b7839
7 changed files with 146 additions and 69 deletions

View File

@@ -11,7 +11,6 @@
}, },
"plugins": [ "plugins": [
"config/jsdoc/info/api-plugin", "config/jsdoc/info/api-plugin",
"config/jsdoc/info/define-plugin", "config/jsdoc/info/module-plugin"
"config/jsdoc/info/virtual-plugin"
] ]
} }

View File

@@ -1,35 +0,0 @@
/**
* @fileoverview This plugin extracts info from boolean defines. This only
* handles boolean defines with the default value in the description. Default
* is assumed to be provided with something like "default is `true`" (case
* insensitive, with or without ticks).
*/
const DEFAULT_VALUE = /default\s+is\s+`?(true|false)`?/i;
/**
* Hook to define new tags.
* @param {Object} dictionary The tag dictionary.
*/
exports.defineTags = function(dictionary) {
dictionary.defineTag('define', {
canHaveType: true,
mustHaveValue: true,
onTagged: function(doclet, tag) {
const types = tag.value.type.names;
if (types.length === 1 && types[0] === 'boolean') {
const match = tag.value.description.match(DEFAULT_VALUE);
if (match) {
doclet.define = {
default: match[1] === 'true'
};
doclet.description = tag.value.description;
}
}
}
});
};

View File

@@ -0,0 +1,135 @@
const path = require('path');
const exportLookup = {};
const moduleLookup = {};
const MODULE_PATH = /^module:(.*)~(\w+)$/;
/**
* Add exports to modules.
*/
exports.handlers = {
symbolFound(event) {
const filename = event.filename;
const node = event.astnode;
let local, exported;
switch (node.type) {
case 'ExportDefaultDeclaration': {
exported = 'default';
switch (node.declaration.type) {
case 'Identifier': {
// export default foo;
local = node.declaration.name;
break;
}
case 'FunctionDeclaration': {
if (!node.declaration.id) {
// export default function() {}
local = '';
} else {
// export default function foo() {}
local = node.declaration.id.name;
}
break;
}
default: {
local = '';
}
}
break;
}
case 'ExportNamedDeclaration': {
if (!node.declaration) {
// export {foo}
// export {foo as bar}
// handled below in ExportSpecifier
return;
}
switch (node.declaration.type) {
case 'FunctionDeclaration': {
if (!node.declaration.id) {
throw new Error(`Expected function declaration to have an id in ${filename}`);
}
const name = node.declaration.id.name;
local = name;
exported = name;
break;
}
default: {
return;
}
}
break;
}
case 'ExportSpecifier': {
if (node.exported.type === 'Identifier') {
exported = node.exported.name;
if (node.local.type === 'Identifier') {
local = node.local.name;
if (node.parent.source) {
const resolved = path.resolve(path.dirname(filename), node.parent.source.value);
local = `module:${resolved}~${local}`;
}
} else {
local = '';
}
} else {
return;
}
break;
}
default: {
return;
}
}
if (!(filename in exportLookup)) {
exportLookup[filename] = {};
}
const exports = exportLookup[filename];
if (exports.hasOwnProperty(exported)) {
throw new Error(`Duplicate export {${local} as ${exported}} in ${filename}`);
}
exports[exported] = local;
},
newDoclet(event) {
const doclet = event.doclet;
if (doclet.kind === 'module') {
const filepath = path.join(doclet.meta.path, doclet.meta.filename);
if (filepath in moduleLookup) {
throw new Error(`Duplicate @module annotation in ${filepath}`);
}
moduleLookup[filepath] = doclet;
}
},
parseComplete(event) {
for (const filepath in moduleLookup) {
const doclet = moduleLookup[filepath];
const exports = exportLookup[filepath];
for (const exported in exports) {
const local = exports[exported];
const match = local.match(MODULE_PATH);
if (match) {
const filepath = match[1];
const mod = moduleLookup[filepath];
if (mod) {
exports[exported] = `module:${mod.name}~${match[2]}`;
}
}
}
doclet.exports = exports; // undefined if no exports
}
}
};

View File

@@ -1,6 +1,5 @@
/** /**
* @fileoverview Generates JSON output based on exportable symbols (those with * @fileoverview Generates JSON output based on exportable symbols.
* an api tag) and boolean defines (with a define tag and a default value).
*/ */
const assert = require('assert'); const assert = require('assert');
const path = require('path'); const path = require('path');
@@ -22,7 +21,7 @@ exports.publish = function(data, opts) {
return types; return types;
} }
// get all doclets with the "api" property or define (excluding events) // get all doclets with the "api" property
const classes = {}; const classes = {};
const docs = data( const docs = data(
[ [
@@ -34,7 +33,7 @@ exports.publish = function(data, opts) {
return true; return true;
} }
} }
return (typeof this.api == 'boolean' || return (this.kind === 'module' || typeof this.api == 'boolean' ||
this.meta && (/[\\\/]externs$/).test(this.meta.path)); this.meta && (/[\\\/]externs$/).test(this.meta.path));
} }
], ],
@@ -43,9 +42,9 @@ exports.publish = function(data, opts) {
// get symbols data, filter out those that are members of private classes // get symbols data, filter out those that are members of private classes
const symbols = []; const symbols = [];
const defines = [];
const typedefs = []; const typedefs = [];
const externs = []; const externs = [];
const modules = [];
let base = []; let base = [];
const augments = {}; const augments = {};
const symbolsByName = {}; const symbolsByName = {};
@@ -60,12 +59,11 @@ exports.publish = function(data, opts) {
return include; return include;
}).forEach(function(doc) { }).forEach(function(doc) {
const isExterns = (/[\\\/]externs$/).test(doc.meta.path); const isExterns = (/[\\\/]externs$/).test(doc.meta.path);
if (doc.define) { if (doc.kind == 'module') {
defines.push({ modules.push({
name: doc.longname, name: doc.longname,
description: doc.description, exports: doc.exports || null,
path: path.join(doc.meta.path, doc.meta.filename), path: path.join(doc.meta.path, doc.meta.filename)
default: doc.define.default
}); });
} else if (doc.kind == 'typedef' || doc.isEnum === true) { } else if (doc.kind == 'typedef' || doc.isEnum === true) {
typedefs.push({ typedefs.push({
@@ -155,10 +153,10 @@ exports.publish = function(data, opts) {
process.stdout.write( process.stdout.write(
JSON.stringify({ JSON.stringify({
symbols: symbols, symbols: symbols,
defines: defines,
typedefs: typedefs, typedefs: typedefs,
externs: externs, externs: externs,
base: base base: base,
modules: modules
}, null, 2)); }, null, 2));
}); });

View File

@@ -1,16 +0,0 @@
/**
* Handle the interface and abstract annotations.
* @param {Object} dictionary The tag dictionary.
*/
exports.defineTags = function(dictionary) {
const classTag = dictionary.lookUp('class');
dictionary.defineTag('interface', {
mustHaveValue: false,
onTagged: function(doclet, tag) {
classTag.onTagged.apply(this, arguments);
doclet.virtual = true;
}
});
};

View File

@@ -4,7 +4,6 @@ const rollup = require('rollup');
const resolve = require('rollup-plugin-node-resolve'); const resolve = require('rollup-plugin-node-resolve');
const common = require('rollup-plugin-commonjs'); const common = require('rollup-plugin-commonjs');
const fse = require('fs-extra'); const fse = require('fs-extra');
const promisify = require('util').promisify;
const compileTemplate = require('string-template/compile'); const compileTemplate = require('string-template/compile');
let rollupCache; let rollupCache;

View File

@@ -99,9 +99,6 @@ function parseOutput(output) {
if (!Array.isArray(info.symbols)) { if (!Array.isArray(info.symbols)) {
throw new Error('Expected symbols array: ' + output); throw new Error('Expected symbols array: ' + output);
} }
if (!Array.isArray(info.defines)) {
throw new Error('Expected defines array: ' + output);
}
return info; return info;
} }