Skip to content

Commit

Permalink
Merge pull request #63 from devongovett/source_locations
Browse files Browse the repository at this point in the history
Add optional support for source locations
  • Loading branch information
Scrum authored May 11, 2021
2 parents f0b209b + e18551c commit b7bade4
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 2 deletions.
5 changes: 5 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ Type: `Boolean`
Default: `false`
Description: *If set to true, self-closing tags will trigger the `onclosetag` event even if `xmlMode` is not set to `true`. NOTE: If `xmlMode` is set to `true` then self-closing tags will always be recognized.*

### `sourceLocations`
Type: `Boolean`
Default: `false`
Description: *If set to true, AST nodes will have a `location` property containing the `start` and `end` line and column position of the node.*

## License

[MIT](LICENSE)
18 changes: 16 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Parser, ParserOptions} from 'htmlparser2';
import {Directive, Node, Options, Attributes} from '../types/index.d';
import {Directive, Node, NodeTag, Options, Attributes} from '../types/index.d';
import {LocationTracker} from './location-tracker';

const defaultOptions: ParserOptions = {
lowerCaseTags: false,
Expand All @@ -16,6 +17,7 @@ const defaultDirectives: Directive[] = [
];

const parser = (html: string, options: Options = {}): Node[] => {
const locationTracker = new LocationTracker(html);
const bufArray: Node[] = [];
const results: Node[] = [];

Expand Down Expand Up @@ -92,7 +94,15 @@ const parser = (html: string, options: Options = {}): Node[] => {
}

function onopentag(tag: string, attrs: Attributes) {
const buf: Node = {tag};
const start = locationTracker.getPosition(parser.startIndex);
const buf: NodeTag = {tag};

if (options.sourceLocations) {
buf.location = {
start,
end: start
};
}

if (Object.keys(attrs).length > 0) {
buf.attrs = normalizeArributes(attrs);
Expand All @@ -104,6 +114,10 @@ const parser = (html: string, options: Options = {}): Node[] => {
function onclosetag() {
const buf: Node | undefined = bufArray.pop();

if (buf && typeof buf === 'object' && buf.location && parser.endIndex !== null) {
buf.location.end = locationTracker.getPosition(parser.endIndex);
}

if (buf) {
const last = bufferArrayLast();

Expand Down
39 changes: 39 additions & 0 deletions src/location-tracker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {Position} from '../types/index.d';

export class LocationTracker {
private readonly source: string;
private lastPosition: Position;
private lastIndex: number;

constructor(source: string) {
this.source = source;
this.lastPosition = {
line: 1,
column: 1
};

this.lastIndex = 0;
}

getPosition(index: number): Position {
if (index < this.lastIndex) {
throw new Error('Source indices must be monotonic');
}

while (this.lastIndex < index) {
if (this.source.charCodeAt(this.lastIndex) === /* \n */ 10) {
this.lastPosition.line++;
this.lastPosition.column = 1;
} else {
this.lastPosition.column++;
}

this.lastIndex++;
}

return {
line: this.lastPosition.line,
column: this.lastPosition.column
};
}
}
52 changes: 52 additions & 0 deletions test/test-core.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,55 @@ test('should be not converting html entity name', t => {
const expected = ['&zwnj;&nbsp;&copy;'];
t.deepEqual(tree, expected);
});

test('should parse with source locations', t => {
const html = '<h1>Test</h1>\n<p><b>Foo</b></p>';
const tree = parser(html, {sourceLocations: true});
const expected = [
{
tag: 'h1',
content: ['Test'],
location: {
start: {
line: 1,
column: 1
},
end: {
line: 1,
column: 13
}
}
},
'\n',
{
tag: 'p',
content: [
{
tag: 'b',
content: ['Foo'],
location: {
start: {
line: 2,
column: 4
},
end: {
line: 2,
column: 13
}
}
}
],
location: {
start: {
line: 2,
column: 1
},
end: {
line: 2,
column: 17
}
}
}
];
t.deepEqual(tree, expected);
});
12 changes: 12 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type Directive = {

export type Options = {
directives?: Directive[];
sourceLocations?: boolean;
} & ParserOptions;

export type Tag = string | boolean;
Expand All @@ -23,6 +24,17 @@ export type NodeTag = {
tag?: Tag;
attrs?: Attributes;
content?: Content;
location?: SourceLocation;
};

export type Node = NodeText | NodeTag;

export type SourceLocation = {
start: Position;
end: Position;
};

export type Position = {
line: number;
column: number;
};

0 comments on commit b7bade4

Please sign in to comment.