@cjser/parse5 8.0.1-cjser.2 → 8.1.0-cjser.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/dist/index.d.ts CHANGED
@@ -16,8 +16,8 @@ export declare namespace DefaultTreeAdapterTypes {
16
16
  type Node = DefaultTreeAdapter.Node;
17
17
  type DefaultTreeAdapterMap = DefaultTreeAdapter.DefaultTreeAdapterMap;
18
18
  }
19
- export type { TreeAdapter, TreeAdapterTypeMap } from './tree-adapters/interface.js';
20
- export { type ParserOptions, /** @internal */ Parser } from './parser/index.js';
19
+ export type { ElementCreationContext, TreeAdapter, TreeAdapterTypeMap } from './tree-adapters/interface.js';
20
+ export { type ParserOptions, /** @internal */ Parser, InsertionMode } from './parser/index.js';
21
21
  export { serialize, serializeOuter, type SerializerOptions } from './serializer/index.js';
22
22
  export { ERR as ErrorCodes, type ParserError, type ParserErrorHandler } from './common/error-codes.js';
23
23
  /** @internal */
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Parser } from './parser/index.js';
2
2
  export { defaultTreeAdapter } from './tree-adapters/default.js';
3
- export { /** @internal */ Parser } from './parser/index.js';
3
+ export { /** @internal */ Parser, InsertionMode } from './parser/index.js';
4
4
  export { serialize, serializeOuter } from './serializer/index.js';
5
5
  export { ERR as ErrorCodes } from './common/error-codes.js';
6
6
  /** @internal */
@@ -4,8 +4,8 @@ import { FormattingElementList } from './formatting-element-list.js';
4
4
  import { ERR, type ParserErrorHandler } from '../common/error-codes.js';
5
5
  import { TAG_ID as $, NS } from '../common/html.js';
6
6
  import type { TreeAdapter, TreeAdapterTypeMap } from '../tree-adapters/interface.js';
