@ryupold/vode 0.9.3 → 0.9.4
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/.github/workflows/npm-publish.yml +24 -23
- package/LICENSE +21 -21
- package/README.md +126 -126
- package/index.ts +3 -5
- package/package.json +6 -3
- package/src/html.ts +37 -37
- package/src/vode-tags.ts +206 -206
- package/src/vode.ts +693 -693
- package/tsconfig.json +30 -28
- package/index.js +0 -6
- package/src/api-call.ts +0 -116
- package/src/helpers.ts +0 -44
- package/src/style.ts +0 -17
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
name: Publish to https://registry.npmjs.org
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- 'main'
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
build:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
permissions:
|
|
12
|
-
contents: read
|
|
13
|
-
id-token: write
|
|
14
|
-
steps:
|
|
15
|
-
- uses: actions/checkout@v4
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- run:
|
|
22
|
-
- run:
|
|
23
|
-
|
|
1
|
+
name: Publish to https://registry.npmjs.org
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- 'main'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
id-token: write
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-node@v4
|
|
17
|
+
with:
|
|
18
|
+
node-version: '22.x'
|
|
19
|
+
registry-url: 'https://registry.npmjs.org'
|
|
20
|
+
- uses: oven-sh/setup-bun@v2
|
|
21
|
+
- run: bun install
|
|
22
|
+
- run: bun run build
|
|
23
|
+
- run: bun publish --provenance --access public
|
|
24
|
+
env:
|
|
24
25
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Michael Scherbakow
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Michael Scherbakow
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,127 +1,127 @@
|
|
|
1
|
-
# vode
|
|
2
|
-
|
|
3
|
-
Small web framework for minimal websites.
|
|
4
|
-
Each vode app has its own state and renders a tree of HTML elements.
|
|
5
|
-
The state is a singleton object that can be updated, and the UI will re-render when a patch is supplied. Nesting vode-apps is undefined behavior for now.
|
|
6
|
-
|
|
7
|
-
## Install
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @ryupold/vode --save
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Patch
|
|
14
|
-
|
|
15
|
-
The `patch` function returned by `app(...)` is a function that can be passed an object called `Patch` this object is used to update the state and re-render the UI. It takes a `Patch` object that describes the changes to be made to the state in a "trickle down manner". The `Patch` can be a simple object or a function that returns a new `Patch` to the current state. It can also be an async and/or genrator function that yields `Patch`es. Events also can return a `Patch`. When a number | boolean | string | null | undefined is applied as a `Patch`, it will be ignored.
|
|
16
|
-
|
|
17
|
-
## Usage
|
|
18
|
-
|
|
19
|
-
index.html
|
|
20
|
-
|
|
21
|
-
```html
|
|
22
|
-
<html>
|
|
23
|
-
<head>
|
|
24
|
-
<title>Vode Example</title>
|
|
25
|
-
<script type="module" src="main.js"></script>
|
|
26
|
-
</head>
|
|
27
|
-
<body>
|
|
28
|
-
<div id="app"></div>
|
|
29
|
-
</body>
|
|
30
|
-
</html>
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
main.ts
|
|
34
|
-
```ts
|
|
35
|
-
// import { app, createState} from 'vode.js';
|
|
36
|
-
// import { BR, DIV, INPUT, SPAN } from 'vode-tags.js';
|
|
37
|
-
import { app, createState, BR, DIV, INPUT, SPAN } from '@ryupold/vode';
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const init = createState({
|
|
41
|
-
counter: 0,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
type State = typeof init;
|
|
45
|
-
|
|
46
|
-
const appNode = document.getElementById('app');
|
|
47
|
-
|
|
48
|
-
app<State>(appNode, init,
|
|
49
|
-
(s: State) => [DIV,
|
|
50
|
-
[INPUT, {
|
|
51
|
-
type: 'button',
|
|
52
|
-
onclick: { counter: s.counter + 1 },
|
|
53
|
-
value: 'Click me',
|
|
54
|
-
}],
|
|
55
|
-
[BR],
|
|
56
|
-
[SPAN, { style: { color: 'red' } }, `${s.counter}`],
|
|
57
|
-
]
|
|
58
|
-
);
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Claude
|
|
62
|
-
|
|
63
|
-
> Prompt: "what can you tell me about the overall design philosophy of the vode library based on what you see in `src/vode.ts`"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
Based on my analysis of `src/vode.ts`, here's the overall design philosophy of the vode library:
|
|
67
|
-
|
|
68
|
-
### Core Design Philosophy
|
|
69
|
-
|
|
70
|
-
**Functional Virtual DOM with Minimal Abstractions**
|
|
71
|
-
- Uses array-based virtual nodes (`[tag, props?, ...children]`) instead of object-based structures
|
|
72
|
-
- Prioritizes simplicity and performance over complex abstractions
|
|
73
|
-
|
|
74
|
-
**State-Centric Architecture**
|
|
75
|
-
- Central state object with patch-based updates
|
|
76
|
-
- Immutable-style state management with deep merging
|
|
77
|
-
- Single source of truth pattern
|
|
78
|
-
|
|
79
|
-
**Zero-Dependency Minimalism**
|
|
80
|
-
- Pure TypeScript/JavaScript implementation
|
|
81
|
-
- No external dependencies or build tools required
|
|
82
|
-
- Direct DOM manipulation without heavy runtime
|
|
83
|
-
|
|
84
|
-
### Key Architectural Decisions
|
|
85
|
-
|
|
86
|
-
#### Virtual DOM Structure
|
|
87
|
-
```typescript
|
|
88
|
-
// Simple array-based nodes
|
|
89
|
-
["div", { class: "container" }, "Hello", ["span", "World"]]
|
|
90
|
-
```
|
|
91
|
-
- Minimal memory footprint
|
|
92
|
-
- Easy serialization/debugging
|
|
93
|
-
- Type-safe with TypeScript generics
|
|
94
|
-
|
|
95
|
-
#### Reactive State Management
|
|
96
|
-
```typescript
|
|
97
|
-
state.patch(newData) // Direct updates
|
|
98
|
-
state.patch([effect, payload]) // Function-based effects
|
|
99
|
-
state.patch(promise) // Async support
|
|
100
|
-
```
|
|
101
|
-
- Unified patch system for all state changes
|
|
102
|
-
- Built-in async/generator support
|
|
103
|
-
- Functional effects pattern
|
|
104
|
-
|
|
105
|
-
#### Performance Optimizations
|
|
106
|
-
- **Memoization**: `memo()` for component caching
|
|
107
|
-
- **Reconciliation**: Intelligent DOM diffing
|
|
108
|
-
- **Batched Updates**: RequestAnimationFrame-based rendering
|
|
109
|
-
- **Memory Management**: Proper cleanup with mount/unmount hooks
|
|
110
|
-
|
|
111
|
-
#### Developer Experience
|
|
112
|
-
- **Type Safety**: Full TypeScript support with generics
|
|
113
|
-
- **Debugging**: Built-in performance statistics
|
|
114
|
-
- **Flexibility**: Support for HTML, SVG, and MathML
|
|
115
|
-
- **Composability**: Functional component model
|
|
116
|
-
|
|
117
|
-
### Philosophy Summary
|
|
118
|
-
|
|
119
|
-
Vode follows a **"less is more"** philosophy, providing a lightweight alternative to heavyweight frameworks while maintaining modern reactive patterns. It emphasizes:
|
|
120
|
-
|
|
121
|
-
1. **Explicitness over magic** - Clear, predictable behavior
|
|
122
|
-
2. **Performance by design** - Minimal overhead, efficient updates
|
|
123
|
-
3. **Developer control** - Direct state manipulation without hidden abstractions
|
|
124
|
-
4. **Functional paradigms** - Immutable updates, pure components
|
|
125
|
-
5. **Modern web standards** - Leverages native browser APIs effectively
|
|
126
|
-
|
|
1
|
+
# vode
|
|
2
|
+
|
|
3
|
+
Small web framework for minimal websites.
|
|
4
|
+
Each vode app has its own state and renders a tree of HTML elements.
|
|
5
|
+
The state is a singleton object that can be updated, and the UI will re-render when a patch is supplied. Nesting vode-apps is undefined behavior for now.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @ryupold/vode --save
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Patch
|
|
14
|
+
|
|
15
|
+
The `patch` function returned by `app(...)` is a function that can be passed an object called `Patch` this object is used to update the state and re-render the UI. It takes a `Patch` object that describes the changes to be made to the state in a "trickle down manner". The `Patch` can be a simple object or a function that returns a new `Patch` to the current state. It can also be an async and/or genrator function that yields `Patch`es. Events also can return a `Patch`. When a number | boolean | string | null | undefined is applied as a `Patch`, it will be ignored.
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
index.html
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<html>
|
|
23
|
+
<head>
|
|
24
|
+
<title>Vode Example</title>
|
|
25
|
+
<script type="module" src="main.js"></script>
|
|
26
|
+
</head>
|
|
27
|
+
<body>
|
|
28
|
+
<div id="app"></div>
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
main.ts
|
|
34
|
+
```ts
|
|
35
|
+
// import { app, createState} from 'vode.js';
|
|
36
|
+
// import { BR, DIV, INPUT, SPAN } from 'vode-tags.js';
|
|
37
|
+
import { app, createState, BR, DIV, INPUT, SPAN } from '@ryupold/vode';
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
const init = createState({
|
|
41
|
+
counter: 0,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
type State = typeof init;
|
|
45
|
+
|
|
46
|
+
const appNode = document.getElementById('app');
|
|
47
|
+
|
|
48
|
+
app<State>(appNode, init,
|
|
49
|
+
(s: State) => [DIV,
|
|
50
|
+
[INPUT, {
|
|
51
|
+
type: 'button',
|
|
52
|
+
onclick: { counter: s.counter + 1 },
|
|
53
|
+
value: 'Click me',
|
|
54
|
+
}],
|
|
55
|
+
[BR],
|
|
56
|
+
[SPAN, { style: { color: 'red' } }, `${s.counter}`],
|
|
57
|
+
]
|
|
58
|
+
);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Claude
|
|
62
|
+
|
|
63
|
+
> Prompt: "what can you tell me about the overall design philosophy of the vode library based on what you see in `src/vode.ts`"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
Based on my analysis of `src/vode.ts`, here's the overall design philosophy of the vode library:
|
|
67
|
+
|
|
68
|
+
### Core Design Philosophy
|
|
69
|
+
|
|
70
|
+
**Functional Virtual DOM with Minimal Abstractions**
|
|
71
|
+
- Uses array-based virtual nodes (`[tag, props?, ...children]`) instead of object-based structures
|
|
72
|
+
- Prioritizes simplicity and performance over complex abstractions
|
|
73
|
+
|
|
74
|
+
**State-Centric Architecture**
|
|
75
|
+
- Central state object with patch-based updates
|
|
76
|
+
- Immutable-style state management with deep merging
|
|
77
|
+
- Single source of truth pattern
|
|
78
|
+
|
|
79
|
+
**Zero-Dependency Minimalism**
|
|
80
|
+
- Pure TypeScript/JavaScript implementation
|
|
81
|
+
- No external dependencies or build tools required
|
|
82
|
+
- Direct DOM manipulation without heavy runtime
|
|
83
|
+
|
|
84
|
+
### Key Architectural Decisions
|
|
85
|
+
|
|
86
|
+
#### Virtual DOM Structure
|
|
87
|
+
```typescript
|
|
88
|
+
// Simple array-based nodes
|
|
89
|
+
["div", { class: "container" }, "Hello", ["span", "World"]]
|
|
90
|
+
```
|
|
91
|
+
- Minimal memory footprint
|
|
92
|
+
- Easy serialization/debugging
|
|
93
|
+
- Type-safe with TypeScript generics
|
|
94
|
+
|
|
95
|
+
#### Reactive State Management
|
|
96
|
+
```typescript
|
|
97
|
+
state.patch(newData) // Direct updates
|
|
98
|
+
state.patch([effect, payload]) // Function-based effects
|
|
99
|
+
state.patch(promise) // Async support
|
|
100
|
+
```
|
|
101
|
+
- Unified patch system for all state changes
|
|
102
|
+
- Built-in async/generator support
|
|
103
|
+
- Functional effects pattern
|
|
104
|
+
|
|
105
|
+
#### Performance Optimizations
|
|
106
|
+
- **Memoization**: `memo()` for component caching
|
|
107
|
+
- **Reconciliation**: Intelligent DOM diffing
|
|
108
|
+
- **Batched Updates**: RequestAnimationFrame-based rendering
|
|
109
|
+
- **Memory Management**: Proper cleanup with mount/unmount hooks
|
|
110
|
+
|
|
111
|
+
#### Developer Experience
|
|
112
|
+
- **Type Safety**: Full TypeScript support with generics
|
|
113
|
+
- **Debugging**: Built-in performance statistics
|
|
114
|
+
- **Flexibility**: Support for HTML, SVG, and MathML
|
|
115
|
+
- **Composability**: Functional component model
|
|
116
|
+
|
|
117
|
+
### Philosophy Summary
|
|
118
|
+
|
|
119
|
+
Vode follows a **"less is more"** philosophy, providing a lightweight alternative to heavyweight frameworks while maintaining modern reactive patterns. It emphasizes:
|
|
120
|
+
|
|
121
|
+
1. **Explicitness over magic** - Clear, predictable behavior
|
|
122
|
+
2. **Performance by design** - Minimal overhead, efficient updates
|
|
123
|
+
3. **Developer control** - Direct state manipulation without hidden abstractions
|
|
124
|
+
4. **Functional paradigms** - Immutable updates, pure components
|
|
125
|
+
5. **Modern web standards** - Leverages native browser APIs effectively
|
|
126
|
+
|
|
127
127
|
The library appears designed for developers who want React-like reactivity without the complexity and bundle size of modern frameworks.
|
package/index.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export * from "./src/vode.js";
|
|
2
|
-
export * from "./src/vode-tags.js";
|
|
3
|
-
export * from "./src/
|
|
4
|
-
export * from "./src/html.js";
|
|
5
|
-
export * from "./src/helpers.js";
|
|
1
|
+
export * from "./src/vode.js";
|
|
2
|
+
export * from "./src/vode-tags.js";
|
|
3
|
+
export * from "./src/html.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryupold/vode",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "Small web framework for minimal websites",
|
|
5
5
|
"author": "Michael Scherbakow (ryupold)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,13 +20,16 @@
|
|
|
20
20
|
"url": "https://github.com/ryupold/vode/issues"
|
|
21
21
|
},
|
|
22
22
|
"homepage": "https://github.com/ryupold/vode#readme",
|
|
23
|
-
"
|
|
23
|
+
"module": "index.ts",
|
|
24
24
|
"scripts": {
|
|
25
|
+
"build": "bun build index.ts --outfile index.js",
|
|
26
|
+
"pack": "bun run build && bun pm pack",
|
|
27
|
+
"publish": "bun publish --provenance --access public",
|
|
25
28
|
"clean": "tsc -b --clean",
|
|
26
|
-
"build": "tsc -b",
|
|
27
29
|
"watch": "tsc -b -w"
|
|
28
30
|
},
|
|
29
31
|
"devDependencies": {
|
|
32
|
+
"bun": "^1.2.18",
|
|
30
33
|
"typescript": "^5.8.3"
|
|
31
34
|
}
|
|
32
35
|
}
|
package/src/html.ts
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import { Props, Vode } from "./vode.js";
|
|
2
|
-
|
|
3
|
-
export function htmlToVode<S extends object | unknown>(html: string): (Vode<S> | string)[] {
|
|
4
|
-
const div = document.createElement('div');
|
|
5
|
-
div.innerHTML = html.trim();
|
|
6
|
-
|
|
7
|
-
const vodes: (Vode<S> | string)[] = [];
|
|
8
|
-
for (const child of div.childNodes) {
|
|
9
|
-
const v = elementToVode<S>(<Element>child);
|
|
10
|
-
if (v != null) vodes.push(v);
|
|
11
|
-
}
|
|
12
|
-
return vodes;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function elementToVode<S>(element: Element): Vode<S> | string | undefined | null {
|
|
16
|
-
if (element.nodeType === Node.TEXT_NODE) {
|
|
17
|
-
return element.textContent;
|
|
18
|
-
}
|
|
19
|
-
if (element.nodeType !== Node.ELEMENT_NODE) {
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
const vode = <Vode<S>>[element.tagName.toLowerCase()];
|
|
23
|
-
|
|
24
|
-
if (element.hasAttributes()) {
|
|
25
|
-
const props = <Props<S>>{};
|
|
26
|
-
for (const att of element.attributes) {
|
|
27
|
-
props[att.name] = att.value;
|
|
28
|
-
}
|
|
29
|
-
(<any[]>vode).push(props);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
for (const child of element.childNodes) {
|
|
33
|
-
const v = elementToVode(<Element>child);
|
|
34
|
-
if (v && (typeof v !== "string" || v.length > 0)) (<any[]>vode).push(v);
|
|
35
|
-
}
|
|
36
|
-
return vode;
|
|
37
|
-
}
|
|
1
|
+
import { Props, Vode } from "./vode.js";
|
|
2
|
+
|
|
3
|
+
export function htmlToVode<S extends object | unknown>(html: string): (Vode<S> | string)[] {
|
|
4
|
+
const div = document.createElement('div');
|
|
5
|
+
div.innerHTML = html.trim();
|
|
6
|
+
|
|
7
|
+
const vodes: (Vode<S> | string)[] = [];
|
|
8
|
+
for (const child of div.childNodes) {
|
|
9
|
+
const v = elementToVode<S>(<Element>child);
|
|
10
|
+
if (v != null) vodes.push(v);
|
|
11
|
+
}
|
|
12
|
+
return vodes;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function elementToVode<S>(element: Element): Vode<S> | string | undefined | null {
|
|
16
|
+
if (element.nodeType === Node.TEXT_NODE) {
|
|
17
|
+
return element.textContent;
|
|
18
|
+
}
|
|
19
|
+
if (element.nodeType !== Node.ELEMENT_NODE) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const vode = <Vode<S>>[element.tagName.toLowerCase()];
|
|
23
|
+
|
|
24
|
+
if (element.hasAttributes()) {
|
|
25
|
+
const props = <Props<S>>{};
|
|
26
|
+
for (const att of element.attributes) {
|
|
27
|
+
props[att.name] = att.value;
|
|
28
|
+
}
|
|
29
|
+
(<any[]>vode).push(props);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const child of element.childNodes) {
|
|
33
|
+
const v = elementToVode(<Element>child);
|
|
34
|
+
if (v && (typeof v !== "string" || v.length > 0)) (<any[]>vode).push(v);
|
|
35
|
+
}
|
|
36
|
+
return vode;
|
|
37
|
+
}
|