ladrillosjs 2.0.0-rc.7 → 2.0.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.
Files changed (43) hide show
  1. package/README.md +171 -16
  2. package/dist/core/cache/expressionCache.d.ts +5 -70
  3. package/dist/core/component/loader.d.ts +1 -1
  4. package/dist/core/configure.d.ts +24 -0
  5. package/dist/core/diff/listDiff.d.ts +16 -0
  6. package/dist/core/html/controlTagEscape.d.ts +47 -0
  7. package/dist/core/js/moduleExecutor.d.ts +19 -0
  8. package/dist/core/js/scriptParser.d.ts +35 -1
  9. package/dist/core.d.ts +2 -2
  10. package/dist/core.dev.js +12 -0
  11. package/dist/core.dev.js.map +1 -0
  12. package/dist/core.js +1 -1
  13. package/dist/core.js.map +1 -1
  14. package/dist/events.dev.js +2 -0
  15. package/dist/events.js +1 -1
  16. package/dist/index.d.ts +2 -2
  17. package/dist/index.dev.js +22 -0
  18. package/dist/index.dev.js.map +1 -0
  19. package/dist/index.js +1 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/lazy.dev.js +2 -0
  22. package/dist/lazy.js +1 -1
  23. package/dist/shared-CMbR-Hhy.js +3 -0
  24. package/dist/shared-CMbR-Hhy.js.map +1 -0
  25. package/dist/shared-DqWGO1vN.js +2 -0
  26. package/dist/{shared-D-P0qKQY.js.map → shared-DqWGO1vN.js.map} +1 -1
  27. package/dist/shared-R1SSiaIW.dev.js +6785 -0
  28. package/dist/shared-R1SSiaIW.dev.js.map +1 -0
  29. package/dist/shared-Sk_U4mcq.dev.js +161 -0
  30. package/dist/shared-Sk_U4mcq.dev.js.map +1 -0
  31. package/dist/types/index.d.ts +6 -0
  32. package/dist/utils/devWarnings.d.ts +24 -3
  33. package/dist/utils/directives.d.ts +10 -0
  34. package/dist/utils/jsevents.d.ts +5 -0
  35. package/dist/utils/sandbox.d.ts +11 -0
  36. package/dist/utils/stateTransform.d.ts +58 -0
  37. package/package.json +22 -8
  38. package/dist/core/reactivity/dependencyTracker.d.ts +0 -120
  39. package/dist/shared-18P4lj9w.js +0 -2
  40. package/dist/shared-18P4lj9w.js.map +0 -1
  41. package/dist/shared-D-P0qKQY.js +0 -2
  42. package/dist/shared-mjNRZBte.js +0 -3
  43. package/dist/shared-mjNRZBte.js.map +0 -1
