pulse-js-framework 1.4.1 → 1.4.3
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 +414 -414
- package/cli/analyze.js +499 -499
- package/cli/build.js +341 -341
- package/cli/dev.js +3 -3
- package/cli/format.js +704 -704
- package/cli/index.js +398 -398
- package/cli/lint.js +642 -642
- package/cli/utils/file-utils.js +298 -298
- package/compiler/lexer.js +766 -766
- package/compiler/parser.js +1797 -1797
- package/compiler/transformer.js +1332 -1332
- package/index.js +1 -1
- package/loader/vite-plugin.js +1 -1
- package/package.json +68 -68
- package/runtime/router.js +596 -596
package/README.md
CHANGED
|
@@ -1,414 +1,414 @@
|
|
|
1
|
-
# Pulse Framework
|
|
2
|
-
|
|
3
|
-
[](https://github.com/vincenthirtz/pulse-js-framework/actions/workflows/ci.yml)
|
|
4
|
-
[](https://app.netlify.com/projects/pulse-js/deploys)
|
|
5
|
-
|
|
6
|
-
A declarative DOM framework with CSS selector-based structure and reactive pulsations.
|
|
7
|
-
|
|
8
|
-
## Features
|
|
9
|
-
|
|
10
|
-
- **CSS Selector Syntax** - Create DOM elements using familiar CSS selectors
|
|
11
|
-
- **Reactive Pulsations** - Automatic UI updates when state changes
|
|
12
|
-
- **Custom DSL** - Optional `.pulse` file format for cleaner code
|
|
13
|
-
- **No Build Required** - Works directly in the browser
|
|
14
|
-
- **Lightweight** - Minimal footprint, maximum performance
|
|
15
|
-
- **Router & Store** - Built-in SPA routing and state management
|
|
16
|
-
- **Mobile Apps** - Build native Android & iOS apps (zero dependencies)
|
|
17
|
-
|
|
18
|
-
## Installation
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
npm install pulse-js-framework
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Quick Start
|
|
25
|
-
|
|
26
|
-
### Create a new project
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
npx pulse create my-app
|
|
30
|
-
cd my-app
|
|
31
|
-
npm install
|
|
32
|
-
npm run dev
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Or use directly
|
|
36
|
-
|
|
37
|
-
```javascript
|
|
38
|
-
import { pulse, effect, el, mount } from 'pulse-js-framework';
|
|
39
|
-
|
|
40
|
-
// Create reactive state
|
|
41
|
-
const count = pulse(0);
|
|
42
|
-
|
|
43
|
-
// Build UI with CSS selector syntax
|
|
44
|
-
function Counter() {
|
|
45
|
-
const div = el('.counter');
|
|
46
|
-
|
|
47
|
-
const display = el('h1');
|
|
48
|
-
effect(() => {
|
|
49
|
-
display.textContent = `Count: ${count.get()}`;
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const increment = el('button.btn', '+');
|
|
53
|
-
increment.onclick = () => count.update(n => n + 1);
|
|
54
|
-
|
|
55
|
-
const decrement = el('button.btn', '-');
|
|
56
|
-
decrement.onclick = () => count.update(n => n - 1);
|
|
57
|
-
|
|
58
|
-
div.append(display, increment, decrement);
|
|
59
|
-
return div;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
mount('#app', Counter());
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## CSS Selector Syntax
|
|
66
|
-
|
|
67
|
-
Create DOM elements using familiar CSS syntax:
|
|
68
|
-
|
|
69
|
-
```javascript
|
|
70
|
-
el('div') // <div></div>
|
|
71
|
-
el('.container') // <div class="container"></div>
|
|
72
|
-
el('#app') // <div id="app"></div>
|
|
73
|
-
el('button.btn.primary') // <button class="btn primary"></button>
|
|
74
|
-
el('input[type=text]') // <input type="text">
|
|
75
|
-
el('h1', 'Hello World') // <h1>Hello World</h1>
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## Reactivity
|
|
79
|
-
|
|
80
|
-
```javascript
|
|
81
|
-
import { pulse, effect, computed } from 'pulse-js-framework';
|
|
82
|
-
|
|
83
|
-
// Create reactive values
|
|
84
|
-
const firstName = pulse('John');
|
|
85
|
-
const lastName = pulse('Doe');
|
|
86
|
-
|
|
87
|
-
// Computed values
|
|
88
|
-
const fullName = computed(() =>
|
|
89
|
-
`${firstName.get()} ${lastName.get()}`
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
// Effects auto-run when dependencies change
|
|
93
|
-
effect(() => {
|
|
94
|
-
console.log(`Hello, ${fullName.get()}!`);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
firstName.set('Jane'); // Logs: "Hello, Jane Doe!"
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## .pulse File Format
|
|
101
|
-
|
|
102
|
-
```pulse
|
|
103
|
-
@page Counter
|
|
104
|
-
|
|
105
|
-
// Import other components
|
|
106
|
-
import Button from './Button.pulse'
|
|
107
|
-
import { Header, Footer } from './components.pulse'
|
|
108
|
-
|
|
109
|
-
state {
|
|
110
|
-
count: 0
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
view {
|
|
114
|
-
.counter {
|
|
115
|
-
Header
|
|
116
|
-
h1 "Count: {count}"
|
|
117
|
-
Button @click(count++) "+"
|
|
118
|
-
Button @click(count--) "-"
|
|
119
|
-
Footer
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
style {
|
|
124
|
-
.counter {
|
|
125
|
-
text-align: center
|
|
126
|
-
padding: 20px
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
**Compiler Features:**
|
|
132
|
-
- Import statements for component composition
|
|
133
|
-
- Slots for content projection (`slot`, `slot "name"`)
|
|
134
|
-
- CSS scoping (styles are automatically scoped to component)
|
|
135
|
-
- Native `router {}` and `store {}` blocks
|
|
136
|
-
- Detailed error messages with line/column info
|
|
137
|
-
|
|
138
|
-
### Router & Store DSL (v1.4.0)
|
|
139
|
-
|
|
140
|
-
Define routing and state management directly in your `.pulse` files:
|
|
141
|
-
|
|
142
|
-
```pulse
|
|
143
|
-
@page App
|
|
144
|
-
|
|
145
|
-
router {
|
|
146
|
-
mode: "hash"
|
|
147
|
-
base: "/app"
|
|
148
|
-
routes {
|
|
149
|
-
"/": HomePage
|
|
150
|
-
"/users": UsersPage
|
|
151
|
-
"/users/:id": UserDetailPage
|
|
152
|
-
}
|
|
153
|
-
beforeEach(to, from) {
|
|
154
|
-
if (!store.isAuthenticated) return "/login"
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
store {
|
|
159
|
-
state {
|
|
160
|
-
user: null
|
|
161
|
-
theme: "dark"
|
|
162
|
-
}
|
|
163
|
-
getters {
|
|
164
|
-
isAuthenticated() { return this.user !== null }
|
|
165
|
-
}
|
|
166
|
-
actions {
|
|
167
|
-
login(userData) { this.user = userData }
|
|
168
|
-
logout() { this.user = null }
|
|
169
|
-
toggleTheme() { this.theme = this.theme === "dark" ? "light" : "dark" }
|
|
170
|
-
}
|
|
171
|
-
persist: true
|
|
172
|
-
storageKey: "my-app"
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
view {
|
|
176
|
-
.app {
|
|
177
|
-
nav {
|
|
178
|
-
@link("/") "Home"
|
|
179
|
-
@link("/users") "Users"
|
|
180
|
-
}
|
|
181
|
-
main {
|
|
182
|
-
@outlet
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
**Router DSL Features:**
|
|
189
|
-
- Route definitions with path parameters (`:id`) and wildcards (`*path`)
|
|
190
|
-
- Navigation guards (`beforeEach`, `afterEach`)
|
|
191
|
-
- `@link("/path")` directive for navigation links
|
|
192
|
-
- `@outlet` directive for route content
|
|
193
|
-
- `@navigate`, `@back`, `@forward` directives
|
|
194
|
-
|
|
195
|
-
**Store DSL Features:**
|
|
196
|
-
- Reactive state with automatic signal creation
|
|
197
|
-
- Getters with `this.x` → `store.x.get()` transformation
|
|
198
|
-
- Actions with `this.x = y` → `store.x.set(y)` transformation
|
|
199
|
-
- Built-in persistence via `persist: true`
|
|
200
|
-
- Custom storage key
|
|
201
|
-
|
|
202
|
-
## API Reference
|
|
203
|
-
|
|
204
|
-
### Reactivity
|
|
205
|
-
|
|
206
|
-
- `pulse(value)` - Create a reactive value
|
|
207
|
-
- `pulse.get()` - Read value (tracks dependency)
|
|
208
|
-
- `pulse.set(value)` - Set new value
|
|
209
|
-
- `pulse.update(fn)` - Update with function
|
|
210
|
-
- `pulse.peek()` - Read without tracking
|
|
211
|
-
- `effect(fn)` - Create reactive side effect
|
|
212
|
-
- `computed(fn)` - Create derived value
|
|
213
|
-
- `batch(fn)` - Batch multiple updates
|
|
214
|
-
|
|
215
|
-
### DOM
|
|
216
|
-
|
|
217
|
-
- `el(selector, ...children)` - Create element
|
|
218
|
-
- `mount(target, element)` - Mount to DOM
|
|
219
|
-
- `text(fn)` - Reactive text node
|
|
220
|
-
- `list(items, template)` - Reactive list
|
|
221
|
-
- `when(condition, then, else)` - Conditional render
|
|
222
|
-
|
|
223
|
-
### Router
|
|
224
|
-
|
|
225
|
-
```javascript
|
|
226
|
-
import { createRouter } from 'pulse-js-framework/runtime/router.js';
|
|
227
|
-
|
|
228
|
-
const router = createRouter({
|
|
229
|
-
routes: {
|
|
230
|
-
'/': HomePage,
|
|
231
|
-
'/about': AboutPage,
|
|
232
|
-
'/users/:id': UserPage,
|
|
233
|
-
'/admin': {
|
|
234
|
-
handler: AdminPage,
|
|
235
|
-
meta: { requiresAuth: true },
|
|
236
|
-
beforeEnter: (to, from) => {
|
|
237
|
-
if (!isAuthenticated()) return '/login';
|
|
238
|
-
},
|
|
239
|
-
children: {
|
|
240
|
-
'/users': AdminUsersPage,
|
|
241
|
-
'/settings': AdminSettingsPage
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
},
|
|
245
|
-
scrollBehavior: (to, from, savedPosition) => {
|
|
246
|
-
return savedPosition || { x: 0, y: 0 };
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// Navigation guards
|
|
251
|
-
router.beforeEach((to, from) => {
|
|
252
|
-
if (to.meta.requiresAuth && !isAuthenticated()) {
|
|
253
|
-
return '/login';
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
router.start();
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
**Router Features:**
|
|
261
|
-
- Route params (`:id`) and wildcards (`*path`)
|
|
262
|
-
- Nested routes with `children`
|
|
263
|
-
- Route meta fields
|
|
264
|
-
- Per-route guards (`beforeEnter`)
|
|
265
|
-
- Global guards (`beforeEach`, `beforeResolve`, `afterEach`)
|
|
266
|
-
- Scroll restoration
|
|
267
|
-
- Lazy-loaded routes (async handlers)
|
|
268
|
-
|
|
269
|
-
### Store
|
|
270
|
-
|
|
271
|
-
```javascript
|
|
272
|
-
import { createStore } from 'pulse-js-framework/runtime/store.js';
|
|
273
|
-
|
|
274
|
-
const store = createStore({
|
|
275
|
-
user: null,
|
|
276
|
-
theme: 'light'
|
|
277
|
-
}, { persist: true });
|
|
278
|
-
|
|
279
|
-
store.user.set({ name: 'John' });
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
## CLI Commands
|
|
283
|
-
|
|
284
|
-
```bash
|
|
285
|
-
# Project Commands
|
|
286
|
-
pulse create <name> # Create new project
|
|
287
|
-
pulse dev [port] # Start dev server (default: 3000)
|
|
288
|
-
pulse build # Build for production
|
|
289
|
-
pulse preview [port] # Preview production build (default: 4173)
|
|
290
|
-
pulse compile <file> # Compile .pulse file to JavaScript
|
|
291
|
-
|
|
292
|
-
# Code Quality
|
|
293
|
-
pulse lint [files] # Validate .pulse files for errors and style
|
|
294
|
-
pulse lint --fix # Auto-fix fixable issues
|
|
295
|
-
pulse format [files] # Format .pulse files consistently
|
|
296
|
-
pulse format --check # Check formatting without writing
|
|
297
|
-
pulse analyze # Analyze bundle size and dependencies
|
|
298
|
-
pulse analyze --json # Output analysis as JSON
|
|
299
|
-
|
|
300
|
-
# Mobile
|
|
301
|
-
pulse mobile init # Initialize mobile platforms
|
|
302
|
-
pulse mobile build android|ios # Build native app
|
|
303
|
-
pulse mobile run android|ios # Run on device/emulator
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
### Lint Command
|
|
307
|
-
|
|
308
|
-
Validates `.pulse` files for errors and style issues:
|
|
309
|
-
|
|
310
|
-
```bash
|
|
311
|
-
pulse lint src/ # Lint all files in src/
|
|
312
|
-
pulse lint "**/*.pulse" # Lint all .pulse files
|
|
313
|
-
pulse lint --fix # Auto-fix fixable issues
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
**Checks performed:**
|
|
317
|
-
- Undefined references (state variables, components)
|
|
318
|
-
- Unused imports and state variables
|
|
319
|
-
- Naming conventions (PascalCase for pages, camelCase for state)
|
|
320
|
-
- Empty blocks
|
|
321
|
-
- Import order
|
|
322
|
-
|
|
323
|
-
### Format Command
|
|
324
|
-
|
|
325
|
-
Formats `.pulse` files with consistent style:
|
|
326
|
-
|
|
327
|
-
```bash
|
|
328
|
-
pulse format # Format all .pulse files
|
|
329
|
-
pulse format src/App.pulse # Format specific file
|
|
330
|
-
pulse format --check # Check without writing (CI mode)
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
**Formatting rules:**
|
|
334
|
-
- 2-space indentation
|
|
335
|
-
- Sorted imports (alphabetically)
|
|
336
|
-
- Consistent brace placement
|
|
337
|
-
- Proper spacing around operators
|
|
338
|
-
|
|
339
|
-
### Analyze Command
|
|
340
|
-
|
|
341
|
-
Analyzes your Pulse project for bundle insights:
|
|
342
|
-
|
|
343
|
-
```bash
|
|
344
|
-
pulse analyze # Console report
|
|
345
|
-
pulse analyze --json # JSON output
|
|
346
|
-
pulse analyze --verbose # Detailed metrics
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
**Analysis includes:**
|
|
350
|
-
- File count and total size
|
|
351
|
-
- Component complexity scores
|
|
352
|
-
- Import dependency graph
|
|
353
|
-
- Dead code detection (unreachable files)
|
|
354
|
-
|
|
355
|
-
## Mobile Apps
|
|
356
|
-
|
|
357
|
-
Build native Android and iOS apps from your Pulse project with zero external dependencies:
|
|
358
|
-
|
|
359
|
-
```bash
|
|
360
|
-
# Initialize mobile platforms
|
|
361
|
-
pulse mobile init
|
|
362
|
-
|
|
363
|
-
# Build your web app first
|
|
364
|
-
pulse build
|
|
365
|
-
|
|
366
|
-
# Build for Android (requires Android SDK)
|
|
367
|
-
pulse mobile build android
|
|
368
|
-
|
|
369
|
-
# Build for iOS (requires macOS + Xcode)
|
|
370
|
-
pulse mobile build ios
|
|
371
|
-
|
|
372
|
-
# Run on device/emulator
|
|
373
|
-
pulse mobile run android
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
### Native APIs
|
|
377
|
-
|
|
378
|
-
Access native features in your Pulse app:
|
|
379
|
-
|
|
380
|
-
```javascript
|
|
381
|
-
import { createNativeStorage, NativeUI, onNativeReady } from 'pulse-js-framework/runtime/native';
|
|
382
|
-
|
|
383
|
-
onNativeReady(({ platform }) => {
|
|
384
|
-
console.log(`Running on ${platform}`); // 'android', 'ios', or 'web'
|
|
385
|
-
|
|
386
|
-
// Persistent native storage with Pulse reactivity
|
|
387
|
-
const storage = createNativeStorage();
|
|
388
|
-
const count = storage.get('count', 0);
|
|
389
|
-
count.set(42); // Auto-persisted to native storage
|
|
390
|
-
|
|
391
|
-
// Native toast notification
|
|
392
|
-
NativeUI.toast('Hello from Pulse!');
|
|
393
|
-
|
|
394
|
-
// Haptic feedback
|
|
395
|
-
NativeUI.vibrate(100);
|
|
396
|
-
});
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
**Available APIs:** Storage, Device Info, Network Status, Toast, Vibration, Clipboard, App Lifecycle
|
|
400
|
-
|
|
401
|
-
## Examples
|
|
402
|
-
|
|
403
|
-
- [Blog](examples/blog) - Full blog app with CRUD, categories, search, dark mode
|
|
404
|
-
- [Todo App](examples/todo) - Task management with filters and persistence
|
|
405
|
-
- [Chat App](examples/chat) - Real-time messaging interface
|
|
406
|
-
- [E-commerce](examples/ecommerce) - Shopping cart with product catalog
|
|
407
|
-
- [Weather App](examples/meteo) - Weather dashboard with forecasts
|
|
408
|
-
- [Router Demo](examples/router) - SPA routing with guards
|
|
409
|
-
- [Store Demo](examples/store) - State management with undo/redo
|
|
410
|
-
- [Admin Dashboard](examples/dashboard) - Complete admin UI with all features
|
|
411
|
-
|
|
412
|
-
## License
|
|
413
|
-
|
|
414
|
-
MIT
|
|
1
|
+
# Pulse Framework
|
|
2
|
+
|
|
3
|
+
[](https://github.com/vincenthirtz/pulse-js-framework/actions/workflows/ci.yml)
|
|
4
|
+
[](https://app.netlify.com/projects/pulse-js/deploys)
|
|
5
|
+
|
|
6
|
+
A declarative DOM framework with CSS selector-based structure and reactive pulsations.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **CSS Selector Syntax** - Create DOM elements using familiar CSS selectors
|
|
11
|
+
- **Reactive Pulsations** - Automatic UI updates when state changes
|
|
12
|
+
- **Custom DSL** - Optional `.pulse` file format for cleaner code
|
|
13
|
+
- **No Build Required** - Works directly in the browser
|
|
14
|
+
- **Lightweight** - Minimal footprint, maximum performance
|
|
15
|
+
- **Router & Store** - Built-in SPA routing and state management
|
|
16
|
+
- **Mobile Apps** - Build native Android & iOS apps (zero dependencies)
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install pulse-js-framework
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### Create a new project
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx pulse-js-framework create my-app
|
|
30
|
+
cd my-app
|
|
31
|
+
npm install
|
|
32
|
+
npm run dev
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Or use directly
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
import { pulse, effect, el, mount } from 'pulse-js-framework';
|
|
39
|
+
|
|
40
|
+
// Create reactive state
|
|
41
|
+
const count = pulse(0);
|
|
42
|
+
|
|
43
|
+
// Build UI with CSS selector syntax
|
|
44
|
+
function Counter() {
|
|
45
|
+
const div = el('.counter');
|
|
46
|
+
|
|
47
|
+
const display = el('h1');
|
|
48
|
+
effect(() => {
|
|
49
|
+
display.textContent = `Count: ${count.get()}`;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const increment = el('button.btn', '+');
|
|
53
|
+
increment.onclick = () => count.update(n => n + 1);
|
|
54
|
+
|
|
55
|
+
const decrement = el('button.btn', '-');
|
|
56
|
+
decrement.onclick = () => count.update(n => n - 1);
|
|
57
|
+
|
|
58
|
+
div.append(display, increment, decrement);
|
|
59
|
+
return div;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
mount('#app', Counter());
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## CSS Selector Syntax
|
|
66
|
+
|
|
67
|
+
Create DOM elements using familiar CSS syntax:
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
el('div') // <div></div>
|
|
71
|
+
el('.container') // <div class="container"></div>
|
|
72
|
+
el('#app') // <div id="app"></div>
|
|
73
|
+
el('button.btn.primary') // <button class="btn primary"></button>
|
|
74
|
+
el('input[type=text]') // <input type="text">
|
|
75
|
+
el('h1', 'Hello World') // <h1>Hello World</h1>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Reactivity
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
import { pulse, effect, computed } from 'pulse-js-framework';
|
|
82
|
+
|
|
83
|
+
// Create reactive values
|
|
84
|
+
const firstName = pulse('John');
|
|
85
|
+
const lastName = pulse('Doe');
|
|
86
|
+
|
|
87
|
+
// Computed values
|
|
88
|
+
const fullName = computed(() =>
|
|
89
|
+
`${firstName.get()} ${lastName.get()}`
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Effects auto-run when dependencies change
|
|
93
|
+
effect(() => {
|
|
94
|
+
console.log(`Hello, ${fullName.get()}!`);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
firstName.set('Jane'); // Logs: "Hello, Jane Doe!"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## .pulse File Format
|
|
101
|
+
|
|
102
|
+
```pulse
|
|
103
|
+
@page Counter
|
|
104
|
+
|
|
105
|
+
// Import other components
|
|
106
|
+
import Button from './Button.pulse'
|
|
107
|
+
import { Header, Footer } from './components.pulse'
|
|
108
|
+
|
|
109
|
+
state {
|
|
110
|
+
count: 0
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
view {
|
|
114
|
+
.counter {
|
|
115
|
+
Header
|
|
116
|
+
h1 "Count: {count}"
|
|
117
|
+
Button @click(count++) "+"
|
|
118
|
+
Button @click(count--) "-"
|
|
119
|
+
Footer
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
style {
|
|
124
|
+
.counter {
|
|
125
|
+
text-align: center
|
|
126
|
+
padding: 20px
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Compiler Features:**
|
|
132
|
+
- Import statements for component composition
|
|
133
|
+
- Slots for content projection (`slot`, `slot "name"`)
|
|
134
|
+
- CSS scoping (styles are automatically scoped to component)
|
|
135
|
+
- Native `router {}` and `store {}` blocks
|
|
136
|
+
- Detailed error messages with line/column info
|
|
137
|
+
|
|
138
|
+
### Router & Store DSL (v1.4.0)
|
|
139
|
+
|
|
140
|
+
Define routing and state management directly in your `.pulse` files:
|
|
141
|
+
|
|
142
|
+
```pulse
|
|
143
|
+
@page App
|
|
144
|
+
|
|
145
|
+
router {
|
|
146
|
+
mode: "hash"
|
|
147
|
+
base: "/app"
|
|
148
|
+
routes {
|
|
149
|
+
"/": HomePage
|
|
150
|
+
"/users": UsersPage
|
|
151
|
+
"/users/:id": UserDetailPage
|
|
152
|
+
}
|
|
153
|
+
beforeEach(to, from) {
|
|
154
|
+
if (!store.isAuthenticated) return "/login"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
store {
|
|
159
|
+
state {
|
|
160
|
+
user: null
|
|
161
|
+
theme: "dark"
|
|
162
|
+
}
|
|
163
|
+
getters {
|
|
164
|
+
isAuthenticated() { return this.user !== null }
|
|
165
|
+
}
|
|
166
|
+
actions {
|
|
167
|
+
login(userData) { this.user = userData }
|
|
168
|
+
logout() { this.user = null }
|
|
169
|
+
toggleTheme() { this.theme = this.theme === "dark" ? "light" : "dark" }
|
|
170
|
+
}
|
|
171
|
+
persist: true
|
|
172
|
+
storageKey: "my-app"
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
view {
|
|
176
|
+
.app {
|
|
177
|
+
nav {
|
|
178
|
+
@link("/") "Home"
|
|
179
|
+
@link("/users") "Users"
|
|
180
|
+
}
|
|
181
|
+
main {
|
|
182
|
+
@outlet
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Router DSL Features:**
|
|
189
|
+
- Route definitions with path parameters (`:id`) and wildcards (`*path`)
|
|
190
|
+
- Navigation guards (`beforeEach`, `afterEach`)
|
|
191
|
+
- `@link("/path")` directive for navigation links
|
|
192
|
+
- `@outlet` directive for route content
|
|
193
|
+
- `@navigate`, `@back`, `@forward` directives
|
|
194
|
+
|
|
195
|
+
**Store DSL Features:**
|
|
196
|
+
- Reactive state with automatic signal creation
|
|
197
|
+
- Getters with `this.x` → `store.x.get()` transformation
|
|
198
|
+
- Actions with `this.x = y` → `store.x.set(y)` transformation
|
|
199
|
+
- Built-in persistence via `persist: true`
|
|
200
|
+
- Custom storage key
|
|
201
|
+
|
|
202
|
+
## API Reference
|
|
203
|
+
|
|
204
|
+
### Reactivity
|
|
205
|
+
|
|
206
|
+
- `pulse(value)` - Create a reactive value
|
|
207
|
+
- `pulse.get()` - Read value (tracks dependency)
|
|
208
|
+
- `pulse.set(value)` - Set new value
|
|
209
|
+
- `pulse.update(fn)` - Update with function
|
|
210
|
+
- `pulse.peek()` - Read without tracking
|
|
211
|
+
- `effect(fn)` - Create reactive side effect
|
|
212
|
+
- `computed(fn)` - Create derived value
|
|
213
|
+
- `batch(fn)` - Batch multiple updates
|
|
214
|
+
|
|
215
|
+
### DOM
|
|
216
|
+
|
|
217
|
+
- `el(selector, ...children)` - Create element
|
|
218
|
+
- `mount(target, element)` - Mount to DOM
|
|
219
|
+
- `text(fn)` - Reactive text node
|
|
220
|
+
- `list(items, template)` - Reactive list
|
|
221
|
+
- `when(condition, then, else)` - Conditional render
|
|
222
|
+
|
|
223
|
+
### Router
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
import { createRouter } from 'pulse-js-framework/runtime/router.js';
|
|
227
|
+
|
|
228
|
+
const router = createRouter({
|
|
229
|
+
routes: {
|
|
230
|
+
'/': HomePage,
|
|
231
|
+
'/about': AboutPage,
|
|
232
|
+
'/users/:id': UserPage,
|
|
233
|
+
'/admin': {
|
|
234
|
+
handler: AdminPage,
|
|
235
|
+
meta: { requiresAuth: true },
|
|
236
|
+
beforeEnter: (to, from) => {
|
|
237
|
+
if (!isAuthenticated()) return '/login';
|
|
238
|
+
},
|
|
239
|
+
children: {
|
|
240
|
+
'/users': AdminUsersPage,
|
|
241
|
+
'/settings': AdminSettingsPage
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
scrollBehavior: (to, from, savedPosition) => {
|
|
246
|
+
return savedPosition || { x: 0, y: 0 };
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Navigation guards
|
|
251
|
+
router.beforeEach((to, from) => {
|
|
252
|
+
if (to.meta.requiresAuth && !isAuthenticated()) {
|
|
253
|
+
return '/login';
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
router.start();
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Router Features:**
|
|
261
|
+
- Route params (`:id`) and wildcards (`*path`)
|
|
262
|
+
- Nested routes with `children`
|
|
263
|
+
- Route meta fields
|
|
264
|
+
- Per-route guards (`beforeEnter`)
|
|
265
|
+
- Global guards (`beforeEach`, `beforeResolve`, `afterEach`)
|
|
266
|
+
- Scroll restoration
|
|
267
|
+
- Lazy-loaded routes (async handlers)
|
|
268
|
+
|
|
269
|
+
### Store
|
|
270
|
+
|
|
271
|
+
```javascript
|
|
272
|
+
import { createStore } from 'pulse-js-framework/runtime/store.js';
|
|
273
|
+
|
|
274
|
+
const store = createStore({
|
|
275
|
+
user: null,
|
|
276
|
+
theme: 'light'
|
|
277
|
+
}, { persist: true });
|
|
278
|
+
|
|
279
|
+
store.user.set({ name: 'John' });
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## CLI Commands
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
# Project Commands
|
|
286
|
+
pulse create <name> # Create new project
|
|
287
|
+
pulse dev [port] # Start dev server (default: 3000)
|
|
288
|
+
pulse build # Build for production
|
|
289
|
+
pulse preview [port] # Preview production build (default: 4173)
|
|
290
|
+
pulse compile <file> # Compile .pulse file to JavaScript
|
|
291
|
+
|
|
292
|
+
# Code Quality
|
|
293
|
+
pulse lint [files] # Validate .pulse files for errors and style
|
|
294
|
+
pulse lint --fix # Auto-fix fixable issues
|
|
295
|
+
pulse format [files] # Format .pulse files consistently
|
|
296
|
+
pulse format --check # Check formatting without writing
|
|
297
|
+
pulse analyze # Analyze bundle size and dependencies
|
|
298
|
+
pulse analyze --json # Output analysis as JSON
|
|
299
|
+
|
|
300
|
+
# Mobile
|
|
301
|
+
pulse mobile init # Initialize mobile platforms
|
|
302
|
+
pulse mobile build android|ios # Build native app
|
|
303
|
+
pulse mobile run android|ios # Run on device/emulator
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Lint Command
|
|
307
|
+
|
|
308
|
+
Validates `.pulse` files for errors and style issues:
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
pulse lint src/ # Lint all files in src/
|
|
312
|
+
pulse lint "**/*.pulse" # Lint all .pulse files
|
|
313
|
+
pulse lint --fix # Auto-fix fixable issues
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Checks performed:**
|
|
317
|
+
- Undefined references (state variables, components)
|
|
318
|
+
- Unused imports and state variables
|
|
319
|
+
- Naming conventions (PascalCase for pages, camelCase for state)
|
|
320
|
+
- Empty blocks
|
|
321
|
+
- Import order
|
|
322
|
+
|
|
323
|
+
### Format Command
|
|
324
|
+
|
|
325
|
+
Formats `.pulse` files with consistent style:
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
pulse format # Format all .pulse files
|
|
329
|
+
pulse format src/App.pulse # Format specific file
|
|
330
|
+
pulse format --check # Check without writing (CI mode)
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Formatting rules:**
|
|
334
|
+
- 2-space indentation
|
|
335
|
+
- Sorted imports (alphabetically)
|
|
336
|
+
- Consistent brace placement
|
|
337
|
+
- Proper spacing around operators
|
|
338
|
+
|
|
339
|
+
### Analyze Command
|
|
340
|
+
|
|
341
|
+
Analyzes your Pulse project for bundle insights:
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
pulse analyze # Console report
|
|
345
|
+
pulse analyze --json # JSON output
|
|
346
|
+
pulse analyze --verbose # Detailed metrics
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Analysis includes:**
|
|
350
|
+
- File count and total size
|
|
351
|
+
- Component complexity scores
|
|
352
|
+
- Import dependency graph
|
|
353
|
+
- Dead code detection (unreachable files)
|
|
354
|
+
|
|
355
|
+
## Mobile Apps
|
|
356
|
+
|
|
357
|
+
Build native Android and iOS apps from your Pulse project with zero external dependencies:
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
# Initialize mobile platforms
|
|
361
|
+
pulse mobile init
|
|
362
|
+
|
|
363
|
+
# Build your web app first
|
|
364
|
+
pulse build
|
|
365
|
+
|
|
366
|
+
# Build for Android (requires Android SDK)
|
|
367
|
+
pulse mobile build android
|
|
368
|
+
|
|
369
|
+
# Build for iOS (requires macOS + Xcode)
|
|
370
|
+
pulse mobile build ios
|
|
371
|
+
|
|
372
|
+
# Run on device/emulator
|
|
373
|
+
pulse mobile run android
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Native APIs
|
|
377
|
+
|
|
378
|
+
Access native features in your Pulse app:
|
|
379
|
+
|
|
380
|
+
```javascript
|
|
381
|
+
import { createNativeStorage, NativeUI, onNativeReady } from 'pulse-js-framework/runtime/native';
|
|
382
|
+
|
|
383
|
+
onNativeReady(({ platform }) => {
|
|
384
|
+
console.log(`Running on ${platform}`); // 'android', 'ios', or 'web'
|
|
385
|
+
|
|
386
|
+
// Persistent native storage with Pulse reactivity
|
|
387
|
+
const storage = createNativeStorage();
|
|
388
|
+
const count = storage.get('count', 0);
|
|
389
|
+
count.set(42); // Auto-persisted to native storage
|
|
390
|
+
|
|
391
|
+
// Native toast notification
|
|
392
|
+
NativeUI.toast('Hello from Pulse!');
|
|
393
|
+
|
|
394
|
+
// Haptic feedback
|
|
395
|
+
NativeUI.vibrate(100);
|
|
396
|
+
});
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**Available APIs:** Storage, Device Info, Network Status, Toast, Vibration, Clipboard, App Lifecycle
|
|
400
|
+
|
|
401
|
+
## Examples
|
|
402
|
+
|
|
403
|
+
- [Blog](examples/blog) - Full blog app with CRUD, categories, search, dark mode
|
|
404
|
+
- [Todo App](examples/todo) - Task management with filters and persistence
|
|
405
|
+
- [Chat App](examples/chat) - Real-time messaging interface
|
|
406
|
+
- [E-commerce](examples/ecommerce) - Shopping cart with product catalog
|
|
407
|
+
- [Weather App](examples/meteo) - Weather dashboard with forecasts
|
|
408
|
+
- [Router Demo](examples/router) - SPA routing with guards
|
|
409
|
+
- [Store Demo](examples/store) - State management with undo/redo
|
|
410
|
+
- [Admin Dashboard](examples/dashboard) - Complete admin UI with all features
|
|
411
|
+
|
|
412
|
+
## License
|
|
413
|
+
|
|
414
|
+
MIT
|