diff --git a/config/jsdoc/info/conf.json b/config/jsdoc/info/conf.json index b40d1d7c43..702520f349 100644 --- a/config/jsdoc/info/conf.json +++ b/config/jsdoc/info/conf.json @@ -11,7 +11,6 @@ }, "plugins": [ "config/jsdoc/info/api-plugin", - "config/jsdoc/info/define-plugin", - "config/jsdoc/info/virtual-plugin" + "config/jsdoc/info/module-plugin" ] } diff --git a/config/jsdoc/info/define-plugin.js b/config/jsdoc/info/define-plugin.js deleted file mode 100644 index 78634499ba..0000000000 --- a/config/jsdoc/info/define-plugin.js +++ /dev/null @@ -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; - } - } - } - }); - -}; diff --git a/config/jsdoc/info/module-plugin.js b/config/jsdoc/info/module-plugin.js new file mode 100644 index 0000000000..91351c48c5 --- /dev/null +++ b/config/jsdoc/info/module-plugin.js @@ -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 + } + } + +}; diff --git a/config/jsdoc/info/publish.js b/config/jsdoc/info/publish.js index bc01c31f89..6c4fc780aa 100644 --- a/config/jsdoc/info/publish.js +++ b/config/jsdoc/info/publish.js @@ -1,6 +1,5 @@ /** - * @fileoverview Generates JSON output based on exportable symbols (those with - * an api tag) and boolean defines (with a define tag and a default value). + * @fileoverview Generates JSON output based on exportable symbols. */ const assert = require('assert'); const path = require('path'); @@ -22,7 +21,7 @@ exports.publish = function(data, opts) { return types; } - // get all doclets with the "api" property or define (excluding events) + // get all doclets with the "api" property const classes = {}; const docs = data( [ @@ -34,7 +33,7 @@ exports.publish = function(data, opts) { return true; } } - return (typeof this.api == 'boolean' || + return (this.kind === 'module' || typeof this.api == 'boolean' || 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 const symbols = []; - const defines = []; const typedefs = []; const externs = []; + const modules = []; let base = []; const augments = {}; const symbolsByName = {}; @@ -60,12 +59,11 @@ exports.publish = function(data, opts) { return include; }).forEach(function(doc) { const isExterns = (/[\\\/]externs$/).test(doc.meta.path); - if (doc.define) { - defines.push({ + if (doc.kind == 'module') { + modules.push({ name: doc.longname, - description: doc.description, - path: path.join(doc.meta.path, doc.meta.filename), - default: doc.define.default + exports: doc.exports || null, + path: path.join(doc.meta.path, doc.meta.filename) }); } else if (doc.kind == 'typedef' || doc.isEnum === true) { typedefs.push({ @@ -155,10 +153,10 @@ exports.publish = function(data, opts) { process.stdout.write( JSON.stringify({ symbols: symbols, - defines: defines, typedefs: typedefs, externs: externs, - base: base + base: base, + modules: modules }, null, 2)); }); diff --git a/config/jsdoc/info/virtual-plugin.js b/config/jsdoc/info/virtual-plugin.js deleted file mode 100644 index 9209724c27..0000000000 --- a/config/jsdoc/info/virtual-plugin.js +++ /dev/null @@ -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; - } - }); - -}; diff --git a/site/plugins/examples/gatsby-node.js b/site/plugins/examples/gatsby-node.js index 6fe017c848..fa5148f55f 100644 --- a/site/plugins/examples/gatsby-node.js +++ b/site/plugins/examples/gatsby-node.js @@ -4,7 +4,6 @@ const rollup = require('rollup'); const resolve = require('rollup-plugin-node-resolve'); const common = require('rollup-plugin-commonjs'); const fse = require('fs-extra'); -const promisify = require('util').promisify; const compileTemplate = require('string-template/compile'); let rollupCache; diff --git a/tasks/generate-info.js b/tasks/generate-info.js index 555d086403..cdefc642be 100644 --- a/tasks/generate-info.js +++ b/tasks/generate-info.js @@ -99,9 +99,6 @@ function parseOutput(output) { if (!Array.isArray(info.symbols)) { throw new Error('Expected symbols array: ' + output); } - if (!Array.isArray(info.defines)) { - throw new Error('Expected defines array: ' + output); - } return info; }