parse5 2.2.2 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -1,108 +1,44 @@
1
- 'use strict';
2
-
3
- var Parser = require('./parser'),
4
- Serializer = require('./serializer');
5
-
6
- /** @namespace parse5 */
7
-
8
- /**
9
- * Parses an HTML string.
10
- * @function parse
11
- * @memberof parse5
12
- * @instance
13
- * @param {string} html - Input HTML string.
14
- * @param {ParserOptions} [options] - Parsing options.
15
- * @returns {ASTNode<Document>} document
16
- * @example
17
- * var parse5 = require('parse5');
18
- *
19
- * var document = parse5.parse('<!DOCTYPE html><html><head></head><body>Hi there!</body></html>');
20
- */
21
- exports.parse = function parse(html, options) {
22
- var parser = new Parser(options);
23
-
24
- return parser.parse(html);
25
- };
26
-
27
- /**
28
- * Parses an HTML fragment.
29
- * @function parseFragment
30
- * @memberof parse5
31
- * @instance
32
- * @param {ASTNode} [fragmentContext] - Parsing context element. If specified, given fragment
33
- * will be parsed as if it was set to the context element's `innerHTML` property.
34
- * @param {string} html - Input HTML fragment string.
35
- * @param {ParserOptions} [options] - Parsing options.
36
- * @returns {ASTNode<DocumentFragment>} documentFragment
37
- * @example
38
- * var parse5 = require('parse5');
39
- *
40
- * var documentFragment = parse5.parseFragment('<table></table>');
41
- *
42
- * // Parses the html fragment in the context of the parsed <table> element.
43
- * var trFragment = parser.parseFragment(documentFragment.childNodes[0], '<tr><td>Shake it, baby</td></tr>');
44
- */
45
- exports.parseFragment = function parseFragment(fragmentContext, html, options) {
46
- if (typeof fragmentContext === 'string') {
47
- options = html;
48
- html = fragmentContext;
49
- fragmentContext = null;
50
- }
51
-
52
- var parser = new Parser(options);
53
-
54
- return parser.parseFragment(html, fragmentContext);
55
- };
56
-
57
- /**
58
- * Serializes an AST node to an HTML string.
59
- * @function serialize
60
- * @memberof parse5
61
- * @instance
62
- * @param {ASTNode} node - Node to serialize.
63
- * @param {SerializerOptions} [options] - Serialization options.
64
- * @returns {String} html
65
- * @example
66
- * var parse5 = require('parse5');
67
- *
68
- * var document = parse5.parse('<!DOCTYPE html><html><head></head><body>Hi there!</body></html>');
69
- *
70
- * // Serializes a document.
71
- * var html = parse5.serialize(document);
72
- *
73
- * // Serializes the <body> element content.
74
- * var bodyInnerHtml = parse5.serialize(document.childNodes[0].childNodes[1]);
75
- */
76
- exports.serialize = function (node, options) {
77
- var serializer = new Serializer(node, options);
78
-
79
- return serializer.serialize();
80
- };
81
-
82
- /**
83
- * Provides built-in tree adapters that can be used for parsing and serialization.
84
- * @var treeAdapters
85
- * @memberof parse5
86
- * @instance
87
- * @property {TreeAdapter} default - Default tree format for parse5.
88
- * @property {TreeAdapter} htmlparser2 - Quite popular [htmlparser2](https://github.com/fb55/htmlparser2) tree format
89
- * (e.g. used by [cheerio](https://github.com/MatthewMueller/cheerio) and [jsdom](https://github.com/tmpvar/jsdom)).
90
- * @example
91
- * var parse5 = require('parse5');
92
- *
93
- * // Uses the default tree adapter for parsing.
94
- * var document = parse5.parse('<div></div>', { treeAdapter: parse5.treeAdapters.default });
95
- *
96
- * // Uses the htmlparser2 tree adapter with the SerializerStream.
97
- * var serializer = new parse5.SerializerStream(node, { treeAdapter: parse5.treeAdapters.htmlparser2 });
98
- */
99
- exports.treeAdapters = {
100
- default: require('./tree_adapters/default'),
101
- htmlparser2: require('./tree_adapters/htmlparser2')
102
- };
103
-
104
-
105
- // Streaming
106
- exports.ParserStream = require('./parser/stream');
107
- exports.SerializerStream = require('./serializer/stream');
108
- exports.SAXParser = require('./sax');
1
+ 'use strict';
2
+
3
+ var Parser = require('./parser'),
4
+ Serializer = require('./serializer');
5
+
6
+
7
+ // Shorthands
8
+ exports.parse = function parse(html, options) {
9
+ var parser = new Parser(options);
10
+
11
+ return parser.parse(html);
12
+ };
13
+
14
+ exports.parseFragment = function parseFragment(fragmentContext, html, options) {
15
+ if (typeof fragmentContext === 'string') {
16
+ options = html;
17
+ html = fragmentContext;
18
+ fragmentContext = null;
19
+ }
20
+
21
+ var parser = new Parser(options);
22
+
23
+ return parser.parseFragment(html, fragmentContext);
24
+ };
25
+
26
+ exports.serialize = function (node, options) {
27
+ var serializer = new Serializer(node, options);
28
+
29
+ return serializer.serialize();
30
+ };
31
+
32
+
33
+ // Tree adapters
34
+ exports.treeAdapters = {
35
+ default: require('./tree_adapters/default'),
36
+ htmlparser2: require('./tree_adapters/htmlparser2')
37
+ };
38
+
39
+
40
+ // Streaming
41
+ exports.ParserStream = require('./parser/parser_stream');
42
+ exports.PlainTextConversionStream = require('./parser/plain_text_conversion_stream');
43
+ exports.SerializerStream = require('./serializer/serializer_stream');
44
+ exports.SAXParser = require('./sax');
@@ -8,53 +8,6 @@ var OpenElementStack = require('../parser/open_element_stack'),
8
8
  //Aliases
