pulse-js-framework 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -0
- package/cli/build.js +124 -5
- package/cli/index.js +32 -5
- package/cli/mobile.js +1473 -0
- package/compiler/lexer.js +19 -2
- package/mobile/bridge/pulse-native.js +420 -0
- package/package.json +13 -6
- package/runtime/dom.js +363 -33
- package/runtime/index.js +2 -0
- package/runtime/native.js +368 -0
- package/runtime/pulse.js +247 -13
- package/runtime/router.js +10 -1
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Pulse Framework
|
|
2
2
|
|
|
3
|
+
[](https://github.com/vincenthirtz/pulse-js-framework/actions/workflows/ci.yml)
|
|
4
|
+
[](https://app.netlify.com/projects/pulse-js/deploys)
|
|
5
|
+
|
|
3
6
|
A declarative DOM framework with CSS selector-based structure and reactive pulsations.
|
|
4
7
|
|
|
5
8
|
## Features
|
|
@@ -10,6 +13,7 @@ A declarative DOM framework with CSS selector-based structure and reactive pulsa
|
|
|
10
13
|
- **No Build Required** - Works directly in the browser
|
|
11
14
|
- **Lightweight** - Minimal footprint, maximum performance
|
|
12
15
|
- **Router & Store** - Built-in SPA routing and state management
|
|
16
|
+
- **Mobile Apps** - Build native Android & iOS apps (zero dependencies)
|
|
13
17
|
|
|
14
18
|
## Installation
|
|
15
19
|
|
|
@@ -175,8 +179,67 @@ pulse create <name> # Create new project
|
|
|
175
179
|
pulse dev [port] # Start dev server
|
|
176
180
|
pulse build # Build for production
|
|
177
181
|
pulse compile <file> # Compile .pulse file
|
|
182
|
+
pulse mobile init # Initialize mobile platforms
|
|
183
|
+
pulse mobile build android|ios # Build native app
|
|
184
|
+
pulse mobile run android|ios # Run on device/emulator
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Mobile Apps
|
|
188
|
+
|
|
189
|
+
Build native Android and iOS apps from your Pulse project with zero external dependencies:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Initialize mobile platforms
|
|
193
|
+
pulse mobile init
|
|
194
|
+
|
|
195
|
+
# Build your web app first
|
|
196
|
+
pulse build
|
|
197
|
+
|
|
198
|
+
# Build for Android (requires Android SDK)
|
|
199
|
+
pulse mobile build android
|
|
200
|
+
|
|
201
|
+
# Build for iOS (requires macOS + Xcode)
|
|
202
|
+
pulse mobile build ios
|
|
203
|
+
|
|
204
|
+
# Run on device/emulator
|
|
205
|
+
pulse mobile run android
|
|
178
206
|
```
|
|
179
207
|
|
|
208
|
+
### Native APIs
|
|
209
|
+
|
|
210
|
+
Access native features in your Pulse app:
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
import { createNativeStorage, NativeUI, onNativeReady } from 'pulse-js-framework/runtime/native';
|
|
214
|
+
|
|
215
|
+
onNativeReady(({ platform }) => {
|
|
216
|
+
console.log(`Running on ${platform}`); // 'android', 'ios', or 'web'
|
|
217
|
+
|
|
218
|
+
// Persistent native storage with Pulse reactivity
|
|
219
|
+
const storage = createNativeStorage();
|
|
220
|
+
const count = storage.get('count', 0);
|
|
221
|
+
count.set(42); // Auto-persisted to native storage
|
|
222
|
+
|
|
223
|
+
// Native toast notification
|
|
224
|
+
NativeUI.toast('Hello from Pulse!');
|
|
225
|
+
|
|
226
|
+
// Haptic feedback
|
|
227
|
+
NativeUI.vibrate(100);
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Available APIs:** Storage, Device Info, Network Status, Toast, Vibration, Clipboard, App Lifecycle
|
|
232
|
+
|
|
233
|
+
## Examples
|
|
234
|
+
|
|
235
|
+
- [Todo App](examples/todo) - Task management with filters and persistence
|
|
236
|
+
- [Chat App](examples/chat) - Real-time messaging interface
|
|
237
|
+
- [E-commerce](examples/ecommerce) - Shopping cart with product catalog
|
|
238
|
+
- [Weather App](examples/meteo) - Weather dashboard with forecasts
|
|
239
|
+
- [Router Demo](examples/router) - SPA routing with guards
|
|
240
|
+
- [Store Demo](examples/store) - State management with undo/redo
|
|
241
|
+
- [Admin Dashboard](examples/dashboard) - Complete admin UI with all features
|
|
242
|
+
|
|
180
243
|
## License
|
|
181
244
|
|
|
182
245
|
MIT
|
package/cli/build.js
CHANGED
|
@@ -124,9 +124,12 @@ function processDirectory(srcDir, outDir) {
|
|
|
124
124
|
"from './runtime.js'"
|
|
125
125
|
);
|
|
126
126
|
|
|
127
|
+
// Minify
|
|
128
|
+
content = minifyJS(content);
|
|
129
|
+
|
|
127
130
|
const outPath = join(outDir, file);
|
|
128
131
|
writeFileSync(outPath, content);
|
|
129
|
-
console.log(` Processed: ${file}`);
|
|
132
|
+
console.log(` Processed & minified: ${file}`);
|
|
130
133
|
} else {
|
|
131
134
|
// Copy other files
|
|
132
135
|
const outPath = join(outDir, file);
|
|
@@ -138,11 +141,11 @@ function processDirectory(srcDir, outDir) {
|
|
|
138
141
|
/**
|
|
139
142
|
* Bundle the runtime into a single file
|
|
140
143
|
*/
|
|
141
|
-
function bundleRuntime(outDir) {
|
|
144
|
+
function bundleRuntime(outDir, shouldMinify = true) {
|
|
142
145
|
// For simplicity, we'll create a minimal runtime bundle
|
|
143
146
|
// In production, you'd want to use a proper bundler
|
|
144
147
|
|
|
145
|
-
|
|
148
|
+
let runtimeCode = `
|
|
146
149
|
// Pulse Runtime (bundled)
|
|
147
150
|
${readRuntimeFile('pulse.js')}
|
|
148
151
|
${readRuntimeFile('dom.js')}
|
|
@@ -150,8 +153,14 @@ ${readRuntimeFile('router.js')}
|
|
|
150
153
|
${readRuntimeFile('store.js')}
|
|
151
154
|
`;
|
|
152
155
|
|
|
156
|
+
if (shouldMinify) {
|
|
157
|
+
runtimeCode = minifyJS(runtimeCode);
|
|
158
|
+
console.log(' Bundled & minified: runtime.js');
|
|
159
|
+
} else {
|
|
160
|
+
console.log(' Bundled: runtime.js');
|
|
161
|
+
}
|
|
162
|
+
|
|
153
163
|
writeFileSync(join(outDir, 'assets', 'runtime.js'), runtimeCode);
|
|
154
|
-
console.log(' Bundled: runtime.js');
|
|
155
164
|
}
|
|
156
165
|
|
|
157
166
|
/**
|
|
@@ -173,6 +182,32 @@ function readRuntimeFile(filename) {
|
|
|
173
182
|
return '';
|
|
174
183
|
}
|
|
175
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Minify JavaScript code (simple minification)
|
|
187
|
+
*/
|
|
188
|
+
export function minifyJS(code) {
|
|
189
|
+
return code
|
|
190
|
+
// Remove single-line comments (but not URLs with //)
|
|
191
|
+
.replace(/(?<!:)\/\/.*$/gm, '')
|
|
192
|
+
// Remove multi-line comments
|
|
193
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
194
|
+
// Remove leading/trailing whitespace per line
|
|
195
|
+
.split('\n')
|
|
196
|
+
.map(line => line.trim())
|
|
197
|
+
.filter(line => line.length > 0)
|
|
198
|
+
.join('\n')
|
|
199
|
+
// Collapse multiple newlines
|
|
200
|
+
.replace(/\n{2,}/g, '\n')
|
|
201
|
+
// Remove spaces around operators (simple)
|
|
202
|
+
.replace(/\s*([{};,:])\s*/g, '$1')
|
|
203
|
+
.replace(/\s*=\s*/g, '=')
|
|
204
|
+
.replace(/\s*\(\s*/g, '(')
|
|
205
|
+
.replace(/\s*\)\s*/g, ')')
|
|
206
|
+
// Collapse remaining whitespace
|
|
207
|
+
.replace(/\s+/g, ' ')
|
|
208
|
+
.trim();
|
|
209
|
+
}
|
|
210
|
+
|
|
176
211
|
/**
|
|
177
212
|
* Copy a directory recursively
|
|
178
213
|
*/
|
|
@@ -196,4 +231,88 @@ function copyDir(src, dest) {
|
|
|
196
231
|
}
|
|
197
232
|
}
|
|
198
233
|
|
|
199
|
-
|
|
234
|
+
/**
|
|
235
|
+
* Preview production build
|
|
236
|
+
*/
|
|
237
|
+
export async function previewBuild(args) {
|
|
238
|
+
const port = parseInt(args[0]) || 4173;
|
|
239
|
+
const root = process.cwd();
|
|
240
|
+
const distDir = join(root, 'dist');
|
|
241
|
+
|
|
242
|
+
if (!existsSync(distDir)) {
|
|
243
|
+
console.error('No dist folder found. Run "pulse build" first.');
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Check if vite is available for preview
|
|
248
|
+
try {
|
|
249
|
+
const viteConfig = join(root, 'vite.config.js');
|
|
250
|
+
if (existsSync(viteConfig)) {
|
|
251
|
+
console.log('Using Vite preview...');
|
|
252
|
+
const { preview } = await import('vite');
|
|
253
|
+
const server = await preview({
|
|
254
|
+
root,
|
|
255
|
+
preview: { port }
|
|
256
|
+
});
|
|
257
|
+
server.printUrls();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
} catch (e) {
|
|
261
|
+
// Vite not available, use built-in server
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Built-in static server for dist
|
|
265
|
+
const { createServer } = await import('http');
|
|
266
|
+
|
|
267
|
+
const MIME_TYPES = {
|
|
268
|
+
'.html': 'text/html',
|
|
269
|
+
'.js': 'application/javascript',
|
|
270
|
+
'.css': 'text/css',
|
|
271
|
+
'.json': 'application/json',
|
|
272
|
+
'.png': 'image/png',
|
|
273
|
+
'.jpg': 'image/jpeg',
|
|
274
|
+
'.gif': 'image/gif',
|
|
275
|
+
'.svg': 'image/svg+xml',
|
|
276
|
+
'.ico': 'image/x-icon',
|
|
277
|
+
'.woff': 'font/woff',
|
|
278
|
+
'.woff2': 'font/woff2'
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const server = createServer((req, res) => {
|
|
282
|
+
let pathname = new URL(req.url, `http://localhost:${port}`).pathname;
|
|
283
|
+
|
|
284
|
+
// SPA fallback - serve index.html for routes
|
|
285
|
+
if (pathname === '/' || !pathname.includes('.')) {
|
|
286
|
+
pathname = '/index.html';
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const filePath = join(distDir, pathname);
|
|
290
|
+
|
|
291
|
+
if (existsSync(filePath) && statSync(filePath).isFile()) {
|
|
292
|
+
const ext = filePath.substring(filePath.lastIndexOf('.'));
|
|
293
|
+
const mimeType = MIME_TYPES[ext] || 'application/octet-stream';
|
|
294
|
+
|
|
295
|
+
res.writeHead(200, {
|
|
296
|
+
'Content-Type': mimeType,
|
|
297
|
+
'Cache-Control': 'public, max-age=31536000'
|
|
298
|
+
});
|
|
299
|
+
res.end(readFileSync(filePath));
|
|
300
|
+
} else {
|
|
301
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
302
|
+
res.end('Not Found');
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
server.listen(port, () => {
|
|
307
|
+
console.log(`
|
|
308
|
+
Pulse Preview Server running at:
|
|
309
|
+
|
|
310
|
+
Local: http://localhost:${port}/
|
|
311
|
+
|
|
312
|
+
Serving production build from: dist/
|
|
313
|
+
Press Ctrl+C to stop.
|
|
314
|
+
`);
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export default { buildProject, previewBuild, minifyJS };
|
package/cli/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { existsSync, mkdirSync, writeFileSync, readFileSync, cpSync } from 'fs';
|
|
|
11
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
12
|
const __dirname = dirname(__filename);
|
|
13
13
|
|
|
14
|
-
const VERSION = '1.
|
|
14
|
+
const VERSION = '1.2.0';
|
|
15
15
|
|
|
16
16
|
// Command handlers
|
|
17
17
|
const commands = {
|
|
@@ -20,7 +20,9 @@ const commands = {
|
|
|
20
20
|
create: createProject,
|
|
21
21
|
dev: runDev,
|
|
22
22
|
build: runBuild,
|
|
23
|
-
|
|
23
|
+
preview: runPreview,
|
|
24
|
+
compile: compileFile,
|
|
25
|
+
mobile: runMobile
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
/**
|
|
@@ -50,19 +52,26 @@ Usage: pulse <command> [options]
|
|
|
50
52
|
|
|
51
53
|
Commands:
|
|
52
54
|
create <name> Create a new Pulse project
|
|
53
|
-
dev
|
|
54
|
-
build Build for production
|
|
55
|
+
dev [port] Start development server (default: 3000)
|
|
56
|
+
build Build for production (minified)
|
|
57
|
+
preview [port] Preview production build (default: 4173)
|
|
55
58
|
compile <file> Compile a .pulse file to JavaScript
|
|
59
|
+
mobile <cmd> Mobile app commands (init, build, run)
|
|
56
60
|
version Show version number
|
|
57
61
|
help Show this help message
|
|
58
62
|
|
|
59
63
|
Examples:
|
|
60
64
|
pulse create my-app
|
|
61
65
|
pulse dev
|
|
66
|
+
pulse dev 8080
|
|
62
67
|
pulse build
|
|
68
|
+
pulse preview
|
|
69
|
+
pulse mobile init
|
|
70
|
+
pulse mobile build android
|
|
71
|
+
pulse mobile run ios
|
|
63
72
|
pulse compile src/App.pulse
|
|
64
73
|
|
|
65
|
-
Documentation: https://github.com/pulse-framework
|
|
74
|
+
Documentation: https://github.com/vincenthirtz/pulse-js-framework
|
|
66
75
|
`);
|
|
67
76
|
}
|
|
68
77
|
|
|
@@ -282,6 +291,24 @@ async function runBuild(args) {
|
|
|
282
291
|
await buildProject(args);
|
|
283
292
|
}
|
|
284
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Preview production build
|
|
296
|
+
*/
|
|
297
|
+
async function runPreview(args) {
|
|
298
|
+
console.log('Starting Pulse preview server...');
|
|
299
|
+
|
|
300
|
+
const { previewBuild } = await import('./build.js');
|
|
301
|
+
await previewBuild(args);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Run mobile commands
|
|
306
|
+
*/
|
|
307
|
+
async function runMobile(args) {
|
|
308
|
+
const { handleMobileCommand } = await import('./mobile.js');
|
|
309
|
+
await handleMobileCommand(args);
|
|
310
|
+
}
|
|
311
|
+
|
|
285
312
|
/**
|
|
286
313
|
* Compile a single .pulse file
|
|
287
314
|
*/
|