UNPKG

@mieweb/wikigdrive

Version:

Google Drive to MarkDown synchronization

183 lines (182 loc) 5.77 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ import * as htmlparser2 from 'htmlparser2'; function createObj(classType, attributes) { const obj = new classType(); if (classType._attrs) { for (const xmlAttrName in attributes) { const attr = classType._attrs[xmlAttrName]; if (attr) { const value = attributes[xmlAttrName]; obj[attr.fieldName] = value; } } } return obj; } function addXmlHandlers(classType) { classType.onOpenTag = (context, xmlElementTag, attributes) => { if (!classType._elems) return; const elemDef = classType._elems[xmlElementTag]; if (elemDef) { if (!elemDef.classTypeName || typeof elemDef.classTypeName !== 'string') { throw new Error('noClassType for tag: ' + xmlElementTag + ', fieldName: ' + elemDef.fieldName); } const subObj = createObj(context.getClass(elemDef.classTypeName), attributes); const obj = context.top.val; if (elemDef.isArray) { if (!Array.isArray(obj[elemDef.fieldName])) { obj[elemDef.fieldName] = []; } obj[elemDef.fieldName].push(subObj); } else { obj[elemDef.fieldName] = subObj; } context.push({ val: subObj, classType: context.getClass(elemDef.classTypeName), closeTag: xmlElementTag }); } }; classType.onCloseTag = (context, xmlElementTag) => { if (context.top.closeTag === xmlElementTag) { context.pop(); } }; classType.onText = (context, text) => { const fieldName = context.top.classType._xmlTextField; if (fieldName) { const obj = context.top.val; const props = classType._xmlTextProps; if (props.isArray) { if (!obj[fieldName]) { obj[fieldName] = []; } obj[fieldName].push(text); } else { if (!obj[fieldName]) { obj[fieldName] = ''; } obj[fieldName] += text; } } }; } export function XmlRootElement(xmlElementTag) { return (classType) => { classType._xmlElementTag = xmlElementTag; addXmlHandlers(classType); }; } export function XmlElement() { return (classType) => { addXmlHandlers(classType); }; } export function XmlAttribute(xmlAttrName, fieldName) { return (classType) => { if (!classType._attrs) classType._attrs = {}; classType._attrs[xmlAttrName] = { fieldName }; }; } export function XmlElementChild(xmlElementTag, fieldName, subClassTypeName, props = { isArray: false }) { if (!subClassTypeName) { throw new Error('No subClassType'); } return (classType) => { if (!classType._elems) classType._elems = {}; const xmlElemDef = { fieldName, classTypeName: subClassTypeName, ...props }; classType._elems[xmlElementTag] = xmlElemDef; }; } export function XmlText(fieldName, props = { isArray: false }) { return (classType) => { classType._xmlTextField = fieldName; classType._xmlTextProps = props; }; } function createRootStackPos(rootClassTypeName, rootClassType) { const val = { retVal: null }; const classType = { _elems: { [rootClassType._xmlElementTag]: { fieldName: 'retVal', classTypeName: rootClassTypeName, } } }; addXmlHandlers(classType); return { val, classType, closeTag: '' }; } export class UnMarshaller { constructor(classes, rootClassTypeName) { Object.defineProperty(this, "classes", { enumerable: true, configurable: true, writable: true, value: classes }); Object.defineProperty(this, "rootClassTypeName", { enumerable: true, configurable: true, writable: true, value: rootClassTypeName }); Object.defineProperty(this, "parser", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "stack", { enumerable: true, configurable: true, writable: true, value: [] }); this.stack.push(createRootStackPos(this.rootClassTypeName, this.getClass(this.rootClassTypeName))); this.parser = new htmlparser2.Parser({ onopentag: (tagname, attributes) => this.top.classType.onOpenTag(this, tagname, attributes), ontext: (text) => this.top.classType.onText(this, text), onclosetag: (tagname) => this.top.classType.onCloseTag(this, tagname), }, { xmlMode: true }); } get top() { return this.stack[this.stack.length - 1]; } push(val) { this.stack.push(val); } pop() { return this.stack.pop(); } unmarshal(content) { if ('string' !== typeof content) { content = new TextDecoder().decode(content); } this.parser.write(content); this.parser.end(); return this.top.val['retVal']; } getClass(classTypeName) { if (!this.classes[classTypeName]) { throw new Error('No class registered: ' + classTypeName); } return this.classes[classTypeName]; } }