mirror of
https://github.com/processing/p5.js.git
synced 2025-12-08 17:36:35 +00:00
Decode the HTML entities in the descriptions of classes, methods and fields in the JSON file generated from the documentation.
326 lines
9.0 KiB
JavaScript
326 lines
9.0 KiB
JavaScript
const marked = require('marked');
|
|
const Entities = require('html-entities').AllHtmlEntities;
|
|
|
|
const DocumentedMethod = require('./documented-method');
|
|
|
|
function smokeTestMethods(data) {
|
|
data.classitems.forEach(function(classitem) {
|
|
if (classitem.itemtype === 'method') {
|
|
new DocumentedMethod(classitem);
|
|
|
|
if (
|
|
classitem.access !== 'private' &&
|
|
classitem.file.substr(0, 3) === 'src' &&
|
|
classitem.name &&
|
|
!classitem.example
|
|
) {
|
|
console.log(
|
|
classitem.file +
|
|
':' +
|
|
classitem.line +
|
|
': ' +
|
|
classitem.itemtype +
|
|
' ' +
|
|
classitem.class +
|
|
'.' +
|
|
classitem.name +
|
|
' missing example'
|
|
);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function cleanExamples(data) {
|
|
data.classitems.forEach(function(classitem) {
|
|
if (classitem.itemtype === 'method' && classitem.example) {
|
|
classitem.example = classitem.example.map(i =>
|
|
i.replace(/[^\n]*\/\/\s*prettier-ignore.*\r?\n/g, '')
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
function mergeOverloadedMethods(data) {
|
|
let methodsByFullName = {};
|
|
let paramsForOverloadedMethods = {};
|
|
|
|
let consts = (data.consts = {});
|
|
|
|
data.classitems = data.classitems.filter(function(classitem) {
|
|
if (classitem.access === 'private') {
|
|
return false;
|
|
}
|
|
|
|
const itemClass = data.classes[classitem.class];
|
|
if (!itemClass || itemClass.private) {
|
|
return false;
|
|
}
|
|
|
|
let methodConsts = {};
|
|
|
|
let fullName, method;
|
|
|
|
var assertEqual = function(a, b, msg) {
|
|
if (a !== b) {
|
|
throw new Error(
|
|
'for ' +
|
|
fullName +
|
|
'() defined in ' +
|
|
classitem.file +
|
|
':' +
|
|
classitem.line +
|
|
', ' +
|
|
msg +
|
|
' (' +
|
|
JSON.stringify(a) +
|
|
' !== ' +
|
|
JSON.stringify(b) +
|
|
')'
|
|
);
|
|
}
|
|
};
|
|
|
|
var extractConsts = function(param) {
|
|
if (!param.type) {
|
|
console.log(param);
|
|
}
|
|
if (param.type.split('|').indexOf('Constant') >= 0) {
|
|
let match;
|
|
if (classitem.name === 'endShape' && param.name === 'mode') {
|
|
match = 'CLOSE';
|
|
} else {
|
|
const constantRe = /either\s+(?:[A-Z0-9_]+\s*,?\s*(?:or)?\s*)+/g;
|
|
const execResult = constantRe.exec(param.description);
|
|
match = execResult && execResult[0];
|
|
if (!match) {
|
|
throw new Error(
|
|
classitem.file +
|
|
':' +
|
|
classitem.line +
|
|
', Constant-typed parameter ' +
|
|
fullName +
|
|
'(...' +
|
|
param.name +
|
|
'...) is missing valid value enumeration. ' +
|
|
'See inline_documentation.md#specify-parameters.'
|
|
);
|
|
}
|
|
}
|
|
if (match) {
|
|
const reConst = /[A-Z0-9_]+/g;
|
|
let matchConst;
|
|
while ((matchConst = reConst.exec(match)) !== null) {
|
|
methodConsts[matchConst] = true;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
var processOverloadedParams = function(params) {
|
|
let paramNames;
|
|
|
|
if (!(fullName in paramsForOverloadedMethods)) {
|
|
paramsForOverloadedMethods[fullName] = {};
|
|
}
|
|
|
|
paramNames = paramsForOverloadedMethods[fullName];
|
|
|
|
params.forEach(function(param) {
|
|
const origParam = paramNames[param.name];
|
|
|
|
if (origParam) {
|
|
assertEqual(
|
|
origParam.type,
|
|
param.type,
|
|
'types for param "' +
|
|
param.name +
|
|
'" must match ' +
|
|
'across all overloads'
|
|
);
|
|
assertEqual(
|
|
param.description,
|
|
'',
|
|
'description for param "' +
|
|
param.name +
|
|
'" should ' +
|
|
'only be defined in its first use; subsequent ' +
|
|
'overloads should leave it empty'
|
|
);
|
|
} else {
|
|
paramNames[param.name] = param;
|
|
extractConsts(param);
|
|
}
|
|
});
|
|
|
|
return params;
|
|
};
|
|
|
|
if (classitem.itemtype && classitem.itemtype === 'method') {
|
|
fullName = classitem.class + '.' + classitem.name;
|
|
if (fullName in methodsByFullName) {
|
|
// It's an overloaded version of a method that we've already
|
|
// indexed. We need to make sure that we don't list it multiple
|
|
// times in our index pages and such.
|
|
|
|
method = methodsByFullName[fullName];
|
|
|
|
assertEqual(
|
|
method.file,
|
|
classitem.file,
|
|
'all overloads must be defined in the same file'
|
|
);
|
|
assertEqual(
|
|
method.module,
|
|
classitem.module,
|
|
'all overloads must be defined in the same module'
|
|
);
|
|
assertEqual(
|
|
method.submodule,
|
|
classitem.submodule,
|
|
'all overloads must be defined in the same submodule'
|
|
);
|
|
assertEqual(
|
|
classitem.description || '',
|
|
'',
|
|
'additional overloads should have no description'
|
|
);
|
|
|
|
var makeOverload = function(method) {
|
|
const overload = {
|
|
line: method.line,
|
|
params: processOverloadedParams(method.params || [])
|
|
};
|
|
// TODO: the doc renderer assumes (incorrectly) that
|
|
// these are the same for all overrides
|
|
if (method.static) overload.static = method.static;
|
|
if (method.chainable) overload.chainable = method.chainable;
|
|
if (method.return) overload.return = method.return;
|
|
return overload;
|
|
};
|
|
|
|
if (!method.overloads) {
|
|
method.overloads = [makeOverload(method)];
|
|
delete method.params;
|
|
}
|
|
method.overloads.push(makeOverload(classitem));
|
|
return false;
|
|
} else {
|
|
if (classitem.params) {
|
|
classitem.params.forEach(function(param) {
|
|
extractConsts(param);
|
|
});
|
|
}
|
|
methodsByFullName[fullName] = classitem;
|
|
}
|
|
|
|
Object.keys(methodConsts).forEach(constName =>
|
|
(consts[constName] || (consts[constName] = [])).push(fullName)
|
|
);
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
|
|
// build a copy of data.json for the FES, restructured for object lookup on
|
|
// classitems and removing all the parts not needed by the FES
|
|
function buildParamDocs(docs) {
|
|
let newClassItems = {};
|
|
// the fields we need for the FES, discard everything else
|
|
let allowed = new Set(['name', 'class', 'module', 'params', 'overloads']);
|
|
for (let classitem of docs.classitems) {
|
|
if (classitem.name && classitem.class) {
|
|
for (let key in classitem) {
|
|
if (!allowed.has(key)) {
|
|
delete classitem[key];
|
|
}
|
|
}
|
|
if (classitem.hasOwnProperty('overloads')) {
|
|
for (let overload of classitem.overloads) {
|
|
// remove line number and return type
|
|
if (overload.line) {
|
|
delete overload.line;
|
|
}
|
|
|
|
if (overload.return) {
|
|
delete overload.return;
|
|
}
|
|
}
|
|
}
|
|
if (!newClassItems[classitem.class]) {
|
|
newClassItems[classitem.class] = {};
|
|
}
|
|
|
|
newClassItems[classitem.class][classitem.name] = classitem;
|
|
}
|
|
}
|
|
|
|
let fs = require('fs');
|
|
let path = require('path');
|
|
let out = fs.createWriteStream(
|
|
path.join(process.cwd(), 'docs', 'parameterData.json'),
|
|
{
|
|
flags: 'w',
|
|
mode: '0644'
|
|
}
|
|
);
|
|
out.write(JSON.stringify(newClassItems, null, 2));
|
|
out.end();
|
|
}
|
|
|
|
function renderItemDescriptionsAsMarkdown(item) {
|
|
if (item.description) {
|
|
const entities = new Entities();
|
|
item.description = entities.decode(marked(item.description));
|
|
}
|
|
if (item.params) {
|
|
item.params.forEach(renderItemDescriptionsAsMarkdown);
|
|
}
|
|
}
|
|
|
|
function renderDescriptionsAsMarkdown(data) {
|
|
Object.keys(data.modules).forEach(function(moduleName) {
|
|
renderItemDescriptionsAsMarkdown(data.modules[moduleName]);
|
|
});
|
|
Object.keys(data.classes).forEach(function(className) {
|
|
renderItemDescriptionsAsMarkdown(data.classes[className]);
|
|
});
|
|
data.classitems.forEach(function(classitem) {
|
|
renderItemDescriptionsAsMarkdown(classitem);
|
|
});
|
|
}
|
|
|
|
module.exports = (data, options) => {
|
|
data.classitems
|
|
.filter(
|
|
ci => !ci.itemtype && (ci.params || ci.return) && ci.access !== 'private'
|
|
)
|
|
.forEach(ci => {
|
|
console.error(ci.file + ':' + ci.line + ': unnamed public member');
|
|
});
|
|
|
|
Object.keys(data.classes)
|
|
.filter(k => data.classes[k].access === 'private')
|
|
.forEach(k => delete data.classes[k]);
|
|
|
|
renderDescriptionsAsMarkdown(data);
|
|
mergeOverloadedMethods(data);
|
|
smokeTestMethods(data);
|
|
cleanExamples(data);
|
|
buildParamDocs(JSON.parse(JSON.stringify(data)));
|
|
};
|
|
|
|
module.exports.mergeOverloadedMethods = mergeOverloadedMethods;
|
|
module.exports.renderDescriptionsAsMarkdown = renderDescriptionsAsMarkdown;
|
|
|
|
module.exports.register = (Handlebars, options) => {
|
|
Handlebars.registerHelper('root', function(context, options) {
|
|
// if (this.language === 'en') {
|
|
// return '';
|
|
// } else {
|
|
// return '/'+this.language;
|
|
// }
|
|
return window.location.pathname;
|
|
});
|
|
};
|