Skip to content

Commit 1f6c99e

Browse files
committed
create small xml util for editable output
1 parent 7cd7d7d commit 1f6c99e

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

internal/xml/index.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Generic XML element. None of the implementations' output is sanitized.
2+
export interface IElement {
3+
render(): string;
4+
}
5+
6+
// Unstructured element that will render as the provided string.
7+
export class RawElement implements IElement {
8+
public text: string;
9+
10+
public constructor(text: string) {
11+
this.text = text;
12+
}
13+
14+
public render(): string {
15+
return this.text;
16+
}
17+
}
18+
19+
// Structured element with tag, attributes and children.
20+
export class Element implements IElement {
21+
public tag: string;
22+
public attributes: Record<string, string | number>;
23+
public children: IElement[];
24+
25+
public constructor(tag: string, attributes?: Element["attributes"]) {
26+
this.tag = tag;
27+
this.attributes = attributes || {};
28+
this.children = [];
29+
}
30+
31+
public add(child: IElement): Element {
32+
this.children.push(child);
33+
return this;
34+
}
35+
36+
public render(): string {
37+
const attributes = this.renderAttributes();
38+
const content = this.renderChildren();
39+
return `<${this.tag}${attributes}>${content}</${this.tag}>`;
40+
}
41+
42+
private renderAttributes(): string {
43+
const attributes = Object.keys(this.attributes);
44+
if (attributes.length === 0) return "";
45+
let out = "";
46+
for (const attribute of attributes) {
47+
out += ` ${attribute}="${this.attributes[attribute]}"`;
48+
}
49+
return out;
50+
}
51+
52+
private renderChildren(): string {
53+
let out = "";
54+
for (const child of this.children) {
55+
out += child.render();
56+
}
57+
return out;
58+
}
59+
}

internal/xml/test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {Element, RawElement} from ".";
2+
3+
describe("internal/xml/xml", () => {
4+
describe("render", () => {
5+
it("should render raw elements", () => {
6+
const elem = new RawElement("test");
7+
expect(elem.render()).toBe("test");
8+
});
9+
10+
it("should render element tags", () => {
11+
const elem = new Element("test");
12+
expect(elem.render()).toBe("<test></test>");
13+
});
14+
15+
it("should render element attributes", () => {
16+
const elem = new Element("test", {a: 1, "b-c": "d"});
17+
expect(elem.render()).toBe('<test a="1" b-c="d"></test>');
18+
});
19+
20+
it("should render element children", () => {
21+
const a = new Element("a");
22+
const aa = new Element("aa");
23+
const ab = new Element("ab");
24+
const aba = new RawElement("aba");
25+
a.add(aa).add(ab);
26+
ab.add(aba);
27+
expect(a.render()).toBe('<a><aa></aa><ab>aba</ab></a>');
28+
});
29+
});
30+
});

0 commit comments

Comments
 (0)