Skip to content

Commit

Permalink
feat: add xmldecl event
Browse files Browse the repository at this point in the history
  • Loading branch information
lddubeau committed Jan 29, 2020
1 parent 2ee0b65 commit a2e677f
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 10 deletions.
16 changes: 15 additions & 1 deletion src/saxes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ const FORBIDDEN_BRACKET_BRACKET = 2;
* The list of supported events.
*/
export const EVENTS = [
"xmldecl",
"text",
"processinginstruction",
"doctype",
Expand All @@ -190,6 +191,7 @@ export const EVENTS = [
] as const;

const EVENT_NAME_TO_HANDLER_NAME: Record<EventName, string> = {
xmldecl: "xmldeclHandler",
text: "textHandler",
processinginstruction: "piHandler",
doctype: "doctypeHandler",
Expand All @@ -204,6 +206,14 @@ const EVENT_NAME_TO_HANDLER_NAME: Record<EventName, string> = {
ready: "readyHandler",
};

/**
* Event handler for the
*
* @param text The text data encountered by the parser.
*
*/
export type XMLDeclHandler = (decl: XMLDecl) => void;

/**
* Event handler for text data.
*
Expand Down Expand Up @@ -298,6 +308,7 @@ export type ErrorHandler = (err: Error) => void;

export type EventName = (typeof EVENTS)[number];
export type EventNameToHandler<O, N extends EventName> = {
"xmldecl": XMLDeclHandler;
"text": TextHandler;
"processinginstruction": PIHandler;
"doctype": DoctypeHandler;
Expand Down Expand Up @@ -347,7 +358,7 @@ export interface SaxesAttributeNS {
* prior to the URI being resolvable. This is what is passed to the attribute
* event handler.
*/
export type SaxesAttributeNSIncomplete = Exclude<SaxesAttributeNS, "uri">;
export type SaxesAttributeNSIncomplete = Exclude<SaxesAttributeNS, "uri">;

/**
* This interface defines the structure of attributes when the parser is
Expand Down Expand Up @@ -612,6 +623,7 @@ export class SaxesParser<O extends SaxesOptions = {}> {
private _closed!: boolean;
private currentXMLVersion!: string;
private readonly stateTable: ((this: SaxesParser<O>) => void)[];
private xmldeclHandler?: XMLDeclHandler;
private textHandler?: TextHandler;
private piHandler?: PIHandler;
private doctypeHandler?: DoctypeHandler;
Expand Down Expand Up @@ -1951,6 +1963,8 @@ export class SaxesParser<O extends SaxesOptions = {}> {
this.xmlDeclExpects.includes("version")) {
this.fail("XML declaration must contain a version.");
}
// eslint-disable-next-line no-unused-expressions
this.xmldeclHandler?.(this.xmlDecl);
this.name = "";
this.piTarget = this.text = "";
this.state = S_TEXT;
Expand Down
24 changes: 24 additions & 0 deletions test/dtd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ describe("dtd", () => {
]>
<root/>`,
expect: [
[
"xmldecl",
{
encoding: "UTF-8",
version: "1.0",
standalone: undefined,
},
],
["text", "\n"],
["doctype", ` root [
<!-- I'm a test. -->
Expand All @@ -30,6 +38,14 @@ describe("dtd", () => {
]>
<root/>`,
expect: [
[
"xmldecl",
{
encoding: "UTF-8",
version: "1.0",
standalone: undefined,
},
],
["text", "\n"],
["doctype", ` root [
<? I'm a test. ?>
Expand All @@ -50,6 +66,14 @@ describe("dtd", () => {
]>
<root/>`,
expect: [
[
"xmldecl",
{
encoding: "UTF-8",
version: "1.0",
standalone: undefined,
},
],
["text", "\n"],
["doctype", ` root [
<!NOTATION not1 SYSTEM "]>">
Expand Down
41 changes: 35 additions & 6 deletions test/eol-handling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ describe("eol handling", () => {
/* eslint-enable linebreak-style */

const expect = [
[
"xmldecl",
{
encoding: "utf-8",
version: "1.0",
standalone: undefined,
},
],
["text", "\n\n"],
["opentagstart", { name: "moo", attributes: {} }],
["attribute", {
Expand Down Expand Up @@ -124,6 +132,14 @@ a
const xmlDeclEnd = nl.indexOf("?>");

const expect = [
[
"xmldecl",
{
encoding: "utf-8",
version: "1.0",
standalone: "no",
},
],
["text", "\n"],
["doctype", `
root
Expand Down Expand Up @@ -200,7 +216,12 @@ abc
isSelfClosing: false,
}],
["text", "\n"],
];
] as const;

const fixed11 =
expect.map(x => (x[0] === "xmldecl" ?
[x[0], { ...x[1], version: "1.1" }] : x));


describe("nl", () => {
test({
Expand Down Expand Up @@ -266,17 +287,17 @@ abc
describe("nel", () => {
// We have to switch the EOL characters after the XML declaration.
const nel = nl.slice(0, xmlDeclEnd).replace("1.0", "1.1") +
nl.slice(xmlDeclEnd).replace(/\n/g, "\u0085");
nl.slice(xmlDeclEnd).replace(/\n/g, "\u0085");

test({
name: "one chunk",
xml: nel,
expect,
expect: fixed11,
});

test({
name: "char-by-char",
expect,
expect: fixed11,
fn(parser: SaxesParser): void {
for (const x of nel) {
parser.write(x);
Expand All @@ -294,12 +315,12 @@ abc
test({
name: "one chunk",
xml: ls,
expect,
expect: fixed11,
});

test({
name: "char-by-char",
expect,
expect: fixed11,
fn(parser: SaxesParser): void {
for (const x of ls) {
parser.write(x);
Expand All @@ -318,6 +339,14 @@ abc
"error",
"2:6: an XML declaration must be at the start of the document.",
],
[
"xmldecl",
{
encoding: "utf-8",
version: "1.0",
standalone: undefined,
},
],
["opentagstart", {
name: "doc",
attributes: {},
Expand Down
6 changes: 3 additions & 3 deletions test/testutil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { EventName, EVENTS, SaxesOptions,
SaxesParser } from "../build/dist/saxes";

export interface TestOptions {
xml?: string | string[];
xml?: string | readonly string[];
name: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
expect: any[];
expect: readonly any[];
fn?: (parser: SaxesParser<{}>) => void;
opt?: SaxesOptions;
events?: EventName[];
events?: readonly EventName[];
}

export function test(options: TestOptions): void {
Expand Down
48 changes: 48 additions & 0 deletions test/xml-declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ describe("xml declaration", () => {
xml: "<?xml?><root/>",
expect: [
["error", "1:7: XML declaration must contain a version."],
[
"xmldecl",
{
encoding: undefined,
version: undefined,
standalone: undefined,
},
],
["opentagstart", { name: "root", attributes: {}, ns: {} }],
[
"opentag",
Expand Down Expand Up @@ -55,6 +63,14 @@ describe("xml declaration", () => {
xml: "<?xml version?><root/>",
expect: [
["error", "1:14: XML declaration is incomplete."],
[
"xmldecl",
{
encoding: undefined,
version: undefined,
standalone: undefined,
},
],
["opentagstart", { name: "root", attributes: {}, ns: {} }],
[
"opentag",
Expand Down Expand Up @@ -91,6 +107,14 @@ describe("xml declaration", () => {
xml: "<?xml version=?><root/>",
expect: [
["error", "1:15: XML declaration is incomplete."],
[
"xmldecl",
{
encoding: undefined,
version: undefined,
standalone: undefined,
},
],
["opentagstart", { name: "root", attributes: {}, ns: {} }],
[
"opentag",
Expand Down Expand Up @@ -128,6 +152,14 @@ describe("xml declaration", () => {
expect: [
["error", "1:15: value must be quoted."],
["error", "1:16: XML declaration is incomplete."],
[
"xmldecl",
{
encoding: undefined,
version: undefined,
standalone: undefined,
},
],
["opentagstart", { name: "root", attributes: {}, ns: {} }],
[
"opentag",
Expand Down Expand Up @@ -164,6 +196,14 @@ describe("xml declaration", () => {
xml: "<?xml version=\"a?><root/>",
expect: [
["error", "1:17: XML declaration is incomplete."],
[
"xmldecl",
{
encoding: undefined,
version: undefined,
standalone: undefined,
},
],
["opentagstart", { name: "root", attributes: {}, ns: {} }],
[
"opentag",
Expand Down Expand Up @@ -200,6 +240,14 @@ describe("xml declaration", () => {
xml: "<?xml version=\"a\"?><root/>",
expect: [
["error", "1:17: version number must match /^1\\.[0-9]+$/."],
[
"xmldecl",
{
encoding: undefined,
version: "a",
standalone: undefined,
},
],
["opentagstart", { name: "root", attributes: {}, ns: {} }],
[
"opentag",
Expand Down

0 comments on commit a2e677f

Please sign in to comment.