Skip to content
This repository was archived by the owner on Mar 24, 2022. It is now read-only.

Commit 1e3643e

Browse files
committed
refactor: Remove the last mixin
1 parent a40c20c commit 1e3643e

8 files changed

Lines changed: 84 additions & 152 deletions

File tree

packages/htmlparser2-tree-adapter/lib/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { ElementLocation } from './../../parse5/lib/tree-adapters/interface';
21
import * as doctype from '@parse5/parse5/lib/common/doctype.js';
32
import { DOCUMENT_MODE, NAMESPACES as NS } from '@parse5/parse5/lib/common/html.js';
4-
import type { Attribute } from '@parse5/parse5/lib/common/token.js';
3+
import type { Attribute, ElementLocation } from '@parse5/parse5/lib/common/token.js';
54
import type { TreeAdapterTypeMap } from '@parse5/parse5/lib/tree-adapters/interface.js';
65
import {
76
Node,

packages/parse5/lib/common/token.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ export interface LocationWithAttributes extends Location {
2929
attrs?: Record<string, Location>;
3030
}
3131

32+
export interface ElementLocation extends LocationWithAttributes {
33+
/** Start tag attributes' location info. */
34+
attrs?: Record<string, Location>;
35+
/** Element's start tag location info. */
36+
startTag?: Location;
37+
/**
38+
* Element's end tag location info.
39+
* This property is undefined, if the element has no closing tag.
40+
*/
41+
endTag?: Location;
42+
}
43+
3244
interface TokenBase {
3345
readonly type: TokenType;
3446
location: Location | null;

packages/parse5/lib/extensions/location-info/parser-mixin.ts

Lines changed: 0 additions & 90 deletions
This file was deleted.

packages/parse5/lib/parser/index.ts

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { Tokenizer, TokenizerMode } from '../tokenizer/index.js';
22
import { OpenElementStack } from './open-element-stack.js';
33
import { FormattingElementList, ElementEntry, EntryType } from './formatting-element-list.js';
4-
import { LocationInfoParserMixin } from '../extensions/location-info/parser-mixin.js';
5-
import { Mixin } from '../utils/mixin.js';
64
import * as defaultTreeAdapter from '../tree-adapters/default.js';
75
import * as doctype from '../common/doctype.js';
86
import * as foreignContent from '../common/foreign-content.js';
@@ -26,6 +24,7 @@ import {
2624
TagToken,
2725
DoctypeToken,
2826
LocationWithAttributes,
27+
ElementLocation,
2928
} from '../common/token.js';
3029

3130
//Misc constants
@@ -141,6 +140,7 @@ export class Parser<T extends TreeAdapterTypeMap> {
141140
treeAdapter: TreeAdapter<T>;
142141
pendingScript: null | T['element'];
143142
private onParseError: ParserErrorHandler | null;
143+
private currentToken: Token | null = null;
144144

145145
constructor(options?: ParserOptions<T>) {
146146
this.options = {
@@ -159,10 +159,6 @@ export class Parser<T extends TreeAdapterTypeMap> {
159159
if (this.options.onParseError) {
160160
this.options.sourceCodeLocationInfo = true;
161161
}
162-
163-
if (this.options.sourceCodeLocationInfo) {
164-
Mixin.install(this, LocationInfoParserMixin as any);
165-
}
166162
}
167163

168164
// API
@@ -250,8 +246,14 @@ export class Parser<T extends TreeAdapterTypeMap> {
250246

251247
this.headElement = null;
252248
this.formElement = null;
249+
this.currentToken = null;
253250

254251
this.openElements = new OpenElementStack(this.document, this.treeAdapter);
252+
253+
if (this.options.sourceCodeLocationInfo) {
254+
this.openElements.onItemPop = (element) => this._setEndLocation(element, this.currentToken!);
255+
}
256+
255257
this.activeFormattingElements = new FormattingElementList(this.treeAdapter);
256258

257259
this.tmplInsertionModeStack = [];
@@ -293,10 +295,15 @@ export class Parser<T extends TreeAdapterTypeMap> {
293295
break;
294296
}
295297

298+
this.currentToken = token;
299+
296300
if (this.skipNextNewLine) {
297301
this.skipNextNewLine = false;
298302

299-
if (token.type === TokenType.WHITESPACE_CHARACTER && token.chars[0] === '\n') {
303+
if (
304+
token.type === TokenType.WHITESPACE_CHARACTER &&
305+
token.chars.charCodeAt(0) === unicode.CODE_POINTS.LINE_FEED
306+
) {
300307
if (token.chars.length === 1) {
301308
continue;
302309
}
@@ -311,6 +318,14 @@ export class Parser<T extends TreeAdapterTypeMap> {
311318
break;
312319
}
313320
}
321+
322+
if (this.options.sourceCodeLocationInfo) {
323+
// NOTE: generate location info for elements
324+
// that remains on open element stack
325+
for (let i = this.openElements.stackTop; i >= 0; i--) {
326+
this._setEndLocation(this.openElements.items[i], this.currentToken!);
327+
}
328+
}
314329
}
315330

316331
runParsingLoopForCurrentChunk(
@@ -335,7 +350,7 @@ export class Parser<T extends TreeAdapterTypeMap> {
335350
}
336351

337352
//Text parsing
338-
_setupTokenizerCDATAMode() {
353+
private _setupTokenizerCDATAMode() {
339354
const current = this._getAdjustedCurrentElement();
340355

341356
this.tokenizer.allowCDATA =
@@ -523,6 +538,32 @@ export class Parser<T extends TreeAdapterTypeMap> {
523538
}
524539
}
525540

541+
private _setEndLocation(element: T['element'], closingToken: Token) {
542+
const loc = this.treeAdapter.getNodeSourceCodeLocation(element);
543+
544+
if (loc && closingToken.location) {
545+
const ctLoc = closingToken.location;
546+
const tn = this.treeAdapter.getTagName(element);
547+
548+
// NOTE: For cases like <p> <p> </p> - First 'p' closes without a closing
549+
// tag and for cases like <td> <p> </td> - 'p' closes without a closing tag.
550+
const isClosingEndTag = closingToken.type === TokenType.END_TAG && tn === closingToken.tagName;
551+
const endLoc: Partial<ElementLocation> = {};
552+
if (isClosingEndTag) {
553+
endLoc.endTag = { ...ctLoc };
554+
endLoc.endLine = ctLoc.endLine;
555+
endLoc.endCol = ctLoc.endCol;
556+
endLoc.endOffset = ctLoc.endOffset;
557+
} else {
558+
endLoc.endLine = ctLoc.startLine;
559+
endLoc.endCol = ctLoc.startCol;
560+
endLoc.endOffset = ctLoc.startOffset;
561+
}
562+
563+
this.treeAdapter.updateNodeSourceCodeLocation(element, endLoc);
564+
}
565+
}
566+
526567
//Token processing
527568
_shouldProcessTokenInForeignContent(token: Token) {
528569
const current = this._getAdjustedCurrentElement();
@@ -613,6 +654,23 @@ export class Parser<T extends TreeAdapterTypeMap> {
613654
} else if (this.insertionMode === InsertionMode.AFTER_AFTER_FRAMESET) {
614655
modeAfterAfterFrameset(this, token);
615656
}
657+
658+
//NOTE: <body> and <html> are never popped from the stack, so we need to updated
659+
//their end location explicitly.
660+
if (
661+
this.options.sourceCodeLocationInfo &&
662+
token.type === TokenType.END_TAG &&
663+
(token.tagName === $.HTML || (token.tagName === $.BODY && this.openElements.hasInScope($.BODY)))
664+
) {
665+
for (let i = this.openElements.stackTop; i >= 0; i--) {
666+
const element = this.openElements.items[i];
667+
668+
if (this.treeAdapter.getTagName(element) === token.tagName) {
669+
this._setEndLocation(element, token);
670+
break;
671+
}
672+
}
673+
}
616674
}
617675

618676
_processTokenInForeignContent(token: Token) {

packages/parse5/lib/tree-adapters/default.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DOCUMENT_MODE, NAMESPACES } from '../common/html.js';
2-
import type { Attribute, Location } from '../common/token.js';
3-
import type { TreeAdapterTypeMap, ElementLocation } from './interface.js';
2+
import type { Attribute, Location, ElementLocation } from '../common/token.js';
3+
import type { TreeAdapterTypeMap } from './interface.js';
44

55
export enum NodeType {
66
Document = '#document',

packages/parse5/lib/tree-adapters/interface.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DOCUMENT_MODE, NAMESPACES } from '../common/html.js';
2-
import type { Attribute, Location } from '../common/token.js';
2+
import type { Attribute, ElementLocation } from '../common/token.js';
33

44
export interface TreeAdapterTypeMap<
55
// eslint-disable-next-line @typescript-eslint/ban-types
@@ -25,18 +25,6 @@ export interface TreeAdapterTypeMap<
2525
documentType: DocumentType;
2626
}
2727

28-
export interface ElementLocation extends Location {
29-
/** Start tag attributes' location info. */
30-
attrs?: Record<string, Location>;
31-
/** Element's start tag location info. */
32-
startTag?: Location;
33-
/**
34-
* Element's end tag location info.
35-
* This property is undefined, if the element has no closing tag.
36-
*/
37-
endTag?: Location;
38-
}
39-
4028
/**
4129
* Tree adapter is a set of utility functions that provides minimal required abstraction layer beetween parser and a specific AST format.
4230
* Note that `TreeAdapter` is not designed to be a general purpose AST manipulation library. You can build such library

packages/parse5/lib/utils/mixin.ts

Lines changed: 0 additions & 35 deletions
This file was deleted.

test/utils/generate-location-info-parser-tests.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ParserOptions } from './../../packages/parse5/lib/parser/index';
2-
import { Location } from './../../packages/parse5/lib/common/token';
3-
import { TreeAdapter, TreeAdapterTypeMap, ElementLocation } from './../../packages/parse5/lib/tree-adapters/interface';
2+
import { Location, ElementLocation } from './../../packages/parse5/lib/common/token';
3+
import { TreeAdapter, TreeAdapterTypeMap } from './../../packages/parse5/lib/tree-adapters/interface';
44
import * as assert from 'assert';
55
import * as fs from 'fs';
66
import * as path from 'path';

0 commit comments

Comments
 (0)