lego-dom 1.0.0 → 1.3.4

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 (74) hide show
  1. package/.legodom +87 -0
  2. package/CHANGELOG.md +87 -3
  3. package/cdn.html +10 -5
  4. package/docs/.vitepress/config.js +23 -7
  5. package/docs/api/config.md +95 -0
  6. package/docs/api/define.md +29 -2
  7. package/docs/api/directives.md +10 -2
  8. package/docs/api/index.md +1 -0
  9. package/docs/contributing/01-welcome.md +2 -0
  10. package/docs/contributing/02-registry.md +37 -3
  11. package/docs/contributing/06-init.md +13 -2
  12. package/docs/contributing/07-observer.md +3 -0
  13. package/docs/contributing/08-snap.md +15 -1
  14. package/docs/contributing/10-studs.md +3 -1
  15. package/docs/contributing/11-scanner.md +13 -0
  16. package/docs/contributing/12-render.md +32 -10
  17. package/docs/contributing/13-directives.md +19 -1
  18. package/docs/contributing/14-events.md +1 -1
  19. package/docs/contributing/15-router.md +49 -1
  20. package/docs/contributing/16-state.md +9 -10
  21. package/docs/contributing/17-legodom.md +1 -8
  22. package/docs/contributing/index.md +23 -4
  23. package/docs/examples/form.md +1 -1
  24. package/docs/examples/index.md +3 -3
  25. package/docs/examples/routing.md +10 -10
  26. package/docs/examples/sfc-showcase.md +1 -1
  27. package/docs/examples/todo-app.md +7 -7
  28. package/docs/guide/cdn-usage.md +44 -18
  29. package/docs/guide/components.md +18 -12
  30. package/docs/guide/directives.md +131 -22
  31. package/docs/guide/directory-structure.md +248 -0
  32. package/docs/guide/faq.md +210 -0
  33. package/docs/guide/getting-started.md +14 -10
  34. package/docs/guide/index.md +1 -1
  35. package/docs/guide/lifecycle.md +32 -0
  36. package/docs/guide/quick-start.md +4 -4
  37. package/docs/guide/reactivity.md +2 -2
  38. package/docs/guide/routing.md +69 -8
  39. package/docs/guide/server-side.md +134 -0
  40. package/docs/guide/sfc.md +96 -13
  41. package/docs/guide/templating.md +62 -57
  42. package/docs/index.md +9 -9
  43. package/docs/router/basic-routing.md +8 -8
  44. package/docs/router/cold-entry.md +2 -2
  45. package/docs/router/history.md +7 -7
  46. package/docs/router/index.md +1 -1
  47. package/docs/router/resolver.md +5 -5
  48. package/docs/router/surgical-swaps.md +5 -5
  49. package/docs/tutorial/01-project-setup.md +152 -0
  50. package/docs/tutorial/02-your-first-component.md +226 -0
  51. package/docs/tutorial/03-adding-routes.md +279 -0
  52. package/docs/tutorial/04-multi-page-app.md +329 -0
  53. package/docs/tutorial/05-state-and-globals.md +285 -0
  54. package/docs/tutorial/index.md +40 -0
  55. package/examples/vite-app/index.html +1 -0
  56. package/examples/vite-app/src/app.js +2 -2
  57. package/examples/vite-app/src/components/side-menu.lego +46 -0
  58. package/examples/vite-app/vite.config.js +2 -1
  59. package/main.js +261 -72
  60. package/main.min.js +7 -0
  61. package/monitoring-plugin.js +111 -0
  62. package/package.json +4 -2
  63. package/parse-lego.js +49 -22
  64. package/tests/error.test.js +74 -0
  65. package/tests/main.test.js +2 -2
  66. package/tests/memory.test.js +68 -0
  67. package/tests/monitoring.test.js +74 -0
  68. package/tests/naming.test.js +74 -0
  69. package/tests/parse-lego.test.js +2 -2
  70. package/tests/security.test.js +67 -0
  71. package/tests/server.test.js +114 -0
  72. package/tests/syntax.test.js +67 -0
  73. package/vite-plugin.js +3 -2
  74. package/docs/guide/contributing.md +0 -32
