Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

saxophone

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

saxophone - npm Package Compare versions

Comparing version 0.4.3 to 0.5.0

2

benchmark/index.js
'use strict';
require('babel-register');
// benchmarking libraries

@@ -6,0 +4,0 @@ const Benchmark = require('benchmark');

5

benchmark/require-failsafe.js

@@ -34,4 +34,5 @@ 'use strict';

console.error(
'/!\\ To run benchmarks, please install the following ' +
'missing modules:\n npm install ' + notFound.join(' ')
'/!\\ To run benchmarks, please install the following '
+ 'missing modules:\n npm install --no-save '
+ notFound.join(' ')
);

@@ -38,0 +39,0 @@ process.exit();

@@ -0,3 +1,10 @@

<!-- vim: set spelllang=en : -->
# Changelog
## v0.5.0
### New features
* Report an error when opening and closing tags are mismatched or omitted.
## v0.4.3

@@ -4,0 +11,0 @@

@@ -0,1 +1,2 @@

<!-- vim: set spelllang=en : -->
# Contributing

@@ -2,0 +3,0 @@

const {Writable} = require('readable-stream');
const {StringDecoder} = require('string_decoder');
const TOKENS = {
TEXT: 'text',
CDATA: 'cdata',
COMMENT: 'comment',
PROCESSINGINSTRUCTION: 'processinginstruction',
TAGOPEN: 'tagopen',
TAGCLOSE: 'tagclose'
/**
* Information about a text node.
*
* @typedef TextNode
* @type {object}
* @prop {string} contents The text value.
*/
/**
* Emitted whenever a text node is encountered.
*
* @event Saxophone#text
* @type {TextNode}
*/
/**
* Information about a CDATA node
* (<![CDATA[ ... ]]>).
*
* @typedef CDATANode
* @type {object}
* @prop {string} contents The CDATA contents.
*/
/**
* Emitted whenever a CDATA node is encountered.
*
* @event Saxophone#cdata
* @type {CDATANode}
*/
/**
* Information about a comment node
* (<!-- ... -->).
*
* @typedef CommentNode
* @type {object}
* @prop {string} contents The comment contents
*/
/**
* Emitted whenever a comment node is encountered.
*
* @event Saxophone#comment
* @type {CommentNode}
*/
/**
* Information about a processing instruction node
* (<? ... ?>).
*
* @typedef ProcessingInstructionNode
* @type {object}
* @prop {string} contents The instruction contents
*/
/**
* Emitted whenever a processing instruction node is encountered.
*
* @event Saxophone#processinginstruction
* @type {ProcessingInstructionNode}
*/
/**
* Information about an opened tag
* (<tag attr="value">).
*
* @typedef TagOpenNode
* @type {object}
* @prop {string} name Name of the tag that was opened.
* @prop {string} attrs Attributes passed to the tag, in a string representation
* (use Saxophone.parseAttributes to get an attribute-value mapping).
* @prop {bool} isSelfClosing Whether the tag self-closes (tags of the form
* `<tag />`). Such tags will not be followed by a closing tag.
*/
/**
* Emitted whenever an opening tag node is encountered.
*
* @event Saxophone#tagopen
* @type {TagOpen}
*/
/**
* Information about a closed tag
* (</tag>).
*
* @typedef TagCloseNode
* @type {object}
* @prop {string} name The tag name
*/
/**
* Emitted whenever a closing tag node is encountered.
*
* @event Saxophone#tagclose
* @type {TagCloseNode}
*/
/**
* Nodes that can be found inside an XML stream.
* @private
*/
const Node = {
text: 'text',
cdata: 'cdata',
comment: 'comment',
processingInstruction: 'processinginstruction',
tagOpen: 'tagopen',
tagClose: 'tagclose',
};

@@ -40,2 +143,5 @@

// Stack of tags that were opened up until the current cursor position
this._tagStack = [];
// Not stalled initially

@@ -70,2 +176,18 @@ this._stall(null);

/**
* Handle the opening of a tag in the text stream.
*
* Push the tag into the opened tag stack and emit the
* corresponding event on the event emitter.
*
* @param {TagOpen} node Information about the opened tag.
*/
_handleTagOpening(node) {
if (!node.isSelfClosing) {
this._tagStack.push(node.name);
}
this.emit(Node.tagOpen, node);
}
/**
* Parse a XML chunk.

@@ -93,3 +215,3 @@ *

this._stall(
TOKENS.TEXT,
Node.text,
input.slice(chunkPos)

@@ -102,12 +224,4 @@ );

// we have all the data needed for the TEXT node
/**
* Text token event
*
* @event Saxophone#text
* @type {object}
* @prop {string} contents The text value
*/
this.emit(
TOKENS.TEXT,
Node.text,
{contents: input.slice(chunkPos, nextTag)}

@@ -140,3 +254,3 @@ );

this._stall(
TOKENS.CDATA,
Node.cdata,
input.slice(chunkPos - 9)

@@ -147,12 +261,4 @@ );

/**
* CDATA token event
* (<![CDATA[ ... ]]>)
*
* @event Saxophone#cdata
* @type {object}
* @prop {string} contents The CDATA contents
*/
this.emit(
TOKENS.CDATA,
Node.cdata,
{contents: input.slice(chunkPos, cdataClose)}

@@ -173,3 +279,3 @@ );

this._stall(
TOKENS.COMMENT,
Node.comment,
input.slice(chunkPos - 4)

@@ -185,12 +291,4 @@ );

/**
* Comment token event
* (<!-- ... -->)
*
* @event Saxophone#comment
* @type {object}
* @prop {string} contents The comment contents
*/
this.emit(
TOKENS.COMMENT,
Node.comment,
{contents: input.slice(chunkPos, commentClose)}

@@ -216,3 +314,3 @@ );

this._stall(
TOKENS.PROCESSINGINSTRUCTION,
Node.processingInstruction,
input.slice(chunkPos - 2)

@@ -223,12 +321,4 @@ );

/**
* Processing instruction token event
* (<? ... ?>)
*
* @event Saxophone#processinginstruction
* @type {object}
* @prop {string} contents The instruction contents
*/
this.emit(
TOKENS.PROCESSINGINSTRUCTION,
Node.processingInstruction,
{contents: input.slice(chunkPos, piClose)}

@@ -246,3 +336,3 @@ );

this._stall(
TOKENS.TAGOPEN,
Node.tagOpen,
input.slice(chunkPos - 1)

@@ -255,13 +345,14 @@ );

if (input[chunkPos] === '/') {
/**
* Closing tag token event
* (</tag>)
*
* @event Saxophone#tagclose
* @type {object}
* @prop {string} name The tag name
*/
const tagName = input.slice(chunkPos + 1, tagClose);
const stackedTagName = this._tagStack.pop();
if (stackedTagName !== tagName) {
callback(new Error(`Unclosed tag: ${stackedTagName}`));
this._tagStack.length = 0;
return;
}
this.emit(
TOKENS.TAGCLOSE,
{name: input.slice(chunkPos + 1, tagClose)}
Node.tagClose,
{name: tagName}
);

@@ -282,16 +373,3 @@

// Tag without any attribute
/**
* Opening tag token event
* (<tag attr="value">)
*
* @event Saxophone#tagopen
* @type {object}
* @prop {string} name The tag name
* @prop {string} attrs The tag attributes (use
* Saxophone.parseAttributes) to parse the string to a hash
* @prop {bool} isSelfClosing Whether the tag is self-closing
* (<tag />)
*/
this.emit(TOKENS.TAGOPEN, {
this._handleTagOpening({
name: input.slice(chunkPos, realTagClose),

@@ -306,3 +384,3 @@ attrs: '',

// Tag with attributes
this.emit(TOKENS.TAGOPEN, {
this._handleTagOpening({
name: input.slice(chunkPos, chunkPos + whitespace),

@@ -353,3 +431,3 @@ attrs: input.slice(chunkPos + whitespace, realTagClose),

switch (this._stalled) {
case TOKENS.TEXT:
case Node.text:
// Text nodes are implicitly closed

@@ -361,13 +439,13 @@ this.emit(

break;
case TOKENS.CDATA:
case Node.cdata:
callback(new Error('Unclosed CDATA section'));
return;
case TOKENS.COMMENT:
case Node.comment:
callback(new Error('Unclosed comment'));
return;
case TOKENS.PROCESSINGINSTRUCTION:
case Node.processingInstruction:
callback(new Error('Unclosed processing instruction'));
return;
case TOKENS.TAGOPEN:
case TOKENS.TAGCLOSE:
case Node.tagOpen:
case Node.tagClose:
// We do not distinguish between unclosed opening

@@ -381,2 +459,7 @@ // or unclosed closing tags

if (this._tagStack.length !== 0) {
callback(new Error(`Unclosed tags: ${this._tagStack.join(',')}`));
return;
}
callback();

@@ -383,0 +466,0 @@ });

@@ -101,4 +101,7 @@ const {Readable} = require('readable-stream');

expectEvents(assert,
'<tag>',
[['tagopen', {name: 'tag', attrs: '', isSelfClosing: false}]]
'<tag></tag>',
[
['tagopen', {name: 'tag', attrs: '', isSelfClosing: false}],
['tagclose', {name: 'tag'}]
]
);

@@ -114,2 +117,20 @@ });

test('should not parse unclosed tags 2', assert => {
expectEvents(assert,
'<tag>',
[['error', new Error('Unclosed tags: tag')]]
);
});
test('should not parse unclosed tags 3', assert => {
expectEvents(assert,
'<closed><unclosed></closed>',
[
['tagopen', {name: 'closed', attrs: '', isSelfClosing: false}],
['tagopen', {name: 'unclosed', attrs: '', isSelfClosing: false}],
['error', new Error('Unclosed tag: unclosed')],
]
);
});
test('should not parse DOCTYPEs', assert => {

@@ -138,4 +159,7 @@ expectEvents(assert,

expectEvents(assert,
'</closed>',
[['tagclose', {name: 'closed'}]]
'<closed></closed>',
[
['tagopen', {name: 'closed', attrs: '', isSelfClosing: false}],
['tagclose', {name: 'closed'}]
]
);

@@ -142,0 +166,0 @@ });

{
"name": "saxophone",
"description": "Fast and lightweight event-driven XML parser in pure JavaScript",
"version": "0.4.3",
"version": "0.5.0",
"license": "MIT",

@@ -6,0 +6,0 @@ "main": "lib/index.js",

@@ -0,1 +1,2 @@

<!-- vim: set spelllang=en : -->
# Saxophone 🎷

@@ -19,3 +20,3 @@

This library works both in Node.JS ≥4.0 and recent browsers.
This library works both in Node.JS ≥6.0 and recent browsers.
To install with `npm`:

@@ -31,9 +32,9 @@

| Library | Operations per second (higher is better) |
|--------------------|-----------------------------------------:|
| **Saxophone** | **3,717 ops/sec ±1.41%** |
| **EasySax** | **4,346 ops/sec ±2.56%** |
| node-expat | 1,161 ops/sec ±1.94% |
| libxmljs.SaxParser | 1,040 ops/sec ±1.53% |
| sax-js | 760 ops/sec ±2.30% |
Library | Version | Operations per second (higher is better)
-------------------|--------:|----------------------------------------:
**Saxophone** | 0.5.0 | **6,840 ±1.48%**
**EasySax** | 0.3.2 | **7,354 ±1.16%**
node-expat | 2.3.17 | 1,251 ±0.60%
libxmljs.SaxParser | 0.19.5 | 1,007 ±0.81%
sax-js | 1.2.4 | 982 ±1.50%

@@ -46,3 +47,3 @@ To run the benchmark by yourself, use the following commands:

$ npm install
$ npm install easysax node-expat libxmljs sax
$ npm install --no-save easysax node-expat libxmljs sax
$ npm run benchmark

@@ -217,4 +218,10 @@ ```

Emitted when a parsing error is encountered while reading the XML stream such that the rest of the XML cannot be correctly interpreted.
Emitted when a parsing error is encountered while reading the XML stream such that the rest of the XML cannot be correctly interpreted:
* when a DOCTYPE node is found (not supported yet);
* when a comment node contains the `--` sequence;
* when opening and closing tags are mismatched or missing;
* when a tag name starts with white space;
* when nodes are unclosed (missing their final `>`).
Because this library's goal is not to provide accurate error reports, the passed error will only contain a short description of the syntax error (without giving the position, for example).

@@ -230,3 +237,3 @@

Thanks to [Norman Rzepka](https://github.com/normanrz) for implementing the streaming API.
Thanks to [Norman Rzepka](https://github.com/normanrz) for implementing the streaming API and the check for opening and closing tags mismatch.

@@ -233,0 +240,0 @@ ## License

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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