lego-dom 1.3.4 → 1.5.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 (93) hide show
  1. package/CHANGELOG.md +72 -1
  2. package/main.js +48 -17
  3. package/main.min.js +2 -2
  4. package/package.json +1 -1
  5. package/parse-lego.js +2 -2
  6. package/vite-plugin.js +0 -14
  7. package/.github/workflows/deploy-docs.yml +0 -56
  8. package/.legodom +0 -87
  9. package/docs/.vitepress/config.js +0 -161
  10. package/docs/api/config.md +0 -95
  11. package/docs/api/define.md +0 -58
  12. package/docs/api/directives.md +0 -50
  13. package/docs/api/globals.md +0 -29
  14. package/docs/api/index.md +0 -30
  15. package/docs/api/lifecycle.md +0 -40
  16. package/docs/api/route.md +0 -37
  17. package/docs/api/vite-plugin.md +0 -58
  18. package/docs/contributing/01-welcome.md +0 -38
  19. package/docs/contributing/02-registry.md +0 -133
  20. package/docs/contributing/03-batcher.md +0 -110
  21. package/docs/contributing/04-reactivity.md +0 -87
  22. package/docs/contributing/05-caching.md +0 -59
  23. package/docs/contributing/06-init.md +0 -136
  24. package/docs/contributing/07-observer.md +0 -72
  25. package/docs/contributing/08-snap.md +0 -140
  26. package/docs/contributing/09-diffing.md +0 -69
  27. package/docs/contributing/10-studs.md +0 -78
  28. package/docs/contributing/11-scanner.md +0 -117
  29. package/docs/contributing/12-render.md +0 -138
  30. package/docs/contributing/13-directives.md +0 -243
  31. package/docs/contributing/14-events.md +0 -57
  32. package/docs/contributing/15-router.md +0 -57
  33. package/docs/contributing/16-state.md +0 -47
  34. package/docs/contributing/17-legodom.md +0 -48
  35. package/docs/contributing/index.md +0 -24
  36. package/docs/examples/form.md +0 -42
  37. package/docs/examples/index.md +0 -104
  38. package/docs/examples/routing.md +0 -409
  39. package/docs/examples/sfc-showcase.md +0 -34
  40. package/docs/examples/todo-app.md +0 -383
  41. package/docs/guide/cdn-usage.md +0 -354
  42. package/docs/guide/components.md +0 -418
  43. package/docs/guide/directives.md +0 -539
  44. package/docs/guide/directory-structure.md +0 -248
  45. package/docs/guide/faq.md +0 -210
  46. package/docs/guide/getting-started.md +0 -262
  47. package/docs/guide/index.md +0 -88
  48. package/docs/guide/lifecycle.md +0 -525
  49. package/docs/guide/quick-start.md +0 -49
  50. package/docs/guide/reactivity.md +0 -415
  51. package/docs/guide/routing.md +0 -334
  52. package/docs/guide/server-side.md +0 -134
  53. package/docs/guide/sfc.md +0 -464
  54. package/docs/guide/templating.md +0 -388
  55. package/docs/index.md +0 -160
  56. package/docs/public/logo.svg +0 -17
  57. package/docs/router/basic-routing.md +0 -103
  58. package/docs/router/cold-entry.md +0 -91
  59. package/docs/router/history.md +0 -69
  60. package/docs/router/index.md +0 -73
  61. package/docs/router/resolver.md +0 -74
  62. package/docs/router/surgical-swaps.md +0 -134
  63. package/docs/tutorial/01-project-setup.md +0 -152
  64. package/docs/tutorial/02-your-first-component.md +0 -226
  65. package/docs/tutorial/03-adding-routes.md +0 -279
  66. package/docs/tutorial/04-multi-page-app.md +0 -329
  67. package/docs/tutorial/05-state-and-globals.md +0 -285
  68. package/docs/tutorial/index.md +0 -40
  69. package/examples/vite-app/README.md +0 -71
  70. package/examples/vite-app/index.html +0 -42
  71. package/examples/vite-app/package.json +0 -18
  72. package/examples/vite-app/src/app.css +0 -3
  73. package/examples/vite-app/src/app.js +0 -29
  74. package/examples/vite-app/src/components/app-navbar.lego +0 -34
  75. package/examples/vite-app/src/components/customers/customer-details.lego +0 -24
  76. package/examples/vite-app/src/components/customers/customer-orders.lego +0 -21
  77. package/examples/vite-app/src/components/customers/order-list.lego +0 -55
  78. package/examples/vite-app/src/components/greeting-card.lego +0 -41
  79. package/examples/vite-app/src/components/sample-component.lego +0 -75
  80. package/examples/vite-app/src/components/shells/customers-shell.lego +0 -21
  81. package/examples/vite-app/src/components/side-menu.lego +0 -46
  82. package/examples/vite-app/src/components/todo-list.lego +0 -239
  83. package/examples/vite-app/src/components/widgets/user-card.lego +0 -27
  84. package/examples/vite-app/vite.config.js +0 -22
  85. package/tests/error.test.js +0 -74
  86. package/tests/main.test.js +0 -103
  87. package/tests/memory.test.js +0 -68
  88. package/tests/monitoring.test.js +0 -74
  89. package/tests/naming.test.js +0 -74
  90. package/tests/parse-lego.test.js +0 -65
  91. package/tests/security.test.js +0 -67
  92. package/tests/server.test.js +0 -114
  93. package/tests/syntax.test.js +0 -67
