Skip to content

Commit 2941bb5

Browse files
committed
Return value (in 'stream mode') is now a Transform stream
1 parent 74037fb commit 2941bb5

File tree

2 files changed

+70
-21
lines changed

2 files changed

+70
-21
lines changed

index.js

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,68 @@
1+
var util = require('util');
2+
var Transform = require('stream').Transform;
13
var Parser = require('htmlparser2').Parser;
24
var isEmpty = require('lodash.isempty');
35

4-
module.exports = function(markup, callback) {
5-
var parent = [];
6-
var parser = new Parser({
6+
util.inherits(JSONMLParser, Transform);
7+
8+
function JSONMLParser() {
9+
Transform.call(this);
10+
this._readableState.objectMode = true;
11+
this._parent = [];
12+
this.source = new Parser(this._createSourceOptions());
13+
}
14+
15+
JSONMLParser.prototype._transform = function(chunk, encoding, done) {
16+
this.source.write(chunk, encoding);
17+
done();
18+
};
19+
20+
JSONMLParser.prototype._flush = function(done) {
21+
this._onParseDone = done;
22+
this.source.end();
23+
done();
24+
};
25+
26+
JSONMLParser.prototype._createSourceOptions = function() {
27+
var transform = this;
28+
return {
729
onopentag: function(tagName, attributes) {
830
var elementList = [];
931
var element = [tagName, attributes, elementList];
10-
parent.push(element);
11-
elementList.parent = parent;
12-
parent = elementList;
32+
transform._parent.push(element);
33+
elementList.parent = transform._parent;
34+
transform._parent = elementList;
1335
},
1436
ontext: function(text) {
15-
parent.push(['#text', text]);
37+
transform._parent.push(['#text', text]);
1638
},
1739
oncomment: function(text) {
18-
parent.push(['#comment', text]);
40+
transform._parent.push(['#comment', text]);
1941
},
2042
onclosetag: function() {
21-
var p = parent.parent;
43+
var p = transform._parent.parent;
2244
// Delete elementList and/or attributes if empty
2345
var lastChild = p[p.length - 1];
2446
for (var i = 2; i > 0; i--) {
2547
if (isEmpty(lastChild[i])) {
2648
lastChild.splice(i, 1);
2749
}
2850
}
29-
delete parent.parent;
30-
parent = p;
51+
delete transform._parent.parent;
52+
transform._parent = p;
3153
},
3254
onerror: function(err) {
33-
parser.emit('error', err);
55+
transform.emit('error', err);
3456
},
3557
onend: function() {
36-
parser.emit('data', parent);
37-
parser.emit('end');
58+
transform.push(transform._parent);
59+
transform._onParseDone();
3860
}
39-
});
61+
};
62+
};
63+
64+
module.exports = function(markup, callback) {
65+
var parser = new JSONMLParser();
4066
if (isEmpty(arguments)) {
4167
return parser;
4268
} else {

test/test.js

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,33 @@
11
var fs = require('fs');
22
var test = require('tape');
3+
var Transform = require('stream').Transform;
34
var jsonmlify = require('../');
45

56
function verify(markup, expected, t) {
7+
var result;
68
jsonmlify()
79
.on('data', function(data) {
8-
t.deepEqual(data, expected);
10+
result = data;
11+
})
12+
.on('end', function() {
13+
t.deepEqual(result, expected);
914
t.end();
1015
})
1116
.end(markup);
1217
}
1318

14-
test('should return an empty array for empty input markup', function(t) {
19+
test('should return a transform stream', function(t) {
20+
t.ok(jsonmlify() instanceof Transform);
21+
t.ok(new jsonmlify() instanceof Transform);
22+
t.end();
23+
});
24+
25+
test('should not return anything in \'callback mode\'', function(t) {
26+
t.equal(jsonmlify('foo', function() {}), undefined);
27+
t.end();
28+
});
29+
30+
test('should emit an empty array for empty input markup', function(t) {
1531
var markup = fs.readFileSync(__dirname + '/01-empty/markup.html', 'utf8');
1632
var expected = require('./01-empty/expected.json');
1733
verify(markup, expected, t);
@@ -41,7 +57,7 @@ test('should parse comments', function(t) {
4157
verify(markup, expected, t);
4258
});
4359

44-
test('should also expose a node-style callback API', function(t) {
60+
test('should also offer a node-style callback API', function(t) {
4561
jsonmlify('<div></div>', function(err, result) {
4662
t.notOk(err);
4763
t.deepEqual(result, [['div']]);
@@ -50,11 +66,18 @@ test('should also expose a node-style callback API', function(t) {
5066
});
5167

5268
test('should report errors', function(t) {
69+
var errorCount = 0;
5370
var stream = jsonmlify();
5471
stream.on('error', function(err) {
72+
errorCount++;
5573
t.ok(err);
56-
t.end();
74+
if (errorCount === 2) {
75+
t.end();
76+
}
5777
});
5878
stream.end();
59-
stream.write('<div></div>'); // Write after close to provoke an error
60-
});
79+
// Write after end to provoke an error in the writable stream
80+
stream.write('<div></div>');
81+
// Write to HTML parser after end to provoke an internal error
82+
stream.source.write('<div></div>');
83+
});

0 commit comments

Comments
 (0)