9
9
  var $ = HTML.TAG_NAMES;
10
10
 
11
-
12
- function setEndLocation(element, closingToken, treeAdapter) {
13
- var loc = element.__location;
14
-
15
- if (!loc)
16
- return;
17
-
18
- /**
19
- * @typedef {Object} ElementLocationInfo
20
- * @extends StartTagLocationInfo
21
- *
22
- * @property {StartTagLocationInfo} startTag - Element's start tag location info.
23
- * @property {LocationInfo} endTag - Element's end tag location info.
24
- */
25
- if (!loc.startTag) {
26
- loc.startTag = {
27
- line: loc.line,
28
- col: loc.col,
29
- startOffset: loc.startOffset,
30
- endOffset: loc.endOffset
31
- };
32
- if (loc.attrs)
33
- loc.startTag.attrs = loc.attrs;
34
- }
35
-
36
- if (closingToken.location) {
37
- var ctLocation = closingToken.location,
38
- tn = treeAdapter.getTagName(element),
39
- // NOTE: For cases like <p> <p> </p> - First 'p' closes without a closing tag and
40
- // for cases like <td> <p> </td> - 'p' closes without a closing tag
41
- isClosingEndTag = closingToken.type === Tokenizer.END_TAG_TOKEN &&
42
- tn === closingToken.tagName;
43
-
44
- if (isClosingEndTag) {
45
- loc.endTag = {
46
- line: ctLocation.line,
47
- col: ctLocation.col,
48
- startOffset: ctLocation.startOffset,
49
- endOffset: ctLocation.endOffset
50
- };
51
- }
52
-
53
- loc.endOffset = ctLocation.startOffset;
54
- }
55
- }
56
-
57
-
58
11
  exports.assign = function (parser) {
59
12
  //NOTE: obtain Parser proto this way to avoid module circular references
60
13
  var parserProto = Object.getPrototypeOf(parser),
@@ -63,6 +16,50 @@ exports.assign = function (parser) {
63
16
  lastFosterParentingLocation = null,
64
17
  currentToken = null;
65
18
 
19
+ function setEndLocation(element, closingToken) {
20
+ var loc = element.__location;
21
+
22
+ if (!loc)
23
+ return;
24
+
25
+ if (!loc.startTag) {
26
+ loc.startTag = {
27
+ line: loc.line,
28
+ col: loc.col,
29
+ startOffset: loc.startOffset,
30
+ endOffset: loc.endOffset
31
+ };
32
+
33
+ if (loc.attrs)
34
+ loc.startTag.attrs = loc.attrs;
35
+ }
36
+
37
+ if (closingToken.location) {
38
+ var ctLocation = closingToken.location,
39
+ tn = treeAdapter.getTagName(element),
40
+ // NOTE: For cases like <p> <p> </p> - First 'p' closes without a closing tag and
41
+ // for cases like <td> <p> </td> - 'p' closes without a closing tag
42
+ isClosingEndTag = closingToken.type === Tokenizer.END_TAG_TOKEN &&
43
+ tn === closingToken.tagName;
44
+
45
+ if (isClosingEndTag) {
46
+ loc.endTag = {
47
+ line: ctLocation.line,
48
+ col: ctLocation.col,
49
+ startOffset: ctLocation.startOffset,
50
+ endOffset: ctLocation.endOffset
51
+ };
52
+ }
53
+
54
+ if (isClosingEndTag)
55
+ loc.endOffset = ctLocation.endOffset;
56
+ else
57
+ loc.endOffset = ctLocation.startOffset;
58
+ }
59
+
60
+ else if (closingToken.type === Tokenizer.EOF_TOKEN)
61
+ loc.endOffset = parser.tokenizer.preprocessor.sourcePos;
62
+ }
66
63
 
67
64
  //NOTE: patch _bootstrap method
68
65
  parser._bootstrap = function (document, fragmentContext) {
@@ -74,23 +71,32 @@ exports.assign = function (parser) {
74
71
 
75
72
  //OpenElementStack
76
73
  parser.openElements.pop = function () {
77
- setEndLocation(this.current, currentToken, treeAdapter);
74
+ setEndLocation(this.current, currentToken);
78
75
  OpenElementStack.prototype.pop.call(this);
79
76
  };
80
77
 
81
78
  parser.openElements.popAllUpToHtmlElement = function () {
82
79
  for (var i = this.stackTop; i > 0; i--)
83
- setEndLocation(this.items[i], currentToken, treeAdapter);
80
+ setEndLocation(this.items[i], currentToken);
84
81
 
85
82
  OpenElementStack.prototype.popAllUpToHtmlElement.call(this);
86
83
  };
87
84
 
88
85
  parser.openElements.remove = function (element) {
89
- setEndLocation(element, currentToken, treeAdapter);
86
+ setEndLocation(element, currentToken);
90
87
  OpenElementStack.prototype.remove.call(this, element);
91
88
  };
92
89
  };
93
90
 
91
+ parser._runParsingLoop = function (scriptHandler) {
92
+ parserProto._runParsingLoop.call(this, scriptHandler);
93
+
94
+ // NOTE: generate location info for elements
95
+ // that remains on open element stack
96
+ for (var i = parser.openElements.stackTop; i >= 0; i--)
97
+ setEndLocation(parser.openElements.items[i], currentToken);
98
+ };
99
+
94
100
 
95
101
  //Token processing
96
102
  parser._processTokenInForeignContent = function (token) {
@@ -111,7 +117,7 @@ exports.assign = function (parser) {
111
117
  var element = this.openElements.items[i];
112
118
 
113
119
  if (this.treeAdapter.getTagName(element) === token.tagName) {
114
- setEndLocation(element, token, treeAdapter);
120
+ setEndLocation(element, token);
115
121
  break;
116
122
  }
117
123
  }
@@ -13,20 +13,11 @@ exports.assign = function (tokenizer) {
13
13
  tokenCol = -1,
14
14
  tokenLine = 1,
15
15
  isEol = false,
16
- lineStartPosStack = [0],
17
16
  lineStartPos = 0,
18
17
  col = -1,
19
18
  line = 1;
20
19
 
21
20
  function attachLocationInfo(token) {
22
- /**
23
- * @typedef {Object} LocationInfo
24
- *
25
- * @property {Number} line - One-based line index
26
- * @property {Number} col - One-based column index
27
- * @property {Number} startOffset - Zero-based first character index
28
- * @property {Number} endOffset - Zero-based last character index
29
- */
30
21
  token.location = {
31
22
  line: tokenLine,
32
23
  col: tokenCol,
@@ -43,7 +34,6 @@ exports.assign = function (tokenizer) {
43
34
  if (isEol) {
44
35
  isEol = false;
45
36
  line++;
46
- lineStartPosStack.push(this.preprocessor.sourcePos);
47
37
  lineStartPos = this.preprocessor.sourcePos;
48
38
  }
49
39
 
@@ -59,11 +49,6 @@ exports.assign = function (tokenizer) {
59
49
  tokenizerProto._unconsume.call(this);
60
50
  isEol = false;
61
51
 
62
- while (lineStartPos > this.preprocessor.sourcePos && lineStartPosStack.length > 1) {
63
- lineStartPos = lineStartPosStack.pop();
64
- line--;
65
- }
66
-
67
52
  col = this.preprocessor.sourcePos - lineStartPos + 1;
68
53
  };
69
54
 
@@ -117,14 +102,8 @@ exports.assign = function (tokenizer) {
117
102
  this.currentAttrLocation.endOffset = this.preprocessor.sourcePos;
118
103
 
119
104
  if (!this.currentToken.location.attrs)
120
- this.currentToken.location.attrs = {};
121
-
122
- /**
123
- * @typedef {Object} StartTagLocationInfo
124
- * @extends LocationInfo
125
- *
126
- * @property {Dictionary<String, LocationInfo>} attrs - Start tag attributes' location info.
127
- */
105
+ this.currentToken.location.attrs = Object.create(null);
106
+
128
107
  this.currentToken.location.attrs[this.currentAttr.name] = this.currentAttrLocation;
129
108
  };
130
109