pulse-js-framework 1.7.4 → 1.7.6
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 +78 -392
- package/cli/analyze.js +127 -46
- package/cli/build.js +51 -13
- package/cli/dev.js +14 -0
- package/cli/docs-test.js +633 -0
- package/cli/format.js +64 -8
- package/cli/index.js +313 -31
- package/cli/lint.js +121 -27
- package/cli/logger.js +32 -4
- package/cli/release.js +50 -20
- package/cli/utils/cli-ui.js +452 -0
- package/compiler/parser.js +19 -2
- package/core/errors.js +2 -297
- package/package.json +16 -4
- package/runtime/async.js +282 -14
- package/runtime/dom-adapter.js +920 -0
- package/runtime/dom-advanced.js +357 -0
- package/runtime/dom-binding.js +230 -0
- package/runtime/dom-conditional.js +133 -0
- package/runtime/dom-element.js +142 -0
- package/runtime/dom-lifecycle.js +178 -0
- package/runtime/dom-list.js +267 -0
- package/runtime/dom-selector.js +267 -0
- package/runtime/dom.js +131 -1122
- package/runtime/errors.js +575 -0
- package/runtime/form.js +417 -22
- package/runtime/logger.js +144 -69
- package/runtime/logger.prod.js +43 -18
- package/runtime/native.js +398 -52
- package/runtime/pulse.js +202 -80
- package/runtime/router.js +31 -42
- package/runtime/store.js +90 -12
- package/runtime/utils.js +279 -18
- package/types/async.d.ts +310 -0
- package/types/form.d.ts +378 -0
- package/types/index.d.ts +44 -0
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,17 @@ 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
|
+
* @param {boolean} options.fix - Auto-fix issues
|
|
710
|
+
* @param {boolean} options.dryRun - Show fixes without applying
|
|
711
|
+
* @param {boolean} options.quiet - Suppress output
|
|
712
|
+
* @returns {Object} Summary with totals
|
|
705
713
|
*/
|
|
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`);
|
|
714
|
+
async function lintFiles(files, options = {}) {
|
|
715
|
+
const { fix = false, dryRun = false, quiet = false } = options;
|
|
716
|
+
const timer = createTimer();
|
|
719
717
|
|
|
720
718
|
let totalErrors = 0;
|
|
721
719
|
let totalWarnings = 0;
|
|
@@ -725,7 +723,7 @@ export async function runLint(args) {
|
|
|
725
723
|
const result = await lintFile(file, { fix });
|
|
726
724
|
const relPath = relativePath(file);
|
|
727
725
|
|
|
728
|
-
if (result.diagnostics.length > 0) {
|
|
726
|
+
if (result.diagnostics.length > 0 && !quiet) {
|
|
729
727
|
log.info(`\n${relPath}`);
|
|
730
728
|
|
|
731
729
|
for (const diag of result.diagnostics) {
|
|
@@ -740,21 +738,117 @@ export async function runLint(args) {
|
|
|
740
738
|
}
|
|
741
739
|
}
|
|
742
740
|
|
|
743
|
-
|
|
741
|
+
return {
|
|
742
|
+
errors: totalErrors,
|
|
743
|
+
warnings: totalWarnings,
|
|
744
|
+
info: totalInfo,
|
|
745
|
+
elapsed: timer.elapsed()
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Main lint command handler
|
|
751
|
+
*/
|
|
752
|
+
export async function runLint(args) {
|
|
753
|
+
const { options, patterns } = parseArgs(args);
|
|
754
|
+
const fix = options.fix || false;
|
|
755
|
+
const dryRun = options['dry-run'] || false;
|
|
756
|
+
const watchMode = options.watch || options.w || false;
|
|
757
|
+
|
|
758
|
+
// Dry-run only makes sense with --fix
|
|
759
|
+
if (dryRun && !fix) {
|
|
760
|
+
log.warn('Note: --dry-run has no effect without --fix');
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// Find files to lint
|
|
764
|
+
const files = findPulseFiles(patterns);
|
|
765
|
+
|
|
766
|
+
if (files.length === 0) {
|
|
767
|
+
log.info('No .pulse files found to lint.');
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// Initial lint run
|
|
772
|
+
log.info(`Linting ${files.length} file(s)...${dryRun ? ' (dry-run)' : ''}\n`);
|
|
773
|
+
const summary = await lintFiles(files, { fix, dryRun });
|
|
774
|
+
|
|
775
|
+
// Print summary
|
|
776
|
+
printLintSummary(summary, files.length);
|
|
777
|
+
|
|
778
|
+
// Watch mode
|
|
779
|
+
if (watchMode) {
|
|
780
|
+
log.info('\nWatching for changes... (Ctrl+C to stop)\n');
|
|
781
|
+
|
|
782
|
+
const watchedDirs = new Set();
|
|
783
|
+
const debounceTimers = new Map();
|
|
784
|
+
|
|
785
|
+
// Collect directories to watch
|
|
786
|
+
for (const file of files) {
|
|
787
|
+
watchedDirs.add(dirname(file));
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// Watch each directory
|
|
791
|
+
for (const dir of watchedDirs) {
|
|
792
|
+
watch(dir, { recursive: false }, (_eventType, filename) => {
|
|
793
|
+
if (!filename || !filename.endsWith('.pulse')) return;
|
|
794
|
+
|
|
795
|
+
const filePath = files.find(f => f.endsWith(filename));
|
|
796
|
+
if (!filePath) return;
|
|
797
|
+
|
|
798
|
+
// Debounce rapid changes
|
|
799
|
+
if (debounceTimers.has(filePath)) {
|
|
800
|
+
clearTimeout(debounceTimers.get(filePath));
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
debounceTimers.set(filePath, setTimeout(() => {
|
|
804
|
+
debounceTimers.delete(filePath);
|
|
805
|
+
|
|
806
|
+
log.info(`\n[${new Date().toLocaleTimeString()}] File changed: ${relativePath(filePath)}`);
|
|
807
|
+
lintFiles([filePath], { fix, dryRun }).then(result => {
|
|
808
|
+
printLintSummary(result, 1, true);
|
|
809
|
+
});
|
|
810
|
+
}, 100));
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// Keep process running
|
|
815
|
+
return new Promise(() => {});
|
|
816
|
+
} else {
|
|
817
|
+
// Exit with error code if errors found
|
|
818
|
+
if (summary.errors > 0) {
|
|
819
|
+
process.exit(1);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Print lint summary
|
|
826
|
+
*/
|
|
827
|
+
function printLintSummary(summary, fileCount, compact = false) {
|
|
828
|
+
const { errors, warnings, info, elapsed } = summary;
|
|
829
|
+
const timeStr = formatDuration(elapsed);
|
|
830
|
+
|
|
831
|
+
if (compact) {
|
|
832
|
+
const parts = [];
|
|
833
|
+
if (errors > 0) parts.push(`${errors} error(s)`);
|
|
834
|
+
if (warnings > 0) parts.push(`${warnings} warning(s)`);
|
|
835
|
+
if (parts.length === 0) {
|
|
836
|
+
log.success(`✓ Passed (${timeStr})`);
|
|
837
|
+
} else {
|
|
838
|
+
log.error(`✗ ${parts.join(', ')} (${timeStr})`);
|
|
839
|
+
}
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
|
|
744
843
|
log.info('\n' + '─'.repeat(60));
|
|
745
844
|
const parts = [];
|
|
746
|
-
if (
|
|
747
|
-
if (
|
|
748
|
-
if (
|
|
845
|
+
if (errors > 0) parts.push(`${errors} error(s)`);
|
|
846
|
+
if (warnings > 0) parts.push(`${warnings} warning(s)`);
|
|
847
|
+
if (info > 0) parts.push(`${info} info`);
|
|
749
848
|
|
|
750
849
|
if (parts.length === 0) {
|
|
751
|
-
log.success(`✓ ${
|
|
850
|
+
log.success(`✓ ${fileCount} file(s) passed (${timeStr})`);
|
|
752
851
|
} 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);
|
|
852
|
+
log.error(`✗ ${parts.join(', ')} in ${fileCount} file(s) (${timeStr})`);
|
|
759
853
|
}
|
|
760
854
|
}
|
package/cli/logger.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pulse CLI Logger
|
|
3
|
-
*
|
|
3
|
+
* Adapter for CLI tools using the unified runtime logger
|
|
4
4
|
* @module pulse-cli/logger
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { createLogger, setLogLevel, LogLevel } from '../runtime/logger.js';
|
|
8
|
+
|
|
9
|
+
// Create CLI-namespaced logger
|
|
10
|
+
const cliLogger = createLogger('CLI');
|
|
11
|
+
|
|
7
12
|
/** @type {boolean} */
|
|
8
13
|
let verboseMode = false;
|
|
9
14
|
|
|
@@ -17,6 +22,12 @@ let verboseMode = false;
|
|
|
17
22
|
*/
|
|
18
23
|
export function setVerbose(enabled) {
|
|
19
24
|
verboseMode = enabled;
|
|
25
|
+
// Update global log level to include debug when verbose
|
|
26
|
+
if (enabled) {
|
|
27
|
+
setLogLevel(LogLevel.DEBUG);
|
|
28
|
+
} else {
|
|
29
|
+
setLogLevel(LogLevel.INFO);
|
|
30
|
+
}
|
|
20
31
|
}
|
|
21
32
|
|
|
22
33
|
/**
|
|
@@ -33,6 +44,7 @@ export function isVerbose() {
|
|
|
33
44
|
|
|
34
45
|
/**
|
|
35
46
|
* CLI Logger object with console-like API
|
|
47
|
+
* Uses the runtime logger under the hood for consistency
|
|
36
48
|
* @namespace log
|
|
37
49
|
*/
|
|
38
50
|
export const log = {
|
|
@@ -44,6 +56,7 @@ export const log = {
|
|
|
44
56
|
* log.info('Starting server on port', 3000);
|
|
45
57
|
*/
|
|
46
58
|
info(...args) {
|
|
59
|
+
// Use console directly for CLI output (no namespace prefix for info)
|
|
47
60
|
console.log(...args);
|
|
48
61
|
},
|
|
49
62
|
|
|
@@ -66,7 +79,7 @@ export const log = {
|
|
|
66
79
|
* log.warn('Deprecated feature used');
|
|
67
80
|
*/
|
|
68
81
|
warn(...args) {
|
|
69
|
-
|
|
82
|
+
cliLogger.warn(...args);
|
|
70
83
|
},
|
|
71
84
|
|
|
72
85
|
/**
|
|
@@ -77,7 +90,7 @@ export const log = {
|
|
|
77
90
|
* log.error('Failed to compile:', error.message);
|
|
78
91
|
*/
|
|
79
92
|
error(...args) {
|
|
80
|
-
|
|
93
|
+
cliLogger.error(...args);
|
|
81
94
|
},
|
|
82
95
|
|
|
83
96
|
/**
|
|
@@ -89,7 +102,7 @@ export const log = {
|
|
|
89
102
|
*/
|
|
90
103
|
debug(...args) {
|
|
91
104
|
if (verboseMode) {
|
|
92
|
-
|
|
105
|
+
cliLogger.debug(...args);
|
|
93
106
|
}
|
|
94
107
|
},
|
|
95
108
|
|
|
@@ -116,7 +129,22 @@ export const log = {
|
|
|
116
129
|
*/
|
|
117
130
|
newline() {
|
|
118
131
|
console.log();
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Create a child logger with a sub-namespace
|
|
136
|
+
* @param {string} namespace - Child namespace
|
|
137
|
+
* @returns {import('../runtime/logger.js').Logger} Child logger instance
|
|
138
|
+
* @example
|
|
139
|
+
* const buildLog = log.child('Build');
|
|
140
|
+
* buildLog.info('Starting...'); // [CLI:Build] Starting...
|
|
141
|
+
*/
|
|
142
|
+
child(namespace) {
|
|
143
|
+
return cliLogger.child(namespace);
|
|
119
144
|
}
|
|
120
145
|
};
|
|
121
146
|
|
|
147
|
+
// Re-export LogLevel for convenience
|
|
148
|
+
export { LogLevel };
|
|
149
|
+
|
|
122
150
|
export default log;
|
package/cli/release.js
CHANGED
|
@@ -10,13 +10,15 @@
|
|
|
10
10
|
* - Push to remote
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
13
|
+
import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';
|
|
14
14
|
import { join, dirname } from 'path';
|
|
15
15
|
import { fileURLToPath } from 'url';
|
|
16
16
|
import { execSync } from 'child_process';
|
|
17
|
+
import { tmpdir } from 'os';
|
|
17
18
|
import { createInterface } from 'readline';
|
|
18
19
|
import https from 'https';
|
|
19
20
|
import { log } from './logger.js';
|
|
21
|
+
import { runDocsTest } from './docs-test.js';
|
|
20
22
|
|
|
21
23
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
24
|
const root = join(__dirname, '..');
|
|
@@ -525,14 +527,15 @@ function gitCommitTagPush(newVersion, title, changes, dryRun = false) {
|
|
|
525
527
|
log.info(' Running: git add -A...');
|
|
526
528
|
execSync('git add -A', { cwd: root, stdio: 'inherit' });
|
|
527
529
|
|
|
528
|
-
// git commit
|
|
530
|
+
// git commit using temp file for cross-platform compatibility
|
|
529
531
|
log.info(' Running: git commit...');
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
stdio: 'inherit'
|
|
534
|
-
|
|
535
|
-
|
|
532
|
+
const tempFile = join(tmpdir(), `pulse-release-${Date.now()}.txt`);
|
|
533
|
+
writeFileSync(tempFile, commitMessage, 'utf-8');
|
|
534
|
+
try {
|
|
535
|
+
execSync(`git commit -F "${tempFile}"`, { cwd: root, stdio: 'inherit' });
|
|
536
|
+
} finally {
|
|
537
|
+
unlinkSync(tempFile);
|
|
538
|
+
}
|
|
536
539
|
|
|
537
540
|
// git tag
|
|
538
541
|
log.info(` Running: git tag v${newVersion}...`);
|
|
@@ -559,12 +562,13 @@ Types:
|
|
|
559
562
|
major Bump major version (1.0.0 -> 2.0.0)
|
|
560
563
|
|
|
561
564
|
Options:
|
|
562
|
-
--dry-run
|
|
563
|
-
--no-push
|
|
564
|
-
--title <text>
|
|
565
|
-
--skip-prompt
|
|
566
|
-
--
|
|
567
|
-
--
|
|
565
|
+
--dry-run Show what would be done without making changes
|
|
566
|
+
--no-push Create commit and tag but don't push
|
|
567
|
+
--title <text> Release title (e.g., "Performance Improvements")
|
|
568
|
+
--skip-prompt Use empty changelog (for automated releases)
|
|
569
|
+
--skip-docs-test Skip documentation validation before release
|
|
570
|
+
--from-commits Auto-extract changelog from git commits since last tag
|
|
571
|
+
--yes, -y Auto-confirm all prompts
|
|
568
572
|
--changes <json> Pass changelog as JSON (e.g., '{"added":["Feature 1"],"fixed":["Bug 1"]}')
|
|
569
573
|
--added <items> Comma-separated list of added features
|
|
570
574
|
--changed <items> Comma-separated list of changes
|
|
@@ -599,6 +603,7 @@ export async function runRelease(args) {
|
|
|
599
603
|
const skipPrompt = args.includes('--skip-prompt');
|
|
600
604
|
const fromCommits = args.includes('--from-commits');
|
|
601
605
|
const autoConfirm = args.includes('--yes') || args.includes('-y');
|
|
606
|
+
const skipDocsTest = args.includes('--skip-docs-test');
|
|
602
607
|
|
|
603
608
|
let title = '';
|
|
604
609
|
const titleIndex = args.indexOf('--title');
|
|
@@ -675,6 +680,30 @@ export async function runRelease(args) {
|
|
|
675
680
|
process.exit(1);
|
|
676
681
|
}
|
|
677
682
|
|
|
683
|
+
// Run documentation tests
|
|
684
|
+
if (!skipDocsTest) {
|
|
685
|
+
log.info('');
|
|
686
|
+
log.info('Running documentation tests...');
|
|
687
|
+
|
|
688
|
+
const docsTestResult = await runDocsTest({ verbose: false, httpTest: true });
|
|
689
|
+
|
|
690
|
+
if (!docsTestResult.success) {
|
|
691
|
+
log.error('Documentation tests failed. Fix errors before releasing.');
|
|
692
|
+
if (!autoConfirm) {
|
|
693
|
+
const proceed = await prompt('Continue anyway? (y/N) ');
|
|
694
|
+
if (proceed.toLowerCase() !== 'y') {
|
|
695
|
+
log.info('Aborted.');
|
|
696
|
+
process.exit(1);
|
|
697
|
+
}
|
|
698
|
+
} else {
|
|
699
|
+
log.error('Aborting release due to documentation errors.');
|
|
700
|
+
process.exit(1);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
} else {
|
|
704
|
+
log.warn('Skipping documentation tests (--skip-docs-test)');
|
|
705
|
+
}
|
|
706
|
+
|
|
678
707
|
// Collect changelog entries
|
|
679
708
|
let changes = { added: [], changed: [], fixed: [], removed: [] };
|
|
680
709
|
|
|
@@ -786,12 +815,13 @@ export async function runRelease(args) {
|
|
|
786
815
|
// Only commit and tag, no push
|
|
787
816
|
const commitMessage = buildCommitMessage(newVersion, title, changes);
|
|
788
817
|
execSync('git add -A', { cwd: root, stdio: 'inherit' });
|
|
789
|
-
const
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
stdio: 'inherit'
|
|
793
|
-
|
|
794
|
-
|
|
818
|
+
const tempFile = join(tmpdir(), `pulse-release-${Date.now()}.txt`);
|
|
819
|
+
writeFileSync(tempFile, commitMessage, 'utf-8');
|
|
820
|
+
try {
|
|
821
|
+
execSync(`git commit -F "${tempFile}"`, { cwd: root, stdio: 'inherit' });
|
|
822
|
+
} finally {
|
|
823
|
+
unlinkSync(tempFile);
|
|
824
|
+
}
|
|
795
825
|
execSync(`git tag -a v${newVersion} -m "Release v${newVersion}"`, { cwd: root, stdio: 'inherit' });
|
|
796
826
|
log.info(' Created commit and tag (--no-push specified)');
|
|
797
827
|
} else {
|