c9-core/plugins/c9.ide.language.javascript..../scrape/nodemanual.js

250 wiersze
7.8 KiB
JavaScript

var fs = require('fs');
var marked = require('marked');
var util = require("./util");
var patch = require("./patch.js");
var addLinkTargets = util.addLinkTargets;
var stripHtml = util.stripHtml;
var nodeJSON = JSON.parse(fs.readFileSync(__dirname + "/nodemanual.input.jst", 'UTF-8'));
var builtins = JSON.parse(fs.readFileSync(__dirname + "/../builtin.jst", 'UTF-8'));
var VERSION = "latest";
var URL_PREFIX = "http://nodemanual.org/" + VERSION + "/nodejs_ref_guide/";
var GUID_PREFIX = "nodejs_" + VERSION + ":";
var KIND_PACKAGE = "package";
var KIND_HIDDEN = "hidden";
var KIND_DEFAULT = undefined;
var LINK_TARGET = "c9doc";
marked.setOptions({
gfm: true,
pedantic: false,
sanitize: false,
/* callback for code highlighter
highlight: function(code, lang) {
if (lang === 'js') {
return javascriptHighlighter(code);
}
return code;
}
*/
});
function Container(id) {
this.guid = toC9TypeName(id);
this.properties = {
_prototype: [{
guid: this.guid + "/prototype",
properties: {}
}]
};
this.kind = this.guid.match("/") ? KIND_HIDDEN : KIND_PACKAGE;
console.log(this.guid);
}
Container.prototype.add = function(id, property) {
var props;
if (isStatic(property, this)) {
props = this.properties;
property.guid = this.guid + "/" + property.guid;
// console.log(this.guid + ":" + id);
} else {
props = this.properties._prototype[0].properties;
property.guid = this.guid + "/prototype/" + property.guid;
// console.log(this.guid + "." + id);
}
var targetProps = props["_" + id] = props["_" + id] || [];
targetProps.push(property);
};
function Property(parent, id) {
this.guid = id;
this.properties = {};
}
function extractRoot(root) {
var results = {};
/* Preprocess classes to recognize globals
for (var i = 0; i < root.tree.children.length; i++) {
var child = root.tree.children[i];
if (child.type === "class")
extractItem(child, results);
}
*/
// Process all children
for (var i = 0; i < root.tree.children.length; i++) {
var child = root.tree.children[i];
extractItem(child, results);
}
return results;
}
function extractItem(item, results) {
if (item.id === "Modules") // HACK: Modules is not a class
return;
if (item.type === "class method" || item.type === "class property")
extractProperty(item, results);
else if (item.type === "class")
extractContainer(item, results);
else if (item.type === "constructor")
extractConstructor(item, results);
else if (item.type === "event")
extractEvent(item, results);
else
throw new Error("Unknown item type: " + item.type);
}
function extractContainer(item, results) {
var matcher = item.id.match(/^(.*)\.(.*?)$/);
if (matcher) {
var parentId = matcher[1];
var parentClass = results[toC9TypeName(parentId)];
if (!parentClass)
parentClass = results[toC9TypeName(parentId)] = new Container(parentId);
}
var result = results[toC9TypeName(item.id)] || new Container(item.id);
results[toC9TypeName(item.id)] = result;
// TODO: don't copy doc from class to _prototype??
result.docUrl = result.properties._prototype.docUrl = extractDocUrl(item);
result.doc = result.properties._prototype.doc = extractDoc(item);
if (result.kind === KIND_PACKAGE && (isClassNotPackage(item) || matcher)) {
result.kind = KIND_DEFAULT;
// TODO: prototype chain
}
for (var i = 0; i < item.children.length; i++) {
extractItem(item.children[i], results);
}
}
function isClassNotPackage(item) {
if (item.metadata) {
if (item.metadata.indexOf)
item.metadata = JSON.parse(item.metadata);
if (item.metadata.type === "global")
return true;
}
// HACK: assume if something is a constructor, it is a class with instance properties
for (var i = 0; i < item.children.length; i++) {
if (item.children[i].type === "constructor")
return true;
}
return false;
}
function isStatic(property, container) {
// HACK: we don't have metadata for this yet
return container.kind === "package" ||
property.guid.match(/^[A-Z]/) || container.guid.match("(/|:)[a-z][^/]*$");
}
function extractProperty(item, results, isConstructor) {
var matcher = item.id.match(/^(.*)\.(.*?)$/);
var parentId = matcher[1];
var childId = matcher[2];
var parentClass = results[toC9TypeName(parentId)];
if (!parentClass)
parentClass = results[toC9TypeName(parentId)] = new Container(parentId);
var signatures = item.signatures || [{}];
for (var i = 0; i < signatures.length; i++) {
var signature = signatures[i];
var result = new Property(parentClass, childId + "[" + i + "]");
result.docUrl = extractDoc(signature) || extractDocUrl(item);
result.doc = extractDoc(item) || extractDoc(item);
extractSignature(item, signature, result);
if (isConstructor)
extractSignature(item, signature, parentClass);
parentClass.add(childId, result);
}
}
function extractSignature(item, signature, result) {
var returns = extractType(signature.returns ? signature.returns[0].type : null, true);
if (item.type === "class property") {
result.properties.___proto__ = returns;
return;
}
result.properties._return = returns;
result.fargs = [];
result.properties.___proto__ = result.properties.___proto__ || [];
result.properties.___proto__.push("es5:Function/prototype");
if (!signature.args)
return;
extractSignatureArgs(signature, result.fargs);
}
function extractSignatureArgs(signature, results) {
for (var i = 0; i < signature.args.length; i++) {
// TODO: store callback signatures
// for example, see readline.interface.question
// TODO: store function sig like { name: 'callback', args: [], optional: true }
var arg = signature.args[i];
var argFargs = arg.args && extractSignatureArgs(arg, []);
results.push({
id: arg.name,
type: extractType(arg.type, true),
doc: arg.description,
opt: arg.optional,
fargs: argFargs
});
}
return results;
}
function extractConstructor(item, results) {
item.id = item.id.replace(/^new (.*)/, "$1") + ".constructor";
extractProperty(item, results, true);
}
function extractEvent(item, results) {
// TODO: extract events?
}
function extractType(type) {
if (type) {
if (builtins["es5:" + type])
return ["es5:" + type + "/prototype"];
else if (type instanceof Array)
return type.map(extractType);
else
return [toC9TypeName(type) + "/prototype"];
}
else {
return ["es5:Object/prototype"];
}
}
function toC9TypeName(typename) {
return GUID_PREFIX + typename.replace(/\./g, "/");
}
function extractDocUrl(item) {
// TODO: why do urls like http://nodemanual.org/latest/nodejs_ref_guide/debugger.html#L145 not exist?
return URL_PREFIX + item.resultingFile;
}
function extractDoc(item) {
if (!item.short_description || item.short_description.match(/^Stability:[^\\]*(\\n[^\\]*)?/))
return null;
// TODO: support (some) documentation links?
var result = marked(item.short_description);
result = result.replace(/\[\[[^ \]]+\s+([^\]]+)\]\]/gm, "$1");
result = result.replace(/\[([^\]]+)\]\([^\)]+\)/gm, "$1");
result = addLinkTargets(result, LINK_TARGET);
return result;
}
var allResults = extractRoot(nodeJSON);
allResults = patch.patchNodemanual(allResults);
fs.writeFileSync(__dirname + "/../builtin.nodejs.jst", JSON.stringify(allResults, null, 2), "UTF-8");