p5.js/docs/preprocessor.js
Akshay Padte 330a916f6c fix validateParameters problems
1. Store argument types to increase speed a bit and to prevent
   repetive logging
2. Create new json file parameterData.json from data.json and
   use it for the FES instead so as to reduce library size
2020-05-19 06:03:21 +05:30

324 lines
8.9 KiB
JavaScript

const marked = require('marked');
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) {
item.description = 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;
});
};