pulse-js-framework 1.7.3 → 1.7.5
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/analyze.js +127 -46
- package/cli/build.js +148 -34
- package/cli/dev.js +20 -5
- package/cli/format.js +64 -8
- package/cli/lint.js +112 -27
- package/cli/utils/cli-ui.js +452 -0
- package/compiler/parser.js +19 -2
- package/core/errors.js +281 -6
- package/package.json +7 -2
- package/runtime/async.js +282 -14
- package/runtime/dom-adapter.js +920 -0
- package/runtime/dom.js +331 -162
- package/runtime/logger.js +144 -69
- package/runtime/logger.prod.js +43 -18
- package/runtime/pulse.js +202 -80
- package/runtime/router.js +27 -39
- package/runtime/store.js +10 -7
- package/runtime/utils.js +279 -18
package/cli/format.js
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
* Formats .pulse files consistently
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { readFileSync, writeFileSync } from 'fs';
|
|
6
|
+
import { readFileSync, writeFileSync, watch } from 'fs';
|
|
7
|
+
import { dirname } from 'path';
|
|
7
8
|
import { findPulseFiles, parseArgs, relativePath } from './utils/file-utils.js';
|
|
8
9
|
import { log } from './logger.js';
|
|
10
|
+
import { createTimer, formatDuration } from './utils/cli-ui.js';
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Default format options
|
|
@@ -641,6 +643,7 @@ export async function runFormat(args) {
|
|
|
641
643
|
const { options, patterns } = parseArgs(args);
|
|
642
644
|
const check = options.check || false;
|
|
643
645
|
const write = !check; // Default to write unless --check is specified
|
|
646
|
+
const watchMode = options.watch || options.w || false;
|
|
644
647
|
|
|
645
648
|
// Find files to format
|
|
646
649
|
const files = findPulseFiles(patterns);
|
|
@@ -650,7 +653,57 @@ export async function runFormat(args) {
|
|
|
650
653
|
return;
|
|
651
654
|
}
|
|
652
655
|
|
|
653
|
-
|
|
656
|
+
// Run initial format
|
|
657
|
+
const result = await runFormatOnFiles(files, { check, write, options });
|
|
658
|
+
|
|
659
|
+
// If watch mode, set up file watchers
|
|
660
|
+
if (watchMode) {
|
|
661
|
+
if (check) {
|
|
662
|
+
log.warn('--watch mode is not available with --check');
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
log.info('\nWatching for changes... (Ctrl+C to stop)\n');
|
|
667
|
+
|
|
668
|
+
// Get unique directories to watch
|
|
669
|
+
const watchedDirs = new Set(files.map(f => dirname(f)));
|
|
670
|
+
|
|
671
|
+
// Debounce timer
|
|
672
|
+
let debounceTimer = null;
|
|
673
|
+
const debounceDelay = 100;
|
|
674
|
+
|
|
675
|
+
for (const dir of watchedDirs) {
|
|
676
|
+
watch(dir, { recursive: false }, (_eventType, filename) => {
|
|
677
|
+
if (!filename || !filename.endsWith('.pulse')) return;
|
|
678
|
+
|
|
679
|
+
// Debounce rapid changes
|
|
680
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
681
|
+
debounceTimer = setTimeout(() => {
|
|
682
|
+
const changedFiles = findPulseFiles(patterns);
|
|
683
|
+
runFormatOnFiles(changedFiles, { check: false, write: true, options, isRerun: true })
|
|
684
|
+
.then(() => {
|
|
685
|
+
log.info('Watching for changes...\n');
|
|
686
|
+
});
|
|
687
|
+
}, debounceDelay);
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// Keep process alive
|
|
692
|
+
process.stdin.resume();
|
|
693
|
+
} else if (check && result.changedCount > 0) {
|
|
694
|
+
process.exit(1);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Run format on a list of files
|
|
700
|
+
*/
|
|
701
|
+
async function runFormatOnFiles(files, { check, write, options, isRerun = false }) {
|
|
702
|
+
const timer = createTimer();
|
|
703
|
+
|
|
704
|
+
if (!isRerun) {
|
|
705
|
+
log.info(`${check ? 'Checking' : 'Formatting'} ${files.length} file(s)...\n`);
|
|
706
|
+
}
|
|
654
707
|
|
|
655
708
|
let changedCount = 0;
|
|
656
709
|
let errorCount = 0;
|
|
@@ -675,12 +728,14 @@ export async function runFormat(args) {
|
|
|
675
728
|
log.info(` ${relPath} - formatted`);
|
|
676
729
|
}
|
|
677
730
|
} else {
|
|
678
|
-
if (!check) {
|
|
731
|
+
if (!check && !isRerun) {
|
|
679
732
|
log.info(` ${relPath} - unchanged`);
|
|
680
733
|
}
|
|
681
734
|
}
|
|
682
735
|
}
|
|
683
736
|
|
|
737
|
+
const elapsed = timer.elapsed();
|
|
738
|
+
|
|
684
739
|
// Summary
|
|
685
740
|
log.info('\n' + '─'.repeat(60));
|
|
686
741
|
|
|
@@ -690,16 +745,17 @@ export async function runFormat(args) {
|
|
|
690
745
|
|
|
691
746
|
if (check) {
|
|
692
747
|
if (changedCount > 0) {
|
|
693
|
-
log.error(`✗ ${changedCount} file(s) need formatting`);
|
|
694
|
-
process.exit(1);
|
|
748
|
+
log.error(`✗ ${changedCount} file(s) need formatting (${formatDuration(elapsed)})`);
|
|
695
749
|
} else {
|
|
696
|
-
log.success(`✓ All ${files.length} file(s) are properly formatted`);
|
|
750
|
+
log.success(`✓ All ${files.length} file(s) are properly formatted (${formatDuration(elapsed)})`);
|
|
697
751
|
}
|
|
698
752
|
} else {
|
|
699
753
|
if (changedCount > 0) {
|
|
700
|
-
log.success(`✓ ${changedCount} file(s) formatted`);
|
|
754
|
+
log.success(`✓ ${changedCount} file(s) formatted (${formatDuration(elapsed)})`);
|
|
701
755
|
} else {
|
|
702
|
-
log.success(`✓ All ${files.length} file(s) were already formatted`);
|
|
756
|
+
log.success(`✓ All ${files.length} file(s) were already formatted (${formatDuration(elapsed)})`);
|
|
703
757
|
}
|
|
704
758
|
}
|
|
759
|
+
|
|
760
|
+
return { changedCount, errorCount };
|
|
705
761
|
}
|
package/cli/lint.js
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
* Validates .pulse files for errors and style issues
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { readFileSync, writeFileSync } from 'fs';
|
|
6
|
+
import { readFileSync, writeFileSync, watch } from 'fs';
|
|
7
|
+
import { dirname } from 'path';
|
|
7
8
|
import { findPulseFiles, parseArgs, relativePath } from './utils/file-utils.js';
|
|
8
9
|
import { log } from './logger.js';
|
|
10
|
+
import { createTimer, formatDuration } from './utils/cli-ui.js';
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Lint rules configuration
|
|
@@ -701,21 +703,14 @@ export async function lintFile(filePath, options = {}) {
|
|
|
701
703
|
}
|
|
702
704
|
|
|
703
705
|
/**
|
|
704
|
-
*
|
|
706
|
+
* Lint files and return summary
|
|
707
|
+
* @param {string[]} files - Files to lint
|
|
708
|
+
* @param {Object} options - Lint options
|
|
709
|
+
* @returns {Object} Summary with totals
|
|
705
710
|
*/
|
|
706
|
-
|
|
707
|
-
const {
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
// Find files to lint
|
|
711
|
-
const files = findPulseFiles(patterns);
|
|
712
|
-
|
|
713
|
-
if (files.length === 0) {
|
|
714
|
-
log.info('No .pulse files found to lint.');
|
|
715
|
-
return;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
log.info(`Linting ${files.length} file(s)...\n`);
|
|
711
|
+
async function lintFiles(files, options = {}) {
|
|
712
|
+
const { fix = false, quiet = false } = options;
|
|
713
|
+
const timer = createTimer();
|
|
719
714
|
|
|
720
715
|
let totalErrors = 0;
|
|
721
716
|
let totalWarnings = 0;
|
|
@@ -725,7 +720,7 @@ export async function runLint(args) {
|
|
|
725
720
|
const result = await lintFile(file, { fix });
|
|
726
721
|
const relPath = relativePath(file);
|
|
727
722
|
|
|
728
|
-
if (result.diagnostics.length > 0) {
|
|
723
|
+
if (result.diagnostics.length > 0 && !quiet) {
|
|
729
724
|
log.info(`\n${relPath}`);
|
|
730
725
|
|
|
731
726
|
for (const diag of result.diagnostics) {
|
|
@@ -740,21 +735,111 @@ export async function runLint(args) {
|
|
|
740
735
|
}
|
|
741
736
|
}
|
|
742
737
|
|
|
743
|
-
|
|
738
|
+
return {
|
|
739
|
+
errors: totalErrors,
|
|
740
|
+
warnings: totalWarnings,
|
|
741
|
+
info: totalInfo,
|
|
742
|
+
elapsed: timer.elapsed()
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Main lint command handler
|
|
748
|
+
*/
|
|
749
|
+
export async function runLint(args) {
|
|
750
|
+
const { options, patterns } = parseArgs(args);
|
|
751
|
+
const fix = options.fix || false;
|
|
752
|
+
const watchMode = options.watch || options.w || false;
|
|
753
|
+
|
|
754
|
+
// Find files to lint
|
|
755
|
+
const files = findPulseFiles(patterns);
|
|
756
|
+
|
|
757
|
+
if (files.length === 0) {
|
|
758
|
+
log.info('No .pulse files found to lint.');
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// Initial lint run
|
|
763
|
+
log.info(`Linting ${files.length} file(s)...\n`);
|
|
764
|
+
const summary = await lintFiles(files, { fix });
|
|
765
|
+
|
|
766
|
+
// Print summary
|
|
767
|
+
printLintSummary(summary, files.length);
|
|
768
|
+
|
|
769
|
+
// Watch mode
|
|
770
|
+
if (watchMode) {
|
|
771
|
+
log.info('\nWatching for changes... (Ctrl+C to stop)\n');
|
|
772
|
+
|
|
773
|
+
const watchedDirs = new Set();
|
|
774
|
+
const debounceTimers = new Map();
|
|
775
|
+
|
|
776
|
+
// Collect directories to watch
|
|
777
|
+
for (const file of files) {
|
|
778
|
+
watchedDirs.add(dirname(file));
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Watch each directory
|
|
782
|
+
for (const dir of watchedDirs) {
|
|
783
|
+
watch(dir, { recursive: false }, (_eventType, filename) => {
|
|
784
|
+
if (!filename || !filename.endsWith('.pulse')) return;
|
|
785
|
+
|
|
786
|
+
const filePath = files.find(f => f.endsWith(filename));
|
|
787
|
+
if (!filePath) return;
|
|
788
|
+
|
|
789
|
+
// Debounce rapid changes
|
|
790
|
+
if (debounceTimers.has(filePath)) {
|
|
791
|
+
clearTimeout(debounceTimers.get(filePath));
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
debounceTimers.set(filePath, setTimeout(() => {
|
|
795
|
+
debounceTimers.delete(filePath);
|
|
796
|
+
|
|
797
|
+
log.info(`\n[${new Date().toLocaleTimeString()}] File changed: ${relativePath(filePath)}`);
|
|
798
|
+
lintFiles([filePath], { fix }).then(result => {
|
|
799
|
+
printLintSummary(result, 1, true);
|
|
800
|
+
});
|
|
801
|
+
}, 100));
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// Keep process running
|
|
806
|
+
return new Promise(() => {});
|
|
807
|
+
} else {
|
|
808
|
+
// Exit with error code if errors found
|
|
809
|
+
if (summary.errors > 0) {
|
|
810
|
+
process.exit(1);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Print lint summary
|
|
817
|
+
*/
|
|
818
|
+
function printLintSummary(summary, fileCount, compact = false) {
|
|
819
|
+
const { errors, warnings, info, elapsed } = summary;
|
|
820
|
+
const timeStr = formatDuration(elapsed);
|
|
821
|
+
|
|
822
|
+
if (compact) {
|
|
823
|
+
const parts = [];
|
|
824
|
+
if (errors > 0) parts.push(`${errors} error(s)`);
|
|
825
|
+
if (warnings > 0) parts.push(`${warnings} warning(s)`);
|
|
826
|
+
if (parts.length === 0) {
|
|
827
|
+
log.success(`✓ Passed (${timeStr})`);
|
|
828
|
+
} else {
|
|
829
|
+
log.error(`✗ ${parts.join(', ')} (${timeStr})`);
|
|
830
|
+
}
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
|
|
744
834
|
log.info('\n' + '─'.repeat(60));
|
|
745
835
|
const parts = [];
|
|
746
|
-
if (
|
|
747
|
-
if (
|
|
748
|
-
if (
|
|
836
|
+
if (errors > 0) parts.push(`${errors} error(s)`);
|
|
837
|
+
if (warnings > 0) parts.push(`${warnings} warning(s)`);
|
|
838
|
+
if (info > 0) parts.push(`${info} info`);
|
|
749
839
|
|
|
750
840
|
if (parts.length === 0) {
|
|
751
|
-
log.success(`✓ ${
|
|
841
|
+
log.success(`✓ ${fileCount} file(s) passed (${timeStr})`);
|
|
752
842
|
} else {
|
|
753
|
-
log.error(`✗ ${parts.join(', ')} in ${
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
// Exit with error code if errors found
|
|
757
|
-
if (totalErrors > 0) {
|
|
758
|
-
process.exit(1);
|
|
843
|
+
log.error(`✗ ${parts.join(', ')} in ${fileCount} file(s) (${timeStr})`);
|
|
759
844
|
}
|
|
760
845
|
}
|