@@ -1,88 +0,0 @@
1
- # What is Lego?
2
-
3
- LegoDOM is a tiny, zero-dependency JavaScript library for building reactive Web Components directly in the browser.
4
-
5
- ## The Philosophy
6
-
7
- LegoDOM is built on a simple belief: **the DOM is not your enemy**.
8
-
9
- Modern frameworks introduced virtual DOMs and compilation steps to solve problems that arose from trying to make the DOM do things it wasn't designed for. Lego takes a different approach—it embraces the DOM and Web Components as they were intended to be used.
10
-
11
- ## Key Principles
12
-
13
- ### 1. Mental Model Simplicity
14
-
15
- There are no new concepts to learn. If you know:
16
- - HTML
17
- - JavaScript objects
18
- - Basic DOM events
19
-
20
- You already know Lego.
21
-
22
- ### 2. No Build Step Required
23
-
24
- Drop a `<script>` tag in your HTML and you're ready to go. Build tools are optional, not mandatory.
25
-
26
- ### 3. True Reactivity
27
-
28
- Change an object → the DOM updates. That's it. No `setState`, no `dispatch`, no `computed properties` to configure.
29
-
30
- ```js
31
- // This just works
32
- component._studs.count++;
33
- ```
34
-
35
- ### 4. Web Standards First
36
-
37
- Lego uses:
38
- - **Web Components** - Standard custom elements
39
- - **Shadow DOM** - Native encapsulation
40
- - **ES6 Proxies** - For reactivity
41
- - **Template literals** - For templating
42
-
43
- No proprietary APIs. Everything is built on web standards.
44
-
45
- ## When to Use Lego
46
-
47
- ### ✅ Lego is Great For:
48
-
49
- - **Small to medium applications** where framework overhead isn't worth it
50
- - **Embedded widgets** that need to work anywhere
51
- - **Progressive enhancement** of existing sites
52
- - **Learning** how reactive systems work under the hood
53
- - **Projects** where you want full control and no dependencies
54
-
55
- ### ⚠️ Consider Alternatives If:
56
-
57
- - You need a massive ecosystem of pre-built components
58
- - Your team is already invested in React/Vue/Angular
59
- - You need SSR (Server-Side Rendering) out of the box
60
- - You're building something extremely complex with hundreds of components
61
-
62
- ## How Small Is It?
63
-
64
- The core library (`main.js`) is **under 500 lines** of well-commented JavaScript.
65
-
66
- - **No dependencies** - Zero `node_modules` bloat
67
- - **~22KB** - Unminified, human-readable code
68
- - **~7KB** - Minified and gzipped
69
-
70
- Compare that to:
71
- - Vue 3: ~33KB (minified + gzipped)
72
- - React + ReactDOM: ~40KB (minified + gzipped)
73
- - Angular: ~100KB+ (minified + gzipped)
74
-
75
- ## What Makes It Different?
76
-
77
- | Aspect | Lego | Traditional Frameworks |
78
- |--------|--------|----------------------|
79
- | **Reactivity** | Direct object mutation | setState / dispatch / ref() |
80
- | **Templates** | HTML with <code v-pre>[[ ]]</code> | JSX / template syntax |
81
- | **Styles** | Shadow DOM (native) | CSS-in-JS / scoped CSS |
82
- | **Build** | Optional | Required |
83
- | **Learning Curve** | Hours | Days/Weeks |
84
- |**Philosophy** | Embrace the platform | Abstract the platform |
85
-
86
- ## Next Steps
87
-
88
- Ready to dive in? Head to the [Getting Started](/guide/getting-started) guide to build your first component in under 5 minutes.
@@ -1,525 +0,0 @@
1
- # Lifecycle Hooks
2
-
3
- Learn about component lifecycle hooks in Lego.
4
-
5
- ## Overview
6
-
7
- Components have three lifecycle hooks:
8
-
9
- - `mounted()` - Called after component is added to DOM
10
- - `updated()` - Called after state changes and re-render
11
- - `unmounted()` - Called when component is removed from DOM
12
-
13
- ## mounted()
14
-
15
- Called once when the component is first attached to the DOM.
16
-
17
- ### Usage
18
-
19
- ```js
20
- {
21
- data: null,
22
-
23
- mounted() {
24
- console.log('Component is now in the DOM');
25
- this.fetchData();
26
- },
27
-
28
- async fetchData() {
29
- this.data = await fetch('/api/data').then(r => r.json());
30
- }
31
- }
32
- ```
33
-
34
- ### Common Use Cases
35
-
36
- **Fetch Data:**
37
- ```js
38
- {
39
- mounted() {
40
- this.loadUserData();
41
- }
42
- }
43
- ```
44
-
45
- **Start Timers:**
46
- ```js
47
- {
48
- timer: null,
49
-
50
- mounted() {
51
- this.timer = setInterval(() => {
52
- this.tick();
53
- }, 1000);
54
- }
55
- }
56
- ```
57
-
58
- **Add Event Listeners:**
59
- ```js
60
- {
61
- mounted() {
62
- window.addEventListener('resize', this.handleResize.bind(this));
63
- }
64
- }
65
- ```
66
-
67
- **Initialize Third-Party Libraries:**
68
- ```js
69
- {
70
- mounted() {
71
- this.chart = new Chart(this.$element.shadowRoot.querySelector('canvas'), {
72
- type: 'bar',
73
- data: this.chartData
74
- });
75
- }
76
- }
77
- ```
78
-
79
- ## updated()
80
-
81
- Called after every state change and re-render.
82
-
83
- ### Usage
84
-
85
- ```js
86
- {
87
- count: 0,
88
-
89
- updated() {
90
- console.log('Component re-rendered, count is:', this.count);
91
- }
92
- }
93
- ```
94
-
95
- ### Common Use Cases
96
-
97
- **Track Changes:**
98
- ```js
99
- {
100
- previousValue: null,
101
- value: 0,
102
-
103
- updated() {
104
- if (this.value !== this.previousValue) {
105
- console.log('Value changed from', this.previousValue, 'to', this.value);
106
- this.previousValue = this.value;
107
- }
108
- }
109
- }
110
- ```
111
-
112
- **Update Third-Party Libraries:**
113
- ```js
114
- {
115
- chartData: [],
116
-
117
- updated() {
118
- if (this.chart) {
119
- this.chart.data = this.chartData;
120
- this.chart.update();
121
- }
122
- }
123
- }
124
- ```
125
-
126
- **Analytics:**
127
- ```js
128
- {
129
- updated() {
130
- if (window.gtag) {
131
- gtag('event', 'state_change', {
132
- component: 'my-component',
133
- count: this.count
134
- });
135
- }
136
- }
137
- }
138
- ```
139
-
140
- ::: warning Performance
141
- `updated()` runs on every state change. Keep it lightweight!
142
- :::
143
-
144
- ## unmounted()
145
-
146
- Called when the component is removed from the DOM.
147
-
148
- ### Usage
149
-
150
- ```js
151
- {
152
- unmounted() {
153
- console.log('Component is being removed');
154
- this.cleanup();
155
- }
156
- }
157
- ```
158
-
159
- ### Common Use Cases
160
-
161
- **Clear Timers:**
162
- ```js
163
- {
164
- timer: null,
165
-
166
- mounted() {
167
- this.timer = setInterval(() => this.tick(), 1000);
168
- },
169
-
170
- unmounted() {
171
- clearInterval(this.timer);
172
- }
173
- }
174
- ```
175
-
176
- **Remove Event Listeners:**
177
- ```js
178
- {
179
- handleResize: null,
180
-
181
- mounted() {
182
- this.handleResize = () => {
183
- this.width = window.innerWidth;
184
- };
185
- window.addEventListener('resize', this.handleResize);
186
- },
187
-
188
- unmounted() {
189
- window.removeEventListener('resize', this.handleResize);
190
- }
191
- }
192
- ```
193
-
194
- **Destroy Third-Party Instances:**
195
- ```js
196
- {
197
- chart: null,
198
-
199
- mounted() {
200
- this.chart = new Chart(...);
201
- },
202
-
203
- unmounted() {
204
- if (this.chart) {
205
- this.chart.destroy();
206
- }
207
- }
208
- }
209
- ```
210
-
211
- **Cancel Pending Requests:**
212
- ```js
213
- {
214
- controller: null,
215
-
216
- async fetchData() {
217
- this.controller = new AbortController();
218
- try {
219
- const data = await fetch('/api/data', {
220
- signal: this.controller.signal
221
- }).then(r => r.json());
222
- this.data = data;
223
- } catch (err) {
224
- if (err.name === 'AbortError') {
225
- console.log('Fetch aborted');
226
- }
227
- }
228
- },
229
-
230
- unmounted() {
231
- if (this.controller) {
232
- this.controller.abort();
233
- }
234
- }
235
- }
236
- }
237
- }
238
- ```
239
-
240
- ## Performance Hooks (Metrics)
241
-
242
- For advanced monitoring, LegoDOM provides global hooks in `Lego.config.metrics`. These run for **every** component.
243
-
244
- ### `onRenderStart` & `onRenderEnd`
245
-
246
- Useful for tracking how long renders take, which helps identify slow components.
247
-
248
- ```javascript
249
- Lego.config.metrics = {
250
- onRenderStart(el) {
251
- console.time(`render-${el.tagName}`);
252
- },
253
- onRenderEnd(el) {
254
- console.timeEnd(`render-${el.tagName}`);
255
- }
256
- };
257
- ```
258
-
259
- ### `onAllSettled` (New in v2.0)
260
-
261
- Called when the entire component tree (including Shadow DOM children) has finished its initial render pass. This is perfect for removing loading spinners or measuring "Time to Interactive".
262
-
263
- ```javascript
264
- /* main.js */
265
- Lego.init(document.body).then(() => {
266
- document.getElementById('global-loader').remove();
267
- });
268
- ```
269
-
270
- ## Lifecycle Flow
271
-
272
- ```
273
- 1. Component created (HTML element instantiated)
274
- 2. Shadow DOM attached
275
- 3. Template rendered
276
- 4. mounted() hook called → Component is interactive
277
- 5. User interaction / state change
278
- 6. Component re-rendered
279
- 7. updated() hook called
280
- 8. (repeat steps 5-7 as needed)
281
- 9. Component removed from DOM
282
- 10. unmounted() hook called → Cleanup
283
- ```
284
-
285
- ## Complete Example
286
-
287
- ```js
288
- {
289
- // State
290
- count: 0,
291
- timer: null,
292
- data: null,
293
-
294
- // Lifecycle: Component mounted
295
- mounted() {
296
- console.log('[mounted] Component added to DOM');
297
-
298
- // Fetch initial data
299
- this.fetchData();
300
-
301
- // Start interval
302
- this.timer = setInterval(() => {
303
- this.count++;
304
- }, 1000);
305
-
306
- // Add event listener
307
- this.handleKeyPress = (e) => {
308
- if (e.key === 'Escape') {
309
- this.reset();
310
- }
311
- };
312
- document.addEventListener('keydown', this.handleKeyPress);
313
- },
314
-
315
- // Lifecycle: Component updated
316
- updated() {
317
- console.log('[updated] Component re-rendered, count:', this.count);
318
-
319
- // Log when count reaches milestone
320
- if (this.count % 10 === 0) {
321
- console.log('Milestone:', this.count);
322
- }
323
- },
324
-
325
- // Lifecycle: Component unmounted
326
- unmounted() {
327
- console.log('[unmounted] Component removed from DOM');
328
-
329
- // Clear interval
330
- if (this.timer) {
331
- clearInterval(this.timer);
332
- }
333
-
334
- // Remove event listener
335
- document.removeEventListener('keydown', this.handleKeyPress);
336
- },
337
-
338
- // Methods
339
- async fetchData() {
340
- this.data = await fetch('/api/data').then(r => r.json());
341
- },
342
-
343
- reset() {
344
- this.count = 0;
345
- }
346
- }
347
- ```
348
-
349
- ## Best Practices
350
-
351
- ### 1. Initialize in mounted()
352
-
353
- Don't fetch data or start timers in the state object:
354
-
355
- ```js
356
- // ❌ Bad
357
- {
358
- data: fetch('/api/data').then(r => r.json()), // Executes immediately
359
- timer: setInterval(() => {}, 1000) // Starts before component exists
360
- }
361
-
362
- // ✅ Good
363
- {
364
- data: null,
365
- timer: null,
366
- mounted() {
367
- this.fetchData();
368
- this.timer = setInterval(() => {}, 1000);
369
- }
370
- }
371
- ```
372
-
373
- ### 2. Always Clean Up
374
-
375
- If you start something in `mounted()`, stop it in `unmounted()`:
376
-
377
- ```js
378
- {
379
- mounted() {
380
- this.timer = setInterval(...);
381
- window.addEventListener('resize', this.handleResize);
382
- },
383
-
384
- unmounted() {
385
- clearInterval(this.timer); // ✅
386
- window.removeEventListener('resize', this.handleResize); // ✅
387
- }
388
- }
389
- ```
390
-
391
- ### 3. Keep updated() Light
392
-
393
- `updated()` runs frequently—avoid heavy operations:
394
-
395
- ```js
396
- // ❌ Bad
397
- {
398
- updated() {
399
- this.expensiveCalculation(); // Runs on every change!
400
- }
401
- }
402
-
403
- // ✅ Good
404
- {
405
- updated() {
406
- // Only log or track
407
- console.log('State changed');
408
- }
409
- }
410
- ```
411
-
412
- ### 4. Guard Against Errors
413
-
414
- ```js
415
- {
416
- unmounted() {
417
- // Check before clearing
418
- if (this.timer) {
419
- clearInterval(this.timer);
420
- }
421
-
422
- // Check before destroying
423
- if (this.chart) {
424
- this.chart.destroy();
425
- }
426
- }
427
- }
428
- ```
429
-
430
- ## Common Patterns
431
-
432
- ### Loading State
433
-
434
- ```js
435
- {
436
- loading: true,
437
- data: null,
438
-
439
- async mounted() {
440
- try {
441
- this.data = await fetch('/api/data').then(r => r.json());
442
- } finally {
443
- this.loading = false;
444
- }
445
- }
446
- }
447
- ```
448
-
449
- ### Polling
450
-
451
- ```js
452
- {
453
- pollInterval: null,
454
-
455
- mounted() {
456
- this.poll();
457
- this.pollInterval = setInterval(() => this.poll(), 5000);
458
- },
459
-
460
- async poll() {
461
- this.data = await fetch('/api/status').then(r => r.json());
462
- },
463
-
464
- unmounted() {
465
- clearInterval(this.pollInterval);
466
- }
467
- }
468
- ```
469
-
470
- ### Scroll Position
471
-
472
- ```js
473
- {
474
- scrollY: 0,
475
- handleScroll: null,
476
-
477
- mounted() {
478
- this.handleScroll = () => {
479
- this.scrollY = window.scrollY;
480
- };
481
- window.addEventListener('scroll', this.handleScroll);
482
- },
483
-
484
- unmounted() {
485
- window.removeEventListener('scroll', this.handleScroll);
486
- }
487
- }
488
- ```
489
-
490
- ### Animation
491
-
492
- ```js
493
- {
494
- mounted() {
495
- const el = this.$element.shadowRoot.querySelector('.animated');
496
- el.classList.add('fade-in');
497
- }
498
- }
499
- ```
500
-
501
- ## Debugging Lifecycle
502
-
503
- Log lifecycle events to understand component behavior:
504
-
505
- ```js
506
- {
507
- mounted() {
508
- console.log('[LIFECYCLE] mounted');
509
- },
510
-
511
- updated() {
512
- console.log('[LIFECYCLE] updated');
513
- },
514
-
515
- unmounted() {
516
- console.log('[LIFECYCLE] unmounted');
517
- }
518
- }
519
- ```
520
-
521
- ## Next Steps
522
-
523
- - See [lifecycle examples](/examples/)
524
- - Learn about [component patterns](/guide/components)
525
- - Explore [state management](/guide/reactivity)
@@ -1,49 +0,0 @@
1
- # Quick Start
2
-
3
- The fastest way to get started with Lego is using the CDN. No build tools required!
4
-
5
- ## 1. Create an HTML file
6
-
7
- ```html
8
- <!DOCTYPE html>
9
- <html lang="en">
10
- <head>
11
- <meta charset="UTF-8">
12
- <title>Lego Quick Start</title>
13
- </head>
14
- <body>
15
- <!-- Interpolation works here too -->
16
- <hello-world name="Lego"></hello-world>
17
-
18
- <!-- 3. Define the template -->
19
- <template b-id="hello-world">
20
- <style>
21
- h1 { color: #646cff; }
22
- </style>
23
- <h1>Hello [[ name ]]!</h1>
24
- <button @click="count++">Count is [[ count ]]</button>
25
- </template>
26
-
27
- <!-- 4. Load Lego -->
28
- <script src="https://unpkg.com/lego-dom/main.js"></script>
29
-
30
- <!-- 5. Define logic & Init -->
31
- <script>
32
- document.querySelector('hello-world').state = {
33
- name: 'World',
34
- toggle() {
35
- this.name = this.name === 'World' ? 'Lego' : 'World';
36
- }
37
- };
38
-
39
- // Start the engine
40
- Lego.init();
41
- </script>
42
- </body>
43
- </html>
44
- ```
45
-
46
- ## Next Steps
47
-
48
- - Explore [Core Concepts](/guide/components)
49
- - Check out the [API Reference](/api/)