98 lines
3.2 KiB
TypeScript
98 lines
3.2 KiB
TypeScript
import fs from 'node:fs/promises';
|
|
import parse, { type DOMNode } from 'html-dom-parser';
|
|
import type { ChildNode } from 'domhandler';
|
|
|
|
const JSD_STRING = /\(\s*(<.*)>\s*\)/gs;
|
|
|
|
export async function parseJSDFile(filename: string) {
|
|
const content = (await fs.readFile(filename)).toString();
|
|
|
|
const matches = JSD_STRING.exec(content);
|
|
if (matches) {
|
|
let html = matches[1] + '>';
|
|
const root = parse(html);
|
|
const translated = translate(root[0]);
|
|
const str = content.replace(matches[1] + '>', translated);
|
|
await fs.writeFile(filename.replace('.tsd', '.ts'), str);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
interface state {
|
|
inInterpolation?: boolean;
|
|
children?: string[][];
|
|
parent?: Text[];
|
|
}
|
|
|
|
function translate(root: DOMNode | ChildNode | null, state: state = {}): string | null {
|
|
if (!root || typeof root !== 'object') return null;
|
|
|
|
let children = [];
|
|
if ('children' in root && Array.isArray(root.children) && root.children.length > 0) {
|
|
for (const child of root.children) {
|
|
const translated = translate(child, state);
|
|
if (translated) {
|
|
if (state.inInterpolation && state.parent[state.children.length - 1] === child) {
|
|
state.children[state.children.length - 1].push(translated);
|
|
} else {
|
|
children.push(translated);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ('nodeType' in root && root.nodeType === 3) {
|
|
if (root.data.trim() === '') return null;
|
|
return parseText(root.data.trim(), state, root);
|
|
}
|
|
|
|
if ('name' in root && root.name) {
|
|
let tagName = root.name || 'unknown';
|
|
let attrs = 'attribs' in root ? root.attribs : {};
|
|
return `StarKitten.createElement("${tagName}", ${JSON.stringify(attrs)}${children.length > 0 ? ', ' + children.join(', ') : ''})`;
|
|
}
|
|
}
|
|
|
|
const JSD_INTERPOLATION = /\{(.+)\}/gs;
|
|
const JSD_START_EXP_INTERPOLATION = /\{(.+)\(/gs;
|
|
const JSD_END_EXP_INTERPOLATION = /\)(.+)\}/gs;
|
|
|
|
function parseText(text: string, state: state = {}, parent: Text = {}): string {
|
|
let interpolations = text.match(JSD_INTERPOLATION);
|
|
if (!interpolations) {
|
|
if (text.match(JSD_START_EXP_INTERPOLATION)) {
|
|
state.inInterpolation = true;
|
|
state.children = state.children || [[]];
|
|
state.parent = state.parent || [];
|
|
state.parent.push(parent);
|
|
return text.substring(1, text.length - 1);
|
|
} else if (text.match(JSD_END_EXP_INTERPOLATION)) {
|
|
const combined = state.children?.[state.children.length - 1].join(' ');
|
|
state.children?.[state.children.length - 1].splice(0);
|
|
state.children?.pop();
|
|
state.parent?.pop();
|
|
if (state.children.length === 0) {
|
|
state.inInterpolation = false;
|
|
return combined + ' ' + text.substring(1, text.length - 1);
|
|
}
|
|
}
|
|
return `"${text}"`;
|
|
} else {
|
|
text = replaceInterpolations(text);
|
|
return `"${text}"`;
|
|
}
|
|
}
|
|
|
|
function replaceInterpolations(text: string, isOnJSON: boolean = false) {
|
|
let interpolations = null;
|
|
|
|
while ((interpolations = JSD_INTERPOLATION.exec(text))) {
|
|
if (isOnJSON) {
|
|
text = text.replace(`"{${interpolations[1]}}"`, interpolations[1]);
|
|
} else {
|
|
text = text.replace(`{${interpolations[1]}}`, `"+ ${interpolations[1]} +"`);
|
|
}
|
|
}
|
|
return text;
|
|
}
|