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.
@@ -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
- return serializeChildNodes(node, opts);
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 serializeNode(node, opts);
89
+ return serializeNodes([node], opts);
82
90
  }
83
- function serializeChildNodes(parentNode, options) {
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
- // Get container of the child nodes
86
- const container = options.treeAdapter.isElementNode(parentNode) &&
87
- options.treeAdapter.getTagName(parentNode) === $.TEMPLATE &&
88
- options.treeAdapter.getNamespaceURI(parentNode) === NS.HTML
89
- ? options.treeAdapter.getTemplateContent(parentNode)
90
- : parentNode;
91
- const childNodes = options.treeAdapter.getChildNodes(container);
92
- if (childNodes) {
93
- for (const currentNode of childNodes) {
94
- html += serializeNode(currentNode, options);
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 { EntityDecoder } from 'entities/decode';
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`' `EntityDecoder` to parse character references.
121
+ * We use `entities`' `HtmlEntityDecoder` to parse character references.
122
122
  *
123
- * All of the following states are handled by the `EntityDecoder`:
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: 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'], chars: string): void;
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
- protected _appendCharToCurrentCharacterToken(type: CharacterToken['type'], ch: string): void;
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;