From 779dc272210b1e5cc8fcf36ebfb8dee58ba6629a Mon Sep 17 00:00:00 2001 From: Yarchik Date: Thu, 23 Jul 2026 18:37:57 +0100 Subject: [PATCH] =?UTF-8?q?Don't=20reject=20a=20document=20that=20starts?= =?UTF-8?q?=20with=20a=20"<=3Fxml=E2=80=A6"=20processing=20instruction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `consumeXmlDeclaration` matched the 5-character literal ``, `` — was forced down the XML-declaration path and rejected with "Invalid XML declaration": parseXml('') // threw Those are valid documents: an XML declaration is `'`, whose bare `xml` target is reserved) is unchanged. --- src/lib/Parser.ts | 9 +++++++++ tests/lib/Parser.test.js | 6 +++--- tests/lib/XmlProcessingInstruction.test.js | 7 +++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/lib/Parser.ts b/src/lib/Parser.ts index 5df9805..82cd65f 100644 --- a/src/lib/Parser.ts +++ b/src/lib/Parser.ts @@ -712,6 +712,15 @@ export class Parser { return false; } + // ``); rewind and let it be parsed as a PI. + if (syntax.isNameChar(scanner.peek())) { + scanner.reset(startIndex); + return false; + } + if (!this.consumeWhitespace()) { throw this.error('Invalid XML declaration'); } diff --git a/tests/lib/Parser.test.js b/tests/lib/Parser.test.js index 0eca1a2..b443efb 100644 --- a/tests/lib/Parser.test.js +++ b/tests/lib/Parser.test.js @@ -178,13 +178,13 @@ describe('Parser', () => { describe('invalid XML declaration', () => { it('throws an error', () => { assert.throws(() => { - parseXml(''); }, { column: 6, - excerpt: '', line: 1, message: 'Invalid XML declaration (line 1, column 6)\n' + - ' \n' + ' ^\n', pos: 5, }); diff --git a/tests/lib/XmlProcessingInstruction.test.js b/tests/lib/XmlProcessingInstruction.test.js index ab8a24a..0394d77 100644 --- a/tests/lib/XmlProcessingInstruction.test.js +++ b/tests/lib/XmlProcessingInstruction.test.js @@ -16,6 +16,13 @@ describe('XmlProcessingInstruction', () => { assert.strictEqual(JSON.stringify(root.children[0]), `{"type":"pi","name":"xml-stylesheet","content":"type=\\"text/xsl\\" href=\\"style.xsl\\""}`); }); + it('may begin the document when its target starts with "xml"', () => { + let doc = parseXml(''); + assert(doc.children[0] instanceof XmlProcessingInstruction); + assert.strictEqual(doc.children[0].name, 'xml-stylesheet'); + assert.strictEqual(doc.root.name, 'root'); + }); + describe('constructor', () => { it('defaults the value of `content` to an empty string if not provided', () => { let pi = new XmlProcessingInstruction('foo');