@daz4126/helium 1.0.0-beta.1 → 1.0.0-beta.3
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.md +1 -3
- package/examples/index-lite.html +9 -0
- package/examples/index.html +16 -1
- package/helium (# Edit conflict 2026-02-04 FmuZMse #).js +629 -0
- package/helium (# Edit conflict 2026-02-04 j7PIppO #).js +629 -0
- package/helium (# Edit conflict 2026-02-04 mqpbJka #).js +629 -0
- package/helium-csp.js +17 -9
- package/helium-lite.js +3 -0
- package/helium.js +28 -18
- package/package.json +1 -1
package/helium.js
CHANGED
|
@@ -46,19 +46,23 @@ const defaultEngine = {
|
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
// Factory function to create helium with custom engine
|
|
49
|
+
// Factory function to create helium with custom engine
|
|
50
50
|
export function createHelium(options = {}) {
|
|
51
51
|
const engine = options.engine ?? defaultEngine;
|
|
52
|
-
const prefix = options.prefix ?? 'he'; // 'he' or 'xe'
|
|
53
|
-
const rootAttr = options.rootAttr ?? 'helium'; // 'helium' or 'xenon'
|
|
54
52
|
|
|
55
|
-
//
|
|
56
|
-
const
|
|
53
|
+
// Engine-aware expression parser (CSP-safe when using jexpr engine)
|
|
54
|
+
const evalExpr = v => {
|
|
55
|
+
try {
|
|
56
|
+
const compiled = engine.compile(v, true);
|
|
57
|
+
const scope = engine.createScope ? engine.createScope({ state: {}, el: null, event: {}, refs: {}, $: null, html: null, get: null, post: null, put: null, patch: null, del: null }) : {};
|
|
58
|
+
return compiled.execute(scope);
|
|
59
|
+
} catch { return v; }
|
|
60
|
+
};
|
|
57
61
|
|
|
58
62
|
const match = (name, ...attrs) => {
|
|
59
63
|
const p = name.split(/[.:]/)[0];
|
|
60
64
|
if (p === ":" || p === "") return false;
|
|
61
|
-
return attrs.some(a => p === `@${a}` || p ===
|
|
65
|
+
return attrs.some(a => p === `@${a}` || p === 'data-he-' + a);
|
|
62
66
|
};
|
|
63
67
|
|
|
64
68
|
// Single global object to hold all settings (scoped to this instance)
|
|
@@ -67,7 +71,8 @@ export function createHelium(options = {}) {
|
|
|
67
71
|
async function helium(initialState = {}) {
|
|
68
72
|
const inits = [];
|
|
69
73
|
const ALL = Symbol("all");
|
|
70
|
-
const root = document.querySelector(
|
|
74
|
+
const root = document.querySelector('[\\@helium]') || document.querySelector('[data-helium]') || document.body;
|
|
75
|
+
const storageKey = root.getAttribute('@local-storage') || root.getAttribute('data-he-local-storage');
|
|
71
76
|
|
|
72
77
|
// Initialize or reuse HELIUM object
|
|
73
78
|
if (!HELIUM) {
|
|
@@ -225,6 +230,7 @@ export function createHelium(options = {}) {
|
|
|
225
230
|
const parentKey = HELIUM.parentKeys.get(t);
|
|
226
231
|
if (parentKey) HELIUM.bindings.get(parentKey)?.forEach(safeApplyBinding);
|
|
227
232
|
HELIUM.bindings.get(ALL)?.forEach(safeApplyBinding);
|
|
233
|
+
if (storageKey) localStorage.setItem(storageKey, JSON.stringify(state));
|
|
228
234
|
return res
|
|
229
235
|
}
|
|
230
236
|
};
|
|
@@ -232,6 +238,9 @@ export function createHelium(options = {}) {
|
|
|
232
238
|
// Initialize state if it doesn't exist
|
|
233
239
|
const state = new Proxy({}, handler);
|
|
234
240
|
|
|
241
|
+
// Load from localStorage if key exists
|
|
242
|
+
if (storageKey) try { Object.assign(state, JSON.parse(localStorage.getItem(storageKey))); } catch {}
|
|
243
|
+
|
|
235
244
|
// Merge initial state
|
|
236
245
|
Object.assign(state, initialState);
|
|
237
246
|
|
|
@@ -373,7 +382,8 @@ export function createHelium(options = {}) {
|
|
|
373
382
|
const heElements = [element, ...element.querySelectorAll("*")].filter(e => {
|
|
374
383
|
if (HELIUM.processed.has(e)) return false;
|
|
375
384
|
for (let i = 0; i < e.attributes.length; i++) {
|
|
376
|
-
|
|
385
|
+
const n = e.attributes[i].name;
|
|
386
|
+
if (n[0] === '@' || n[0] === ':' || n.startsWith('data-he')) return true;
|
|
377
387
|
}
|
|
378
388
|
return false;
|
|
379
389
|
});
|
|
@@ -386,7 +396,7 @@ export function createHelium(options = {}) {
|
|
|
386
396
|
const importPromises = [];
|
|
387
397
|
|
|
388
398
|
heElements.forEach((el) => {
|
|
389
|
-
const importAttr = el.getAttribute("@import") || el.getAttribute(
|
|
399
|
+
const importAttr = el.getAttribute("@import") || el.getAttribute('data-he-import');
|
|
390
400
|
if (importAttr) {
|
|
391
401
|
importAttr.split(",").map((m) => m.trim()).forEach((moduleName) => {
|
|
392
402
|
// Build path: use as-is for URLs, otherwise resolve relative to document location
|
|
@@ -434,7 +444,7 @@ export function createHelium(options = {}) {
|
|
|
434
444
|
const {name, value} = attrs[i];
|
|
435
445
|
|
|
436
446
|
// Skip non-helium attributes early
|
|
437
|
-
if (
|
|
447
|
+
if (name[0] !== '@' && name[0] !== ':' && !name.startsWith('data-he')) continue;
|
|
438
448
|
|
|
439
449
|
// Initialize state first if needed (skip reserved words, only if not already set)
|
|
440
450
|
if (match(name, "text", "html", "bind") && isValidIdentifier(value) && !RESERVED.has(value) && state[value] == null) {
|
|
@@ -442,11 +452,11 @@ export function createHelium(options = {}) {
|
|
|
442
452
|
}
|
|
443
453
|
|
|
444
454
|
// Process the attribute
|
|
445
|
-
if (name === "@data" || name ===
|
|
446
|
-
Object.assign(state,
|
|
455
|
+
if (name === "@data" || name === 'data-he') {
|
|
456
|
+
Object.assign(state, evalExpr(value));
|
|
447
457
|
}
|
|
448
|
-
else if (name.startsWith(":") || name.startsWith(
|
|
449
|
-
const propName = name.startsWith(":") ? name.slice(1) : name.slice(
|
|
458
|
+
else if (name.startsWith(":") || name.startsWith('data-he-attr:')) {
|
|
459
|
+
const propName = name.startsWith(":") ? name.slice(1) : name.slice(13);
|
|
450
460
|
deferredBindings.push({el, prop: propName, compiled: compile(value, true)});
|
|
451
461
|
}
|
|
452
462
|
else if (match(name, "ref")) {
|
|
@@ -482,8 +492,8 @@ export function createHelium(options = {}) {
|
|
|
482
492
|
else if (match(name, "init")) {
|
|
483
493
|
inits.push({ compiled: compile(value, true), el });
|
|
484
494
|
}
|
|
485
|
-
else if (name.startsWith("@") || name.startsWith(
|
|
486
|
-
const fullName = name.startsWith("@") ? name.slice(1) : name.slice(
|
|
495
|
+
else if (name.startsWith("@") || name.startsWith('data-he')) {
|
|
496
|
+
const fullName = name.startsWith("@") ? name.slice(1) : name.slice(8);
|
|
487
497
|
const [eventName, ...mods] = fullName.split(".");
|
|
488
498
|
const isHttpMethod = ["get", "post", "put", "patch", "delete"].includes(eventName);
|
|
489
499
|
const event = isHttpMethod ? getEvent(el) : eventName;
|
|
@@ -503,12 +513,12 @@ export function createHelium(options = {}) {
|
|
|
503
513
|
}
|
|
504
514
|
if (!mods.includes("outside") || !el.contains(e.target)) {
|
|
505
515
|
if (isHttpMethod) {
|
|
506
|
-
const getAttr =
|
|
516
|
+
const getAttr = a => el.getAttribute('data-he-' + a) || el.getAttribute('@' + a);
|
|
507
517
|
const pairs = (getAttr('target') || "").split(",").map(p => p.split(":").map(s => s.trim()));
|
|
508
518
|
const target = pairs.map(([target]) => target);
|
|
509
519
|
const action = pairs.map(([, action]) => action);
|
|
510
520
|
const options = {
|
|
511
|
-
...(getAttr("options") &&
|
|
521
|
+
...(getAttr("options") && evalExpr(getAttr("options") || "{}")),
|
|
512
522
|
...(target && { target }),
|
|
513
523
|
...(action && { action }),
|
|
514
524
|
...(getAttr("template") && { template: execExpr(getAttr("template")),}),
|