zero-query 0.2.5 → 0.2.8
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 +256 -1401
- package/cli.js +400 -33
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +2 -2
- package/dist/zquery.min.js +2 -2
- package/package.json +2 -3
package/cli.js
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* zQuery CLI
|
|
5
5
|
*
|
|
6
|
-
* Zero-dependency command-line tool for building the zQuery library
|
|
7
|
-
*
|
|
6
|
+
* Zero-dependency command-line tool for building the zQuery library,
|
|
7
|
+
* bundling zQuery-based applications, and running a dev server with
|
|
8
|
+
* live-reload.
|
|
8
9
|
*
|
|
9
10
|
* Usage:
|
|
10
11
|
* zquery build Build the zQuery library (dist/)
|
|
@@ -13,7 +14,9 @@
|
|
|
13
14
|
* zquery bundle scripts/app.js Specify entry explicitly
|
|
14
15
|
* zquery bundle -o build/ Custom output directory
|
|
15
16
|
* zquery bundle --html other.html Use a specific HTML file instead of auto-detected
|
|
16
|
-
*
|
|
17
|
+
*
|
|
18
|
+
* zquery dev [root] Start dev server with live-reload
|
|
19
|
+
* zquery dev --port 8080 Custom port (default: 3100)
|
|
17
20
|
*
|
|
18
21
|
* Smart defaults (no flags needed for typical projects):
|
|
19
22
|
* - Entry is auto-detected from index.html's <script type="module" src="...">
|
|
@@ -24,6 +27,7 @@
|
|
|
24
27
|
* Examples:
|
|
25
28
|
* cd my-app && npx zero-query bundle # just works!
|
|
26
29
|
* npx zero-query bundle path/to/scripts/app.js # works from anywhere
|
|
30
|
+
* cd my-app && npx zquery dev # dev server with live-reload
|
|
27
31
|
*/
|
|
28
32
|
|
|
29
33
|
const fs = require('fs');
|
|
@@ -412,7 +416,6 @@ function bundleApp() {
|
|
|
412
416
|
}
|
|
413
417
|
|
|
414
418
|
const outPath = option('out', 'o', null);
|
|
415
|
-
const watchMode = flag('watch', 'w');
|
|
416
419
|
|
|
417
420
|
// Auto-detect index.html by walking up from the entry file, then check cwd
|
|
418
421
|
let htmlFile = option('html', null, null);
|
|
@@ -595,26 +598,6 @@ function bundleApp() {
|
|
|
595
598
|
}
|
|
596
599
|
|
|
597
600
|
doBuild();
|
|
598
|
-
|
|
599
|
-
// Watch mode
|
|
600
|
-
if (watchMode) {
|
|
601
|
-
const watchDirs = new Set();
|
|
602
|
-
const files = walkImportGraph(entry);
|
|
603
|
-
files.forEach(f => watchDirs.add(path.dirname(f)));
|
|
604
|
-
|
|
605
|
-
console.log(' Watching for changes...\n');
|
|
606
|
-
let debounceTimer;
|
|
607
|
-
for (const dir of watchDirs) {
|
|
608
|
-
fs.watch(dir, { recursive: true }, (_, filename) => {
|
|
609
|
-
if (!filename || !filename.endsWith('.js')) return;
|
|
610
|
-
clearTimeout(debounceTimer);
|
|
611
|
-
debounceTimer = setTimeout(() => {
|
|
612
|
-
console.log(` Changed: ${filename} — rebuilding...`);
|
|
613
|
-
try { doBuild(); } catch (e) { console.error(` ✗ ${e.message}`); }
|
|
614
|
-
}, 200);
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
601
|
}
|
|
619
602
|
|
|
620
603
|
/**
|
|
@@ -745,23 +728,400 @@ function rewriteHtml(projectRoot, htmlRelPath, bundleFile, includeLib, bundledFi
|
|
|
745
728
|
}
|
|
746
729
|
|
|
747
730
|
|
|
731
|
+
// ---------------------------------------------------------------------------
|
|
732
|
+
// "dev" command — development server with live-reload
|
|
733
|
+
// ---------------------------------------------------------------------------
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* SSE live-reload client script injected into served HTML.
|
|
737
|
+
* Connects to /__zq_reload, reloads on 'reload' events,
|
|
738
|
+
* and hot-swaps CSS on 'css' events without a full reload.
|
|
739
|
+
*/
|
|
740
|
+
const LIVE_RELOAD_SNIPPET = `<script>
|
|
741
|
+
(function(){
|
|
742
|
+
var es, timer;
|
|
743
|
+
function connect(){
|
|
744
|
+
es = new EventSource('/__zq_reload');
|
|
745
|
+
es.addEventListener('reload', function(){ location.reload(); });
|
|
746
|
+
es.addEventListener('css', function(e){
|
|
747
|
+
var sheets = document.querySelectorAll('link[rel="stylesheet"]');
|
|
748
|
+
sheets.forEach(function(l){
|
|
749
|
+
var href = l.getAttribute('href');
|
|
750
|
+
if(!href) return;
|
|
751
|
+
var sep = href.indexOf('?') >= 0 ? '&' : '?';
|
|
752
|
+
l.setAttribute('href', href.replace(/[?&]_zqr=\\d+/, '') + sep + '_zqr=' + Date.now());
|
|
753
|
+
});
|
|
754
|
+
});
|
|
755
|
+
es.onerror = function(){
|
|
756
|
+
es.close();
|
|
757
|
+
clearTimeout(timer);
|
|
758
|
+
timer = setTimeout(connect, 2000);
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
connect();
|
|
762
|
+
})();
|
|
763
|
+
</script>`;
|
|
764
|
+
|
|
765
|
+
function devServer() {
|
|
766
|
+
let zeroHttp;
|
|
767
|
+
try {
|
|
768
|
+
zeroHttp = require('zero-http');
|
|
769
|
+
} catch (_) {
|
|
770
|
+
console.error(`\n ✗ zero-http is required for the dev server.`);
|
|
771
|
+
console.error(` Install it: npm install zero-http --save-dev\n`);
|
|
772
|
+
process.exit(1);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
const { createApp, static: serveStatic } = zeroHttp;
|
|
776
|
+
|
|
777
|
+
// Determine the project root to serve
|
|
778
|
+
let root = null;
|
|
779
|
+
for (let i = 1; i < args.length; i++) {
|
|
780
|
+
if (!args[i].startsWith('-') && args[i - 1] !== '-p' && args[i - 1] !== '--port') {
|
|
781
|
+
root = path.resolve(process.cwd(), args[i]);
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (!root) {
|
|
786
|
+
// Auto-detect: look for index.html in cwd or common sub-dirs
|
|
787
|
+
const candidates = [
|
|
788
|
+
process.cwd(),
|
|
789
|
+
path.join(process.cwd(), 'public'),
|
|
790
|
+
path.join(process.cwd(), 'src'),
|
|
791
|
+
];
|
|
792
|
+
for (const c of candidates) {
|
|
793
|
+
if (fs.existsSync(path.join(c, 'index.html'))) { root = c; break; }
|
|
794
|
+
}
|
|
795
|
+
if (!root) root = process.cwd();
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const PORT = parseInt(option('port', 'p', '3100'));
|
|
799
|
+
|
|
800
|
+
// SSE clients for live-reload
|
|
801
|
+
const sseClients = new Set();
|
|
802
|
+
|
|
803
|
+
const app = createApp();
|
|
804
|
+
|
|
805
|
+
// SSE endpoint — clients connect here for reload notifications
|
|
806
|
+
app.get('/__zq_reload', (req, res) => {
|
|
807
|
+
const sse = res.sse({ keepAlive: 30000, keepAliveComment: 'ping' });
|
|
808
|
+
sseClients.add(sse);
|
|
809
|
+
sse.on('close', () => sseClients.delete(sse));
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
// Auto-resolve zquery.min.js — serve the freshest version regardless of
|
|
813
|
+
// what's on disk in the project. Priority:
|
|
814
|
+
// 1. Package dist/ (when running from the repo after `npm run build`)
|
|
815
|
+
// 2. node_modules/zero-query/dist/ (when installed as a dependency)
|
|
816
|
+
// 3. Fall through to static serving (vendor copy on disk)
|
|
817
|
+
// Registered as middleware so it runs BEFORE serveStatic.
|
|
818
|
+
app.use((req, res, next) => {
|
|
819
|
+
const basename = path.basename(req.url.split('?')[0]).toLowerCase();
|
|
820
|
+
if (basename !== 'zquery.min.js') return next();
|
|
821
|
+
|
|
822
|
+
const candidates = [
|
|
823
|
+
path.join(__dirname, 'dist', 'zquery.min.js'), // package repo
|
|
824
|
+
path.join(root, 'node_modules', 'zero-query', 'dist', 'zquery.min.js'), // npm dep
|
|
825
|
+
];
|
|
826
|
+
for (const p of candidates) {
|
|
827
|
+
if (fs.existsSync(p)) {
|
|
828
|
+
res.set('Content-Type', 'application/javascript; charset=utf-8');
|
|
829
|
+
res.set('Cache-Control', 'no-cache');
|
|
830
|
+
res.send(fs.readFileSync(p, 'utf-8'));
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
next(); // fall through to static / 404
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
// Static file serving
|
|
838
|
+
app.use(serveStatic(root, { index: false, dotfiles: 'ignore' }));
|
|
839
|
+
|
|
840
|
+
// SPA fallback — inject live-reload snippet into HTML
|
|
841
|
+
app.get('*', (req, res) => {
|
|
842
|
+
if (path.extname(req.url) && path.extname(req.url) !== '.html') {
|
|
843
|
+
res.status(404).send('Not Found');
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
const indexPath = path.join(root, 'index.html');
|
|
847
|
+
if (!fs.existsSync(indexPath)) {
|
|
848
|
+
res.status(404).send('index.html not found');
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
let html = fs.readFileSync(indexPath, 'utf-8');
|
|
852
|
+
// Inject live-reload snippet before </body> or at end
|
|
853
|
+
if (html.includes('</body>')) {
|
|
854
|
+
html = html.replace('</body>', LIVE_RELOAD_SNIPPET + '\n</body>');
|
|
855
|
+
} else {
|
|
856
|
+
html += LIVE_RELOAD_SNIPPET;
|
|
857
|
+
}
|
|
858
|
+
res.html(html);
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
// Broadcast a reload event to all connected SSE clients
|
|
862
|
+
function broadcast(eventType, data) {
|
|
863
|
+
for (const sse of sseClients) {
|
|
864
|
+
try { sse.event(eventType, data || ''); } catch (_) { sseClients.delete(sse); }
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// File watcher — watch the project root for changes
|
|
869
|
+
const WATCH_EXTS = new Set(['.js', '.css', '.html', '.htm', '.json', '.svg']);
|
|
870
|
+
const IGNORE_DIRS = new Set(['node_modules', '.git', 'dist', '.cache']);
|
|
871
|
+
let debounceTimer;
|
|
872
|
+
|
|
873
|
+
function shouldWatch(filename) {
|
|
874
|
+
if (!filename) return false;
|
|
875
|
+
const ext = path.extname(filename).toLowerCase();
|
|
876
|
+
return WATCH_EXTS.has(ext);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function isIgnored(filepath) {
|
|
880
|
+
const parts = filepath.split(path.sep);
|
|
881
|
+
return parts.some(p => IGNORE_DIRS.has(p));
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// Collect directories to watch (walk root, skip ignored)
|
|
885
|
+
function collectWatchDirs(dir) {
|
|
886
|
+
const dirs = [dir];
|
|
887
|
+
try {
|
|
888
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
889
|
+
for (const entry of entries) {
|
|
890
|
+
if (!entry.isDirectory()) continue;
|
|
891
|
+
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
892
|
+
const sub = path.join(dir, entry.name);
|
|
893
|
+
dirs.push(...collectWatchDirs(sub));
|
|
894
|
+
}
|
|
895
|
+
} catch (_) {}
|
|
896
|
+
return dirs;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
const watchDirs = collectWatchDirs(root);
|
|
900
|
+
const watchers = [];
|
|
901
|
+
|
|
902
|
+
for (const dir of watchDirs) {
|
|
903
|
+
try {
|
|
904
|
+
const watcher = fs.watch(dir, (eventType, filename) => {
|
|
905
|
+
if (!shouldWatch(filename)) return;
|
|
906
|
+
const fullPath = path.join(dir, filename || '');
|
|
907
|
+
if (isIgnored(fullPath)) return;
|
|
908
|
+
|
|
909
|
+
clearTimeout(debounceTimer);
|
|
910
|
+
debounceTimer = setTimeout(() => {
|
|
911
|
+
const rel = path.relative(root, fullPath).replace(/\\/g, '/');
|
|
912
|
+
const ext = path.extname(filename).toLowerCase();
|
|
913
|
+
const now = new Date().toLocaleTimeString();
|
|
914
|
+
|
|
915
|
+
if (ext === '.css') {
|
|
916
|
+
console.log(` ${now} \x1b[35m css \x1b[0m ${rel}`);
|
|
917
|
+
broadcast('css', rel);
|
|
918
|
+
} else {
|
|
919
|
+
console.log(` ${now} \x1b[36m reload \x1b[0m ${rel}`);
|
|
920
|
+
broadcast('reload', rel);
|
|
921
|
+
}
|
|
922
|
+
}, 100);
|
|
923
|
+
});
|
|
924
|
+
watchers.push(watcher);
|
|
925
|
+
} catch (_) {}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
app.listen(PORT, () => {
|
|
929
|
+
console.log(`\n \x1b[1mzQuery Dev Server\x1b[0m`);
|
|
930
|
+
console.log(` \x1b[2m${'─'.repeat(40)}\x1b[0m`);
|
|
931
|
+
console.log(` Local: \x1b[36mhttp://localhost:${PORT}/\x1b[0m`);
|
|
932
|
+
console.log(` Root: ${path.relative(process.cwd(), root) || '.'}`);
|
|
933
|
+
console.log(` Live Reload: \x1b[32menabled\x1b[0m (SSE)`);
|
|
934
|
+
console.log(` Watching: ${WATCH_EXTS.size} file types in ${watchDirs.length} director${watchDirs.length === 1 ? 'y' : 'ies'}`);
|
|
935
|
+
console.log(` \x1b[2m${'─'.repeat(40)}\x1b[0m`);
|
|
936
|
+
console.log(` Press Ctrl+C to stop\n`);
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
// Graceful shutdown
|
|
940
|
+
process.on('SIGINT', () => {
|
|
941
|
+
console.log('\n Shutting down...');
|
|
942
|
+
watchers.forEach(w => w.close());
|
|
943
|
+
for (const sse of sseClients) { try { sse.close(); } catch (_) {} }
|
|
944
|
+
app.close(() => process.exit(0));
|
|
945
|
+
setTimeout(() => process.exit(0), 1000);
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
|
|
950
|
+
// ---------------------------------------------------------------------------
|
|
951
|
+
// Create — scaffold a new zQuery project
|
|
952
|
+
// ---------------------------------------------------------------------------
|
|
953
|
+
|
|
954
|
+
function createProject() {
|
|
955
|
+
const target = args[1] ? path.resolve(args[1]) : process.cwd();
|
|
956
|
+
const name = path.basename(target);
|
|
957
|
+
|
|
958
|
+
// Guard: refuse to overwrite existing files
|
|
959
|
+
const conflicts = ['index.html', 'scripts'].filter(f =>
|
|
960
|
+
fs.existsSync(path.join(target, f))
|
|
961
|
+
);
|
|
962
|
+
if (conflicts.length) {
|
|
963
|
+
console.error(`\n ✗ Directory already contains: ${conflicts.join(', ')}`);
|
|
964
|
+
console.error(` Aborting to avoid overwriting existing files.\n`);
|
|
965
|
+
process.exit(1);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
console.log(`\n zQuery — Create Project\n`);
|
|
969
|
+
console.log(` Scaffolding into ${target}\n`);
|
|
970
|
+
|
|
971
|
+
// ---- templates ----
|
|
972
|
+
|
|
973
|
+
const indexHTML = `<!DOCTYPE html>
|
|
974
|
+
<html lang="en">
|
|
975
|
+
<head>
|
|
976
|
+
<meta charset="UTF-8">
|
|
977
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
978
|
+
<title>${name}</title>
|
|
979
|
+
<link rel="stylesheet" href="styles/styles.css">
|
|
980
|
+
<script src="scripts/vendor/zQuery.min.js"></script>
|
|
981
|
+
<script type="module" src="scripts/app.js"></script>
|
|
982
|
+
</head>
|
|
983
|
+
<body>
|
|
984
|
+
<nav>
|
|
985
|
+
<a z-link="/">Home</a>
|
|
986
|
+
<a z-link="/about">About</a>
|
|
987
|
+
</nav>
|
|
988
|
+
<div id="app"></div>
|
|
989
|
+
</body>
|
|
990
|
+
</html>`;
|
|
991
|
+
|
|
992
|
+
const appJS = `// scripts/app.js — entry point
|
|
993
|
+
import './components/home.js';
|
|
994
|
+
import './components/about.js';
|
|
995
|
+
import { routes } from './routes.js';
|
|
996
|
+
|
|
997
|
+
$.router({ el: '#app', routes, fallback: 'not-found' });
|
|
998
|
+
|
|
999
|
+
$.ready(() => {
|
|
1000
|
+
console.log('zQuery v' + $.version + ' loaded');
|
|
1001
|
+
});`;
|
|
1002
|
+
|
|
1003
|
+
const routesJS = `// scripts/routes.js
|
|
1004
|
+
export const routes = [
|
|
1005
|
+
{ path: '/', component: 'home-page' },
|
|
1006
|
+
{ path: '/about', component: 'about-page' },
|
|
1007
|
+
];`;
|
|
1008
|
+
|
|
1009
|
+
const homeJS = `// scripts/components/home.js
|
|
1010
|
+
$.component('home-page', {
|
|
1011
|
+
state: () => ({ count: 0 }),
|
|
1012
|
+
|
|
1013
|
+
increment() { this.state.count++; },
|
|
1014
|
+
|
|
1015
|
+
render() {
|
|
1016
|
+
return \`
|
|
1017
|
+
<h1>Home</h1>
|
|
1018
|
+
<p>Count: \${this.state.count}</p>
|
|
1019
|
+
<button @click="increment">+1</button>
|
|
1020
|
+
\`;
|
|
1021
|
+
}
|
|
1022
|
+
});`;
|
|
1023
|
+
|
|
1024
|
+
const aboutJS = `// scripts/components/about.js
|
|
1025
|
+
$.component('about-page', {
|
|
1026
|
+
render() {
|
|
1027
|
+
return \`
|
|
1028
|
+
<h1>About</h1>
|
|
1029
|
+
<p>Built with <strong>zQuery</strong> — a lightweight, zero-dependency frontend library.</p>
|
|
1030
|
+
\`;
|
|
1031
|
+
}
|
|
1032
|
+
});`;
|
|
1033
|
+
|
|
1034
|
+
const stylesCSS = `/* styles/styles.css */
|
|
1035
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
1036
|
+
|
|
1037
|
+
body {
|
|
1038
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
1039
|
+
line-height: 1.6;
|
|
1040
|
+
color: #e6edf3;
|
|
1041
|
+
background: #0d1117;
|
|
1042
|
+
padding: 2rem;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
nav {
|
|
1046
|
+
display: flex;
|
|
1047
|
+
gap: 1rem;
|
|
1048
|
+
margin-bottom: 2rem;
|
|
1049
|
+
padding-bottom: 1rem;
|
|
1050
|
+
border-bottom: 1px solid #30363d;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
nav a {
|
|
1054
|
+
color: #58a6ff;
|
|
1055
|
+
text-decoration: none;
|
|
1056
|
+
font-weight: 500;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
nav a:hover { text-decoration: underline; }
|
|
1060
|
+
|
|
1061
|
+
h1 { margin-bottom: 0.5rem; }
|
|
1062
|
+
|
|
1063
|
+
button {
|
|
1064
|
+
margin-top: 0.75rem;
|
|
1065
|
+
padding: 0.5rem 1.25rem;
|
|
1066
|
+
background: #238636;
|
|
1067
|
+
color: #fff;
|
|
1068
|
+
border: none;
|
|
1069
|
+
border-radius: 6px;
|
|
1070
|
+
cursor: pointer;
|
|
1071
|
+
font-size: 0.95rem;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
button:hover { background: #2ea043; }`;
|
|
1075
|
+
|
|
1076
|
+
// ---- write files ----
|
|
1077
|
+
|
|
1078
|
+
const files = {
|
|
1079
|
+
'index.html': indexHTML,
|
|
1080
|
+
'scripts/app.js': appJS,
|
|
1081
|
+
'scripts/routes.js': routesJS,
|
|
1082
|
+
'scripts/components/home.js': homeJS,
|
|
1083
|
+
'scripts/components/about.js': aboutJS,
|
|
1084
|
+
'styles/styles.css': stylesCSS,
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
for (const [rel, content] of Object.entries(files)) {
|
|
1088
|
+
const abs = path.join(target, rel);
|
|
1089
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
1090
|
+
fs.writeFileSync(abs, content, 'utf-8');
|
|
1091
|
+
console.log(` ✓ ${rel}`);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
console.log(`
|
|
1095
|
+
Done! Next steps:
|
|
1096
|
+
|
|
1097
|
+
${target !== process.cwd() ? `cd ${args[1]}\n ` : ''}npx zquery dev
|
|
1098
|
+
`);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
|
|
748
1102
|
// ---------------------------------------------------------------------------
|
|
749
1103
|
// Help
|
|
750
1104
|
// ---------------------------------------------------------------------------
|
|
751
1105
|
|
|
752
1106
|
function showHelp() {
|
|
753
1107
|
console.log(`
|
|
754
|
-
zQuery CLI —
|
|
1108
|
+
zQuery CLI — create, dev, bundle & build
|
|
755
1109
|
|
|
756
1110
|
COMMANDS
|
|
757
1111
|
|
|
758
|
-
|
|
759
|
-
|
|
1112
|
+
create [dir] Scaffold a new zQuery project
|
|
1113
|
+
Creates index.html, scripts/, styles/ in the target directory
|
|
1114
|
+
(defaults to the current directory)
|
|
1115
|
+
|
|
1116
|
+
dev [root] Start a dev server with live-reload
|
|
1117
|
+
--port, -p <number> Port number (default: 3100)
|
|
760
1118
|
|
|
761
1119
|
bundle [entry] Bundle app ES modules into a single file
|
|
762
1120
|
--out, -o <path> Output directory (default: dist/ next to index.html)
|
|
763
1121
|
--html <file> Use a specific HTML file (default: auto-detected)
|
|
764
|
-
|
|
1122
|
+
|
|
1123
|
+
build Build the zQuery library → dist/
|
|
1124
|
+
(must be run from the project root where src/ lives)
|
|
765
1125
|
|
|
766
1126
|
SMART DEFAULTS
|
|
767
1127
|
|
|
@@ -788,11 +1148,17 @@ function showHelp() {
|
|
|
788
1148
|
|
|
789
1149
|
DEVELOPMENT
|
|
790
1150
|
|
|
791
|
-
|
|
792
|
-
|
|
1151
|
+
zquery dev start a dev server with live-reload (port 3100)
|
|
1152
|
+
zquery dev --port 8080 custom port
|
|
793
1153
|
|
|
794
1154
|
EXAMPLES
|
|
795
1155
|
|
|
1156
|
+
# Scaffold a new project and start developing
|
|
1157
|
+
zquery create my-app && cd my-app && zquery dev
|
|
1158
|
+
|
|
1159
|
+
# Start dev server with live-reload
|
|
1160
|
+
cd my-app && zquery dev
|
|
1161
|
+
|
|
796
1162
|
# Build the library only
|
|
797
1163
|
zquery build
|
|
798
1164
|
|
|
@@ -805,9 +1171,6 @@ function showHelp() {
|
|
|
805
1171
|
# Custom output directory
|
|
806
1172
|
zquery bundle -o build/
|
|
807
1173
|
|
|
808
|
-
# Watch mode
|
|
809
|
-
zquery bundle --watch
|
|
810
|
-
|
|
811
1174
|
The bundler walks the ES module import graph starting from the entry
|
|
812
1175
|
file, topologically sorts dependencies, strips import/export syntax,
|
|
813
1176
|
and concatenates everything into a single IIFE with content-hashed
|
|
@@ -822,11 +1185,15 @@ function showHelp() {
|
|
|
822
1185
|
|
|
823
1186
|
if (!command || command === '--help' || command === '-h' || command === 'help') {
|
|
824
1187
|
showHelp();
|
|
1188
|
+
} else if (command === 'create') {
|
|
1189
|
+
createProject();
|
|
825
1190
|
} else if (command === 'build') {
|
|
826
1191
|
console.log('\n zQuery Library Build\n');
|
|
827
1192
|
buildLibrary();
|
|
828
1193
|
} else if (command === 'bundle') {
|
|
829
1194
|
bundleApp();
|
|
1195
|
+
} else if (command === 'dev') {
|
|
1196
|
+
devServer();
|
|
830
1197
|
} else {
|
|
831
1198
|
console.error(`\n Unknown command: ${command}\n Run "zquery --help" for usage.\n`);
|
|
832
1199
|
process.exit(1);
|
package/dist/zquery.dist.zip
CHANGED
|
Binary file
|
package/dist/zquery.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* zQuery (zeroQuery) v0.2.
|
|
2
|
+
* zQuery (zeroQuery) v0.2.8
|
|
3
3
|
* Lightweight Frontend Library
|
|
4
4
|
* https://github.com/tonywied17/zero-query
|
|
5
5
|
* (c) 2026 Anthony Wiedman — MIT License
|
|
@@ -2576,7 +2576,7 @@ $.session = session;
|
|
|
2576
2576
|
$.bus = bus;
|
|
2577
2577
|
|
|
2578
2578
|
// --- Meta ------------------------------------------------------------------
|
|
2579
|
-
$.version = '0.2.
|
|
2579
|
+
$.version = '0.2.8';
|
|
2580
2580
|
$.meta = {}; // populated at build time by CLI bundler
|
|
2581
2581
|
|
|
2582
2582
|
$.noConflict = () => {
|
package/dist/zquery.min.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* zQuery (zeroQuery) v0.2.
|
|
2
|
+
* zQuery (zeroQuery) v0.2.8
|
|
3
3
|
* Lightweight Frontend Library
|
|
4
4
|
* https://github.com/tonywied17/zero-query
|
|
5
5
|
* (c) 2026 Anthony Wiedman — MIT License
|
|
@@ -13,5 +13,5 @@ class Router { constructor(config = {}) { this._el = null; const isFile = typeof
|
|
|
13
13
|
class Store { constructor(config = {}) { this._subscribers = new Map(); this._wildcards = new Set(); this._actions = config.actions || {}; this._getters = config.getters || {}; this._middleware = []; this._history = []; this._debug = config.debug || false; const initial = typeof config.state === 'function' ? config.state() : { ...(config.state || {}) }; this.state = reactive(initial, (key, value, old) => { const subs = this._subscribers.get(key); if (subs) subs.forEach(fn => fn(value, old, key)); this._wildcards.forEach(fn => fn(key, value, old)); }); this.getters = {}; for (const [name, fn] of Object.entries(this._getters)) { Object.defineProperty(this.getters, name, { get: () => fn(this.state.__raw || this.state), enumerable: true }); } } dispatch(name, ...args) { const action = this._actions[name]; if (!action) { console.warn(`zQuery Store: Unknown action "${name}"`); return; } for (const mw of this._middleware) { const result = mw(name, args, this.state); if (result === false) return; } if (this._debug) { console.log(`%c[Store] ${name}`, 'color: #4CAF50; font-weight: bold;', ...args); } const result = action(this.state, ...args); this._history.push({ action: name, args, timestamp: Date.now() }); return result; } subscribe(keyOrFn, fn) { if (typeof keyOrFn === 'function') { this._wildcards.add(keyOrFn); return () => this._wildcards.delete(keyOrFn); } if (!this._subscribers.has(keyOrFn)) { this._subscribers.set(keyOrFn, new Set()); } this._subscribers.get(keyOrFn).add(fn); return () => this._subscribers.get(keyOrFn)?.delete(fn); } snapshot() { return JSON.parse(JSON.stringify(this.state.__raw || this.state)); } replaceState(newState) { const raw = this.state.__raw || this.state; for (const key of Object.keys(raw)) { delete this.state[key]; } Object.assign(this.state, newState); } use(fn) { this._middleware.push(fn); return this; } get history() { return [...this._history]; } reset(initialState) { this.replaceState(initialState); this._history = []; }
|
|
14
14
|
const _config = { baseURL: '', headers: { 'Content-Type': 'application/json' }, timeout: 30000,
|
|
15
15
|
function debounce(fn, ms = 250) { let timer; const debounced = (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), ms); }; debounced.cancel = () => clearTimeout(timer); return debounced;
|
|
16
|
-
function $(selector, context) { if (typeof selector === 'function') { query.ready(selector); return; } return query(selector, context);
|
|
16
|
+
function $(selector, context) { if (typeof selector === 'function') { query.ready(selector); return; } return query(selector, context);
|
|
17
17
|
})(typeof window !== 'undefined' ? window : globalThis);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zero-query",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Lightweight modern frontend library — jQuery-like selectors, reactive components, SPA router, and state management with zero dependencies.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -18,8 +18,7 @@
|
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "node build.js",
|
|
21
|
-
"dev": "
|
|
22
|
-
"serve": "node examples/starter-app/local-server.js",
|
|
21
|
+
"dev": "node cli.js dev examples/starter-app",
|
|
23
22
|
"dev-lib": "node build.js --watch",
|
|
24
23
|
"bundle": "node cli.js bundle",
|
|
25
24
|
"bundle:app": "node cli.js bundle examples/starter-app/scripts/app.js"
|