parse5 8.0.1 → 8.1.1
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/README.md +12 -0
- package/dist/common/foreign-content.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/parser/formatting-element-list.d.ts +38 -10
- package/dist/parser/formatting-element-list.js +153 -70
- package/dist/parser/index.d.ts +18 -9
- package/dist/parser/index.js +215 -152
- package/dist/parser/open-element-stack.d.ts +85 -8
- package/dist/parser/open-element-stack.js +500 -150
- package/dist/serializer/index.js +48 -34
- package/dist/tokenizer/index.d.ts +37 -7
- package/dist/tokenizer/index.js +321 -71
- package/dist/tokenizer/preprocessor.d.ts +8 -1
- package/dist/tokenizer/preprocessor.js +20 -7
- package/dist/tree-adapters/default.js +50 -6
- package/dist/tree-adapters/interface.d.ts +17 -0
- package/package.json +7 -3
|
@@ -18,6 +18,9 @@ export declare class Preprocessor {
|
|
|
18
18
|
});
|
|
19
19
|
/** The column on the current line. If we just saw a gap (eg. a surrogate pair), return the index before. */
|
|
20
20
|
get col(): number;
|
|
21
|
+
/** The index in `html` at which the current code point starts (the high surrogate of a pair). */
|
|
22
|
+
get codePointStart(): number;
|
|
23
|
+
/** The offset of the current code point, before the second unit of a surrogate pair. */
|
|
21
24
|
get offset(): number;
|
|
22
25
|
getError(code: ERR, cpOffset: number): ParserError;
|
|
23
26
|
private lastErrOffset;
|
|
@@ -25,7 +28,11 @@ export declare class Preprocessor {
|
|
|
25
28
|
private _addGap;
|
|
26
29
|
private _processSurrogate;
|
|
27
30
|
willDropParsedChunk(): boolean;
|
|
28
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Drop the parsed input, except for the current code point and the `keep` code units before it. The gap history is
|
|
33
|
+
* reset, so the kept units must contain no preprocessing gap.
|
|
34
|
+
*/
|
|
35
|
+
dropParsedChunk(keep?: number): void;
|
|
29
36
|
write(chunk: string, isLastChunk: boolean): void;
|
|
30
37
|
insertHtmlAtCurrentPos(chunk: string): void;
|
|
31
38
|
startsWith(pattern: string, caseSensitive: boolean): boolean;
|
|
@@ -28,8 +28,13 @@ export class Preprocessor {
|
|
|
28
28
|
get col() {
|
|
29
29
|
return this.pos - this.lineStartPos + Number(this.lastGapPos !== this.pos);
|
|
30
30
|
}
|
|
31
|
+
/** The index in `html` at which the current code point starts (the high surrogate of a pair). */
|
|
32
|
+
get codePointStart() {
|
|
33
|
+
return this.pos - Number(this.lastGapPos === this.pos);
|
|
34
|
+
}
|
|
35
|
+
/** The offset of the current code point, before the second unit of a surrogate pair. */
|
|
31
36
|
get offset() {
|
|
32
|
-
return this.droppedBufferSize + this.
|
|
37
|
+
return this.droppedBufferSize + this.codePointStart;
|
|
33
38
|
}
|
|
34
39
|
getError(code, cpOffset) {
|
|
35
40
|
const { line, col, offset } = this;
|
|
@@ -59,7 +64,7 @@ export class Preprocessor {
|
|
|
59
64
|
//NOTE: try to peek a surrogate pair
|
|
60
65
|
if (this.pos !== this.html.length - 1) {
|
|
61
66
|
const nextCp = this.html.charCodeAt(this.pos + 1);
|
|
62
|
-
if (isSurrogatePair(nextCp)) {
|
|
67
|
+
if (!isSurrogatePair(cp) && isSurrogatePair(nextCp)) {
|
|
63
68
|
//NOTE: we have a surrogate pair. Peek pair character and recalculate code point.
|
|
64
69
|
this.pos++;
|
|
65
70
|
//NOTE: add a gap that should be avoided during retreat
|
|
@@ -79,14 +84,22 @@ export class Preprocessor {
|
|
|
79
84
|
willDropParsedChunk() {
|
|
80
85
|
return this.pos > this.bufferWaterline;
|
|
81
86
|
}
|
|
82
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Drop the parsed input, except for the current code point and the `keep` code units before it. The gap history is
|
|
89
|
+
* reset, so the kept units must contain no preprocessing gap.
|
|
90
|
+
*/
|
|
91
|
+
dropParsedChunk(keep = 0) {
|
|
83
92
|
if (this.willDropParsedChunk()) {
|
|
84
|
-
|
|
85
|
-
this.
|
|
86
|
-
this.
|
|
87
|
-
this.
|
|
93
|
+
// Retain the entire current code point, including both units of a surrogate pair.
|
|
94
|
+
const droppedSize = this.offset - this.droppedBufferSize - keep;
|
|
95
|
+
this.html = this.html.substring(droppedSize);
|
|
96
|
+
this.lineStartPos -= droppedSize;
|
|
97
|
+
this.droppedBufferSize += droppedSize;
|
|
98
|
+
this.pos -= droppedSize;
|
|
88
99
|
this.lastGapPos = -2;
|
|
89
100
|
this.gapStack.length = 0;
|
|
101
|
+
if (this.pos > keep)
|
|
102
|
+
this._addGap();
|
|
90
103
|
}
|
|
91
104
|
}
|
|
92
105
|
write(chunk, isLastChunk) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { DOCUMENT_MODE } from '../common/html.js';
|
|
2
|
+
/** The position of each name in the attribute lists `adoptAttributes` merged into, kept for the next merge into that list. */
|
|
3
|
+
const attrListIndexes = new WeakMap();
|
|
2
4
|
export const defaultTreeAdapter = {
|
|
3
5
|
//Node construction
|
|
4
6
|
createDocument() {
|
|
@@ -44,8 +46,12 @@ export const defaultTreeAdapter = {
|
|
|
44
46
|
newNode.parentNode = parentNode;
|
|
45
47
|
},
|
|
46
48
|
insertBefore(parentNode, newNode, referenceNode) {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
+
const { childNodes } = parentNode;
|
|
50
|
+
// The reference is usually the last child (fostered content precedes the table), so check that first.
|
|
51
|
+
const insertionIdx = childNodes[childNodes.length - 1] === referenceNode
|
|
52
|
+
? childNodes.length - 1
|
|
53
|
+
: childNodes.indexOf(referenceNode);
|
|
54
|
+
childNodes.splice(insertionIdx, 0, newNode);
|
|
49
55
|
newNode.parentNode = parentNode;
|
|
50
56
|
},
|
|
51
57
|
setTemplateContent(templateElement, contentElement) {
|
|
@@ -80,8 +86,10 @@ export const defaultTreeAdapter = {
|
|
|
80
86
|
},
|
|
81
87
|
detachNode(node) {
|
|
82
88
|
if (node.parentNode) {
|
|
83
|
-
const
|
|
84
|
-
|
|
89
|
+
const { childNodes } = node.parentNode;
|
|
90
|
+
// The parser detaches from the end when it moves children, so check the last child first.
|
|
91
|
+
const idx = childNodes[childNodes.length - 1] === node ? childNodes.length - 1 : childNodes.indexOf(node);
|
|
92
|
+
childNodes.splice(idx, 1);
|
|
85
93
|
node.parentNode = null;
|
|
86
94
|
}
|
|
87
95
|
},
|
|
@@ -96,7 +104,11 @@ export const defaultTreeAdapter = {
|
|
|
96
104
|
defaultTreeAdapter.appendChild(parentNode, defaultTreeAdapter.createTextNode(text));
|
|
97
105
|
},
|
|
98
106
|
insertTextBefore(parentNode, text, referenceNode) {
|
|
99
|
-
const
|
|
107
|
+
const { childNodes } = parentNode;
|
|
108
|
+
const referenceIdx = childNodes[childNodes.length - 1] === referenceNode
|
|
109
|
+
? childNodes.length - 1
|
|
110
|
+
: childNodes.indexOf(referenceNode);
|
|
111
|
+
const prevNode = childNodes[referenceIdx - 1];
|
|
100
112
|
if (prevNode && defaultTreeAdapter.isTextNode(prevNode)) {
|
|
101
113
|
prevNode.value += text;
|
|
102
114
|
}
|
|
@@ -104,13 +116,45 @@ export const defaultTreeAdapter = {
|
|
|
104
116
|
defaultTreeAdapter.insertBefore(parentNode, defaultTreeAdapter.createTextNode(text), referenceNode);
|
|
105
117
|
}
|
|
106
118
|
},
|
|
119
|
+
/**
|
|
120
|
+
* Keeps the recipient's names between merges as a map from name to position. The map is rebuilt when the list changed
|
|
121
|
+
* length or has a repeated name, or when a merged name it holds no longer names the entry at that position. A name that
|
|
122
|
+
* other code gives an existing entry between two merges is missed otherwise, and merging it appends it a second time.
|
|
123
|
+
*/
|
|
107
124
|
adoptAttributes(recipient, attrs) {
|
|
108
|
-
|
|
125
|
+
// Merging a list into itself adds nothing: every name is already present.
|
|
126
|
+
if (attrs.length === 0 || attrs === recipient.attrs)
|
|
127
|
+
return;
|
|
128
|
+
const cached = attrListIndexes.get(recipient.attrs);
|
|
129
|
+
const isIndexed = (cached === null || cached === void 0 ? void 0 : cached.size) === recipient.attrs.length &&
|
|
130
|
+
attrs.every(({ name }) => {
|
|
131
|
+
var _a;
|
|
132
|
+
const index = cached.get(name);
|
|
133
|
+
return index === undefined || ((_a = recipient.attrs[index]) === null || _a === void 0 ? void 0 : _a.name) === name;
|
|
134
|
+
});
|
|
135
|
+
const recipientAttrsMap = isIndexed ? cached : new Map();
|
|
136
|
+
if (!isIndexed) {
|
|
137
|
+
for (let i = 0; i < recipient.attrs.length; i++) {
|
|
138
|
+
const attr = recipient.attrs[i];
|
|
139
|
+
// A sparse list is indexed again on every merge, as its size never matches its length.
|
|
140
|
+
if (attr !== undefined)
|
|
141
|
+
recipientAttrsMap.set(attr.name, i);
|
|
142
|
+
}
|
|
143
|
+
attrListIndexes.set(recipient.attrs, recipientAttrsMap);
|
|
144
|
+
}
|
|
145
|
+
const adoptedFrom = recipient.attrs.length;
|
|
109
146
|
for (let j = 0; j < attrs.length; j++) {
|
|
110
147
|
if (!recipientAttrsMap.has(attrs[j].name)) {
|
|
111
148
|
recipient.attrs.push(attrs[j]);
|
|
112
149
|
}
|
|
113
150
|
}
|
|
151
|
+
for (let j = adoptedFrom; j < recipient.attrs.length; j++) {
|
|
152
|
+
recipientAttrsMap.set(recipient.attrs[j].name, j);
|
|
153
|
+
}
|
|
154
|
+
// A list with a repeated name is indexed again on the next merge.
|
|
155
|
+
if (recipientAttrsMap.size !== recipient.attrs.length) {
|
|
156
|
+
attrListIndexes.delete(recipient.attrs);
|
|
157
|
+
}
|
|
114
158
|
},
|
|
115
159
|
//Tree traversing
|
|
116
160
|
getFirstChild(node) {
|
|
@@ -12,6 +12,15 @@ export interface TreeAdapterTypeMap<Node = unknown, ParentNode = unknown, ChildN
|
|
|
12
12
|
template: Template;
|
|
13
13
|
documentType: DocumentType;
|
|
14
14
|
}
|
|
15
|
+
/** Parser state needed to establish a form-associated element's parser-established form owner. */
|
|
16
|
+
export interface ElementCreationContext<T extends TreeAdapterTypeMap = TreeAdapterTypeMap> {
|
|
17
|
+
/** The target parent from the appropriate place for inserting a node. */
|
|
18
|
+
intendedParent: T['parentNode'];
|
|
19
|
+
/** The element referenced by the parser's form element pointer, if any. */
|
|
20
|
+
formElement: T['element'] | null;
|
|
21
|
+
/** Whether the stack of open elements contains an HTML `template` element. */
|
|
22
|
+
isInTemplate: boolean;
|
|
23
|
+
}
|
|
15
24
|
/**
|
|
16
25
|
* Tree adapter is a set of utility functions that provides minimal required abstraction layer beetween parser and a specific AST format.
|
|
17
26
|
* Note that `TreeAdapter` is not designed to be a general purpose AST manipulation library. You can build such library
|
|
@@ -240,6 +249,14 @@ export interface TreeAdapter<T extends TreeAdapterTypeMap = TreeAdapterTypeMap>
|
|
|
240
249
|
* @param contentElement - Content element.
|
|
241
250
|
*/
|
|
242
251
|
setTemplateContent(templateElement: T['template'], contentElement: T['documentFragment']): void;
|
|
252
|
+
/**
|
|
253
|
+
* Optional callback invoked after an element is created from a token and before it is inserted. This exposes the
|
|
254
|
+
* parser state needed to establish a form-associated element's parser-established form owner.
|
|
255
|
+
*
|
|
256
|
+
* @param element The created element.
|
|
257
|
+
* @param context Relevant parser state at the time of creation.
|
|
258
|
+
*/
|
|
259
|
+
onElementCreated?: (element: T['element'], context: ElementCreationContext<T>) => void;
|
|
243
260
|
/**
|
|
244
261
|
* Optional callback for elements being pushed to the stack of open elements.
|
|
245
262
|
*
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "parse5",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"description": "HTML parser and serializer.",
|
|
5
|
-
"version": "8.
|
|
5
|
+
"version": "8.1.1",
|
|
6
6
|
"author": "Ivan Nikulin <ifaaan@gmail.com> (https://github.com/inikulin)",
|
|
7
7
|
"contributors": [
|
|
8
8
|
"James Garbutt (https://github.com/43081j)",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"homepage": "https://parse5.js.org",
|
|
14
14
|
"funding": "https://github.com/inikulin/parse5?sponsor=1",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"entities": "^
|
|
16
|
+
"entities": "^9.0.0"
|
|
17
17
|
},
|
|
18
18
|
"keywords": [
|
|
19
19
|
"html",
|
|
@@ -49,5 +49,9 @@
|
|
|
49
49
|
"files": [
|
|
50
50
|
"dist/**/*.js",
|
|
51
51
|
"dist/**/*.d.ts"
|
|
52
|
-
]
|
|
52
|
+
],
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"parse5-htmlparser2-tree-adapter": "^8.0.0",
|
|
55
|
+
"parse5-test-utils": "1.0.0"
|
|
56
|
+
}
|
|
53
57
|
}
|