package/.legodom ADDED
@@ -0,0 +1,87 @@
1
+ # LegoDOM Framework Definition (.legodom)
2
+
3
+ This file defines the valid syntax, API, and behavior of the LegoDOM framework. Use this context to write valid `.lego` components and understand the runtime behavior.
4
+
5
+ ## 1. Component Structure (.lego)
6
+ A `.lego` file is a Single File Component (SFC) composed of three sections:
7
+
8
+ ```html
9
+ <template b-styles="optional-style-id">
10
+ <!-- HTML template with directives -->
11
+ </template>
12
+
13
+ <script>
14
+ export default {
15
+ // Reactive State
16
+ count: 0,
17
+
18
+ // Lifecycle Hooks
19
+ mounted() { console.log('Mounted'); },
20
+ updated() { console.log('State updated'); },
21
+ unmounted() { console.log('Destroyed'); },
22
+
23
+ // Methods
24
+ increment() { this.count++ }
25
+ }
26
+ </script>
27
+
28
+ <style>
29
+ /* Scoped CSS. 'self' is replaced with ':host' automatically */
30
+ self { display: block; }
31
+ h1 { color: blue; }
32
+ </style>
33
+ ```
34
+
35
+ ## 2. Template Directives
36
+
37
+ | Directive | Syntax | Description |
38
+ |-----------|--------|-------------|
39
+ | **b-if** | `b-if="expr"` | Conditionally renders element if `expr` is truthy. Replaced by comment anchor if false. |
40
+ | **b-show**| `b-show="expr"` | Toggles `display: none` based on `expr`. |
41
+ | **b-for** | `b-for="item in list"` | Renders element for each item in `list`. `item` is available in scope. |
42
+ | **b-text**| `b-text="path"` | Sets `textContent` to value at `path` (e.g. `user.name`). |
43
+ | **b-html**| `b-html="expr"` | Sets `innerHTML` to result of `expr`. **Unsafe**. |
44
+ | **b-sync**| `b-sync="path"` | Two-way binding for inputs. Updates `state.path` on input/change. |
45
+ | **Event** | `@event="expr"` | Native event listener. `event` is available in scope. E.g. `@click="save()"`. |
46
+ | **Interpolation** | `[[ expr ]]` | Text interpolation. Delimiters are `[[` and `]]` by default. |
47
+
48
+ Note: `b-data` can be used on `<template>` or the custom element tag to inject initial state from JSON string.
49
+
50
+ ## 3. Script Context & Helpers
51
+ The `script` exports a default object which becomes the reactive state (`this`).
52
+ Inside methods and template expressions, the following helpers are available:
53
+
54
+ ### Scope
55
+ - **this**: The reactive state of the component.
56
+ - **event**: The native DOM event (only in `@event` handlers).
57
+
58
+ ### Helpers
59
+ - **$element**: The component's host DOM element.
60
+ - **$emit(name, detail)**: Dispatches a CustomEvent `name` with `detail` (bubbles: true, composed: true).
61
+ - **$go(path, ...targets)**: Router navigation helper. Returns object with method calls.
62
+ - `this.$go('/url', '#target').get()`
63
+ - `this.$go('/api', '#response').post({data})`
64
+ - **$ancestors(tagName)**: Finds the state of the nearest ancestor component with `tagName`.
65
+ - **$registry(tagName)**: Access shared state of logical-only components defined via `Lego.define`.
66
+ - **$route**: Global route state object.
67
+
68
+ ## 4. Router & Targets
69
+ LegoDOM uses a "Target-Oriented" router. Navigation updates specific DOM elements (targets) rather than full page reloads.
70
+
71
+ - **Definition**: `Lego.route('/path/:id', 'tag-name', middleware)`
72
+ - **Targets**:
73
+ - `string`: CSS selector (e.g., `#main`, `user-card`).
74
+ - `function`: `(allComponents) => elements`
75
+ - **Usage**:
76
+ - HTML: `<a href="/path" b-target="#main">Link</a>` creates a surgical navigation link.
77
+ - JS: `this.$go('/path', '#main').get()`
78
+
79
+ ## 5. Lifecycle Hooks
80
+ - **mounted()**: Called when the component is attached to DOM, shadow root created, and initial render complete.
81
+ - **updated()**: Called after reactive state changes have been batched and applied to the DOM.
82
+ - **unmounted()**: Called when component is removed from the DOM.
83
+
84
+ ## 6. Global Configuration
85
+ - `Lego.init(root, { styles: { ... }, loader: callback })`: Initializes the app.
86
+ - `Lego.globals`: Global reactive state shared across all components.
87
+ ```
package/CHANGELOG.md CHANGED
@@ -2,12 +2,96 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.3.4] - 2026-01-16
6
+
7
+ ### Improvements
8
+
9
+ - **Minified Build:** Added a build script (`npm run build`) that uses `esbuild` to generate a `main.min.js` file, reducing file size by ~55% for optimal CDN performance.
10
+
11
+ ## [1.3.3] - 2026-01-16
12
+
13
+ ### Fixes
14
+
15
+ - **Inline Arrays in `b-for`:** Fixed a bug where inline arrays like `b-for="item in [{ name: 'A' }, { name: 'B' }]"` would fail to render. The fix changes `b-for` to clone the entire node as a template instead of storing innerHTML.
16
+
17
+ ### Documentation
18
+
19
+ - **Large Apps Guide:** Added "Scaling to Multi-Domain Apps" section for enterprise projects with multiple business domains (HRIS, Finance, Planning, etc.).
20
+
21
+ ### Improvements
22
+
23
+ - **Vite Plugin:** Added `importPath` option to `legoPlugin()`, allowing developers to override where the `Lego` core is imported from (e.g., for local testing or custom builds).
24
+
25
+ ## [1.3.1] - 2026-01-16
26
+
27
+ ### Fixes
28
+
29
+ - **`this.$emit()` in Script Methods:** `$emit` is now available on the component state, allowing event dispatching from script logic.
30
+ ```javascript
31
+ handleSave() {
32
+ this.$emit('save', { id: this.itemId });
33
+ }
34
+ ```
35
+
36
+ ## [1.3.0] - 2026-01-16
37
+
38
+ ### New Features
39
+
40
+ - **`b-var` Directive:** Access DOM elements directly via `this.$vars.name`. Useful for triggering `.click()`, `.focus()`, or `.play()` on hidden inputs, video elements, etc.
41
+ ```html
42
+ <input type="file" b-var="fileInput" style="display:none">
43
+ <button @click="$vars.fileInput.click()">Upload</button>
44
+ ```
45
+
46
+ ### Documentation
47
+
48
+ - **New Guide: Large Apps ("Chaos Tends To A Minimum"):** Added a comprehensive architectural guide for structuring enterprise-scale projects with 200+ components. Introduces the Blocks, Widgets, Components, Pages hierarchy to cleanly separate Identity, Intent, Computation, and Coordination.
49
+ - **`b-if` Directive:** Documented the `b-if` directive for conditional rendering (DOM insertion/removal).
50
+ - **`b-text` Directive:** Documented the limited `b-text` directive (property paths only, no expressions).
51
+ - **Directives "See Also":** Added cross-references to `b-target`, `b-id`, `b-styles` in the Directives guide.
52
+
53
+ ## [1.2.0] - 2026-01-15
54
+
55
+ **Breaking Change: New Template Syntax** 🚨
56
+ To ensure compatibility with server-side frameworks (Jinja, Django, Flask) and JavaScript template literals, **LegoDOM now uses `[[ ]]` by default** instead of `{{ }}`.
57
+
58
+ - **Breaking:** Default interpolation syntax changed from `{{ variable }}` to `[[ variable ]]`.
59
+ - **Feature:** Added `Lego.config.syntax` to configure delimiters.
60
+ - Set to `'mustache'` to revert to `{{ }}`.
61
+ - Set to `'brackets'` (default) for `[[ ]]`.
62
+ - **Feature:** Added support for snake_case, PascalCase, camelCase, and kebab-case component names for `.lego` files.
63
+ - **Feature:** Added `Lego.config.loader` to fetch SFCs from a server endpoint.
64
+ - **Fix:** Fixed a critical bug where `snap()` triggered double renders and mounted hooks.
65
+
66
+ ## [1.1.0] - 2026-01-12
67
+
68
+ **The Enterprise Readiness Update**
69
+ This release focuses on Security, Performance, and Resilience, making LegoDOM suitable for high-traffic production environments.
70
+
71
+ ### Security Hardening
72
+
73
+ - **Secure Expression Evaluation:** Implemented a new `safeEval` validator that blocks dangerous keywords (e.g., `function`, `eval`, `constructor`) to prevent arbitrary code execution.
74
+ - **XSS Protection:** `safeEval` now automatically escapes output by default.
75
+ - **New `b-html` Directive:** Added `b-html` for safely rendering raw HTML content (replacing `innerHTML`), requiring explicit opt-in for potential XSS risks.
76
+
77
+ ### Performance
78
+
79
+ - **Expression Caching:** Compiled expressions are now cached in a `WeakMap`, transforming `O(n)` compilation costs into `O(1)` for repeated renders. This yields a massive performance boost for large lists (`b-for`).
80
+ - **Optimized Binding:** Replaced `querySelectorAll('*')` with `TreeWalker`, significantly reducing memory allocation and DOM traversal time during component initialization.
81
+
82
+ ### Resilience & Scalability
83
+
84
+ - **Global Error Handling:** Introduced `Lego.config.onError` hook for centralized error reporting (compatible with Sentry/Datadog).
85
+ - **Graceful Rendering:** Rendering errors (e.g., accessing undefined properties in `{{ }}`) are now caught and reported, but do not crash the component or application.
86
+ - **Memory Management:** Fixed nested component lifecycle issues where Shadow DOM children were not correctly tracked, preventing memory leaks in complex trees.
87
+ - **Monitoring Plugin:** Added performance monitoring hooks (`onRenderStart`, `onRenderEnd`) and a new `monitoring-plugin.js` for realtime metrics.
88
+
5
89
  ## [1.0.0] - 2026-01-10
6
90
 
7
91
  **The Launch Release!** 🚀
8
92
  LegoDOM moves out of beta with a finalized API, robust routing, and a hybrid rendering engine.
9
93
 
10
- ### 🌟 Major features
94
+ ### Major features
11
95
 
12
96
  - **Surgical Routing:** Introduced a groundbreaking `b-target` attribute that allows any link to update any part of the page without a full reload.
13
97
  - **Smart History:** The router now tracks surgical updates in `history.state`, correctly restoring "fragment" states when using the Back/Forward buttons.
@@ -21,7 +105,7 @@ LegoDOM moves out of beta with a finalized API, robust routing, and a hybrid ren
21
105
  - **Cleaner Templates:** Template expressions now support `$route` directly (`{{ $route.params.id }}`) without `global.` prefix.
22
106
  - **HMR 2.0:** The Vite plugin now correctly handles adding/deleting `.lego` files and performs smarter hot updates.
23
107
 
24
- ### Improvements
108
+ ### Improvements
25
109
 
26
110
  - **Router:**
27
111
  - Exposed `$go(path, ...targets).get()` for programmatic surgical navigation.
@@ -37,7 +121,7 @@ LegoDOM moves out of beta with a finalized API, robust routing, and a hybrid ren
37
121
  - Complete overhaul of Routing guide with "Surgical Swaps", "Deep Linking", and "Self-Healing" patterns.
38
122
  - Clarified component naming conventions (Filename for Vite vs. `b-id` for CDN).
39
123
 
40
- ### 🐛 Bug Fixes
124
+ ### Bug Fixes
41
125
 
42
126
  - Fixed "Literal Mustaches" appearing in `href` and text content on initial load.
43
127
  - Fixed Deep Linking where hitting Refresh on a sub-route would render an empty shell (addressed via Self-Healing pattern).
package/cdn.html CHANGED
@@ -56,8 +56,8 @@
56
56
  }
57
57
  </style>
58
58
 
59
- <h3>{{title}}</h3>
60
- <p>{{ items.filter(t => !t.done).length }} of {{ items.length }} items remaining</p>
59
+ <h3>[[title]]</h3>
60
+ <p>[[items.filter(t => !t.done).length]] of [[items.length]] items remaining</p>
61
61
  <div style="margin-bottom: 20px;">
62
62
  <input b-sync="newItem" placeholder="Add task..." class="input">
63
63
  <button @click="() => {
@@ -71,7 +71,7 @@
71
71
  <div b-for="todo in items">
72
72
  <div class="item-row {{todo.done ? 'done' : ''}}">
73
73
  <input type="checkbox" b-sync="todo.done">
74
- <span>{{todo.text}}</span>
74
+ <span>[[todo.text]]</span>
75
75
  </div>
76
76
  </div>
77
77
  </template>
@@ -96,8 +96,8 @@
96
96
  }
97
97
  </style>
98
98
 
99
- <h3>{{name}}</h3>
100
- <p>{{bio}}</p>
99
+ <h3>[[name]]</h3>
100
+ <p>[[bio]]</p>
101
101
  <slot></slot>
102
102
  </template>
103
103
 
@@ -119,6 +119,11 @@
119
119
  }">
120
120
  </todo-list>
121
121
  </user-card>
122
+ <script>
123
+ // Initialize Lego
124
+ // We need to wait for DOM, but script at end of body is fine
125
+ Lego.init();
126
+ </script>
122
127
  </body>
123
128
 
124
129
  </html>
@@ -10,14 +10,15 @@ export default defineConfig({
10
10
 
11
11
  nav: [
12
12
  { text: 'Guide', link: '/guide/' },
13
+ { text: 'Tutorial', link: '/tutorial/' },
13
14
  { text: 'Contributing', link: '/contributing/' },
14
15
  { text: 'API', link: '/api/' },
15
16
  { text: 'Examples', link: '/examples/' },
16
17
  { text: 'Router', link: '/router/' },
17
18
  {
18
- text: 'v1.0.0',
19
+ text: 'v1.3.4',
19
20
  items: [
20
- { text: 'Changelog', link: 'https://github.com/rayattack/LegoJS/releases' }
21
+ { text: 'Changelog', link: 'https://github.com/rayattack/LegoDOM/releases' }
21
22
 
22
23
  ]
23
24
  }
@@ -48,14 +49,26 @@ export default defineConfig({
48
49
  ]
49
50
  }
50
51
  ],
52
+ '/tutorial/': [
53
+ {
54
+ text: 'Tutorial: Your First App',
55
+ items: [
56
+ { text: 'Overview', link: '/tutorial/' },
57
+ { text: '1. Project Setup', link: '/tutorial/01-project-setup' },
58
+ { text: '2. Your First Component', link: '/tutorial/02-your-first-component' },
59
+ { text: '3. Adding Routes', link: '/tutorial/03-adding-routes' },
60
+ { text: '4. Multi-Page App', link: '/tutorial/04-multi-page-app' },
61
+ { text: '5. State & Globals', link: '/tutorial/05-state-and-globals' }
62
+ ]
63
+ }
64
+ ],
51
65
  '/guide/': [
52
66
  {
53
67
  text: 'Introduction',
54
68
  items: [
55
- { text: 'What is Lego?', link: '/guide/' },
69
+ { text: 'What is LegoDOM?', link: '/guide/' },
56
70
  { text: 'Getting Started', link: '/guide/getting-started' },
57
71
  { text: 'Quick Start', link: '/guide/quick-start' },
58
- { text: 'Contributing', link: '/guide/contributing' }
59
72
  ]
60
73
  },
61
74
  {
@@ -73,7 +86,9 @@ export default defineConfig({
73
86
  { text: 'Single File Components', link: '/guide/sfc' },
74
87
  { text: 'Routing', link: '/guide/routing' },
75
88
  { text: 'CDN Usage', link: '/guide/cdn-usage' },
76
- { text: 'Lifecycle Hooks', link: '/guide/lifecycle' }
89
+ { text: 'Lifecycle Hooks', link: '/guide/lifecycle' },
90
+ { text: 'Large Apps', link: '/guide/directory-structure' },
91
+ { text: 'FAQ', link: '/guide/faq' }
77
92
  ]
78
93
  }
79
94
  ],
@@ -85,6 +100,7 @@ export default defineConfig({
85
100
  { text: 'Lego.define()', link: '/api/define' },
86
101
  { text: 'Lego.route()', link: '/api/route' },
87
102
  { text: 'Lego.globals', link: '/api/globals' },
103
+ { text: 'Lego.config', link: '/api/config' },
88
104
  { text: 'Directives', link: '/api/directives' },
89
105
  { text: 'Lifecycle Hooks', link: '/api/lifecycle' },
90
106
  { text: 'Vite Plugin', link: '/api/vite-plugin' }
@@ -118,7 +134,7 @@ export default defineConfig({
118
134
  },
119
135
 
120
136
  socialLinks: [
121
- { icon: 'github', link: 'https://github.com/rayattack/LegoJS' }
137
+ { icon: 'github', link: 'https://github.com/rayattack/LegoDOM' }
122
138
  ],
123
139
 
124
140
  footer: {
@@ -131,7 +147,7 @@ export default defineConfig({
131
147
  },
132
148
 
133
149
  editLink: {
134
- pattern: 'https://github.com/rayattack/LegoJS/edit/main/docs/:path',
150
+ pattern: 'https://github.com/rayattack/LegoDOM/edit/main/docs/:path',
135
151
  text: 'Edit this page on GitHub'
136
152
  }
137
153
  },
@@ -0,0 +1,95 @@
1
+ # Global Configuration
2
+
3
+ `Lego.config` allows you to customize framework behavior, including error handling and metrics.
4
+
5
+ ## Properties
6
+
7
+ ### `syntax`
8
+
9
+ * **Type**: `'mustache' | 'brackets'`
10
+ * **Default**: `'brackets'`
11
+
12
+ Configures the template delimiter style.
13
+
14
+ * `'brackets'`: Uses <code v-pre>[[ variable ]]</code> (Default)
15
+ * `'mustache'`: Uses <code v-pre>{{ variable }}</code> (Legacy/Vue-style)
16
+
17
+ ```javascript
18
+ // Switch back to mustache syntax if preferred
19
+ Lego.config.syntax = 'mustache';
20
+ ```
21
+
22
+ ### `loader`
23
+
24
+ * **Type**: `(tagName: string) => string | Promise<string> | null`
25
+ * **Default**: `undefined`
26
+
27
+ Use this hook to implement **Server-Side Component Delivery**.
28
+
29
+ **Option 1: Simple Mode (Return URL)**
30
+ We fetch it for you.
31
+ ```javascript
32
+ loader: (tag) => `/components/${tag}.lego`
33
+ ```
34
+
35
+ **Option 2: Power Mode (Return Promise)**
36
+ You control the fetch. Useful for **Authentication** (Cookies, JWT) or Custom Headers.
37
+
38
+ ```javascript
39
+ Lego.init(document.body, {
40
+ loader: async (tagName) => {
41
+ // Custom Authenticated Fetch
42
+ const res = await fetch(`/components/${tagName}.lego`, {
43
+ credentials: 'include', // Send Cookies
44
+ headers: { 'Authorization': getToken() }
45
+ });
46
+ return await res.text(); // Return SFC content directly
47
+ }
48
+ });
49
+ ```
50
+ **Mechanism:**
51
+ 1. Browser sees unknown `<admin-widget>`.
52
+ 2. `Lego` calls `config.loader('admin-widget')`.
53
+ 3. If URL returned, it fetches the file.
54
+ 4. The server returns raw SFC content (`<template>...`).
55
+ 5. `Lego.defineSFC()` parses and upgrades the element instantly.
56
+
57
+ ### `onError`
58
+
59
+ * **Type**: `(error: Error, type: string, context: HTMLElement) => void`
60
+ * **Default**: `undefined`
61
+
62
+ Global error handler hook. Called when an error occurs during:
63
+ * `render`: Template rendering (expression evaluation)
64
+ * `event-handler`: `@event` callbacks
65
+ * `define`: Component definition
66
+ * `sync-update`: `b-sync` updates
67
+
68
+ ```javascript
69
+ Lego.config.onError = (err, type, context) => {
70
+ console.error(`Error in ${type}:`, err);
71
+ // Send to Sentry/Datadog
72
+ captureException(err, { tags: { type } });
73
+ };
74
+ ```
75
+
76
+ ### `metrics`
77
+
78
+ * **Type**: `Object`
79
+
80
+ Performance monitoring hooks, primarily used by plugins.
81
+
82
+ * `onRenderStart(el)`: Called before a component renders.
83
+ * `onRenderEnd(el)`: Called after a component finishes rendering.
84
+
85
+ ```javascript
86
+ // Example monitoring implementation
87
+ Lego.config.metrics = {
88
+ onRenderStart(el) {
89
+ console.time(el.tagName);
90
+ },
91
+ onRenderEnd(el) {
92
+ console.timeEnd(el.tagName);
93
+ }
94
+ };
95
+ ```
@@ -21,11 +21,38 @@ import { Lego } from 'lego-dom';
21
21
 
22
22
  Lego.define('user-card', `
