ladrillosjs 2.0.0-beta.1 → 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.
package/README.md CHANGED
@@ -4,19 +4,15 @@
4
4
 
5
5
  A lightweight, zero-dependency web component framework for building modular web applications.
6
6
 
7
- **Version 2.0** - Now rewritten in TypeScript with enhanced performance, better developer experience, and powerful new features.
7
+ **Version 2.0** - Rewritten in TypeScript with enhanced performance, improved developer experience, and powerful new features.
8
8
 
9
- "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."
9
+ > "Empower developers to componentize code efficiently without the complexity of a full-scale framework. Focus on simplicity while leveraging core web fundamentals."
10
10
 
11
11
  ## Table of Contents
12
12
 
13
13
  - [Features](#features)
14
- - [Getting Started](#getting-started)
15
- - [Prerequisites](#prerequisites)
16
- - [What's New in v2.0](#whats-new-in-v20)
17
- - [Example Applications](#example-applications)
18
14
  - [Installation](#installation)
19
- - [Your First Component](#your-first-component)
15
+ - [Quick Start](#quick-start)
20
16
  - [Core Concepts](#core-concepts)
21
17
  - [Component Registration](#component-registration)
22
18
  - [State Management](#state-management)
@@ -25,92 +21,29 @@ A lightweight, zero-dependency web component framework for building modular web
25
21
  - [Conditional Rendering](#conditional-rendering)
26
22
  - [Slots](#slots)
27
23
  - [Advanced Features](#advanced-features)
28
- - [External Scripts](#external-scripts)
29
24
  - [Global Event Bus](#global-event-bus)
25
+ - [External Scripts](#external-scripts)
30
26
  - [Shadow DOM](#shadow-dom)
31
27
  - [Performance & Caching](#performance--caching)
32
28
  - [API Reference](#api-reference)
33
29
  - [Examples](#examples)
34
- - [Component Communication](#component-communication)
35
- - [Dynamic Component Creation](#dynamic-component-creation)
36
- - [Passing Complex Data](#passing-complex-data)
37
- - [Migration Guide (v1.x to v2.0)](#migration-guide-v1x-to-v20)
38
- - [Contributing](#contributing)
30
+ - [Development](#development)
39
31
  - [License](#license)
40
32
 
41
33
  ## Features
42
34
 
43
35
  - 🚀 **Zero Dependencies** - Pure JavaScript, no build tools required
44
36
  - 📦 **Single-File Components** - HTML, CSS, and JavaScript in one file
45
- - ⚡ **Reactive State** - Automatic re-rendering on state changes with optimized proxies
46
- - 🎯 **Event System** - Built-in event emission and global event bus for component communication
47
- - 🔄 **Two-Way Data Binding** - Seamless binding for form inputs with `$bind`
37
+ - ⚡ **Reactive State** - Automatic re-rendering on state changes
38
+ - 🎯 **Event System** - Global event bus for component communication
39
+ - 🔄 **Two-Way Data Binding** - Seamless form input binding with `$bind`
48
40
  - 🎨 **Scoped Styles** - Component styles with optional Shadow DOM
49
- - 🏪 **Global Event Bus** - Cross-component communication without prop drilling
50
- - 🔌 **Slots Support** - Content projection with named and default slots
51
- - 📝 **TypeScript Support** - Full type definitions and TypeScript source code
52
- - 🎭 **Conditional Rendering** - `data-if`, `data-else-if`, and `data-else` directives
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
53
44
  - 🚄 **Smart Caching** - LRU cache for components and compiled functions
54
- - 🔧 **Framework Utilities** - `$state`, `$setState`, `$emit`, `$listen`, `$querySelector` helpers
55
- - ⚙️ **Automatic Reactivity** - Variable assignments automatically trigger re-renders
56
- - 🧩 **External Scripts** - Load and bind external JavaScript with your components
57
-
58
- ## Getting Started
59
-
60
- ### Prerequisites
61
-
62
- - **Node.js**: Version 20.19+ or 22.12+ is required for development
63
- - **TypeScript**: v5.8+ (included in devDependencies)
64
-
65
- ### What's New in v2.0
66
-
67
- - **🔄 Complete TypeScript Rewrite** - Full type safety and improved IDE support
68
- - **⚡ Performance Enhancements** - LRU caching for components and compiled functions
69
- - **🎯 Global Event Bus** - New `$emit` and `$listen` for cross-component communication
70
- - **🔧 Framework Utilities** - New `$state`, `$setState`, `$querySelector` helpers
71
- - **🚀 Automatic Reactivity** - Variable assignments like `count++` automatically trigger re-renders
72
- - **📦 Better Build System** - Vite-powered builds with sourcemaps and multiple output formats (ESM, UMD, CJS)
73
- - **🧪 Testing** - Vitest with coverage reporting
74
- - **🎭 Enhanced Conditionals** - More robust `data-if`, `data-else-if`, `data-else` rendering
75
- - **🔄 Two-Way Binding** - Simplified with `$bind` prefix for automatic state synchronization
76
- - **🧹 Memory Management** - Automatic cleanup of event listeners on component disconnect
77
-
78
- ### Example Applications
79
-
80
- The repository includes several example applications that demonstrate various features:
81
-
82
- - **[Todo App](samples/apps/todo)** - Classic todo list with component composition
83
- - **[Notes App](samples/apps/notes)** - Multi-component app with global event bus
84
- - **[Markdown Editor](samples/apps/markdown)** - Real-time markdown preview
85
- - **[API Example](samples/apps/api)** - Fetching and displaying external data
86
- - **[Business Card](samples/apps/biz)** - Editable form with two-way data binding using `$bind`
87
- - **[Button Game](samples/apps/button-game)** - Interactive game with component events
88
- - **[Slideshow](samples/apps/slideshow)** - Multi-slide presentation system
89
- - **[Document Chat](samples/apps/document-chat)** - Chat interface with component communication
90
- - **[Docs](samples/apps/docs)** - Documentation viewer with syntax highlighting
91
-
92
- To run the examples:
93
-
94
- ```bash
95
- # Clone the repository
96
- git clone https://github.com/drubiodev/LadrillosJS.git
97
- cd LadrillosJS
98
-
99
- # Install dependencies
100
- npm install
101
-
102
- # Start the development server (Vite)
103
- npm run dev
104
-
105
- # Build the library
106
- npm run build
107
-
108
- # Run tests
109
- npm test
110
-
111
- # Run tests with coverage
112
- npm run test:coverage
113
- ```
45
+ - 🔧 **Framework Utilities** - Helper functions for common tasks
46
+ - 🧩 **External Scripts** - Load and bind external JavaScript modules
114
47
 
115
48
  ## Installation
116
49
 
@@ -123,45 +56,42 @@ npm install ladrillosjs
123
56
  ### CDN
124
57
 
125
58
  ```html
126
- <!-- Latest version -->
59
+ <!-- ES Module (Recommended) -->
127
60
  <script type="module">
128
61
  import { registerComponent } from "https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.es.js";
129
62
  registerComponent("my-component", "./my-component.html");
130
63
  </script>
131
64
 
132
- <!-- UMD (for legacy browsers) -->
65
+ <!-- UMD (Browser Global) -->
133
66
  <script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
134
67
  <script>
135
- // Access via global ladrillosjs object
136
68
  ladrillosjs.registerComponent("my-component", "./my-component.html");
137
69
  </script>
138
70
  ```
139
71
 
140
- ## Your First Component
141
-
142
- 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
143
73
 
144
- ### 1. Create a Component File
74
+ ### 1. Create Your First Component
145
75
 
146
- Create `hello-world.html`:
76
+ Create a file called `hello-world.html`:
147
77
 
148
78
  ```html
149
79
  <!-- hello-world.html -->
150
80
  <div class="greeting">
151
81
  <h1>{title}</h1>
152
82
  <p>Hello, {name}!</p>
153
- <button onclick="greet">Click me ({count})</button>
83
+ <button onclick="greet()">Greet ({count})</button>
154
84
  </div>
155
85
 
156
86
  <script>
157
- // Component state
87
+ // Component state - automatically reactive
158
88
  let title = "Welcome to LadrillosJS";
159
89
  let name = "World";
160
90
  let count = 0;
161
91
 
162
92
  // Event handler
163
93
  const greet = () => {
164
- count++;
94
+ count++; // Automatically triggers re-render
165
95
  name = prompt("What's your name?") || "World";
166
96
  };
167
97
  </script>
@@ -170,14 +100,24 @@ Create `hello-world.html`:
170
100
  .greeting {
171
101
  text-align: center;
172
102
  padding: 2rem;
173
- background: #f0f0f0;
103
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
104
+ color: white;
174
105
  border-radius: 8px;
175
106
  }
176
107
 
177
108
  button {
178
- padding: 0.5rem 1rem;
109
+ padding: 0.75rem 1.5rem;
179
110
  font-size: 1rem;
111
+ background: white;
112
+ color: #667eea;
113
+ border: none;
114
+ border-radius: 4px;
180
115
  cursor: pointer;
116
+ transition: transform 0.2s;
117
+ }
118
+
119
+ button:hover {
120
+ transform: scale(1.05);
181
121
  }
182
122
  </style>
183
123
  ```
@@ -186,15 +126,17 @@ Create `hello-world.html`:
186
126
 
187
127
  ```html
188
128
  <!DOCTYPE html>
189
- <html>
129
+ <html lang="en">
190
130
  <head>
191
- <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>
192
134
  </head>
193
135
  <body>
194
136
  <!-- Use your component -->
195
137
  <hello-world></hello-world>
196
138
 
197
- <!-- Register component -->
139
+ <!-- Register the component -->
198
140
  <script type="module">
199
141
  import { registerComponent } from "ladrillosjs";
200
142
  registerComponent("hello-world", "./hello-world.html");
@@ -203,231 +145,267 @@ Create `hello-world.html`:
203
145
  </html>
204
146
  ```
205
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
+
206
166
  ## Core Concepts
207
167
 
208
168
  ### Component Registration
209
169
 
210
- Register single or multiple components:
170
+ Register components to make them available as custom HTML elements:
211
171
 
212
172
  ```javascript
213
- // Single component
214
- import { registerComponent } from "ladrillosjs";
215
- await 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");
216
177
 
217
- // Multiple components (v2.0 feature - currently in development)
218
- import { registerComponents } from "ladrillosjs";
178
+ // Register multiple components
219
179
  await registerComponents([
220
180
  { name: "app-header", path: "./components/header.html" },
221
181
  { name: "app-footer", path: "./components/footer.html" },
222
182
  { name: "user-card", path: "./components/user-card.html" },
223
183
  ]);
224
184
 
225
- // Using CDN
226
- ladrillosjs.registerComponent("my-component", "./my-component.html");
185
+ // Disable Shadow DOM for a component
186
+ await registerComponent("global-styles", "./components/global.html", false);
227
187
  ```
228
188
 
229
- **Note:** The `registerComponents` function is planned for v2.0 to enable bulk registration with concurrency control.
230
-
231
189
  ### State Management
232
190
 
233
- Components have reactive state that automatically triggers re-renders. In v2.0, variable assignments are automatically tracked and trigger reactivity:
191
+ Components have reactive state that automatically triggers re-renders when changed:
234
192
 
235
193
  ```html
236
194
  <div>
237
- <h2>User: {user.name}</h2>
238
- <p>Score: {score}</p>
239
- <button onclick="updateScore">Add Point</button>
240
- <button onclick="increment">Count: {count}</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>
241
200
  </div>
242
201
 
243
202
  <script>
244
- // Initial state - automatically tracked
245
- let score = 0;
203
+ // State variables - automatically reactive
246
204
  let count = 0;
247
- let user = {
248
- name: "Player 1",
249
- };
250
-
251
- const updateScore = () => {
252
- // Direct assignment triggers re-render automatically
253
- score++;
254
- };
205
+ let user = { name: "John", age: 25 };
255
206
 
256
207
  const increment = () => {
257
- // All these automatically trigger re-renders in v2.0
258
- count++; // Increment
259
- count += 5; // Compound assignment
260
- count = count * 2; // Direct assignment
208
+ count++; // Automatically triggers re-render
261
209
  };
262
210
 
263
- // You can also use the explicit setState method
264
211
  const updateUser = () => {
265
- $setState({ user: { name: "Jane", age: 30 } });
212
+ user.name = "Jane"; // Direct mutation triggers re-render
213
+ };
214
+
215
+ // You can also use $setState for explicit updates
216
+ const reset = () => {
217
+ $setState({ count: 0, user: { name: "Anonymous", age: 0 } });
266
218
  };
267
219
  </script>
268
220
  ```
269
221
 
270
- **New in v2.0:**
222
+ **Available State Utilities:**
271
223
 
272
- - **Automatic Reactivity**: `count++`, `count += 5`, and direct assignments automatically trigger re-renders
273
- - **`$state`**: Direct access to component state within scripts
274
- - **`$setState(updates)`**: Explicit state updates (merges with existing state)
224
+ - Direct assignment: `count++`, `name = "New"`
225
+ - `$setState(updates)`: Merge updates into state
226
+ - `$getState()`: Access state in external modules
275
227
 
276
228
  ### Event Handling
277
229
 
278
- Multiple ways to handle events:
230
+ Attach event handlers directly to elements:
279
231
 
280
232
  ```html
281
233
  <!-- Method reference -->
282
- <button onclick="handleClick">Click me: {count}</button>
234
+ <button onclick="handleClick(event)">Click me</button>
283
235
 
284
236
  <!-- Function with arguments -->
285
- <button onclick="addItem('Hello', 123)">Add Item</button>
237
+ <button onclick="addItem('apple', 5)">Add Item</button>
286
238
 
287
239
  <!-- Inline arrow function -->
288
- <button onclick="(e) => console.log(e.target)">Log Target</button>
289
-
290
- <!-- Component communication with event bus (v2.0) -->
291
- <button onclick="notifyOthers">Emit Event</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
+ />
292
251
 
293
252
  <script>
294
- let count = 0;
295
253
  let items = [];
254
+ let isValid = false;
296
255
 
297
256
  const handleClick = (event) => {
298
- console.log("Clicked!", event);
299
- count++;
257
+ console.log("Button clicked!", event);
300
258
  };
301
259
 
302
- const addItem = (name, value) => {
303
- items = [...items, { name, value }];
260
+ const addItem = (name, quantity) => {
261
+ items = [...items, { name, quantity }];
262
+ console.log("Items:", items);
304
263
  };
305
264
 
306
- // v2.0: Use $emit to send events to other components
307
- const notifyOthers = () => {
308
- $emit("item-added", { count, timestamp: Date.now() });
265
+ const validateInput = (e) => {
266
+ const value = e.target.value;
267
+ isValid = value.length >= 3;
309
268
  };
310
269
 
311
- // v2.0: Listen for events from other components
312
- $listen("user-logged-in", (data) => {
313
- console.log("User logged in:", data);
314
- count = 0; // Reset count
315
- });
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);
277
+ };
316
278
  </script>
317
279
  ```
318
280
 
319
- **New in v2.0:**
320
-
321
- - **`$emit(eventName, data)`**: Send events to other components via global event bus
322
- - **`$listen(eventName, callback)`**: Listen for events from any component
323
- - **Automatic Cleanup**: Event listeners are automatically removed when component is disconnected
324
-
325
281
  ### Data Binding
326
282
 
327
- LadrillosJS supports both one-way and two-way data binding:
283
+ #### One-Way Binding (Display Data)
328
284
 
329
- #### One-Way Binding (Template Interpolation)
285
+ Use curly braces `{}` to display state in your template:
330
286
 
331
287
  ```html
332
288
  <div>
333
289
  <h1>{title}</h1>
334
290
  <p>{user.name} - {user.email}</p>
335
- <span>Items: {items.length}</span>
291
+ <span>Total: {items.length} items</span>
292
+ <p>Formatted: {formatPrice(price)}</p>
336
293
  </div>
337
294
 
338
295
  <script>
339
296
  let title = "My App";
340
297
  let user = { name: "John", email: "john@example.com" };
341
- let items = [1, 2, 3];
298
+ let items = ["apple", "banana", "orange"];
299
+ let price = 29.99;
300
+
301
+ const formatPrice = (value) => `$${value.toFixed(2)}`;
342
302
  </script>
343
303
  ```
344
304
 
345
- #### Two-Way Binding (v2.0 Enhanced)
305
+ #### Two-Way Binding (Form Inputs)
346
306
 
347
- Use the `$bind` prefix to create automatic two-way bindings with form inputs:
307
+ Use the `$bind` attribute for automatic synchronization between inputs and state:
348
308
 
349
309
  ```html
350
310
  <div>
351
- <h2>Hello, {$name}!</h2>
352
- <input type="text" $bind="name" placeholder="Enter your name" />
311
+ <h2>Hello, {name}!</h2>
312
+ <input type="text" $bind="name" placeholder="Your name" />
353
313
 
354
- <p>Email: {$email}</p>
314
+ <p>Email: {email}</p>
355
315
  <input type="email" $bind="email" />
356
316
 
357
- <p>Bio: {$bio}</p>
317
+ <p>Bio: {bio}</p>
358
318
  <textarea $bind="bio"></textarea>
359
319
 
360
- <p>Country: {$country}</p>
320
+ <p>Country: {country}</p>
361
321
  <select $bind="country">
362
322
  <option value="us">United States</option>
363
323
  <option value="uk">United Kingdom</option>
364
324
  <option value="ca">Canada</option>
365
325
  </select>
326
+
327
+ <label>
328
+ <input type="checkbox" $bind="subscribe" />
329
+ Subscribe to newsletter: {subscribe}
330
+ </label>
366
331
  </div>
367
332
 
368
333
  <script>
369
- // Variables with $bind are automatically synced with inputs
370
- let $name = "World";
371
- let $email = "";
372
- let $bio = "";
373
- let $country = "us";
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;
374
340
  </script>
375
341
  ```
376
342
 
377
- **New in v2.0:**
378
-
379
- - **`$bind` attribute**: Simplified two-way binding syntax
380
- - **Automatic State Sync**: Input changes automatically update component state
381
- - **All Input Types**: Works with text inputs, textareas, selects, checkboxes, and radio buttons
382
- - **Nested Paths**: Supports nested object bindings like `$bind="user.email"`
383
-
384
343
  ### Conditional Rendering
385
344
 
386
- Control element visibility with conditional directives:
345
+ Show or hide elements based on conditions:
387
346
 
388
347
  ```html
389
348
  <div>
390
- <h1>Shopping Cart ({items.length} items)</h1>
349
+ <h1>Shopping Cart</h1>
391
350
 
392
- <div data-if="items.length === 0">
393
- <p>Your cart is empty</p>
394
- </div>
351
+ <!-- Simple condition -->
352
+ <p $if="{items.length === 0}">Your cart is empty</p>
395
353
 
396
- <div data-else-if="items.length < 3">
397
- <p>You have a few items</p>
354
+ <!-- Multiple conditions -->
355
+ <div $if="{items.length > 0 && items.length < 5}">
356
+ <p>You have {items.length} items</p>
398
357
  </div>
399
358
 
400
- <div data-else>
401
- <p>You have many items!</p>
359
+ <div $else-if="{items.length >= 5}">
360
+ <p>Your cart is full! ({items.length} items)</p>
402
361
  </div>
403
362
 
404
- <button data-if="!isLoggedIn" onclick="login">Login</button>
405
- <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>
406
373
  </div>
407
374
 
408
375
  <script>
409
- let items = ["apple", "banana"];
376
+ let items = [];
410
377
  let isLoggedIn = false;
378
+ let user = null;
411
379
 
412
380
  const login = () => {
413
381
  isLoggedIn = true;
382
+ user = { name: "John", role: "Admin" };
414
383
  };
415
384
 
416
385
  const logout = () => {
417
386
  isLoggedIn = false;
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
+ }
418
396
  };
419
397
  </script>
420
398
  ```
421
399
 
422
400
  **Conditional Directives:**
423
401
 
424
- - **`data-if="expression"`**: Show element if expression is truthy
425
- - **`data-else-if="expression"`**: Chain multiple conditions
426
- - **`data-else`**: Fallback when all previous conditions are false
402
+ - `$if="{expression}"`: Show if expression is truthy
403
+ - `$else-if="{expression}"`: Chain multiple conditions
404
+ - `$else`: Fallback when previous conditions are false
427
405
 
428
406
  ### Slots
429
407
 
430
- Content projection using slots:
408
+ Project content from parent to child components:
431
409
 
432
410
  ```html
433
411
  <!-- card.html -->
@@ -444,448 +422,393 @@ Content projection using slots:
444
422
  </div>
445
423
  </div>
446
424
 
447
- <!-- Usage -->
448
- <my-card>
449
- <h2 slot="header">User Profile</h2>
450
- <p>This goes in the default slot</p>
451
- <button slot="footer">Save</button>
452
- </my-card>
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>
453
441
  ```
454
442
 
455
- ## Advanced Features
456
-
457
- ### External Scripts
458
-
459
- Load external JavaScript with components and bind them to the component context:
443
+ **Usage:**
460
444
 
461
445
  ```html
462
- <!-- With 'bind' attribute for component context -->
463
- <script src="./helpers.js" bind></script>
464
-
465
- <!-- ES modules with bind -->
466
- <script src="./component-logic.js" type="module" bind></script>
467
-
468
- <!-- Regular external script (global scope) -->
469
- <script src="https://cdn.example.com/library.js"></script>
470
- ```
471
-
472
- For modules with `bind`, export a default function that receives the component context:
473
-
474
- ```javascript
475
- // component-logic.js
476
- export default function () {
477
- // 'this' refers to the component instance
478
- // Access component utilities
479
- const { $state, $setState, $emit, $listen } = this;
480
-
481
- this.formatDate = (date) => {
482
- return new Intl.DateTimeFormat("en-US").format(date);
483
- };
484
-
485
- this.loadData = async () => {
486
- const response = await fetch("/api/data");
487
- const data = await response.json();
488
- $setState({ data });
489
- };
490
-
491
- // Listen for events from other components
492
- $listen("refresh-data", () => {
493
- this.loadData();
494
- });
495
-
496
- // Called automatically if defined
497
- if (this.init) {
498
- this.init();
499
- }
500
- }
446
+ <my-card>
447
+ <h2 slot="header">User Profile</h2>
448
+ <p>Name: John Doe</p>
449
+ <p>Email: john@example.com</p>
450
+ <button slot="footer">Save Changes</button>
451
+ </my-card>
501
452
  ```
502
453
 
503
- **New in v2.0:**
504
-
505
- - **Better Context Binding**: External scripts get full access to component utilities
506
- - **`$emit` and `$listen`**: Available in external scripts for event communication
507
- - **Automatic Initialization**: Functions are auto-attached to component context
454
+ ## Advanced Features
508
455
 
509
456
  ### Global Event Bus
510
457
 
511
- **New in v2.0:** The global event bus enables cross-component communication without prop drilling or shared state.
458
+ The global event bus enables communication between components without prop drilling:
512
459
 
513
460
  ```javascript
514
- // In any component script
515
- // Emit an event
461
+ // Emit events to other components
516
462
  $emit("user-logged-in", { userId: 123, username: "john" });
517
463
 
518
- // Listen for events
464
+ // Listen for events from any component
519
465
  $listen("user-logged-in", (data) => {
520
466
  console.log(`User ${data.username} logged in`);
521
- // Update local state
522
467
  isLoggedIn = true;
523
468
  currentUser = data;
524
469
  });
525
470
  ```
526
471
 
527
- #### Example: Header & Login Components
472
+ **Example: Cross-Component Communication**
528
473
 
529
474
  ```html
530
475
  <!-- header.html -->
531
476
  <header>
532
- <span data-if="isLoggedIn">Welcome, {username}!</span>
533
- <button data-else onclick="showLogin">Login</button>
477
+ <span $if="{isLoggedIn}">Welcome, {username}!</span>
478
+ <button $else onclick="requestLogin()">Login</button>
534
479
  </header>
535
480
 
536
481
  <script>
537
482
  let isLoggedIn = false;
538
483
  let username = "";
539
484
 
540
- // Listen for login event from other components
541
485
  $listen("user-logged-in", (user) => {
486
+ console.log("User logged in:", user);
542
487
  isLoggedIn = true;
543
488
  username = user.username;
544
489
  });
545
490
 
546
- const showLogin = () => {
547
- $emit("show-login-modal");
491
+ const requestLogin = () => {
492
+ $emit("show-login-dialog");
548
493
  };
549
494
  </script>
550
495
  ```
551
496
 
552
497
  ```html
553
498
  <!-- login-form.html -->
554
- <form onsubmit="handleLogin">
499
+ <form onsubmit="handleLogin(event)" $if="{showLoginDialog}">
555
500
  <input type="text" $bind="username" placeholder="Username" />
556
501
  <input type="password" $bind="password" placeholder="Password" />
557
502
  <button type="submit">Login</button>
558
503
  </form>
559
504
 
560
505
  <script>
561
- let $username = "";
562
- let $password = "";
506
+ let showLoginDialog = false;
507
+
508
+ $listen("show-login-dialog", () => {
509
+ showLoginDialog = true;
510
+ });
563
511
 
564
512
  const handleLogin = (e) => {
565
513
  e.preventDefault();
566
-
567
- // Emit login success event
568
- $emit("user-logged-in", {
569
- userId: 123,
570
- username: $username,
571
- });
572
-
573
- // Clear form
574
- $username = "";
575
- $password = "";
514
+ $emit("user-logged-in", { userId: 123, username });
515
+ username = "";
516
+ password = "";
517
+ showLoginDialog = false;
576
518
  };
577
519
  </script>
578
520
  ```
579
521
 
580
- **Event Bus Benefits:**
522
+ ### External Scripts
523
+
524
+ LadrillosJS supports three ways to include external JavaScript:
525
+
526
+ #### 1. Component-Scoped Scripts (Default)
581
527
 
582
- - **No Prop Drilling**: Components can communicate directly
583
- - **Decoupled Architecture**: Components don't need to know about each other
584
- - **Automatic Cleanup**: Listeners are removed when components disconnect
585
- - **Promise Support**: `$emit` returns a promise when listeners are async
528
+ Regular script tags execute within the component's context and have access to component state and utilities:
586
529
 
587
- ### Removed: Global State Stores
530
+ ```html
531
+ <!-- alert-button.html -->
532
+ <button onclick="increaseCount()">{title}: {count}</button>
588
533
 
589
- **Breaking Change in v2.0:** The `createStore` API has been removed in favor of the more powerful global event bus pattern. Instead of shared stores, use the event bus for cross-component communication:
534
+ <!-- This script runs in the component context -->
535
+ <script src="./alert.js"></script>
536
+ ```
590
537
 
591
- **Before (v1.x with stores):**
538
+ **alert.js:**
592
539
 
593
540
  ```javascript
594
- import { createStore } from "ladrillosjs";
541
+ // Variables and functions are available to the component
542
+ let count = 0;
543
+ const title = "Button Count";
595
544
 
596
- export const userStore = createStore({
597
- user: null,
598
- isAuthenticated: false,
599
- });
545
+ const increaseCount = () => {
546
+ count++; // Updates component state
547
+ };
548
+ ```
600
549
 
601
- userStore.subscribe((state) => {
602
- this.setState(state);
603
- });
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>
604
564
  ```
605
565
 
606
- **After (v2.0 with event bus):**
566
+ **side.js:**
607
567
 
608
568
  ```javascript
609
- // Emit events to notify components of changes
610
- $emit("user-updated", { user: userData, isAuthenticated: true });
611
-
612
- // Listen for changes in components that need them
613
- $listen("user-updated", ({ user, isAuthenticated }) => {
614
- // Update local component state
615
- currentUser = user;
616
- loggedIn = isAuthenticated;
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
+ }
617
582
  });
618
583
  ```
619
584
 
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>
610
+ ```
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
+
620
618
  ### Shadow DOM
621
619
 
622
- Components use Shadow DOM by default for style encapsulation. To disable:
620
+ Components use Shadow DOM by default for style encapsulation:
623
621
 
624
622
  ```javascript
625
- // Disable Shadow DOM for a component
626
- await registerComponent("my-component", "./my-component.html", false);
627
-
628
- // With Shadow DOM enabled (default)
623
+ // Shadow DOM enabled (default)
624
+ await registerComponent("isolated-widget", "./widget.html");
629
625
  await registerComponent("isolated-widget", "./widget.html", true);
626
+
627
+ // Shadow DOM disabled
628
+ await registerComponent("global-styles", "./global.html", false);
630
629
  ```
631
630
 
632
631
  **Shadow DOM Benefits:**
633
632
 
634
633
  - **Style Isolation**: Component styles don't leak to global scope
635
- - **Encapsulation**: Internal DOM structure is hidden from parent
636
- - **Cleaner DOM**: Styles and scripts are scoped to component
634
+ - **Encapsulation**: Internal DOM is hidden from parent
635
+ - **Clean Separation**: Each component has its own styling context
637
636
 
638
637
  **When to Disable:**
639
638
 
640
- - Need global CSS styles to apply
641
- - Using third-party CSS frameworks
642
- - Debugging with browser dev tools (easier without shadow DOM)
639
+ - Need to apply global CSS frameworks (Bootstrap, Tailwind)
640
+ - Using third-party libraries that expect normal DOM
641
+ - Easier debugging without shadow boundaries
643
642
 
644
643
  ### Performance & Caching
645
644
 
646
- **New in v2.0:** LRU (Least Recently Used) caching for improved performance:
645
+ LadrillosJS includes built-in performance optimizations:
647
646
 
648
647
  #### Component Caching
649
648
 
650
- - **25 Components**: Automatically caches up to 25 component HTML files
651
- - **LRU Eviction**: Least recently used components are removed when cache is full
652
- - **Faster Re-renders**: Cached components load instantly on re-use
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
653
652
 
654
653
  #### Function Caching
655
654
 
656
- - **100 Functions**: Caches up to 100 compiled template expressions
657
- - **Prevents Memory Leaks**: Reuses Function objects for identical expressions
658
- - **Example**: `{formatName("John")}` compiles once and is reused on every render
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
659
658
 
660
- **Performance Improvements:**
659
+ **Performance Tips:**
661
660
 
662
- - Reduced HTTP requests for components
663
- - Faster template rendering with cached functions
664
- - Optimized state updates with change detection
665
- - Efficient re-rendering with minimal DOM updates
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
666
665
 
667
666
  ## API Reference
668
667
 
669
- ### Component Methods
670
-
671
- | Method | Description |
672
- | ------------------- | ------------------------------------------------------------------------- |
673
- | `setState(partial)` | Update component state and trigger re-render (merges with existing state) |
674
-
675
- ### Framework Utilities (v2.0)
676
-
677
- Available within component `<script>` tags:
678
-
679
- | Utility | Description |
680
- | ------------------------------ | -------------------------------------------------------------------- |
681
- | `$state` | Direct access to component state object |
682
- | `$setState(updates)` | Update state explicitly (alternative to direct assignments) |
683
- | `$emit(eventName, data?)` | Emit event to other components via global event bus |
684
- | `$listen(eventName, callback)` | Listen for events from other components (auto-cleanup on disconnect) |
685
- | `$querySelector(selector)` | Query element within component's DOM (respects Shadow DOM) |
686
- | `$querySelectorAll(selector)` | Query all matching elements within component |
687
-
688
- ### Component Attributes
689
-
690
- | Attribute | Description |
691
- | --------------------------- | ----------------------------------------------------------------------------------- |
692
- | `$bind="variableName"` | Create two-way data binding with form inputs |
693
- | `data-if="expression"` | Conditionally render element if expression is truthy |
694
- | `data-else-if="expression"` | Chain multiple conditional expressions |
695
- | `data-else` | Render when all previous conditions are false |
696
- | `onclick="handler"` (etc.) | Attach event handlers (supports method names, inline functions, or arrow functions) |
697
-
698
668
  ### Registration Functions
699
669
 
700
- | Function | Description |
701
- | ---------------------------------------------- | ------------------------------------------------------ |
702
- | `registerComponent(name, path, useShadowDOM?)` | Register a single component (returns Promise) |
703
- | `registerComponents(components)` | **Coming soon** - Register multiple components at once |
704
-
705
- ## Examples
706
-
707
- ### Component Communication
708
-
709
- **v2.0 uses the global event bus instead of custom events:**
710
-
711
- ```html
712
- <!-- parent.html -->
713
- <div>
714
- <h2>Parent Component</h2>
715
- <p>Messages received: {messageCount}</p>
716
- <child-component></child-component>
717
- </div>
718
-
719
- <script>
720
- let messageCount = 0;
721
-
722
- // Listen for events from child
723
- $listen("child-message", (data) => {
724
- console.log("Received from child:", data);
725
- messageCount++;
726
- });
727
- </script>
670
+ ```typescript
671
+ registerComponent(name: string, path: string, useShadowDOM?: boolean): Promise<void>
672
+ registerComponents(components: Array<{name, path, useShadowDOM?}>): Promise<void>
728
673
  ```
729
674
 
730
- ```html
731
- <!-- child.html -->
732
- <div>
733
- <h3>Child Component</h3>
734
- <button onclick="sendMessage">Send Message to Parent</button>
735
- </div>
675
+ ### Component Utilities
736
676
 
737
- <script>
738
- let count = 0;
677
+ Available within component `<script>` tags:
739
678
 
740
- const sendMessage = () => {
741
- count++;
742
- // Emit event that parent (or any component) can listen to
743
- $emit("child-message", {
744
- message: `Hello from child! (${count})`,
745
- timestamp: Date.now(),
746
- });
747
- };
748
- </script>
749
- ```
679
+ ```typescript
680
+ // State management
681
+ $getState(): object // Access component state
682
+ $setState(updates: object): void // Update state and re-render
750
683
 
751
- ### Dynamic Component Creation
684
+ // Event bus
685
+ $emit(eventName: string, data?: any): void
686
+ $listen(eventName: string, callback: (data?) => void): () => void
752
687
 
753
- ```javascript
754
- // Create components programmatically
755
- const createCard = (userData) => {
756
- const card = document.createElement("user-card");
757
- card.setAttribute("user-id", userData.id);
758
- card.setAttribute("name", userData.name);
759
- card.setAttribute("email", userData.email);
760
- document.querySelector("#user-list").appendChild(card);
761
- };
688
+ // DOM queries (respects Shadow DOM boundaries)
689
+ $querySelector(selector: string): Element | null
690
+ $querySelectorAll(selector: string): NodeListOf<Element>
762
691
 
763
- // Fetch and create multiple components
764
- fetch("/api/users")
765
- .then((res) => res.json())
766
- .then((users) => users.forEach(createCard));
692
+ // Reactive variables (for external modules)
693
+ $reactive(name: string, initialValue: any): (value: any) => void
767
694
  ```
768
695
 
769
- ### Passing Complex Data
770
-
771
- Use JSON.stringify for passing objects/arrays as attributes:
696
+ ### Component Attributes
772
697
 
773
698
  ```html
774
- <!-- In parent component -->
775
- <script>
776
- const user = { id: 1, name: "John", roles: ["admin", "user"] };
777
- const items = [1, 2, 3, 4, 5];
778
-
779
- // Create HTML with stringified data
780
- const cardHtml = `
781
- <user-card data-user='${JSON.stringify(user)}'></user-card>
782
- <list-component data-items='${JSON.stringify(items)}'></list-component>
783
- `;
784
-
785
- // Or use the built-in stringify helper in v2.0
786
- const cardHtml2 = `
787
- <user-card data-user="${this.stringify(user)}"></user-card>
788
- `;
789
- </script>
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 -->
790
717
  ```
791
718
 
792
- ```html
793
- <!-- In child component (user-card.html) -->
794
- <div>
795
- <h3>{user.name}</h3>
796
- <p>ID: {user.id}</p>
797
- <p>Roles: {user.roles.join(", ")}</p>
798
- </div>
799
-
800
- <script>
801
- // data-user is automatically parsed from JSON
802
- let user = this.state["data-user"];
803
- </script>
804
- ```
719
+ ### Template Syntax
805
720
 
806
- ## Migration Guide (v1.x to v2.0)
807
-
808
- ### Breaking Changes
809
-
810
- 1. **Global State Stores Removed**
811
-
812
- - **Before:** `createStore()` API
813
- - **After:** Use global event bus with `$emit` and `$listen`
814
-
815
- 2. **Component Registration**
816
-
817
- - **Before:** `registerComponent()` was synchronous
818
- - **After:** Returns a Promise, use `await` or `.then()`
819
-
820
- 3. **Framework Utilities**
821
-
822
- - **Before:** `this.emit()`, `this.listen()`, `this.setState()`
823
- - **After:** Use `$emit()`, `$listen()`, `$setState()` in scripts (legacy methods still available for compatibility)
721
+ ```html
722
+ <!-- Variable interpolation -->
723
+ {variableName} {object.property} {array[0]}
824
724
 
825
- 4. **Two-Way Binding**
826
- - **Before:** No built-in support (manual implementation)
827
- - **After:** Use `$bind` attribute for automatic two-way binding
725
+ <!-- Function calls -->
726
+ {functionName(arg1, arg2)} {object.method()}
828
727
 
829
- ### New Features to Adopt
728
+ <!-- Expressions -->
729
+ {count + 1} {isActive ? 'Yes' : 'No'} {items.length > 0 ? 'Has items' : 'Empty'}
730
+ ```
830
731
 
831
- - ✅ Use `$bind` for two-way data binding instead of manual input handling
832
- - ✅ Replace store subscriptions with `$emit`/`$listen` event patterns
833
- - ✅ Direct variable assignments now trigger reactivity (`count++`)
834
- - ✅ Use `$state` for direct state access in scripts
835
- - ✅ Leverage conditional directives: `data-if`, `data-else-if`, `data-else`
732
+ ## Development
836
733
 
837
- ## Contributing
734
+ ### Prerequisites
838
735
 
839
- Contributions are welcome! Please feel free to submit a Pull Request.
736
+ - **Node.js**: v20.19+ or v22.12+
737
+ - **npm**: v9+ (comes with Node.js)
840
738
 
841
- ### Development
739
+ ### Setup
842
740
 
843
741
  ```bash
742
+ # Clone the repository
743
+ git clone https://github.com/drubiodev/LadrillosJS.git
744
+ cd LadrillosJS
745
+
844
746
  # Install dependencies
845
747
  npm install
846
748
 
847
- # Run development server with hot reload
749
+ # Start development server
848
750
  npm run dev
849
751
 
752
+ # Build the library
753
+ npm run build
754
+
850
755
  # Run tests
851
756
  npm test
852
757
 
853
758
  # Run tests with coverage
854
759
  npm run test:coverage
855
-
856
- # Build the library
857
- npm run build
858
-
859
- # Build TypeScript types
860
- npm run build:types
861
760
  ```
862
761
 
863
762
  ### Project Structure
864
763
 
865
764
  ```
866
- src/
867
- ├── index.ts # Main entry point
868
- ├── core/
869
- │ ├── main.ts # Core Ladrillos class
870
- │ ├── webcomponent.ts # Web component definition
871
- │ ├── componentParser.ts # Component file parser
872
- │ ├── componentSource.ts # Component fetching with cache
873
- │ ├── eventBus.ts # Global event bus
874
- │ ├── css/
875
- │ │ └── cssParser.ts
876
- ├── html/
877
- │ │ ├── htmlparser.ts
878
- │ │ └── htmlRenderer.ts
879
- │ └── js/
880
- └── scriptParser.ts
881
- ├── cache/
882
- │ ├── index.ts # LRU cache for components
883
- └── functionCache.ts # LRU cache for compiled functions
884
- ├── types/
885
- └── LadrilloTypes.ts # TypeScript type definitions
886
- └── utils/
887
- ├── logger.ts # Logging utilities
888
- └── regex.ts # Regex patterns
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
+ ```
802
+
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
889
812
  ```
890
813
 
891
814
  ## License
@@ -896,4 +819,4 @@ MIT License - see [LICENSE](LICENSE) file for details.
896
819
 
897
820
  **LadrillosJS v2.0** - Built with ❤️ by [Daniel Rubio](https://github.com/drubiodev)
898
821
 
899
- Rewritten in TypeScript for better performance, developer experience, and maintainability.
822
+ 🌟 [GitHub](https://github.com/drubiodev/LadrillosJS) 📦 [NPM](https://www.npmjs.com/package/ladrillosjs) 📖 [Documentation](https://github.com/drubiodev/LadrillosJS/blob/main/README.md)