@qooxdoo/framework
Version:
The JS Framework for Coders
190 lines (177 loc) • 5.6 kB
JavaScript
/* ************************************************************************
*
* qooxdoo-compiler - node.js based replacement for the Qooxdoo python
* toolchain
*
* https://github.com/qooxdoo/qooxdoo
*
* Copyright:
* 2011-2017 Zenesis Limited, http://www.zenesis.com
*
* License:
* MIT: https://opensource.org/licenses/MIT
*
* This software is provided under the same licensing terms as Qooxdoo,
* please see the LICENSE file in the Qooxdoo project's top-level directory
* for details.
*
* Authors:
* * John Spackman (john.spackman@zenesis.com, @johnspackman)
*
* *********************************************************************** */
const showdown = require("showdown");
/**
* JSDoc Parser
*/
qx.Class.define("qx.tool.compiler.jsdoc.Parser", {
extend: qx.core.Object,
statics: {
/**
* Parses a JSDoc comment, returns an object where the keys are the name of the JSDoc (eg "@description")
* and the value are an array of objects, one for each entry that was found. The individual entries
* consist of the name, the body of the JSDoc entry, and optional, key-specific parsed data (where supported)
*/
parseComment(comment) {
/*
JSDoc starts with /**
babel parses this to * - the comment starting end ending is striped
So whe have to test for valid JSDoc comment.
*/
comment = comment.trim();
if (
!(
comment.startsWith("* ") ||
comment.startsWith("*\n") ||
comment.startsWith("*\r")
)
) {
return {};
}
var current = { name: "@description", body: "" };
var cmds = [current];
// special handling for code section
comment = comment.replace(/`([^`]*)`/gm, "<code>$1</code>");
// Strip optional leading *
comment = comment.replace(/^[ \t]*\*/gm, "");
// special handling for as markdown lists - * in qooxdoo
comment = comment.replace(/^\s*\*/gm, "*");
comment = comment.replace(/^\s*\*\*\*\*/gm, "\t\t\t*");
comment = comment.replace(/^\s*\*\*\*/gm, "\t\t*");
comment = comment.replace(/^\s*\*\*/gm, "\t*");
comment = comment.split("\n");
comment.forEach(function (line) {
line = line.trimRight();
if (!line) {
return;
}
// Strip trailing single line comment
let m = line.match(/(^.*)([^:]\/\/.*)$/);
if (m) {
line = m[1].trimRight();
}
// Look for command at the beginning of the line
m = line.match(/^\s*(\@[a-zA-Z0-9_]+)(.*)$/);
if (!m) {
// Clean starting * as markdown lists
if (current.body.length) {
current.body += "\n";
}
current.body += line;
return;
}
var name = m[1];
var body = m[2];
// Patch common command names
if (name == "@returns") {
name = "@return";
}
if (name == "@throw") {
name = "@throws";
}
// store it
current = { name: name, body: body };
cmds.push(current);
});
var result = {};
let converter = new showdown.Converter();
cmds.forEach(function (cmd) {
if (cmd.name === "@description") {
try {
cmd.body = converter.makeHtml(cmd.body);
} catch (e) {
if (qx.tool.compiler.Console.getInstance().isVerbose()) {
qx.tool.compiler.Console.info(`
Markdown conversion problem:
Error "${e.message}" was thrown parsing
"${cmd.body.trim()}".
Please review your doc comments for compatibility with Markdown syntax.
`);
}
}
} else {
// If the body is surrounded by parameters, remove them
let m = cmd.body.match(/^\s*\(([\s\S]*)\)\s*$/m);
if (m) {
cmd.body = m[1];
}
cmd.body = cmd.body.trim();
}
if (result[cmd.name]) {
result[cmd.name].push(cmd);
} else {
result[cmd.name] = [cmd];
}
});
return result;
},
/**
*
* @param {*} jsdoc POJO
*/
parseJsDoc(jsdoc, typeResolver) {
for (var key in jsdoc) {
var parser = this.__PARSERS[key];
if (parser) {
jsdoc[key].forEach(pdoc => parser.parseCommand(pdoc, typeResolver));
}
}
},
__PARSERS: {
"@param": new qx.tool.compiler.jsdoc.ParamParser(),
"@return": new qx.tool.compiler.jsdoc.ReturnParser(),
"@throws": new qx.tool.compiler.jsdoc.ThrowsParser(),
"@throw": new qx.tool.compiler.jsdoc.ThrowsParser(),
"@childControl": new qx.tool.compiler.jsdoc.ChildControlParser()
},
getTypeExpression(text) {
const chars = text.split("");
let open = 0;
let firstInclude;
let firstExclude;
for (let idx = 0; idx < chars.length; idx++) {
const current = chars[idx];
if (current == "{") {
open++;
if (!firstInclude) {
firstInclude = idx + 1;
}
}
if (current == "}") {
open--;
if (open === 0 && !firstExclude) {
firstExclude = idx;
break;
}
}
}
if (!firstInclude || !firstExclude) {
return null;
}
return {
expr: text.slice(firstInclude, firstExclude),
start: firstInclude,
end: firstExclude
};
}
}
});