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

findatag

Package Overview
Dependencies
Maintainers
3
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

findatag - npm Package Compare versions

Comparing version 0.0.9 to 1.0.0

CHANGELOG.md

21

index.js

@@ -23,2 +23,3 @@ /***@@@ BEGIN LICENSE @@@***/

var fs = require('fs'),
bl = require('bl'),
ParseStream = require('./lib/parseStream');

@@ -45,17 +46,13 @@

parseStream = this.createParseStream(entityHandler);
parseStream.on('error', callback);
parseStream.on('data', function (chunk) {
chunks.push(chunk);
});
parseStream.on('finish', function () {
callback(null, Buffer.concat(chunks).toString('utf8'));
});
// Start processing
readStream.pipe(parseStream);
readStream.pipe(parseStream).pipe(bl(function (err, result) {
if (err) {
return callback(err);
} else {
return callback(null, result.toString('utf8'));
}
}));
}
};
};

@@ -24,15 +24,25 @@ /***@@@ BEGIN LICENSE @@@***/

async = require('async'),
stream = require('readable-stream'),
Parser = require('./parser');
stream = require('stream');
function ParseStream(entityHandler) {
function ParseStream(options) {
ParseStream.super_.call(this);
this._handler = entityHandler;
this._queue = [];
options = options || {};
var parser = this._parser = new Parser({ tags: entityHandler.tags });
parser.on('tag', this._queueTag.bind(this));
parser.on('text', this._queueText.bind(this));
var tags;
this._state = State.BEGIN;
tags = options.tags;
if (Array.isArray(tags)) {
tags = tags.join(',');
}
this._tags = createDict(tags, /\s*,\s*/g);
this._textNode = '';
this._tagName = '';
this._attributeName = '';
this._attributeValue = '';
this._attributes = {};
this._options = options;
}

@@ -44,62 +54,294 @@

ParseStream.prototype.__defineGetter__('entityHandler', function () {
return this._handler;
return this._options;
});
ParseStream.prototype.__defineSetter__('entityHandler', function (value) {
this._handler = value;
});
ParseStream.prototype._transform = function (chunk, encoding, cb) {
if (Buffer.isBuffer(chunk)) {
encoding = 'utf8';
chunk = chunk.toString(encoding);
}
return this.parseChunk(chunk, 0, cb);
};
ParseStream.prototype._queueTag = function (chunk) {
var that = this;
this._queue.push(function (cb) {
that._handler.onTag(chunk, function (err, result) {
that.push(result);
cb(err);
});
});
function createDict(str, del) {
return str && Object.freeze(str.split(del || '').reduce(function (s, c) {
s[c] = true;
return s;
}, {}));
}
var ORD = 0;
var State = {
BEGIN: ORD++,
TEXT: ORD++,
OPEN_CHAR: ORD++,
OPEN_TAG: ORD++,
ATTRIB: ORD++,
ATTRIB_NAME: ORD++,
ATTRIB_VALUE: ORD++,
QUOTED_ATTRIB_VALUE: ORD++,
QUOTED_ATTRIB_VALUE_ESCAPE: ORD++,
CLOSE_TAG: ORD++
};
ParseStream.prototype._queueText = function (chunk) {
var CharSet = {
WHITESPACE: createDict('\n\r\t '),
ESCAPEABLE_CONTROL_CHARS: createDict('bfnrtv'),
ALPHANUM: createDict('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890._')
};
ParseStream.prototype._is = function (charSet, character) {
return charSet[character];
};
ParseStream.prototype._closeText = function (cb) {
var that = this;
this._queue.push(function (cb) {
if (that._handler.onText) {
that._handler.onText(chunk, function (err, result) {
if (this._textNode && this._options.onText) {
this._options.onText(this._textNode, function (err, result) {
if (err) {
return cb(err);
} else {
that.push(result);
cb(err);
});
} else {
that.push(chunk);
cb();
return cb();
}
});
this._textNode = '';
} else {
if (this._textNode) {
this.push(this._textNode);
this._textNode = '';
}
});
return cb();
}
};
ParseStream.prototype._transform = function (chunk, encoding, cb) {
if (Buffer.isBuffer(chunk)) {
encoding = 'utf8';
chunk = chunk.toString(encoding);
}
ParseStream.prototype._closeTag = function (cb) {
var that = this;
that._parser.once('end', function () {
async.series(that._queue, function (err) {
if (this._tagName && this._options.onTag) {
this._options.onTag({
name: this._tagName,
attributes: this._attributes
}, function (err, result) {
if (err) {
cb(err);
return;
return cb(err);
} else {
that.push(result);
return cb();
}
that._parser.resume();
that._queue = [];
cb();
});
});
that._parser.write(chunk).close();
} else {
return cb();
}
};
module.exports = ParseStream;
ParseStream.prototype.parseChunk = function (chunk, pos, callback) {
var c;
var escCharMap = {"b":"\x08", "f":"\x0C", "n":"\x0A", "r":"\x0D", "t":"\x09", "v":"\x0B"};
var that = this;
while (c = chunk.charAt(pos++)) {
switch (this._state) {
case State.BEGIN:
this._textNode = '';
this._state = State.TEXT;
// XXX: Intentionally fall through
/* falls through */
case State.TEXT:
this._tagName = '';
this._attributeName = '';
this._attributeValue = '';
this._attributes = {};
if (c === '{') {
this._state = State.OPEN_CHAR;
} else {
this._textNode += c;
}
break;
case State.OPEN_CHAR:
if (c === '@') {
this._state = State.OPEN_TAG;
} else {
// Revert back to text state, including the previously skipped '{'
this._state = State.TEXT;
this._textNode += '{' + c;
}
break;
case State.OPEN_TAG:
if (this._is(CharSet.ALPHANUM, c)) {
this._tagName += c;
} else if (this._is(CharSet.WHITESPACE, c)) {
if (this._tagName && (!this._tags || this._tags[this._tagName])) {
// Officially a tag so notify end of text node.
this._state = State.ATTRIB;
} else {
// Revert back to text state, including the character and continuing as text node.
this._textNode += '{@' + this._tagName + c;
this._state = State.TEXT;
}
} else if (c === '/') {
if (this._tagName && (!this._tags || this._tags[this._tagName])) {
// It's a tag w/ no attrs so close the prev textNode and initiate close
this._state = State.CLOSE_TAG;
return this._closeText(cont());
} else {
// Not a tag, but we got here somehow, so just add tag-like chars
this._textNode += '{@' + this._tagName + c;
this._state = State.TEXT;
}
} else if (c === '}') {
// Symmetrical tag, so ignore
this._textNode += '{@' + this._tagName + c;
this._state = State.TEXT;
} else {
if (this._tagName) {
return callback(new Error('Malformed tag. Tag not closed correctly.'));
} else {
// Hit a character that's not valid in a tag, so put together what we've found so far and
// continue as text node.
this._textNode += '{@' + this._tagName + c;
this._state = State.TEXT;
}
}
break;
case State.ATTRIB:
if (this._is(CharSet.ALPHANUM, c)) {
this._attributeName = c;
this._attributeValue = '';
this._state = State.ATTRIB_NAME;
} else if (this._is(CharSet.WHITESPACE, c)) {
// noop
} else if (c === '/') {
this._state = State.CLOSE_TAG;
return this._closeText(cont());
} else {
return callback(new Error('Malformed tag. Tag not closed correctly.'));
}
break;
case State.ATTRIB_NAME:
if (this._is(CharSet.ALPHANUM, c)) {
this._attributeName += c;
} else if (this._is(CharSet.WHITESPACE, c)) {
if (this._attributeName) {
this._attributes[this._attributeName] = this._attributeName;
}
this._state = State.ATTRIB;
} else if (c === '=') {
this._state = State.ATTRIB_VALUE;
} else if (c === '/') {
if (this._attributeName) {
this._attributes[this._attributeName] = this._attributeName;
}
this._state = State.CLOSE_TAG;
} else {
this._textNode += c;
this._state = State.TEXT;
}
break;
case State.QUOTED_ATTRIB_VALUE:
if (c === '"') {
this._attributes[this._attributeName] = this._attributeValue;
this._state = State.ATTRIB;
} else if (c === '\\') {
this._state = State.QUOTED_ATTRIB_VALUE_ESCAPE;
} else {
this._attributeValue += c;
}
break;
case State.QUOTED_ATTRIB_VALUE_ESCAPE:
// Only control chars must be mapped to different codes.
if (this._is(CharSet.ESCAPEABLE_CONTROL_CHARS, c)) {
this._attributeValue += escCharMap[c];
} else {
this._attributeValue += c;
}
this._state = State.QUOTED_ATTRIB_VALUE;
break;
case State.ATTRIB_VALUE:
if (c === '/') {
this._attributes[this._attributeName] = this._attributeValue;
this._state = State.CLOSE_TAG;
} else if (c === '"') {
if (!this._attributeValue) {
this._state = State.QUOTED_ATTRIB_VALUE;
} else {
return callback(new Error('Malformed tag. Invalid quote.'));
}
} else if (c === "'") {
return callback(new Error('Malformed tag. Attribute values must use double quotes.'));
} else if (this._is(CharSet.WHITESPACE, c)) {
if (this._attributeValue) {
this._attributes[this._attributeName] = this._attributeValue;
this._state = State.ATTRIB;
}
} else {
this._attributeValue += c;
}
break;
case State.CLOSE_TAG:
if (c === '}') {
this._state = State.TEXT;
return this._closeTag(cont());
}
break;
}
}
if (this._state === State.TEXT) {
return this._closeText(cont());
} else {
return cont()();
}
function cont() {
if (pos >= chunk.length) {
return callback;
} else {
return function maybeContinueParsing(err, data) {
if (err || pos >= chunk.length) {
return callback(err);
} else {
setImmediate(function() {
return that.parseChunk(chunk, pos, callback);
});
}
};
}
}
};
module.exports = ParseStream;
{
"name": "findatag",
"version": "0.0.9",
"version": "1.0.0",
"description": "A specialized tokenizer for finding dust-style tags ({@tagname [attributes]/})",
"main": "index.js",
"scripts": {
"test": "make test"
"test": "grunt test"
},
"repository": {
"type": "git",
"url": "git://github.com/paypal/findatag.git"
"url": "git://github.com/krakenjs/findatag.git"
},
"engines": {
"node": ">=0.10.4"
},
"keywords": [

@@ -25,12 +22,17 @@ "dust",

"email": "ertoth@paypal.com"
},
{
"name": "Aria Stewart",
"email": "ariastewart@paypal.com"
}
],
"licenses": [
{
"type": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
],
{
"type": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
],
"dependencies": {
"async": "0.2.x",
"bl": "^0.9.0",
"readable-stream": "1.0.15"

@@ -40,5 +42,8 @@ },

"chai": "~1.5.0",
"mocha": "~1.9.0"
"mocha": "~1.9.0",
"grunt": "~0.4.1",
"grunt-contrib-jshint": "~0.7.0",
"grunt-mocha-test": "~0.7.0"
},
"homepage": "https://github.com/paypal/findatag"
}
"homepage": "https://github.com/krakenjs/findatag"
}
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