pulse-js-framework 1.7.5 → 1.7.8
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 +78 -392
- package/cli/dev.js +14 -0
- package/cli/docs-test.js +633 -0
- package/cli/index.js +313 -31
- package/cli/lint.js +13 -4
- package/cli/logger.js +32 -4
- package/cli/release.js +50 -20
- package/compiler/parser.js +1 -1
- package/package.json +11 -4
- package/runtime/dom-advanced.js +357 -0
- package/runtime/dom-binding.js +230 -0
- package/runtime/dom-conditional.js +133 -0
- package/runtime/dom-element.js +142 -0
- package/runtime/dom-lifecycle.js +178 -0
- package/runtime/dom-list.js +267 -0
- package/runtime/dom-selector.js +267 -0
- package/runtime/dom.js +119 -1279
- package/runtime/form.js +417 -22
- package/runtime/native.js +398 -52
- package/runtime/pulse.js +1 -1
- package/runtime/router.js +6 -5
- package/runtime/store.js +81 -6
- package/types/async.d.ts +310 -0
- package/types/form.d.ts +378 -0
- package/types/index.d.ts +44 -0
- /package/{core → runtime}/errors.js +0 -0
package/README.md
CHANGED
|
@@ -13,9 +13,12 @@ A declarative DOM framework with CSS selector-based structure and reactive pulsa
|
|
|
13
13
|
- **No Build Required** - Works directly in the browser
|
|
14
14
|
- **Lightweight** - Minimal footprint, maximum performance
|
|
15
15
|
- **Router & Store** - Built-in SPA routing and state management
|
|
16
|
+
- **Form Handling** - Validation, async validators, field arrays
|
|
17
|
+
- **Async Primitives** - useAsync, useResource, usePolling with SWR caching
|
|
16
18
|
- **Hot Module Replacement** - Full HMR with state preservation
|
|
17
19
|
- **Mobile Apps** - Build native Android & iOS apps (zero dependencies)
|
|
18
20
|
- **TypeScript Support** - Full type definitions for IDE autocomplete
|
|
21
|
+
- **DevTools** - Time-travel debugging, dependency graph visualization
|
|
19
22
|
|
|
20
23
|
## Installation
|
|
21
24
|
|
|
@@ -54,10 +57,7 @@ function Counter() {
|
|
|
54
57
|
const increment = el('button.btn', '+');
|
|
55
58
|
increment.onclick = () => count.update(n => n + 1);
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
decrement.onclick = () => count.update(n => n - 1);
|
|
59
|
-
|
|
60
|
-
div.append(display, increment, decrement);
|
|
60
|
+
div.append(display, increment);
|
|
61
61
|
return div;
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -66,8 +66,6 @@ mount('#app', Counter());
|
|
|
66
66
|
|
|
67
67
|
## CSS Selector Syntax
|
|
68
68
|
|
|
69
|
-
Create DOM elements using familiar CSS syntax:
|
|
70
|
-
|
|
71
69
|
```javascript
|
|
72
70
|
el('div') // <div></div>
|
|
73
71
|
el('.container') // <div class="container"></div>
|
|
@@ -80,23 +78,24 @@ el('h1', 'Hello World') // <h1>Hello World</h1>
|
|
|
80
78
|
## Reactivity
|
|
81
79
|
|
|
82
80
|
```javascript
|
|
83
|
-
import { pulse, effect, computed } from 'pulse-js-framework';
|
|
81
|
+
import { pulse, effect, computed, batch } from 'pulse-js-framework';
|
|
84
82
|
|
|
85
|
-
// Create reactive values
|
|
86
83
|
const firstName = pulse('John');
|
|
87
84
|
const lastName = pulse('Doe');
|
|
88
85
|
|
|
89
86
|
// Computed values
|
|
90
|
-
const fullName = computed(() =>
|
|
91
|
-
`${firstName.get()} ${lastName.get()}`
|
|
92
|
-
);
|
|
87
|
+
const fullName = computed(() => `${firstName.get()} ${lastName.get()}`);
|
|
93
88
|
|
|
94
89
|
// Effects auto-run when dependencies change
|
|
95
|
-
effect(() => {
|
|
96
|
-
console.log(`Hello, ${fullName.get()}!`);
|
|
97
|
-
});
|
|
90
|
+
effect(() => console.log(`Hello, ${fullName.get()}!`));
|
|
98
91
|
|
|
99
92
|
firstName.set('Jane'); // Logs: "Hello, Jane Doe!"
|
|
93
|
+
|
|
94
|
+
// Batch updates (effects run once)
|
|
95
|
+
batch(() => {
|
|
96
|
+
firstName.set('John');
|
|
97
|
+
lastName.set('Smith');
|
|
98
|
+
});
|
|
100
99
|
```
|
|
101
100
|
|
|
102
101
|
## .pulse File Format
|
|
@@ -104,472 +103,159 @@ firstName.set('Jane'); // Logs: "Hello, Jane Doe!"
|
|
|
104
103
|
```pulse
|
|
105
104
|
@page Counter
|
|
106
105
|
|
|
107
|
-
// Import other components
|
|
108
|
-
import Button from './Button.pulse'
|
|
109
|
-
import { Header, Footer } from './components.pulse'
|
|
110
|
-
|
|
111
106
|
state {
|
|
112
107
|
count: 0
|
|
113
108
|
}
|
|
114
109
|
|
|
115
110
|
view {
|
|
116
111
|
.counter {
|
|
117
|
-
Header
|
|
118
112
|
h1 "Count: {count}"
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
Footer
|
|
113
|
+
button @click(count++) "+"
|
|
114
|
+
button @click(count--) "-"
|
|
122
115
|
}
|
|
123
116
|
}
|
|
124
117
|
|
|
125
118
|
style {
|
|
126
|
-
.counter {
|
|
127
|
-
text-align: center
|
|
128
|
-
padding: 20px
|
|
129
|
-
}
|
|
119
|
+
.counter { text-align: center; padding: 20px }
|
|
130
120
|
}
|
|
131
121
|
```
|
|
132
122
|
|
|
133
|
-
|
|
134
|
-
- Import statements for component composition
|
|
135
|
-
- Slots for content projection (`slot`, `slot "name"`)
|
|
136
|
-
- CSS scoping (styles are automatically scoped to component)
|
|
137
|
-
- Native `router {}` and `store {}` blocks
|
|
138
|
-
- Detailed error messages with line/column info
|
|
139
|
-
- Source map generation (v1.4.9) for debugging original `.pulse` code
|
|
140
|
-
|
|
141
|
-
### Router & Store DSL (v1.4.0)
|
|
123
|
+
See [Pulse DSL documentation](docs/pulse-dsl.md) for full syntax reference.
|
|
142
124
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
```pulse
|
|
146
|
-
@page App
|
|
147
|
-
|
|
148
|
-
router {
|
|
149
|
-
mode: "hash"
|
|
150
|
-
base: "/app"
|
|
151
|
-
routes {
|
|
152
|
-
"/": HomePage
|
|
153
|
-
"/users": UsersPage
|
|
154
|
-
"/users/:id": UserDetailPage
|
|
155
|
-
}
|
|
156
|
-
beforeEach(to, from) {
|
|
157
|
-
if (!store.isAuthenticated) return "/login"
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
store {
|
|
162
|
-
state {
|
|
163
|
-
user: null
|
|
164
|
-
theme: "dark"
|
|
165
|
-
}
|
|
166
|
-
getters {
|
|
167
|
-
isAuthenticated() { return this.user !== null }
|
|
168
|
-
}
|
|
169
|
-
actions {
|
|
170
|
-
login(userData) { this.user = userData }
|
|
171
|
-
logout() { this.user = null }
|
|
172
|
-
toggleTheme() { this.theme = this.theme === "dark" ? "light" : "dark" }
|
|
173
|
-
}
|
|
174
|
-
persist: true
|
|
175
|
-
storageKey: "my-app"
|
|
176
|
-
}
|
|
125
|
+
## CLI Commands
|
|
177
126
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
127
|
+
```bash
|
|
128
|
+
pulse create <name> # Create new project
|
|
129
|
+
pulse dev [port] # Start dev server (default: 3000)
|
|
130
|
+
pulse build # Build for production
|
|
131
|
+
pulse compile <file> # Compile .pulse file
|
|
132
|
+
pulse lint [files] # Validate .pulse files
|
|
133
|
+
pulse format [files] # Format .pulse files
|
|
134
|
+
pulse analyze # Analyze bundle
|
|
189
135
|
```
|
|
190
136
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
- `@link("/path")` directive for navigation links
|
|
195
|
-
- `@outlet` directive for route content
|
|
196
|
-
- `@navigate`, `@back`, `@forward` directives
|
|
197
|
-
|
|
198
|
-
**Store DSL Features:**
|
|
199
|
-
- Reactive state with automatic signal creation
|
|
200
|
-
- Getters with `this.x` → `store.x.get()` transformation
|
|
201
|
-
- Actions with `this.x = y` → `store.x.set(y)` transformation
|
|
202
|
-
- Built-in persistence via `persist: true`
|
|
203
|
-
- Custom storage key
|
|
204
|
-
|
|
205
|
-
## API Reference
|
|
206
|
-
|
|
207
|
-
### Reactivity
|
|
208
|
-
|
|
209
|
-
- `pulse(value)` - Create a reactive value
|
|
210
|
-
- `pulse.get()` - Read value (tracks dependency)
|
|
211
|
-
- `pulse.set(value)` - Set new value
|
|
212
|
-
- `pulse.update(fn)` - Update with function
|
|
213
|
-
- `pulse.peek()` - Read without tracking
|
|
214
|
-
- `effect(fn)` - Create reactive side effect
|
|
215
|
-
- `computed(fn)` - Create derived value
|
|
216
|
-
- `batch(fn)` - Batch multiple updates
|
|
217
|
-
|
|
218
|
-
### DOM
|
|
219
|
-
|
|
220
|
-
- `el(selector, ...children)` - Create element
|
|
221
|
-
- `mount(target, element)` - Mount to DOM
|
|
222
|
-
- `text(fn)` - Reactive text node
|
|
223
|
-
- `list(items, template)` - Reactive list
|
|
224
|
-
- `when(condition, then, else)` - Conditional render
|
|
137
|
+
See [CLI documentation](docs/cli.md) for full command reference.
|
|
138
|
+
|
|
139
|
+
## Core Modules
|
|
225
140
|
|
|
226
141
|
### Router
|
|
227
142
|
|
|
228
143
|
```javascript
|
|
229
|
-
import { createRouter } from 'pulse-js-framework/runtime/router
|
|
144
|
+
import { createRouter, lazy } from 'pulse-js-framework/runtime/router';
|
|
230
145
|
|
|
231
146
|
const router = createRouter({
|
|
232
147
|
routes: {
|
|
233
148
|
'/': HomePage,
|
|
234
|
-
'/about': AboutPage,
|
|
235
149
|
'/users/:id': UserPage,
|
|
236
|
-
'/
|
|
237
|
-
handler: AdminPage,
|
|
238
|
-
meta: { requiresAuth: true },
|
|
239
|
-
beforeEnter: (to, from) => {
|
|
240
|
-
if (!isAuthenticated()) return '/login';
|
|
241
|
-
},
|
|
242
|
-
children: {
|
|
243
|
-
'/users': AdminUsersPage,
|
|
244
|
-
'/settings': AdminSettingsPage
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
|
-
scrollBehavior: (to, from, savedPosition) => {
|
|
249
|
-
return savedPosition || { x: 0, y: 0 };
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
// Navigation guards
|
|
254
|
-
router.beforeEach((to, from) => {
|
|
255
|
-
if (to.meta.requiresAuth && !isAuthenticated()) {
|
|
256
|
-
return '/login';
|
|
150
|
+
'/dashboard': lazy(() => import('./Dashboard.js'))
|
|
257
151
|
}
|
|
258
152
|
});
|
|
259
|
-
|
|
260
|
-
router.start();
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
**Router Features:**
|
|
264
|
-
- Route params (`:id`) and wildcards (`*path`)
|
|
265
|
-
- Nested routes with `children`
|
|
266
|
-
- Route meta fields
|
|
267
|
-
- Per-route guards (`beforeEnter`)
|
|
268
|
-
- Global guards (`beforeEach`, `beforeResolve`, `afterEach`)
|
|
269
|
-
- Scroll restoration
|
|
270
|
-
- Lazy-loaded routes with `lazy()` and `preload()`
|
|
271
|
-
- Middleware pipeline (Koa-style)
|
|
272
|
-
|
|
273
|
-
#### Lazy Loading & Middleware (v1.4.9)
|
|
274
|
-
|
|
275
|
-
```javascript
|
|
276
|
-
import { createRouter, lazy, preload } from 'pulse-js-framework/runtime/router';
|
|
277
|
-
|
|
278
|
-
// Lazy load components
|
|
279
|
-
const routes = {
|
|
280
|
-
'/': HomePage,
|
|
281
|
-
'/dashboard': lazy(() => import('./Dashboard.js')),
|
|
282
|
-
'/settings': lazy(() => import('./Settings.js'), {
|
|
283
|
-
loading: () => el('div.spinner', 'Loading...'),
|
|
284
|
-
error: (err) => el('div.error', `Failed: ${err.message}`),
|
|
285
|
-
timeout: 5000
|
|
286
|
-
})
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
// Preload on hover
|
|
290
|
-
link.addEventListener('mouseenter', () => preload(routes['/dashboard']));
|
|
291
|
-
|
|
292
|
-
// Middleware
|
|
293
|
-
const router = createRouter({
|
|
294
|
-
routes,
|
|
295
|
-
middleware: [
|
|
296
|
-
// Logger middleware
|
|
297
|
-
async (ctx, next) => {
|
|
298
|
-
console.log('Navigating to:', ctx.to.path);
|
|
299
|
-
await next();
|
|
300
|
-
},
|
|
301
|
-
// Auth middleware
|
|
302
|
-
async (ctx, next) => {
|
|
303
|
-
if (ctx.to.meta.requiresAuth && !isLoggedIn()) {
|
|
304
|
-
return ctx.redirect('/login');
|
|
305
|
-
}
|
|
306
|
-
await next();
|
|
307
|
-
}
|
|
308
|
-
]
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
// Add middleware dynamically
|
|
312
|
-
const unsubscribe = router.use(async (ctx, next) => {
|
|
313
|
-
ctx.meta.startTime = Date.now();
|
|
314
|
-
await next();
|
|
315
|
-
});
|
|
316
153
|
```
|
|
317
154
|
|
|
318
155
|
### Store
|
|
319
156
|
|
|
320
157
|
```javascript
|
|
321
|
-
import { createStore } from 'pulse-js-framework/runtime/store
|
|
322
|
-
|
|
323
|
-
const store = createStore({
|
|
324
|
-
user: null,
|
|
325
|
-
theme: 'light'
|
|
326
|
-
}, { persist: true });
|
|
327
|
-
|
|
328
|
-
store.user.set({ name: 'John' });
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
### HMR (Hot Module Replacement)
|
|
332
|
-
|
|
333
|
-
Pulse supports full HMR with state preservation during development:
|
|
334
|
-
|
|
335
|
-
```javascript
|
|
336
|
-
import { createHMRContext } from 'pulse-js-framework/runtime/hmr';
|
|
158
|
+
import { createStore, createActions } from 'pulse-js-framework/runtime/store';
|
|
337
159
|
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const count = hmr.preservePulse('count', 0);
|
|
342
|
-
const items = hmr.preservePulse('items', []);
|
|
343
|
-
|
|
344
|
-
// Effects tracked for automatic cleanup
|
|
345
|
-
hmr.setup(() => {
|
|
346
|
-
effect(() => {
|
|
347
|
-
document.title = `Count: ${count.get()}`;
|
|
348
|
-
});
|
|
160
|
+
const store = createStore({ user: null, theme: 'light' }, { persist: true });
|
|
161
|
+
const actions = createActions(store, {
|
|
162
|
+
login: (store, user) => store.user.set(user)
|
|
349
163
|
});
|
|
350
|
-
|
|
351
|
-
// Accept HMR updates
|
|
352
|
-
hmr.accept();
|
|
353
164
|
```
|
|
354
165
|
|
|
355
|
-
|
|
356
|
-
- `preservePulse(key, value)` - Create pulses that survive module replacement
|
|
357
|
-
- `setup(callback)` - Execute with effect tracking for cleanup
|
|
358
|
-
- Automatic event listener cleanup (no accumulation)
|
|
359
|
-
- Works with Vite dev server
|
|
360
|
-
|
|
361
|
-
### Logger
|
|
166
|
+
### Form
|
|
362
167
|
|
|
363
168
|
```javascript
|
|
364
|
-
import {
|
|
365
|
-
|
|
366
|
-
// Create a namespaced logger
|
|
367
|
-
const log = createLogger('MyComponent');
|
|
368
|
-
|
|
369
|
-
log.info('Component initialized'); // [MyComponent] Component initialized
|
|
370
|
-
log.warn('Deprecated method used');
|
|
371
|
-
log.error('Failed to load', error);
|
|
372
|
-
log.debug('Detailed info'); // Only shown at DEBUG level
|
|
373
|
-
|
|
374
|
-
// Set global log level
|
|
375
|
-
setLogLevel(LogLevel.DEBUG); // SILENT, ERROR, WARN, INFO, DEBUG
|
|
376
|
-
|
|
377
|
-
// Child loggers for sub-namespaces
|
|
378
|
-
const childLog = log.child('Validation');
|
|
379
|
-
childLog.info('Validating'); // [MyComponent:Validation] Validating
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
## CLI Commands
|
|
383
|
-
|
|
384
|
-
```bash
|
|
385
|
-
# Project Commands
|
|
386
|
-
pulse create <name> # Create new project
|
|
387
|
-
pulse dev [port] # Start dev server (default: 3000)
|
|
388
|
-
pulse build # Build for production
|
|
389
|
-
pulse preview [port] # Preview production build (default: 4173)
|
|
390
|
-
pulse compile <file> # Compile .pulse file to JavaScript
|
|
391
|
-
|
|
392
|
-
# Code Quality
|
|
393
|
-
pulse lint [files] # Validate .pulse files for errors and style
|
|
394
|
-
pulse lint --fix # Auto-fix fixable issues
|
|
395
|
-
pulse format [files] # Format .pulse files consistently
|
|
396
|
-
pulse format --check # Check formatting without writing
|
|
397
|
-
pulse analyze # Analyze bundle size and dependencies
|
|
398
|
-
pulse analyze --json # Output analysis as JSON
|
|
399
|
-
|
|
400
|
-
# Mobile
|
|
401
|
-
pulse mobile init # Initialize mobile platforms
|
|
402
|
-
pulse mobile build android|ios # Build native app
|
|
403
|
-
pulse mobile run android|ios # Run on device/emulator
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
### Lint Command
|
|
407
|
-
|
|
408
|
-
Validates `.pulse` files for errors and style issues:
|
|
409
|
-
|
|
410
|
-
```bash
|
|
411
|
-
pulse lint src/ # Lint all files in src/
|
|
412
|
-
pulse lint "**/*.pulse" # Lint all .pulse files
|
|
413
|
-
pulse lint --fix # Auto-fix fixable issues
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
**Checks performed:**
|
|
417
|
-
- Undefined references (state variables, components)
|
|
418
|
-
- Unused imports and state variables
|
|
419
|
-
- Naming conventions (PascalCase for pages, camelCase for state)
|
|
420
|
-
- Empty blocks
|
|
421
|
-
- Import order
|
|
169
|
+
import { useForm, validators } from 'pulse-js-framework/runtime/form';
|
|
422
170
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
pulse format --check # Check without writing (CI mode)
|
|
171
|
+
const { fields, handleSubmit, isValid } = useForm(
|
|
172
|
+
{ email: '', password: '' },
|
|
173
|
+
{
|
|
174
|
+
email: [validators.required(), validators.email()],
|
|
175
|
+
password: [validators.required(), validators.minLength(8)]
|
|
176
|
+
}
|
|
177
|
+
);
|
|
431
178
|
```
|
|
432
179
|
|
|
433
|
-
|
|
434
|
-
- 2-space indentation
|
|
435
|
-
- Sorted imports (alphabetically)
|
|
436
|
-
- Consistent brace placement
|
|
437
|
-
- Proper spacing around operators
|
|
180
|
+
### Async
|
|
438
181
|
|
|
439
|
-
|
|
182
|
+
```javascript
|
|
183
|
+
import { useAsync, useResource } from 'pulse-js-framework/runtime/async';
|
|
440
184
|
|
|
441
|
-
|
|
185
|
+
const { data, loading } = useAsync(() => fetch('/api/users').then(r => r.json()));
|
|
442
186
|
|
|
443
|
-
|
|
444
|
-
pulse analyze # Console report
|
|
445
|
-
pulse analyze --json # JSON output
|
|
446
|
-
pulse analyze --verbose # Detailed metrics
|
|
187
|
+
const users = useResource('users', fetchUsers, { refreshInterval: 30000 });
|
|
447
188
|
```
|
|
448
189
|
|
|
449
|
-
|
|
450
|
-
- File count and total size
|
|
451
|
-
- Component complexity scores
|
|
452
|
-
- Import dependency graph
|
|
453
|
-
- Dead code detection (unreachable files)
|
|
190
|
+
See [API documentation](docs/api.md) for full reference.
|
|
454
191
|
|
|
455
192
|
## Mobile Apps
|
|
456
193
|
|
|
457
|
-
Build native Android and iOS apps
|
|
194
|
+
Build native Android and iOS apps:
|
|
458
195
|
|
|
459
196
|
```bash
|
|
460
|
-
# Initialize mobile platforms
|
|
461
197
|
pulse mobile init
|
|
462
|
-
|
|
463
|
-
# Build your web app first
|
|
464
198
|
pulse build
|
|
465
|
-
|
|
466
|
-
# Build for Android (requires Android SDK)
|
|
467
199
|
pulse mobile build android
|
|
468
|
-
|
|
469
|
-
# Build for iOS (requires macOS + Xcode)
|
|
470
|
-
pulse mobile build ios
|
|
471
|
-
|
|
472
|
-
# Run on device/emulator
|
|
473
200
|
pulse mobile run android
|
|
474
201
|
```
|
|
475
202
|
|
|
476
|
-
### Native APIs
|
|
477
|
-
|
|
478
|
-
Access native features in your Pulse app:
|
|
479
|
-
|
|
480
203
|
```javascript
|
|
481
|
-
import { createNativeStorage, NativeUI
|
|
482
|
-
|
|
483
|
-
onNativeReady(({ platform }) => {
|
|
484
|
-
console.log(`Running on ${platform}`); // 'android', 'ios', or 'web'
|
|
485
|
-
|
|
486
|
-
// Persistent native storage with Pulse reactivity
|
|
487
|
-
const storage = createNativeStorage();
|
|
488
|
-
const count = storage.get('count', 0);
|
|
489
|
-
count.set(42); // Auto-persisted to native storage
|
|
204
|
+
import { createNativeStorage, NativeUI } from 'pulse-js-framework/runtime/native';
|
|
490
205
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
// Haptic feedback
|
|
495
|
-
NativeUI.vibrate(100);
|
|
496
|
-
});
|
|
206
|
+
const storage = createNativeStorage();
|
|
207
|
+
const theme = storage.get('theme', 'light');
|
|
208
|
+
NativeUI.toast('Hello from native!');
|
|
497
209
|
```
|
|
498
210
|
|
|
499
|
-
|
|
211
|
+
See [Mobile documentation](docs/mobile.md) for full guide.
|
|
500
212
|
|
|
501
213
|
## IDE Extensions
|
|
502
214
|
|
|
503
|
-
Pulse provides extensions for popular IDEs with syntax highlighting and snippets.
|
|
504
|
-
|
|
505
215
|
### VS Code
|
|
506
216
|
|
|
507
217
|
```bash
|
|
508
|
-
|
|
509
|
-
cd vscode-extension
|
|
510
|
-
powershell -ExecutionPolicy Bypass -File install.ps1
|
|
511
|
-
|
|
512
|
-
# macOS/Linux
|
|
513
|
-
cd vscode-extension
|
|
514
|
-
bash install.sh
|
|
218
|
+
cd vscode-extension && bash install.sh
|
|
515
219
|
```
|
|
516
220
|
|
|
517
|
-
Then restart VS Code. You'll get:
|
|
518
|
-
- Syntax highlighting for `.pulse` files
|
|
519
|
-
- Code snippets (`page`, `state`, `view`, `@click`, etc.)
|
|
520
|
-
- Bracket matching and auto-closing
|
|
521
|
-
- Comment toggling (Ctrl+/)
|
|
522
|
-
|
|
523
221
|
### IntelliJ IDEA / WebStorm
|
|
524
222
|
|
|
525
223
|
```bash
|
|
526
|
-
cd intellij-plugin
|
|
527
|
-
./gradlew buildPlugin
|
|
224
|
+
cd intellij-plugin && ./gradlew buildPlugin
|
|
528
225
|
```
|
|
529
226
|
|
|
530
|
-
|
|
531
|
-
1. Open **Settings** > **Plugins**
|
|
532
|
-
2. Click gear icon > **Install Plugin from Disk...**
|
|
533
|
-
3. Select the `.zip` file and restart
|
|
534
|
-
|
|
535
|
-
Features:
|
|
536
|
-
- Syntax highlighting for `.pulse` files
|
|
537
|
-
- 17 Live Templates (snippets)
|
|
538
|
-
- Code folding for blocks
|
|
539
|
-
- Customizable color scheme
|
|
227
|
+
Features: Syntax highlighting, code snippets, bracket matching.
|
|
540
228
|
|
|
541
229
|
## TypeScript Support
|
|
542
230
|
|
|
543
|
-
|
|
231
|
+
Full type definitions included:
|
|
544
232
|
|
|
545
233
|
```typescript
|
|
546
|
-
import { pulse,
|
|
547
|
-
import { el, list, when } from 'pulse-js-framework/runtime';
|
|
234
|
+
import { pulse, Pulse } from 'pulse-js-framework/runtime';
|
|
548
235
|
import { createRouter, Router } from 'pulse-js-framework/runtime/router';
|
|
549
|
-
import { createStore, Store } from 'pulse-js-framework/runtime/store';
|
|
550
236
|
|
|
551
|
-
// Full autocomplete for all APIs
|
|
552
237
|
const count: Pulse<number> = pulse(0);
|
|
553
|
-
const doubled = computed(() => count.get() * 2);
|
|
554
|
-
|
|
555
|
-
effect(() => {
|
|
556
|
-
console.log(count.get()); // Type-safe access
|
|
557
|
-
});
|
|
558
238
|
```
|
|
559
239
|
|
|
560
|
-
Types are automatically detected by IDEs (VS Code, WebStorm) without additional configuration.
|
|
561
|
-
|
|
562
240
|
## Examples
|
|
563
241
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
242
|
+
| Example | Description |
|
|
243
|
+
|---------|-------------|
|
|
244
|
+
| [Todo App](examples/todo) | Task management with filters |
|
|
245
|
+
| [Blog](examples/blog) | CRUD, categories, search |
|
|
246
|
+
| [Chat App](examples/chat) | Real-time messaging |
|
|
247
|
+
| [E-commerce](examples/ecommerce) | Shopping cart |
|
|
248
|
+
| [Dashboard](examples/dashboard) | Admin UI |
|
|
249
|
+
| [HMR Demo](examples/hmr) | Hot module replacement |
|
|
250
|
+
| [Router Demo](examples/router) | SPA routing |
|
|
251
|
+
| [Store Demo](examples/store) | State with undo/redo |
|
|
252
|
+
|
|
253
|
+
## Documentation
|
|
254
|
+
|
|
255
|
+
- [API Reference](docs/api.md) - Complete API documentation
|
|
256
|
+
- [CLI Commands](docs/cli.md) - Command line interface
|
|
257
|
+
- [Pulse DSL](docs/pulse-dsl.md) - .pulse file syntax
|
|
258
|
+
- [Mobile Apps](docs/mobile.md) - Native Android & iOS
|
|
573
259
|
|
|
574
260
|
## License
|
|
575
261
|
|
package/cli/dev.js
CHANGED
|
@@ -242,6 +242,20 @@ export async function startDevServer(args) {
|
|
|
242
242
|
return;
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
// SPA fallback: serve index.html for routes without file extensions
|
|
246
|
+
// This handles client-side routing (e.g., /fr/, /getting-started, /fr/api-reference)
|
|
247
|
+
const ext = extname(pathname);
|
|
248
|
+
if (!ext || ext === '') {
|
|
249
|
+
const indexPath = join(root, 'index.html');
|
|
250
|
+
if (existsSync(indexPath)) {
|
|
251
|
+
let content = readFileSync(indexPath, 'utf-8');
|
|
252
|
+
content = content.replace('</body>', LIVERELOAD_SCRIPT);
|
|
253
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
254
|
+
res.end(content);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
245
259
|
// 404
|
|
246
260
|
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
247
261
|
res.end('Not Found');
|