7
- import { type Token, type CommentToken, type CharacterToken, type TagToken, type DoctypeToken, type EOFToken, type LocationWithAttributes } from '../common/token.js';
8
- declare enum InsertionMode {
7
+ import { type Token, type CommentToken, type CharacterToken, type TagToken, type DoctypeToken, type EOFToken, type LocationWithAttributes, type Attribute } from '../common/token.js';
8
+ export declare enum InsertionMode {
9
9
  INITIAL = 0,
10
10
  BEFORE_HTML = 1,
11
11
  BEFORE_HEAD = 2,
@@ -30,6 +30,10 @@ declare enum InsertionMode {
30
30
  AFTER_AFTER_BODY = 21,
31
31
  AFTER_AFTER_FRAMESET = 22
32
32
  }
33
+ interface InsertionLocation<T extends TreeAdapterTypeMap> {
34
+ parent: T['parentNode'];
35
+ beforeElement: T['element'] | null;
36
+ }
33
37
  export interface ParserOptions<T extends TreeAdapterTypeMap> {
34
38
  /**
35
39
  * The [scripting flag](https://html.spec.whatwg.org/multipage/parsing.html#scripting-flag). If set
@@ -134,7 +138,11 @@ export declare class Parser<T extends TreeAdapterTypeMap> implements TokenHandle
134
138
  /** @protected */
135
139
  _setDocumentType(token: DoctypeToken): void;
136
140
  /** @protected */
137
- _attachElementToTree(element: T['element'], location: LocationWithAttributes | null): void;
141
+ _attachElementToTree(element: T['element'], location: LocationWithAttributes | null, insertionLocation: InsertionLocation<T>): void;
142
+ /** @protected */
143
+ _getInsertionLocation(): InsertionLocation<T>;
144
+ /** @protected */
145
+ _createElement(tagName: string, namespaceURI: NS, attrs: Attribute[], intendedParent: T['parentNode']): T['element'];
138
146
  /**
139
147
  * For self-closing tags. Add an element to the tree, but skip adding it
140
148
  * to the stack.
@@ -177,10 +185,7 @@ export declare class Parser<T extends TreeAdapterTypeMap> implements TokenHandle
177
185
  /** @protected */
178
186
  _shouldFosterParentOnInsertion(): boolean;
179
187
  /** @protected */
180
- _findFosterParentingLocation(): {
181
- parent: T['parentNode'];
182
- beforeElement: T['element'] | null;
183
- };
188
+ _findFosterParentingLocation(): InsertionLocation<T>;
184
189
  /** @protected */
185
190
  _fosterParentElement(element: T['element']): void;
186
191
  /** @protected */
@@ -14,7 +14,7 @@ const HIDDEN_INPUT_TYPE = 'hidden';
14
14
  const AA_OUTER_LOOP_ITER = 8;
15
15
  const AA_INNER_LOOP_ITER = 3;
16
16
  //Insertion modes
17
- var InsertionMode;
17
+ export var InsertionMode;
18
18
  (function (InsertionMode) {
19
19
  InsertionMode[InsertionMode["INITIAL"] = 0] = "INITIAL";
20
20
  InsertionMode[InsertionMode["BEFORE_HTML"] = 1] = "BEFORE_HTML";
@@ -273,7 +273,7 @@ export class Parser {
273
273
  }
274
274
  }
275
275
  /** @protected */
276
- _attachElementToTree(element, location) {
276
+ _attachElementToTree(element, location, insertionLocation) {
277
277
  if (this.options.sourceCodeLocationInfo) {
278
278
  const loc = location && {
279
279
  ...location,
@@ -281,13 +281,34 @@ export class Parser {
281
281
  };
282
282
  this.treeAdapter.setNodeSourceCodeLocation(element, loc);
283
283
  }
284
- if (this._shouldFosterParentOnInsertion()) {
285
- this._fosterParentElement(element);
284
+ if (insertionLocation.beforeElement) {
285
+ this.treeAdapter.insertBefore(insertionLocation.parent, element, insertionLocation.beforeElement);
286
286
  }
287
287
  else {
288
- const parent = this.openElements.currentTmplContentOrNode;
289
- this.treeAdapter.appendChild(parent !== null && parent !== void 0 ? parent : this.document, element);
288
+ this.treeAdapter.appendChild(insertionLocation.parent, element);
289
+ }
290
+ }
291
+ /** @protected */
292
+ _getInsertionLocation() {
293
+ var _a;
294
+ if (this._shouldFosterParentOnInsertion()) {
295
+ return this._findFosterParentingLocation();
290
296
  }
297
+ return {
298
+ parent: (_a = this.openElements.currentTmplContentOrNode) !== null && _a !== void 0 ? _a : this.document,
299
+ beforeElement: null,
300
+ };
301
+ }
302
+ /** @protected */
303
+ _createElement(tagName, namespaceURI, attrs, intendedParent) {
304
+ var _a, _b;
305
+ const element = this.treeAdapter.createElement(tagName, namespaceURI, attrs);
306
+ (_b = (_a = this.treeAdapter).onElementCreated) === null || _b === void 0 ? void 0 : _b.call(_a, element, {
307
+ intendedParent,
308
+ formElement: this.formElement,
309
+ isInTemplate: this.openElements.tmplCount > 0,
310
+ });
311
+ return element;
291
312
  }
292
313
  /**
293
314
  * For self-closing tags. Add an element to the tree, but skip adding it
@@ -295,37 +316,41 @@ export class Parser {
295
316
  */
296
317
  /** @protected */
297
318
  _appendElement(token, namespaceURI) {
298
- const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs);
299
- this._attachElementToTree(element, token.location);
319
+ const insertionLocation = this._getInsertionLocation();
320
+ const element = this._createElement(token.tagName, namespaceURI, token.attrs, insertionLocation.parent);
321
+ this._attachElementToTree(element, token.location, insertionLocation);
300
322
  }
301
323
  /** @protected */
302
324
  _insertElement(token, namespaceURI) {
303
- const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs);
304
- this._attachElementToTree(element, token.location);
325
+ const insertionLocation = this._getInsertionLocation();
326
+ const element = this._createElement(token.tagName, namespaceURI, token.attrs, insertionLocation.parent);
327
+ this._attachElementToTree(element, token.location, insertionLocation);
305
328
  this.openElements.push(element, token.tagID);
306
329
  }
307
330
  /** @protected */
308
331
  _insertFakeElement(tagName, tagID) {
309
- const element = this.treeAdapter.createElement(tagName, NS.HTML, []);
310
- this._attachElementToTree(element, null);
332
+ const insertionLocation = this._getInsertionLocation();
333
+ const element = this._createElement(tagName, NS.HTML, [], insertionLocation.parent);
334
+ this._attachElementToTree(element, null, insertionLocation);
311
335
  this.openElements.push(element, tagID);
312
336
  }
313
337
  /** @protected */
314
338
  _insertTemplate(token) {
315
- const tmpl = this.treeAdapter.createElement(token.tagName, NS.HTML, token.attrs);
339
+ const insertionLocation = this._getInsertionLocation();
340
+ const tmpl = this._createElement(token.tagName, NS.HTML, token.attrs, insertionLocation.parent);
316
341
  const content = this.treeAdapter.createDocumentFragment();
317
342
  this.treeAdapter.setTemplateContent(tmpl, content);
318
- this._attachElementToTree(tmpl, token.location);
343
+ this._attachElementToTree(tmpl, token.location, insertionLocation);
319
344
  this.openElements.push(tmpl, token.tagID);
320
345
  if (this.options.sourceCodeLocationInfo)
321
346
  this.treeAdapter.setNodeSourceCodeLocation(content, null);
322
347
  }
323
348
  /** @protected */
324
349
  _insertFakeRootElement() {
325
- const element = this.treeAdapter.createElement(TN.HTML, NS.HTML, []);
350
+ const element = this._createElement(TN.HTML, NS.HTML, [], this.document);
326
351
  if (this.options.sourceCodeLocationInfo)
327
352
  this.treeAdapter.setNodeSourceCodeLocation(element, null);
328
- this.treeAdapter.appendChild(this.openElements.current, element);
353
+ this.treeAdapter.appendChild(this.document, element);
329
354
  this.openElements.push(element, $.HTML);
330
355
  }
331
356
  /** @protected */
@@ -1217,6 +1242,7 @@ function aaObtainFurthestBlock(p, formattingElementEntry) {
1217
1242
  function aaInnerLoop(p, furthestBlock, formattingElement) {
1218
1243
  let lastElement = furthestBlock;
1219
1244
  let nextElement = p.openElements.getCommonAncestor(furthestBlock);
1245
+ const intendedParent = p.openElements.getCommonAncestor(formattingElement);
1220
1246
  for (let i = 0, element = nextElement; element !== formattingElement; i++, element = nextElement) {
1221
1247
  //NOTE: store the next element for the next loop iteration (it may be deleted from the stack by step 9.5)
1222
1248
  nextElement = p.openElements.getCommonAncestor(element);
@@ -1230,7 +1256,7 @@ function aaInnerLoop(p, furthestBlock, formattingElement) {
1230
1256
  p.openElements.remove(element);
1231
1257
  }
1232
1258
  else {
1233
- element = aaRecreateElementFromEntry(p, elementEntry);
1259
+ element = aaRecreateElementFromEntry(p, elementEntry, intendedParent);
1234
1260
  if (lastElement === furthestBlock) {
1235
1261
  p.activeFormattingElements.bookmark = elementEntry;
1236
1262
  }
@@ -1242,9 +1268,9 @@ function aaInnerLoop(p, furthestBlock, formattingElement) {
1242
1268
  return lastElement;
1243
1269
  }
1244
1270
  //Step 13.7 of the algorithm
1245
- function aaRecreateElementFromEntry(p, elementEntry) {
1271
+ function aaRecreateElementFromEntry(p, elementEntry, intendedParent) {
1246
1272
  const ns = p.treeAdapter.getNamespaceURI(elementEntry.element);
1247
- const newElement = p.treeAdapter.createElement(elementEntry.token.tagName, ns, elementEntry.token.attrs);
1273
+ const newElement = p._createElement(elementEntry.token.tagName, ns, elementEntry.token.attrs, intendedParent);
1248
1274
  p.openElements.replace(elementEntry.element, newElement);
1249
1275
  elementEntry.element = newElement;
1250
1276
  return newElement;
@@ -1268,7 +1294,7 @@ function aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement) {
1268
1294
  function aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry) {
1269
1295
  const ns = p.treeAdapter.getNamespaceURI(formattingElementEntry.element);
1270
1296
  const { token } = formattingElementEntry;
1271
- const newElement = p.treeAdapter.createElement(token.tagName, ns, token.attrs);
1297
+ const newElement = p._createElement(token.tagName, ns, token.attrs, furthestBlock);
1272
1298
  p._adoptNodes(furthestBlock, newElement);
1273
1299
  p.treeAdapter.appendChild(furthestBlock, newElement);
1274
1300
  p.activeFormattingElements.insertElementAfterBookmark(newElement, token);
@@ -2227,7 +2253,10 @@ function genericEndTagInBody(p, token) {
2227
2253
  const element = p.openElements.items[i];
2228
2254
  const elementId = p.openElements.tagIDs[i];
2229
2255
  // Compare the tag name here, as the tag might not be a known tag with an ID.
2230
- if (tid === elementId && (tid !== $.UNKNOWN || p.treeAdapter.getTagName(element) === tn)) {
2256
+ // Only HTML elements match; a foreign element falls through to the special-element check.
2257
+ if (tid === elementId &&
2258
+ p.treeAdapter.getNamespaceURI(element) === NS.HTML &&
2259
+ (tid !== $.UNKNOWN || p.treeAdapter.getTagName(element) === tn)) {
2231
2260
  p.openElements.generateImpliedEndTagsWithExclusion(tid);
2232
2261
  if (p.openElements.stackTop >= i)
2233
2262
  p.openElements.shortenToLength(i);
@@ -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 '@cjser/entities/decode';
3
+ import { HtmlEntityDecoder } from '@cjser/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,17 +129,28 @@ 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
+ /**
141
+ * `[start, end)` of `preprocessor.html` still to be appended to the attribute value, comment data or character
142
+ * token being built (`_flushRun`): input that is used as is is appended as one slice, not a code point at a time.
143
+ */
144
+ protected runStart: number;
145
+ protected runEnd: number;
139
146
  constructor(options: TokenizerOptions, handler: TokenHandler);
140
147
  protected _err(code: ERR, cpOffset?: number): void;
141
148
  protected getCurrentLocation(offset: number): Location | null;
142
149
  protected _runParsingLoop(): void;
150
+ /** Emit buffered character tokens without completing the current token or emitting EOF. */
151
+ flushCharacters(): void;
152
+ private getCharacterTokenEndLocation;
153
+ private getCharacterTokenEndOffset;
143
154
  pause(): void;
144
155
  resume(writeCallback?: () => void): void;
145
156
  write(chunk: string, isLastChunk: boolean, writeCallback?: () => void): void;
@@ -152,19 +163,35 @@ export declare class Tokenizer {
152
163
  protected _createEndTagToken(): void;
153
164
  protected _createCommentToken(offset: number): void;
154
165
  protected _createDoctypeToken(initialName: string | null): void;
155
- protected _createCharacterToken(type: CharacterToken['type'], chars: string): void;
166
+ protected _createCharacterToken(type: CharacterToken['type']): void;
156
167
  protected _createAttr(attrNameFirstCh: string): void;
157
168
  protected _leaveAttrName(): void;
158
169
  protected _leaveAttrValue(): void;
170
+ /**
171
+ * Append the pending run and `text` to the attribute value, comment data or character token being built.
172
+ * The run is a string of its own: V8 copies a slice of fewer than 13 characters but lets a longer one share
173
+ * the input's buffer, which would keep that buffer alive for as long as any attribute value or text cut from
174
+ * it is; a concatenation is flattened into a buffer of its own when it is sliced.
175
+ */
176
+ protected _flushRun(text: string): void;
177
+ /** Extend the pending run by the current input code point, read as `cp`, or flush it and start a new one. */
178
+ protected _extendRun(cp: number): void;
179
+ /**
180
+ * Extend the pending run by `text`, the input that ends `cpOffset` code points after the current one, or flush
181
+ * the run and append `text` where the input differs (a NUL replacement, a decoded reference, a dropped chunk).
182
+ */
183
+ protected _extendRunWith(text: string, cpOffset: number): void;
159
184
  protected prepareToken(ct: Token): void;
160
185
  protected emitCurrentTagToken(): void;
161
186
  protected emitCurrentComment(ct: CommentToken): void;
162
187
  protected emitCurrentDoctype(ct: DoctypeToken): void;
163
188
  protected _emitCurrentCharacterToken(nextLocation: Location | null): void;
164
189
  protected _emitEOFToken(): void;
165
- protected _appendCharToCurrentCharacterToken(type: CharacterToken['type'], ch: string): void;
190
+ /** Make the current character token one of `type`, emitting the current one if it has another type. */
191
+ protected _ensureCharacterToken(type: CharacterToken['type']): void;
192
+ /** Emit the current input code point, read as `cp`. */
166
193
  protected _emitCodePoint(cp: number): void;
167
- protected _emitChars(ch: string): void;
194
+ protected _emitChars(ch: string, cpOffset?: number): void;
168
195
  protected _startCharacterReference(): void;
169
196
  protected _isCharacterReferenceInAttribute(): boolean;
170
197
  protected _flushCodePointConsumedAsCharacterReference(cp: number): void;