188 lines
4.2 KiB
JavaScript
188 lines
4.2 KiB
JavaScript
const fs = require('fs');
|
|
const util = require('util');
|
|
const path = require('path');
|
|
|
|
const readFile = util.promisify(fs.readFile);
|
|
const writeFile = util.promisify(fs.writeFile);
|
|
const externs = path.resolve(__dirname, '../externs');
|
|
|
|
function parse(source) {
|
|
const length = source.length;
|
|
|
|
let index = 0;
|
|
const IN_CODE = 'in code';
|
|
const IN_COMMENT = 'in comment';
|
|
|
|
function next() {
|
|
const char = source.charAt(index);
|
|
++index;
|
|
return char;
|
|
}
|
|
|
|
function scanLine() {
|
|
let char = next();
|
|
let line = '';
|
|
while (index < length && char !== '\n') {
|
|
line += char;
|
|
char = next();
|
|
}
|
|
return line;
|
|
}
|
|
|
|
let state = IN_CODE;
|
|
const blocks = [];
|
|
let comment;
|
|
while (index < length) {
|
|
const line = scanLine();
|
|
if (state === IN_CODE) {
|
|
if (line === '/**') {
|
|
state = IN_COMMENT;
|
|
comment = '';
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// in a comment
|
|
if (!line) {
|
|
continue;
|
|
}
|
|
|
|
if (line !== ' */') {
|
|
comment += line + '\n';
|
|
continue;
|
|
}
|
|
|
|
// comment is done
|
|
const code = scanLine();
|
|
if (code.startsWith('olx')) {
|
|
blocks.push({code, comment});
|
|
}
|
|
state = IN_CODE;
|
|
}
|
|
return blocks;
|
|
}
|
|
|
|
function processParam(name, comment) {
|
|
const IN_DESCRIPTION = 'in description';
|
|
const IN_TYPE = 'in type';
|
|
const DONE = 'done';
|
|
|
|
const lines = comment.split('\n');
|
|
lines.pop(); // ends with newline
|
|
let description = '';
|
|
let type;
|
|
let state = IN_DESCRIPTION;
|
|
lines.forEach(line => {
|
|
if (state === DONE) {
|
|
throw new Error(`Extra comment after @api for param ${name}:\n${comment}`);
|
|
}
|
|
|
|
if (!(line.startsWith(' * ') || line === ' *')) {
|
|
throw new Error(`Unexpected comment start for param ${name}:\n${comment}`);
|
|
}
|
|
|
|
if (line.indexOf('@type ') === 3) {
|
|
state = IN_TYPE;
|
|
if (type) {
|
|
throw new Error(`Duplicate type for param ${name}:\n${comment}`);
|
|
}
|
|
type = line.slice(9);
|
|
return;
|
|
}
|
|
|
|
if (line.indexOf('@api') === 3) {
|
|
state = DONE;
|
|
return;
|
|
}
|
|
|
|
if (state === IN_DESCRIPTION) {
|
|
if (type) {
|
|
throw new Error(`Description after type for param ${name}:\n${comment}`);
|
|
}
|
|
description += line.slice(3) + '\n';
|
|
return;
|
|
}
|
|
|
|
type += line.slice(3) + '\n';
|
|
});
|
|
|
|
return {name, type, description};
|
|
}
|
|
|
|
function getName(name) {
|
|
if (!name.startsWith('olx.')) {
|
|
throw new Error(`Unexpected name: ${name}`);
|
|
}
|
|
return name.slice(4).replace(/\./g, '_');
|
|
}
|
|
|
|
function processBlock(block, data) {
|
|
const name = getName(block.code.slice(0, -1));
|
|
|
|
const protoStart = name.indexOf('_prototype_');
|
|
if (protoStart > 0) {
|
|
const parentName = name.slice(0, protoStart);
|
|
const childName = name.slice(protoStart + 11);
|
|
if (!(parentName in data)) {
|
|
throw new Error(`No parent for ${block.code}`);
|
|
}
|
|
const param = processParam(childName, block.comment);
|
|
data[parentName].params.push(param);
|
|
return;
|
|
}
|
|
|
|
if (block.comment.indexOf('@typedef') === -1) {
|
|
if (block.comment.indexOf('Namespace.') === -1) {
|
|
throw new Error(`Unexpected comment for ${block.code}`);
|
|
}
|
|
return;
|
|
}
|
|
|
|
data[name] = {
|
|
name,
|
|
comment: block.comment,
|
|
params: []
|
|
};
|
|
}
|
|
|
|
function processBlocks(blocks) {
|
|
const data = {};
|
|
blocks.forEach(block => processBlock(block, data));
|
|
return data;
|
|
}
|
|
|
|
function format(data) {
|
|
let source = '';
|
|
for (const name in data) {
|
|
|
|
// add the @typedef
|
|
source += `\n/**\n * @typedef {Object} ${name}\n`;
|
|
|
|
const params = data[name].params;
|
|
if (!params.length) {
|
|
throw new Error(`No params for ${name}`);
|
|
}
|
|
|
|
params.forEach(param => {
|
|
const description = param.description.replace(/\n$/, '').split('\n').join('\n * ');
|
|
source += ` * @property ${param.type} ${param.name} ${description}\n`;
|
|
});
|
|
source += ' */\n\n';
|
|
}
|
|
return source;
|
|
}
|
|
|
|
async function main() {
|
|
const source = String(await readFile(path.join(externs, 'olx.js')));
|
|
const blocks = parse(source);
|
|
const data = processBlocks(blocks);
|
|
const output = format(data);
|
|
await writeFile(path.join(externs, 'xol.js'), output);
|
|
}
|
|
|
|
if (require.main === module) {
|
|
main().catch(err => {
|
|
process.stdout.write(`${err.stack}\n`, () => process.exit(1));
|
|
});
|
|
}
|