23
23
  <div class="card">
24
- <h3>{{ name }}</h3>
25
- <p>{{ role }}</p>
24
+ <h3>[[ name ]]</h3>
25
+ <p>[[ role ]]</p>
26
26
  </div>
27
27
  `, {
28
28
  name: 'John Doe',
29
29
  role: 'Admin'
30
30
  });
31
31
  ```
32
+
33
+ ## Lego.defineSFC()
34
+
35
+ Runtime parser for Single File Components (SFC). Useful for **Server-Side Rendering** or dynamic loading architectures.
36
+
37
+ ```ts
38
+ Lego.defineSFC(content: string, filename?: string)
39
+ ```
40
+
41
+ ### Example
42
+
43
+ ```javascript
44
+ const sfc = `
45
+ <template>
46
+ <h1>[[ title ]]</h1>
47
+ </template>
48
+ <script>
49
+ export default { title: 'Hello World' }
50
+ </script>
51
+ <style>
52
+ h1 { color: red; }
53
+ </style>
54
+ `;
55
+
56
+ // Registers <my-component> instantly
57
+ Lego.defineSFC(sfc, 'my-component.lego');
58
+ ```
@@ -11,6 +11,14 @@ Conditionally render an element.
11
11
  <div b-show="!loading">Content loaded!</div>
