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 CHANGED
@@ -1,5 +1,8 @@
1
1
  # Pulse Framework
2
2
 
3
+ [![CI](https://github.com/vincenthirtz/pulse-js-framework/actions/workflows/ci.yml/badge.svg)](https://github.com/vincenthirtz/pulse-js-framework/actions/workflows/ci.yml)
4
+ [![Netlify Status](https://api.netlify.com/api/v1/badges/2597dac2-228a-4d3e-bea8-4e7ef8ac5c53/deploy-status)](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
- const runtimeCode = `
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
- export default { buildProject };
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.0.0';
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
- compile: compileFile
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 Start development server
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/pulse
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
  */