pulse-js-framework 1.7.12 → 1.7.13

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/cli/index.js CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  import { fileURLToPath } from 'url';
8
8
  import { dirname, join, resolve, relative } from 'path';
9
- import { existsSync, mkdirSync, writeFileSync, readFileSync, cpSync, watch } from 'fs';
9
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, watch } from 'fs';
10
10
  import { log } from './logger.js';
11
11
  import { findPulseFiles, parseArgs } from './utils/file-utils.js';
12
12
 
@@ -22,6 +22,7 @@ const commands = {
22
22
  help: showHelp,
23
23
  version: showVersion,
24
24
  create: createProject,
25
+ init: initProject,
25
26
  dev: runDev,
26
27
  build: runBuild,
27
28
  preview: runPreview,
@@ -30,6 +31,10 @@ const commands = {
30
31
  lint: runLint,
31
32
  format: runFormat,
32
33
  analyze: runAnalyze,
34
+ test: runTest,
35
+ doctor: runDoctorCmd,
36
+ scaffold: runScaffoldCmd,
37
+ docs: runDocsCmd,
33
38
  release: runReleaseCmd,
34
39
  'docs-test': runDocsTestCmd
35
40
  };
@@ -58,7 +63,16 @@ const commandAliases = {
58
63
  'moble': 'mobile',
59
64
  'moblie': 'mobile',
60
65
  'relase': 'release',
61
- 'realease': 'release'
66
+ 'realease': 'release',
67
+ 'tset': 'test',
68
+ 'testt': 'test',
69
+ 'doctr': 'doctor',
70
+ 'docter': 'doctor',
71
+ 'scafold': 'scaffold',
72
+ 'scaffod': 'scaffold',
73
+ 'scafflod': 'scaffold',
74
+ 'doc': 'docs',
75
+ 'dcos': 'docs'
62
76
  };
63
77
 
64
78
  /**
@@ -150,76 +164,99 @@ Pulse Framework CLI v${VERSION}
150
164
  Usage: pulse <command> [options]
151
165
 
152
166
  Commands:
153
- create <name> Create a new Pulse project
154
- dev [port] Start development server (default: 3000)
155
- build Build for production (minified)
156
- preview [port] Preview production build (default: 4173)
157
- compile <file> Compile a .pulse file to JavaScript
158
- mobile <cmd> Mobile app commands (init, build, run)
159
- lint [files] Validate .pulse files for errors and style
160
- format [files] Format .pulse files consistently
161
- analyze Analyze bundle size and dependencies
162
- release <type> Create a new release (patch, minor, major)
163
- docs-test Test documentation (syntax, imports, HTTP)
164
- version Show version number
165
- help Show this help message
167
+ create <name> Create a new Pulse project
168
+ init [options] Initialize project in current directory
169
+ dev [port] Start development server (default: 3000)
170
+ build Build for production (minified)
171
+ preview [port] Preview production build (default: 4173)
172
+ compile <file> Compile a .pulse file to JavaScript
173
+ mobile <cmd> Mobile app commands (init, build, run)
174
+ lint [files] Validate .pulse files for errors and style
175
+ format [files] Format .pulse files consistently
176
+ analyze Analyze bundle size and dependencies
177
+ test [files] Run tests with coverage support
178
+ doctor Run project diagnostics
179
+ scaffold <type> Generate components, pages, stores
180
+ docs Generate API documentation from JSDoc
181
+ release <type> Create a new release (patch, minor, major)
182
+ docs-test Test documentation (syntax, imports, HTTP)
183
+ version Show version number
184
+ help Show this help message
185
+
186
+ Create/Init Options:
187
+ --typescript Create TypeScript project
188
+ --minimal Create minimal project structure
166
189
 
167
190
  Compile Options:
168
- --watch, -w Watch files and recompile on changes
169
- --dry-run Show what would be compiled without writing
170
- --output, -o Output directory (default: same as input)
191
+ --watch, -w Watch files and recompile on changes
192
+ --dry-run Show what would be compiled without writing
193
+ --output, -o Output directory (default: same as input)
171
194
 
172
195
  Lint Options:
173
- --fix Auto-fix fixable issues
174
- --watch, -w Watch files and re-lint on changes
175
- --dry-run Show fixes without applying (use with --fix)
196
+ --fix Auto-fix fixable issues
197
+ --watch, -w Watch files and re-lint on changes
198
+ --dry-run Show fixes without applying (use with --fix)
176
199
 
177
200
  Format Options:
178
- --check Check formatting without writing (dry-run)
179
- --watch, -w Watch files and re-format on changes
180
- --write Write formatted output (default)
201
+ --check Check formatting without writing (dry-run)
202
+ --watch, -w Watch files and re-format on changes
203
+ --write Write formatted output (default)
181
204
 
182
205
  Analyze Options:
183
- --json Output analysis as JSON
184
- --verbose Show detailed metrics
206
+ --json Output analysis as JSON
207
+ --verbose Show detailed metrics
208
+
209
+ Test Options:
210
+ --coverage, -c Collect code coverage
211
+ --watch, -w Watch files and re-run tests
212
+ --filter, -f Filter tests by name pattern
213
+ --timeout, -t Test timeout in ms (default: 30000)
214
+ --bail, -b Stop on first failure
215
+ --create <name> Generate a new test file
216
+
217
+ Doctor Options:
218
+ --verbose, -v Show detailed diagnostics
219
+ --json Output as JSON
220
+
221
+ Scaffold Options:
222
+ --dir, -d <path> Output directory
223
+ --force, -f Overwrite existing files
224
+ --props Include props section (components)
225
+
226
+ Docs Options:
227
+ --generate, -g Generate documentation
228
+ --format, -f Output format: markdown, json, html
229
+ --output, -o Output directory (default: docs/api)
185
230
 
186
231
  Release Options:
187
- --dry-run Show what would be done without making changes
188
- --no-push Create commit and tag but don't push
189
- --title <text> Release title for changelog
190
- --skip-prompt Use empty changelog (for automation)
191
- --skip-docs-test Skip documentation tests before release
192
- --from-commits Auto-extract changelog from git commits since last tag
193
-
194
- Docs-test Options:
195
- --verbose, -v Show detailed output
196
- --no-http Skip HTTP server tests
232
+ --dry-run Show what would be done without making changes
233
+ --no-push Create commit and tag but don't push
234
+ --title <text> Release title for changelog
235
+ --skip-prompt Use empty changelog (for automation)
236
+ --skip-docs-test Skip documentation tests before release
237
+ --from-commits Auto-extract changelog from git commits since last tag
197
238
 
198
239
  Examples:
199
240
  pulse create my-app
241
+ pulse create my-app --typescript
242
+ pulse init --typescript
200
243
  pulse dev
201
- pulse dev 8080
202
244
  pulse build
203
- pulse preview
204
- pulse mobile init
205
- pulse mobile build android
206
- pulse mobile run ios
245
+ pulse test
246
+ pulse test --coverage --watch
247
+ pulse test --create MyComponent
248
+ pulse doctor
249
+ pulse doctor --verbose
250
+ pulse scaffold component Button
251
+ pulse scaffold page Dashboard
252
+ pulse scaffold store user
253
+ pulse docs --generate
254
+ pulse docs --generate --format html
207
255
  pulse compile src/App.pulse
208
- pulse compile src/ --watch
209
- pulse compile "**/*.pulse" --dry-run
210
- pulse lint src/
211
- pulse lint src/ --fix --dry-run
212
- pulse lint "**/*.pulse" --fix
256
+ pulse lint src/ --fix
213
257
  pulse format --check
214
- pulse format src/App.pulse
215
- pulse analyze
216
258
  pulse analyze --json
217
259
  pulse release patch
218
- pulse release minor --title "New Features"
219
- pulse release major --dry-run
220
- pulse release patch --from-commits
221
- pulse docs-test
222
- pulse docs-test --verbose
223
260
 
224
261
  Documentation: https://github.com/vincenthirtz/pulse-js-framework
225
262
  `);
@@ -236,7 +273,8 @@ function showVersion() {
236
273
  * Create a new project
237
274
  */
238
275
  async function createProject(args) {
239
- const projectName = args[0];
276
+ const { options, patterns } = parseArgs(args);
277
+ const projectName = patterns[0];
240
278
 
241
279
  if (!projectName) {
242
280
  log.error('Please provide a project name.');
@@ -251,13 +289,21 @@ async function createProject(args) {
251
289
  process.exit(1);
252
290
  }
253
291
 
254
- log.info(`Creating new Pulse project: ${projectName}`);
292
+ const useTypescript = options.typescript || options.ts || false;
293
+ const minimal = options.minimal || false;
294
+
295
+ log.info(`Creating new Pulse project: ${projectName}${useTypescript ? ' (TypeScript)' : ''}`);
255
296
 
256
297
  // Create project structure
257
298
  mkdirSync(projectPath);
258
299
  mkdirSync(join(projectPath, 'src'));
259
300
  mkdirSync(join(projectPath, 'public'));
260
301
 
302
+ // TypeScript-specific directories
303
+ if (useTypescript && !minimal) {
304
+ mkdirSync(join(projectPath, 'src', 'types'));
305
+ }
306
+
261
307
  // Create package.json
262
308
  const packageJson = {
263
309
  name: projectName,
@@ -266,13 +312,20 @@ async function createProject(args) {
266
312
  scripts: {
267
313
  dev: 'pulse dev',
268
314
  build: 'pulse build',
269
- preview: 'vite preview'
315
+ preview: 'vite preview',
316
+ test: 'pulse test',
317
+ lint: 'pulse lint',
318
+ ...(useTypescript ? { typecheck: 'tsc --noEmit' } : {})
270
319
  },
271
320
  dependencies: {
272
321
  'pulse-js-framework': '^1.0.0'
273
322
  },
274
323
  devDependencies: {
275
- vite: '^5.0.0'
324
+ vite: '^5.0.0',
325
+ ...(useTypescript ? {
326
+ 'typescript': '^5.3.0',
327
+ '@types/node': '^20.0.0'
328
+ } : {})
276
329
  }
277
330
  };
278
331
 
@@ -281,7 +334,8 @@ async function createProject(args) {
281
334
  JSON.stringify(packageJson, null, 2)
282
335
  );
283
336
 
284
- // Create vite.config.js
337
+ // Create vite.config
338
+ const viteConfigExt = useTypescript ? 'ts' : 'js';
285
339
  const viteConfig = `import { defineConfig } from 'vite';
286
340
  import pulse from 'pulse-js-framework/vite';
287
341
 
@@ -290,9 +344,63 @@ export default defineConfig({
290
344
  });
291
345
  `;
292
346
 
293
- writeFileSync(join(projectPath, 'vite.config.js'), viteConfig);
347
+ writeFileSync(join(projectPath, `vite.config.${viteConfigExt}`), viteConfig);
348
+
349
+ // Create TypeScript config if needed
350
+ if (useTypescript) {
351
+ const tsConfig = {
352
+ compilerOptions: {
353
+ target: 'ES2022',
354
+ useDefineForClassFields: true,
355
+ module: 'ESNext',
356
+ lib: ['ES2022', 'DOM', 'DOM.Iterable'],
357
+ skipLibCheck: true,
358
+ moduleResolution: 'bundler',
359
+ allowImportingTsExtensions: true,
360
+ resolveJsonModule: true,
361
+ isolatedModules: true,
362
+ noEmit: true,
363
+ strict: true,
364
+ noUnusedLocals: true,
365
+ noUnusedParameters: true,
366
+ noFallthroughCasesInSwitch: true,
367
+ types: ['node']
368
+ },
369
+ include: ['src/**/*', 'vite.config.ts'],
370
+ exclude: ['node_modules', 'dist']
371
+ };
372
+
373
+ writeFileSync(
374
+ join(projectPath, 'tsconfig.json'),
375
+ JSON.stringify(tsConfig, null, 2)
376
+ );
377
+
378
+ // Create types file
379
+ if (!minimal) {
380
+ const typesContent = `/**
381
+ * Type declarations for ${projectName}
382
+ */
383
+
384
+ // Pulse framework module declarations
385
+ declare module '*.pulse' {
386
+ const component: {
387
+ mount(selector: string): void;
388
+ };
389
+ export default component;
390
+ }
391
+
392
+ // Add your custom types here
393
+ export interface AppState {
394
+ count: number;
395
+ name: string;
396
+ }
397
+ `;
398
+ writeFileSync(join(projectPath, 'src', 'types', 'index.d.ts'), typesContent);
399
+ }
400
+ }
294
401
 
295
402
  // Create index.html
403
+ const mainExt = useTypescript ? 'ts' : 'js';
296
404
  const indexHtml = `<!DOCTYPE html>
297
405
  <html lang="en">
298
406
  <head>
@@ -302,20 +410,31 @@ export default defineConfig({
302
410
  </head>
303
411
  <body>
304
412
  <div id="app"></div>
305
- <script type="module" src="/src/main.js"></script>
413
+ <script type="module" src="/src/main.${mainExt}"></script>
306
414
  </body>
307
415
  </html>
308
416
  `;
309
417
 
310
418
  writeFileSync(join(projectPath, 'index.html'), indexHtml);
311
419
 
312
- // Create main.js
313
- const mainJs = `import App from './App.pulse';
420
+ // Create main file
421
+ const mainContent = useTypescript
422
+ ? `import App from './App.pulse';
423
+
424
+ // Type-safe app mounting
425
+ App.mount('#app');
426
+
427
+ // Enable HMR in development
428
+ if (import.meta.hot) {
429
+ import.meta.hot.accept();
430
+ }
431
+ `
432
+ : `import App from './App.pulse';
314
433
 
315
434
  App.mount('#app');
316
435
  `;
317
436
 
318
- writeFileSync(join(projectPath, 'src', 'main.js'), mainJs);
437
+ writeFileSync(join(projectPath, 'src', `main.${mainExt}`), mainContent);
319
438
 
320
439
  // Create App.pulse
321
440
  const appPulse = `@page App
@@ -404,7 +523,7 @@ style {
404
523
  dist
405
524
  .DS_Store
406
525
  *.local
407
- `;
526
+ ${useTypescript ? '*.tsbuildinfo\n' : ''}`;
408
527
 
409
528
  writeFileSync(join(projectPath, '.gitignore'), gitignore);
410
529
 
@@ -415,11 +534,133 @@ Next steps:
415
534
  cd ${projectName}
416
535
  npm install
417
536
  npm run dev
418
-
537
+ ${useTypescript ? '\nTypeScript enabled. Run "npm run typecheck" to check types.\n' : ''}
419
538
  Happy coding with Pulse!
420
539
  `);
421
540
  }
422
541
 
542
+ /**
543
+ * Initialize project in current directory
544
+ */
545
+ async function initProject(args) {
546
+ const { options } = parseArgs(args);
547
+ const cwd = process.cwd();
548
+ const projectName = cwd.split(/[\\/]/).pop();
549
+
550
+ const useTypescript = options.typescript || options.ts || false;
551
+
552
+ // Check if directory is empty or has only hidden files
553
+ const entries = readdirSync(cwd).filter(e => !e.startsWith('.'));
554
+ if (entries.length > 0 && !options.force) {
555
+ log.warn('Directory is not empty. Use --force to initialize anyway.');
556
+ log.info('Existing files: ' + entries.slice(0, 5).join(', ') + (entries.length > 5 ? '...' : ''));
557
+ return;
558
+ }
559
+
560
+ log.info(`Initializing Pulse project in current directory${useTypescript ? ' (TypeScript)' : ''}...`);
561
+
562
+ // Create src directory if it doesn't exist
563
+ if (!existsSync(join(cwd, 'src'))) {
564
+ mkdirSync(join(cwd, 'src'));
565
+ }
566
+
567
+ // Check for existing package.json
568
+ const pkgPath = join(cwd, 'package.json');
569
+ let pkg = {};
570
+
571
+ if (existsSync(pkgPath)) {
572
+ try {
573
+ pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
574
+ log.info('Found existing package.json, merging...');
575
+ } catch (e) {
576
+ log.warn('Could not parse existing package.json, creating new one.');
577
+ }
578
+ }
579
+
580
+ // Merge with Pulse requirements
581
+ pkg = {
582
+ ...pkg,
583
+ name: pkg.name || projectName,
584
+ version: pkg.version || '0.1.0',
585
+ type: 'module',
586
+ scripts: {
587
+ ...(pkg.scripts || {}),
588
+ dev: 'pulse dev',
589
+ build: 'pulse build',
590
+ preview: 'vite preview',
591
+ test: 'pulse test',
592
+ lint: 'pulse lint',
593
+ ...(useTypescript ? { typecheck: 'tsc --noEmit' } : {})
594
+ },
595
+ dependencies: {
596
+ ...(pkg.dependencies || {}),
597
+ 'pulse-js-framework': '^1.0.0'
598
+ },
599
+ devDependencies: {
600
+ ...(pkg.devDependencies || {}),
601
+ vite: '^5.0.0',
602
+ ...(useTypescript ? {
603
+ 'typescript': '^5.3.0',
604
+ '@types/node': '^20.0.0'
605
+ } : {})
606
+ }
607
+ };
608
+
609
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
610
+ log.success('Updated package.json');
611
+
612
+ // Create vite.config if it doesn't exist
613
+ const viteConfigExt = useTypescript ? 'ts' : 'js';
614
+ const viteConfigPath = join(cwd, `vite.config.${viteConfigExt}`);
615
+
616
+ if (!existsSync(viteConfigPath) && !existsSync(join(cwd, 'vite.config.js')) && !existsSync(join(cwd, 'vite.config.ts'))) {
617
+ const viteConfig = `import { defineConfig } from 'vite';
618
+ import pulse from 'pulse-js-framework/vite';
619
+
620
+ export default defineConfig({
621
+ plugins: [pulse()]
622
+ });
623
+ `;
624
+ writeFileSync(viteConfigPath, viteConfig);
625
+ log.success(`Created vite.config.${viteConfigExt}`);
626
+ }
627
+
628
+ // Create tsconfig if TypeScript
629
+ if (useTypescript && !existsSync(join(cwd, 'tsconfig.json'))) {
630
+ const tsConfig = {
631
+ compilerOptions: {
632
+ target: 'ES2022',
633
+ useDefineForClassFields: true,
634
+ module: 'ESNext',
635
+ lib: ['ES2022', 'DOM', 'DOM.Iterable'],
636
+ skipLibCheck: true,
637
+ moduleResolution: 'bundler',
638
+ allowImportingTsExtensions: true,
639
+ resolveJsonModule: true,
640
+ isolatedModules: true,
641
+ noEmit: true,
642
+ strict: true,
643
+ noUnusedLocals: true,
644
+ noUnusedParameters: true,
645
+ noFallthroughCasesInSwitch: true
646
+ },
647
+ include: ['src/**/*', 'vite.config.ts'],
648
+ exclude: ['node_modules', 'dist']
649
+ };
650
+
651
+ writeFileSync(join(cwd, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2));
652
+ log.success('Created tsconfig.json');
653
+ }
654
+
655
+ log.info(`
656
+ Initialization complete!
657
+
658
+ Next steps:
659
+ npm install
660
+ npm run dev
661
+ `);
662
+ }
663
+
423
664
  /**
424
665
  * Run development server
425
666
  */
@@ -499,6 +740,38 @@ async function runDocsTestCmd(args) {
499
740
  await runDocsTestCli(args);
500
741
  }
501
742
 
743
+ /**
744
+ * Run test command
745
+ */
746
+ async function runTest(args) {
747
+ const { runTestCommand } = await import('./test.js');
748
+ await runTestCommand(args);
749
+ }
750
+
751
+ /**
752
+ * Run doctor command
753
+ */
754
+ async function runDoctorCmd(args) {
755
+ const { runDoctor } = await import('./doctor.js');
756
+ await runDoctor(args);
757
+ }
758
+
759
+ /**
760
+ * Run scaffold command
761
+ */
762
+ async function runScaffoldCmd(args) {
763
+ const { runScaffold } = await import('./scaffold.js');
764
+ await runScaffold(args);
765
+ }
766
+
767
+ /**
768
+ * Run docs command
769
+ */
770
+ async function runDocsCmd(args) {
771
+ const { runDocs } = await import('./docs.js');
772
+ await runDocs(args);
773
+ }
774
+
502
775
  /**
503
776
  * Compile .pulse files to JavaScript
504
777
  * Supports multiple files, watch mode, and dry-run