@plist/openstep.serialize
Version:
TypeScript library for serializing OpenStep/NEXTStep Property Lists, in browser and node.js.
73 lines (64 loc) • 1.75 kB
text/typescript
import { Value } from '@plist/common';
const UNQUOTED_STRING_REGEX = /^[A-Za-z0-9_.-]+$/;
const escapeString = (string: string) =>
JSON.stringify(string)
.slice(1, -1)
.replace(
/[^\x20-\x7F]/g,
x => '\\u' + x.codePointAt(0)!.toString(16).padStart(4, '0')
);
const pad = (n: number) => {
return n < 10 ? '0' + n : n;
};
const ISODateString = (d: Date): string => {
return (
d.getUTCFullYear() +
'-' +
pad(d.getUTCMonth() + 1) +
'-' +
pad(d.getUTCDate()) +
'T' +
pad(d.getUTCHours()) +
':' +
pad(d.getUTCMinutes()) +
':' +
pad(d.getUTCSeconds()) +
'Z'
);
};
export const serialize = (value: Value): string => {
if (value === null) {
throw new Error('Unsupported null value');
}
switch (typeof value) {
case 'string':
if (UNQUOTED_STRING_REGEX.test(value)) {
return value;
} else {
return `"${escapeString(value)}"`;
}
case 'bigint':
case 'number':
return value.toString();
case 'boolean':
return value ? '1' : '0';
case 'object':
if (Array.isArray(value)) {
return `(${value.map(serialize).join(',')})`;
} else if (value instanceof Date) {
return ISODateString(value);
} else if (value instanceof ArrayBuffer) {
return `<${new Uint8Array(value).reduce((str, byte) => {
return str + byte.toString(16).padStart(2, '0');
}, '')}>`;
} else {
if (Object.keys(value).length === 0) {
return '{}';
}
return `{${Object.keys(value)
.map(key => `${serialize(key)}=${serialize(value[key])}`)
.join(';')};}`;
}
}
throw new Error(`Unsupported value type ${typeof value}`);
};