(xml: string, options?: ParseOptions)
| 86 | * @throws {XmlSyntaxError} If the XML is malformed. |
| 87 | */ |
| 88 | export function parseSync(xml: string, options?: ParseOptions): XmlDocument { |
| 89 | const ignoreWhitespace = options?.ignoreWhitespace ?? false; |
| 90 | const ignoreComments = options?.ignoreComments ?? false; |
| 91 | const trackPosition = options?.trackPosition ?? true; |
| 92 | const disallowDoctype = options?.disallowDoctype ?? true; |
| 93 | const maxDepth = options?.maxDepth ?? Infinity; |
| 94 | const maxAttributes = options?.maxAttributes ?? Infinity; |
| 95 | const xml11 = options?.xmlVersion === "1.1"; |
| 96 | |
| 97 | // Normalize line endings (XML 1.0 §2.11 / XML 1.1 §2.11) |
| 98 | const version = xml11 ? "1.1" : "1.0"; |
| 99 | const needsNormalization = xml11 |
| 100 | ? xml.includes("\r") || xml.includes("\x85") || xml.includes("\u2028") |
| 101 | : xml.includes("\r"); |
| 102 | const input = needsNormalization |
| 103 | ? xml.replace(LINE_ENDING_REGEXP[version], "\n") |
| 104 | : xml; |
| 105 | const len = input.length; |
| 106 | |
| 107 | function isIllegalLiteralChar(code: number): boolean { |
| 108 | return isIllegalXmlLiteralChar(code, xml11); |
| 109 | } |
| 110 | |
| 111 | // Parser state - only track position offset, not line/column |
| 112 | let pos = 0; |
| 113 | |
| 114 | // Tree building state |
| 115 | const stack: MutableElement[] = []; |
| 116 | let root: MutableElement | undefined; |
| 117 | let declaration: XmlDeclarationEvent | undefined; |
| 118 | let rootClosed = false; // Track whether root element has been closed |
| 119 | |
| 120 | // Namespace tracking (lazy initialization for performance) |
| 121 | // Only created when first namespace prefix is encountered |
| 122 | // Using object wrapper to avoid TypeScript control flow narrowing issues with error() |
| 123 | const ns: { bindings: Map<string, string> | null } = { bindings: null }; |
| 124 | // Stack of namespace bindings per element scope (array of [prefix, previousUri] tuples) |
| 125 | // Used to restore bindings when element closes |
| 126 | const nsStack: Array<Array<[string, string | undefined]>> = []; |
| 127 | // Reusable empty array to avoid allocations for elements without namespace bindings |
| 128 | const EMPTY_NS_SCOPE: Array<[string, string | undefined]> = []; |
| 129 | |
| 130 | /** Get or create namespace bindings map with xml prefix pre-bound */ |
| 131 | function getNsBindings(): Map<string, string> { |
| 132 | if (!ns.bindings) { |
| 133 | ns.bindings = new Map([["xml", XML_NAMESPACE]]); |
| 134 | } |
| 135 | return ns.bindings; |
| 136 | } |
| 137 | |
| 138 | // Note: We do NOT expand custom entities from DTD. |
| 139 | // We only support the 5 predefined XML entities: lt, gt, amp, apos, quot. |
| 140 | // External entities (SYSTEM/PUBLIC) are also not supported. |
| 141 | |
| 142 | /** |
| 143 | * Compute line and column from offset on-demand (lazy position tracking). |
| 144 | * Only called when an error occurs, avoiding overhead during successful parsing. |
| 145 | */ |
no test coverage detected