12
12
  ```
13
13
 
14
+ ## b-html
15
+
16
+ Renders raw HTML. **Security Risk**: Use with caution.
17
+
18
+ ```html
19
+ <div b-html="htmlContent"></div>
20
+ ```
21
+
14
22
  ## b-for
15
23
 
16
24
  Render a list of items.
@@ -18,7 +26,7 @@ Render a list of items.
18
26
  ```html
19
27
  <ul>
20
28
  <li b-for="user in users">
21
- {{ user.name }}
29
+ [[ user.name ]]
22
30
  </li>
23
31
  </ul>
24
32
  ```
@@ -29,7 +37,7 @@ Two-way data binding for form inputs.
29
37
 
30
38
  ```html
31
39
  <input b-sync="username" placeholder="Enter username">
32
- <p>You typed: {{ username }}</p>
40
+ <p>You typed: [[ username ]]</p>
33
41
  ```
34
42
 
35
43
  ## @event
package/docs/api/index.md CHANGED
@@ -7,6 +7,7 @@ Welcome to the Lego API documentation.
7
7
  - [Lego.define()](/api/define) - Defining components
8
8
  - [Lego.route()](/api/route) - Client-side routing
9
9
  - [Lego.globals](/api/globals) - Global state
10
+ - [Lego.config](/api/config) - Configuration & Error Handling
10
11
  - [Lifecycle Hooks](/api/lifecycle) - Component lifecycle methods
11
12
 
12
13
  ## Templates & Binding
@@ -6,8 +6,10 @@ LegoDOM is wrapped in an **IIFE** (Immediately Invoked Function Expression) assi
6
6
  const Lego = (() => {
7
7
  // ... all the logic ...
8
8
  return {
9
+ init: () => { ... },
9
10
  init: () => { ... },
10
11
  define: (tagName, templateHTML, logic = {}) => { ... },
12
+ defineSFC: (content, filename) => { ... }, // Runtime SFC Parser
11
13
  // ...
12
14
  };
13
15
  })();
@@ -94,6 +94,40 @@ async load(id) {
94
94
 
95
95
  - It **injects** a `Lego.define()` call into your JavaScript bundle automatically.
96
96
 
97
- - **Result:** You just create a file named `UserCard.lego`, and suddenly `<user-card>` is a valid HTML tag in your app.
98
-
99
- **Key Insight:** Notice in `main.js` how `define` uses `sfcLogic.set(tagName, logic)`. This is where the plugin "parks" your component's JavaScript code so that when the component "snaps" (Topic 23), it can find its specific logic.
97
+ - **Result:** You just create a file named `user-card.lego`, and suddenly `<user-card>` is a valid HTML tag in your app.
98
+
99
+ **Paradigm 3: Runtime Component Definition (New in v2.0)**
100
+
101
+ ```js
102
+ defineSFC: (content, filename) => {
103
+ // Regex parsing of <template>, <script>, <style>
104
+ const templateMatch = content.match(/<template([\s\S]*?)>([\s\S]*?)<\/template>/);
105
+ // ...
106
+ const logicObj = new Function(`return ${script}`)();
107
+ // ...
108
+ sfcLogic.set(name, logicObj);
109
+ }
110
+ ```
111
+
112
+ This is the power behind the **Server Loader**. We can fetch a string from the server and "compile" it in the browser using `new Function()`. It populates `registry` and `sfcLogic` just like the build-time tools, but on the fly.
113
+
114
+ ### 4. Component Naming Conventions ("Explicit or Go Home")
115
+
116
+ When you define a component via a file (e.g. `.lego`), the library automatically derives the tag name. To keep the web platform happy, we enforce **Custom Element Best Practices**:
117
+
118
+ 1. **Automatic Conversion**: All filenames are converted to `kebab-case`.
119
+ - `UserProfile.lego` -> `<user-profile>`
120
+ - `navBar.lego` -> `<nav-bar>`
121
+ - `data_table.lego` -> `<data-table>`
122
+
123
+ 2. **The Hyphen Rule**: A custom element **MUST** contain a hyphen.
124
+ - `Button.lego` -> Error
125
+ - `Adidas.lego` -> Error
126
+
127
+ > [!TIP]
128
+ > **Single Word Component? Namespace It!**
129
+ > Custom Element specs require a hyphen to distinguish from native tags. To ensure forward compatibility, **LegoDOM will throw an error** if you try to define a single-word component.
130
+ > Simply add your app or product prefix to the filename:
131
+ > - `fb-button.lego` -> `<fb-button>`
132
+ > - `shop-card.lego` -> `<shop-card>`
133
+ > - `mobile-button.lego` -> `<mobile-button>`
@@ -77,13 +77,24 @@ This is the most critical part of the initialization. The library creates a `Mut
77
77
 
