parse5 3.0.1 → 4.0.0

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
@@ -38,7 +38,29 @@ exports.treeAdapters = {
38
38
 
39
39
 
40
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');
41
+ // NOTE: streaming API is lazy loadable to enable bundling for platforms
42
+ // that are different from Node.js.
43
+ // See https://github.com/inikulin/parse5/issues/235.
44
+ var streamingAPI = {
45
+ ParserStream: './parser/parser_stream',
46
+ PlainTextConversionStream: './parser/plain_text_conversion_stream',
47
+ SerializerStream: './serializer/serializer_stream',
48
+ SAXParser: './sax'
49
+ };
50
+
51
+ Object.keys(streamingAPI).forEach(function (cls) {
52
+ Object.defineProperty(exports, cls, {
53
+ get: function () {
54
+ try {
55
+ return require(streamingAPI[cls]);
56
+ }
57
+
58
+ catch (e) {
59
+ throw new Error(
60
+ cls + ' is supported only for Node.js.' +
61
+ 'See https://github.com/inikulin/parse5/issues/235 for the details.'
62
+ );
63
+ }
64
+ }
65
+ });
66
+ });
@@ -3,11 +3,11 @@
3
3
  var Tokenizer = require('../tokenizer'),
4
4
  OpenElementStack = require('./open_element_stack'),
5
5
  FormattingElementList = require('./formatting_element_list'),
6
- locationInfoMixin = require('../location_info/parser_mixin'),
6
+ LocationInfoParserMixin = require('../extensions/location_info/parser_mixin'),
7
7
  defaultTreeAdapter = require('../tree_adapters/default'),
8
+ mergeOptions = require('../utils/merge_options'),
8
9
  doctype = require('../common/doctype'),
9
10
  foreignContent = require('../common/foreign_content'),
10
- mergeOptions = require('../common/merge_options'),
11
11
  UNICODE = require('../common/unicode'),
12
12
  HTML = require('../common/html');
13
13
 
@@ -310,7 +310,7 @@ var Parser = module.exports = function (options) {
310
310
  this.pendingScript = null;
311
311
 
312
312
  if (this.options.locationInfo)
313
- locationInfoMixin.assign(this);
313
+ new LocationInfoParserMixin(this);
314
314
  };
315
315
 
316
316
  // API
@@ -319,7 +319,7 @@ Parser.prototype.parse = function (html) {
319
319
 
320
320
  this._bootstrap(document, null);
321
321
  this.tokenizer.write(html, true);
322
- this._runParsingLoop(null, null);
322
+ this._runParsingLoop(null);
323
323
 
324
324
  return document;
325
325
  };
@@ -345,7 +345,7 @@ Parser.prototype.parseFragment = function (html, fragmentContext) {
345
345
  this._resetInsertionMode();
346
346
  this._findFormInFragmentContext();
347
347
  this.tokenizer.write(html, true);
348
- this._runParsingLoop(null, null);
348
+ this._runParsingLoop(null);
349
349
 
350
350
  var rootElement = this.treeAdapter.getFirstChild(documentMock),
351
351
  fragment = this.treeAdapter.createDocumentFragment();
@@ -386,7 +386,7 @@ Parser.prototype._bootstrap = function (document, fragmentContext) {
386
386
  };
387
387
 
388
388
  //Parsing loop
389
- Parser.prototype._runParsingLoop = function (writeCallback, scriptHandler) {
389
+ Parser.prototype._runParsingLoop = function (scriptHandler) {
390
390
  while (!this.stopped) {
391
391
  this._setupTokenizerCDATAMode();
392
392
 
@@ -411,6 +411,10 @@ Parser.prototype._runParsingLoop = function (writeCallback, scriptHandler) {
411
411
  if (scriptHandler && this.pendingScript)
412
412
  break;
413
413
  }
414
+ };
415
+
416
+ Parser.prototype.runParsingLoopForCurrentChunk = function (writeCallback, scriptHandler) {
417
+ this._runParsingLoop(scriptHandler);
414
418
 
415
419
  if (scriptHandler && this.pendingScript) {
416
420
  var script = this.pendingScript;
@@ -35,12 +35,12 @@ ParserStream.prototype._write = function (chunk, encoding, callback) {
35
35
 
36
36
  ParserStream.prototype.end = function (chunk, encoding, callback) {
37
37
  this.lastChunkWritten = true;
38
- WritableStream.prototype.end.call(this, chunk, encoding, callback);
38
+ WritableStream.prototype.end.call(this, chunk || '', encoding, callback);
39
39
  };
40
40
 
41
41
  //Scriptable parser implementation
42
42
  ParserStream.prototype._runParsingLoop = function () {
43
- this.parser._runParsingLoop(this.writeCallback, this._scriptHandler);
43
+ this.parser.runParsingLoopForCurrentChunk(this.writeCallback, this._scriptHandler);
44
44
  };
45
45
 
46
46
  ParserStream.prototype._resume = function () {
@@ -1,14 +1,14 @@
1
- 'use strict';
2
-
3
- var WritableStream = require('stream').Writable,
4
- util = require('util');
5
-
6
- var DevNullStream = module.exports = function () {
7
- WritableStream.call(this);
8
- };
9
-
10
- util.inherits(DevNullStream, WritableStream);
11
-
12
- DevNullStream.prototype._write = function (chunk, encoding, cb) {
13
- cb();
14
- };
1
+ 'use strict';
2
+
3
+ var WritableStream = require('stream').Writable,
4
+ util = require('util');
5
+
6
+ var DevNullStream = module.exports = function () {
7
+ WritableStream.call(this);
8
+ };
9
+
10
+ util.inherits(DevNullStream, WritableStream);
11
+
12
+ DevNullStream.prototype._write = function (chunk, encoding, cb) {
13
+ cb();
14
+ };
package/lib/sax/index.js CHANGED
@@ -4,8 +4,9 @@ var TransformStream = require('stream').Transform,
4
4
  DevNullStream = require('./dev_null_stream'),
5
5
  inherits = require('util').inherits,
6
6
  Tokenizer = require('../tokenizer'),
7
+ LocationInfoTokenizerMixin = require('../extensions/location_info/tokenizer_mixin'),
7
8
  ParserFeedbackSimulator = require('./parser_feedback_simulator'),
8
- mergeOptions = require('../common/merge_options');
9
+ mergeOptions = require('../utils/merge_options');
9
10
 
10
11
  var DEFAULT_OPTIONS = {
11
12
  locationInfo: false
@@ -17,6 +18,10 @@ var SAXParser = module.exports = function (options) {
17
18
  this.options = mergeOptions(DEFAULT_OPTIONS, options);
18
19
 
19
20
  this.tokenizer = new Tokenizer(options);
21
+
22
+ if (this.options.locationInfo)
23
+ new LocationInfoTokenizerMixin(this.tokenizer);
24
+
20
25
  this.parserFeedbackSimulator = new ParserFeedbackSimulator(this.tokenizer);
21
26
 
22
27
  this.pendingText = null;
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var defaultTreeAdapter = require('../tree_adapters/default'),
4
+ mergeOptions = require('../utils/merge_options'),
4
5
  doctype = require('../common/doctype'),
5
- mergeOptions = require('../common/merge_options'),
6
6
  HTML = require('../common/html');
7
7
 
8
8
  //Aliases
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  var Preprocessor = require('./preprocessor'),
4
- locationInfoMixin = require('../location_info/tokenizer_mixin'),
5
4
  UNICODE = require('../common/unicode'),
6
5
  neTree = require('./named_entity_data');
7
6
 
@@ -173,7 +172,7 @@ function findNamedEntityTreeBranch(nodeIx, cp) {
173
172
 
174
173
 
175
174
  //Tokenizer
176
- var Tokenizer = module.exports = function (options) {
175
+ var Tokenizer = module.exports = function () {
177
176
  this.preprocessor = new Preprocessor();
178
177
 
179
178
  this.tokenQueue = [];
@@ -193,9 +192,6 @@ var Tokenizer = module.exports = function (options) {
193
192
  this.currentCharacterToken = null;
194
193
  this.currentToken = null;
195
194
  this.currentAttr = null;
196
-
197
- if (options && options.locationInfo)
198
- locationInfoMixin.assign(this);
199
195
  };
200
196
 
201
197
  //Token types
@@ -210,7 +206,7 @@ Tokenizer.EOF_TOKEN = 'EOF_TOKEN';
210
206
  Tokenizer.HIBERNATION_TOKEN = 'HIBERNATION_TOKEN';
211
207
 
212
208
  //Tokenizer initial states for different modes
213
- Tokenizer.MODE = Tokenizer.prototype.MODE = {
209
+ Tokenizer.MODE = {
214
210
  DATA: DATA_STATE,
215
211
  RCDATA: RCDATA_STATE,
216
212
  RAWTEXT: RAWTEXT_STATE,
@@ -32,7 +32,6 @@ var Preprocessor = module.exports = function () {
32
32
  this.pos = -1;
33
33
  this.lastGapPos = -1;
34
34
  this.lastCharPos = -1;
35
- this.droppedBufferSize = 0;
36
35
 
37
36
  this.gapStack = [];
38
37
 
@@ -43,16 +42,9 @@ var Preprocessor = module.exports = function () {
43
42
  this.bufferWaterline = DEFAULT_BUFFER_WATERLINE;
44
43
  };
45
44
 
46
- Object.defineProperty(Preprocessor.prototype, 'sourcePos', {
47
- get: function () {
48
- return this.droppedBufferSize + this.pos;
49
- }
50
- });
51
-
52
45
  Preprocessor.prototype.dropParsedChunk = function () {
53
46
  if (this.pos > this.bufferWaterline) {
54
47
  this.lastCharPos -= this.pos;
55
- this.droppedBufferSize += this.pos;
56
48
  this.html = this.html.substring(this.pos);
57
49
  this.pos = 0;
58
50
  this.lastGapPos = -1;
File without changes
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ var Mixin = module.exports = function (host) {
4
+ var originalMethods = {},
5
+ overriddenMethods = this._getOverriddenMethods(this, originalMethods);
6
+
7
+ Object.keys(overriddenMethods).forEach(function (key) {
8
+ if (typeof overriddenMethods[key] === 'function') {
9
+ originalMethods[key] = host[key];
10
+ host[key] = overriddenMethods[key];
11
+ }
12
+ });
13
+ };
14
+
15
+ Mixin.prototype._getOverriddenMethods = function () {
16
+ throw new Error('Not implemented');
17
+ };
18
+
package/package.json CHANGED
@@ -1,63 +1,60 @@
1
- {
2
- "name": "parse5",
3
- "description": "HTML parsing/serialization toolset for Node.js. WHATWG HTML Living Standard (aka HTML5)-compliant.",
4
- "version": "3.0.1",
5
- "author": "Ivan Nikulin <ifaaan@gmail.com> (https://github.com/inikulin)",
6
- "contributors": "https://github.com/inikulin/parse5/graphs/contributors",
7
- "homepage": "https://github.com/inikulin/parse5",
8
- "devDependencies": {
9
- "del": "^2.0.2",
10
- "gulp": "^3.9.0",
11
- "gulp-benchmark": "^1.1.1",
12
- "gulp-download": "0.0.1",
13
- "gulp-eslint": "^3.0.1",
14
- "gulp-install": "^0.6.0",
15
- "gulp-mocha": "^2.1.3",
16
- "gulp-rename": "^1.2.2",
17
- "gulp-typedoc": "^2.0.0",
18
- "gulp-typescript": "^3.1.2",
19
- "publish-please": "^2.2.0",
20
- "through2": "^2.0.0",
21
- "typedoc": "^0.5.1",
22
- "typescript": "^2.0.6"
23
- },
24
- "keywords": [
25
- "html",
26
- "parser",
27
- "html5",
28
- "WHATWG",
29
- "specification",
30
- "fast",
31
- "html parser",
32
- "html5 parser",
33
- "htmlparser",
34
- "parse5",
35
- "serializer",
36
- "html serializer",
37
- "htmlserializer",
38
- "sax",
39
- "simple api",
40
- "parse",
41
- "tokenize",
42
- "serialize",
43
- "tokenizer"
44
- ],
45
- "license": "MIT",
46
- "main": "./lib/index.js",
47
- "types": "./lib/index.d.ts",
48
- "repository": {
49
- "type": "git",
50
- "url": "git://github.com/inikulin/parse5.git"
51
- },
52
- "scripts": {
53
- "test": "gulp test",
54
- "publish-please": "publish-please",
55
- "prepublish": "publish-please guard"
56
- },
57
- "files": [
58
- "lib"
59
- ],
60
- "dependencies": {
61
- "@types/node": "^6.0.46"
62
- }
63
- }
1
+ {
2
+ "name": "parse5",
3
+ "description": "HTML parsing/serialization toolset for Node.js. WHATWG HTML Living Standard (aka HTML5)-compliant.",
4
+ "version": "4.0.0",
5
+ "author": "Ivan Nikulin <ifaaan@gmail.com> (https://github.com/inikulin)",
6
+ "contributors": "https://github.com/inikulin/parse5/graphs/contributors",
7
+ "homepage": "https://github.com/inikulin/parse5",
8
+ "devDependencies": {
9
+ "@types/node": "*",
10
+ "del": "^2.0.2",
11
+ "gulp": "^3.9.0",
12
+ "gulp-benchmark": "^1.1.1",
13
+ "gulp-download": "0.0.1",
14
+ "gulp-eslint": "^3.0.1",
15
+ "gulp-install": "^0.6.0",
16
+ "gulp-mocha": "^2.1.3",
17
+ "gulp-rename": "^1.2.2",
18
+ "gulp-typedoc": "^2.0.0",
19
+ "gulp-typescript": "^3.1.2",
20
+ "publish-please": "^2.2.0",
21
+ "through2": "^2.0.0",
22
+ "typedoc": "^0.5.1",
23
+ "typescript": "^2.0.6"
24
+ },
25
+ "keywords": [
26
+ "html",
27
+ "parser",
28
+ "html5",
29
+ "WHATWG",
30
+ "specification",
31
+ "fast",
32
+ "html parser",
33
+ "html5 parser",
34
+ "htmlparser",
35
+ "parse5",
36
+ "serializer",
37
+ "html serializer",
38
+ "htmlserializer",
39
+ "sax",
40
+ "simple api",
41
+ "parse",
42
+ "tokenize",
43
+ "serialize",
44
+ "tokenizer"
45
+ ],
46
+ "license": "MIT",
47
+ "main": "./lib/index.js",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git://github.com/inikulin/parse5.git"
51
+ },
52
+ "scripts": {
53
+ "test": "gulp test",
54
+ "publish-please": "publish-please",
55
+ "prepublish": "publish-please guard"
56
+ },
57
+ "files": [
58
+ "lib"
59
+ ]
60
+ }
@@ -1,210 +0,0 @@
1
- 'use strict';
2
-
3
- var OpenElementStack = require('../parser/open_element_stack'),
4
- Tokenizer = require('../tokenizer'),
5
- HTML = require('../common/html');
6
-
7
-
8
- //Aliases
9
- var $ = HTML.TAG_NAMES;
10
-
11
-
12
- function setEndLocation(element, closingToken, treeAdapter) {
13
- var loc = element.__location;
14
-
15
- if (!loc)
16
- return;
17
-
18
- if (!loc.startTag) {
19
- loc.startTag = {
20
- line: loc.line,
21
- col: loc.col,
22
- startOffset: loc.startOffset,
23
- endOffset: loc.endOffset
24
- };
25
- if (loc.attrs)
26
- loc.startTag.attrs = loc.attrs;
27
- }
28
-
29
- if (closingToken.location) {
30
- var ctLocation = closingToken.location,
31
- tn = treeAdapter.getTagName(element),
32
- // NOTE: For cases like <p> <p> </p> - First 'p' closes without a closing tag and
33
- // for cases like <td> <p> </td> - 'p' closes without a closing tag
34
- isClosingEndTag = closingToken.type === Tokenizer.END_TAG_TOKEN &&
35
- tn === closingToken.tagName;
36
-
37
- if (isClosingEndTag) {
38
- loc.endTag = {
39
- line: ctLocation.line,
40
- col: ctLocation.col,
41
- startOffset: ctLocation.startOffset,
42
- endOffset: ctLocation.endOffset
43
- };
44
- }
45
-
46
- if (isClosingEndTag)
47
- loc.endOffset = ctLocation.endOffset;
48
- else
49
- loc.endOffset = ctLocation.startOffset;
50
- }
51
- }
52
-
53
-
54
- exports.assign = function (parser) {
55
- //NOTE: obtain Parser proto this way to avoid module circular references
56
- var parserProto = Object.getPrototypeOf(parser),
57
- treeAdapter = parser.treeAdapter,
58
- attachableElementLocation = null,
59
- lastFosterParentingLocation = null,
60
- currentToken = null;
61
-
62
-
63
- //NOTE: patch _bootstrap method
64
- parser._bootstrap = function (document, fragmentContext) {
65
- parserProto._bootstrap.call(this, document, fragmentContext);
66
-
67
- attachableElementLocation = null;
68
- lastFosterParentingLocation = null;
69
- currentToken = null;
70
-
71
- //OpenElementStack
72
- parser.openElements.pop = function () {
73
- setEndLocation(this.current, currentToken, treeAdapter);
74
- OpenElementStack.prototype.pop.call(this);
75
- };
76
-
77
- parser.openElements.popAllUpToHtmlElement = function () {
78
- for (var i = this.stackTop; i > 0; i--)
79
- setEndLocation(this.items[i], currentToken, treeAdapter);
80
-
81
- OpenElementStack.prototype.popAllUpToHtmlElement.call(this);
82
- };
83
-
84
- parser.openElements.remove = function (element) {
85
- setEndLocation(element, currentToken, treeAdapter);
86
- OpenElementStack.prototype.remove.call(this, element);
87
- };
88
- };
89
-
90
-
91
- //Token processing
92
- parser._processTokenInForeignContent = function (token) {
93
- currentToken = token;
94
- parserProto._processTokenInForeignContent.call(this, token);
95
- };
96
-
97
- parser._processToken = function (token) {
98
- currentToken = token;
99
- parserProto._processToken.call(this, token);
100
-
101
- //NOTE: <body> and <html> are never popped from the stack, so we need to updated
102
- //their end location explicitly.
103
- if (token.type === Tokenizer.END_TAG_TOKEN &&
104
- (token.tagName === $.HTML ||
105
- token.tagName === $.BODY && this.openElements.hasInScope($.BODY))) {
106
- for (var i = this.openElements.stackTop; i >= 0; i--) {
107
- var element = this.openElements.items[i];
108
-
109
- if (this.treeAdapter.getTagName(element) === token.tagName) {
110
- setEndLocation(element, token, treeAdapter);
111
- break;
112
- }
113
- }
114
- }
115
- };
116
-
117
-
118
- //Doctype
119
- parser._setDocumentType = function (token) {
120
- parserProto._setDocumentType.call(this, token);
121
-
122
- var documentChildren = this.treeAdapter.getChildNodes(this.document),
123
- cnLength = documentChildren.length;
124
-
125
- for (var i = 0; i < cnLength; i++) {
126
- var node = documentChildren[i];
127
-
128
- if (this.treeAdapter.isDocumentTypeNode(node)) {
129
- node.__location = token.location;
130
- break;
131
- }
132
- }
133
- };
134
-
135
-
136
- //Elements
137
- parser._attachElementToTree = function (element) {
138
- //NOTE: _attachElementToTree is called from _appendElement, _insertElement and _insertTemplate methods.
139
- //So we will use token location stored in this methods for the element.
140
- element.__location = attachableElementLocation || null;
141
- attachableElementLocation = null;
142
- parserProto._attachElementToTree.call(this, element);
143
- };
144
-
145
- parser._appendElement = function (token, namespaceURI) {
146
- attachableElementLocation = token.location;
147
- parserProto._appendElement.call(this, token, namespaceURI);
148
- };
149
-
150
- parser._insertElement = function (token, namespaceURI) {
151
- attachableElementLocation = token.location;
152
- parserProto._insertElement.call(this, token, namespaceURI);
153
- };
154
-
155
- parser._insertTemplate = function (token) {
156
- attachableElementLocation = token.location;
157
- parserProto._insertTemplate.call(this, token);
158
-
159
- var tmplContent = this.treeAdapter.getTemplateContent(this.openElements.current);
160
-
161
- tmplContent.__location = null;
162
- };
163
-
164
- parser._insertFakeRootElement = function () {
165
- parserProto._insertFakeRootElement.call(this);
166
- this.openElements.current.__location = null;
167
- };
168
-
169
-
170
- //Comments
171
- parser._appendCommentNode = function (token, parent) {
172
- parserProto._appendCommentNode.call(this, token, parent);
173
-
174
- var children = this.treeAdapter.getChildNodes(parent),
175
- commentNode = children[children.length - 1];
176
-
177
- commentNode.__location = token.location;
178
- };
179
-
180
-
181
- //Text
182
- parser._findFosterParentingLocation = function () {
183
- //NOTE: store last foster parenting location, so we will be able to find inserted text
184
- //in case of foster parenting
185
- lastFosterParentingLocation = parserProto._findFosterParentingLocation.call(this);
186
- return lastFosterParentingLocation;
187
- };
188
-
189
- parser._insertCharacters = function (token) {
190
- parserProto._insertCharacters.call(this, token);
191
-
192
- var hasFosterParent = this._shouldFosterParentOnInsertion(),
193
- parent = hasFosterParent && lastFosterParentingLocation.parent ||
194
- this.openElements.currentTmplContent ||
195
- this.openElements.current,
196
- siblings = this.treeAdapter.getChildNodes(parent),
197
- textNodeIdx = hasFosterParent && lastFosterParentingLocation.beforeElement ?
198
- siblings.indexOf(lastFosterParentingLocation.beforeElement) - 1 :
199
- siblings.length - 1,
200
- textNode = siblings[textNodeIdx];
201
-
202
- //NOTE: if we have location assigned by another token, then just update end position
203
- if (textNode.__location)
204
- textNode.__location.endOffset = token.location.endOffset;
205
-
206
- else
207
- textNode.__location = token.location;
208
- };
209
- };
210
-