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
package/dist/serializer/index.js
CHANGED
|
@@ -51,11 +51,19 @@ const defaultOpts = { treeAdapter: defaultTreeAdapter, scriptingEnabled: true };
|
|
|
51
51
|
* @param options Serialization options.
|
|
52
52
|
*/
|
|
53
53
|
export function serialize(node, options) {
|
|
54
|
+
var _a;
|
|
54
55
|
const opts = { ...defaultOpts, ...options };
|
|
55
56
|
if (isVoidElement(node, opts)) {
|
|
56
57
|
return '';
|
|
57
58
|
}
|
|
58
|
-
|
|
59
|
+
const { treeAdapter } = opts;
|
|
60
|
+
// A template's children are those of its content fragment.
|
|
61
|
+
const container = treeAdapter.isElementNode(node) &&
|
|
62
|
+
treeAdapter.getTagName(node) === $.TEMPLATE &&
|
|
63
|
+
treeAdapter.getNamespaceURI(node) === NS.HTML
|
|
64
|
+
? treeAdapter.getTemplateContent(node)
|
|
65
|
+
: node;
|
|
66
|
+
return serializeNodes((_a = treeAdapter.getChildNodes(container)) !== null && _a !== void 0 ? _a : [], opts);
|
|
59
67
|
}
|
|
60
68
|
/**
|
|
61
69
|
* Serializes an AST element node to an HTML string, including the element node.
|
|
@@ -78,43 +86,49 @@ export function serialize(node, options) {
|
|
|
78
86
|
*/
|
|
79
87
|
export function serializeOuter(node, options) {
|
|
80
88
|
const opts = { ...defaultOpts, ...options };
|
|
81
|
-
return
|
|
89
|
+
return serializeNodes([node], opts);
|
|
82
90
|
}
|
|
83
|
-
function
|
|
91
|
+
function serializeNodes(nodes, options) {
|
|
92
|
+
var _a;
|
|
93
|
+
const { treeAdapter } = options;
|
|
94
|
+
// The depth of the tree is not bounded, so the open elements (their
|
|
95
|
+
// remaining siblings and end tags) are kept on explicit stacks.
|
|
96
|
+
const stack = [];
|
|
84
97
|
let html = '';
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
let index = 0;
|
|
99
|
+
for (;;) {
|
|
100
|
+
if (index < nodes.length) {
|
|
101
|
+
const node = nodes[index++];
|
|
102
|
+
if (treeAdapter.isElementNode(node)) {
|
|
103
|
+
const tn = treeAdapter.getTagName(node);
|
|
104
|
+
html += `<${tn}${serializeAttributes(node, options)}>`;
|
|
105
|
+
const isHtml = treeAdapter.getNamespaceURI(node) === NS.HTML;
|
|
106
|
+
if (!(isHtml && VOID_ELEMENTS.has(tn))) {
|
|
107
|
+
stack.push({ nodes, index, endTag: `</${tn}>` });
|
|
108
|
+
nodes =
|
|
109
|
+
(_a = treeAdapter.getChildNodes(isHtml && tn === $.TEMPLATE ? treeAdapter.getTemplateContent(node) : node)) !== null && _a !== void 0 ? _a : [];
|
|
110
|
+
index = 0;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else if (treeAdapter.isTextNode(node)) {
|
|
114
|
+
html += serializeTextNode(node, options);
|
|
115
|
+
}
|
|
116
|
+
else if (treeAdapter.isCommentNode(node)) {
|
|
117
|
+
html += serializeCommentNode(node, options);
|
|
118
|
+
}
|
|
119
|
+
else if (treeAdapter.isDocumentTypeNode(node)) {
|
|
120
|
+
html += serializeDocumentTypeNode(node, options);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const parent = stack.pop();
|
|
125
|
+
if (!parent) {
|
|
126
|
+
return html;
|
|
127
|
+
}
|
|
128
|
+
html += parent.endTag;
|
|
129
|
+
({ nodes, index } = parent);
|
|
95
130
|
}
|
|
96
131
|
}
|
|
97
|
-
return html;
|
|
98
|
-
}
|
|
99
|
-
function serializeNode(node, options) {
|
|
100
|
-
if (options.treeAdapter.isElementNode(node)) {
|
|
101
|
-
return serializeElement(node, options);
|
|
102
|
-
}
|
|
103
|
-
if (options.treeAdapter.isTextNode(node)) {
|
|
104
|
-
return serializeTextNode(node, options);
|
|
105
|
-
}
|
|
106
|
-
if (options.treeAdapter.isCommentNode(node)) {
|
|
107
|
-
return serializeCommentNode(node, options);
|
|
108
|
-
}
|
|
109
|
-
if (options.treeAdapter.isDocumentTypeNode(node)) {
|
|
110
|
-
return serializeDocumentTypeNode(node, options);
|
|
111
|
-
}
|
|
112
|
-
// Return an empty string for unknown nodes
|
|
113
|
-
return '';
|
|
114
|
-
}
|
|
115
|
-
function serializeElement(node, options) {
|
|
116
|
-
const tn = options.treeAdapter.getTagName(node);
|
|
117
|
-
return `<${tn}${serializeAttributes(node, options)}>${isVoidElement(node, options) ? '' : `${serializeChildNodes(node, options)}</${tn}>`}`;
|
|
118
132
|
}
|
|
119
133
|
function serializeAttributes(node, { treeAdapter }) {
|
|
120
134
|
let html = '';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Preprocessor } from './preprocessor.js';
|
|
2
2
|
import { type Token, type CharacterToken, type DoctypeToken, type TagToken, type EOFToken, type CommentToken, type Attribute, type Location } from '../common/token.js';
|
|
3
|
-
import {
|
|
3
|
+
import { HtmlEntityDecoder } from 'entities/decode';
|
|
4
4
|
import { ERR, type ParserErrorHandler } from '../common/error-codes.js';
|
|
5
5
|
declare const enum State {
|
|
6
6
|
DATA = 0,
|
|
@@ -118,9 +118,9 @@ export declare class Tokenizer {
|
|
|
118
118
|
state: State;
|
|
119
119
|
protected returnState: State;
|
|
120
120
|
/**
|
|
121
|
-
* We use `entities`' `
|
|
121
|
+
* We use `entities`' `HtmlEntityDecoder` to parse character references.
|
|
122
122
|
*
|
|
123
|
-
* All of the following states are handled by the `
|
|
123
|
+
* All of the following states are handled by the `HtmlEntityDecoder`:
|
|
124
124
|
*
|
|
125
125
|
* - Named character reference state
|
|
126
126
|
* - Numeric character reference state
|
|
@@ -129,21 +129,35 @@ export declare class Tokenizer {
|
|
|
129
129
|
* - Decimal character reference state
|
|
130
130
|
* - Numeric character reference end state
|
|
131
131
|
*/
|
|
132
|
-
protected entityDecoder:
|
|
132
|
+
protected entityDecoder: HtmlEntityDecoder;
|
|
133
133
|
protected entityStartPos: number;
|
|
134
134
|
protected consumedAfterSnapshot: number;
|
|
135
135
|
protected currentLocation: Location | null;
|
|
136
136
|
protected currentCharacterToken: CharacterToken | null;
|
|
137
137
|
protected currentToken: Token | null;
|
|
138
138
|
protected currentAttr: Attribute;
|
|
139
|
+
private currentAttrLocation;
|
|
140
|
+
private currentAttrNames;
|
|
141
|
+
/**
|
|
142
|
+
* `[start, end)` of `preprocessor.html` still to be appended to the attribute value, comment data or character
|
|
143
|
+
* token being built (`_flushRun`): input that is used as is is appended as one slice, not a code point at a time.
|
|
144
|
+
*/
|
|
145
|
+
protected runStart: number;
|
|
146
|
+
protected runEnd: number;
|
|
139
147
|
constructor(options: TokenizerOptions, handler: TokenHandler);
|
|
140
148
|
protected _err(code: ERR, cpOffset?: number): void;
|
|
141
149
|
protected getCurrentLocation(offset: number): Location | null;
|
|
142
150
|
protected _runParsingLoop(): void;
|
|
151
|
+
/** Emit buffered character tokens without completing the current token or emitting EOF. */
|
|
152
|
+
flushCharacters(): void;
|
|
153
|
+
private getCharacterTokenEndLocation;
|
|
154
|
+
private getCharacterTokenEndOffset;
|
|
143
155
|
pause(): void;
|
|
144
156
|
resume(writeCallback?: () => void): void;
|
|
145
157
|
write(chunk: string, isLastChunk: boolean, writeCallback?: () => void): void;
|
|
146
158
|
insertHtmlAtCurrentPos(chunk: string): void;
|
|
159
|
+
/** @internal Flush completed streaming text without splitting an unfinished token. */
|
|
160
|
+
flushCurrentCharacterToken(): void;
|
|
147
161
|
protected _ensureHibernation(): boolean;
|
|
148
162
|
protected _consume(): number;
|
|
149
163
|
protected _advanceBy(count: number): void;
|
|
@@ -152,19 +166,35 @@ export declare class Tokenizer {
|
|
|
152
166
|
protected _createEndTagToken(): void;
|
|
153
167
|
protected _createCommentToken(offset: number): void;
|
|
154
168
|
protected _createDoctypeToken(initialName: string | null): void;
|
|
155
|
-
protected _createCharacterToken(type: CharacterToken['type']
|
|
169
|
+
protected _createCharacterToken(type: CharacterToken['type']): void;
|
|
156
170
|
protected _createAttr(attrNameFirstCh: string): void;
|
|
157
171
|
protected _leaveAttrName(): void;
|
|
158
172
|
protected _leaveAttrValue(): void;
|
|
173
|
+
/**
|
|
174
|
+
* Append the pending run and `text` to the attribute value, comment data or character token being built.
|
|
175
|
+
* The run is a string of its own: V8 copies a slice of fewer than 13 characters but lets a longer one share
|
|
176
|
+
* the input's buffer, which would keep that buffer alive for as long as any attribute value or text cut from
|
|
177
|
+
* it is; a concatenation is flattened into a buffer of its own when it is sliced.
|
|
178
|
+
*/
|
|
179
|
+
protected _flushRun(text: string): void;
|
|
180
|
+
/** Extend the pending run by the current input code point, read as `cp`, or flush it and start a new one. */
|
|
181
|
+
protected _extendRun(cp: number): void;
|
|
182
|
+
/**
|
|
183
|
+
* Extend the pending run by `text`, the input that ends `cpOffset` code points after the current one, or flush
|
|
184
|
+
* the run and append `text` where the input differs (a NUL replacement, a decoded reference, a dropped chunk).
|
|
185
|
+
*/
|
|
186
|
+
protected _extendRunWith(text: string, cpOffset: number): void;
|
|
159
187
|
protected prepareToken(ct: Token): void;
|
|
160
188
|
protected emitCurrentTagToken(): void;
|
|
161
189
|
protected emitCurrentComment(ct: CommentToken): void;
|
|
162
190
|
protected emitCurrentDoctype(ct: DoctypeToken): void;
|
|
163
191
|
protected _emitCurrentCharacterToken(nextLocation: Location | null): void;
|
|
164
192
|
protected _emitEOFToken(): void;
|
|
165
|
-
|
|
193
|
+
/** Make the current character token one of `type`, emitting the current one if it has another type. */
|
|
194
|
+
protected _ensureCharacterToken(type: CharacterToken['type']): void;
|
|
195
|
+
/** Emit the current input code point, read as `cp`. */
|
|
166
196
|
protected _emitCodePoint(cp: number): void;
|
|
167
|
-
protected _emitChars(ch: string): void;
|
|
197
|
+
protected _emitChars(ch: string, cpOffset?: number): void;
|
|
168
198
|
protected _startCharacterReference(): void;
|
|
169
199
|
protected _isCharacterReferenceInAttribute(): boolean;
|
|
170
200
|
protected _flushCodePointConsumedAsCharacterReference(cp: number): void;
|