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
@@ -0,0 +1,279 @@
1
+ # Step 3: Adding Routes
2
+
3
+ Now let's make your app navigable. By the end of this page, you'll understand exactly **where routes go**, **how to define them**, and **how to navigate between pages**.
4
+
5
+ ## The Golden Question: Where Do Routes Go?
6
+
7
+ **Answer: In your entry file (`app.js`), before `Lego.init()`.**
8
+
9
+ ```javascript
10
+ // src/app.js
11
+
12
+ import { Lego } from 'lego-dom';
13
+ import registerComponents from 'virtual:lego-components';
14
+
15
+ // 1. Register components
16
+ registerComponents();
17
+
18
+ // 2. Define ALL your routes here ⭐
19
+ Lego.route('/', 'home-page');
20
+ Lego.route('/login', 'login-page');
21
+ Lego.route('/welcome', 'welcome-page');
22
+ Lego.route('/users/:id', 'user-profile'); // Dynamic route
23
+
24
+ // 3. Initialize (must come AFTER routes)
25
+ await Lego.init();
26
+ ```
27
+
28
+ ::: tip Why Before init()?
29
+ `Lego.init()` starts the router. If routes aren't defined yet, the router has nothing to match. Always define routes **before** calling `init()`.
30
+ :::
31
+
32
+ ## Route Syntax
33
+
34
+ ```javascript
35
+ Lego.route(path, componentName, middleware?)
36
+ ```
37
+
38
+ | Argument | Description | Example |
39
+ |----------|-------------|---------|
40
+ | `path` | URL pattern | `'/'`, `'/login'`, `'/users/:id'` |
41
+ | `componentName` | The component to render (filename minus `.lego`) | `'home-page'`, `'login-page'` |
42
+ | `middleware` | Optional guard function | `(params, globals) => boolean` |
43
+
44
+ ### Dynamic Routes
45
+
46
+ Use `:param` for URL parameters:
47
+
48
+ ```javascript
49
+ Lego.route('/users/:id', 'user-profile');
50
+ Lego.route('/posts/:postId/comments/:commentId', 'comment-view');
51
+ ```
52
+
53
+ Access them in your component:
54
+
55
+ ```javascript
56
+ // In your .lego file's <script>
57
+ mounted() {
58
+ const userId = this.$route.params.id;
59
+ console.log('User ID:', userId);
60
+ }
61
+ ```
62
+
63
+ ## Navigating Between Pages
64
+
65
+ ### Method 1: The `b-link` Attribute (Declarative)
66
+
67
+ ```html
68
+ <!-- In any component template -->
69
+ <a href="/login" b-link>Go to Login</a>
70
+ <a href="/users/42" b-link>View User 42</a>
71
+ ```
72
+
73
+ The `b-link` attribute turns a regular `<a>` tag into a SPA link. No page reload!
74
+
75
+ ### Method 2: The `$go()` Helper (Programmatic)
76
+
77
+ ```javascript
78
+ // In your component methods
79
+ handleLoginClick() {
80
+ this.$go('/login').get();
81
+ }
82
+
83
+ // Or with the global reference
84
+ Lego.globals.$go('/welcome').get();
85
+ ```
86
+
87
+ ### Method 3: Using `b-target` (Surgical Updates)
88
+
89
+ Want to update only a specific part of the page?
90
+
91
+ ```html
92
+ <!-- Update just the sidebar, not the whole page -->
93
+ <a href="/sidebar/settings" b-target="#sidebar" b-link="false">Settings</a>
94
+ ```
95
+
96
+ ## Create a Login Page
97
+
98
+ Let's add a login page to navigate to.
99
+
100
+ Create `src/components/login-page.lego`:
101
+
102
+ ```html
103
+ <template>
104
+ <div class="login-container">
105
+ <h1>Login</h1>
106
+ <form @submit="handleSubmit(event)">
107
+ <div class="form-group">
108
+ <label>Email</label>
109
+ <input type="email" b-sync="email" placeholder="you@example.com" required>
110
+ </div>
111
+ <div class="form-group">
112
+ <label>Password</label>
113
+ <input type="password" b-sync="password" placeholder="••••••••" required>
114
+ </div>
115
+ <button type="submit">Sign In</button>
116
+ </form>
117
+ <p class="back-link">
118
+ <a href="/" b-link>← Back to Home</a>
119
+ </p>
120
+ </div>
121
+ </template>
122
+
123
+ <style>
124
+ self {
125
+ display: flex;
126
+ align-items: center;
127
+ justify-content: center;
128
+ min-height: 100vh;
129
+ background: #f5f5f5;
130
+ }
131
+
132
+ .login-container {
133
+ background: white;
134
+ padding: 3rem;
135
+ border-radius: 16px;
136
+ box-shadow: 0 10px 40px rgba(0,0,0,0.1);
137
+ width: 100%;
138
+ max-width: 400px;
139
+ }
140
+
141
+ h1 {
142
+ text-align: center;
143
+ margin-bottom: 2rem;
144
+ color: #333;
145
+ }
146
+
147
+ .form-group {
148
+ margin-bottom: 1.5rem;
149
+ }
150
+
151
+ label {
152
+ display: block;
153
+ margin-bottom: 0.5rem;
154
+ font-weight: 600;
155
+ color: #555;
156
+ }
157
+
158
+ input {
159
+ width: 100%;
160
+ padding: 0.75rem 1rem;
161
+ border: 2px solid #e0e0e0;
162
+ border-radius: 8px;
163
+ font-size: 1rem;
164
+ transition: border-color 0.2s;
165
+ }
166
+
167
+ input:focus {
168
+ outline: none;
169
+ border-color: #667eea;
170
+ }
171
+
172
+ button {
173
+ width: 100%;
174
+ padding: 1rem;
175
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
176
+ color: white;
177
+ border: none;
178
+ border-radius: 8px;
179
+ font-size: 1rem;
180
+ font-weight: 600;
181
+ cursor: pointer;
182
+ transition: transform 0.2s;
183
+ }
184
+
185
+ button:hover {
186
+ transform: translateY(-2px);
187
+ }
188
+
189
+ .back-link {
190
+ text-align: center;
191
+ margin-top: 1.5rem;
192
+ }
193
+
194
+ .back-link a {
195
+ color: #667eea;
196
+ text-decoration: none;
197
+ }
198
+ </style>
199
+
200
+ <script>
201
+ export default {
202
+ email: '',
203
+ password: '',
204
+
205
+ handleSubmit(event) {
206
+ event.preventDefault();
207
+ console.log('Login attempt:', this.email);
208
+
209
+ // Navigate to welcome page
210
+ this.$go('/welcome').get();
211
+ }
212
+ }
213
+ </script>
214
+ ```
215
+
216
+ ## Update Your Routes
217
+
218
+ ```javascript
219
+ // src/app.js
220
+ import { Lego } from 'lego-dom';
221
+ import registerComponents from 'virtual:lego-components';
222
+
223
+ registerComponents();
224
+
225
+ Lego.route('/', 'home-page');
226
+ Lego.route('/login', 'login-page'); // Add this!
227
+
228
+ await Lego.init();
229
+ ```
230
+
231
+ ## Wire Up Navigation from Home
232
+
233
+ Update `home-page.lego`'s button to navigate:
234
+
235
+ ```javascript
236
+ // In the <script> section
237
+ handleClick() {
238
+ this.$go('/login').get();
239
+ }
240
+ ```
241
+
242
+ ## Test It!
243
+
244
+ ```bash
245
+ npm run dev
246
+ ```
247
+
248
+ 1. Open `http://localhost:5173` – see your home page
249
+ 2. Click "Get Started" – navigates to `/login`
250
+ 3. Click "← Back to Home" – navigates back to `/`
251
+ 4. Use browser back/forward – it just works!
252
+
253
+ ## What You've Learned
254
+
255
+ ✅ Routes go in `app.js`, before `Lego.init()`
256
+ ✅ `Lego.route(path, component)` maps URLs to components
257
+ ✅ `b-link` on `<a>` tags for declarative navigation
258
+ ✅ `$go(path).get()` for programmatic navigation
259
+ ✅ Dynamic routes with `:param` syntax
260
+
261
+ ## Quick Reference
262
+
263
+ | I want to... | Use this |
264
+ |--------------|----------|
265
+ | Define a route | `Lego.route('/path', 'component-name')` |
266
+ | Navigate via link | `<a href="/path" b-link>Click</a>` |
267
+ | Navigate via JS | `this.$go('/path').get()` |
268
+ | Get route params | `this.$route.params.id` |
269
+
270
+ ---
271
+
272
+ <div style="display: flex; justify-content: space-between; margin-top: 3rem;">
273
+ <a href="./02-your-first-component" style="display: inline-block; background: #eee; color: #333; padding: 0.75rem 1.5rem; border-radius: 6px; text-decoration: none; font-weight: 600;">
274
+ ← Previous: Your First Component
275
+ </a>
276
+ <a href="./04-multi-page-app" style="display: inline-block; background: #4CAF50; color: white; padding: 0.75rem 1.5rem; border-radius: 6px; text-decoration: none; font-weight: 600;">
277
+ Next: Multi-Page App →
278
+ </a>
279
+ </div>
@@ -0,0 +1,329 @@
1
+ # Step 4: Building a Multi-Page App
2
+
3
+ Let's bring it all together! In this step, we'll complete the **index → login → welcome** flow that every real app needs. You'll see exactly how data flows between pages.
4
+
5
+ ## The Complete App Structure
6
+
7
+ By the end of this page, you'll have:
8
+
9
+ ```
10
+ src/components/
11
+ ├── home-page.lego ← Landing page (/)
12
+ ├── login-page.lego ← Login form (/login)
13
+ └── welcome-page.lego ← Dashboard (/welcome)
14
+ ```
15
+
16
+ ## Step 1: Create the Welcome Page
17
+
18
+ Create `src/components/welcome-page.lego`:
19
+
20
+ ```html
21
+ <template>
22
+ <div class="dashboard">
23
+ <header>
24
+ <h1>Welcome, [[ username ]]! 🎉</h1>
25
+ <button @click="handleLogout()">Logout</button>
26
+ </header>
27
+
28
+ <main>
29
+ <div class="card">
30
+ <h2>You're In!</h2>
31
+ <p>You've successfully navigated through a complete authentication flow built with LegoDOM.</p>
32
+ <p>This page received your username from the login form using global state.</p>
33
+ </div>
34
+
35
+ <div class="stats">
36
+ <div class="stat">
37
+ <span class="value">[[ visitCount ]]</span>
38
+ <span class="label">Page Views</span>
39
+ </div>
40
+ <div class="stat">
41
+ <span class="value">[[ formatTime() ]]</span>
42
+ <span class="label">Current Time</span>
43
+ </div>
44
+ <div class="stat">
45
+ <span class="value">✓</span>
46
+ <span class="label">Logged In</span>
47
+ </div>
48
+ </div>
49
+
50
+ <div class="actions">
51
+ <a href="/" b-link class="btn">← Back to Home</a>
52
+ </div>
53
+ </main>
54
+ </div>
55
+ </template>
56
+
57
+ <style>
58
+ self {
59
+ display: block;
60
+ min-height: 100vh;
61
+ background: linear-gradient(180deg, #1a1a2e 0%, #16213e 100%);
62
+ color: white;
63
+ padding: 2rem;
64
+ }
65
+
66
+ header {
67
+ display: flex;
68
+ justify-content: space-between;
69
+ align-items: center;
70
+ max-width: 800px;
71
+ margin: 0 auto 3rem;
72
+ }
73
+
74
+ header h1 {
75
+ font-size: 1.75rem;
76
+ }
77
+
78
+ header button {
79
+ background: rgba(255,255,255,0.1);
80
+ color: white;
81
+ border: 1px solid rgba(255,255,255,0.2);
82
+ padding: 0.5rem 1rem;
83
+ border-radius: 6px;
84
+ cursor: pointer;
85
+ transition: background 0.2s;
86
+ }
87
+
88
+ header button:hover {
89
+ background: rgba(255,255,255,0.2);
90
+ }
91
+
92
+ main {
93
+ max-width: 800px;
94
+ margin: 0 auto;
95
+ }
96
+
97
+ .card {
98
+ background: rgba(255,255,255,0.05);
99
+ padding: 2rem;
100
+ border-radius: 16px;
101
+ margin-bottom: 2rem;
102
+ border: 1px solid rgba(255,255,255,0.1);
103
+ }
104
+
105
+ .card h2 {
106
+ color: #4ecca3;
107
+ margin-bottom: 1rem;
108
+ }
109
+
110
+ .card p {
111
+ opacity: 0.8;
112
+ line-height: 1.6;
113
+ margin-bottom: 0.5rem;
114
+ }
115
+
116
+ .stats {
117
+ display: grid;
118
+ grid-template-columns: repeat(3, 1fr);
119
+ gap: 1rem;
120
+ margin-bottom: 2rem;
121
+ }
122
+
123
+ .stat {
124
+ background: rgba(255,255,255,0.05);
125
+ padding: 1.5rem;
126
+ border-radius: 12px;
127
+ text-align: center;
128
+ border: 1px solid rgba(255,255,255,0.1);
129
+ }
130
+
131
+ .stat .value {
132
+ display: block;
133
+ font-size: 2rem;
134
+ font-weight: bold;
135
+ color: #4ecca3;
136
+ margin-bottom: 0.5rem;
137
+ }
138
+
139
+ .stat .label {
140
+ font-size: 0.85rem;
141
+ opacity: 0.7;
142
+ }
143
+
144
+ .actions {
145
+ text-align: center;
146
+ }
147
+
148
+ .btn {
149
+ display: inline-block;
150
+ padding: 0.75rem 1.5rem;
151
+ background: #4ecca3;
152
+ color: #1a1a2e;
153
+ text-decoration: none;
154
+ border-radius: 8px;
155
+ font-weight: 600;
156
+ }
157
+ </style>
158
+
159
+ <script>
160
+ export default {
161
+ visitCount: 0,
162
+
163
+ get username() {
164
+ // Get username from global state (set by login page)
165
+ return Lego.globals.user?.name || 'Guest';
166
+ },
167
+
168
+ mounted() {
169
+ this.visitCount++;
170
+
171
+ // Redirect to login if not authenticated
172
+ if (!Lego.globals.user) {
173
+ console.log('Not logged in, redirecting...');
174
+ this.$go('/login').get();
175
+ }
176
+ },
177
+
178
+ formatTime() {
179
+ return new Date().toLocaleTimeString();
180
+ },
181
+
182
+ handleLogout() {
183
+ // Clear global user state
184
+ Lego.globals.user = null;
185
+ this.$go('/').get();
186
+ }
187
+ }
188
+ </script>
189
+ ```
190
+
191
+ ## Step 2: Update Login to Set Global State
192
+
193
+ The login page needs to store the user in global state. Update `src/components/login-page.lego`'s script section:
194
+
195
+ ```javascript
196
+ <script>
197
+ export default {
198
+ email: '',
199
+ password: '',
200
+
201
+ handleSubmit(event) {
202
+ event.preventDefault();
203
+
204
+ // Store user in GLOBAL state (accessible from any component!)
205
+ Lego.globals.user = {
206
+ name: this.email.split('@')[0], // Use email prefix as name
207
+ email: this.email,
208
+ loggedInAt: new Date()
209
+ };
210
+
211
+ console.log('User logged in:', Lego.globals.user);
212
+
213
+ // Navigate to welcome page
214
+ this.$go('/welcome').get();
215
+ }
216
+ }
217
+ </script>
218
+ ```
219
+
220
+ ## Step 3: Update Home Page Navigation
221
+
222
+ Update `src/components/home-page.lego`'s `handleClick` method:
223
+
224
+ ```javascript
225
+ handleClick() {
226
+ this.$go('/login').get();
227
+ }
228
+ ```
229
+
230
+ ## Step 4: Complete `app.js`
231
+
232
+ Here's the final `src/app.js`:
233
+
234
+ ```javascript
235
+ import { Lego } from 'lego-dom';
236
+ import registerComponents from 'virtual:lego-components';
237
+
238
+ // Register all .lego components
239
+ registerComponents();
240
+
241
+ // Define routes
242
+ Lego.route('/', 'home-page');
243
+ Lego.route('/login', 'login-page');
244
+ Lego.route('/welcome', 'welcome-page');
245
+
246
+ // Initialize with default user state
247
+ Lego.globals.user = null;
248
+
249
+ // Start the engine
250
+ await Lego.init();
251
+ ```
252
+
253
+ ## Test the Complete Flow
254
+
255
+ ```bash
256
+ npm run dev
257
+ ```
258
+
259
+ 1. **Home Page** (`/`) – Click "Get Started"
260
+ 2. **Login Page** (`/login`) – Enter an email, click "Sign In"
261
+ 3. **Welcome Page** (`/welcome`) – See your username displayed!
262
+ 4. Click "Logout" – Clears state, returns to home
263
+ 5. Try navigating directly to `/welcome` – Redirects to login!
264
+
265
+ ## The Data Flow
266
+
267
+ ```
268
+ ┌─────────────────────────────────────────────────────────────┐
269
+ │ Lego.globals │
270
+ │ ┌─────────────────────────────────────────────────────┐ │
271
+ │ │ user: { name: 'john', email: 'john@test.com' } │ │
272
+ │ └─────────────────────────────────────────────────────┘ │
273
+ └─────────────────────────────────────────────────────────────┘
274
+ ▲ │
275
+ │ writes │ reads
276
+ │ ▼
277
+ ┌──────────┐ ┌──────────────┐
278
+ │ login │ │ welcome │
279
+ │ page │ │ page │
280
+ └──────────┘ └──────────────┘
281
+ ```
282
+
283
+ ## Auth Guard Pattern
284
+
285
+ Want to protect routes? Add middleware:
286
+
287
+ ```javascript
288
+ // In app.js
289
+
290
+ const requireAuth = (params, globals) => {
291
+ if (!globals.user) {
292
+ globals.$go('/login').get();
293
+ return false; // Block navigation
294
+ }
295
+ return true; // Allow navigation
296
+ };
297
+
298
+ Lego.route('/welcome', 'welcome-page', requireAuth);
299
+ Lego.route('/dashboard', 'dashboard-page', requireAuth);
300
+ Lego.route('/settings', 'settings-page', requireAuth);
301
+ ```
302
+
303
+ ## What You've Built
304
+
305
+ ✅ A complete login flow: home → login → welcome
306
+ ✅ Global state shared across components
307
+ ✅ Logout functionality that clears state
308
+ ✅ Automatic redirect when not authenticated
309
+ ✅ Route protection with middleware
310
+
311
+ ## The Full Picture
312
+
313
+ | File | Purpose |
314
+ |------|---------|
315
+ | `index.html` | Entry HTML with `<lego-router>` |
316
+ | `src/app.js` | Routes, globals, initialization |
317
+ | `src/components/*.lego` | Your page components |
318
+ | `vite.config.js` | Vite + LegoDOM plugin setup |
319
+
320
+ ---
321
+
322
+ <div style="display: flex; justify-content: space-between; margin-top: 3rem;">
323
+ <a href="./03-adding-routes" style="display: inline-block; background: #eee; color: #333; padding: 0.75rem 1.5rem; border-radius: 6px; text-decoration: none; font-weight: 600;">
324
+ ← Previous: Adding Routes
325
+ </a>
326
+ <a href="./05-state-and-globals" style="display: inline-block; background: #4CAF50; color: white; padding: 0.75rem 1.5rem; border-radius: 6px; text-decoration: none; font-weight: 600;">
327
+ Next: State & Globals →
328
+ </a>
329
+ </div>