bio-dts
Version:
Generate sane and clean types from JavaScript sources
193 lines (142 loc) • 3.22 kB
JavaScript
/**
* @typedef { {
* start: number,
* end: number,
* name: string,
* type?: {
* start: number,
* end: number,
* value: string
* },
* param: {
* start: number,
* end: number,
* name?: string,
* contents?: string,
* value: string
* },
* description?: {
* start: number,
* end: number,
* value: string
* }
* } } JsdocTag
*/
function skipWhitespace(str, pos) {
let i = pos;
let char;
for (; (char = str.charAt(i)) && char !== '\n' && /\s/.test(char); i++) {
// ignore
}
return i;
}
function skipText(str, pos) {
let i = pos;
let char;
for (; (char = str.charAt(i)) && /\S/.test(char); i++) {
// ignore
}
return i;
}
function findClose(str, pos, openChar, closeChar) {
if (str.charAt(pos) !== openChar) {
return false;
}
let char;
let i = pos;
let open = 0;
while ((char = str.charAt(i++))) {
switch (char) {
case openChar:
open++;
break;
case closeChar:
if (--open === 0) {
return {
start: pos,
end: i,
value: str.substring(pos, i)
};
}
break;
}
}
}
function findNewline(str, pos) {
let i = pos;
let char;
for (; (char = str.charAt(i)) && /[^\n]/.test(char); i++) {
// ignore
}
return i;
}
/**
* @param {string} str
*
* @return {JsdocTag[]}
*/
export function parse(str) {
var i = 0, j;
var tags = [];
/**
* @type { Partial<JsdocTag> }
*/
var tag;
while ((i = str.indexOf('@', i)) && i !== -1) {
j = i + 1;
tag = {
start: i
};
// parse tag name
j = skipText(str, i);
tag.name = str.substring(i + 1, j);
i = skipWhitespace(str, j);
// parse type
if ([ 'param', 'return', 'returns', 'typedef', 'template' ].includes(tag.name)) {
let match = findClose(str, i, '{', '}');
if (match) {
tag.type = match;
i = skipWhitespace(str, match.end);
}
}
// parse param
if ([ 'param', 'typedef', 'template' ].includes(tag.name)) {
let match = tag.name === 'param' && findClose(str, i, '[', ']');
if (match) {
tag.param = match;
const contents = tag.param.contents = str.substring(match.start + 1, match.end - 1);
const idxEq = contents.indexOf('=');
tag.param.name = idxEq !== -1 ? contents.substring(0, idxEq).trim() : contents;
} else {
j = skipText(str, i);
tag.param = {
start: i,
end: j,
name: str.substring(i, j),
value: str.substring(i, j)
};
}
i = skipWhitespace(str, tag.param.end);
}
j = findNewline(str, i);
tag.description = i !== j ? {
start: i,
end: j,
value: str.substring(i, j)
} : null;
tag.end = j;
tags.push(/** @type { JsdocTag } */ (tag));
i = j + 1;
}
return tags;
}
export function dbg(str, pos) {
const arr = Array.from(str);
arr.splice(pos, 0, '|');
return arr.join('');
}
export function replace(str, range, contents) {
const arr = Array.from(str);
arr.splice(range.start, range.end - range.start, contents);
return arr.join('');
}