package/README.md CHANGED
@@ -26,14 +26,16 @@
26
26
  - [Quick Start](#-quick-start)
27
27
  - [Installation](#-installation)
28
28
  - [Core Concepts](#-core-concepts)
29
- - [Directives](#-directives)
29
+ - [Built-in Elements & Directives](#-built-in-elements--directives)
30
30
  - [Event Bus](#-event-bus)
31
31
  - [Element References](#-element-references)
32
32
  - [Lazy Loading](#-lazy-loading)
33
33
  - [API Reference](#-api-reference)
34
34
  - [Using with Vite](#-using-with-vite)
35
- - [Browser Support](#-browser-support)
35
+ - [Benchmarks](#-benchmarks)
36
36
  - [Examples](#-examples)
37
+ - [Documentation](#-documentation)
38
+ - [Security & Trust Model](#-security--trust-model)
37
39
  - [Contributing](#-contributing)
38
40
  - [License](#-license)
39
41
 
@@ -41,16 +43,7 @@
41
43
 
42
44
  ## 🚀 Quick Start
43
45
 
44
- ### 1. Add the Script
45
-
46
- ```html
47
- <script type="module">
48
- import ladrillosjs from "https://cdn.jsdelivr.net/npm/ladrillosjs@2/dist/index.js";
49
- window.ladrillosjs = ladrillosjs;
50
- </script>
51
- ```
52
-
53
- ### 2. Create a Component
46
+ ### 1. Create a Component
54
47
 
55
48
  Save this as `counter.html`:
56
49
 
@@ -86,7 +79,10 @@ Save this as `counter.html`:
86
79
  </style>
87
80
  ```
88
81
 
89
- ### 3. Use It
82
+ ### 2. Register and Use It
83
+
84
+ In your `index.html`, import LadrillosJS from the CDN, register the
85
+ component, and drop the tag anywhere on the page:
90
86
 
91
87
  ```html
92
88
  <!DOCTYPE html>
@@ -103,8 +99,21 @@ Save this as `counter.html`:
103
99
  </html>
104
100
  ```
105
101
 
102
+ ### 3. Serve It
103
+
104
+ Components are fetched over HTTP, so serve the folder with any static server
105
+ (opening the file directly via `file://` won't work — browsers block `fetch`
106
+ from local files):
107
+
108
+ ```bash
109
+ npx serve # or: python -m http.server 8080
110
+ ```
111
+
106
112
  That's it! Your reactive component is ready. 🎉
107
113
 
114
+ > 📚 **Want more?** The [full documentation](docs/README.md) covers every
115
+ > feature step by step, from your first component to building a design system.
116
+
108
117
  ---
109
118
 
110
119
  ## 📦 Installation
@@ -205,6 +214,20 @@ Attach events directly in HTML with inline expressions or function calls:
205
214
  <form onsubmit="handleSubmit(event)">...</form>
206
215
  ```
207
216
 
217
+ Or use the `$on:` directive with **modifiers** — dot-separated flags for
218
+ common patterns like `preventDefault`, key filtering, and modifier keys:
219
+
220
+ ```html
221
+ <form $on:submit.prevent="handleSubmit()">...</form>
222
+ <input $on:keyup.enter="search()" />
223
+ <textarea $on:keydown.ctrl.s.prevent="save()"></textarea>
224
+ <div $on:click.self="closeModal()">...</div>
225
+ <button $on:click.once="trackFirstClick()">Buy</button>
226
+ ```
227
+
228
+ See the [Event Modifiers reference](docs/20-event-modifiers.md) for every
229
+ modifier and combination.
230
+
208
231
  ### Two-Way Binding
209
232
 
210
233
  Use `$bind` to sync form inputs with state:
@@ -325,6 +348,8 @@ Use `<lazy>` to defer rendering until a trigger fires (viewport, idle, delay, in
325
348
  | `<lazy>` | Defer rendering | `<lazy idle><analytics-pixel /></lazy>` |
326
349
  | `$bind` | Two-way binding | `<input $bind="email" />` |
327
350
  | `$ref` | Element reference | `<input $ref="inputEl" />` |
351
+ | `$on:` + modifiers | Events with modifiers | `<form $on:submit.prevent="save()">` |
352
+ | `$no:bind` | Escape `{}` binding | `<code $no:bind>{literal}</code>` |
328
353
 
329
354
  ---
330
355
 
@@ -530,10 +555,45 @@ import { configure } from "ladrillosjs";
530
555
 
531
556
  configure({
532
557
  cacheSize: 50, // Component LRU cache size (default: 25)
533
- onError: (err) => telemetry.capture(err), // Custom error handler
558
+ onError: (err, context) => telemetry.capture(err, { context }),
559
+ delegateLoopEvents: true, // Opt-in loop event delegation (default: false)
534
560
  });
535
561
  ```
536
562
 
563
+ Development builds include actionable `LJSxxx` diagnostics with component and
564
+ file context, a suggested fix, and a link to the
565
+ [error reference](docs/21-error-handling.md). Vite and other bundlers that
566
+ honor the `development` package condition select this build automatically. To
567
+ select it explicitly, import from `ladrillosjs/dev` (or
568
+ `ladrillosjs/core/dev`). Production builds retain coded errors but remove
569
+ development-only warnings.
570
+
571
+ The `onError` callback is also suitable for telemetry. Coded framework errors
572
+ are instances of `LadrillosError` and expose `code`, `docsUrl`, `hint`,
573
+ `componentContext`, and the original `cause`.
574
+
575
+ #### `delegateLoopEvents`
576
+
577
+ By default every handler inside a `<for>` row gets its own listener. With
578
+ `delegateLoopEvents: true`, eligible handlers share **one listener per event
579
+ type** on the loop's container — thousands of rows, a handful of listeners.
580
+ Templates and handler code don't change at all, and render performance is
581
+ the same either way; enable it when listener count itself matters (very
582
+ large lists, memory-constrained devices, many handlers per row).
583
+
584
+ Fine print:
585
+
586
+ - Non-bubbling events (`focus`, `blur`, `mouseenter`, …) and handlers using
587
+ the `.self`, `.capture`, `.once`, or `.passive` modifiers automatically
588
+ keep per-element listeners. `.stop`, `.prevent`, and key/system modifiers
589
+ work identically in both modes.
590
+ - `event.currentTarget` inside a delegated handler is the list container,
591
+ not the row element (`event.target` is unaffected).
592
+ - If you manually attach your own listener on an element inside a row and
593
+ call `stopPropagation()`, delegated handlers above it won't fire.
594
+ - Call `configure()` before your components render; already-rendered loops
595
+ keep the mode they were created with.
596
+
537
597
  ### Event Bus
538
598
 
539
599
  | Function | Description |
@@ -575,6 +635,54 @@ See the [samples/](samples/) directory for complete examples:
575
635
 
576
636
  ---
577
637
 
638
+ ## 📊 Benchmarks
639
+
640
+ A [js-framework-benchmark](https://github.com/krausest/js-framework-benchmark)–style
641
+ suite lives in [benchmarks/](benchmarks/). It renders the same keyed
642
+ 1,000-row list in LadrillosJS, React 18 (keyed, memoized rows — the
643
+ idiomatic fast path), and hand-optimized vanilla JS, and measures each
644
+ operation from invocation until the live DOM reflects the change.
645
+ Medians of 10 runs, headless Chromium on an Apple M5 Pro,
646
+ `ladrillosjs@2.0.0-beta.11`:
647
+
648
+ | Operation | LadrillosJS | React 18.3 (keyed, memoized rows) | Vanilla JS (hand-optimized) |
649
+ |---|---:|---:|---:|
650
+ | create 1,000 rows | **3.7 ms** | 4.1 ms | 1.8 ms |
651
+ | replace all 1,000 rows | **4.1 ms** | 7 ms | 2 ms |
652
+ | partial update (every 10th of 1,000) | 1.1 ms | 1.2 ms | 0.2 ms |
653
+ | select row | 0.7 ms | 0.4 ms | 0 ms |
654
+ | swap 2 rows | **1.1 ms** | 3.3 ms | 0.1 ms |
655
+ | remove row | 1 ms | 1 ms | 0 ms |
656
+ | append 1,000 to 1,000 rows | **3.8 ms** | 4.3 ms | 1.3 ms |
657
+ | clear 1,000 rows | **1.3 ms** | 5.1 ms | 0.8 ms |
658
+ | create 10,000 rows | **28.9 ms** | 218.3 ms | 13 ms |
659
+ | **JS payload (min+gzip)** | **25.9 KB** | **47 KB** | ~1 KB |
660
+ | JS heap after 1,000 rows | 2.4 MB | 6.2 MB | 1.3 MB |
661
+
662
+ **How to read this honestly:**
663
+
664
+ - Every operation lands well within a single 60 fps frame (16.7 ms):
665
+ updates on a 1,000-row list take about a millisecond, and bulk creation
666
+ of 1,000 rows is under 5 ms.
667
+ - LadrillosJS beats React on bulk creation (and by 7× on 10,000 rows),
668
+ full replaces, row swaps, and clearing, while shipping **half the JS**
669
+ and using **~60% less memory** for the same UI.
670
+ - React 18 remains faster on single-row selection, and partial updates
671
+ are a tie. We publish these numbers to track and improve them — not to
672
+ claim LadrillosJS wins everything.
673
+ - Vanilla JS is the baseline.
674
+
675
+ Reproduce it yourself (no benchmark numbers should be trusted otherwise):
676
+
677
+ ```bash
678
+ cd benchmarks && npm install && npm run bench
679
+ ```
680
+
681
+ Methodology, fairness notes, and raw samples: [benchmarks/README.md](benchmarks/README.md)
682
+ and [benchmarks/results.json](benchmarks/results.json).
683
+
684
+ ---
685
+
578
686
  ## 📚 Examples
579
687
 
580
688
  ### Todo List
@@ -591,9 +699,9 @@ A complete CRUD example combining all directives:
591
699
  <ul>
592
700
  <for each="todo in todos" key="todo.id">
593
701
  <li>
594
- <input type="checkbox" onclick="toggleTodo({todo.id})" />
702
+ <input type="checkbox" onclick="toggleTodo(todo.id)" />
595
703
  <span class="{todo.completed ? 'done' : ''}">{todo.text}</span>
596
- <button onclick="removeTodo({todo.id})">🗑️</button>
704
+ <button onclick="removeTodo(todo.id)">🗑️</button>
597
705
  </li>
598
706
  </for>
599
707
  </ul>
@@ -633,6 +741,52 @@ A complete CRUD example combining all directives:
633
741
 
634
742
  ---
635
743
 
744
+ ## 📚 Documentation
745
+
746
+ Full guides live in the [docs/](docs/README.md) folder:
747
+
748
+ | Start here | Reference | Advanced |
749
+ | ---------- | --------- | -------- |
750
+ | [Quick Start](docs/01-quick-start.md) | [Built-in Elements & Directives](docs/06-directives.md) | [Event Bus](docs/12-event-bus.md) |
751
+ | [Installation](docs/02-installation.md) | [Event Modifiers (`$on:`)](docs/20-event-modifiers.md) | [Lazy Loading](docs/13-lazy-loading.md) |
752
+ | [Components](docs/03-components.md) | [Conditionals & Loops](docs/07-conditionals.md) | [Shadow DOM](docs/14-shadow-dom.md) |
753
+ | [Reactivity](docs/04-reactivity.md) | [Two-Way Binding](docs/09-two-way-binding.md) | [Design System Guide](docs/19-design-system.md) |
754
+
755
+ Upgrading from v1? See the [Migration Guide](docs/17-migration-v1-to-v2.md).
756
+ Using TypeScript? See the [TypeScript guide](docs/18-typescript.md).
757
+
758
+ ---
759
+
760
+ ## 🔒 Security & Trust Model
761
+
762
+ **Component HTML is trusted code. Treat a `.html` component the same way you'd
763
+ treat a `.js` file you `import`.**
764
+
765
+ LadrillosJS runs the `<script>` in each component, evaluates your `{expression}`
766
+ bindings, and compiles your inline event handlers as JavaScript with full access
767
+ to the page (`window`, `document`, `fetch`, `localStorage`, …). This is by design
768
+ — it's what lets components feel like plain HTML + JS with no build step, exactly
769
+ like the template in a Vue single-file component or a Svelte `.svelte` file. It
770
+ also means the framework is **not** a sandbox: you should only register and run
771
+ component files that you wrote or otherwise trust.
772
+
773
+ Practical guidance:
774
+
775
+ - ✅ **Do** author your own components and load them from your own origin.
776
+ - ❌ **Don't** fetch and register component HTML from untrusted third parties, or
777
+ build component files by concatenating user input into a template — that's
778
+ equivalent to running arbitrary code from that source.
779
+ - ✅ **Dynamic *data* is fine.** Rendering untrusted **data** — API responses,
780
+ user comments, list items — through bindings and `<for>` loops is safe: values
781
+ are set via `textContent`/`setAttribute` and passed to event handlers as
782
+ arguments, never executed as code. (Interpolating into `innerHTML` yourself, or
783
+ into a `<script>`/handler you assemble by hand, is not — same rule as above.)
784
+
785
+ In short: untrusted **data** through LadrillosJS's bindings is safe; untrusted
786
+ **templates/scripts** are not, because those *are* your application code.
787
+
788
+ ---
789
+
636
790
  ## 🤝 Contributing
637
791
 
638
792
  Contributions are welcome! Here's how you can help:
@@ -651,6 +805,7 @@ cd LadrillosJS
651
805
  npm install
652
806
  npm run dev # Watch mode for development
653
807
  npm run build # Build for production
808
+ npm test # Unit tests (Vitest)
654
809
  ```
655
810
 
656
811
  ---
@@ -1,33 +1,11 @@
1
1
  /**
2
- * Expression Cache
2
+ * Variable Regex Cache
3
3
  *
4
- * Caches compiled expression evaluators to avoid repeated Function() construction.
5
- *
6
- * Creating functions via new Function() is expensive:
7
- * - Parsing the function body
8
- * - JIT compilation
9
- * - Memory allocation
10
- *
11
- * By caching the compiled functions, we only pay this cost once per unique expression.
12
- */
13
- type ExpressionEvaluator = (context: Record<string, unknown>) => unknown;
14
- /**
15
- * Gets or creates a cached expression evaluator.
16
- *
17
- * @param expression - The expression to compile (e.g., "count + 1")
18
- * @param contextKeys - Variable names available in scope
19
- * @returns A function that evaluates the expression against a context
20
- *
21
- * @example
22
- * const evaluate = getCachedEvaluator("count * 2", ["count"]);
23
- * const result = evaluate({ count: 5 }); // 10
4
+ * Building a `RegExp` is comparatively expensive, and the reactivity layer
5
+ * checks the same variable names against many binding expressions when working
6
+ * out which bindings depend on which state keys. Caching the compiled regex per
7
+ * variable name avoids recreating it on every check.
24
8
  */
25
- export declare function getCachedEvaluator(expression: string, contextKeys: string[]): ExpressionEvaluator;
26
- /**
27
- * Clears all expression caches.
28
- * Useful for testing or when context shape changes dramatically.
29
- */
30
- export declare function clearExpressionCache(): void;
31
9
  /**
32
10
  * Gets or creates a cached regex for variable boundary matching.
33
11
  *
@@ -35,46 +13,3 @@ export declare function clearExpressionCache(): void;
35
13
  * @returns A regex that matches the variable as a whole word
36
14
  */
37
15
  export declare function getCachedVariableRegex(variableName: string): RegExp;
38
- /**
39
- * Cached check if an expression depends on a variable.
40
- *
41
- * @param expression - The expression to check
42
- * @param variableName - The variable to look for
43
- * @returns true if the expression references the variable
44
- */
45
- export declare function expressionDependsOnCached(expression: string, variableName: string): boolean;
46
- /**
47
- * Gets or creates a cached path array from a dot-notation string.
48
- *
49
- * @param pathString - The dot-notation path (e.g., "person.address.city")
50
- * @returns Array of path segments
51
- *
52
- * @example
53
- * getCachedPath("person.name") // ["person", "name"]
54
- */
55
- export declare function getCachedPath(pathString: string): readonly string[];
56
- /**
57
- * Resolves a value from an object using a cached path.
58
- *
59
- * @param obj - The object to read from
60
- * @param pathString - Dot-notation path
61
- * @returns The value at the path, or undefined
62
- *
63
- * @example
64
- * getByPath({ person: { name: "John" } }, "person.name") // "John"
65
- */
66
- export declare function getByPath(obj: Record<string, unknown>, pathString: string): unknown;
67
- /**
68
- * Sets a value on an object using a cached path.
69
- *
70
- * @param obj - The object to write to
71
- * @param pathString - Dot-notation path
72
- * @param value - The value to set
73
- *
74
- * @example
75
- * const obj = { person: { name: "John" } };
76
- * setByPath(obj, "person.name", "Jane");
77
- * // obj.person.name === "Jane"
78
- */
79
- export declare function setByPath(obj: Record<string, unknown>, pathString: string, value: unknown): void;
80
- export {};
@@ -7,4 +7,4 @@ export interface FetchComponentResult {
7
7
  /** The actual resolved path (may differ from input for folder-as-component) */
8
8
  resolvedPath: string;
9
9
  }
10
- export declare function fetchComponentSource(path: string): Promise<FetchComponentResult | undefined>;
10
+ export declare function fetchComponentSource(path: string): Promise<FetchComponentResult>;
@@ -18,7 +18,31 @@ export interface LadrillosConfig {
18
18
  * });
19
19
  */
20
20
  onError?: LadrillosErrorHandler | null;
21
+ /**
22
+ * Opt-in event delegation for `<for>` loop rows. When enabled, eligible
23
+ * inline handlers (onclick, $on:… on bubbling events) inside loop rows
24
+ * share ONE listener per event type on the loop's container instead of
25
+ * one listener per element — faster bulk row creation and less memory on
26
+ * large lists. Handler code and template syntax are unchanged.
27
+ *
28
+ * Two observable differences versus direct listeners:
29
+ * 1. `event.currentTarget` inside a loop handler is the list container,
30
+ * not the row element (`event.target` is unaffected).
31
+ * 2. A manual `stopPropagation()` call from a listener you attach
32
+ * yourself between the row and the container stops delegated
33
+ * handlers from firing.
34
+ *
35
+ * Non-bubbling events (focus, blur, mouseenter, …) and handlers using
36
+ * the `.self`, `.capture`, `.once`, or `.passive` modifiers automatically
37
+ * keep per-element listeners.
38
+ *
39
+ * Set this before components render; templates already rendered keep the
40
+ * mode they were created with. Defaults to false.
41
+ */
42
+ delegateLoopEvents?: boolean;
21
43
  }
44
+ /** Whether opt-in loop event delegation is active (see LadrillosConfig). */
45
+ export declare function isLoopDelegationEnabled(): boolean;
22
46
  /**
23
47
  * Configure framework-level options.
24
48
  *
@@ -57,6 +57,22 @@ export declare function diffKeyed<T>(oldItems: T[], newItems: T[], getKey: (item
57
57
  * @returns Array of operations
58
58
  */
59
59
  export declare function diffUnkeyed<T>(oldLength: number, newLength: number, newItems: T[]): DiffOperation[];
60
+ /**
61
+ * Computes the set of positions that form the longest increasing subsequence
62
+ * of `source`, ignoring entries whose value is < 0 (used to mark brand-new
63
+ * items that must always be (re)inserted).
64
+ *
65
+ * The loop renderer uses this to decide which reused elements are already in
66
+ * correct relative DOM order and can therefore stay put — only the remaining
67
+ * elements need to be moved. This turns an in-order content update into zero
68
+ * DOM moves and minimizes moves for partial reorders.
69
+ *
70
+ * Runs in O(n log n) using patience sorting with predecessor links.
71
+ *
72
+ * @param source - For each new position, the element's previous index, or -1 if new
73
+ * @returns Set of new-position indices that should NOT be moved
74
+ */
75
+ export declare function getStableIndices(source: number[]): Set<number>;
60
76
  /**
61
77
  * Creates a key getter function from a key expression.
62
78
  *
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Table-safe parsing for built-in control elements.
3
+ *
4
+ * The HTML parser's table insertion modes foster-parent anything that is
5
+ * not valid table content out of `<table>`/`<thead>`/`<tbody>`/`<tr>`.
6
+ * Built-in control elements (`<for>`, `<if>`, `<else-if>`, `<else>`,
7
+ * `<show>`) are unknown to the parser, so markup like
8
+ *
9
+ * <table><tbody>
10
+ * <for each="row in rows" key="row.id">
11
+ * <tr><td>{row.id}</td></tr>
12
+ * </for>
13
+ * </tbody></table>
14
+ *
15
+ * gets mangled BEFORE the framework ever sees the tree: `<for>` is
16
+ * hoisted out of the table and the raw `<tr>` template is left behind,
17
+ * un-bound. This applies to every parse entry point — `DOMParser` and
18
+ * `template.innerHTML` alike.
19
+ *
20
+ * `<template>` elements, however, ARE valid anywhere inside a table, and
21
+ * their content model is parsed leniently (a bare `<tr>` or `<td>` inside
22
+ * template content survives). So before parsing we rewrite control tags to
23
+ * `<template data-l-ctrl="…">` placeholders at the string level, and after
24
+ * parsing we rebuild the real control elements with DOM APIs — which the
25
+ * parser's content rules cannot touch.
26
+ */
27
+ /**
28
+ * Rewrites control-element tags to `<template data-l-ctrl="…">`
29
+ * placeholders so the HTML parser cannot foster-parent them out of table
30
+ * contexts. Must be paired with {@link restoreControlTags} on the parsed
31
+ * tree.
32
+ */
33
+ export declare function escapeControlTags(html: string): string;
34
+ /**
35
+ * Rebuilds real control elements from the placeholders produced by
36
+ * {@link escapeControlTags}. Runs until no placeholder remains: restoring
37
+ * an outer placeholder moves its (previously inert) template content into
38
+ * the live tree, exposing any nested placeholders to the next iteration.
39
+ * User-authored `<template>` elements are recursed into so control tags
40
+ * inside their inert content are restored as well.
41
+ */
42
+ export declare function restoreControlTags(root: ParentNode): void;
43
+ /**
44
+ * Returns true when the element is a restored control element
45
+ * (`<for>`, `<if>`, `<else-if>`, `<else>`, `<show>`).
46
+ */
47
+ export declare function isControlElement(el: Element): boolean;
@@ -20,6 +20,25 @@ export declare function rewriteImports(code: string, baseUrl: string): string;
20
20
  * @returns Promise that resolves when the module has executed
21
21
  */
22
22
  export declare function executeModuleScript(script: ScriptElement, componentUrl: string, componentId?: string): Promise<unknown>;
23
+ /**
24
+ * Transforms import statements to wrap imported values in reactive proxies.
25
+ * This enables imported arrays to trigger UI updates when mutated.
26
+ *
27
+ * Transforms:
28
+ * import { foo, bar } from "./module.js";
29
+ *
30
+ * Into:
31
+ * import { foo as __raw_foo, bar as __raw_bar } from "./module.js";
32
+ * const foo = __wrapReactiveArray(__raw_foo, __ladrillos_componentId, "foo");
33
+ * const bar = __wrapReactiveArray(__raw_bar, __ladrillos_componentId, "bar");
34
+ *
35
+ * @param code - The module code with imports
36
+ * @returns Transformed code with reactive import wrapping
37
+ *
38
+ * Exported for tests (the blob-URL execution path can't run under Node).
39
+ */
40
+ export declare function transformImportsForReactivity(code: string): string;
41
+ export declare function generateHelperInjectionCode(componentId?: string, componentUrl?: string, exclude?: ReadonlySet<string>): string;
23
42
  /**
24
43
  * Executes an external module script.
25
44
  * For external scripts, we fetch the content, auto-export all declarations,
@@ -31,6 +31,8 @@ export declare function applyBindingsDeferred(host: HTMLElement | ShadowRoot, bi
31
31
  * Extracts function definitions from script content.
32
32
  * These will be re-created in the event handler context with current state values.
33
33
  *
34
+ * Memoized: the output is a pure function of (content, skipFunctions).
35
+ *
34
36
  * @param content - The script content to extract functions from
35
37
  * @param skipFunctions - Function names to skip (already in state as reactive functions)
36
38
  */
@@ -44,9 +46,41 @@ export declare function extractFunctionDefinitions(content: string, skipFunction
44
46
  * Exported so webcomponent.ts can use it for observedAttributes.
45
47
  */
46
48
  export declare function extractVariableNames(content: string): string[];
49
+ /**
50
+ * A pass-scoped evaluator bound to one context object, produced by
51
+ * `DirectiveEvaluator.forContext`. Callable as `(expr) => unknown`; the
52
+ * optional members let hot loops skip per-eval overhead (see
53
+ * `createContextEvaluator` for the mode semantics).
54
+ */
55
+ export interface BoundEvaluator {
56
+ (expr: string): unknown;
57
+ /**
58
+ * Refill argument slots from the context: the volatile slots in static
59
+ * mode, all slots in legacy mode. Static-mode callers MUST call this
60
+ * after mutating a volatile context value.
61
+ */
62
+ refresh?: () => void;
63
+ /** Compiled Function for `expr` under this key set, or null on a syntax error. */
64
+ compile?: (expr: string) => Function | null;
65
+ /** Invoke a Function from `compile` against the current argument slots. */
66
+ invoke?: (fn: Function, expr: string) => unknown;
67
+ /** Key-set signature; compiled Functions are only valid for a matching sig. */
68
+ sig?: string;
69
+ }
70
+ /**
71
+ * Directive-facing expression evaluator. Callable as
72
+ * `(expr, context) => unknown`; `forContext` additionally builds a
73
+ * pass-scoped fast evaluator bound to one context object (see
74
+ * `createContextEvaluator`) whose key set must not change for the
75
+ * lifetime of the returned function.
76
+ */
77
+ export interface DirectiveEvaluator {
78
+ (expr: string, context: Record<string, unknown>): unknown;
79
+ forContext(context: Record<string, unknown>, volatileKeys?: readonly string[]): BoundEvaluator;
80
+ }
47
81
  /**
48
82
  * Creates and returns an expression evaluator function for use by directives.
49
83
  * This allows directives to evaluate expressions like "item.name" or "count > 5"
50
84
  * in the context of the component's state.
51
85
  */
52
- export declare function createExpressionEvaluator(): (expr: string, context: Record<string, unknown>) => unknown;
86
+ export declare function createExpressionEvaluator(): DirectiveEvaluator;
package/dist/core.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { ComponentConfig, RegisterComponentsResult } from './core/ladrillos';
2
2
  import { registerComponent, registerComponents, $use } from './core/helpers/frameworkHelpers';
3
3
  import { configure, LadrillosConfig } from './core/configure';
4
- import { ErrorCode, LadrillosErrorHandler } from './utils/devWarnings';
4
+ import { ErrorCode, LadrillosError, LadrillosErrorHandler } from './utils/devWarnings';
5
5
  export type { ComponentConfig, RegisterComponentsResult, LadrillosConfig, LadrillosErrorHandler, };
6
6
  export type { LadrillosComponent } from './types';
7
- export { ErrorCode };
7
+ export { ErrorCode, LadrillosError };
8
8
  export { registerComponent, registerComponents, $use, configure };
9
9
  declare const _default: {
10
10
  registerComponent: (name: string, path: string, useShadowDOM?: boolean, lazy?: boolean | import('.').LazyStrategy) => Promise<void>;
@@ -0,0 +1,12 @@
1
+ import { d as ErrorCode, f as LadrillosError, h as registerComponents, i as configure, m as registerComponent, p as $use } from "./shared-R1SSiaIW.dev.js";
2
+ //#region src/core.ts
3
+ var core_default = {
4
+ registerComponent,
5
+ registerComponents,
6
+ $use,
7
+ configure
8
+ };
9
+ //#endregion
10
+ export { $use, ErrorCode, LadrillosError, configure, core_default as default, registerComponent, registerComponents };
11
+
12
+ //# sourceMappingURL=core.dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.dev.js","names":[],"sources":["../src/core.ts"],"sourcesContent":["/**\r\n * LadrillosJS Core - Minimal bundle\r\n *\r\n * This is the core module containing only essential functionality.\r\n * Use this for the smallest possible bundle size.\r\n *\r\n * @example\r\n * ```ts\r\n * import { registerComponent } from 'ladrillosjs/core';\r\n * ```\r\n *\r\n * For additional features, import from:\r\n * - 'ladrillosjs/lazy' - Lazy loading strategies\r\n * - 'ladrillosjs/events' - Event bus for cross-component communication\r\n */\r\n\r\nimport { ComponentConfig, RegisterComponentsResult } from \"./core/ladrillos\";\r\nimport\r\n {\r\n registerComponent,\r\n registerComponents,\r\n $use,\r\n } from \"./core/helpers/frameworkHelpers\";\r\nimport { configure, LadrillosConfig } from \"./core/configure\";\r\nimport\r\n {\r\n ErrorCode,\r\n LadrillosError,\r\n type LadrillosErrorHandler,\r\n } from \"./utils/devWarnings\";\r\n\r\n// Export public types\r\nexport type {\r\n ComponentConfig,\r\n RegisterComponentsResult,\r\n LadrillosConfig,\r\n LadrillosErrorHandler,\r\n};\r\nexport type { LadrillosComponent } from \"./types\";\r\n\r\n// Export error code enum\r\nexport { ErrorCode, LadrillosError };\r\n\r\n// Public exports\r\nexport { registerComponent, registerComponents, $use, configure };\r\n\r\n// Default export for CDN usage\r\nexport default {\r\n registerComponent,\r\n registerComponents,\r\n $use,\r\n configure,\r\n};\r\n"],"mappings":";;AA+CA,IAAA,eAAe;CACb;CACA;CACA;CACA;AACF"}
package/dist/core.js CHANGED
@@ -1,2 +1,2 @@
1
- import{d as s,h as r,m as e,p as a}from"./shared-mjNRZBte.js";import{t as o}from"./shared-18P4lj9w.js";var t={registerComponent:e,registerComponents:r,$use:a,configure:o};export{a as $use,s as ErrorCode,o as configure,t as default,e as registerComponent,r as registerComponents};
1
+ import{d as s,f as e,h as a,i as r,m as o,p as t}from"./shared-CMbR-Hhy.js";var i={registerComponent:o,registerComponents:a,$use:t,configure:r};export{t as $use,s as ErrorCode,e as LadrillosError,r as configure,i as default,o as registerComponent,a as registerComponents};
2
2
  //# sourceMappingURL=core.js.map
package/dist/core.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","names":[],"sources":["../src/core.ts"],"sourcesContent":["/**\r\n * LadrillosJS Core - Minimal bundle\r\n *\r\n * This is the core module containing only essential functionality.\r\n * Use this for the smallest possible bundle size.\r\n *\r\n * @example\r\n * ```ts\r\n * import { registerComponent } from 'ladrillosjs/core';\r\n * ```\r\n *\r\n * For additional features, import from:\r\n * - 'ladrillosjs/lazy' - Lazy loading strategies\r\n * - 'ladrillosjs/events' - Event bus for cross-component communication\r\n */\r\n\r\nimport { ComponentConfig, RegisterComponentsResult } from \"./core/ladrillos\";\r\nimport {\r\n registerComponent,\r\n registerComponents,\r\n $use,\r\n} from \"./core/helpers/frameworkHelpers\";\r\nimport { configure, LadrillosConfig } from \"./core/configure\";\r\nimport { ErrorCode, type LadrillosErrorHandler } from \"./utils/devWarnings\";\r\n\r\n// Export public types\r\nexport type {\r\n ComponentConfig,\r\n RegisterComponentsResult,\r\n LadrillosConfig,\r\n LadrillosErrorHandler,\r\n};\r\nexport type { LadrillosComponent } from \"./types\";\r\n\r\n// Export error code enum\r\nexport { ErrorCode };\r\n\r\n// Public exports\r\nexport { registerComponent, registerComponents, $use, configure };\r\n\r\n// Default export for CDN usage\r\nexport default {\r\n registerComponent,\r\n registerComponents,\r\n $use,\r\n configure,\r\n};\r\n"],"mappings":"uGAyCA,IAAA,EAAe,CACb,oBACA,qBACA,OACA"}
1
+ {"version":3,"file":"core.js","names":[],"sources":["../src/core.ts"],"sourcesContent":["/**\r\n * LadrillosJS Core - Minimal bundle\r\n *\r\n * This is the core module containing only essential functionality.\r\n * Use this for the smallest possible bundle size.\r\n *\r\n * @example\r\n * ```ts\r\n * import { registerComponent } from 'ladrillosjs/core';\r\n * ```\r\n *\r\n * For additional features, import from:\r\n * - 'ladrillosjs/lazy' - Lazy loading strategies\r\n * - 'ladrillosjs/events' - Event bus for cross-component communication\r\n */\r\n\r\nimport { ComponentConfig, RegisterComponentsResult } from \"./core/ladrillos\";\r\nimport\r\n {\r\n registerComponent,\r\n registerComponents,\r\n $use,\r\n } from \"./core/helpers/frameworkHelpers\";\r\nimport { configure, LadrillosConfig } from \"./core/configure\";\r\nimport\r\n {\r\n ErrorCode,\r\n LadrillosError,\r\n type LadrillosErrorHandler,\r\n } from \"./utils/devWarnings\";\r\n\r\n// Export public types\r\nexport type {\r\n ComponentConfig,\r\n RegisterComponentsResult,\r\n LadrillosConfig,\r\n LadrillosErrorHandler,\r\n};\r\nexport type { LadrillosComponent } from \"./types\";\r\n\r\n// Export error code enum\r\nexport { ErrorCode, LadrillosError };\r\n\r\n// Public exports\r\nexport { registerComponent, registerComponents, $use, configure };\r\n\r\n// Default export for CDN usage\r\nexport default {\r\n registerComponent,\r\n registerComponents,\r\n $use,\r\n configure,\r\n};\r\n"],"mappings":"4EA+CA,IAAA,EAAe,CACb,oBACA,qBACA,OACA"}
@@ -0,0 +1,2 @@
1
+ import { n as $listen, t as $emit } from "./shared-Sk_U4mcq.dev.js";
2
+ export { $emit, $listen };
package/dist/events.js CHANGED
@@ -1 +1 @@
1
- import{n as r,t as s}from"./shared-D-P0qKQY.js";export{s as $emit,r as $listen};
1
+ import{n as r,t as s}from"./shared-DqWGO1vN.js";export{s as $emit,r as $listen};
package/dist/index.d.ts CHANGED
@@ -2,11 +2,11 @@ import { ComponentConfig, RegisterComponentsResult } from './core/ladrillos';
2
2
  import { registerComponent, registerComponents, $use } from './core/helpers/frameworkHelpers';
3
3
  import { $emit, $listen, EventCallback } from './core/events/eventBus';
4
4
  import { configure, LadrillosConfig } from './core/configure';
5
- import { ErrorCode, LadrillosErrorHandler } from './utils/devWarnings';
5
+ import { ErrorCode, LadrillosError, LadrillosErrorHandler } from './utils/devWarnings';
6
6
  import { LazyStrategy, lazyOnIdle, lazyOnVisible, lazyOnMedia, lazyOnInteraction, lazyOnDelay } from './core/lazy';
7
7
  export type { ComponentConfig, RegisterComponentsResult, EventCallback, LazyStrategy, LadrillosConfig, LadrillosErrorHandler, };
8
8
  export type { LadrillosComponent } from './types';
9
- export { ErrorCode };
9
+ export { ErrorCode, LadrillosError };
10
10
  export { lazyOnIdle, lazyOnVisible, lazyOnMedia, lazyOnInteraction, lazyOnDelay, };
11
11
  export declare const loadLazyComponent: (name: string) => Promise<import('./types').LadrillosComponent | undefined>;
12
12
  export { configure };
@@ -0,0 +1,22 @@
1
+ import { c as lazyOnInteraction, d as ErrorCode, f as LadrillosError, h as registerComponents, i as configure, l as lazyOnMedia, m as registerComponent, o as lazyOnDelay, p as $use, s as lazyOnIdle, t as ladrillos, u as lazyOnVisible } from "./shared-R1SSiaIW.dev.js";
2
+ import { n as $listen, t as $emit } from "./shared-Sk_U4mcq.dev.js";
3
+ //#region src/index.ts
4
+ var loadLazyComponent = (name) => ladrillos.loadLazyComponent(name);
5
+ var src_default = {
6
+ registerComponent,
7
+ registerComponents,
8
+ $use,
9
+ $emit,
10
+ $listen,
11
+ loadLazyComponent,
12
+ configure,
13
+ lazyOnIdle,
14
+ lazyOnVisible,
15
+ lazyOnMedia,
16
+ lazyOnInteraction,
17
+ lazyOnDelay
18
+ };
19
+ //#endregion
20
+ export { $emit, $listen, $use, ErrorCode, LadrillosError, configure, src_default as default, lazyOnDelay, lazyOnIdle, lazyOnInteraction, lazyOnMedia, lazyOnVisible, loadLazyComponent, registerComponent, registerComponents };
21
+
22
+ //# sourceMappingURL=index.dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.dev.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import\r\n {\r\n ladrillos,\r\n ComponentConfig,\r\n RegisterComponentsResult,\r\n } from \"./core/ladrillos\";\r\nimport\r\n {\r\n registerComponent,\r\n registerComponents,\r\n $use,\r\n } from \"./core/helpers/frameworkHelpers\";\r\nimport { $emit, $listen, EventCallback } from \"./core/events/eventBus\";\r\nimport { configure, LadrillosConfig } from \"./core/configure\";\r\nimport\r\n {\r\n ErrorCode,\r\n LadrillosError,\r\n type LadrillosErrorHandler,\r\n } from \"./utils/devWarnings\";\r\n\r\n// Import lazy loading strategies\r\nimport\r\n {\r\n LazyStrategy,\r\n lazyOnIdle,\r\n lazyOnVisible,\r\n lazyOnMedia,\r\n lazyOnInteraction,\r\n lazyOnDelay,\r\n } from \"./core/lazy\";\r\n\r\n// Export public types\r\nexport type {\r\n ComponentConfig,\r\n RegisterComponentsResult,\r\n EventCallback,\r\n LazyStrategy,\r\n LadrillosConfig,\r\n LadrillosErrorHandler,\r\n};\r\nexport type { LadrillosComponent } from \"./types\";\r\n\r\n// Export error code enum for consumers to branch on\r\nexport { ErrorCode, LadrillosError };\r\n\r\n// Export lazy loading strategies\r\nexport\r\n{\r\n lazyOnIdle,\r\n lazyOnVisible,\r\n lazyOnMedia,\r\n lazyOnInteraction,\r\n lazyOnDelay,\r\n};\r\n\r\n// Force load a lazy component\r\nexport const loadLazyComponent = (name: string) =>\r\n ladrillos.loadLazyComponent(name);\r\n\r\n// Framework configuration\r\nexport { configure };\r\n\r\n// Component registration + $use alias + event bus helpers\r\nexport { registerComponent, registerComponents, $use, $emit, $listen };\r\n\r\n// Default export with all methods for CDN usage\r\n// This allows: ladrillosjs.registerComponent() in CDN mode\r\nexport default {\r\n registerComponent,\r\n registerComponents,\r\n $use,\r\n $emit,\r\n $listen,\r\n loadLazyComponent,\r\n configure,\r\n // Lazy loading strategies\r\n lazyOnIdle,\r\n lazyOnVisible,\r\n lazyOnMedia,\r\n lazyOnInteraction,\r\n lazyOnDelay,\r\n};\r\n"],"mappings":";;;AAyDA,IAAa,qBAAqB,SAChC,UAAU,kBAAkB,IAAI;AAUlC,IAAA,cAAe;CACb;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;AACF"}