ladrillosjs 1.0.2 → 2.0.0-beta.1.1

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 (37) hide show
  1. package/README.md +588 -237
  2. package/dist/cache/functionCache.d.ts +15 -0
  3. package/dist/cache/index.d.ts +16 -0
  4. package/dist/core/componentParser.d.ts +29 -0
  5. package/dist/core/componentSource.d.ts +12 -0
  6. package/dist/core/css/cssParser.d.ts +3 -0
  7. package/dist/core/eventBus.d.ts +41 -0
  8. package/dist/core/html/htmlRenderer.d.ts +18 -0
  9. package/dist/core/html/htmlparser.d.ts +16 -0
  10. package/dist/core/js/scriptParser.d.ts +3 -0
  11. package/dist/core/main.d.ts +4 -26
  12. package/dist/core/webcomponent.d.ts +2 -2
  13. package/dist/index-CPSzLUwq.js +8 -0
  14. package/dist/index-CPSzLUwq.js.map +1 -0
  15. package/dist/index-I-w3IPex.mjs +361 -0
  16. package/dist/index-I-w3IPex.mjs.map +1 -0
  17. package/dist/index.d.ts +56 -6
  18. package/dist/ladrillosjs.cjs.js +2 -1
  19. package/dist/ladrillosjs.cjs.js.map +1 -0
  20. package/dist/ladrillosjs.es.js +12 -4
  21. package/dist/ladrillosjs.es.js.map +1 -0
  22. package/dist/ladrillosjs.umd.js +80 -18
  23. package/dist/ladrillosjs.umd.js.map +1 -0
  24. package/dist/types/LadrilloTypes.d.ts +49 -14
  25. package/dist/utils/logger.d.ts +14 -11
  26. package/dist/utils/regex.d.ts +2 -0
  27. package/dist/webcomponent-Vt40CdPC.mjs +818 -0
  28. package/dist/webcomponent-Vt40CdPC.mjs.map +1 -0
  29. package/dist/webcomponent-oNIeezqN.js +74 -0
  30. package/dist/webcomponent-oNIeezqN.js.map +1 -0
  31. package/package.json +8 -4
  32. package/dist/core/store.d.ts +0 -6
  33. package/dist/index-D_dHFObN.mjs +0 -215
  34. package/dist/index-X9YN_DbT.js +0 -3
  35. package/dist/utils/stringify.d.ts +0 -7
  36. package/dist/webcomponent-Cga3h8cx.js +0 -16
  37. package/dist/webcomponent-UTcwAakf.mjs +0 -769
package/README.md CHANGED
@@ -4,14 +4,15 @@
4
4
 
5
5
  A lightweight, zero-dependency web component framework for building modular web applications.
6
6
 
7
- "I designed this framework to empower developers with the ability to componentize their code efficiently and effectively, without the need for a full-scale framework. By focusing on simplicity and leveraging core web fundamentals, my goal was to create a lightweight and accessible solution that enhances development while staying true to the basics."
7
+ **Version 2.0** - Rewritten in TypeScript with enhanced performance, improved developer experience, and powerful new features.
8
+
9
+ > "Empower developers to componentize code efficiently without the complexity of a full-scale framework. Focus on simplicity while leveraging core web fundamentals."
8
10
 
9
11
  ## Table of Contents
10
12
 
