86 lines
2.3 KiB
TypeScript
86 lines
2.3 KiB
TypeScript
const DTYPE_QUALIFIED = "plist";
|
|
const DTYPE_PUBLIC = "-//Apple Inc//DTD PLIST 1.0//EN";
|
|
const DTYPE_SYSTEM = "http://www.apple.com/DTDs/PropertyList-1.0.dtd";
|
|
|
|
const PLIST_VERSION = "1.0";
|
|
const XML_HEADER = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
|
|
export class Plist {
|
|
static stringify(value: unknown) {
|
|
const root = document.implementation.createDocument(
|
|
null,
|
|
"plist",
|
|
document.implementation.createDocumentType(
|
|
DTYPE_QUALIFIED,
|
|
DTYPE_PUBLIC,
|
|
DTYPE_SYSTEM,
|
|
),
|
|
);
|
|
root.lastElementChild!.setAttribute("version", PLIST_VERSION);
|
|
|
|
new Serializer(root)
|
|
.insert(root.lastElementChild!, value);
|
|
|
|
return XML_HEADER + new XMLSerializer().serializeToString(root);
|
|
}
|
|
}
|
|
|
|
class Serializer {
|
|
#doc: XMLDocument;
|
|
|
|
constructor(doc: XMLDocument) {
|
|
this.#doc = doc;
|
|
}
|
|
|
|
insert(cur: Element, value: unknown) {
|
|
switch (typeof value) {
|
|
case "bigint": {
|
|
const int = this.#doc.createElement("integer");
|
|
int.textContent = value.toString();
|
|
cur.append(int);
|
|
break;
|
|
}
|
|
case "number": {
|
|
const real = this.#doc.createElement("real");
|
|
real.textContent = value.toString();
|
|
cur.append(real);
|
|
break;
|
|
}
|
|
case "object":
|
|
if (Array.isArray(value)) {
|
|
const arr = this.#doc.createElement("array");
|
|
cur.append(arr);
|
|
|
|
for (const val of value) {
|
|
this.insert(arr, val);
|
|
}
|
|
} else if (value instanceof Uint8Array) {
|
|
const data = this.#doc.createElement("data");
|
|
data.textContent = btoa(String.fromCharCode(...value));
|
|
cur.append(data);
|
|
} else if (value !== null) {
|
|
const dict = this.#doc.createElement("dict");
|
|
cur.append(dict);
|
|
|
|
for (const [key, val] of Object.entries(value)) {
|
|
const pKey = this.#doc.createElement("key");
|
|
pKey.textContent = key;
|
|
dict.append(pKey);
|
|
this.insert(dict, val);
|
|
}
|
|
} else {
|
|
throw new TypeError(`Unsupported plist type 'null'`);
|
|
}
|
|
break;
|
|
case "string": {
|
|
const str = this.#doc.createElement("string");
|
|
str.textContent = value;
|
|
cur.append(str);
|
|
break;
|
|
}
|
|
default:
|
|
throw new TypeError(`Unsupported plist type '${typeof value}'`);
|
|
}
|
|
}
|
|
}
|