78
78
  ```js
79
79
  const observer = new MutationObserver(m => m.forEach(r => {
80
- r.addedNodes.forEach(n => n.nodeType === Node.ELEMENT_NODE && snap(n));
80
+ r.addedNodes.forEach(n => {
81
+ if (n.nodeType === Node.ELEMENT_NODE) {
82
+ snap(n);
83
+
84
+ // Auto-Discovery (v2.0): Check for remote components
85
+ const tagName = n.tagName.toLowerCase();
86
+ if (tagName.includes('-') && !registry[tagName] && config.loader && !activeComponents.has(n)) {
87
+ // ... Call loader ...
88
+ }
89
+ }
90
+ });
81
91
  r.removedNodes.forEach(n => n.nodeType === Node.ELEMENT_NODE && unsnap(n));
82
92
  }));
83
93
  observer.observe(document.body, { childList: true, subtree: true });
84
94
  ```
85
95
 
86
- - **What it does**: It watches the `document.body` for any changes to the HTML structure.
96
+ - **What it does**: It watches the `document.body` for any changes to the HTML structure.
97
+ - **Auto-Discovery (New in v2.0)**: If `snap(n)` fails because the component isn't in the registry, the observer now checks `config.loader`. If a loader is defined, it triggers a fetch to pull the component definition from the server. This enables the "HTMX+Components" pattern.
87
98
 
