Socket
Socket
Sign inDemoInstall

saxes

Package Overview
Dependencies
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

saxes - npm Package Compare versions

Comparing version 3.0.0 to 3.1.0

21

CHANGELOG.md

@@ -0,1 +1,22 @@

<a name="3.1.0"></a>
# [3.1.0](https://github.com/lddubeau/saxes/compare/v3.0.0...v3.1.0) (2018-08-28)
### Bug Fixes
* correct typo ([97bc5da](https://github.com/lddubeau/saxes/commit/97bc5da))
### Performance Improvements
* add emitNodes to skip checking text buffer more than needed ([9d5e357](https://github.com/lddubeau/saxes/commit/9d5e357))
* capture names in the ``name`` field ([c7dffd5](https://github.com/lddubeau/saxes/commit/c7dffd5))
* introduce a specialized version of captureWhile ([04855d6](https://github.com/lddubeau/saxes/commit/04855d6))
* introduce captureTo and captureToChar ([76eb95a](https://github.com/lddubeau/saxes/commit/76eb95a))
* remove skipWhitespace ([c8b7ae2](https://github.com/lddubeau/saxes/commit/c8b7ae2))
* remove some redundant buffer resets ([5ded326](https://github.com/lddubeau/saxes/commit/5ded326))
* use charCodeAt and handle surrogates ourselves ([b8ec232](https://github.com/lddubeau/saxes/commit/b8ec232))
<a name="3.0.0"></a>

@@ -2,0 +23,0 @@ # [3.0.0](https://github.com/lddubeau/saxes/compare/v2.2.1...v3.0.0) (2018-08-21)

737

lib/saxes.js

@@ -85,8 +85,2 @@ "use strict";

const buffers = [
"comment", "openWakaBang", "textNode", "tagName", "doctype", "piTarget",
"piBody", "entity", "attribName", "attribValue", "cdata", "xmlDeclName",
"xmlDeclValue",
];
const NL = 0xA;

@@ -114,2 +108,11 @@ const SPACE = 0x20;

const QUOTES = [DQUOTE, SQUOTE];
const S = [SPACE, NL, 0xD, 9];
const TEXT_TERMINATOR = [LESS, AMP];
const DOCTYPE_TERMINATOR = [...QUOTES, OPEN_BRACKET, GREATER];
const DOCTYPE_DTD_TERMINATOR = [...QUOTES, CLOSE_BRACKET];
const XML_DECL_NAME_TERMINATOR = [EQUAL, QUESTION, ...S];
const ATTRIB_VALUE_UNQUOTED_TERMINATOR = [...S, GREATER, AMP, LESS];
function isEntityStartChar(c) {

@@ -204,15 +207,2 @@ return isNameStartChar(c) || c === HASH;

/**
* @typedef ChunkState
*
* @private
*
* @property {string} chunk The chunk being read. This is readonly.
*
* @property {number} limit The size of the chunk. This is readonly.
*
* @property {number} i The offset into the chunk at which we are to read the
* next character.
*/
/**
* @typedef XMLDecl

@@ -275,5 +265,14 @@ *

_init(opt) {
for (const buffer of buffers) {
this[buffer] = "";
}
this.comment = "";
this.openWakaBang = "";
this.textNode = "";
this.name = "";
this.doctype = "";
this.piTarget = "";
this.piBody = "";
this.entity = "";
this.attribValue = "";
this.cdata = "";
this.xmlDeclName = "";
this.xmlDeclValue = "";

@@ -310,2 +309,5 @@ /**

this.tag = null;
this.chunk = "";
this.chunkPosition = 0;
this.i = 0;
/**

@@ -322,9 +324,10 @@ * A map of entity name to expansion.

this.state = this.opt.fragment ? S_TEXT : S_BEGIN_WHITESPACE;
const fragmentOpt = this.fragmentOpt = !!this.opt.fragment;
this.state = fragmentOpt ? S_TEXT : S_BEGIN_WHITESPACE;
// We want these to be all true if we are dealing with a fragment.
this.reportedTextBeforeRoot = this.reportedTextAfterRoot =
this.closedRoot = this.sawRoot = this.inRoot = this.opt.fragment;
this.closedRoot = this.sawRoot = this.inRoot = fragmentOpt;
// An XML declaration is intially possible only when parsing whole
// documents.
this.xmlDeclPossible = !this.opt.fragment;
this.xmlDeclPossible = !fragmentOpt;

@@ -341,4 +344,5 @@ this.piIsXMLDecl = false;

this.textNodeCheckedBefore = 0;
this.xmlnsOpt = !!this.opt.xmlns;
if (this.opt.xmlns) {
if (this.xmlnsOpt) {
this.ns = Object.assign({ __proto__: null }, rootNS);

@@ -353,17 +357,17 @@ const additional = this.opt.additionalNamespaces;

this.trackPosition = this.opt.position !== false;
if (this.trackPosition) {
/** The line number the parser is currently looking at. */
this.line = 1;
/** The line number the parser is currently looking at. */
this.line = 1;
/** The stream position the parser is currently looking at. */
this.position = 0;
/** The column the parser is currently looking at. */
this.column = 0;
/** The column the parser is currently looking at. */
this.column = 0;
this.fileName = this.opt.fileName;
}
this.fileName = this.opt.fileName;
this.onready();
}
/** The stream position the parser is currently looking at. */
get position() {
return this.chunkPosition + this.i;
}
/* eslint-disable class-methods-use-this */

@@ -498,11 +502,9 @@ /**

// ``Array.from`` but don't want to be dependent on Node.)
const limit = chunk.length;
const chunkState = {
chunk,
limit,
i: 0,
};
while (chunkState.i < limit) {
this[this.state].call(this, chunkState);
const limit = this.limit = chunk.length;
this.chunk = chunk;
this.i = 0;
while (this.i < limit) {
this[this.state]();
}
this.chunkPosition += limit;

@@ -528,9 +530,19 @@ return this;

*
* @param {ChunkState} chunkState The chunk state.
*
* @returns {number} The character read.
*/
getCode(chunkState) {
const code = chunkState.chunk.codePointAt(chunkState.i);
getCode() {
const { chunk, i } = this;
// Using charCodeAt and handling the surrogates ourselves is faster
// than using codePointAt.
let code = chunk.charCodeAt(i);
let skip = 1;
if (code >= 0xD800 && code <= 0xDBFF) {
skip = 2;
code = 0x10000 + ((code - 0xD800) * 0x400) +
(chunk.charCodeAt(i + 1) - 0xDC00);
}
this.i = i + skip;
if (!isChar(code)) {

@@ -540,15 +552,9 @@ this.fail("disallowed character.");

const skip = code <= 0xFFFF ? 1 : 2;
chunkState.i += skip;
if (code && this.trackPosition) {
this.position += skip;
if (code === NL) {
this.line++;
this.column = 0;
}
else {
this.column += skip;
}
if (code === NL) {
this.line++;
this.column = 0;
}
else {
this.column += skip;
}

@@ -570,13 +576,6 @@ return code;

/**
* Capture characters into a buffer while a condition is true. A sequence of
* ``write`` calls may require the capture of text into a buffer as multiple
* "fragments". For instance, given ``write("<x>Multiple")`` and
* ``write("parts</x>")``, the text which is part of the ``x`` element will be
* recorded in two steps: one recording ``"Multiple"`` and one recording
* ``"parts"``. These are two fragments.
* Capture characters into a buffer while a condition is true.
*
* @private
*
* @param {ChunkState} chunkState The current chunk state.
*
* @param {CharacterTest} test A test to perform on each character. The

@@ -590,10 +589,10 @@ * capture ends when the test returns false.

*/
captureWhile(chunkState, test, buffer) {
const { limit, chunk, i: start } = chunkState;
while (chunkState.i < limit) {
const c = this.getCode(chunkState);
captureWhile(test, buffer) {
const { chunk, limit, i: start } = this;
while (this.i < limit) {
const c = this.getCode();
if (!test(c)) {
// This is faster than adding codepoints one by one.
this[buffer] += chunk.substring(start,
chunkState.i - (c <= 0xFFFF ? 1 : 2));
this.i - (c <= 0xFFFF ? 1 : 2));
return c;

@@ -609,19 +608,77 @@ }

/**
* Skip characters while a condition is true.
* Capture characters into a buffer until encountering one of a set of
* characters.
*
* @private
*
* @param {ChunkState} chunkState Chunk information
* @param {number[]} chars An array of codepoints. Encountering a character in
* the array ends the capture.
*
* @param {CharacterTest} test A test to perform on each character. The skip
* ends when the test returns false.
* @param {string} buffer The name of the buffer to save into.
*
* @return {string|undefined} The character that made the capture end, or
* ``undefined`` if we hit the end of the chunk.
*/
captureTo(chars, buffer) {
const { chunk, limit, i: start } = this;
while (this.i < limit) {
const c = this.getCode();
if (chars.includes(c)) {
// This is faster than adding codepoints one by one.
this[buffer] += chunk.substring(start,
this.i - (c <= 0xFFFF ? 1 : 2));
return c;
}
}
// This is faster than adding codepoints one by one.
this[buffer] += chunk.substring(start);
return undefined;
}
/**
* Capture characters into a buffer until encountering a character.
*
* @private
*
* @param {number} char The codepoint that ends the capture.
*
* @param {string} buffer The name of the buffer to save into.
*
* @return {boolean} ``true`` if we ran into the character. Otherwise, we ran
* into the end of the current chunk.
*/
captureToChar(char, buffer) {
const { chunk, limit, i: start } = this;
while (this.i < limit) {
const c = this.getCode();
if (c === char) {
// This is faster than adding codepoints one by one.
this[buffer] += chunk.substring(start,
this.i - (c <= 0xFFFF ? 1 : 2));
return true;
}
}
// This is faster than adding codepoints one by one.
this[buffer] += chunk.substring(start);
return false;
}
/**
* Capture characters that satisfy ``isNameChar`` into a buffer.
*
* @private
*
* @return {string|undefined} The character that made the test fail, or
* ``undefined`` if we hit the end of the chunk.
*/
skipWhile(chunkState, test) {
const { limit } = chunkState;
while (chunkState.i < limit) {
const c = this.getCode(chunkState);
if (!test(c)) {
captureName() {
const { chunk, limit, i: start } = this;
while (this.i < limit) {
const c = this.getCode();
if (!isNameChar(c)) {
// This is faster than adding codepoints one by one.
this.name += chunk.substring(start,
this.i - (c <= 0xFFFF ? 1 : 2));
return c;

@@ -631,2 +688,4 @@ }

// This is faster than adding codepoints one by one.
this.name += chunk.substring(start);
return undefined;

@@ -636,7 +695,8 @@ }

/**
* Skip whitespace characters.
* Skip characters while a condition is true.
*
* @private
*
* @param {ChunkState} chunkState The current chunk state.
* @param {CharacterTest} test A test to perform on each character. The skip
* ends when the test returns false.
*

@@ -646,7 +706,7 @@ * @return {string|undefined} The character that made the test fail, or

*/
skipWhitespace(chunkState) {
const { limit } = chunkState;
while (chunkState.i < limit) {
const c = this.getCode(chunkState);
if (!isS(c)) {
skipWhile(test) {
const { limit } = this;
while (this.i < limit) {
const c = this.getCode();
if (!test(c)) {
return c;

@@ -659,15 +719,14 @@ }

// STATE HANDLERS
/** @private */
sBeginWhitespace(chunkState) {
const { limit } = chunkState;
let c = this.getCode(chunkState);
sBeginWhitespace() {
const { limit } = this;
let c = this.getCode();
if (this.initial && c === 0xFEFF) {
this.initial = false;
if (chunkState.i >= limit) {
if (this.i >= limit) {
return;
}
c = this.getCode(chunkState);
c = this.getCode();
}

@@ -679,4 +738,4 @@ else {

// read character first.
while (chunkState.i < limit && isS(c)) {
c = this.getCode(chunkState);
while (this.i < limit && isS(c)) {
c = this.getCode();
this.xmlDeclPossible = false;

@@ -702,6 +761,4 @@ }

/** @private */
sText(chunkState) {
const c = this.captureWhile(chunkState,
cx => cx !== LESS && cx !== AMP,
"textNode");
sText() {
const c = this.captureTo(TEXT_TERMINATOR, "textNode");

@@ -746,8 +803,8 @@ if (!this.inRoot && (/\S/.test(this.textNode) || c === AMP)) {

/** @private */
sOpenWaka(chunkState) {
const c = this.getCode(chunkState);
sOpenWaka() {
const c = this.getCode();
// either a /, ?, !, or text is coming next.
if (isNameStartChar(c)) {
this.state = S_OPEN_TAG;
this.tagName = String.fromCodePoint(c);
this.name = String.fromCodePoint(c);
this.xmlDeclPossible = false;

@@ -759,3 +816,2 @@ }

this.state = S_CLOSE_TAG;
this.tagName = "";
this.xmlDeclPossible = false;

@@ -770,3 +826,2 @@ break;

this.state = S_PI;
this.piTarget = this.piBody = "";
break;

@@ -782,4 +837,4 @@ default:

/** @private */
sOpenWakaBang(chunkState) {
const c = String.fromCodePoint(this.getCode(chunkState));
sOpenWakaBang() {
const c = String.fromCodePoint(this.getCode());
this.openWakaBang += c;

@@ -799,7 +854,5 @@ switch (this.openWakaBang) {

this.openWakaBang = "";
this.cdata = "";
break;
case "--":
this.state = S_COMMENT;
this.comment = "";
this.openWakaBang = "";

@@ -812,3 +865,2 @@ break;

}
this.doctype = "";
this.openWakaBang = "";

@@ -826,7 +878,4 @@ break;

/** @private */
sDoctype(chunkState) {
const c = this.captureWhile(chunkState,
cx => cx !== OPEN_BRACKET && !isQuote(cx) &&
cx !== GREATER,
"doctype");
sDoctype() {
const c = this.captureTo(DOCTYPE_TERMINATOR, "doctype");
if (c === GREATER) {

@@ -850,19 +899,14 @@ this.state = S_TEXT;

/** @private */
sDoctypeQuoted(chunkState) {
sDoctypeQuoted() {
const { q } = this;
const c = this.captureWhile(chunkState, cx => cx !== q, "doctype");
if (!c || c !== q) {
return;
if (this.captureToChar(q, "doctype")) {
this.doctype += String.fromCodePoint(q);
this.q = null;
this.state = S_DOCTYPE;
}
this.doctype += String.fromCodePoint(c);
this.q = null;
this.state = S_DOCTYPE;
}
/** @private */
sDoctypeDTD(chunkState) {
const c = this.captureWhile(chunkState,
cx => cx !== CLOSE_BRACKET && !isQuote(cx),
"doctype");
sDoctypeDTD() {
const c = this.captureTo(DOCTYPE_DTD_TERMINATOR, "doctype");
if (!c) {

@@ -883,11 +927,6 @@ return;

/** @private */
sDoctypeDTDQuoted(chunkState) {
sDoctypeDTDQuoted() {
const { q } = this;
const c = this.captureWhile(chunkState, cx => cx !== q, "doctype");
if (!c) {
return;
}
this.doctype += String.fromCodePoint(c);
if (c === q) {
if (this.captureToChar(q, "doctype")) {
this.doctype += String.fromCodePoint(q);
this.state = S_DOCTYPE_DTD;

@@ -899,15 +938,11 @@ this.q = null;

/** @private */
sComment(chunkState) {
const c = this.captureWhile(chunkState, cx => cx !== MINUS, "comment");
if (c === MINUS) {
sComment() {
if (this.captureToChar(MINUS, "comment")) {
this.state = S_COMMENT_ENDING;
}
else if (c) {
this.comment += String.fromCodePoint(c);
}
}
/** @private */
sCommentEnding(chunkState) {
const c = this.getCode(chunkState);
sCommentEnding() {
const c = this.getCode();
if (c === MINUS) {

@@ -925,4 +960,4 @@ this.state = S_COMMENT_ENDED;

/** @private */
sCommentEnded(chunkState) {
const c = this.getCode(chunkState);
sCommentEnded() {
const c = this.getCode();
if (c !== GREATER) {

@@ -940,19 +975,11 @@ this.fail("malformed comment.");

sCData(chunkState) {
const c = this.captureWhile(chunkState, cx => cx !== CLOSE_BRACKET, "cdata");
if (!c) {
return;
}
if (c === CLOSE_BRACKET) {
sCData() {
if (this.captureToChar(CLOSE_BRACKET, "cdata")) {
this.state = S_CDATA_ENDING;
}
else {
this.cdata += String.fromCodePoint(c);
}
}
/** @private */
sCDataEnding(chunkState) {
const c = this.getCode(chunkState);
sCDataEnding() {
const c = this.getCode();
if (c === CLOSE_BRACKET) {

@@ -968,4 +995,4 @@ this.state = S_CDATA_ENDING_2;

/** @private */
sCDataEnding2(chunkState) {
const c = this.getCode(chunkState);
sCDataEnding2() {
const c = this.getCode();
switch (c) {

@@ -987,3 +1014,3 @@ case GREATER:

/** @private */
sPI(chunkState) {
sPI() {
// We have to perform the isNameStartChar check here because we do not feed

@@ -993,11 +1020,10 @@ // the first character in piTarget elsehwere.

const c = this.captureWhile(
chunkState,
// When namespaces are used, colons are not allowed in pi targets
// names.
// https://www.w3.org/XML/xml-names-19990114-errata.html
// NE08
this.xmlnsOpt ?
(cx) => {
if (cx !== QUESTION && !isS(cx)) {
if (!(check(cx) &&
// When namespaces are used, colons are not allowed in entity
// names.
// https://www.w3.org/XML/xml-names-19990114-errata.html
// NE08
(!this.opt.xmlns || cx !== COLON))) {
if (!(check(cx) && cx !== COLON)) {
this.fail("disallowed characer in processing instruction name.");

@@ -1011,2 +1037,14 @@ }

return false;
} :
(cx) => {
if (cx !== QUESTION && !isS(cx)) {
if (!check(cx)) {
this.fail("disallowed characer in processing instruction name.");
}
check = isNameChar;
return true;
}
return false;
},

@@ -1027,3 +1065,3 @@ "piTarget");

/** @private */
sPIBody(chunkState) {
sPIBody() {
let c;

@@ -1033,3 +1071,3 @@ if (this.piIsXMLDecl) {

case S_XML_DECL_NAME_START:
c = this.skipWhile(chunkState, (cx) => {
c = this.skipWhile((cx) => {
if (isS(cx)) {

@@ -1063,5 +1101,3 @@ this.requiredSeparator = undefined;

case S_XML_DECL_NAME:
c = this.captureWhile(chunkState,
cx => cx !== QUESTION && !isS(cx) && cx !== EQUAL,
"xmlDeclName");
c = this.captureTo(XML_DECL_NAME_TERMINATOR, "xmlDeclName");
// The question mark character is not valid inside any of the XML

@@ -1092,3 +1128,3 @@ // declaration name/value pairs.

case S_XML_DECL_EQ:
c = this.skipWhitespace(chunkState);
c = this.getCode();
// The question mark character is not valid inside any of the XML

@@ -1101,3 +1137,3 @@ // declaration name/value pairs.

if (c) {
if (c && !isS(c)) {
if (c !== EQUAL) {

@@ -1110,3 +1146,3 @@ this.fail("value required.");

case S_XML_DECL_VALUE_START:
c = this.skipWhitespace(chunkState);
c = this.getCode();
// The question mark character is not valid inside any of the XML

@@ -1119,3 +1155,3 @@ // declaration name/value pairs.

if (c) {
if (c && !isS(c)) {
if (!isQuote(c)) {

@@ -1129,9 +1165,6 @@ this.fail("value must be quoted.");

this.xmlDeclState = S_XML_DECL_VALUE;
this.xmlDeclValue = "";
}
break;
case S_XML_DECL_VALUE:
c = this.captureWhile(chunkState,
cx => cx !== QUESTION && cx !== this.q,
"xmlDeclValue");
c = this.captureTo([this.q, QUESTION], "xmlDeclValue");

@@ -1184,17 +1217,14 @@ // The question mark character is not valid inside any of the XML

else if (this.piBody.length === 0) {
c = this.skipWhitespace(chunkState);
c = this.getCode();
if (c === QUESTION) {
this.state = S_PI_ENDING;
}
else if (c) {
else if (c && !isS(c)) {
this.piBody = String.fromCodePoint(c);
}
}
else {
c = this.captureWhile(chunkState, cx => cx !== QUESTION, "piBody");
// The question mark character is not valid inside any of the XML
// declaration name/value pairs.
if (c === QUESTION) {
this.state = S_PI_ENDING;
}
// The question mark character is not valid inside any of the XML
// declaration name/value pairs.
else if (this.captureToChar(QUESTION, "piBody")) {
this.state = S_PI_ENDING;
}

@@ -1204,4 +1234,4 @@ }

/** @private */
sPIEnding(chunkState) {
const c = this.getCode(chunkState);
sPIEnding() {
const c = this.getCode();
if (this.piIsXMLDecl) {

@@ -1220,2 +1250,3 @@ if (c === GREATER) {

this.requiredSeparator = undefined;
this.piTarget = this.piBody = "";
this.state = S_TEXT;

@@ -1260,19 +1291,4 @@ }

/** @private */
sOpenTag(chunkState) {
// We don't need to check with isNameStartChar here because the first
// character of tagName is fed elsewhere, and the check is done there.
const c = this.captureWhile(
chunkState,
(cx) => {
if (cx !== GREATER && !isS(cx) && cx !== FORWARD_SLASH) {
if (!isNameChar(cx)) {
this.fail("disallowed characer in tag name.");
}
return true;
}
return false;
},
"tagName");
sOpenTag() {
const c = this.captureName();
if (!c) {

@@ -1283,11 +1299,10 @@ return;

const tag = this.tag = {
name: this.tagName,
name: this.name,
attributes: Object.create(null),
};
if (this.opt.xmlns) {
if (this.xmlnsOpt) {
tag.ns = Object.create(null);
}
this.attribList = [];
this.emitNode("onopentagstart", tag);

@@ -1311,7 +1326,6 @@

/** @private */
sOpenTagSlash(chunkState) {
const c = this.getCode(chunkState);
sOpenTagSlash() {
const c = this.getCode();
if (c === GREATER) {
this.openTag(true);
this.closeTag();
}

@@ -1325,9 +1339,9 @@ else {

/** @private */
sAttrib(chunkState) {
const c = this.skipWhitespace(chunkState);
if (!c) {
sAttrib() {
const c = this.getCode();
if (!c || isS(c)) {
return;
}
if (isNameStartChar(c)) {
this.attribName = String.fromCodePoint(c);
this.name = String.fromCodePoint(c);
this.attribValue = "";

@@ -1348,19 +1362,6 @@ this.state = S_ATTRIB_NAME;

/** @private */
sAttribName(chunkState) {
sAttribName() {
// We don't need to check with isNameStartChar here because the first
// character of attribute is fed elsewhere, and the check is done there.
const c = this.captureWhile(
chunkState,
(cx) => {
if (cx !== EQUAL && !isS(cx) && cx !== GREATER) {
if (!isNameChar(cx)) {
this.fail("disallowed characer in attribute name.");
}
return true;
}
return false;
},
"attribName");
const c = this.captureName();
if (c === EQUAL) {

@@ -1374,4 +1375,4 @@ this.state = S_ATTRIB_VALUE;

this.fail("attribute without value.");
this.attribList.push([this.attribName, this.attribName]);
this.attribName = this.attribValue = "";
this.attribList.push({ name: this.name, value: this.name });
this.name = this.attribValue = "";
this.openTag();

@@ -1385,4 +1386,8 @@ }

/** @private */
sAttribNameSawWhite(chunkState) {
const c = this.skipWhitespace(chunkState);
sAttribNameSawWhite() {
const c = this.getCode();
if (isS(c)) {
return;
}
if (c === EQUAL) {

@@ -1393,5 +1398,5 @@ this.state = S_ATTRIB_VALUE;

this.fail("attribute without value.");
this.tag.attributes[this.attribName] = "";
this.tag.attributes[this.name] = "";
this.attribValue = "";
this.attribName = "";
this.name = "";
if (c === GREATER) {

@@ -1401,3 +1406,3 @@ this.openTag();

else if (isNameStartChar(c)) {
this.attribName = String.fromCodePoint(c);
this.name = String.fromCodePoint(c);
this.state = S_ATTRIB_NAME;

@@ -1413,4 +1418,4 @@ }

/** @private */
sAttribValue(chunkState) {
const c = this.skipWhitespace(chunkState);
sAttribValue() {
const c = this.getCode();
if (isQuote(c)) {

@@ -1420,3 +1425,3 @@ this.q = c;

}
else if (c) {
else if (c && !isS(c)) {
this.fail("unquoted attribute value.");

@@ -1429,13 +1434,4 @@ this.state = S_ATTRIB_VALUE_UNQUOTED;

/** @private */
sAttribValueQuoted(chunkState) {
const { q } = this;
const c = this.captureWhile(
chunkState,
(cx) => {
if (cx === LESS) {
this.fail("disallowed character.");
}
return cx !== q && cx !== AMP;
},
"attribValue");
sAttribValueQuoted() {
const c = this.captureTo([this.q, AMP, LESS], "attribValue");
if (c === AMP) {

@@ -1446,2 +1442,5 @@ this.state = S_ENTITY;

}
else if (c === LESS) {
this.fail("disallowed character.");
}
else if (c) {

@@ -1451,4 +1450,4 @@ if (this.attribValue.includes("]]>")) {

}
this.attribList.push([this.attribName, this.attribValue]);
this.attribName = this.attribValue = "";
this.attribList.push({ name: this.name, value: this.attribValue });
this.name = this.attribValue = "";
this.q = null;

@@ -1460,4 +1459,4 @@ this.state = S_ATTRIB_VALUE_CLOSED;

/** @private */
sAttribValueClosed(chunkState) {
const c = this.getCode(chunkState);
sAttribValueClosed() {
const c = this.getCode();
if (isS(c)) {

@@ -1468,3 +1467,3 @@ this.state = S_ATTRIB;

this.fail("no whitespace between attributes.");
this.attribName = String.fromCodePoint(c);
this.name = String.fromCodePoint(c);
this.attribValue = "";

@@ -1485,12 +1484,5 @@ this.state = S_ATTRIB_NAME;

/** @private */
sAttribValueUnquoted(chunkState) {
const c = this.captureWhile(
chunkState,
(cx) => {
if (cx === LESS) {
this.fail("disallowed character.");
}
return cx !== GREATER && cx !== AMP && !isS(cx);
},
"attribValue");
sAttribValueUnquoted() {
const c = this.captureTo(ATTRIB_VALUE_UNQUOTED_TERMINATOR,
"attribValue");
if (c === AMP) {

@@ -1501,2 +1493,5 @@ this.state = S_ENTITY;

}
else if (c === LESS) {
this.fail("disallowed character.");
}
else if (c) {

@@ -1506,4 +1501,4 @@ if (this.attribValue.includes("]]>")) {

}
this.attribList.push([this.attribName, this.attribValue]);
this.attribName = this.attribValue = "";
this.attribList.push({ name: this.name, value: this.attribValue });
this.name = this.attribValue = "";
if (c === GREATER) {

@@ -1519,6 +1514,4 @@ this.openTag();

/** @private */
sCloseTag(chunkState) {
const c = this.captureWhile(chunkState,
cx => cx !== GREATER && !isS(cx),
"tagName");
sCloseTag() {
const c = this.captureName();
if (c === GREATER) {

@@ -1530,11 +1523,14 @@ this.closeTag();

}
else if (c) {
this.fail("disallowed character in closing tag.");
}
}
/** @private */
sCloseTagSawWhite(chunkState) {
const c = this.skipWhitespace(chunkState);
sCloseTagSawWhite() {
const c = this.getCode();
if (c === GREATER) {
this.closeTag();
}
else if (c) {
else if (c && !isS(c)) {
this.fail("disallowed character in closing tag.");

@@ -1545,12 +1541,12 @@ }

/** @private */
sEntity(chunkState) {
sEntity() {
let check = this.entity.length === 0 ? isEntityStartChar : isNameChar;
const c = this.captureWhile(chunkState,
const c = this.captureWhile(
// When namespaces are used, colons are
// not valid in entity names.
// https://www.w3.org/XML/xml-names-19990114-errata.html
// NE08
this.xmlnsOpt ?
(cx) => {
if (check(cx) &&
// When namespaces are used, colons are
// not valid in entity names.
// https://www.w3.org/XML/xml-names-19990114-errata.html
// NE08
(!this.opt.xmlns || cx !== COLON)) {
if (check(cx) && cx !== COLON) {
check = isNameChar;

@@ -1561,2 +1557,9 @@ return true;

return false;
} : (cx) => {
if (check(cx)) {
check = isNameChar;
return true;
}
return false;
},

@@ -1566,3 +1569,3 @@ "entity");

if (c === SEMICOLON) {
this[this.entityBufferName] += this.parseEntity();
this[this.entityBufferName] += this.parseEntity(this.entity);
if (this.entityBufferName === "textNode") {

@@ -1640,2 +1643,31 @@ this.textNodeCheckedBefore = this.textNode.length;

/**
* Emit any buffered text. Then emit the specified node types.
*
* @param {string} nodeTypeA The node type to emit.
*
* @param {string} nodeTypeB The node type to emit.
*
* @param {string} data The data associated with the node type.
*
* @private
*/
emitNodes(nodeTypeA, nodeTypeB, data) {
if (this.textNode) {
this.closeText();
}
this[nodeTypeA](data);
this[nodeTypeB](data);
}
/**
* Resolve a namespace prefix.
*
* @param {string} prefix The prefix to resolve.
*
* @returns {string|undefined} The namespace URI or ``undefined`` if the
* prefix is not defined.
*
* @private
*/
resolve(prefix) {

@@ -1675,18 +1707,20 @@ let uri = this.tag.ns[prefix];

const colon = name.indexOf(":");
let prefix;
let local;
if (colon < 0) {
prefix = "";
local = name;
return { prefix: "", local: name };
}
else {
// A colon at the start of the name is illegal.
if (colon === 0) {
this.fail(`malformed name: ${name}.`);
}
prefix = name.substr(0, colon);
local = name.substr(colon + 1);
// A colon at the start of the name is illegal.
if (colon === 0) {
this.fail(`malformed name: ${name}.`);
}
return { prefix, local };
const local = name.substring(colon + 1);
if (local.indexOf(":") !== -1) {
this.fail(`malformed name: ${name}.`);
}
return {
prefix: name.substring(0, colon),
local,
};
}

@@ -1705,12 +1739,13 @@

const { tag, attribList } = this;
if (this.opt.xmlns) {
const { name: tagName, attributes } = tag;
if (this.xmlnsOpt) {
// emit namespace binding events
const { ns, attributes } = tag;
for (const [name, uri] of attribList) {
const { ns } = tag;
for (const { name, value } of attribList) {
const { prefix, local } = this.qname(name);
if (prefix === "xmlns") {
ns[local] = uri.trim();
ns[local] = value.trim();
}
else if (name === "xmlns") {
ns[""] = uri.trim();
ns[""] = value.trim();
}

@@ -1723,3 +1758,3 @@ }

// add namespace info to tag
const { prefix, local } = this.qname(this.tagName);
const { prefix, local } = this.qname(tagName);
tag.prefix = prefix;

@@ -1744,12 +1779,27 @@ tag.local = local;

// http://www.w3.org/TR/REC-xml-names/#defaulting
for (const [name, value] of attribList) {
const { prefix, local } = this.qname(name, true);
for (const { name, value } of attribList) {
const { prefix, local } = this.qname(name);
let uri;
let eqname;
if (prefix === "") {
uri = (name === "xmlns") ? XMLNS_NAMESPACE : "";
eqname = name;
}
else {
uri = this.resolve(prefix) || "";
uri = this.resolve(prefix);
// if there's any attributes with an undefined namespace,
// then fail on them now.
if (!uri) {
this.fail(`unbound namespace prefix: ${JSON.stringify(prefix)}.`);
uri = prefix;
}
eqname = `{${uri}}${local}`;
}
const a = {
if (seen.has(eqname)) {
this.fail(`duplicate attribute: ${eqname}.`);
}
seen.add(eqname);
attributes[name] = {
name,

@@ -1761,21 +1811,6 @@ value,

};
const eqname = `{${uri}}${local}`;
if (seen.has(eqname)) {
this.fail(`duplicate attribute: ${eqname}.`);
}
seen.add(eqname);
// if there's any attributes with an undefined namespace,
// then fail on them now.
if (prefix && !uri) {
this.fail(`unbound namespace prefix: ${JSON.stringify(prefix)}.`);
a.uri = prefix;
}
attributes[name] = a;
}
}
else {
const { attributes } = this.tag;
for (const [name, value] of attribList) {
for (const { name, value } of attribList) {
if (attributes[name]) {

@@ -1789,20 +1824,25 @@ this.fail(`duplicate attribute: ${name}.`);

tag.isSelfClosing = !!selfClosing;
selfClosing = !!selfClosing;
tag.isSelfClosing = selfClosing;
// process the tag
if (!this.opt.fragment && this.closedRoot) {
if (!this.fragmentOpt && this.closedRoot) {
this.fail("documents may contain only one root.");
}
this.sawRoot = true;
const { tags } = this;
if (selfClosing) {
this.emitNodes("onopentag", "onclosetag", tag);
const top = this.tag = tags[tags.length - 1];
if (!top) {
this.closedRoot = true;
}
}
else {
this.emitNode("onopentag", tag);
this.inRoot = true;
tags.push(tag);
}
this.sawRoot = true;
this.tags.push(tag);
this.emitNode("onopentag", tag);
if (!selfClosing) {
this.state = S_TEXT;
this.tag = null;
this.tagName = "";
}
this.attribName = this.attribValue = "";
this.state = S_TEXT;
this.name = "";
}

@@ -1818,3 +1858,3 @@

closeTag() {
const { tags, tagName } = this;
const { tags, name } = this;

@@ -1824,5 +1864,5 @@ // Our state after this will be S_TEXT, no matter what, and we can clear

this.state = S_TEXT;
this.tagName = "";
this.name = "";
if (!tagName) {
if (!name) {
this.fail("weird empty close tag.");

@@ -1837,3 +1877,3 @@ this.textNode += "</>";

this.emitNode("onclosetag", tag);
if (tag.name !== tagName) {
if (tag.name !== name) {
this.fail("unexpected close tag.");

@@ -1851,4 +1891,4 @@ }

else if (l < 0) {
this.fail(`unmatched closing tag: ${tagName}.`);
this.textNode += `</${tagName}>`;
this.fail(`unmatched closing tag: ${name}.`);
this.textNode += `</${name}>`;
}

@@ -1858,12 +1898,11 @@ }

/**
* Resolves an entity stored in the ``entity`` buffer. Makes any necessary
* well-formedness checks.
* Resolves an entity. Makes any necessary well-formedness checks.
*
* @private
*
* @param {string} entity The entity to resolve.
*
* @returns {string} The parsed entity.
*/
parseEntity() {
const { entity } = this;
parseEntity(entity) {
const defined = this.ENTITIES[entity];

@@ -1870,0 +1909,0 @@ if (defined) {

@@ -5,3 +5,3 @@ {

"author": "Louis-Dominique Dubeau <ldd@lddubeau.com>",
"version": "3.0.0",
"version": "3.1.0",
"main": "lib/saxes.js",

@@ -8,0 +8,0 @@ "types": "lib/saxes.d.ts",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc