Files
openlayers/tasks/t.js
2018-03-11 09:12:14 -06:00

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));
});
}