janus-parse 1.0.4 → 1.2.0
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/README.md +12 -16
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/janus-parse.d.ts +2 -2
- package/dist/janus-parse.d.ts.map +1 -1
- package/dist/janus-parse.js +8 -14
- package/dist/utils/index.d.ts +15 -4
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +51 -20
- package/package.json +14 -5
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# janus-parse
|
|
1
|
+
# janus-parse [](https://www.npmjs.com/package/janus-parse)  [](https://github.com/Casneil/janus-parse/actions/workflows/publish.yml) [](https://github.com/Casneil/janus-parse/actions/workflows/build.yml)
|
|
2
2
|
|
|
3
3
|
A lightweight, high-performance, isomorphic HTML text extractor and sanitizer built entirely in TypeScript. It safely strips out targeted layout elements or malicious scripts and returns clean, whitespace-normalized text.
|
|
4
4
|
|
|
@@ -49,9 +49,7 @@ const rawHtml = `
|
|
|
49
49
|
</main>
|
|
50
50
|
`;
|
|
51
51
|
|
|
52
|
-
const cleanText = await janusServer(rawHtml);
|
|
53
|
-
console.log(cleanText);
|
|
54
|
-
// Output: "Hello Universe This is a highly spaced sentence."
|
|
52
|
+
const cleanText = await janusServer(rawHtml); // Output: "Hello Universe This is a highly spaced sentence."
|
|
55
53
|
```
|
|
56
54
|
|
|
57
55
|
### 2. Client-Side Execution (Browser)
|
|
@@ -64,9 +62,7 @@ import { janusClient } from "janus-parse";
|
|
|
64
62
|
const webMarkup =
|
|
65
63
|
"<div> Dynamic Web App <style>body { display: none; }</style></div>";
|
|
66
64
|
|
|
67
|
-
const textOnly = janusClient(webMarkup);
|
|
68
|
-
console.log(textOnly);
|
|
69
|
-
// Output: " Dynamic Web App"
|
|
65
|
+
const textOnly = janusClient(webMarkup); // Output: " Dynamic Web App"
|
|
70
66
|
```
|
|
71
67
|
|
|
72
68
|
### 3. Custom Configurations
|
|
@@ -75,8 +71,8 @@ You can modify which HTML tags are targeted for destruction by passing an option
|
|
|
75
71
|
|
|
76
72
|
```typescript
|
|
77
73
|
const structuralConfig = {
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
tagsToRemove: ["span", "section"] // Adds additonal blacklist candidates,
|
|
75
|
+
tagsToPreserve: ["script"] // Preserves script tag in output,
|
|
80
76
|
};
|
|
81
77
|
```
|
|
82
78
|
|
|
@@ -87,9 +83,9 @@ const processedTextServer = await janusServer(htmlSource, structuralConfig);
|
|
|
87
83
|
```
|
|
88
84
|
|
|
89
85
|
```typescript
|
|
90
|
-
import { janusClient } from
|
|
86
|
+
import { janusClient } from "janus-parse";
|
|
91
87
|
|
|
92
|
-
const processedTextClient =
|
|
88
|
+
const processedTextClient = janusClient(htmlSource, structuralConfig);
|
|
93
89
|
```
|
|
94
90
|
|
|
95
91
|
---
|
|
@@ -101,19 +97,19 @@ const processedTextClient = janusClienthtmlSource, structuralConfig);
|
|
|
101
97
|
TypeScript interface passed to fine-tune tag removal behaviors.
|
|
102
98
|
|
|
103
99
|
```typescript
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
type Config {
|
|
101
|
+
tagsToRemove?: string[];
|
|
102
|
+
tagsToPreserve?: string[];
|
|
107
103
|
}
|
|
108
104
|
```
|
|
109
105
|
|
|
110
|
-
|
|
106
|
+
#### `janusServer(text: string, config?: Config): Promise<string>`
|
|
111
107
|
|
|
112
108
|
- **`text`**: The input raw HTML string.
|
|
113
109
|
- **`config`**: Optional rule blocks to override standard cleaning lists.
|
|
114
110
|
- **Returns**: A promise that resolves to a stripped, whitespace-normalized single-line string.
|
|
115
111
|
|
|
116
|
-
|
|
112
|
+
#### `janusClient(text: string, config?: Config): string`
|
|
117
113
|
|
|
118
114
|
- **`text`**: The input raw HTML string.
|
|
119
115
|
- **`config`**: Optional rule blocks to override standard cleaning lists.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./janus-parse
|
|
1
|
+
export * from "./janus-parse";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "./janus-parse
|
|
1
|
+
export * from "./janus-parse";
|
package/dist/janus-parse.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Config } from "./utils
|
|
1
|
+
import type { Config } from "./utils";
|
|
2
2
|
export declare function janusServer(text: string, config?: Config): Promise<string>;
|
|
3
|
-
export declare function janusClient(text: string, config?: Config): string
|
|
3
|
+
export declare function janusClient(text: string, config?: Config): string;
|
|
4
4
|
//# sourceMappingURL=janus-parse.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"janus-parse.d.ts","sourceRoot":"","sources":["../lib/janus-parse.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"janus-parse.d.ts","sourceRoot":"","sources":["../lib/janus-parse.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAOtC,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,MAAoB,mBAS3E;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,MAAoB,UASrE"}
|
package/dist/janus-parse.js
CHANGED
|
@@ -1,25 +1,19 @@
|
|
|
1
|
-
import { validateText,
|
|
1
|
+
import { validateText, getTags, normalizeWhitespace, serialize } from "./utils";
|
|
2
2
|
const janusConfig = {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
tagsToRemove: [],
|
|
4
|
+
tagsToPreserve: [],
|
|
5
5
|
};
|
|
6
6
|
export async function janusServer(text, config = janusConfig) {
|
|
7
7
|
validateText(text);
|
|
8
|
-
const tags = getTags(config);
|
|
9
8
|
const parser = await import("node-html-parser");
|
|
10
9
|
const root = parser.parse(text);
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
removeNodes(nodesToRemove);
|
|
14
|
-
return root.textContent.replace(new RegExp("\\s+", "g"), " ").trim();
|
|
10
|
+
const { removedTags, preservedTags } = getTags(config);
|
|
11
|
+
return normalizeWhitespace(serialize({ node: root, removedTags, preservedTags }));
|
|
15
12
|
}
|
|
16
13
|
export function janusClient(text, config = janusConfig) {
|
|
17
14
|
validateText(text);
|
|
18
|
-
const tags = getTags(config);
|
|
19
15
|
const parser = new DOMParser();
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
const cleanText = virtualDoc.body.textContent;
|
|
24
|
-
return cleanText;
|
|
16
|
+
const { removedTags, preservedTags } = getTags(config);
|
|
17
|
+
const root = parser.parseFromString(text, "text/html").body;
|
|
18
|
+
return normalizeWhitespace(serialize({ node: root, removedTags, preservedTags }));
|
|
25
19
|
}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
|
+
import type { HTMLElement as NodeHTMLElement } from "node-html-parser";
|
|
1
2
|
export type Config = {
|
|
2
|
-
|
|
3
|
-
|
|
3
|
+
tagsToRemove?: string[];
|
|
4
|
+
tagsToPreserve?: string[];
|
|
4
5
|
};
|
|
5
6
|
export declare function validateText(text: string): void;
|
|
6
|
-
export declare function
|
|
7
|
-
export declare function
|
|
7
|
+
export declare function normalizeWhitespace(text?: string): string;
|
|
8
|
+
export declare function getTags(config: Config): {
|
|
9
|
+
removedTags: Set<string>;
|
|
10
|
+
preservedTags: Set<string>;
|
|
11
|
+
};
|
|
12
|
+
export declare function serialize({ node, removedTags, preservedTags, }: {
|
|
13
|
+
node: Node;
|
|
14
|
+
removedTags: Set<string>;
|
|
15
|
+
preservedTags: Set<string>;
|
|
16
|
+
}): string;
|
|
17
|
+
type Node = NodeHTMLElement | ChildNode;
|
|
18
|
+
export {};
|
|
8
19
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/utils/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG;IACnB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEvE,MAAM,MAAM,MAAM,GAAG;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAIF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,QAUxC;AAED,wBAAgB,mBAAmB,CAAC,IAAI,SAAK,UAE5C;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM;;;EAiBrC;AAED,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,WAAW,EACX,aAAa,GACd,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B,GAAG,MAAM,CAgCT;AAwBD,KAAK,IAAI,GAAG,eAAe,GAAG,SAAS,CAAC"}
|
package/dist/utils/index.js
CHANGED
|
@@ -5,33 +5,64 @@ export function validateText(text) {
|
|
|
5
5
|
throw new Error("The text is not a valid string. Please provide a valid string to parse.", { cause: text });
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
+
export function normalizeWhitespace(text = "") {
|
|
9
|
+
return text.replace(new RegExp(String.raw `\s+`, "g"), " ").trim();
|
|
10
|
+
}
|
|
8
11
|
export function getTags(config) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
const removedTags = new Set(defaultBlacklistTags);
|
|
13
|
+
const preservedTags = new Set();
|
|
14
|
+
if (config.tagsToRemove) {
|
|
15
|
+
addBlackListTags(removedTags, config.tagsToRemove);
|
|
12
16
|
}
|
|
13
|
-
if (config.
|
|
14
|
-
|
|
17
|
+
if (config.tagsToPreserve) {
|
|
18
|
+
removeBlackListTags({
|
|
19
|
+
removedTags,
|
|
20
|
+
preservedTags,
|
|
21
|
+
tagsToPreserve: config.tagsToPreserve,
|
|
22
|
+
});
|
|
15
23
|
}
|
|
16
|
-
return
|
|
24
|
+
return { removedTags, preservedTags };
|
|
17
25
|
}
|
|
18
|
-
export function
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
export function serialize({ node, removedTags, preservedTags, }) {
|
|
27
|
+
if (!node)
|
|
28
|
+
return "";
|
|
29
|
+
if (node.nodeType === 3) {
|
|
30
|
+
// DOM Text node => 3
|
|
31
|
+
const rawText = validateNode(node, "rawText");
|
|
32
|
+
const text = validateNode(node, "text");
|
|
33
|
+
return rawText ?? text ?? node.textContent ?? "";
|
|
34
|
+
}
|
|
35
|
+
const tagName = validateNode(node, "tagName") ?? "";
|
|
36
|
+
const tag = tagName.toLowerCase();
|
|
37
|
+
// Completely ignore blacklisted tags
|
|
38
|
+
if (removedTags.has(tag))
|
|
39
|
+
return "";
|
|
40
|
+
// Keep preserved tags intact along with their outer HTML structure
|
|
41
|
+
if (preservedTags.has(tag)) {
|
|
42
|
+
const outerHTML = validateNode(node, "outerHTML");
|
|
43
|
+
return (outerHTML ??
|
|
44
|
+
(typeof node.toString === "function" ? node.toString() : undefined) ??
|
|
45
|
+
"");
|
|
21
46
|
}
|
|
22
|
-
|
|
47
|
+
const children = [...node.childNodes];
|
|
48
|
+
return children
|
|
49
|
+
.map((child) => serialize({ node: child, removedTags, preservedTags }))
|
|
50
|
+
.join("");
|
|
23
51
|
}
|
|
24
|
-
function removeBlackListTags(
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
52
|
+
function removeBlackListTags({ removedTags, preservedTags, tagsToPreserve, }) {
|
|
53
|
+
for (const tag of tagsToPreserve) {
|
|
54
|
+
const normalized = tag.toLowerCase();
|
|
55
|
+
removedTags.delete(normalized);
|
|
56
|
+
preservedTags.add(normalized);
|
|
28
57
|
}
|
|
29
|
-
return blacklistTags;
|
|
30
58
|
}
|
|
31
|
-
function addBlackListTags(
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
blacklistTags.add(tag);
|
|
59
|
+
function addBlackListTags(preserveTags, tagsToRemove) {
|
|
60
|
+
for (const tag of tagsToRemove) {
|
|
61
|
+
preserveTags.add(tag.toLowerCase());
|
|
35
62
|
}
|
|
36
|
-
|
|
63
|
+
}
|
|
64
|
+
function validateNode(node, lookup = "outerHTML") {
|
|
65
|
+
const targetNode = node;
|
|
66
|
+
const value = targetNode[lookup];
|
|
67
|
+
return value ?? undefined;
|
|
37
68
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "janus-parse",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
|
+
"packageManager": "pnpm@11.15.1",
|
|
7
8
|
"keywords": [
|
|
8
9
|
"html-parser",
|
|
9
10
|
"text-extractor",
|
|
@@ -44,9 +45,11 @@
|
|
|
44
45
|
"type": "module",
|
|
45
46
|
"prepack": "pnpm build",
|
|
46
47
|
"scripts": {
|
|
47
|
-
"janus": "tsx ./lib
|
|
48
|
+
"janus": "tsx ./lib",
|
|
48
49
|
"test": "vitest",
|
|
50
|
+
"code:lint": "pnpm eslint --flag unstable_native_nodejs_ts_config --cache .",
|
|
49
51
|
"fmt": "prettier --write .",
|
|
52
|
+
"type:check": "tsc --noEmit",
|
|
50
53
|
"build": "tsc",
|
|
51
54
|
"prepublishOnly": "pnpm build"
|
|
52
55
|
},
|
|
@@ -54,18 +57,24 @@
|
|
|
54
57
|
"node-html-parser": "9.0.0"
|
|
55
58
|
},
|
|
56
59
|
"devDependencies": {
|
|
60
|
+
"@eslint/js": "^10.0.1",
|
|
57
61
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
58
62
|
"@semantic-release/github": "^12.0.9",
|
|
59
63
|
"@semantic-release/npm": "^13.1.5",
|
|
60
64
|
"@semantic-release/release-notes-generator": "^14.1.1",
|
|
61
65
|
"@types/node": "^26.1.1",
|
|
62
66
|
"eslint": "^10.7.0",
|
|
67
|
+
"eslint-import-resolver-typescript": "^4.4.5",
|
|
68
|
+
"eslint-plugin-import-x": "^4.17.1",
|
|
69
|
+
"eslint-plugin-unicorn": "^72.0.0",
|
|
63
70
|
"happy-dom": "^20.11.0",
|
|
64
|
-
"prettier": "^3.9.
|
|
71
|
+
"prettier": "^3.9.6",
|
|
65
72
|
"semantic-release": "^25.0.8",
|
|
66
73
|
"tsx": "4.23.1",
|
|
67
|
-
"typescript": "
|
|
74
|
+
"typescript": "6.0.3",
|
|
75
|
+
"typescript-eslint": "^8.65.0",
|
|
68
76
|
"vitest": "^4.1.10"
|
|
69
77
|
},
|
|
70
|
-
"prettier": {}
|
|
78
|
+
"prettier": {},
|
|
79
|
+
"--Typescript V 7--": "cannot use TS v7 until typescript-eslint adds support"
|
|
71
80
|
}
|