···55 * @see {serialize_xml_to_plist_object}
66 * @see {deserialize_plist_xml_to_plist_object}
77 * @link https://code.google.com/archive/p/networkpx/wikis/PlistSpec.wiki
88- * @description A single-file zero dependency parser for serializing and deserializing Apple Info.plist files.
88+ * @description A single-file zero dependency parser for serializing and deserializing Apple Info.plist files. Supprts only XML formatted plists.
99 * Only supports plists with a single root element, that contain
1010 * only string, number, boolean, array(also nested), and dictionary(also nested) values. Attempts to conform to
1111 * Apple's formatting and specifications for plists in the parts of the spec that are supported.
···88888989// Supporting real values is not supported by this parser, we only support numbers.
9090function serialize_number(value: number, indent: string) {
9191- return `${indent}<number>${value}</number>`
9191+ return `${indent}<integer>${value}</integer>`
9292}
93939494function serialize_boolean(value: boolean, indent: string) {
···129129}
130130131131/* Deserialization */
132132-const plist_parser_regex = /<(dict|array|string|number)>([\s\S]*?)<\/\1>/
132132+const plist_parser_regex = /<(dict|array|string|integer)>([\s\S]*?)<\/\1>/
133133function naive_ends_with_closing_tag(xml_fragment: string, _element_name: string) {
134134 const open = xml_fragment.lastIndexOf('<')
135135 return open !== -1 && xml_fragment[open + 1] === '/' && xml_fragment[xml_fragment.length - 1] === '>'
···151151 if (tag === 'string') return unescape_xml(content!)
152152 else if (tag === 'dict') return deserialize_plist_dict_to_object(content!)
153153 else if (tag === 'array') return deserialize_plist_array_to_object(content!)
154154- else if (tag === 'number' && Number.isSafeInteger(Number(content))) return parseInt(content!) // Real's probably could be supported if we added a check for if the value is a fixed int or not.
155154 else if (tag === 'true' || tag === 'false') return tag === 'true'
155155+ else if (tag === 'integer') {
156156+ const number_value = Number(content!)
157157+ if (Number.isSafeInteger(number_value))
158158+ return parseInt(content!) // Real's probably could be supported if we added a check for if the value is a fixed int or not.
159159+ }
156160157161 throw new Error('invalid_xml' as plist_parser_error_types, {
158162 cause: { xml: xml_fragment },
···167171 // Starting the loop at 1 because we do not want to look at the first element.
168172 for (let index = 1; index + 1 < parts.length; index += 2) {
169173 const key_part = parts[index]
170170- if (typeof key_part !== 'string')
174174+ if (typeof key_part !== 'string' || result[key_part])
171175 throw new Error('invalid_xml' as plist_parser_error_types, { cause: { xml: xml_fragment, key_part, value_part: parts[index + 1] } })
172176173177 const value_part = parts[index + 1]
···180184 return result
181185}
182186183183-const plist_parser_array_self_closing_tag_regex = /<(dict|array|string|true|false)\/>/
184187function deserialize_plist_array_to_object(xml_fragment: string) {
185188 const result: plist_value[] = []
186189 let remaining = xml_fragment.trim()
187190188191 while (remaining) {
189192 const xml_part_length = remaining.length
190190- const [content, tag] = remaining.match(plist_parser_array_self_closing_tag_regex) ?? []
191191-192192- if (content) {
193193- if (tag === 'array') result.push([])
194194- else if (tag === 'dict') result.push({})
195195-196196- remaining = remaining.substring(content.length).trim()
197197- continue
198198- }
199199-200193 if (!plist_parser_regex.test(remaining))
201194 throw new Error('unsupported_tag' as plist_parser_error_types, { cause: { xml: xml_fragment, remaining } })
202195203203- const [item] = remaining.match(plist_parser_regex)! // arrays only support booleans, strings, dicts, and arrays.
196196+ const [item] = remaining.match(plist_parser_regex)!
204197 result.push(deserialize_xml_fragment_to_plist_value_object(item))
205198 remaining = remaining.substring(item.length).trim()
206199 // sanity check: ensure we made progress