88
99
  - **The Config**: It observes `{ childList: true, subtree: true }`. This means it sees if an element is added to the body, or if an element is added deep inside another element.
89
100
 
@@ -27,6 +27,9 @@ Whenever a new piece of HTML is injected (via `innerHTML`, `appendChild`, etc.),
27
27
  - **Filter**: The library checks `n.nodeType === Node.ELEMENT_NODE` to ensure it is an **Element** (like a `<div>` or `<my-comp>`) and not just a fragment of text.
28
28
 
29
29
  - **The Action**: It calls `snap(n)`. This triggers the entire initialization process: attaching the Shadow DOM, creating reactive state, and rendering the template.
30
+
31
+ - **Auto-Discovery (v2.0)**: If `snap(n)` doesn't find a template in the registry, the observer now checks `Lego.config.loader`. If configured, it pauses to fetch the component definition from the server.
32
+
30
33
 
31
34
 
32
35
  ### 3. Processing `removedNodes`
@@ -19,6 +19,11 @@ const snap = (el) => {
19
19
  const tpl = templateNode.content.cloneNode(true);
20
20
  const shadow = el.attachShadow({ mode: 'open' });
21
21
 
22
+ const splitStyles = (templateNode.getAttribute('b-styles') || "").split(/\s+/).filter(Boolean);
23
+ if (splitStyles.length) {
24
+ shadow.adoptedStyleSheets = splitStyles.flatMap(k => styleRegistry.get(k) || []);
25
+ }
26
+
22
27
  // TIER 1: Logic from Lego.define (SFC)
23
28
  const scriptLogic = sfcLogic.get(name) || {};
24
29
 
@@ -32,7 +37,10 @@ const snap = (el) => {
32
37
  el._studs = reactive({
33
38
  ...scriptLogic,
34
39
  ...templateLogic,
35
- ...instanceLogic
40
+ ...instanceLogic,
41
+ // Inject Global Helpers
42
+ get $route() { return Lego.globals.$route },
43
+ get $go() { return Lego.globals.$go }
36
44
  }, el);
37
45
 
38
46
  shadow.appendChild(tpl);
@@ -83,6 +91,12 @@ const shadow = el.attachShadow({ mode: 'open' });
83
91
  - **Encapsulation**: By using `attachShadow`, the component’s internal styles and HTML are shielded from the rest of the page.
84
92
 
85
93
  - **Template Injection**: It clones the content of the template and appends it to this new Shadow Root.
94
+
95
+ #### What about `<slot>`?
96
+ Because we use native Shadow DOM, `<slot>` just works.
97
+ When `snap` attaches the shadow root, any children *already* inside the custom element (the "Light DOM") are automatically projected into the `<slot>` tags defined in your template.
98
+ We don't need to write any code for this—the browser does it for us.
99
+
86
100
 
87
101
 
88
102
  ### 3. CSS "self" Transformation
@@ -25,7 +25,9 @@ Once `snap()` has merged the data from the SFC (Tier 1), the Template (Tier 2),
25
25
  el._studs = reactive({
26
26
  ...scriptLogic,
27
27
  ...templateLogic,
28
- ...instanceLogic
28
+ ...instanceLogic,
29
+ get $route() { return Lego.globals.$route },
30
+ get $go() { return Lego.globals.$go }
29
31
  }, el);
30
32
  //... rest of code
31
33
  ```
@@ -52,6 +52,19 @@ const scanForBindings = (container) => {
52
52
  };
53
53
  ```
54
54
 
55
+ ## Why Regex? (The "Forbidden" Choice)
56
+
57
+ You will notice we use Regular Expressions to find bindings.
58
+
59
+ **"Regex is bad for HTML!"** they say.
60
+ Usually, yes. But we are not parsing *arbitrary* HTML to build a DOM. We are scanning *specific known tokens* inside trusted templates.
61
+
62
+ **The Trade-off:**
63
+ - **AST Parser**: Reliable, but heavy (10kb+).
64
+ - **Regex Scanner**: Good enough for 99% of bindings, extremely light (<1kb).
65
+
66
+ Since LegoDOM targets **speed** and **size** (<4kb), Regex is the correct architectural choice. We mitigate edge cases by ignoring bindings inside `<script>` and `<style>` blocks.
67
+
55
68
  ### 1. The `TreeWalker` Efficiency
56
69
 
57
70
  LegoDOM uses a native browser tool called a `TreeWalker`.