@daz4126/helium 0.29.0 → 0.31.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/.claude/settings.local.json +15 -0
- package/CLAUDE.md +137 -0
- package/README.md +46 -18
- package/examples/index.html +485 -0
- package/examples/sse-demo.html +125 -0
- package/examples/sse-server.js +138 -0
- package/examples/sse-simple.html +49 -0
- package/examples/test-password.html +251 -0
- package/helium.js +568 -336
- package/helium.test.js +562 -212
- package/package.json +6 -2
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"WebFetch(domain:data-star.dev)",
|
|
5
|
+
"WebFetch(domain:github.com)",
|
|
6
|
+
"WebFetch(domain:raw.githubusercontent.com)",
|
|
7
|
+
"WebSearch",
|
|
8
|
+
"WebFetch(domain:cdn.jsdelivr.net)",
|
|
9
|
+
"Bash(npm run test:run:*)",
|
|
10
|
+
"Bash(npm link)",
|
|
11
|
+
"Bash(npm install)",
|
|
12
|
+
"Bash(npm link helium)"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
Helium is an ultra-light (~3KB minified/gzipped) reactive library that makes HTML interactive using declarative attributes. It requires no build step and works directly in the browser.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm test # Run tests in watch mode (Vitest)
|
|
13
|
+
npm run test:ui # Run tests with interactive UI dashboard
|
|
14
|
+
npm run test:run # Run tests once (CI mode)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
There is no build step - `helium.js` is the single-file library used directly.
|
|
18
|
+
|
|
19
|
+
## Architecture
|
|
20
|
+
|
|
21
|
+
### Core Concepts
|
|
22
|
+
|
|
23
|
+
- **Single-file library**: All code lives in `helium.js` (minified/golfed for size)
|
|
24
|
+
- **Proxy-based reactivity**: Uses JavaScript Proxy to intercept state changes and trigger DOM updates
|
|
25
|
+
- **Attribute-driven**: Interactivity is declared via `@` prefixed HTML attributes (with `data-he-*` aliases for HTML validation)
|
|
26
|
+
- **Expression compilation**: Inline expressions are compiled to functions using `new Function()` with caching
|
|
27
|
+
- **Pluggable expression engine**: Use `createHelium()` to create variants with custom expression engines
|
|
28
|
+
|
|
29
|
+
### Data Flow
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
HTML parsed → processElements() registers bindings → expressions compiled (cached)
|
|
33
|
+
→ initial applyBinding() updates DOM → state changes via Proxy → triggers bound updates
|
|
34
|
+
→ MutationObserver watches for new content → processes dynamically added elements
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Key Internal Components
|
|
38
|
+
|
|
39
|
+
- **Global state**: `HELIUM.state` - single proxy object holding all reactive state
|
|
40
|
+
- **Bindings registry**: `HELIUM.bindings` - maps state properties to UI update functions
|
|
41
|
+
- **Expression cache**: Compiled functions are cached to avoid recompilation
|
|
42
|
+
- **MutationObserver**: Automatically processes dynamically added elements
|
|
43
|
+
- **WeakMaps/WeakSets**: Used for listener tracking and processed element tracking (memory-efficient)
|
|
44
|
+
|
|
45
|
+
### File Structure
|
|
46
|
+
|
|
47
|
+
- `helium.js` - Main library implementation
|
|
48
|
+
- `helium.test.js` - Comprehensive test suite
|
|
49
|
+
- `vitest.config.js` - Test configuration (jsdom environment, globals enabled)
|
|
50
|
+
|
|
51
|
+
### Exports
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
import helium from 'helium'; // Default helium function
|
|
55
|
+
import { createHelium } from 'helium'; // Factory for custom variants
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Key Directives
|
|
59
|
+
|
|
60
|
+
| Directive | Purpose |
|
|
61
|
+
|-----------|---------|
|
|
62
|
+
| `@data` | Initialize state: `@data="{ count: 0 }"` |
|
|
63
|
+
| `@text` | Bind text content |
|
|
64
|
+
| `@html` | Bind HTML content (use with caution for XSS) |
|
|
65
|
+
| `@bind` | Two-way binding for inputs |
|
|
66
|
+
| `@hidden/@visible` | Conditional visibility |
|
|
67
|
+
| `:attribute` | Dynamic attribute binding |
|
|
68
|
+
| `@click/@input/etc` | Event handlers (supports modifiers like `.prevent`, `.once`, `.debounce`) |
|
|
69
|
+
| `@ref` | Element reference (accessed as `$refName`) |
|
|
70
|
+
| `@calculate` | Computed properties |
|
|
71
|
+
| `@effect` | Side effects on state changes |
|
|
72
|
+
| `@get/@post/@put/@patch/@delete` | HTTP requests |
|
|
73
|
+
| `@import` | Import ES modules: `@import="utils"` or `@import="https://cdn.example.com/lib.js"` |
|
|
74
|
+
|
|
75
|
+
## Imports
|
|
76
|
+
|
|
77
|
+
`@import` loads ES modules and adds their named exports to state. Paths are flexible:
|
|
78
|
+
- `utils` → `./utils.js` (same folder, .js added automatically)
|
|
79
|
+
- `modules/helpers` → `./modules/helpers.js` (subfolders work)
|
|
80
|
+
- `https://...` → URLs used as-is (for CDNs, GitHub raw files, etc.)
|
|
81
|
+
|
|
82
|
+
## Magic Variables
|
|
83
|
+
|
|
84
|
+
Available in all expressions: `$` (querySelector), `$el` (current element), `$event`, `$data` (reactive state), `$html` (create elements), `$refs`, `$get/$post/$put/$patch/$delete`.
|
|
85
|
+
|
|
86
|
+
## Important Notes
|
|
87
|
+
|
|
88
|
+
- **Functions and reactivity**: Magic variables are NOT available inside functions defined in `@data`. Pass `$data` as an argument to enable reactive updates: `@click="myFunc($data)"`
|
|
89
|
+
- **CSP**: Library uses `new Function()` which requires `unsafe-eval` if using strict Content Security Policy. For CSP-safe environments, see the [Xenon](https://github.com/daz-codes/xenon) variant.
|
|
90
|
+
- **Idiomorph integration**: If Idiomorph is loaded, Helium automatically uses it for efficient DOM morphing
|
|
91
|
+
- **Turbo/Hotwire**: Automatic integration via `turbo:before-render` and `turbo:render` events
|
|
92
|
+
|
|
93
|
+
## Creating Custom Variants
|
|
94
|
+
|
|
95
|
+
Use `createHelium()` to create variants with custom expression engines:
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
import { createHelium } from 'helium';
|
|
99
|
+
|
|
100
|
+
const { helium, heliumTeardown } = createHelium({
|
|
101
|
+
engine: {
|
|
102
|
+
compile(expr, withReturn) {
|
|
103
|
+
// Return { execute(scope), getIds() }
|
|
104
|
+
},
|
|
105
|
+
createScope(ctx) {
|
|
106
|
+
// Return scope object for expression execution
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
prefix: 'my', // For data-my-* attributes
|
|
110
|
+
rootAttr: 'mylib' // For @mylib / data-mylib root element
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Testing Patterns
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
describe('Feature', () => {
|
|
118
|
+
let container;
|
|
119
|
+
|
|
120
|
+
beforeEach(() => {
|
|
121
|
+
container = document.createElement('div');
|
|
122
|
+
container.setAttribute('data-helium', '');
|
|
123
|
+
document.body.appendChild(container);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
afterEach(() => {
|
|
127
|
+
window.heliumTeardown?.();
|
|
128
|
+
document.body.innerHTML = '';
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should work', async () => {
|
|
132
|
+
container.innerHTML = '<div @text="count"></div>';
|
|
133
|
+
await helium({ count: 5 });
|
|
134
|
+
expect(container.querySelector('div').textContent).toBe('5');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
```
|
package/README.md
CHANGED
|
@@ -263,33 +263,61 @@ Runs a side effect whenever specified dependencies change. Use `:*` to run on an
|
|
|
263
263
|
|
|
264
264
|
### @import
|
|
265
265
|
|
|
266
|
-
Imports
|
|
266
|
+
Imports ES modules into Helium's scope, making their exports available in Helium expressions.
|
|
267
267
|
|
|
268
268
|
```html
|
|
269
|
-
<div @import="
|
|
270
|
-
<button @click="
|
|
271
|
-
<p @text="
|
|
269
|
+
<div @import="utils,api">
|
|
270
|
+
<button @click="formatDate(new Date())">Format Date</button>
|
|
271
|
+
<p @text="API_VERSION"></p>
|
|
272
272
|
</div>
|
|
273
273
|
```
|
|
274
274
|
|
|
275
|
-
This
|
|
275
|
+
This dynamically imports modules and adds all their named exports to Helium's state.
|
|
276
|
+
|
|
277
|
+
**Import paths:**
|
|
278
|
+
|
|
279
|
+
| Path | Resolves to |
|
|
280
|
+
|------|-------------|
|
|
281
|
+
| `utils` | `./utils.js` |
|
|
282
|
+
| `modules/helpers` | `./modules/helpers.js` |
|
|
283
|
+
| `./utils.js` | `./utils.js` |
|
|
284
|
+
| `../shared/utils` | `../shared/utils.js` |
|
|
285
|
+
| `https://example.com/lib.js` | `https://example.com/lib.js` |
|
|
286
|
+
|
|
287
|
+
The `.js` extension is added automatically if not provided. URLs are used as-is.
|
|
276
288
|
|
|
277
289
|
**Example:**
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
// utils.js
|
|
293
|
+
export function formatDate(date) {
|
|
294
|
+
return date.toLocaleDateString();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function capitalize(str) {
|
|
298
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```javascript
|
|
303
|
+
// api.js
|
|
304
|
+
export const API_VERSION = '1.0.0';
|
|
305
|
+
export const API_URL = 'https://api.example.com';
|
|
306
|
+
```
|
|
307
|
+
|
|
278
308
|
```html
|
|
279
|
-
<
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
window.appConfig = {
|
|
285
|
-
version: '1.0.0',
|
|
286
|
-
apiUrl: 'https://api.example.com'
|
|
287
|
-
};
|
|
288
|
-
</script>
|
|
309
|
+
<div @import="utils,api">
|
|
310
|
+
<button @click="alert(capitalize('hello'))">Capitalize</button>
|
|
311
|
+
<p @text="API_VERSION"></p>
|
|
312
|
+
</div>
|
|
313
|
+
```
|
|
289
314
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
315
|
+
**Importing from URLs:**
|
|
316
|
+
|
|
317
|
+
```html
|
|
318
|
+
<!-- Import from a CDN or GitHub -->
|
|
319
|
+
<div @import="https://cdn.example.com/helpers.js">
|
|
320
|
+
<button @click="helper()">Use Remote Helper</button>
|
|
293
321
|
</div>
|
|
294
322
|
```
|
|
295
323
|
|