pulse-js-framework 1.7.15 → 1.7.16
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 +37 -0
- package/cli/help.js +583 -0
- package/cli/index.js +24 -105
- package/package.json +3 -2
- package/runtime/async.js +39 -0
- package/runtime/dom-element.js +107 -0
- package/runtime/index.js +2 -0
- package/runtime/pulse.js +40 -0
- package/runtime/ssr-async.js +229 -0
- package/runtime/ssr-hydrator.js +310 -0
- package/runtime/ssr-serializer.js +266 -0
- package/runtime/ssr.js +463 -0
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ No build. No dependencies. Just JavaScript.
|
|
|
16
16
|
- **Router & Store** - Built-in SPA routing and state management
|
|
17
17
|
- **Form Handling** - Validation, async validators, field arrays
|
|
18
18
|
- **Async Primitives** - useAsync, useResource, usePolling with SWR caching
|
|
19
|
+
- **Server-Side Rendering** - Full SSR with hydration and async data fetching
|
|
19
20
|
- **Hot Module Replacement** - Full HMR with state preservation
|
|
20
21
|
- **Mobile Apps** - Build native Android & iOS apps (zero dependencies)
|
|
21
22
|
- **TypeScript Support** - Full type definitions for IDE autocomplete
|
|
@@ -326,6 +327,41 @@ const count: Pulse<number> = pulse(0);
|
|
|
326
327
|
| [Store Demo](examples/store) | State with undo/redo |
|
|
327
328
|
| [Electron App](examples/electron) | Desktop notes app |
|
|
328
329
|
|
|
330
|
+
## Server-Side Rendering
|
|
331
|
+
|
|
332
|
+
Pulse supports full SSR with hydration and async data fetching:
|
|
333
|
+
|
|
334
|
+
```javascript
|
|
335
|
+
// server.js
|
|
336
|
+
import { renderToString, serializeState } from 'pulse-js-framework/runtime/ssr';
|
|
337
|
+
import App from './App.js';
|
|
338
|
+
|
|
339
|
+
app.get('*', async (req, res) => {
|
|
340
|
+
const { html, state } = await renderToString(() => App(), {
|
|
341
|
+
waitForAsync: true // Wait for useAsync to resolve
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
res.send(`
|
|
345
|
+
<!DOCTYPE html>
|
|
346
|
+
<html>
|
|
347
|
+
<body>
|
|
348
|
+
<div id="app">${html}</div>
|
|
349
|
+
<script>window.__PULSE_STATE__ = ${serializeState(state)};</script>
|
|
350
|
+
<script type="module" src="/client.js"></script>
|
|
351
|
+
</body>
|
|
352
|
+
</html>
|
|
353
|
+
`);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// client.js
|
|
357
|
+
import { hydrate } from 'pulse-js-framework/runtime/ssr';
|
|
358
|
+
import App from './App.js';
|
|
359
|
+
|
|
360
|
+
hydrate('#app', () => App(), {
|
|
361
|
+
state: window.__PULSE_STATE__
|
|
362
|
+
});
|
|
363
|
+
```
|
|
364
|
+
|
|
329
365
|
## Documentation
|
|
330
366
|
|
|
331
367
|
- [API Reference](docs/api.md) - Complete API documentation
|
|
@@ -338,6 +374,7 @@ const count: Pulse<number> = pulse(0);
|
|
|
338
374
|
- [Context API](docs/context.md) - Dependency injection
|
|
339
375
|
- [DevTools](docs/devtools.md) - Debugging and profiling
|
|
340
376
|
- [Mobile Apps](docs/mobile.md) - Native Android & iOS
|
|
377
|
+
- [SSR](docs/ssr.md) - Server-side rendering and hydration
|
|
341
378
|
|
|
342
379
|
## License
|
|
343
380
|
|
package/cli/help.js
ADDED
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse CLI Help System
|
|
3
|
+
* Provides detailed help for all CLI commands
|
|
4
|
+
* @module pulse-cli/help
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFileSync } from 'fs';
|
|
8
|
+
import { join, dirname } from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { log } from './logger.js';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
|
|
15
|
+
// Read version from package.json
|
|
16
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
17
|
+
const VERSION = pkg.version;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Command definitions with detailed help information
|
|
21
|
+
*/
|
|
22
|
+
const commandDefinitions = {
|
|
23
|
+
create: {
|
|
24
|
+
name: 'create',
|
|
25
|
+
summary: 'Create a new Pulse project',
|
|
26
|
+
usage: 'pulse create <name> [options]',
|
|
27
|
+
description: `
|
|
28
|
+
Creates a new Pulse project with a complete starter template including:
|
|
29
|
+
- Project structure (src/, public/)
|
|
30
|
+
- Vite configuration for development and building
|
|
31
|
+
- Sample App.pulse component with counter example
|
|
32
|
+
- Package.json with all necessary scripts`,
|
|
33
|
+
arguments: [
|
|
34
|
+
{ name: '<name>', description: 'Name of the project directory to create' }
|
|
35
|
+
],
|
|
36
|
+
options: [
|
|
37
|
+
{ flag: '--typescript, --ts', description: 'Create a TypeScript project with tsconfig.json' },
|
|
38
|
+
{ flag: '--minimal', description: 'Create minimal project structure without extras' }
|
|
39
|
+
],
|
|
40
|
+
examples: [
|
|
41
|
+
{ cmd: 'pulse create my-app', desc: 'Create a new JavaScript project' },
|
|
42
|
+
{ cmd: 'pulse create my-app --typescript', desc: 'Create a new TypeScript project' },
|
|
43
|
+
{ cmd: 'pulse create my-app --minimal', desc: 'Create a minimal project' }
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
init: {
|
|
48
|
+
name: 'init',
|
|
49
|
+
summary: 'Initialize Pulse in current directory',
|
|
50
|
+
usage: 'pulse init [options]',
|
|
51
|
+
description: `
|
|
52
|
+
Initializes a Pulse project in the current directory. This is useful when:
|
|
53
|
+
- Converting an existing project to use Pulse
|
|
54
|
+
- Setting up Pulse in a pre-existing directory
|
|
55
|
+
- Adding Pulse to a monorepo
|
|
56
|
+
|
|
57
|
+
Merges with existing package.json if present.`,
|
|
58
|
+
options: [
|
|
59
|
+
{ flag: '--typescript, --ts', description: 'Initialize as TypeScript project' },
|
|
60
|
+
{ flag: '--force', description: 'Initialize even if directory is not empty' }
|
|
61
|
+
],
|
|
62
|
+
examples: [
|
|
63
|
+
{ cmd: 'pulse init', desc: 'Initialize in current directory' },
|
|
64
|
+
{ cmd: 'pulse init --typescript', desc: 'Initialize with TypeScript support' },
|
|
65
|
+
{ cmd: 'pulse init --force', desc: 'Force init in non-empty directory' }
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
dev: {
|
|
70
|
+
name: 'dev',
|
|
71
|
+
summary: 'Start development server',
|
|
72
|
+
usage: 'pulse dev [port]',
|
|
73
|
+
description: `
|
|
74
|
+
Starts the Vite development server with:
|
|
75
|
+
- Hot Module Replacement (HMR) for instant updates
|
|
76
|
+
- .pulse file compilation on-the-fly
|
|
77
|
+
- Source maps for debugging
|
|
78
|
+
- Fast refresh without losing state`,
|
|
79
|
+
arguments: [
|
|
80
|
+
{ name: '[port]', description: 'Port number (default: 3000)' }
|
|
81
|
+
],
|
|
82
|
+
options: [
|
|
83
|
+
{ flag: '--host', description: 'Expose server to network' },
|
|
84
|
+
{ flag: '--open', description: 'Open browser automatically' }
|
|
85
|
+
],
|
|
86
|
+
examples: [
|
|
87
|
+
{ cmd: 'pulse dev', desc: 'Start on default port 3000' },
|
|
88
|
+
{ cmd: 'pulse dev 8080', desc: 'Start on port 8080' },
|
|
89
|
+
{ cmd: 'pulse dev --host', desc: 'Expose to network (for mobile testing)' }
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
build: {
|
|
94
|
+
name: 'build',
|
|
95
|
+
summary: 'Build for production',
|
|
96
|
+
usage: 'pulse build [options]',
|
|
97
|
+
description: `
|
|
98
|
+
Creates an optimized production build in the dist/ directory:
|
|
99
|
+
- Minified JavaScript and CSS
|
|
100
|
+
- Tree-shaking to remove unused code
|
|
101
|
+
- Asset optimization and hashing
|
|
102
|
+
- Source maps (optional)`,
|
|
103
|
+
options: [
|
|
104
|
+
{ flag: '--sourcemap', description: 'Generate source maps' },
|
|
105
|
+
{ flag: '--minify', description: 'Minify output (default: true)' }
|
|
106
|
+
],
|
|
107
|
+
examples: [
|
|
108
|
+
{ cmd: 'pulse build', desc: 'Create production build' },
|
|
109
|
+
{ cmd: 'pulse build --sourcemap', desc: 'Build with source maps' }
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
preview: {
|
|
114
|
+
name: 'preview',
|
|
115
|
+
summary: 'Preview production build',
|
|
116
|
+
usage: 'pulse preview [port]',
|
|
117
|
+
description: `
|
|
118
|
+
Serves the production build locally for testing before deployment.
|
|
119
|
+
This simulates a production environment to verify the build works correctly.`,
|
|
120
|
+
arguments: [
|
|
121
|
+
{ name: '[port]', description: 'Port number (default: 4173)' }
|
|
122
|
+
],
|
|
123
|
+
examples: [
|
|
124
|
+
{ cmd: 'pulse preview', desc: 'Preview on default port' },
|
|
125
|
+
{ cmd: 'pulse preview 5000', desc: 'Preview on port 5000' }
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
compile: {
|
|
130
|
+
name: 'compile',
|
|
131
|
+
summary: 'Compile .pulse files to JavaScript',
|
|
132
|
+
usage: 'pulse compile <file|dir> [options]',
|
|
133
|
+
description: `
|
|
134
|
+
Compiles .pulse files to JavaScript. Useful for:
|
|
135
|
+
- Pre-compiling components for distribution
|
|
136
|
+
- Debugging compiled output
|
|
137
|
+
- CI/CD pipelines without Vite`,
|
|
138
|
+
arguments: [
|
|
139
|
+
{ name: '<file|dir>', description: 'File or directory to compile' }
|
|
140
|
+
],
|
|
141
|
+
options: [
|
|
142
|
+
{ flag: '--watch, -w', description: 'Watch files and recompile on changes' },
|
|
143
|
+
{ flag: '--dry-run', description: 'Show what would be compiled without writing' },
|
|
144
|
+
{ flag: '--output, -o <dir>', description: 'Output directory (default: same as input)' }
|
|
145
|
+
],
|
|
146
|
+
examples: [
|
|
147
|
+
{ cmd: 'pulse compile src/App.pulse', desc: 'Compile a single file' },
|
|
148
|
+
{ cmd: 'pulse compile src/', desc: 'Compile all .pulse files in directory' },
|
|
149
|
+
{ cmd: 'pulse compile src/ --watch', desc: 'Watch and recompile on changes' },
|
|
150
|
+
{ cmd: 'pulse compile src/ -o dist/', desc: 'Output to dist directory' }
|
|
151
|
+
]
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
lint: {
|
|
155
|
+
name: 'lint',
|
|
156
|
+
summary: 'Validate .pulse files for errors and style',
|
|
157
|
+
usage: 'pulse lint [files] [options]',
|
|
158
|
+
description: `
|
|
159
|
+
Analyzes .pulse files for:
|
|
160
|
+
- Syntax errors and typos
|
|
161
|
+
- Unused state variables
|
|
162
|
+
- Missing key functions in lists
|
|
163
|
+
- Accessibility issues (10 a11y rules)
|
|
164
|
+
- Code style violations`,
|
|
165
|
+
arguments: [
|
|
166
|
+
{ name: '[files]', description: 'Files or directories to lint (default: src/)' }
|
|
167
|
+
],
|
|
168
|
+
options: [
|
|
169
|
+
{ flag: '--fix', description: 'Auto-fix fixable issues' },
|
|
170
|
+
{ flag: '--watch, -w', description: 'Watch files and re-lint on changes' },
|
|
171
|
+
{ flag: '--dry-run', description: 'Show fixes without applying (use with --fix)' }
|
|
172
|
+
],
|
|
173
|
+
examples: [
|
|
174
|
+
{ cmd: 'pulse lint', desc: 'Lint all files in src/' },
|
|
175
|
+
{ cmd: 'pulse lint src/components/', desc: 'Lint specific directory' },
|
|
176
|
+
{ cmd: 'pulse lint --fix', desc: 'Auto-fix fixable issues' },
|
|
177
|
+
{ cmd: 'pulse lint --fix --dry-run', desc: 'Preview fixes without applying' }
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
format: {
|
|
182
|
+
name: 'format',
|
|
183
|
+
summary: 'Format .pulse files consistently',
|
|
184
|
+
usage: 'pulse format [files] [options]',
|
|
185
|
+
description: `
|
|
186
|
+
Formats .pulse files with consistent style:
|
|
187
|
+
- Indentation (2 spaces)
|
|
188
|
+
- Brace placement
|
|
189
|
+
- Attribute ordering
|
|
190
|
+
- Whitespace normalization`,
|
|
191
|
+
arguments: [
|
|
192
|
+
{ name: '[files]', description: 'Files or directories to format (default: src/)' }
|
|
193
|
+
],
|
|
194
|
+
options: [
|
|
195
|
+
{ flag: '--check', description: 'Check formatting without writing (CI mode)' },
|
|
196
|
+
{ flag: '--watch, -w', description: 'Watch files and re-format on changes' },
|
|
197
|
+
{ flag: '--write', description: 'Write formatted output (default)' }
|
|
198
|
+
],
|
|
199
|
+
examples: [
|
|
200
|
+
{ cmd: 'pulse format', desc: 'Format all files in src/' },
|
|
201
|
+
{ cmd: 'pulse format --check', desc: 'Check formatting (for CI)' },
|
|
202
|
+
{ cmd: 'pulse format src/App.pulse', desc: 'Format specific file' }
|
|
203
|
+
]
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
analyze: {
|
|
207
|
+
name: 'analyze',
|
|
208
|
+
summary: 'Analyze bundle size and dependencies',
|
|
209
|
+
usage: 'pulse analyze [options]',
|
|
210
|
+
description: `
|
|
211
|
+
Analyzes your project to provide insights on:
|
|
212
|
+
- Bundle size breakdown
|
|
213
|
+
- Import graph and dependencies
|
|
214
|
+
- Code complexity metrics
|
|
215
|
+
- Potential optimization opportunities`,
|
|
216
|
+
options: [
|
|
217
|
+
{ flag: '--json', description: 'Output analysis as JSON' },
|
|
218
|
+
{ flag: '--verbose', description: 'Show detailed metrics' }
|
|
219
|
+
],
|
|
220
|
+
examples: [
|
|
221
|
+
{ cmd: 'pulse analyze', desc: 'Show analysis summary' },
|
|
222
|
+
{ cmd: 'pulse analyze --json', desc: 'Output as JSON for tooling' },
|
|
223
|
+
{ cmd: 'pulse analyze --verbose', desc: 'Show detailed breakdown' }
|
|
224
|
+
]
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
test: {
|
|
228
|
+
name: 'test',
|
|
229
|
+
summary: 'Run tests with coverage support',
|
|
230
|
+
usage: 'pulse test [files] [options]',
|
|
231
|
+
description: `
|
|
232
|
+
Runs tests using Node.js built-in test runner:
|
|
233
|
+
- Automatic test file discovery
|
|
234
|
+
- Code coverage reporting
|
|
235
|
+
- Watch mode for TDD
|
|
236
|
+
- Test file generation`,
|
|
237
|
+
arguments: [
|
|
238
|
+
{ name: '[files]', description: 'Test files to run (default: test/**/*.test.js)' }
|
|
239
|
+
],
|
|
240
|
+
options: [
|
|
241
|
+
{ flag: '--coverage, -c', description: 'Collect code coverage' },
|
|
242
|
+
{ flag: '--watch, -w', description: 'Watch files and re-run tests' },
|
|
243
|
+
{ flag: '--filter, -f <pattern>', description: 'Filter tests by name pattern' },
|
|
244
|
+
{ flag: '--timeout, -t <ms>', description: 'Test timeout in ms (default: 30000)' },
|
|
245
|
+
{ flag: '--bail, -b', description: 'Stop on first failure' },
|
|
246
|
+
{ flag: '--create <name>', description: 'Generate a new test file' }
|
|
247
|
+
],
|
|
248
|
+
examples: [
|
|
249
|
+
{ cmd: 'pulse test', desc: 'Run all tests' },
|
|
250
|
+
{ cmd: 'pulse test --coverage', desc: 'Run with coverage' },
|
|
251
|
+
{ cmd: 'pulse test --watch', desc: 'Watch mode for TDD' },
|
|
252
|
+
{ cmd: 'pulse test --filter "Button"', desc: 'Run tests matching pattern' },
|
|
253
|
+
{ cmd: 'pulse test --create Button', desc: 'Generate Button.test.js' }
|
|
254
|
+
]
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
doctor: {
|
|
258
|
+
name: 'doctor',
|
|
259
|
+
summary: 'Run project diagnostics',
|
|
260
|
+
usage: 'pulse doctor [options]',
|
|
261
|
+
description: `
|
|
262
|
+
Checks your project health and reports:
|
|
263
|
+
- Missing dependencies
|
|
264
|
+
- Configuration issues
|
|
265
|
+
- Version compatibility
|
|
266
|
+
- Common problems and fixes`,
|
|
267
|
+
options: [
|
|
268
|
+
{ flag: '--verbose, -v', description: 'Show detailed diagnostics' },
|
|
269
|
+
{ flag: '--json', description: 'Output as JSON' }
|
|
270
|
+
],
|
|
271
|
+
examples: [
|
|
272
|
+
{ cmd: 'pulse doctor', desc: 'Run diagnostics' },
|
|
273
|
+
{ cmd: 'pulse doctor --verbose', desc: 'Show detailed output' },
|
|
274
|
+
{ cmd: 'pulse doctor --json', desc: 'Output as JSON' }
|
|
275
|
+
]
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
scaffold: {
|
|
279
|
+
name: 'scaffold',
|
|
280
|
+
summary: 'Generate components, pages, stores, and more',
|
|
281
|
+
usage: 'pulse scaffold <type> <name> [options]',
|
|
282
|
+
description: `
|
|
283
|
+
Generates boilerplate code for common patterns:
|
|
284
|
+
- component: Reusable UI component
|
|
285
|
+
- page: Page component with routing
|
|
286
|
+
- store: State management module
|
|
287
|
+
- hook: Custom reactive hook
|
|
288
|
+
- service: API service module
|
|
289
|
+
- context: Context provider
|
|
290
|
+
- layout: Layout component`,
|
|
291
|
+
arguments: [
|
|
292
|
+
{ name: '<type>', description: 'Type: component, page, store, hook, service, context, layout' },
|
|
293
|
+
{ name: '<name>', description: 'Name of the item to generate' }
|
|
294
|
+
],
|
|
295
|
+
options: [
|
|
296
|
+
{ flag: '--dir, -d <path>', description: 'Output directory' },
|
|
297
|
+
{ flag: '--force, -f', description: 'Overwrite existing files' },
|
|
298
|
+
{ flag: '--props', description: 'Include props section (components)' }
|
|
299
|
+
],
|
|
300
|
+
examples: [
|
|
301
|
+
{ cmd: 'pulse scaffold component Button', desc: 'Generate Button component' },
|
|
302
|
+
{ cmd: 'pulse scaffold page Dashboard', desc: 'Generate Dashboard page' },
|
|
303
|
+
{ cmd: 'pulse scaffold store user', desc: 'Generate user store' },
|
|
304
|
+
{ cmd: 'pulse scaffold hook useAuth', desc: 'Generate useAuth hook' },
|
|
305
|
+
{ cmd: 'pulse scaffold service api', desc: 'Generate api service' }
|
|
306
|
+
]
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
docs: {
|
|
310
|
+
name: 'docs',
|
|
311
|
+
summary: 'Generate API documentation',
|
|
312
|
+
usage: 'pulse docs [options]',
|
|
313
|
+
description: `
|
|
314
|
+
Generates API documentation from JSDoc comments:
|
|
315
|
+
- Markdown format (default)
|
|
316
|
+
- HTML format with styling
|
|
317
|
+
- JSON format for tooling`,
|
|
318
|
+
options: [
|
|
319
|
+
{ flag: '--generate, -g', description: 'Generate documentation' },
|
|
320
|
+
{ flag: '--format, -f <type>', description: 'Output format: markdown, json, html' },
|
|
321
|
+
{ flag: '--output, -o <dir>', description: 'Output directory (default: docs/api)' }
|
|
322
|
+
],
|
|
323
|
+
examples: [
|
|
324
|
+
{ cmd: 'pulse docs --generate', desc: 'Generate Markdown docs' },
|
|
325
|
+
{ cmd: 'pulse docs -g --format html', desc: 'Generate HTML docs' },
|
|
326
|
+
{ cmd: 'pulse docs -g -o api-docs/', desc: 'Output to custom directory' }
|
|
327
|
+
]
|
|
328
|
+
},
|
|
329
|
+
|
|
330
|
+
release: {
|
|
331
|
+
name: 'release',
|
|
332
|
+
summary: 'Create a new release',
|
|
333
|
+
usage: 'pulse release <type> [options]',
|
|
334
|
+
description: `
|
|
335
|
+
Creates a new release with:
|
|
336
|
+
- Version bump (patch, minor, major)
|
|
337
|
+
- Changelog generation
|
|
338
|
+
- Git tag creation
|
|
339
|
+
- Optional push to remote`,
|
|
340
|
+
arguments: [
|
|
341
|
+
{ name: '<type>', description: 'Release type: patch, minor, or major' }
|
|
342
|
+
],
|
|
343
|
+
options: [
|
|
344
|
+
{ flag: '--dry-run', description: 'Show what would be done without making changes' },
|
|
345
|
+
{ flag: '--no-push', description: 'Create commit and tag but do not push' },
|
|
346
|
+
{ flag: '--title <text>', description: 'Release title for changelog' },
|
|
347
|
+
{ flag: '--skip-prompt', description: 'Use empty changelog (for automation)' },
|
|
348
|
+
{ flag: '--from-commits', description: 'Auto-extract changelog from git commits' }
|
|
349
|
+
],
|
|
350
|
+
examples: [
|
|
351
|
+
{ cmd: 'pulse release patch', desc: 'Create patch release (1.0.0 -> 1.0.1)' },
|
|
352
|
+
{ cmd: 'pulse release minor', desc: 'Create minor release (1.0.0 -> 1.1.0)' },
|
|
353
|
+
{ cmd: 'pulse release major', desc: 'Create major release (1.0.0 -> 2.0.0)' },
|
|
354
|
+
{ cmd: 'pulse release patch --dry-run', desc: 'Preview release without changes' }
|
|
355
|
+
]
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
mobile: {
|
|
359
|
+
name: 'mobile',
|
|
360
|
+
summary: 'Mobile app commands',
|
|
361
|
+
usage: 'pulse mobile <command> [options]',
|
|
362
|
+
description: `
|
|
363
|
+
Commands for building mobile apps with Pulse:
|
|
364
|
+
- init: Initialize mobile project structure
|
|
365
|
+
- build: Build for iOS/Android
|
|
366
|
+
- run: Run on device/simulator`,
|
|
367
|
+
arguments: [
|
|
368
|
+
{ name: '<command>', description: 'Mobile command: init, build, run' }
|
|
369
|
+
],
|
|
370
|
+
examples: [
|
|
371
|
+
{ cmd: 'pulse mobile init', desc: 'Initialize mobile project' },
|
|
372
|
+
{ cmd: 'pulse mobile build ios', desc: 'Build for iOS' },
|
|
373
|
+
{ cmd: 'pulse mobile build android', desc: 'Build for Android' },
|
|
374
|
+
{ cmd: 'pulse mobile run ios', desc: 'Run on iOS simulator' }
|
|
375
|
+
]
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
version: {
|
|
379
|
+
name: 'version',
|
|
380
|
+
summary: 'Show version number',
|
|
381
|
+
usage: 'pulse version',
|
|
382
|
+
description: 'Displays the current version of Pulse Framework CLI.',
|
|
383
|
+
examples: [
|
|
384
|
+
{ cmd: 'pulse version', desc: 'Show version' },
|
|
385
|
+
{ cmd: 'pulse --version', desc: 'Alternative syntax' }
|
|
386
|
+
]
|
|
387
|
+
},
|
|
388
|
+
|
|
389
|
+
help: {
|
|
390
|
+
name: 'help',
|
|
391
|
+
summary: 'Show help information',
|
|
392
|
+
usage: 'pulse help [command]',
|
|
393
|
+
description: `
|
|
394
|
+
Displays help information for Pulse CLI commands.
|
|
395
|
+
Run without arguments for overview, or specify a command for detailed help.`,
|
|
396
|
+
arguments: [
|
|
397
|
+
{ name: '[command]', description: 'Command to get help for' }
|
|
398
|
+
],
|
|
399
|
+
examples: [
|
|
400
|
+
{ cmd: 'pulse help', desc: 'Show general help' },
|
|
401
|
+
{ cmd: 'pulse help create', desc: 'Show help for create command' },
|
|
402
|
+
{ cmd: 'pulse help scaffold', desc: 'Show help for scaffold command' }
|
|
403
|
+
]
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Format a section header
|
|
409
|
+
*/
|
|
410
|
+
function formatHeader(text) {
|
|
411
|
+
return `\n${text}\n${'─'.repeat(text.length)}`;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Format command arguments table
|
|
416
|
+
*/
|
|
417
|
+
function formatArguments(args) {
|
|
418
|
+
if (!args || args.length === 0) return '';
|
|
419
|
+
|
|
420
|
+
let output = formatHeader('Arguments');
|
|
421
|
+
const maxLen = Math.max(...args.map(a => a.name.length));
|
|
422
|
+
|
|
423
|
+
for (const arg of args) {
|
|
424
|
+
output += `\n ${arg.name.padEnd(maxLen + 2)}${arg.description}`;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return output;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Format command options table
|
|
432
|
+
*/
|
|
433
|
+
function formatOptions(options) {
|
|
434
|
+
if (!options || options.length === 0) return '';
|
|
435
|
+
|
|
436
|
+
let output = formatHeader('Options');
|
|
437
|
+
const maxLen = Math.max(...options.map(o => o.flag.length));
|
|
438
|
+
|
|
439
|
+
for (const opt of options) {
|
|
440
|
+
output += `\n ${opt.flag.padEnd(maxLen + 2)}${opt.description}`;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return output;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Format examples
|
|
448
|
+
*/
|
|
449
|
+
function formatExamples(examples) {
|
|
450
|
+
if (!examples || examples.length === 0) return '';
|
|
451
|
+
|
|
452
|
+
let output = formatHeader('Examples');
|
|
453
|
+
|
|
454
|
+
for (const ex of examples) {
|
|
455
|
+
output += `\n $ ${ex.cmd}`;
|
|
456
|
+
if (ex.desc) {
|
|
457
|
+
output += `\n ${ex.desc}`;
|
|
458
|
+
}
|
|
459
|
+
output += '\n';
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return output;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Show detailed help for a specific command
|
|
467
|
+
*/
|
|
468
|
+
function showCommandHelp(commandName) {
|
|
469
|
+
const cmd = commandDefinitions[commandName];
|
|
470
|
+
|
|
471
|
+
if (!cmd) {
|
|
472
|
+
log.error(`Unknown command: ${commandName}\n`);
|
|
473
|
+
log.info('Available commands:');
|
|
474
|
+
const cmdList = Object.keys(commandDefinitions).sort();
|
|
475
|
+
log.info(` ${cmdList.join(', ')}\n`);
|
|
476
|
+
log.info('Run "pulse help" for usage information.');
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
let output = `
|
|
481
|
+
${cmd.name} - ${cmd.summary}
|
|
482
|
+
|
|
483
|
+
Usage: ${cmd.usage}
|
|
484
|
+
${cmd.description}`;
|
|
485
|
+
|
|
486
|
+
output += formatArguments(cmd.arguments);
|
|
487
|
+
output += formatOptions(cmd.options);
|
|
488
|
+
output += formatExamples(cmd.examples);
|
|
489
|
+
|
|
490
|
+
output += `
|
|
491
|
+
────────────────────────────────────────
|
|
492
|
+
Documentation: https://pulse-js.fr/cli/${cmd.name}
|
|
493
|
+
`;
|
|
494
|
+
|
|
495
|
+
log.info(output);
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Show general help overview
|
|
501
|
+
*/
|
|
502
|
+
function showGeneralHelp() {
|
|
503
|
+
const commandGroups = {
|
|
504
|
+
'Project Setup': ['create', 'init'],
|
|
505
|
+
'Development': ['dev', 'build', 'preview'],
|
|
506
|
+
'Code Quality': ['compile', 'lint', 'format', 'analyze'],
|
|
507
|
+
'Testing': ['test', 'doctor'],
|
|
508
|
+
'Scaffolding': ['scaffold', 'docs'],
|
|
509
|
+
'Release': ['release'],
|
|
510
|
+
'Mobile': ['mobile'],
|
|
511
|
+
'Information': ['version', 'help']
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
let output = `
|
|
515
|
+
Pulse Framework CLI v${VERSION}
|
|
516
|
+
|
|
517
|
+
Usage: pulse <command> [options]
|
|
518
|
+
|
|
519
|
+
`;
|
|
520
|
+
|
|
521
|
+
// Calculate max command length for alignment
|
|
522
|
+
const allCmds = Object.values(commandGroups).flat();
|
|
523
|
+
const maxCmdLen = Math.max(...allCmds.map(c => c.length));
|
|
524
|
+
|
|
525
|
+
for (const [group, cmds] of Object.entries(commandGroups)) {
|
|
526
|
+
output += `${group}:\n`;
|
|
527
|
+
for (const cmdName of cmds) {
|
|
528
|
+
const cmd = commandDefinitions[cmdName];
|
|
529
|
+
if (cmd) {
|
|
530
|
+
output += ` ${cmdName.padEnd(maxCmdLen + 2)}${cmd.summary}\n`;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
output += '\n';
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
output += `Run "pulse help <command>" for detailed information about a command.
|
|
537
|
+
|
|
538
|
+
Quick Start:
|
|
539
|
+
$ pulse create my-app Create a new project
|
|
540
|
+
$ cd my-app && npm install Install dependencies
|
|
541
|
+
$ npm run dev Start development server
|
|
542
|
+
|
|
543
|
+
Documentation: https://pulse-js.fr
|
|
544
|
+
Repository: https://github.com/vincenthirtz/pulse-js-framework
|
|
545
|
+
`;
|
|
546
|
+
|
|
547
|
+
log.info(output);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Main help command handler
|
|
552
|
+
* @param {string[]} args - Command arguments
|
|
553
|
+
*/
|
|
554
|
+
export function runHelp(args) {
|
|
555
|
+
const commandName = args[0];
|
|
556
|
+
|
|
557
|
+
if (commandName) {
|
|
558
|
+
// Handle aliases
|
|
559
|
+
const normalizedName = commandName.toLowerCase();
|
|
560
|
+
showCommandHelp(normalizedName);
|
|
561
|
+
} else {
|
|
562
|
+
showGeneralHelp();
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Get list of available commands
|
|
568
|
+
* @returns {string[]} Array of command names
|
|
569
|
+
*/
|
|
570
|
+
export function getAvailableCommands() {
|
|
571
|
+
return Object.keys(commandDefinitions);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Get command definition
|
|
576
|
+
* @param {string} name - Command name
|
|
577
|
+
* @returns {Object|undefined} Command definition or undefined
|
|
578
|
+
*/
|
|
579
|
+
export function getCommandDefinition(name) {
|
|
580
|
+
return commandDefinitions[name];
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
export default { runHelp, getAvailableCommands, getCommandDefinition };
|