11
13
  - [Features](#features)
12
- - [Getting Started](#getting-started)
13
14
  - [Installation](#installation)
14
- - [Your First Component](#your-first-component)
15
+ - [Quick Start](#quick-start)
15
16
  - [Core Concepts](#core-concepts)
16
17
  - [Component Registration](#component-registration)
17
18
  - [State Management](#state-management)
@@ -20,12 +21,13 @@ A lightweight, zero-dependency web component framework for building modular web
20
21
  - [Conditional Rendering](#conditional-rendering)
21
22
  - [Slots](#slots)
22
23
  - [Advanced Features](#advanced-features)
24
+ - [Global Event Bus](#global-event-bus)
23
25
  - [External Scripts](#external-scripts)
24
- - [Global State Stores](#global-state-stores)
25
26
  - [Shadow DOM](#shadow-dom)
27
+ - [Performance & Caching](#performance--caching)
26
28
  - [API Reference](#api-reference)
27
29
  - [Examples](#examples)
28
- - [Contributing](#contributing)
30
+ - [Development](#development)
29
31
  - [License](#license)
30
32
 
31
33
  ## Features
@@ -33,39 +35,15 @@ A lightweight, zero-dependency web component framework for building modular web
33
35
  - 🚀 **Zero Dependencies** - Pure JavaScript, no build tools required
34
36
  - 📦 **Single-File Components** - HTML, CSS, and JavaScript in one file
35
37
  - ⚡ **Reactive State** - Automatic re-rendering on state changes
36
- - 🎯 **Event System** - Built-in event emission and listening
37
- - 🔄 **Two-Way Data Binding** - For form inputs and contenteditable elements
38
+ - 🎯 **Event System** - Global event bus for component communication
39
+ - 🔄 **Two-Way Data Binding** - Seamless form input binding with `$bind`
38
40
  - 🎨 **Scoped Styles** - Component styles with optional Shadow DOM
39
- - 🏪 **State Management** - Simple store implementation for shared state
40
- - 🔌 **Slots Support** - Content projection with named and default slots
41
- - 📝 **TypeScript Support** - Includes type definitions
42
-
43
- ## Getting Started
44
-
45
- The repository includes several example applications that demonstrate various features:
46
-
47
- - **[Todo App](samples/apps/todo)** - Classic todo list with component composition
48
- - **[Notes App](samples/apps/notes)** - Multi-component app with global state management
49
- - **[Markdown Editor](samples/apps/markdown)** - Real-time markdown preview
50
- - **[API Example](samples/apps/api)** - Fetching and displaying external data
51
- - **[Business Card](samples/apps/biz)** - Editable form with two-way data binding
52
- - **[Button Game](samples/apps/button-game)** - Interactive game with component events
53
- - **[Slideshow](samples/apps/slideshow)** - Multi-slide presentation system
54
- - **[Docs](samples/apps/docs)** - Documentation viewer with syntax highlighting
55
-
56
- To run the examples:
57
-
58
- ```bash
59
- # Clone the repository
60
- git clone https://github.com/drubiodev/LadrillosJS.git
61
- cd LadrillosJS
62
-
63
- # Install dependencies (for dev server only)
64
- npm install
65
-
66
- # Start the development server
67
- npm run dev
68
- ```
41
+ - 🔌 **Slots** - Content projection with named and default slots
42
+ - 📝 **TypeScript** - Full type definitions and TypeScript source code
43
+ - 🎭 **Conditional Rendering** - `$if`, `$else-if`, and `$else` directives
44
+ - 🚄 **Smart Caching** - LRU cache for components and compiled functions
45
+ - 🔧 **Framework Utilities** - Helper functions for common tasks
46
+ - 🧩 **External Scripts** - Load and bind external JavaScript modules
69
47
 
70
48
  ## Installation
71
49
 
@@ -78,34 +56,42 @@ npm install ladrillosjs
78
56
  ### CDN
79
57
 
80
58
  ```html
81
- <script defer src="https://cdn.jsdelivr.net/npm/ladrillosjs"></script>
82
- ```
59
+ <!-- ES Module (Recommended) -->
60
+ <script type="module">
61
+ import { registerComponent } from "https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.es.js";
62
+ registerComponent("my-component", "./my-component.html");
63
+ </script>
83
64
 
84
- ## Your First Component
65
+ <!-- UMD (Browser Global) -->
66
+ <script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
67
+ <script>
68
+ ladrillosjs.registerComponent("my-component", "./my-component.html");
69
+ </script>
70
+ ```
85
71
 
86
- A component in LadrillosJS is a reusable custom HTML element that bundles its own template, logic, and styles into a single file.
72
+ ## Quick Start
87
73
 
88
- ### 1. Create a Component File
74
+ ### 1. Create Your First Component
89
75
 
90
- Create `hello-world.html`:
76
+ Create a file called `hello-world.html`:
91
77
 
92
78
  ```html
93
79
  <!-- hello-world.html -->
94
80
  <div class="greeting">
95
81
  <h1>{title}</h1>
96
82
  <p>Hello, {name}!</p>
97
- <button onclick="greet">Click me ({count})</button>
83
+ <button onclick="greet()">Greet ({count})</button>
98
84
  </div>
99
85
 
100
86
  <script>
101
- // Component state
87
+ // Component state - automatically reactive
102
88
  let title = "Welcome to LadrillosJS";
103
89
  let name = "World";
104
90
  let count = 0;
105
91
 
106
92
  // Event handler
107
93
  const greet = () => {
108
- count++;
94
+ count++; // Automatically triggers re-render
109
95
  name = prompt("What's your name?") || "World";
110
96
  };
111
97
  </script>
@@ -114,14 +100,24 @@ Create `hello-world.html`:
114
100
  .greeting {
115
101
  text-align: center;
116
102
  padding: 2rem;
117
- background: #f0f0f0;
103
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
104
+ color: white;
118
105
  border-radius: 8px;
119
106
  }
120
107
 
121
108
  button {
122
- padding: 0.5rem 1rem;
109
+ padding: 0.75rem 1.5rem;
123
110
  font-size: 1rem;
111
+ background: white;
112
+ color: #667eea;
113
+ border: none;
114
+ border-radius: 4px;
124
115
  cursor: pointer;
116
+ transition: transform 0.2s;
117
+ }
118
+
119
+ button:hover {
120
+ transform: scale(1.05);
125
121
  }
126
122
  </style>
127
123
  ```
@@ -130,15 +126,17 @@ Create `hello-world.html`:
130
126
 
131
127
  ```html
132
128
  <!DOCTYPE html>
133
- <html>
129
+ <html lang="en">
134
130
  <head>
135
- <title>My App</title>
131
+ <meta charset="UTF-8" />
132
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
133
+ <title>My LadrillosJS App</title>
136
134
  </head>
137
135
  <body>
138
136
  <!-- Use your component -->
139
137
  <hello-world></hello-world>
140
138
 
141
- <!-- Register component -->
139
+ <!-- Register the component -->
142
140
  <script type="module">
143
141
  import { registerComponent } from "ladrillosjs";
144
142
  registerComponent("hello-world", "./hello-world.html");
@@ -147,121 +145,267 @@ Create `hello-world.html`:
147
145
  </html>
148
146
  ```
149
147
 
148
+ ### 3. Explore Example Apps
149
+
150
+ Check out the `samples/apps/` directory for complete examples:
151
+
152
+ - **[Todo App](samples/apps/todo)** - Classic todo list with component composition
153
+ - **[Notes App](samples/apps/notes)** - Multi-component app with event bus
154
+ - **[Simple Button](samples/apps/simple-button)** - Basic interactive component
155
+ - **[Business Card](samples/apps/biz)** - Form with two-way data binding
156
+ - **[Slideshow](samples/apps/slideshow)** - Multi-slide presentation
157
+ - **[Markdown Editor](samples/apps/markdown)** - Real-time markdown preview
158
+
159
+ Run the development server to view examples:
160
+
161
+ ```bash
162
+ npm install
163
+ npm run dev
164
+ ```
165
+
150
166
  ## Core Concepts
151
167
 
152
168
  ### Component Registration
153
169
 
154
- Register single or multiple components:
170
+ Register components to make them available as custom HTML elements:
155
171
 
156
172
  ```javascript
157
- // Single component
158
- import { registerComponent } from "ladrillosjs";
159
- registerComponent("my-component", "./my-component.html");
160
-
161
- // Multiple components with concurrency control
162
- import { registerComponents } from "ladrillosjs";
163
- await registerComponents(
164
- [
165
- { name: "app-header", path: "./components/header.html" },
166
- { name: "app-footer", path: "./components/footer.html" },
167
- { name: "user-card", path: "./components/user-card.html" },
168
- ],
169
- 10
170
- ); // Max 10 parallel fetches (default: 5)
171
-
172
- // Using CDN
173
- ladrillosjs.registerComponent("my-component", "./my-component.html");
173
+ import { registerComponent, registerComponents } from "ladrillosjs";
174
+
175
+ // Register a single component
176
+ await registerComponent("my-button", "./components/my-button.html");
177
+
178
+ // Register multiple components
179
+ await registerComponents([
180
+ { name: "app-header", path: "./components/header.html" },
181
+ { name: "app-footer", path: "./components/footer.html" },
182
+ { name: "user-card", path: "./components/user-card.html" },
183
+ ]);
184
+
185
+ // Disable Shadow DOM for a component
186
+ await registerComponent("global-styles", "./components/global.html", false);
174
187
  ```
175
188
 
176
189
  ### State Management
177
190
 
178
- Components have reactive state that automatically triggers re-renders:
191
+ Components have reactive state that automatically triggers re-renders when changed:
179
192
 
180
193
  ```html
181
194
  <div>
182
- <h2>User: {user.name}</h2>
183
- <p>Score: {score}</p>
184
- <button onclick="updateScore">Add Point</button>
195
+ <h2>Counter: {count}</h2>
196
+ <p>User: {user.name}</p>
197
+ <button onclick="increment()">Add</button>
198
+ <button onclick="updateUser()">Change User</button>
199
+ <button onclick="reset()">Reset</button>
185
200
  </div>
186
201
 
187
202
  <script>
188
- const date = new Date(Date.now());
189
- const formattedDate = date.toLocaleDateString("en-US"); // Format: MM/DD/YYYY
190
- // Initial state
191
- let score = 0;
192
- let user = {
193
- name: "Player 1",
203
+ // State variables - automatically reactive
204
+ let count = 0;
205
+ let user = { name: "John", age: 25 };
206
+
207
+ const increment = () => {
208
+ count++; // Automatically triggers re-render
209
+ };
210
+
211
+ const updateUser = () => {
212
+ user.name = "Jane"; // Direct mutation triggers re-render
194
213
  };
195
214
 
196
- const updateScore = () => {
197
- // Update state and trigger re-render
198
- score++;
215
+ // You can also use $setState for explicit updates
216
+ const reset = () => {
217
+ $setState({ count: 0, user: { name: "Anonymous", age: 0 } });
199
218
  };
200
219
  </script>
201
220
  ```
202
221
 
222
+ **Available State Utilities:**
223
+
224
+ - Direct assignment: `count++`, `name = "New"`
225
+ - `$setState(updates)`: Merge updates into state
226
+ - `$getState()`: Access state in external modules
227
+
203
228
  ### Event Handling
204
229
 
205
- Multiple ways to handle events:
230
+ Attach event handlers directly to elements:
206
231
 
207
232
  ```html
208
233
  <!-- Method reference -->
209
- <button onclick="handleClick">Click me: {count}</button>
234
+ <button onclick="handleClick(event)">Click me</button>
210
235
 
211
236
  <!-- Function with arguments -->
212
- <button onclick="addItem('Hello', 123)">Add Item</button>
237
+ <button onclick="addItem('apple', 5)">Add Item</button>
213
238
 
214
239
  <!-- Inline arrow function -->
215
- <button onclick="(e) => console.log(e.target)">Log Target</button>
240
+ <button onclick="console.log(event.target)">Log Event</button>
241
+
242
+ <!-- Multiple events -->
243
+ <label $if="isValid">Valid</label>
244
+ <input
245
+ type="text"
246
+ placeholder="Enter text (min 3 characters)"
247
+ onkeyup="validateInput(event)"
248
+ onfocus="highlightField(event)"
249
+ onblur="saveField(event)"
250
+ />
216
251
 
217
252
  <script>
218
- const count = 0;
219
- this.setState({ items: [] });
253
+ let items = [];
254
+ let isValid = false;
220
255
 
221
256
  const handleClick = (event) => {
222
- console.log("Clicked!", event);
223
- count++;
257
+ console.log("Button clicked!", event);
258
+ };
259
+
260
+ const addItem = (name, quantity) => {
261
+ items = [...items, { name, quantity }];
262
+ console.log("Items:", items);
224
263
  };
225
264
 
226
- const addItem = (name, value) => {
227
- this.setState({ items: [...this.state.items, { name, value }] });
265
+ const validateInput = (e) => {
266
+ const value = e.target.value;
267
+ isValid = value.length >= 3;
268
+ };
269
+
270
+ const highlightField = (e) => {
271
+ e.target.style.backgroundColor = "lightyellow";
272
+ };
273
+
274
+ const saveField = (e) => {
275
+ e.target.style.backgroundColor = "";
276
+ console.log("Field saved:", e.target.value);
228
277
  };
229
278
  </script>
279
+ ```
280
+
281
+ ### Data Binding
282
+
283
+ #### One-Way Binding (Display Data)
284
+
285
+ Use curly braces `{}` to display state in your template:
286
+
287
+ ```html
230
288
  <div>
231
- <h1>Shopping Cart ({items.length} items)</h1>
289
+ <h1>{title}</h1>
290
+ <p>{user.name} - {user.email}</p>
291
+ <span>Total: {items.length} items</span>
292
+ <p>Formatted: {formatPrice(price)}</p>
293
+ </div>
232
294
 
233
- <div data-if="items.length === 0">
234
- <p>Your cart is empty</p>
235
- </div>
295
+ <script>
296
+ let title = "My App";
297
+ let user = { name: "John", email: "john@example.com" };
298
+ let items = ["apple", "banana", "orange"];
299
+ let price = 29.99;
300
+
301
+ const formatPrice = (value) => `$${value.toFixed(2)}`;
302
+ </script>
303
+ ```
304
+
305
+ #### Two-Way Binding (Form Inputs)
306
+
307
+ Use the `$bind` attribute for automatic synchronization between inputs and state:
308
+
309
+ ```html
310
+ <div>
311
+ <h2>Hello, {name}!</h2>
312
+ <input type="text" $bind="name" placeholder="Your name" />
313
+
314
+ <p>Email: {email}</p>
315
+ <input type="email" $bind="email" />
316
+
317
+ <p>Bio: {bio}</p>
318
+ <textarea $bind="bio"></textarea>
319
+
320
+ <p>Country: {country}</p>
321
+ <select $bind="country">
322
+ <option value="us">United States</option>
323
+ <option value="uk">United Kingdom</option>
324
+ <option value="ca">Canada</option>
325
+ </select>
326
+
327
+ <label>
328
+ <input type="checkbox" $bind="subscribe" />
329
+ Subscribe to newsletter: {subscribe}
330
+ </label>
331
+ </div>
332
+
333
+ <script>
334
+ // Variables are automatically synced with inputs
335
+ let name = "World";
336
+ let email = "";
337
+ let bio = "";
338
+ let country = "us";
339
+ let subscribe = false;
340
+ </script>
341
+ ```
342
+
343
+ ### Conditional Rendering
344
+
345
+ Show or hide elements based on conditions:
346
+
347
+ ```html
348
+ <div>
349
+ <h1>Shopping Cart</h1>
236
350
 
237
- <div data-else-if="items.length < 3">
238
- <p>You have a few items</p>
351
+ <!-- Simple condition -->
352
+ <p $if="{items.length === 0}">Your cart is empty</p>
353
+
354
+ <!-- Multiple conditions -->
355
+ <div $if="{items.length > 0 && items.length < 5}">
356
+ <p>You have {items.length} items</p>
239
357
  </div>
240
358
 
241
- <div data-else>
242
- <p>You have many items!</p>
359
+ <div $else-if="{items.length >= 5}">
360
+ <p>Your cart is full! ({items.length} items)</p>
243
361
  </div>
244
362
 
245
- <button data-if="!isLoggedIn" onclick="login">Login</button>
246
- <button data-else onclick="logout">Logout</button>
363
+ <!-- Login/Logout example -->
364
+ <button $if="{!isLoggedIn}" onclick="login()">Login</button>
365
+ <button $else onclick="logout()">Logout</button>
366
+
367
+ <!-- Complex conditions -->
368
+ <div $if="{user && user.role.toLowerCase() === 'admin'}">
369
+ <p>{user.role} Panel</p>
370
+ Hello {user.name}
371
+ <button onclick="addToCart()">🛒 Add To Cart</button>
372
+ </div>
247
373
  </div>
374
+
248
375
  <script>
249
- const items = ["apple", "banana", "orange"];
250
- const isLoggedIn = false;
376
+ let items = [];
377
+ let isLoggedIn = false;
378
+ let user = null;
251
379
 
252
- function login() {
380
+ const login = () => {
253
381
  isLoggedIn = true;
254
- }
382
+ user = { name: "John", role: "Admin" };
383
+ };
255
384
 
256
- function logout() {
385
+ const logout = () => {
257
386
  isLoggedIn = false;
258
- }
387
+ user = null;
388
+ };
389
+
390
+ const addToCart = () => {
391
+ if (items.length < 5) {
392
+ items.push(`Item ${items.length + 1}`);
393
+ } else {
394
+ alert("Cart is full!");
395
+ }
396
+ };
259
397
  </script>
260
398
  ```
261
399
 
400
+ **Conditional Directives:**
401
+
402
+ - `$if="{expression}"`: Show if expression is truthy
403
+ - `$else-if="{expression}"`: Chain multiple conditions
404
+ - `$else`: Fallback when previous conditions are false
405
+
262
406
  ### Slots
263
407
 
264
- Content projection using slots:
408
+ Project content from parent to child components:
265
409
 
266
410
  ```html
267
411
  <!-- card.html -->
@@ -278,194 +422,401 @@ Content projection using slots:
278
422
  </div>
279
423
  </div>
280
424
 
281
- <!-- Usage -->
425
+ <style>
426
+ .card {
427
+ border: 1px solid #ddd;
428
+ border-radius: 8px;
429
+ padding: 1rem;
430
+ }
431
+ .card-header {
432
+ font-weight: bold;
433
+ margin-bottom: 1rem;
434
+ }
435
+ .card-footer {
436
+ margin-top: 1rem;
437
+ border-top: 1px solid #eee;
438
+ padding-top: 1rem;
439
+ }
440
+ </style>
441
+ ```
442
+
443
+ **Usage:**
444
+
445
+ ```html
282
446
  <my-card>
283
447
  <h2 slot="header">User Profile</h2>
284
- <p>This goes in the default slot</p>
285
- <button slot="footer">Save</button>
448
+ <p>Name: John Doe</p>
449
+ <p>Email: john@example.com</p>
450
+ <button slot="footer">Save Changes</button>
286
451
  </my-card>
287
452
  ```
288
453
 
289
454
  ## Advanced Features
290
455
 
291
- ### External Scripts
456
+ ### Global Event Bus
292
457
 
293
- Load external JavaScript with components:
458
+ The global event bus enables communication between components without prop drilling:
459
+
460
+ ```javascript
461
+ // Emit events to other components
462
+ $emit("user-logged-in", { userId: 123, username: "john" });
463
+
464
+ // Listen for events from any component
465
+ $listen("user-logged-in", (data) => {
466
+ console.log(`User ${data.username} logged in`);
467
+ isLoggedIn = true;
468
+ currentUser = data;
469
+ });
470
+ ```
471
+
472
+ **Example: Cross-Component Communication**
294
473
 
295
474
  ```html
296
- <!-- With 'bind' attribute for component context -->
297
- <script src="./helpers.js" bind></script>
475
+ <!-- header.html -->
476
+ <header>
477
+ <span $if="{isLoggedIn}">Welcome, {username}!</span>
478
+ <button $else onclick="requestLogin()">Login</button>
479
+ </header>
480
+
481
+ <script>
482
+ let isLoggedIn = false;
483
+ let username = "";
298
484
 
299
- <!-- ES modules with bind -->
300
- <script src="./component-logic.js" type="module" bind></script>
485
+ $listen("user-logged-in", (user) => {
486
+ console.log("User logged in:", user);
487
+ isLoggedIn = true;
488
+ username = user.username;
489
+ });
301
490
 
302
- <!-- Regular external script -->
303
- <script src="https://cdn.example.com/library.js"></script>
491
+ const requestLogin = () => {
492
+ $emit("show-login-dialog");
493
+ };
494
+ </script>
304
495
  ```
305
496
 
306
- For modules with `bind`, export a default function:
497
+ ```html
498
+ <!-- login-form.html -->
499
+ <form onsubmit="handleLogin(event)" $if="{showLoginDialog}">
500
+ <input type="text" $bind="username" placeholder="Username" />
501
+ <input type="password" $bind="password" placeholder="Password" />
502
+ <button type="submit">Login</button>
503
+ </form>
307
504
 
308
- ```javascript
309
- // component-logic.js
310
- export default function () {
311
- // 'this' refers to the component instance
312
- this.formatDate = (date) => {
313
- return new Intl.DateTimeFormat("en-US").format(date);
314
- };
505
+ <script>
506
+ let showLoginDialog = false;
315
507
 
316
- this.init = () => {
317
- console.log("Component initialized");
318
- };
508
+ $listen("show-login-dialog", () => {
509
+ showLoginDialog = true;
510
+ });
319
511
 
320
- // Called automatically if defined
321
- this.init();
322
- }
512
+ const handleLogin = (e) => {
513
+ e.preventDefault();
514
+ $emit("user-logged-in", { userId: 123, username });
515
+ username = "";
516
+ password = "";
517
+ showLoginDialog = false;
518
+ };
519
+ </script>
323
520
  ```
324
521
 
325
- ### Global State Stores
522
+ ### External Scripts
326
523
 
327
- Share state across components:
524
+ LadrillosJS supports three ways to include external JavaScript:
328
525
 
329
- ```javascript
330
- // stores/userStore.js
331
- import { createStore } from "ladrillosjs";
526
+ #### 1. Component-Scoped Scripts (Default)
332
527
 
333
- export const userStore = createStore({
334
- user: null,
335
- isAuthenticated: false,
336
- });
528
+ Regular script tags execute within the component's context and have access to component state and utilities:
337
529
 
338
- export function login(userData) {
339
- userStore.setState({
340
- user: userData,
341
- isAuthenticated: true,
342
- });
343
- }
530
+ ```html
531
+ <!-- alert-button.html -->
532
+ <button onclick="increaseCount()">{title}: {count}</button>
344
533
 
345
- export function logout() {
346
- userStore.setState({
347
- user: null,
348
- isAuthenticated: false,
349
- });
350
- }
534
+ <!-- This script runs in the component context -->
535
+ <script src="./alert.js"></script>
351
536
  ```
352
537
 
353
- ```html
354
- <!-- header.html -->
355
- <header>
356
- <span data-if="isAuthenticated">Welcome, {user.name}!</span>
357
- <button data-else onclick="showLogin">Login</button>
358
- </header>
538
+ **alert.js:**
539
+
540
+ ```javascript
541
+ // Variables and functions are available to the component
542
+ let count = 0;
543
+ const title = "Button Count";
359
544
 
360
- <script type="module" src="./header-logic.js" bind></script>
545
+ const increaseCount = () => {
546
+ count++; // Updates component state
547
+ };
361
548
  ```
362
549
 
550
+ #### 2. ES Modules
551
+
552
+ Use `type="module"` for standard ES module imports:
553
+
554
+ ```html
555
+ <!-- side-nav.html -->
556
+ <nav>
557
+ <h1>&lt;Note App/&gt;</h1>
558
+ <button onclick="createNote()">Create Note</button>
559
+ <ul></ul>
560
+ </nav>
561
+
562
+ <!-- Load as ES module -->
563
+ <script type="module" src="../js/side.js"></script>
564
+ ```
565
+
566
+ **side.js:**
567
+
363
568
  ```javascript
364
- // header-logic.js
365
- import { userStore } from "../stores/userStore.js";
366
-
367
- export default function () {
368
- // Subscribe to store changes
369
- userStore.subscribe((state) => {
370
- this.setState({
371
- user: state.user,
372
- isAuthenticated: state.isAuthenticated,
373
- });
374
- });
569
+ import { registerComponent, $listen, $querySelector } from "ladrillosjs";
570
+
571
+ const notes = [];
572
+ registerComponent("note-item", "./components/note-item.html");
573
+
574
+ $listen("note_saved", (data) => {
575
+ notes.push({ ...data });
576
+ const ul = $querySelector("ul");
577
+ if (ul) {
578
+ ul.innerHTML = notes
579
+ .map((n) => `<note-item data-note='${JSON.stringify(n)}'></note-item>`)
580
+ .join("");
581
+ }
582
+ });
583
+ ```
375
584
 
376
- this.showLogin = () => {
377
- this.emit("show-login");
378
- };
379
- }
585
+ #### 3. External Libraries
586
+
587
+ Use the `external` attribute for third-party libraries that shouldn't be bound to component context:
588
+
589
+ ```html
590
+ <!-- codeblock.html -->
591
+ <link
592
+ rel="stylesheet"
593
+ href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css"
594
+ />
595
+ <script
596
+ src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"
597
+ external
598
+ ></script>
599
+
600
+ <div class="code-container">
601
+ <pre><code id="code" class="language-html"></code></pre>
602
+ </div>
603
+
604
+ <script>
605
+ // Access the external library (hljs is global)
606
+ const codeElement = $querySelector("pre code");
607
+ codeElement.textContent = "console.log('Hello')";
608
+ hljs.highlightElement(codeElement); // Use external library
609
+ </script>
380
610
  ```
381
611
 
612
+ **When to use `external`:**
613
+
614
+ - Third-party CDN libraries (highlight.js, Chart.js, etc.)
615
+ - Libraries that need to be loaded globally
616
+ - Scripts that don't need component context or utilities
617
+
382
618
  ### Shadow DOM
383
619
 
384
- Components use Shadow DOM by default for style encapsulation. To disable:
620
+ Components use Shadow DOM by default for style encapsulation:
385
621
 
386
622
  ```javascript
387
- // Disable Shadow DOM for a component
388
- registerComponent("my-component", "./my-component.html", false);
623
+ // Shadow DOM enabled (default)
624
+ await registerComponent("isolated-widget", "./widget.html");
625
+ await registerComponent("isolated-widget", "./widget.html", true);
389
626
 
390
- // Multiple components
391
- registerComponents([
392
- { name: "global-styles", path: "./global.html", useShadowDOM: false },
393
- { name: "isolated-widget", path: "./widget.html", useShadowDOM: true },
394
- ]);
627
+ // Shadow DOM disabled
628
+ await registerComponent("global-styles", "./global.html", false);
395
629
  ```
396
630
 
631
+ **Shadow DOM Benefits:**
632
+
633
+ - **Style Isolation**: Component styles don't leak to global scope
634
+ - **Encapsulation**: Internal DOM is hidden from parent
635
+ - **Clean Separation**: Each component has its own styling context
636
+
637
+ **When to Disable:**
638
+
639
+ - Need to apply global CSS frameworks (Bootstrap, Tailwind)
640
+ - Using third-party libraries that expect normal DOM
641
+ - Easier debugging without shadow boundaries
642
+
643
+ ### Performance & Caching
644
+
645
+ LadrillosJS includes built-in performance optimizations:
646
+
647
+ #### Component Caching
648
+
649
+ - **LRU Cache**: Stores up to 25 most recently used components
650
+ - **Instant Loading**: Cached components load immediately
651
+ - **Reduced Network**: No repeated HTTP requests for components
652
+
653
+ #### Function Caching
654
+
655
+ - **Template Compilation**: Caches up to 100 compiled expressions
656
+ - **Memory Efficient**: Reuses Function objects for identical expressions
657
+ - **Faster Renders**: Expressions like `{formatName(user)}` compile once
658
+
659
+ **Performance Tips:**
660
+
661
+ - Component files are cached after first load
662
+ - State updates trigger minimal DOM changes
663
+ - Event listeners are automatically cleaned up
664
+ - Conditional rendering skips hidden elements
665
+
397
666
  ## API Reference
398
667
 
399
- ### Component Methods
668
+ ### Registration Functions
400
669
 
401
- | Method | Description |
402
- | --------------------------------- | -------------------------------------------- |
403
- | `this.setState(partial)` | Update component state and trigger re-render |
404
- | `this.emit(eventName, data?)` | Dispatch a custom event |
405
- | `this.listen(eventName, handler)` | Listen for custom events |
406
- | `this.querySelector(selector)` | Query element within component |
407
- | `this.querySelectorAll(selector)` | Query all elements within component |
670
+ ```typescript
671
+ registerComponent(name: string, path: string, useShadowDOM?: boolean): Promise<void>
672
+ registerComponents(components: Array<{name, path, useShadowDOM?}>): Promise<void>
673
+ ```
408
674
 
409
- ### Store Methods
675
+ ### Component Utilities
410
676
 
411
- | Method | Description |
412
- | --------------------------- | -------------------------- |
413
- | `createStore(initialState)` | Create a new store |
414
- | `store.getState()` | Get current store state |
415
- | `store.setState(partial)` | Update store state |
416
- | `store.subscribe(callback)` | Subscribe to state changes |
417
- | `store.reset()` | Reset to initial state |
677
+ Available within component `<script>` tags:
418
678
 
419
- ## Examples
679
+ ```typescript
680
+ // State management
681
+ $getState(): object // Access component state
682
+ $setState(updates: object): void // Update state and re-render
420
683
 
421
- ### Component Communication
684
+ // Event bus
685
+ $emit(eventName: string, data?: any): void
686
+ $listen(eventName: string, callback: (data?) => void): () => void
687
+
688
+ // DOM queries (respects Shadow DOM boundaries)
689
+ $querySelector(selector: string): Element | null
690
+ $querySelectorAll(selector: string): NodeListOf<Element>
691
+
692
+ // Reactive variables (for external modules)
693
+ $reactive(name: string, initialValue: any): (value: any) => void
694
+ ```
695
+
696
+ ### Component Attributes
422
697
 
423
698
  ```html
424
- <!-- parent.html -->
425
- <div>
426
- <child-component data-message="Hello"></child-component>
427
- </div>
699
+ <!-- Two-way data binding -->
700
+ <input $bind="variableName" />
701
+
702
+ <!-- Conditional rendering -->
703
+ <div $if="{condition}">...</div>
704
+ <div $else-if="{anotherCondition}">...</div>
705
+ <div $else>...</div>
706
+
707
+ <!-- Event handlers -->
708
+ <button onclick="methodName()">Click</button>
709
+ <button onclick="method(arg1, arg2)">Call with args</button>
710
+ <button onclick="console.log(event)">Inline function</button>
711
+
712
+ <!-- Slots -->
713
+ <slot></slot>
714
+ <!-- Default slot -->
715
+ <slot name="header"></slot>
716
+ <!-- Named slot -->
717
+ ```
428
718
 
429
- <script>
430
- this.listen("child-event", (data) => {
431
- console.log("Received from child:", data);
432
- });
433
- </script>
719
+ ### Template Syntax
434
720
 
435
- <!-- child.html -->
436
- <button onclick="sendMessage">{data-message}</button>
721
+ ```html
722
+ <!-- Variable interpolation -->
723
+ {variableName} {object.property} {array[0]}
437
724
 
438
- <script>
439
- const sendMessage = () => {
440
- this.emit("child-event", {
441
- message: this.state["data-message"],
442
- timestamp: Date.now(),
443
- });
444
- };
445
- </script>
725
+ <!-- Function calls -->
726
+ {functionName(arg1, arg2)} {object.method()}
727
+
728
+ <!-- Expressions -->
729
+ {count + 1} {isActive ? 'Yes' : 'No'} {items.length > 0 ? 'Has items' : 'Empty'}
446
730
  ```
447
731
 
448
- ### Dynamic Component Creation
732
+ ## Development
449
733
 
450
- ```javascript
451
- // Create components programmatically
452
- const createCard = (userData) => {
453
- const card = document.createElement("user-card");
454
- card.setAttribute("user-id", userData.id);
455
- card.setAttribute("name", userData.name);
456
- document.querySelector("#user-list").appendChild(card);
457
- };
734
+ ### Prerequisites
735
+
736
+ - **Node.js**: v20.19+ or v22.12+
737
+ - **npm**: v9+ (comes with Node.js)
738
+
739
+ ### Setup
740
+
741
+ ```bash
742
+ # Clone the repository
743
+ git clone https://github.com/drubiodev/LadrillosJS.git
744
+ cd LadrillosJS
745
+
746
+ # Install dependencies
747
+ npm install
458
748
 
459
- // Fetch and create
460
- fetch("/api/users")
461
- .then((res) => res.json())
462
- .then((users) => users.forEach(createCard));
749
+ # Start development server
750
+ npm run dev
751
+
752
+ # Build the library
753
+ npm run build
754
+
755
+ # Run tests
756
+ npm test
757
+
758
+ # Run tests with coverage
759
+ npm run test:coverage
463
760
  ```
464
761
 
465
- ## Contributing
762
+ ### Project Structure
763
+
764
+ ```
765
+ LadrillosJS/
766
+ ├── src/
767
+ │ ├── index.ts # Main entry point
768
+ │ ├── core/
769
+ │ │ ├── main.ts # Core Ladrillos class
770
+ │ │ ├── webcomponent.ts # Web component wrapper
771
+ │ │ ├── componentParser.ts # Parse component files
772
+ │ │ ├── componentSource.ts # Fetch with caching
773
+ │ │ ├── eventBus.ts # Global event bus
774
+ │ │ ├── css/
775
+ │ │ │ └── cssParser.ts
776
+ │ │ ├── html/
777
+ │ │ │ ├── htmlparser.ts
778
+ │ │ │ └── htmlRenderer.ts
779
+ │ │ └── js/
780
+ │ │ └── scriptParser.ts
781
+ │ ├── cache/
782
+ │ │ ├── index.ts # LRU component cache
783
+ │ │ └── functionCache.ts # LRU function cache
784
+ │ ├── types/
785
+ │ │ └── LadrilloTypes.ts
786
+ │ └── utils/
787
+ │ ├── logger.ts
788
+ │ └── regex.ts
789
+ ├── samples/ # Example applications
790
+ │ └── apps/
791
+ │ ├── todo/
792
+ │ ├── notes/
793
+ │ ├── simple-button/
794
+ │ └── ...
795
+ ├── test/ # Test files
796
+ ├── dist/ # Built files
797
+ ├── package.json
798
+ ├── tsconfig.json
799
+ ├── vite.config.js
800
+ └── vitest.config.js
801
+ ```
466
802
 
467
- Contributions are welcome! Please feel free to submit a Pull Request.
803
+ ### NPM Scripts
804
+
805
+ ```bash
806
+ npm run dev # Start Vite dev server
807
+ npm run build # Build library (ESM, UMD, CJS)
808
+ npm run build:types # Generate TypeScript declarations
809
+ npm test # Run tests with Vitest
810
+ npm run test:coverage # Run tests with coverage report
811
+ npm run preview # Preview production build
812
+ ```
468
813
 
469
814
  ## License
470
815
 
471
816
  MIT License - see [LICENSE](LICENSE) file for details.
817
+
818
+ ---
819
+
820
+ **LadrillosJS v2.0** - Built with ❤️ by [Daniel Rubio](https://github.com/drubiodev)
821
+
822
+ 🌟 [GitHub](https://github.com/drubiodev/LadrillosJS) • 📦 [NPM](https://www.npmjs.com/package/ladrillosjs) • 📖 [Documentation](https://github.com/drubiodev/LadrillosJS/blob/main/README.md)