@fuman/plist 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/README.md +41 -0
- package/_constants.cjs +11 -0
- package/_constants.d.cts +4 -0
- package/_constants.d.ts +4 -0
- package/_constants.js +11 -0
- package/_utils.cjs +12 -0
- package/_utils.d.cts +1 -0
- package/_utils.d.ts +1 -0
- package/_utils.js +12 -0
- package/bplist-reader.cjs +210 -0
- package/bplist-reader.d.cts +14 -0
- package/bplist-reader.d.ts +14 -0
- package/bplist-reader.js +210 -0
- package/bplist-writer.cjs +344 -0
- package/bplist-writer.d.cts +1 -0
- package/bplist-writer.d.ts +1 -0
- package/bplist-writer.js +344 -0
- package/index.cjs +17 -0
- package/index.d.cts +7 -0
- package/index.d.ts +7 -0
- package/index.js +17 -0
- package/ns-keyed-archiver.cjs +129 -0
- package/ns-keyed-archiver.d.cts +1 -0
- package/ns-keyed-archiver.d.ts +1 -0
- package/ns-keyed-archiver.js +129 -0
- package/ns-keyed-unarchiver.cjs +155 -0
- package/ns-keyed-unarchiver.d.cts +9 -0
- package/ns-keyed-unarchiver.d.ts +9 -0
- package/ns-keyed-unarchiver.js +155 -0
- package/package.json +30 -0
- package/plist-reader.cjs +79 -0
- package/plist-reader.d.cts +15 -0
- package/plist-reader.d.ts +15 -0
- package/plist-reader.js +79 -0
- package/plist-writer.cjs +120 -0
- package/plist-writer.d.cts +27 -0
- package/plist-writer.d.ts +27 -0
- package/plist-writer.js +120 -0
- package/types.cjs +25 -0
- package/types.d.cts +27 -0
- package/types.d.ts +27 -0
- package/types.js +25 -0
package/plist-reader.cjs
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const utils = require("@fuman/utils");
|
|
4
|
+
const types = require("./types.cjs");
|
|
5
|
+
function getNextElement(iter) {
|
|
6
|
+
while (true) {
|
|
7
|
+
const node = iter.next();
|
|
8
|
+
if (node.done) return null;
|
|
9
|
+
if (node.value.nodeType === 1) return node.value;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function readXmlPlist(data, params) {
|
|
13
|
+
const { DOMParser = globalThis.DOMParser, parseUid = false } = params ?? {};
|
|
14
|
+
const parser = new DOMParser();
|
|
15
|
+
const doc = parser.parseFromString(data, "text/xml");
|
|
16
|
+
const root = doc.getElementsByTagName("plist")[0];
|
|
17
|
+
if (!root) throw new Error("<plist> not found, invalid plist?");
|
|
18
|
+
const topObject = root.childNodes.filter((node) => node.nodeType === 1);
|
|
19
|
+
if (topObject.length !== 1) throw new Error("expected exactly one top object");
|
|
20
|
+
function parseObject(node) {
|
|
21
|
+
switch (node.tagName) {
|
|
22
|
+
case "dict": {
|
|
23
|
+
const dict = {};
|
|
24
|
+
const childrenIter = node.childNodes[Symbol.iterator]();
|
|
25
|
+
while (true) {
|
|
26
|
+
const key = getNextElement(childrenIter);
|
|
27
|
+
if (!key) break;
|
|
28
|
+
if (key.tagName !== "key") throw new Error(`expected <key>, got <${key.tagName}>`);
|
|
29
|
+
const keyText = key.textContent ?? "";
|
|
30
|
+
const value = getNextElement(childrenIter);
|
|
31
|
+
if (!value) throw new Error(`value for ${keyText} not found`);
|
|
32
|
+
dict[keyText] = parseObject(value);
|
|
33
|
+
}
|
|
34
|
+
if (parseUid && (typeof dict.CF$UID === "number" || typeof dict.CF$UID === "bigint") && Object.keys(dict).length === 1) {
|
|
35
|
+
return new types.PlistValue("uid", dict.CF$UID);
|
|
36
|
+
}
|
|
37
|
+
return dict;
|
|
38
|
+
}
|
|
39
|
+
case "array": {
|
|
40
|
+
const array = [];
|
|
41
|
+
const childrenIter = node.childNodes[Symbol.iterator]();
|
|
42
|
+
while (true) {
|
|
43
|
+
const value = getNextElement(childrenIter);
|
|
44
|
+
if (!value) break;
|
|
45
|
+
array.push(parseObject(value));
|
|
46
|
+
}
|
|
47
|
+
return array;
|
|
48
|
+
}
|
|
49
|
+
case "string":
|
|
50
|
+
return node.textContent ?? "";
|
|
51
|
+
case "data":
|
|
52
|
+
return utils.base64.decode(node.textContent?.trim() ?? "");
|
|
53
|
+
case "integer": {
|
|
54
|
+
const value = node.textContent?.trim() ?? "";
|
|
55
|
+
const numValue = Number(value);
|
|
56
|
+
if (Number.isNaN(numValue)) throw new Error(`invalid integer: ${value}`);
|
|
57
|
+
if (numValue < Number.MIN_SAFE_INTEGER || numValue > Number.MAX_SAFE_INTEGER) {
|
|
58
|
+
return BigInt(value);
|
|
59
|
+
}
|
|
60
|
+
return numValue;
|
|
61
|
+
}
|
|
62
|
+
case "date": {
|
|
63
|
+
const value = new Date(node.textContent?.trim() ?? "");
|
|
64
|
+
if (Number.isNaN(value.getTime())) throw new Error(`invalid date: ${node.textContent}`);
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
case "false":
|
|
68
|
+
return false;
|
|
69
|
+
case "true":
|
|
70
|
+
return true;
|
|
71
|
+
case "real":
|
|
72
|
+
return Number(node.textContent?.trim() ?? "");
|
|
73
|
+
default:
|
|
74
|
+
throw new Error(`unexpected tag: <${node.tagName}>`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return parseObject(topObject[0]);
|
|
78
|
+
}
|
|
79
|
+
exports.readXmlPlist = readXmlPlist;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare function readXmlPlist(data: string, params?: {
|
|
2
|
+
/**
|
|
3
|
+
* implementation of the DOMParser interface (e.g. `@xmldom/xmldom`)
|
|
4
|
+
*
|
|
5
|
+
* @default globalThis.DOMParser
|
|
6
|
+
*/
|
|
7
|
+
DOMParser?: any;
|
|
8
|
+
/**
|
|
9
|
+
* in `xml1` plists, uids are serialized as a `<dict>` with a single integer key `CF$UID`.
|
|
10
|
+
* by default, we keep them as is, but you can opt into parsing them into a `PlistValue<'uid'>` instead.
|
|
11
|
+
*
|
|
12
|
+
* @default false
|
|
13
|
+
*/
|
|
14
|
+
parseUid?: boolean;
|
|
15
|
+
}): unknown;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare function readXmlPlist(data: string, params?: {
|
|
2
|
+
/**
|
|
3
|
+
* implementation of the DOMParser interface (e.g. `@xmldom/xmldom`)
|
|
4
|
+
*
|
|
5
|
+
* @default globalThis.DOMParser
|
|
6
|
+
*/
|
|
7
|
+
DOMParser?: any;
|
|
8
|
+
/**
|
|
9
|
+
* in `xml1` plists, uids are serialized as a `<dict>` with a single integer key `CF$UID`.
|
|
10
|
+
* by default, we keep them as is, but you can opt into parsing them into a `PlistValue<'uid'>` instead.
|
|
11
|
+
*
|
|
12
|
+
* @default false
|
|
13
|
+
*/
|
|
14
|
+
parseUid?: boolean;
|
|
15
|
+
}): unknown;
|
package/plist-reader.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { base64 } from "@fuman/utils";
|
|
2
|
+
import { PlistValue } from "./types.js";
|
|
3
|
+
function getNextElement(iter) {
|
|
4
|
+
while (true) {
|
|
5
|
+
const node = iter.next();
|
|
6
|
+
if (node.done) return null;
|
|
7
|
+
if (node.value.nodeType === 1) return node.value;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function readXmlPlist(data, params) {
|
|
11
|
+
const { DOMParser = globalThis.DOMParser, parseUid = false } = params ?? {};
|
|
12
|
+
const parser = new DOMParser();
|
|
13
|
+
const doc = parser.parseFromString(data, "text/xml");
|
|
14
|
+
const root = doc.getElementsByTagName("plist")[0];
|
|
15
|
+
if (!root) throw new Error("<plist> not found, invalid plist?");
|
|
16
|
+
const topObject = root.childNodes.filter((node) => node.nodeType === 1);
|
|
17
|
+
if (topObject.length !== 1) throw new Error("expected exactly one top object");
|
|
18
|
+
function parseObject(node) {
|
|
19
|
+
switch (node.tagName) {
|
|
20
|
+
case "dict": {
|
|
21
|
+
const dict = {};
|
|
22
|
+
const childrenIter = node.childNodes[Symbol.iterator]();
|
|
23
|
+
while (true) {
|
|
24
|
+
const key = getNextElement(childrenIter);
|
|
25
|
+
if (!key) break;
|
|
26
|
+
if (key.tagName !== "key") throw new Error(`expected <key>, got <${key.tagName}>`);
|
|
27
|
+
const keyText = key.textContent ?? "";
|
|
28
|
+
const value = getNextElement(childrenIter);
|
|
29
|
+
if (!value) throw new Error(`value for ${keyText} not found`);
|
|
30
|
+
dict[keyText] = parseObject(value);
|
|
31
|
+
}
|
|
32
|
+
if (parseUid && (typeof dict.CF$UID === "number" || typeof dict.CF$UID === "bigint") && Object.keys(dict).length === 1) {
|
|
33
|
+
return new PlistValue("uid", dict.CF$UID);
|
|
34
|
+
}
|
|
35
|
+
return dict;
|
|
36
|
+
}
|
|
37
|
+
case "array": {
|
|
38
|
+
const array = [];
|
|
39
|
+
const childrenIter = node.childNodes[Symbol.iterator]();
|
|
40
|
+
while (true) {
|
|
41
|
+
const value = getNextElement(childrenIter);
|
|
42
|
+
if (!value) break;
|
|
43
|
+
array.push(parseObject(value));
|
|
44
|
+
}
|
|
45
|
+
return array;
|
|
46
|
+
}
|
|
47
|
+
case "string":
|
|
48
|
+
return node.textContent ?? "";
|
|
49
|
+
case "data":
|
|
50
|
+
return base64.decode(node.textContent?.trim() ?? "");
|
|
51
|
+
case "integer": {
|
|
52
|
+
const value = node.textContent?.trim() ?? "";
|
|
53
|
+
const numValue = Number(value);
|
|
54
|
+
if (Number.isNaN(numValue)) throw new Error(`invalid integer: ${value}`);
|
|
55
|
+
if (numValue < Number.MIN_SAFE_INTEGER || numValue > Number.MAX_SAFE_INTEGER) {
|
|
56
|
+
return BigInt(value);
|
|
57
|
+
}
|
|
58
|
+
return numValue;
|
|
59
|
+
}
|
|
60
|
+
case "date": {
|
|
61
|
+
const value = new Date(node.textContent?.trim() ?? "");
|
|
62
|
+
if (Number.isNaN(value.getTime())) throw new Error(`invalid date: ${node.textContent}`);
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
case "false":
|
|
66
|
+
return false;
|
|
67
|
+
case "true":
|
|
68
|
+
return true;
|
|
69
|
+
case "real":
|
|
70
|
+
return Number(node.textContent?.trim() ?? "");
|
|
71
|
+
default:
|
|
72
|
+
throw new Error(`unexpected tag: <${node.tagName}>`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return parseObject(topObject[0]);
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
readXmlPlist
|
|
79
|
+
};
|
package/plist-writer.cjs
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const utils = require("@fuman/utils");
|
|
4
|
+
const types = require("./types.cjs");
|
|
5
|
+
function escapeXml(str) {
|
|
6
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
7
|
+
}
|
|
8
|
+
function writeXmlPlist(data, options) {
|
|
9
|
+
const {
|
|
10
|
+
indent: indentStr = " ",
|
|
11
|
+
lineBreak: lineBreakStr = "\n",
|
|
12
|
+
wrapDataAt = 0,
|
|
13
|
+
collapseEmpty = true
|
|
14
|
+
} = options ?? {};
|
|
15
|
+
const lines = [
|
|
16
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
17
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
18
|
+
'<plist version="1.0">'
|
|
19
|
+
];
|
|
20
|
+
let indent = "";
|
|
21
|
+
const pushIndent = () => {
|
|
22
|
+
indent += indentStr;
|
|
23
|
+
};
|
|
24
|
+
const popIndent = () => {
|
|
25
|
+
indent = indent.slice(0, -indentStr.length);
|
|
26
|
+
};
|
|
27
|
+
const pushLine = (line) => {
|
|
28
|
+
lines.push(indent + line);
|
|
29
|
+
};
|
|
30
|
+
function writeObject(object) {
|
|
31
|
+
switch (typeof object) {
|
|
32
|
+
case "object": {
|
|
33
|
+
if (object === null) {
|
|
34
|
+
pushLine("<null/>");
|
|
35
|
+
} else if (Array.isArray(object)) {
|
|
36
|
+
if (collapseEmpty && object.length === 0) {
|
|
37
|
+
pushLine("<array/>");
|
|
38
|
+
} else {
|
|
39
|
+
pushLine("<array>");
|
|
40
|
+
pushIndent();
|
|
41
|
+
for (const value of object) {
|
|
42
|
+
writeObject(value);
|
|
43
|
+
}
|
|
44
|
+
popIndent();
|
|
45
|
+
pushLine("</array>");
|
|
46
|
+
}
|
|
47
|
+
} else if (object instanceof Date) {
|
|
48
|
+
pushLine(`<date>${object.toISOString().replace(/\.\d+(Z|\+)$/, "$1")}</date>`);
|
|
49
|
+
} else if (object instanceof types.PlistValue) {
|
|
50
|
+
switch (object.type) {
|
|
51
|
+
case "float32":
|
|
52
|
+
case "float64":
|
|
53
|
+
pushLine(`<real>${object.value}</real>`);
|
|
54
|
+
break;
|
|
55
|
+
case "int":
|
|
56
|
+
pushLine(`<integer>${object.value}</integer>`);
|
|
57
|
+
break;
|
|
58
|
+
case "uid":
|
|
59
|
+
writeObject({ CF$UID: object.value });
|
|
60
|
+
break;
|
|
61
|
+
case "ascii":
|
|
62
|
+
case "utf16":
|
|
63
|
+
case "utf8":
|
|
64
|
+
pushLine(`<string>${escapeXml(object.value)}</string>`);
|
|
65
|
+
break;
|
|
66
|
+
default:
|
|
67
|
+
throw new Error(`unexpected type: ${object.type}`);
|
|
68
|
+
}
|
|
69
|
+
} else if (object instanceof Uint8Array) {
|
|
70
|
+
const b64 = utils.base64.encode(object);
|
|
71
|
+
if (wrapDataAt === 0) {
|
|
72
|
+
pushLine(`<data>${b64}</data>`);
|
|
73
|
+
} else {
|
|
74
|
+
const wrapLength = wrapDataAt - indent.replace(/\t/g, " ").length;
|
|
75
|
+
pushLine("<data>");
|
|
76
|
+
for (let i = 0; i < b64.length; i += wrapLength) {
|
|
77
|
+
pushLine(b64.slice(i, i + wrapLength));
|
|
78
|
+
}
|
|
79
|
+
pushLine("</data>");
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
const keys = Object.keys(object);
|
|
83
|
+
if (collapseEmpty && keys.length === 0) {
|
|
84
|
+
pushLine("<dict/>");
|
|
85
|
+
} else {
|
|
86
|
+
pushLine("<dict>");
|
|
87
|
+
pushIndent();
|
|
88
|
+
for (const key of keys) {
|
|
89
|
+
pushLine(`<key>${escapeXml(key)}</key>`);
|
|
90
|
+
writeObject(object[key]);
|
|
91
|
+
}
|
|
92
|
+
popIndent();
|
|
93
|
+
pushLine("</dict>");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
case "string":
|
|
99
|
+
pushLine(`<string>${escapeXml(object)}</string>`);
|
|
100
|
+
break;
|
|
101
|
+
case "number": {
|
|
102
|
+
const element = Number.isInteger(object) ? "integer" : "real";
|
|
103
|
+
pushLine(`<${element}>${object}</${element}>`);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
case "bigint":
|
|
107
|
+
pushLine(`<integer>${object.toString()}</integer>`);
|
|
108
|
+
break;
|
|
109
|
+
case "boolean":
|
|
110
|
+
pushLine(`<${object ? "true" : "false"}/>`);
|
|
111
|
+
break;
|
|
112
|
+
default:
|
|
113
|
+
throw new Error(`unexpected type: ${typeof object}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
writeObject(data);
|
|
117
|
+
lines.push("</plist>");
|
|
118
|
+
return lines.join(lineBreakStr);
|
|
119
|
+
}
|
|
120
|
+
exports.writeXmlPlist = writeXmlPlist;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare function writeXmlPlist(data: unknown, options?: {
|
|
2
|
+
/**
|
|
3
|
+
* string to use for indentation
|
|
4
|
+
*
|
|
5
|
+
* @default '\t'
|
|
6
|
+
*/
|
|
7
|
+
indent?: string;
|
|
8
|
+
/**
|
|
9
|
+
* string to use for line breaks
|
|
10
|
+
*
|
|
11
|
+
* @default '\n'
|
|
12
|
+
*/
|
|
13
|
+
lineBreak?: string;
|
|
14
|
+
/**
|
|
15
|
+
* some implementations wrap `<data>` base64 strings at a certain length,
|
|
16
|
+
* this option allows you to set that length
|
|
17
|
+
*
|
|
18
|
+
* @default 0 (no wrapping)
|
|
19
|
+
*/
|
|
20
|
+
wrapDataAt?: number;
|
|
21
|
+
/**
|
|
22
|
+
* whether we should collapse empty tags (e.g. `<dict/>`, <array/>`)
|
|
23
|
+
*
|
|
24
|
+
* @default true
|
|
25
|
+
*/
|
|
26
|
+
collapseEmpty?: boolean;
|
|
27
|
+
}): string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare function writeXmlPlist(data: unknown, options?: {
|
|
2
|
+
/**
|
|
3
|
+
* string to use for indentation
|
|
4
|
+
*
|
|
5
|
+
* @default '\t'
|
|
6
|
+
*/
|
|
7
|
+
indent?: string;
|
|
8
|
+
/**
|
|
9
|
+
* string to use for line breaks
|
|
10
|
+
*
|
|
11
|
+
* @default '\n'
|
|
12
|
+
*/
|
|
13
|
+
lineBreak?: string;
|
|
14
|
+
/**
|
|
15
|
+
* some implementations wrap `<data>` base64 strings at a certain length,
|
|
16
|
+
* this option allows you to set that length
|
|
17
|
+
*
|
|
18
|
+
* @default 0 (no wrapping)
|
|
19
|
+
*/
|
|
20
|
+
wrapDataAt?: number;
|
|
21
|
+
/**
|
|
22
|
+
* whether we should collapse empty tags (e.g. `<dict/>`, <array/>`)
|
|
23
|
+
*
|
|
24
|
+
* @default true
|
|
25
|
+
*/
|
|
26
|
+
collapseEmpty?: boolean;
|
|
27
|
+
}): string;
|
package/plist-writer.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { base64 } from "@fuman/utils";
|
|
2
|
+
import { PlistValue } from "./types.js";
|
|
3
|
+
function escapeXml(str) {
|
|
4
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
5
|
+
}
|
|
6
|
+
function writeXmlPlist(data, options) {
|
|
7
|
+
const {
|
|
8
|
+
indent: indentStr = " ",
|
|
9
|
+
lineBreak: lineBreakStr = "\n",
|
|
10
|
+
wrapDataAt = 0,
|
|
11
|
+
collapseEmpty = true
|
|
12
|
+
} = options ?? {};
|
|
13
|
+
const lines = [
|
|
14
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
15
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
16
|
+
'<plist version="1.0">'
|
|
17
|
+
];
|
|
18
|
+
let indent = "";
|
|
19
|
+
const pushIndent = () => {
|
|
20
|
+
indent += indentStr;
|
|
21
|
+
};
|
|
22
|
+
const popIndent = () => {
|
|
23
|
+
indent = indent.slice(0, -indentStr.length);
|
|
24
|
+
};
|
|
25
|
+
const pushLine = (line) => {
|
|
26
|
+
lines.push(indent + line);
|
|
27
|
+
};
|
|
28
|
+
function writeObject(object) {
|
|
29
|
+
switch (typeof object) {
|
|
30
|
+
case "object": {
|
|
31
|
+
if (object === null) {
|
|
32
|
+
pushLine("<null/>");
|
|
33
|
+
} else if (Array.isArray(object)) {
|
|
34
|
+
if (collapseEmpty && object.length === 0) {
|
|
35
|
+
pushLine("<array/>");
|
|
36
|
+
} else {
|
|
37
|
+
pushLine("<array>");
|
|
38
|
+
pushIndent();
|
|
39
|
+
for (const value of object) {
|
|
40
|
+
writeObject(value);
|
|
41
|
+
}
|
|
42
|
+
popIndent();
|
|
43
|
+
pushLine("</array>");
|
|
44
|
+
}
|
|
45
|
+
} else if (object instanceof Date) {
|
|
46
|
+
pushLine(`<date>${object.toISOString().replace(/\.\d+(Z|\+)$/, "$1")}</date>`);
|
|
47
|
+
} else if (object instanceof PlistValue) {
|
|
48
|
+
switch (object.type) {
|
|
49
|
+
case "float32":
|
|
50
|
+
case "float64":
|
|
51
|
+
pushLine(`<real>${object.value}</real>`);
|
|
52
|
+
break;
|
|
53
|
+
case "int":
|
|
54
|
+
pushLine(`<integer>${object.value}</integer>`);
|
|
55
|
+
break;
|
|
56
|
+
case "uid":
|
|
57
|
+
writeObject({ CF$UID: object.value });
|
|
58
|
+
break;
|
|
59
|
+
case "ascii":
|
|
60
|
+
case "utf16":
|
|
61
|
+
case "utf8":
|
|
62
|
+
pushLine(`<string>${escapeXml(object.value)}</string>`);
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
throw new Error(`unexpected type: ${object.type}`);
|
|
66
|
+
}
|
|
67
|
+
} else if (object instanceof Uint8Array) {
|
|
68
|
+
const b64 = base64.encode(object);
|
|
69
|
+
if (wrapDataAt === 0) {
|
|
70
|
+
pushLine(`<data>${b64}</data>`);
|
|
71
|
+
} else {
|
|
72
|
+
const wrapLength = wrapDataAt - indent.replace(/\t/g, " ").length;
|
|
73
|
+
pushLine("<data>");
|
|
74
|
+
for (let i = 0; i < b64.length; i += wrapLength) {
|
|
75
|
+
pushLine(b64.slice(i, i + wrapLength));
|
|
76
|
+
}
|
|
77
|
+
pushLine("</data>");
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
const keys = Object.keys(object);
|
|
81
|
+
if (collapseEmpty && keys.length === 0) {
|
|
82
|
+
pushLine("<dict/>");
|
|
83
|
+
} else {
|
|
84
|
+
pushLine("<dict>");
|
|
85
|
+
pushIndent();
|
|
86
|
+
for (const key of keys) {
|
|
87
|
+
pushLine(`<key>${escapeXml(key)}</key>`);
|
|
88
|
+
writeObject(object[key]);
|
|
89
|
+
}
|
|
90
|
+
popIndent();
|
|
91
|
+
pushLine("</dict>");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
case "string":
|
|
97
|
+
pushLine(`<string>${escapeXml(object)}</string>`);
|
|
98
|
+
break;
|
|
99
|
+
case "number": {
|
|
100
|
+
const element = Number.isInteger(object) ? "integer" : "real";
|
|
101
|
+
pushLine(`<${element}>${object}</${element}>`);
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case "bigint":
|
|
105
|
+
pushLine(`<integer>${object.toString()}</integer>`);
|
|
106
|
+
break;
|
|
107
|
+
case "boolean":
|
|
108
|
+
pushLine(`<${object ? "true" : "false"}/>`);
|
|
109
|
+
break;
|
|
110
|
+
default:
|
|
111
|
+
throw new Error(`unexpected type: ${typeof object}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
writeObject(data);
|
|
115
|
+
lines.push("</plist>");
|
|
116
|
+
return lines.join(lineBreakStr);
|
|
117
|
+
}
|
|
118
|
+
export {
|
|
119
|
+
writeXmlPlist
|
|
120
|
+
};
|
package/types.cjs
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
class PlistValue {
|
|
4
|
+
constructor(type, value) {
|
|
5
|
+
this.type = type;
|
|
6
|
+
this.value = value;
|
|
7
|
+
}
|
|
8
|
+
valueOf() {
|
|
9
|
+
return this.value;
|
|
10
|
+
}
|
|
11
|
+
toString() {
|
|
12
|
+
return String(this.value);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
class KeyedArchiverValue {
|
|
16
|
+
constructor(header, value) {
|
|
17
|
+
this.header = header;
|
|
18
|
+
this.value = value;
|
|
19
|
+
}
|
|
20
|
+
valueOf() {
|
|
21
|
+
return this.value;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.KeyedArchiverValue = KeyedArchiverValue;
|
|
25
|
+
exports.PlistValue = PlistValue;
|
package/types.d.cts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface PlistValueType {
|
|
2
|
+
float32: number;
|
|
3
|
+
float64: number;
|
|
4
|
+
int: number | bigint;
|
|
5
|
+
uid: number | bigint;
|
|
6
|
+
ascii: string;
|
|
7
|
+
utf16: string;
|
|
8
|
+
utf8: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class PlistValue<Type extends keyof PlistValueType = keyof PlistValueType> {
|
|
11
|
+
readonly type: Type;
|
|
12
|
+
readonly value: PlistValueType[Type];
|
|
13
|
+
constructor(type: Type, value: PlistValueType[Type]);
|
|
14
|
+
valueOf(): PlistValueType[Type];
|
|
15
|
+
toString(): string;
|
|
16
|
+
}
|
|
17
|
+
export interface KeyedArchiverValueHeader {
|
|
18
|
+
$classname: string;
|
|
19
|
+
$classes: string[];
|
|
20
|
+
$classhints?: string[];
|
|
21
|
+
}
|
|
22
|
+
export declare class KeyedArchiverValue {
|
|
23
|
+
readonly header: KeyedArchiverValueHeader;
|
|
24
|
+
readonly value: unknown;
|
|
25
|
+
constructor(header: KeyedArchiverValueHeader, value: unknown);
|
|
26
|
+
valueOf(): unknown;
|
|
27
|
+
}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface PlistValueType {
|
|
2
|
+
float32: number;
|
|
3
|
+
float64: number;
|
|
4
|
+
int: number | bigint;
|
|
5
|
+
uid: number | bigint;
|
|
6
|
+
ascii: string;
|
|
7
|
+
utf16: string;
|
|
8
|
+
utf8: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class PlistValue<Type extends keyof PlistValueType = keyof PlistValueType> {
|
|
11
|
+
readonly type: Type;
|
|
12
|
+
readonly value: PlistValueType[Type];
|
|
13
|
+
constructor(type: Type, value: PlistValueType[Type]);
|
|
14
|
+
valueOf(): PlistValueType[Type];
|
|
15
|
+
toString(): string;
|
|
16
|
+
}
|
|
17
|
+
export interface KeyedArchiverValueHeader {
|
|
18
|
+
$classname: string;
|
|
19
|
+
$classes: string[];
|
|
20
|
+
$classhints?: string[];
|
|
21
|
+
}
|
|
22
|
+
export declare class KeyedArchiverValue {
|
|
23
|
+
readonly header: KeyedArchiverValueHeader;
|
|
24
|
+
readonly value: unknown;
|
|
25
|
+
constructor(header: KeyedArchiverValueHeader, value: unknown);
|
|
26
|
+
valueOf(): unknown;
|
|
27
|
+
}
|
package/types.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class PlistValue {
|
|
2
|
+
constructor(type, value) {
|
|
3
|
+
this.type = type;
|
|
4
|
+
this.value = value;
|
|
5
|
+
}
|
|
6
|
+
valueOf() {
|
|
7
|
+
return this.value;
|
|
8
|
+
}
|
|
9
|
+
toString() {
|
|
10
|
+
return String(this.value);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
class KeyedArchiverValue {
|
|
14
|
+
constructor(header, value) {
|
|
15
|
+
this.header = header;
|
|
16
|
+
this.value = value;
|
|
17
|
+
}
|
|
18
|
+
valueOf() {
|
|
19
|
+
return this.value;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
KeyedArchiverValue,
|
|
24
|
+
PlistValue
|
|
25
|
+
};
|