This commit is contained in:
2022-07-18 02:50:52 +00:00
parent befd344ab0
commit 06181b34d6
8569 changed files with 818704 additions and 352705 deletions
+226
View File
@@ -0,0 +1,226 @@
{
"env": {
"es6": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module",
"ecmaFeatures": {
"impliedStrict": true,
"jsx": true
},
"project": "tsconfig.json",
"extraFileExtensions": [
".ts",
".tsx"
]
},
"plugins": [
"import",
"@typescript-eslint"
],
"rules": {
"no-prototype-builtins": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"arrow-parens": [
"off"
],
"brace-style": [
"off",
"stroustrup"
],
"consistent-return": "off",
"camelcase": "off",
"@typescript-eslint/camelcase": "off",
"curly": [
"error",
"multi-line",
"consistent"
],
"eol-last": "error",
"linebreak-style": [
"error",
"unix"
],
"new-parens": "error",
"no-console": "off",
"no-constant-condition": [
"warn",
{
"checkLoops": false
}
],
"no-caller": "error",
"no-debugger": "warn",
"no-dupe-class-members": "off",
"no-duplicate-imports": "error",
"no-else-return": "warn",
"no-empty": [
"warn",
{
"allowEmptyCatch": true
}
],
"no-eval": "error",
"no-ex-assign": "warn",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-floating-decimal": "error",
"no-implicit-coercion": "error",
"no-implied-eval": "error",
// Turn off until fix for: https://github.com/typescript-eslint/typescript-eslint/issues/239
"no-inner-declarations": "off",
"no-lone-blocks": "error",
"no-lonely-if": "error",
"no-loop-func": "error",
"no-multi-spaces": "error",
"no-return-assign": "error",
"no-return-await": "warn",
"no-self-compare": "error",
"no-sequences": "error",
"no-template-curly-in-string": "warn",
"no-throw-literal": "error",
"no-unmodified-loop-condition": "warn",
"no-unneeded-ternary": "error",
"no-unused-expressions": [
"warn",
{
"allowShortCircuit": true
}
],
"no-use-before-define": "off",
"no-useless-call": "error",
"no-useless-catch": "error",
"no-useless-computed-key": "error",
"no-useless-concat": "error",
"no-useless-rename": "error",
"no-useless-return": "error",
"no-var": "error",
"no-with": "error",
"object-shorthand": "off",
"one-var": [
"error",
"never"
],
"prefer-arrow-callback": "error",
"prefer-const": "error",
"prefer-numeric-literals": "error",
"prefer-object-spread": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"prefer-template": "error",
"quotes": "off",
// Turn off until fix for: https://github.com/eslint/eslint/issues/11899
"require-atomic-updates": "off",
"semi": [
"error",
"always"
],
"semi-style": [
"error",
"last"
],
"sort-imports": [
"error",
{
"ignoreCase": true,
"ignoreDeclarationSort": true,
"ignoreMemberSort": false,
"memberSyntaxSortOrder": [
"none",
"all",
"multiple",
"single"
]
}
],
"yoda": "error",
"import/export": "off",
"import/extensions": [
"error",
"never"
],
"import/named": "off",
"import/namespace": "off",
"import/newline-after-import": "warn",
"import/no-cycle": "off",
"import/no-dynamic-require": "error",
"import/no-default-export": "off",
"import/no-duplicates": "error",
"import/no-self-import": "error",
"import/no-unresolved": [
"warn",
{
"ignore": [
"vscode"
]
}
],
"import/order": [
"warn",
{
"groups": [
"builtin",
"external",
"internal",
[
"index",
"sibling",
"parent"
]
]
}
],
"@typescript-eslint/require-await": "off",
"@typescript-eslint/consistent-type-assertions": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-member-accessibility": "error",
"@typescript-eslint/interface-name-prefix": "off",
"@typescript-eslint/no-empty-function": [
"warn",
{
"allow": [
"constructors"
]
}
],
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-inferrable-types": [
"warn",
{
"ignoreParameters": true,
"ignoreProperties": true
}
],
"@typescript-eslint/no-misused-promises": [
"error",
{
"checksVoidReturn": false
}
],
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-parameter-properties": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
{
"args": "none"
}
],
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/unbound-method": "off" // Too many bugs right now: https://github.com/typescript-eslint/typescript-eslint/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+unbound-method
}
}
+144 -58
View File
@@ -1,7 +1,7 @@
# Fast HTML Parser [![NPM version](https://badge.fury.io/js/node-html-parser.png)](http://badge.fury.io/js/node-html-parser) [![Build Status](https://travis-ci.org/taoqf/node-html-parser.svg?branch=master)](https://travis-ci.org/taoqf/node-html-parser)
Fast HTML Parser is a _very fast_ HTML parser. Which will generate a simplified
DOM tree, with basic element query support.
DOM tree, with element query support.
Per the design, it intends to parse massive HTML files in lowest price, thus the
performance is the top priority. For this reason, some malformatted HTML may not
@@ -10,21 +10,24 @@ no closing `<li>`, `<td>` etc).
## Install
```shell
npm install --save node-html-parser
```
> Note: when using Fast HTML Parser in a Typescript project the minimum Typescript version supported is `^4.1.2`.
## Performance
Faster than htmlparser2!
```shell
node-html-parser:1.94548 ms/file ± 2.15709
libxmljs :5.28893 ms/file ± 3.69863
htmlparser :24.9625 ms/file ± 168.380
htmlparser2 :3.34011 ms/file ± 4.76959
parse5 :13.9589 ms/file ± 9.84068
high5 :6.98078 ms/file ± 4.47575
htmlparser :26.7111 ms/file ± 170.066
cheerio :24.2480 ms/file ± 17.1711
parse5 :13.7239 ms/file ± 8.68561
high5 :7.75466 ms/file ± 5.33549
htmlparser2 :5.27376 ms/file ± 8.68456
node-html-parser:2.85768 ms/file ± 2.87784
```
Tested with [htmlparser-benchmark](https://github.com/AndreasMadsen/htmlparser-benchmark).
@@ -63,7 +66,7 @@ var HTMLParser = require('node-html-parser');
var root = HTMLParser.parse('<ul id="list"><li>Hello World</li></ul>');
```
## API
## Global Methods
### parse(data[, options])
@@ -75,13 +78,120 @@ Parse given data, and return root of the generated DOM.
```js
{
lowerCaseTagName: false, // convert tag name to lower case (hurt performance heavily)
script: false, // retrieve content in <script> (hurt performance slightly)
style: false, // retrieve content in <style> (hurt performance slightly)
pre: false, // retrieve content in <pre> (hurt performance slightly)
comment: false // retrieve comments (hurt performance slightly)
blockTextElements: {
script: true, // keep text content when parsing
noscript: true, // keep text content when parsing
style: true, // keep text content when parsing
pre: true // keep text content when parsing
}
}
```
### valid(data[, options])
Parse given data, return true if the givent data is valid, and return false if not.
## HTMLElement Methods
### HTMLElement#trimRight()
Trim element from right (in block) after seeing pattern in a TextNode.
### HTMLElement#removeWhitespace()
Remove whitespaces in this sub tree.
### HTMLElement#querySelectorAll(selector)
Query CSS selector to find matching nodes.
Note: Full css3 selector supported since v3.0.0.
### HTMLElement#querySelector(selector)
Query CSS Selector to find matching node.
### HTMLElement#closest(selector)
Query closest element by css selector.
### HTMLElement#appendChild(node)
Append a child node to childNodes
### HTMLElement#insertAdjacentHTML(where, html)
parses the specified text as HTML and inserts the resulting nodes into the DOM tree at a specified position.
### HTMLElement#setAttribute(key: string, value: string)
Set `value` to `key` attribute.
### HTMLElement#setAttributes(attrs: Record<string, string>)
Set attributes of the element.
### HTMLElement#removeAttribute(key: string)
Remove `key` attribute.
### HTMLElement#getAttribute(key: string)
Get `key` attribute.
### HTMLElement#exchangeChild(oldNode: Node, newNode: Node)
Exchanges given child with new child.
### HTMLElement#removeChild(node: Node)
Remove child node.
### HTMLElement#toString()
Same as [outerHTML](#htmlelementouterhtml)
### HTMLElement#set_content(content: string | Node | Node[])
Set content. **Notice**: Do not set content of the **root** node.
### HTMLElement#remove()
Remove current element.
### HTMLElement#replaceWith(...nodes: (string | Node)[])
Replace current element with other node(s).
### HTMLElement#classList
#### HTMLElement#classList.add
Add class name.
#### HTMLElement#classList.replace(old: string, new: string)
Replace class name with another one.
#### HTMLElement#classList.remove()
Remove class name.
#### HTMLElement#classList.toggle(className: string):void
Toggle class.
#### HTMLElement#classList.contains(className: string): boolean
Get if contains
#### HTMLElement#classList.values()
get class names
## HTMLElement Properties
### HTMLElement#text
Get unescaped text value of current node and its children. Like `innerText`.
@@ -92,42 +202,18 @@ Get unescaped text value of current node and its children. Like `innerText`.
Get escpaed (as-it) text value of current node and its children. May have
`&amp;` in it. (fast)
### HTMLElement#tagName
Get tag name of HTMLElement. Notice: the returned value would be an uppercase string.
### HTMLElement#structuredText
Get structured Text
### HTMLElement#trimRight()
Trim element from right (in block) after seeing pattern in a TextNode.
### HTMLElement#structure
Get DOM structure
### HTMLElement#removeWhitespace()
Remove whitespaces in this sub tree.
### HTMLElement#querySelectorAll(selector)
Query CSS selector to find matching nodes.
Note: only `tagName`, `#id`, `.class` selectors supported. And not behave the
same as standard `querySelectorAll()` as it will _stop_ searching sub tree after
find a match.
### HTMLElement#querySelector(selector)
Query CSS Selector to find matching node.
### HTMLElement#appendChild(node)
Append a child node to childNodes
### HTMLElement#insertAdjacentHTML(where, html)
parses the specified text as HTML and inserts the resulting nodes into the DOM tree at a specified position.
### HTMLElement#firstChild
Get first child node
@@ -136,30 +222,30 @@ Get first child node
Get last child node
### HTMLElement#setAttribute(key: string, value: string)
Set `value` to `key` attribute.
### HTMLElement#removeAttribute(key: string)
Remove `key` attribute.
### HTMLElement#getAttribute(key: string)
Get `key` attrubte.
### HTMLElement#toString()
Same as [outerHTML](#htmlelementouterhtml)
### HTMLElement#innerHTML
Get innerHTML.
Set or Get innerHTML.
### HTMLElement#outerHTML
Get outerHTML.
### HTMLElement#set_content(content: string | Node | Node[])
### HTMLElement#nextSibling
Set content. **Notice**: Do not set content of the **root** node.
Returns a reference to the next child node of the current element's parent.
### HTMLElement#nextElementSibling
Returns a reference to the next child element of the current element's parent.
### HTMLElement#textContent
Get or Set textContent of current element, more efficient than [set_content](#htmlelementset_contentcontent-string--node--node).
### HTMLElement#attributes
Get all attributes of current element. **Notice: do not try to change the returned value.**
### HTMLElement#classList
Get all attributes of current element. **Notice: do not try to change the returned value.**
+3
View File
@@ -0,0 +1,3 @@
export default function arr_back(arr) {
return arr[arr.length - 1];
}
+7
View File
@@ -0,0 +1,7 @@
export { default as CommentNode } from './nodes/comment';
export { default as HTMLElement } from './nodes/html';
export { default as parse, default } from './parse';
export { default as valid } from './valid';
export { default as Node } from './nodes/node';
export { default as TextNode } from './nodes/text';
export { default as NodeType } from './nodes/type';
+101
View File
@@ -0,0 +1,101 @@
import NodeType from './nodes/type';
function isTag(node) {
return node && node.nodeType === NodeType.ELEMENT_NODE;
}
function getAttributeValue(elem, name) {
return isTag(elem) ? elem.getAttribute(name) : undefined;
}
function getName(elem) {
return ((elem && elem.rawTagName) || '').toLowerCase();
}
function getChildren(node) {
return node && node.childNodes;
}
function getParent(node) {
return node ? node.parentNode : null;
}
function getText(node) {
return node.text;
}
function removeSubsets(nodes) {
let idx = nodes.length;
let node;
let ancestor;
let replace;
// Check if each node (or one of its ancestors) is already contained in the
// array.
while (--idx > -1) {
node = ancestor = nodes[idx];
// Temporarily remove the node under consideration
nodes[idx] = null;
replace = true;
while (ancestor) {
if (nodes.indexOf(ancestor) > -1) {
replace = false;
nodes.splice(idx, 1);
break;
}
ancestor = getParent(ancestor);
}
// If the node has been found to be unique, re-insert it.
if (replace) {
nodes[idx] = node;
}
}
return nodes;
}
function existsOne(test, elems) {
return elems.some((elem) => {
return isTag(elem) ? test(elem) || existsOne(test, getChildren(elem)) : false;
});
}
function getSiblings(node) {
const parent = getParent(node);
return parent && getChildren(parent);
}
function hasAttrib(elem, name) {
return getAttributeValue(elem, name) !== undefined;
}
function findOne(test, elems) {
let elem = null;
for (let i = 0, l = elems.length; i < l && !elem; i++) {
const el = elems[i];
if (test(el)) {
elem = el;
}
else {
const childs = getChildren(el);
if (childs && childs.length > 0) {
elem = findOne(test, childs);
}
}
}
return elem;
}
function findAll(test, nodes) {
let result = [];
for (let i = 0, j = nodes.length; i < j; i++) {
if (!isTag(nodes[i]))
continue;
if (test(nodes[i]))
result.push(nodes[i]);
const childs = getChildren(nodes[i]);
if (childs)
result = result.concat(findAll(test, childs));
}
return result;
}
export default {
isTag,
getAttributeValue,
getName,
getChildren,
getParent,
getText,
removeSubsets,
existsOne,
getSiblings,
hasAttrib,
findOne,
findAll
};
+23
View File
@@ -0,0 +1,23 @@
import Node from './node';
import NodeType from './type';
export default class CommentNode extends Node {
constructor(rawText, parentNode) {
super(parentNode);
this.rawText = rawText;
/**
* Node Type declaration.
* @type {Number}
*/
this.nodeType = NodeType.COMMENT_NODE;
}
/**
* Get unescaped text value of current node and its children.
* @return {string} text content
*/
get text() {
return this.rawText;
}
toString() {
return `<!--${this.rawText}-->`;
}
}
File diff suppressed because it is too large Load Diff
+18
View File
@@ -0,0 +1,18 @@
/**
* Node Class as base class for TextNode and HTMLElement.
*/
export default class Node {
constructor(parentNode = null) {
this.parentNode = parentNode;
this.childNodes = [];
}
get innerText() {
return this.rawText;
}
get textContent() {
return this.rawText;
}
set textContent(val) {
this.rawText = val;
}
}
+69
View File
@@ -0,0 +1,69 @@
import NodeType from './type';
import Node from './node';
/**
* TextNode to contain a text element in DOM tree.
* @param {string} value [description]
*/
export default class TextNode extends Node {
constructor(rawText, parentNode) {
super(parentNode);
this.rawText = rawText;
/**
* Node Type declaration.
* @type {Number}
*/
this.nodeType = NodeType.TEXT_NODE;
}
/**
* Returns text with all whitespace trimmed except single leading/trailing non-breaking space
*/
get trimmedText() {
if (this._trimmedText !== undefined)
return this._trimmedText;
const text = this.rawText;
let i = 0;
let startPos;
let endPos;
while (i >= 0 && i < text.length) {
if (/\S/.test(text[i])) {
if (startPos === undefined) {
startPos = i;
i = text.length;
}
else {
endPos = i;
i = void 0;
}
}
if (startPos === undefined)
i++;
else
i--;
}
if (startPos === undefined)
startPos = 0;
if (endPos === undefined)
endPos = text.length - 1;
const hasLeadingSpace = startPos > 0 && /[^\S\r\n]/.test(text[startPos - 1]);
const hasTrailingSpace = endPos < (text.length - 1) && /[^\S\r\n]/.test(text[endPos + 1]);
this._trimmedText = (hasLeadingSpace ? ' ' : '') + text.slice(startPos, endPos + 1) + (hasTrailingSpace ? ' ' : '');
return this._trimmedText;
}
/**
* Get unescaped text value of current node and its children.
* @return {string} text content
*/
get text() {
return this.rawText;
}
/**
* Detect if the node contains only white space.
* @return {bool}
*/
get isWhitespace() {
return /^(\s|&nbsp;)*$/.test(this.rawText);
}
toString() {
return this.text;
}
}
+7
View File
@@ -0,0 +1,7 @@
var NodeType;
(function (NodeType) {
NodeType[NodeType["ELEMENT_NODE"] = 1] = "ELEMENT_NODE";
NodeType[NodeType["TEXT_NODE"] = 3] = "TEXT_NODE";
NodeType[NodeType["COMMENT_NODE"] = 8] = "COMMENT_NODE";
})(NodeType || (NodeType = {}));
export default NodeType;
+1
View File
@@ -0,0 +1 @@
export { parse as default } from './nodes/html';
+9
View File
@@ -0,0 +1,9 @@
import { base_parse } from './nodes/html';
/**
* Parses HTML and returns a root element
* Parse a chuck of HTML source.
*/
export default function valid(data, options = { lowerCaseTagName: false, comment: false }) {
const stack = base_parse(data, options);
return Boolean(stack.length === 1);
}
+3 -1
View File
@@ -1,5 +1,7 @@
export { default as CommentNode } from './nodes/comment';
export { default as HTMLElement, parse, parse as default, Options } from './nodes/html';
export { default as HTMLElement, Options } from './nodes/html';
export { default as parse, default } from './parse';
export { default as valid } from './valid';
export { default as Node } from './nodes/node';
export { default as TextNode } from './nodes/text';
export { default as NodeType } from './nodes/type';
+14 -7
View File
@@ -1,14 +1,21 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeType = exports.TextNode = exports.Node = exports.valid = exports.default = exports.parse = exports.HTMLElement = exports.CommentNode = void 0;
var comment_1 = require("./nodes/comment");
exports.CommentNode = comment_1.default;
Object.defineProperty(exports, "CommentNode", { enumerable: true, get: function () { return __importDefault(comment_1).default; } });
var html_1 = require("./nodes/html");
exports.HTMLElement = html_1.default;
exports.parse = html_1.parse;
exports.default = html_1.parse;
Object.defineProperty(exports, "HTMLElement", { enumerable: true, get: function () { return __importDefault(html_1).default; } });
var parse_1 = require("./parse");
Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return __importDefault(parse_1).default; } });
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(parse_1).default; } });
var valid_1 = require("./valid");
Object.defineProperty(exports, "valid", { enumerable: true, get: function () { return __importDefault(valid_1).default; } });
var node_1 = require("./nodes/node");
exports.Node = node_1.default;
Object.defineProperty(exports, "Node", { enumerable: true, get: function () { return __importDefault(node_1).default; } });
var text_1 = require("./nodes/text");
exports.TextNode = text_1.default;
Object.defineProperty(exports, "TextNode", { enumerable: true, get: function () { return __importDefault(text_1).default; } });
var type_1 = require("./nodes/type");
exports.NodeType = type_1.default;
Object.defineProperty(exports, "NodeType", { enumerable: true, get: function () { return __importDefault(type_1).default; } });
+933 -574
View File
File diff suppressed because it is too large Load Diff
+5 -40
View File
@@ -1,41 +1,6 @@
import { Adapter } from 'css-select/lib/types';
import HTMLElement from './nodes/html';
/**
* Matcher class to make CSS match
*
* @class Matcher
*/
export default class Matcher {
private matchers;
private nextMatch;
/**
* Creates an instance of Matcher.
* @param {string} selector
*
* @memberof Matcher
*/
constructor(selector: string);
/**
* Trying to advance match pointer
* @param {HTMLElement} el element to make the match
* @return {bool} true when pointer advanced.
*/
advance(el: HTMLElement): boolean;
/**
* Rewind the match pointer
*/
rewind(): void;
/**
* Trying to determine if match made.
* @return {bool} true when the match is made
*/
get matched(): boolean;
/**
* Rest match pointer.
* @return {[type]} [description]
*/
reset(): void;
/**
* flush cache to free memory
*/
flushCache(): void;
}
import Node from './nodes/node';
export declare type Predicate = (node: Node) => node is HTMLElement;
declare const _default: Adapter<Node, HTMLElement>;
export default _default;
+101 -243
View File
@@ -1,248 +1,106 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Cache to store generated match functions
* @type {Object}
*/
var pMatchFunctionCache = {};
/**
* Function cache
*/
var functionCache = {
f145: function (el, tagName, classes) {
'use strict';
tagName = tagName || '';
classes = classes || [];
if (el.id !== tagName.substr(1)) {
return false;
}
for (var cls = classes, i = 0; i < cls.length; i++) {
if (el.classNames.indexOf(cls[i]) === -1) {
return false;
}
}
return true;
},
f45: function (el, tagName, classes) {
'use strict';
tagName = tagName || '';
classes = classes || [];
for (var cls = classes, i = 0; i < cls.length; i++) {
if (el.classNames.indexOf(cls[i]) === -1) {
return false;
}
}
return true;
},
f15: function (el, tagName) {
'use strict';
tagName = tagName || '';
if (el.id !== tagName.substr(1)) {
return false;
}
return true;
},
f1: function (el, tagName) {
'use strict';
tagName = tagName || '';
if (el.id !== tagName.substr(1)) {
return false;
}
},
f5: function () {
'use strict';
return true;
},
f55: function (el, tagName, classes, attr_key) {
'use strict';
tagName = tagName || '';
classes = classes || [];
attr_key = attr_key || '';
var attrs = el.attributes;
return attrs.hasOwnProperty(attr_key);
},
f245: function (el, tagName, classes, attr_key, value) {
'use strict';
tagName = tagName || '';
classes = classes || [];
attr_key = attr_key || '';
value = value || '';
var attrs = el.attributes;
return Object.keys(attrs).some(function (key) {
var val = attrs[key];
return key === attr_key && val === value;
});
// for (let cls = classes, i = 0; i < cls.length; i++) {if (el.classNames.indexOf(cls[i]) === -1){ return false;}}
// return true;
},
f25: function (el, tagName, classes, attr_key, value) {
'use strict';
tagName = tagName || '';
classes = classes || [];
attr_key = attr_key || '';
value = value || '';
var attrs = el.attributes;
return Object.keys(attrs).some(function (key) {
var val = attrs[key];
return key === attr_key && val === value;
});
// return true;
},
f2: function (el, tagName, classes, attr_key, value) {
'use strict';
tagName = tagName || '';
classes = classes || [];
attr_key = attr_key || '';
value = value || '';
var attrs = el.attributes;
return Object.keys(attrs).some(function (key) {
var val = attrs[key];
return key === attr_key && val === value;
});
},
f345: function (el, tagName, classes) {
'use strict';
tagName = tagName || '';
classes = classes || [];
if (el.tagName !== tagName) {
return false;
}
for (var cls = classes, i = 0; i < cls.length; i++) {
if (el.classNames.indexOf(cls[i]) === -1) {
return false;
}
}
return true;
},
f35: function (el, tagName) {
'use strict';
tagName = tagName || '';
return el.tagName === tagName;
},
f3: function (el, tagName) {
'use strict';
tagName = tagName || '';
if (el.tagName !== tagName) {
return false;
}
}
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
/**
* Matcher class to make CSS match
*
* @class Matcher
*/
var Matcher = /** @class */ (function () {
/**
* Creates an instance of Matcher.
* @param {string} selector
*
* @memberof Matcher
*/
function Matcher(selector) {
this.nextMatch = 0;
functionCache.f5 = functionCache.f5;
this.matchers = selector.split(' ').map(function (matcher) {
if (pMatchFunctionCache[matcher])
return pMatchFunctionCache[matcher];
var parts = matcher.split('.');
var tagName = parts[0];
var classes = parts.slice(1).sort();
// let source = '"use strict";';
var function_name = 'f';
var attr_key = '';
var value = '';
if (tagName && tagName !== '*') {
var reg = void 0;
if (tagName.startsWith('#')) {
// source += 'if (el.id != ' + JSON.stringify(tagName.substr(1)) + ') return false;';// 1
function_name += '1';
}
else {
reg = /^\[\s*(\S+)\s*(=|!=)\s*((((["'])([^\6]*)\6))|(\S*?))\]\s*/.exec(tagName);
if (reg) {
attr_key = reg[1];
var method = reg[2];
if (method !== '=' && method !== '!=') {
throw new Error('Selector not supported, Expect [key${op}value].op must be =,!=');
}
if (method === '=') {
method = '==';
}
value = reg[7] || reg[8];
// source += `let attrs = el.attributes;for (let key in attrs){const val = attrs[key]; if (key == "${attr_key}" && val == "${value}"){return true;}} return false;`;// 2
function_name += '2';
}
else if (reg = /^\[(.*?)\]/.exec(tagName)) {
attr_key = reg[1];
function_name += '5';
}
else {
// source += 'if (el.tagName != ' + JSON.stringify(tagName) + ') return false;';// 3
function_name += '3';
}
}
Object.defineProperty(exports, "__esModule", { value: true });
var type_1 = __importDefault(require("./nodes/type"));
function isTag(node) {
return node && node.nodeType === type_1.default.ELEMENT_NODE;
}
function getAttributeValue(elem, name) {
return isTag(elem) ? elem.getAttribute(name) : undefined;
}
function getName(elem) {
return ((elem && elem.rawTagName) || '').toLowerCase();
}
function getChildren(node) {
return node && node.childNodes;
}
function getParent(node) {
return node ? node.parentNode : null;
}
function getText(node) {
return node.text;
}
function removeSubsets(nodes) {
var idx = nodes.length;
var node;
var ancestor;
var replace;
// Check if each node (or one of its ancestors) is already contained in the
// array.
while (--idx > -1) {
node = ancestor = nodes[idx];
// Temporarily remove the node under consideration
nodes[idx] = null;
replace = true;
while (ancestor) {
if (nodes.indexOf(ancestor) > -1) {
replace = false;
nodes.splice(idx, 1);
break;
}
if (classes.length > 0) {
// source += 'for (let cls = ' + JSON.stringify(classes) + ', i = 0; i < cls.length; i++) if (el.classNames.indexOf(cls[i]) === -1) return false;';// 4
function_name += '4';
}
// source += 'return true;';// 5
function_name += '5';
var obj = {
func: functionCache[function_name],
tagName: tagName || '',
classes: classes || '',
attr_key: attr_key || '',
value: value || ''
};
// source = source || '';
return pMatchFunctionCache[matcher] = obj;
});
}
/**
* Trying to advance match pointer
* @param {HTMLElement} el element to make the match
* @return {bool} true when pointer advanced.
*/
Matcher.prototype.advance = function (el) {
if (this.nextMatch < this.matchers.length &&
this.matchers[this.nextMatch].func(el, this.matchers[this.nextMatch].tagName, this.matchers[this.nextMatch].classes, this.matchers[this.nextMatch].attr_key, this.matchers[this.nextMatch].value)) {
this.nextMatch++;
return true;
ancestor = getParent(ancestor);
}
return false;
};
/**
* Rewind the match pointer
*/
Matcher.prototype.rewind = function () {
this.nextMatch--;
};
Object.defineProperty(Matcher.prototype, "matched", {
/**
* Trying to determine if match made.
* @return {bool} true when the match is made
*/
get: function () {
return this.nextMatch === this.matchers.length;
},
enumerable: true,
configurable: true
// If the node has been found to be unique, re-insert it.
if (replace) {
nodes[idx] = node;
}
}
return nodes;
}
function existsOne(test, elems) {
return elems.some(function (elem) {
return isTag(elem) ? test(elem) || existsOne(test, getChildren(elem)) : false;
});
/**
* Rest match pointer.
* @return {[type]} [description]
*/
Matcher.prototype.reset = function () {
this.nextMatch = 0;
};
/**
* flush cache to free memory
*/
Matcher.prototype.flushCache = function () {
pMatchFunctionCache = {};
};
return Matcher;
}());
exports.default = Matcher;
}
function getSiblings(node) {
var parent = getParent(node);
return parent && getChildren(parent);
}
function hasAttrib(elem, name) {
return getAttributeValue(elem, name) !== undefined;
}
function findOne(test, elems) {
var elem = null;
for (var i = 0, l = elems.length; i < l && !elem; i++) {
var el = elems[i];
if (test(el)) {
elem = el;
}
else {
var childs = getChildren(el);
if (childs && childs.length > 0) {
elem = findOne(test, childs);
}
}
}
return elem;
}
function findAll(test, nodes) {
var result = [];
for (var i = 0, j = nodes.length; i < j; i++) {
if (!isTag(nodes[i]))
continue;
if (test(nodes[i]))
result.push(nodes[i]);
var childs = getChildren(nodes[i]);
if (childs)
result = result.concat(findAll(test, childs));
}
return result;
}
exports.default = {
isTag: isTag,
getAttributeValue: getAttributeValue,
getName: getName,
getChildren: getChildren,
getParent: getParent,
getText: getText,
removeSubsets: removeSubsets,
existsOne: existsOne,
getSiblings: getSiblings,
hasAttrib: hasAttrib,
findOne: findOne,
findAll: findAll
};
+4 -2
View File
@@ -1,7 +1,9 @@
import Node from './node';
import NodeType from './type';
import HTMLElement from './html';
export default class CommentNode extends Node {
constructor(value: string);
rawText: string;
constructor(rawText: string, parentNode: HTMLElement);
/**
* Node Type declaration.
* @type {Number}
@@ -12,5 +14,5 @@ export default class CommentNode extends Node {
* @return {string} text content
*/
get text(): string;
toString(): string;
toString(): `<!--${string}-->`;
}
+7 -5
View File
@@ -3,10 +3,12 @@ var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
@@ -20,14 +22,14 @@ var node_1 = __importDefault(require("./node"));
var type_1 = __importDefault(require("./type"));
var CommentNode = /** @class */ (function (_super) {
__extends(CommentNode, _super);
function CommentNode(value) {
var _this = _super.call(this) || this;
function CommentNode(rawText, parentNode) {
var _this = _super.call(this, parentNode) || this;
_this.rawText = rawText;
/**
* Node Type declaration.
* @type {Number}
*/
_this.nodeType = type_1.default.COMMENT_NODE;
_this.rawText = value;
return _this;
}
Object.defineProperty(CommentNode.prototype, "text", {
@@ -38,7 +40,7 @@ var CommentNode = /** @class */ (function (_super) {
get: function () {
return this.rawText;
},
enumerable: true,
enumerable: false,
configurable: true
});
CommentNode.prototype.toString = function () {
+59 -29
View File
@@ -1,7 +1,5 @@
import Node from './node';
import NodeType from './type';
import TextNode from './text';
import Matcher from '../matcher';
export interface KeyAttributes {
id?: string;
class?: string;
@@ -13,6 +11,21 @@ export interface RawAttributes {
[key: string]: string;
}
export declare type InsertPosition = 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend';
declare class DOMTokenList {
private _set;
private _afterUpdate;
private _validate;
constructor(valuesInit?: string[], afterUpdate?: ((t: DOMTokenList) => void));
add(c: string): void;
replace(c1: string, c2: string): void;
remove(c: string): void;
toggle(c: string): void;
contains(c: string): boolean;
get length(): number;
values(): IterableIterator<string>;
get value(): string[];
toString(): string;
}
/**
* HTMLElement, which contains a set of children.
*
@@ -23,17 +36,22 @@ export declare type InsertPosition = 'beforebegin' | 'afterbegin' | 'beforeend'
* @extends {Node}
*/
export default class HTMLElement extends Node {
tagName: string;
private rawAttrs;
parentNode: Node;
private _attrs;
private _rawAttrs;
rawTagName: string;
id: string;
classNames: string[];
classList: DOMTokenList;
/**
* Node Type declaration.
*/
nodeType: NodeType;
/**
* Quote attribute values
* @param attr attribute value
* @returns {string} quoted value
*/
private quoteAttribute;
/**
* Creates an instance of HTMLElement.
* @param keyAttrs id and class attribute
@@ -41,7 +59,11 @@ export default class HTMLElement extends Node {
*
* @memberof HTMLElement
*/
constructor(tagName: string, keyAttrs: KeyAttributes, rawAttrs?: string, parentNode?: Node);
constructor(tagName: string, keyAttrs: KeyAttributes, rawAttrs: string, parentNode: HTMLElement | null);
/**
* Remove current element
*/
remove(): void;
/**
* Remove Child element from childNodes array
* @param {HTMLElement} node node to remove
@@ -53,11 +75,15 @@ export default class HTMLElement extends Node {
* @param {HTMLElement} newNode new node
*/
exchangeChild(oldNode: Node, newNode: Node): void;
get tagName(): string;
get localName(): string;
/**
* Get escpaed (as-it) text value of current node and its children.
* @return {string} text content
*/
get rawText(): string;
get textContent(): string;
set textContent(val: string);
/**
* Get unescaped text value of current node and its children.
* @return {string} text content
@@ -70,7 +96,9 @@ export default class HTMLElement extends Node {
get structuredText(): string;
toString(): string;
get innerHTML(): string;
set innerHTML(content: string);
set_content(content: string | Node | Node[], options?: Options): void;
replaceWith(...nodes: (string | Node)[]): void;
get outerHTML(): string;
/**
* Trim element from right (in block) after seeing pattern in a TextNode.
@@ -91,17 +119,20 @@ export default class HTMLElement extends Node {
/**
* Query CSS selector to find matching nodes.
* @param {string} selector Simplified CSS selector
* @param {Matcher} selector A Matcher instance
* @return {HTMLElement[]} matching elements
*/
querySelectorAll(selector: string | Matcher): HTMLElement[];
querySelectorAll(selector: string): HTMLElement[];
/**
* Query CSS Selector to find matching node.
* @param {string} selector Simplified CSS selector
* @param {Matcher} selector A Matcher instance
* @return {HTMLElement} matching node
*/
querySelector(selector: string | Matcher): HTMLElement;
querySelector(selector: string): HTMLElement;
/**
* traverses the Element and its parents (heading toward the document root) until it finds a node that matches the provided selector string. Will return itself or the matching ancestor. If no such element exists, it returns null.
* @param selector a DOMString containing a selector list
*/
closest(selector: string): Node;
/**
* Append a child node to childNodes
* @param {Node} node node to append
@@ -120,9 +151,11 @@ export default class HTMLElement extends Node {
get lastChild(): Node;
/**
* Get attributes
* @access private
* @return {Object} parsed and unescaped attributes
*/
get attributes(): Attributes;
get attrs(): Attributes;
get attributes(): Record<string, string>;
/**
* Get escaped (as-it) attributes
* @return {Object} parsed attributes
@@ -147,13 +180,16 @@ export default class HTMLElement extends Node {
*/
setAttributes(attributes: Attributes): void;
insertAdjacentHTML(where: InsertPosition, html: string): void;
get nextSibling(): Node;
get nextElementSibling(): HTMLElement;
get classNames(): string;
}
export interface Options {
lowerCaseTagName?: boolean;
script?: boolean;
style?: boolean;
pre?: boolean;
comment?: boolean;
lowerCaseTagName: boolean;
comment: boolean;
blockTextElements: {
[tag: string]: boolean;
};
}
/**
* Parses HTML and returns a root element
@@ -161,16 +197,10 @@ export interface Options {
* @param {string} data html
* @return {HTMLElement} root element
*/
export declare function parse(data: string, options?: Options): HTMLElement & {
valid: boolean;
};
export declare function parse(data: string, options?: Options & {
noFix: false;
}): HTMLElement & {
valid: boolean;
};
export declare function parse(data: string, options?: Options & {
noFix: true;
}): (HTMLElement | TextNode) & {
valid: boolean;
};
export declare function base_parse(data: string, options?: Partial<Options>): HTMLElement[];
/**
* Parses HTML and returns a root element
* Parse a chuck of HTML source.
*/
export declare function parse(data: string, options?: Partial<Options>): HTMLElement;
export {};
+687 -263
View File
File diff suppressed because it is too large Load Diff
+9 -3
View File
@@ -1,11 +1,17 @@
import NodeType from './type';
import HTMLElement from './html';
/**
* Node Class as base class for TextNode and HTMLElement.
*/
export default abstract class Node {
nodeType: NodeType;
parentNode: HTMLElement;
abstract nodeType: NodeType;
childNodes: Node[];
text: string;
rawText: string;
abstract text: string;
abstract rawText: string;
abstract toString(): string;
constructor(parentNode?: HTMLElement);
get innerText(): string;
get textContent(): string;
set textContent(val: string);
}
+20 -1
View File
@@ -4,9 +4,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
* Node Class as base class for TextNode and HTMLElement.
*/
var Node = /** @class */ (function () {
function Node() {
function Node(parentNode) {
if (parentNode === void 0) { parentNode = null; }
this.parentNode = parentNode;
this.childNodes = [];
}
Object.defineProperty(Node.prototype, "innerText", {
get: function () {
return this.rawText;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Node.prototype, "textContent", {
get: function () {
return this.rawText;
},
set: function (val) {
this.rawText = val;
},
enumerable: false,
configurable: true
});
return Node;
}());
exports.default = Node;
+8 -1
View File
@@ -1,16 +1,23 @@
import NodeType from './type';
import Node from './node';
import HTMLElement from './html';
/**
* TextNode to contain a text element in DOM tree.
* @param {string} value [description]
*/
export default class TextNode extends Node {
constructor(value: string);
rawText: string;
constructor(rawText: string, parentNode: HTMLElement);
/**
* Node Type declaration.
* @type {Number}
*/
nodeType: NodeType;
private _trimmedText?;
/**
* Returns text with all whitespace trimmed except single leading/trailing non-breaking space
*/
get trimmedText(): string;
/**
* Get unescaped text value of current node and its children.
* @return {string} text content
+47 -6
View File
@@ -3,10 +3,12 @@ var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
@@ -24,16 +26,55 @@ var node_1 = __importDefault(require("./node"));
*/
var TextNode = /** @class */ (function (_super) {
__extends(TextNode, _super);
function TextNode(value) {
var _this = _super.call(this) || this;
function TextNode(rawText, parentNode) {
var _this = _super.call(this, parentNode) || this;
_this.rawText = rawText;
/**
* Node Type declaration.
* @type {Number}
*/
_this.nodeType = type_1.default.TEXT_NODE;
_this.rawText = value;
return _this;
}
Object.defineProperty(TextNode.prototype, "trimmedText", {
/**
* Returns text with all whitespace trimmed except single leading/trailing non-breaking space
*/
get: function () {
if (this._trimmedText !== undefined)
return this._trimmedText;
var text = this.rawText;
var i = 0;
var startPos;
var endPos;
while (i >= 0 && i < text.length) {
if (/\S/.test(text[i])) {
if (startPos === undefined) {
startPos = i;
i = text.length;
}
else {
endPos = i;
i = void 0;
}
}
if (startPos === undefined)
i++;
else
i--;
}
if (startPos === undefined)
startPos = 0;
if (endPos === undefined)
endPos = text.length - 1;
var hasLeadingSpace = startPos > 0 && /[^\S\r\n]/.test(text[startPos - 1]);
var hasTrailingSpace = endPos < (text.length - 1) && /[^\S\r\n]/.test(text[endPos + 1]);
this._trimmedText = (hasLeadingSpace ? ' ' : '') + text.slice(startPos, endPos + 1) + (hasTrailingSpace ? ' ' : '');
return this._trimmedText;
},
enumerable: false,
configurable: true
});
Object.defineProperty(TextNode.prototype, "text", {
/**
* Get unescaped text value of current node and its children.
@@ -42,7 +83,7 @@ var TextNode = /** @class */ (function (_super) {
get: function () {
return this.rawText;
},
enumerable: true,
enumerable: false,
configurable: true
});
Object.defineProperty(TextNode.prototype, "isWhitespace", {
@@ -53,7 +94,7 @@ var TextNode = /** @class */ (function (_super) {
get: function () {
return /^(\s|&nbsp;)*$/.test(this.rawText);
},
enumerable: true,
enumerable: false,
configurable: true
});
TextNode.prototype.toString = function () {
+1
View File
@@ -0,0 +1 @@
export { parse as default } from './nodes/html';
+5
View File
@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = void 0;
var html_1 = require("./nodes/html");
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return html_1.parse; } });
+6
View File
@@ -0,0 +1,6 @@
import { Options } from './nodes/html';
/**
* Parses HTML and returns a root element
* Parse a chuck of HTML source.
*/
export default function valid(data: string, options?: Partial<Options>): boolean;
+13
View File
@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var html_1 = require("./nodes/html");
/**
* Parses HTML and returns a root element
* Parse a chuck of HTML source.
*/
function valid(data, options) {
if (options === void 0) { options = { lowerCaseTagName: false, comment: false }; }
var stack = html_1.base_parse(data, options);
return Boolean(stack.length === 1);
}
exports.default = valid;
+35 -21
View File
@@ -1,32 +1,32 @@
{
"_args": [
[
"node-html-parser@1.2.20",
"/mnt/Foxconn/Digitalent/Deverloper/liff-push_2series"
"node-html-parser@3.3.6",
"/home/node/nuxt"
]
],
"_from": "node-html-parser@1.2.20",
"_id": "node-html-parser@1.2.20",
"_from": "node-html-parser@3.3.6",
"_id": "node-html-parser@3.3.6",
"_inBundle": false,
"_integrity": "sha512-1fUpYjAducDrrBSE0etRUV1tM+wSFTudmrslMXuk35wL/L29E7e1CLQn4CNzFLnqtYpmDlWhkD6VUloyHA0dwA==",
"_integrity": "sha512-VkWDHvNgFGB3mbQGMyzqRE1i/BG7TKX9wRXC8e/v8kL0kZR/Oy6RjYxXH91K6/+m3g8iQ8dTqRy75lTYoA2Cjg==",
"_location": "/node-html-parser",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "node-html-parser@1.2.20",
"raw": "node-html-parser@3.3.6",
"name": "node-html-parser",
"escapedName": "node-html-parser",
"rawSpec": "1.2.20",
"rawSpec": "3.3.6",
"saveSpec": null,
"fetchSpec": "1.2.20"
"fetchSpec": "3.3.6"
},
"_requiredBy": [
"/@nuxt/generator"
],
"_resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-1.2.20.tgz",
"_spec": "1.2.20",
"_where": "/mnt/Foxconn/Digitalent/Deverloper/liff-push_2series",
"_resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-3.3.6.tgz",
"_spec": "3.3.6",
"_where": "/home/node/nuxt",
"author": {
"name": "Xiaoyi Shi",
"email": "ashi009@gmail.com"
@@ -52,7 +52,8 @@
}
],
"dependencies": {
"he": "1.1.1"
"css-select": "^4.1.3",
"he": "1.2.0"
},
"description": "A very fast HTML parser, generating a simplified DOM, with basic element query support.",
"devDependencies": {
@@ -63,19 +64,21 @@
"@typescript-eslint/eslint-plugin-tslint": "latest",
"@typescript-eslint/parser": "latest",
"blanket": "latest",
"cheerio": "^1.0.0-rc.5",
"del-cli": "latest",
"eslint": "latest",
"eslint-config-prettier": "latest",
"eslint-plugin-import": "latest",
"high5": "^1.0.0",
"htmlparser": "^1.7.7",
"htmlparser-benchmark": "^1.1.3",
"htmlparser2": "^4.1.0",
"libxmljs": "^0.19.7",
"htmlparser2": "^6.0.0",
"mocha": "latest",
"parse5": "^6.0.0",
"np": "latest",
"parse5": "^6.0.1",
"should": "latest",
"spec": "latest",
"travis-cov": "latest",
"tslint": "^5.0.0",
"typescript": "next"
},
"directories": {
@@ -83,25 +86,36 @@
},
"homepage": "https://github.com/taoqf/node-fast-html-parser",
"keywords": [
"fast html parser nodejs typescript"
"parser",
"html",
"nodejs",
"typescript"
],
"license": "MIT",
"main": "dist/index.js",
"module": "dist/esm/index.js",
"name": "node-html-parser",
"publishConfig": {
"registry": "https://registry.npmjs.org"
},
"repository": {
"type": "git",
"url": "git+https://github.com/taoqf/node-fast-html-parser.git"
},
"scripts": {
"build": "npm run lint && npm run clean && npm run ts:cjs && npm run ts:umd",
"build": "npm run lint && npm run clean && npm run ts:cjs && npm run ts:amd && npm run ts:esm",
"clean": "del-cli ./dist/",
"dev": "tsc -w",
"dev": "tsc -w & mocha -w ./test/*.js",
"lint": "eslint ./src/*.ts ./src/**/*.ts",
"prepare": "npm run build",
"pretest": "tsc -m commonjs",
"release": "yarn build && np",
"test": "mocha",
"ts:amd": "tsc -t es5 -m amd -d false --outFile ./dist/main.js",
"ts:cjs": "tsc -m commonjs",
"ts:umd": "tsc -t es5 -m amd -d false --outFile ./dist/main.js"
"ts:esm": "tsc -t esnext -m esnext -d false --outDir ./dist/esm/"
},
"sideEffects": false,
"types": "dist/index.d.ts",
"version": "1.2.20"
"version": "3.3.6"
}