Skip to content

Commit a12b35d

Browse files
authored
feat: first version
1 parent d99eefe commit a12b35d

File tree

11 files changed

+212
-1
lines changed

11 files changed

+212
-1
lines changed

.github/workflows/cicd.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: release
2+
on:
3+
push:
4+
5+
jobs:
6+
build:
7+
uses: cloud-cli/workflows/.github/workflows/npm-build-release.yml@main
8+
secrets:
9+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
10+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
dist/
3+
.cache
4+
coverage/
5+
index.js

.npmignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.github
2+
src/
3+
coverage/
4+
rollup.config.js
5+
tsconfig*
6+
.cache
7+
.prettierrc
8+
karma*
9+
test/
10+
test.js

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
# parse-html-runtime
1+
# @homebots/parse-html-runtime
2+
23
Runtime extensions for @homebots/parse-html

package.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "@homebots/parse-html-runtime",
3+
"version": "0.0.0-dev",
4+
"description": "Runtime extension for @homebots/parse-html",
5+
"main": "./index.js",
6+
"typings": "./dist/index.d.ts",
7+
"type": "module",
8+
"scripts": {
9+
"clean": "rm -rf dist/",
10+
"test": "echo true",
11+
"build": "npm run clean && tsc --project ./tsconfig.json && npm run rollup",
12+
"rollup": "./node_modules/.bin/rollup -c rollup.config.js"
13+
},
14+
"repository": {
15+
"type": "git",
16+
"url": "https://github.com/homebots/parse-html-runtime.git"
17+
},
18+
"author": "Darlan Alves <me@darlanalv.es>",
19+
"license": "MIT",
20+
"publishConfig": {
21+
"access": "public"
22+
},
23+
"devDependencies": {
24+
"@rollup/plugin-commonjs": "^24.0.0",
25+
"@rollup/plugin-node-resolve": "^15.0.1",
26+
"@rollup/plugin-typescript": "^10.0.1",
27+
"@types/node": "^20.0.0",
28+
"rollup": "^2.79.1",
29+
"ts-node": "^10.9.1",
30+
"tslib": "^2.4.1",
31+
"typescript": "^4.9.4",
32+
"@homebots/parse-html": "^1.0.0"
33+
}
34+
}

rollup.config.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import typescript from "@rollup/plugin-typescript";
2+
import commonjs from "@rollup/plugin-commonjs";
3+
import resolve from "@rollup/plugin-node-resolve";
4+
5+
export default {
6+
input: "src/index.ts",
7+
external: [],
8+
output: {
9+
dir: ".",
10+
format: "es",
11+
},
12+
plugins: [
13+
typescript({ module: "esnext" }),
14+
resolve(),
15+
commonjs({ extensions: [".js", ".ts"] }),
16+
],
17+
};

src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export { normalize } from './normalize';
2+
export { serialize } from './serialize';
3+
export { materialize, createComment, createElement, createTextNode } from './materialize';
4+
export type { Visitor } from './materialize';
5+
export type { ParserNode, ParserAttribute, CommentNode, TextNode, ElementNode } from '@homebots/parse-html';

src/materialize.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import type { ParserNode, ParserAttribute, CommentNode, TextNode, ElementNode } from '@homebots/parse-html';
2+
3+
const validAttribute = /^[a-z][a-z0-9-]+$/;
4+
const noop = () => {};
5+
6+
export type Visitor = <T>(el: T, node: ParserNode) => T | null | undefined | void;
7+
8+
export function materialize(node: ParserNode, visitor: Visitor = noop) {
9+
let el: any;
10+
11+
switch (node.type) {
12+
case 'document': {
13+
el = document.createDocumentFragment();
14+
el.append(...node.children.map((n) => materialize(n, visitor)));
15+
break;
16+
}
17+
18+
case 'text':
19+
el = createTextNode(node);
20+
break;
21+
22+
case 'comment':
23+
el = createComment(node);
24+
break;
25+
26+
case 'element': {
27+
el = createElement(node);
28+
el.append(...node.children.map((n) => materialize(n, visitor)));
29+
break;
30+
}
31+
32+
default:
33+
throw new Error(`Invalid node type: ${(node as any).type}`);
34+
}
35+
36+
return visitor(el, node) || el;
37+
}
38+
39+
export function createComment(node: CommentNode) {
40+
return document.createComment(node.text);
41+
}
42+
43+
export function createTextNode(node: TextNode) {
44+
return document.createTextNode(node.text);
45+
}
46+
47+
export function createElement(node: ElementNode) {
48+
const el = document.createElement(node.tag);
49+
el['@attributes'] = node.attributes;
50+
node.attributes.forEach((a: ParserAttribute) => setAttribute(el, a.name, a.value));
51+
52+
return el;
53+
}
54+
55+
export function setAttribute(el: HTMLElement, attribute: string, value: string | number | boolean) {
56+
if (!validAttribute.test(attribute)) {
57+
return;
58+
}
59+
60+
if (typeof value === 'boolean' && value === false) {
61+
el.removeAttribute(attribute);
62+
return;
63+
}
64+
65+
el.setAttribute(attribute, String(value));
66+
}
67+
68+
export function setProperty(el: HTMLElement, property: string, value: any) {
69+
el[property] = value;
70+
}

src/normalize.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export function normalize(node) {
2+
if (!node.children) return node;
3+
4+
node.children = node.children.filter((child) => {
5+
if (child.type === 'text' && child.text.trim() === '') {
6+
return false;
7+
}
8+
9+
normalize(child);
10+
return true;
11+
});
12+
13+
return node;
14+
}

src/serialize.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { ParserNode } from './types';
2+
3+
export function serialize(node: ParserNode) {
4+
switch (node.type) {
5+
case 'document':
6+
return node.children.map(serialize).join('');
7+
8+
case 'text':
9+
return node.text;
10+
11+
case 'comment':
12+
return `<!-- ${node.text} -->`;
13+
14+
case 'element':
15+
const attr = node.attributes.length
16+
? ' ' + node.attributes.map((a) => (a.value !== '' ? `${a.name}="${a.value}"` : a.name)).join(' ')
17+
: '';
18+
19+
const children = node.children.map(serialize).join('');
20+
21+
if (node.selfClose) {
22+
return `<${node.tag} ${attr}/>`;
23+
}
24+
25+
return `<${node.tag}${attr}>${children}</${node.tag}>`;
26+
}
27+
}

tsconfig.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"compileOnSave": true,
3+
"compilerOptions": {
4+
"moduleResolution": "node",
5+
"target": "es2020",
6+
"lib": ["ESNext", "DOM"],
7+
"experimentalDecorators": true,
8+
"emitDecoratorMetadata": true,
9+
"noUnusedLocals": true,
10+
"noUnusedParameters": true,
11+
"typeRoots": ["./node_modules/@types"],
12+
"outDir": "./dist",
13+
"declaration": true,
14+
"baseUrl": "."
15+
},
16+
"files": ["src/index.ts"],
17+
"include": ["src/**/*.ts"]
18+
}

0 commit comments

Comments
 (0)