zero-query 0.4.9 → 0.5.2
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 +8 -4
- package/cli/commands/bundle.js +113 -10
- package/cli/commands/dev.js +8 -4
- package/cli/help.js +18 -6
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +19 -3
- package/dist/zquery.min.js +3 -3
- package/index.d.ts +8 -1
- package/index.js +9 -3
- package/package.json +2 -2
- package/src/core.js +10 -0
package/README.md
CHANGED
|
@@ -162,7 +162,7 @@ my-app/
|
|
|
162
162
|
|
|
163
163
|
## CLI Bundler
|
|
164
164
|
|
|
165
|
-
The CLI
|
|
165
|
+
The CLI compiles your entire app — ES modules, the library, external templates, and assets — into a **single production-ready bundle**. It outputs two builds in one step: a `server/` build for deploying to any web server, and a `local/` build that works straight from disk. No config, no flags — just point it at your app.
|
|
166
166
|
|
|
167
167
|
```bash
|
|
168
168
|
# Auto-detect entry from any .html with a module script
|
|
@@ -170,6 +170,9 @@ npx zquery bundle
|
|
|
170
170
|
|
|
171
171
|
# Or point to an app directory from anywhere
|
|
172
172
|
npx zquery bundle my-app/
|
|
173
|
+
|
|
174
|
+
# Or pass a direct entry file (skips auto-detection)
|
|
175
|
+
npx zquery bundle my-app/scripts/main.js
|
|
173
176
|
```
|
|
174
177
|
|
|
175
178
|
Output goes to `dist/` next to your `index.html`:
|
|
@@ -192,7 +195,8 @@ dist/
|
|
|
192
195
|
| Flag | Short | Description |
|
|
193
196
|
| --- | --- | --- |
|
|
194
197
|
| `--out <path>` | `-o` | Custom output directory |
|
|
195
|
-
| `--
|
|
198
|
+
| `--index <file>` | `-i` | Index HTML file (default: auto-detected) |
|
|
199
|
+
| `--minimal` | `-m` | Only output HTML + bundled JS (skip static assets) |
|
|
196
200
|
|
|
197
201
|
### What the Bundler Does
|
|
198
202
|
|
|
@@ -261,8 +265,8 @@ location / {
|
|
|
261
265
|
| CLI Command | Description |
|
|
262
266
|
| --- | --- |
|
|
263
267
|
| `zquery create [dir]` | Scaffold a new project (index.html, components, store, styles) |
|
|
264
|
-
| `zquery dev [root]` | Dev server with live-reload & error overlay (port 3100) |
|
|
265
|
-
| `zquery bundle [dir]` | Bundle app into a single IIFE file |
|
|
268
|
+
| `zquery dev [root]` | Dev server with live-reload & error overlay (port 3100). `--index` for custom HTML. |
|
|
269
|
+
| `zquery bundle [dir\|file]` | Bundle app into a single IIFE file. Accepts dir or direct entry file. |
|
|
266
270
|
| `zquery build` | Build the zQuery library (`dist/zQuery.min.js`) |
|
|
267
271
|
| `zquery --help` | Show CLI usage |
|
|
268
272
|
|
package/cli/commands/bundle.js
CHANGED
|
@@ -13,7 +13,7 @@ const fs = require('fs');
|
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const crypto = require('crypto');
|
|
15
15
|
|
|
16
|
-
const { args, option }
|
|
16
|
+
const { args, flag, option } = require('../args');
|
|
17
17
|
const { minify, sizeKB } = require('../utils');
|
|
18
18
|
const buildLibrary = require('./build');
|
|
19
19
|
|
|
@@ -104,6 +104,8 @@ function rewriteResourceUrls(code, filePath, projectRoot) {
|
|
|
104
104
|
(match, prefix, quote, url) => {
|
|
105
105
|
if (url.startsWith('/') || url.includes('://')) return match;
|
|
106
106
|
const abs = path.resolve(fileDir, url);
|
|
107
|
+
// Only rewrite if the file actually exists — avoids mangling code examples
|
|
108
|
+
if (!fs.existsSync(abs)) return match;
|
|
107
109
|
const rel = path.relative(projectRoot, abs).replace(/\\/g, '/');
|
|
108
110
|
return `${prefix}${quote}${rel}${quote}`;
|
|
109
111
|
}
|
|
@@ -379,18 +381,68 @@ function rewriteHtml(projectRoot, htmlRelPath, bundleFile, includeLib, bundledFi
|
|
|
379
381
|
console.log(` ✓ Copied ${copiedCount} asset(s) into both dist dirs`);
|
|
380
382
|
}
|
|
381
383
|
|
|
384
|
+
// ---------------------------------------------------------------------------
|
|
385
|
+
// Static asset copying
|
|
386
|
+
// ---------------------------------------------------------------------------
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Copy the entire app directory into both dist/server and dist/local,
|
|
390
|
+
* skipping only build outputs and tooling dirs. This ensures all static
|
|
391
|
+
* assets (icons/, images/, fonts/, styles/, scripts/, manifests, etc.)
|
|
392
|
+
* are available in the built output without maintaining a fragile whitelist.
|
|
393
|
+
*/
|
|
394
|
+
function copyStaticAssets(appRoot, serverDir, localDir, bundledFiles) {
|
|
395
|
+
const SKIP_DIRS = new Set(['dist', 'node_modules', '.git', '.vscode', 'scripts']);
|
|
396
|
+
|
|
397
|
+
let copiedCount = 0;
|
|
398
|
+
|
|
399
|
+
function copyEntry(srcPath, relPath) {
|
|
400
|
+
const stat = fs.statSync(srcPath);
|
|
401
|
+
if (stat.isDirectory()) {
|
|
402
|
+
if (SKIP_DIRS.has(path.basename(srcPath))) return;
|
|
403
|
+
for (const child of fs.readdirSync(srcPath)) {
|
|
404
|
+
copyEntry(path.join(srcPath, child), path.join(relPath, child));
|
|
405
|
+
}
|
|
406
|
+
} else {
|
|
407
|
+
if (bundledFiles && bundledFiles.has(path.resolve(srcPath))) return;
|
|
408
|
+
for (const distDir of [serverDir, localDir]) {
|
|
409
|
+
const dest = path.join(distDir, relPath);
|
|
410
|
+
if (fs.existsSync(dest)) continue; // already copied by rewriteHtml
|
|
411
|
+
const dir = path.dirname(dest);
|
|
412
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
413
|
+
fs.copyFileSync(srcPath, dest);
|
|
414
|
+
}
|
|
415
|
+
copiedCount++;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
for (const entry of fs.readdirSync(appRoot, { withFileTypes: true })) {
|
|
420
|
+
if (entry.isDirectory()) {
|
|
421
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
422
|
+
copyEntry(path.join(appRoot, entry.name), entry.name);
|
|
423
|
+
} else {
|
|
424
|
+
copyEntry(path.join(appRoot, entry.name), entry.name);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (copiedCount > 0) {
|
|
429
|
+
console.log(` \u2713 Copied ${copiedCount} additional static asset(s) into both dist dirs`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
382
433
|
// ---------------------------------------------------------------------------
|
|
383
434
|
// Main bundleApp function
|
|
384
435
|
// ---------------------------------------------------------------------------
|
|
385
436
|
|
|
386
437
|
function bundleApp() {
|
|
387
438
|
const projectRoot = process.cwd();
|
|
439
|
+
const minimal = flag('minimal', 'm');
|
|
388
440
|
|
|
389
|
-
// Entry point —
|
|
441
|
+
// Entry point — positional arg (directory or file) or auto-detection
|
|
390
442
|
let entry = null;
|
|
391
443
|
let targetDir = null;
|
|
392
444
|
for (let i = 1; i < args.length; i++) {
|
|
393
|
-
if (!args[i].startsWith('-') && args[i - 1] !== '-o' && args[i - 1] !== '--out' && args[i - 1] !== '--
|
|
445
|
+
if (!args[i].startsWith('-') && args[i - 1] !== '-o' && args[i - 1] !== '--out' && args[i - 1] !== '--index') {
|
|
394
446
|
const resolved = path.resolve(projectRoot, args[i]);
|
|
395
447
|
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
|
|
396
448
|
targetDir = resolved;
|
|
@@ -405,16 +457,19 @@ function bundleApp() {
|
|
|
405
457
|
|
|
406
458
|
if (!entry || !fs.existsSync(entry)) {
|
|
407
459
|
console.error(`\n \u2717 Could not find entry file.`);
|
|
408
|
-
console.error(` Provide an app directory: zquery bundle my-app
|
|
460
|
+
console.error(` Provide an app directory: zquery bundle my-app/`);
|
|
461
|
+
console.error(` Or pass a direct entry file: zquery bundle my-app/scripts/main.js\n`);
|
|
409
462
|
process.exit(1);
|
|
410
463
|
}
|
|
411
464
|
|
|
412
465
|
const outPath = option('out', 'o', null);
|
|
413
466
|
|
|
414
|
-
// Auto-detect
|
|
415
|
-
let htmlFile = option('
|
|
467
|
+
// Auto-detect HTML file
|
|
468
|
+
let htmlFile = option('index', 'i', null);
|
|
416
469
|
let htmlAbs = htmlFile ? path.resolve(projectRoot, htmlFile) : null;
|
|
417
470
|
if (!htmlFile) {
|
|
471
|
+
// Strategy: first look for index.html walking up from entry, then
|
|
472
|
+
// scan for any .html that references the entry via a module script tag.
|
|
418
473
|
const htmlCandidates = [];
|
|
419
474
|
let entryDir = path.dirname(entry);
|
|
420
475
|
while (entryDir.length >= projectRoot.length) {
|
|
@@ -432,6 +487,47 @@ function bundleApp() {
|
|
|
432
487
|
break;
|
|
433
488
|
}
|
|
434
489
|
}
|
|
490
|
+
|
|
491
|
+
// If no index.html found, scan for any .html file that references
|
|
492
|
+
// the entry point (supports home.html, app.html, etc.)
|
|
493
|
+
if (!htmlAbs) {
|
|
494
|
+
const searchRoot = targetDir || projectRoot;
|
|
495
|
+
const htmlScan = [];
|
|
496
|
+
for (const e of fs.readdirSync(searchRoot, { withFileTypes: true })) {
|
|
497
|
+
if (e.isFile() && e.name.endsWith('.html')) {
|
|
498
|
+
htmlScan.push(path.join(searchRoot, e.name));
|
|
499
|
+
} else if (e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules' && e.name !== 'dist') {
|
|
500
|
+
try {
|
|
501
|
+
for (const child of fs.readdirSync(path.join(searchRoot, e.name), { withFileTypes: true })) {
|
|
502
|
+
if (child.isFile() && child.name.endsWith('.html')) {
|
|
503
|
+
htmlScan.push(path.join(searchRoot, e.name, child.name));
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
} catch { /* skip */ }
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
// Prefer the HTML file that references our entry via a module script
|
|
510
|
+
const moduleScriptRe = /<script[^>]+type\s*=\s*["']module["'][^>]+src\s*=\s*["']([^"']+)["']/g;
|
|
511
|
+
for (const hp of htmlScan) {
|
|
512
|
+
const content = fs.readFileSync(hp, 'utf-8');
|
|
513
|
+
let m;
|
|
514
|
+
moduleScriptRe.lastIndex = 0;
|
|
515
|
+
while ((m = moduleScriptRe.exec(content)) !== null) {
|
|
516
|
+
const resolved = path.resolve(path.dirname(hp), m[1]);
|
|
517
|
+
if (resolved === path.resolve(entry)) {
|
|
518
|
+
htmlAbs = hp;
|
|
519
|
+
htmlFile = path.relative(projectRoot, hp);
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
if (htmlAbs) break;
|
|
524
|
+
}
|
|
525
|
+
// Last resort: use the first .html found
|
|
526
|
+
if (!htmlAbs && htmlScan.length > 0) {
|
|
527
|
+
htmlAbs = htmlScan[0];
|
|
528
|
+
htmlFile = path.relative(projectRoot, htmlScan[0]);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
435
531
|
}
|
|
436
532
|
|
|
437
533
|
// Output directory
|
|
@@ -459,7 +555,8 @@ function bundleApp() {
|
|
|
459
555
|
console.log(` Entry: ${entryRel}`);
|
|
460
556
|
console.log(` Output: ${path.relative(projectRoot, baseDistDir)}/server/ & local/`);
|
|
461
557
|
console.log(` Library: embedded`);
|
|
462
|
-
console.log(` HTML: ${htmlFile || 'not found (no
|
|
558
|
+
console.log(` HTML: ${htmlFile || 'not found (no HTML detected)'}`);
|
|
559
|
+
if (minimal) console.log(` Mode: minimal (HTML + bundle only)`);
|
|
463
560
|
console.log('');
|
|
464
561
|
|
|
465
562
|
// ------ doBuild (inlined) ------
|
|
@@ -571,10 +668,16 @@ function bundleApp() {
|
|
|
571
668
|
console.log(`\n ✓ ${bundleBase} (${sizeKB(fs.readFileSync(bundleFile))} KB)`);
|
|
572
669
|
console.log(` ✓ ${minBase} (${sizeKB(fs.readFileSync(minFile))} KB)`);
|
|
573
670
|
|
|
574
|
-
// Rewrite HTML
|
|
671
|
+
// Rewrite HTML (use full bundle — minified version mangles template literal whitespace)
|
|
672
|
+
const bundledFileSet = new Set(files);
|
|
575
673
|
if (htmlFile) {
|
|
576
|
-
|
|
577
|
-
|
|
674
|
+
rewriteHtml(projectRoot, htmlFile, bundleFile, true, bundledFileSet, serverDir, localDir);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// Copy static asset directories (icons/, images/, fonts/, etc.)
|
|
678
|
+
if (!minimal) {
|
|
679
|
+
const appRoot = htmlAbs ? path.dirname(htmlAbs) : (targetDir || projectRoot);
|
|
680
|
+
copyStaticAssets(appRoot, serverDir, localDir, bundledFileSet);
|
|
578
681
|
}
|
|
579
682
|
|
|
580
683
|
const elapsed = Date.now() - start;
|
package/cli/commands/dev.js
CHANGED
|
@@ -321,10 +321,13 @@ function devServer() {
|
|
|
321
321
|
|
|
322
322
|
const { createApp, static: serveStatic } = zeroHttp;
|
|
323
323
|
|
|
324
|
+
// Custom HTML entry file (default: index.html)
|
|
325
|
+
const htmlEntry = option('index', 'i', 'index.html');
|
|
326
|
+
|
|
324
327
|
// Determine the project root to serve
|
|
325
328
|
let root = null;
|
|
326
329
|
for (let i = 1; i < args.length; i++) {
|
|
327
|
-
if (!args[i].startsWith('-') && args[i - 1] !== '-p' && args[i - 1] !== '--port') {
|
|
330
|
+
if (!args[i].startsWith('-') && args[i - 1] !== '-p' && args[i - 1] !== '--port' && args[i - 1] !== '--index') {
|
|
328
331
|
root = path.resolve(process.cwd(), args[i]);
|
|
329
332
|
break;
|
|
330
333
|
}
|
|
@@ -336,7 +339,7 @@ function devServer() {
|
|
|
336
339
|
path.join(process.cwd(), 'src'),
|
|
337
340
|
];
|
|
338
341
|
for (const c of candidates) {
|
|
339
|
-
if (fs.existsSync(path.join(c,
|
|
342
|
+
if (fs.existsSync(path.join(c, htmlEntry))) { root = c; break; }
|
|
340
343
|
}
|
|
341
344
|
if (!root) root = process.cwd();
|
|
342
345
|
}
|
|
@@ -389,9 +392,9 @@ function devServer() {
|
|
|
389
392
|
res.status(404).send('Not Found');
|
|
390
393
|
return;
|
|
391
394
|
}
|
|
392
|
-
const indexPath = path.join(root,
|
|
395
|
+
const indexPath = path.join(root, htmlEntry);
|
|
393
396
|
if (!fs.existsSync(indexPath)) {
|
|
394
|
-
res.status(404).send(
|
|
397
|
+
res.status(404).send(`${htmlEntry} not found`);
|
|
395
398
|
return;
|
|
396
399
|
}
|
|
397
400
|
let html = fs.readFileSync(indexPath, 'utf-8');
|
|
@@ -495,6 +498,7 @@ function devServer() {
|
|
|
495
498
|
console.log(` \x1b[2m${'-'.repeat(40)}\x1b[0m`);
|
|
496
499
|
console.log(` Local: \x1b[36mhttp://localhost:${PORT}/\x1b[0m`);
|
|
497
500
|
console.log(` Root: ${path.relative(process.cwd(), root) || '.'}`);
|
|
501
|
+
if (htmlEntry !== 'index.html') console.log(` HTML: \x1b[36m${htmlEntry}\x1b[0m`);
|
|
498
502
|
console.log(` Live Reload: \x1b[32menabled\x1b[0m (SSE)`);
|
|
499
503
|
console.log(` Overlay: \x1b[32menabled\x1b[0m (syntax + runtime errors)`);
|
|
500
504
|
if (noIntercept) console.log(` Intercept: \x1b[33mdisabled\x1b[0m (--no-intercept)`);
|
package/cli/help.js
CHANGED
|
@@ -12,6 +12,7 @@ function showHelp() {
|
|
|
12
12
|
|
|
13
13
|
dev [root] Start a dev server with live-reload
|
|
14
14
|
--port, -p <number> Port number (default: 3100)
|
|
15
|
+
--index, -i <file> Index HTML file (default: index.html)
|
|
15
16
|
--no-intercept Disable auto-resolution of zquery.min.js
|
|
16
17
|
(serve the on-disk vendor copy instead)
|
|
17
18
|
|
|
@@ -20,9 +21,10 @@ function showHelp() {
|
|
|
20
21
|
overlay in the browser. Runtime errors and
|
|
21
22
|
unhandled rejections are also captured.
|
|
22
23
|
|
|
23
|
-
bundle [dir]
|
|
24
|
-
--out, -o <path> Output directory (default: dist/ next to
|
|
25
|
-
--
|
|
24
|
+
bundle [dir|file] Bundle app ES modules into a single file
|
|
25
|
+
--out, -o <path> Output directory (default: dist/ next to HTML file)
|
|
26
|
+
--index, -i <file> Index HTML file (default: auto-detected)
|
|
27
|
+
--minimal, -m Only output HTML + bundled JS (skip static assets)
|
|
26
28
|
|
|
27
29
|
build Build the zQuery library \u2192 dist/ --watch, -w Watch src/ and rebuild on changes (must be run from the project root where src/ lives)
|
|
28
30
|
|
|
@@ -34,9 +36,10 @@ function showHelp() {
|
|
|
34
36
|
2. Within HTML: module script pointing to app.js, else first module script
|
|
35
37
|
3. JS scan: $.router( first (entry point), then $.mount( / $.store(
|
|
36
38
|
4. Convention fallbacks (scripts/app.js, app.js, etc.)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
• Passing a directory auto-detects the entry; passing a file uses it directly
|
|
40
|
+
• zquery.min.js is always embedded (auto-built from source if not found)
|
|
41
|
+
• HTML file is auto-detected (any .html, not just index.html)
|
|
42
|
+
• Output goes to dist/server/ and dist/local/ next to the detected HTML file
|
|
40
43
|
|
|
41
44
|
OUTPUT
|
|
42
45
|
|
|
@@ -79,9 +82,18 @@ function showHelp() {
|
|
|
79
82
|
# Bundle an app from the project root
|
|
80
83
|
zquery bundle my-app/
|
|
81
84
|
|
|
85
|
+
# Pass a direct entry file (skip auto-detection)
|
|
86
|
+
zquery bundle my-app/scripts/main.js
|
|
87
|
+
|
|
82
88
|
# Custom output directory
|
|
83
89
|
zquery bundle my-app/ -o build/
|
|
84
90
|
|
|
91
|
+
# Minimal build (only HTML + bundled JS, no static assets)
|
|
92
|
+
zquery bundle my-app/ --minimal
|
|
93
|
+
|
|
94
|
+
# Dev server with a custom index page
|
|
95
|
+
zquery dev my-app/ --index home.html
|
|
96
|
+
|
|
85
97
|
The bundler walks the ES module import graph starting from the entry
|
|
86
98
|
file, topologically sorts dependencies, strips import/export syntax,
|
|
87
99
|
and concatenates everything into a single IIFE with content-hashed
|
package/dist/zquery.dist.zip
CHANGED
|
Binary file
|
package/dist/zquery.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* zQuery (zeroQuery) v0.
|
|
2
|
+
* zQuery (zeroQuery) v0.5.2
|
|
3
3
|
* Lightweight Frontend Library
|
|
4
4
|
* https://github.com/tonywied17/zero-query
|
|
5
5
|
* (c) 2026 Anthony Wiedman — MIT License
|
|
@@ -605,6 +605,16 @@ query.id = (id) => document.getElementById(id);
|
|
|
605
605
|
query.class = (name) => document.querySelector(`.${name}`);
|
|
606
606
|
query.classes = (name) => Array.from(document.getElementsByClassName(name));
|
|
607
607
|
query.tag = (name) => Array.from(document.getElementsByTagName(name));
|
|
608
|
+
Object.defineProperty(query, 'name', {
|
|
609
|
+
value: (name) => Array.from(document.getElementsByName(name)),
|
|
610
|
+
writable: true, configurable: true
|
|
611
|
+
});
|
|
612
|
+
query.attr = (attr, value) => Array.from(
|
|
613
|
+
document.querySelectorAll(value !== undefined ? `[${attr}="${value}"]` : `[${attr}]`)
|
|
614
|
+
);
|
|
615
|
+
query.data = (key, value) => Array.from(
|
|
616
|
+
document.querySelectorAll(value !== undefined ? `[data-${key}="${value}"]` : `[data-${key}]`)
|
|
617
|
+
);
|
|
608
618
|
query.children = (parentId) => {
|
|
609
619
|
const p = document.getElementById(parentId);
|
|
610
620
|
return p ? Array.from(p.children) : [];
|
|
@@ -2862,11 +2872,16 @@ function $(selector, context) {
|
|
|
2862
2872
|
}
|
|
2863
2873
|
|
|
2864
2874
|
|
|
2865
|
-
// --- Quick refs
|
|
2875
|
+
// --- Quick refs (DOM selectors) --------------------------------------------
|
|
2866
2876
|
$.id = query.id;
|
|
2867
2877
|
$.class = query.class;
|
|
2868
2878
|
$.classes = query.classes;
|
|
2869
2879
|
$.tag = query.tag;
|
|
2880
|
+
Object.defineProperty($, 'name', {
|
|
2881
|
+
value: query.name, writable: true, configurable: true
|
|
2882
|
+
});
|
|
2883
|
+
$.attr = query.attr;
|
|
2884
|
+
$.data = query.data;
|
|
2870
2885
|
$.children = query.children;
|
|
2871
2886
|
|
|
2872
2887
|
// --- Collection selector ---------------------------------------------------
|
|
@@ -2895,6 +2910,7 @@ $.fn = query.fn;
|
|
|
2895
2910
|
|
|
2896
2911
|
// --- Reactive primitives ---------------------------------------------------
|
|
2897
2912
|
$.reactive = reactive;
|
|
2913
|
+
$.Signal = Signal;
|
|
2898
2914
|
$.signal = signal;
|
|
2899
2915
|
$.computed = computed;
|
|
2900
2916
|
$.effect = effect;
|
|
@@ -2946,7 +2962,7 @@ $.session = session;
|
|
|
2946
2962
|
$.bus = bus;
|
|
2947
2963
|
|
|
2948
2964
|
// --- Meta ------------------------------------------------------------------
|
|
2949
|
-
$.version = '0.
|
|
2965
|
+
$.version = '0.5.2';
|
|
2950
2966
|
$.meta = {}; // populated at build time by CLI bundler
|
|
2951
2967
|
|
|
2952
2968
|
$.noConflict = () => {
|
package/dist/zquery.min.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* zQuery (zeroQuery) v0.
|
|
2
|
+
* zQuery (zeroQuery) v0.5.2
|
|
3
3
|
* Lightweight Frontend Library
|
|
4
4
|
* https://github.com/tonywied17/zero-query
|
|
5
5
|
* (c) 2026 Anthony Wiedman — MIT License
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
(function(global) {
|
|
8
8
|
'use strict';
|
|
9
9
|
function reactive(target, onChange, _path = '') { if (typeof target !== 'object' || target === null) return target; const proxyCache = new WeakMap(); const handler = { get(obj, key) { if (key === '__isReactive') return true; if (key === '__raw') return obj; const value = obj[key]; if (typeof value === 'object' && value !== null) { if (proxyCache.has(value)) return proxyCache.get(value); const childProxy = new Proxy(value, handler); proxyCache.set(value, childProxy); return childProxy; } return value; }, set(obj, key, value) { const old = obj[key]; if (old === value) return true; obj[key] = value; onChange(key, value, old); return true; }, deleteProperty(obj, key) { const old = obj[key]; delete obj[key]; onChange(key, undefined, old); return true; } }; return new Proxy(target, handler);
|
|
10
|
-
class ZQueryCollection { constructor(elements) { this.elements = Array.isArray(elements) ? elements : [elements]; this.length = this.elements.length; this.elements.forEach((el, i) => { this[i] = el; }); } each(fn) { this.elements.forEach((el, i) => fn.call(el, i, el)); return this; } map(fn) { return this.elements.map((el, i) => fn.call(el, i, el)); } first() { return this.elements[0] || null; } last() { return this.elements[this.length - 1] || null; } eq(i) { return new ZQueryCollection(this.elements[i] ? [this.elements[i]] : []); } toArray(){ return [...this.elements]; } [Symbol.iterator]() { return this.elements[Symbol.iterator](); } find(selector) { const found = []; this.elements.forEach(el => found.push(...el.querySelectorAll(selector))); return new ZQueryCollection(found); } parent() { const parents = [...new Set(this.elements.map(el => el.parentElement).filter(Boolean))]; return new ZQueryCollection(parents); } closest(selector) { return new ZQueryCollection( this.elements.map(el => el.closest(selector)).filter(Boolean) ); } children(selector) { const kids = []; this.elements.forEach(el => { kids.push(...(selector ? el.querySelectorAll(`:scope > ${selector}`) : el.children)); }); return new ZQueryCollection([...kids]); } siblings() { const sibs = []; this.elements.forEach(el => { sibs.push(...[...el.parentElement.children].filter(c => c !== el)); }); return new ZQueryCollection(sibs); } next() { return new ZQueryCollection(this.elements.map(el => el.nextElementSibling).filter(Boolean)); } prev() { return new ZQueryCollection(this.elements.map(el => el.previousElementSibling).filter(Boolean)); } filter(selector) { if (typeof selector === 'function') { return new ZQueryCollection(this.elements.filter(selector)); } return new ZQueryCollection(this.elements.filter(el => el.matches(selector))); } not(selector) { if (typeof selector === 'function') { return new ZQueryCollection(this.elements.filter((el, i) => !selector.call(el, i, el))); } return new ZQueryCollection(this.elements.filter(el => !el.matches(selector))); } has(selector) { return new ZQueryCollection(this.elements.filter(el => el.querySelector(selector))); } addClass(...names) { const classes = names.flatMap(n => n.split(/\s+/)); return this.each((_, el) => el.classList.add(...classes)); } removeClass(...names) { const classes = names.flatMap(n => n.split(/\s+/)); return this.each((_, el) => el.classList.remove(...classes)); } toggleClass(name, force) { return this.each((_, el) => el.classList.toggle(name, force)); } hasClass(name) { return this.first()?.classList.contains(name) || false; } attr(name, value) { if (value === undefined) return this.first()?.getAttribute(name); return this.each((_, el) => el.setAttribute(name, value)); } removeAttr(name) { return this.each((_, el) => el.removeAttribute(name)); } prop(name, value) { if (value === undefined) return this.first()?.[name]; return this.each((_, el) => { el[name] = value; }); } data(key, value) { if (value === undefined) { if (key === undefined) return this.first()?.dataset; const raw = this.first()?.dataset[key]; try { return JSON.parse(raw); } catch { return raw; } } return this.each((_, el) => { el.dataset[key] = typeof value === 'object' ? JSON.stringify(value) : value; }); } css(props) { if (typeof props === 'string') { return getComputedStyle(this.first())[props]; } return this.each((_, el) => Object.assign(el.style, props)); } width() { return this.first()?.getBoundingClientRect().width; } height() { return this.first()?.getBoundingClientRect().height; } offset() { const r = this.first()?.getBoundingClientRect(); return r ? { top: r.top + window.scrollY, left: r.left + window.scrollX, width: r.width, height: r.height } : null; } position() { const el = this.first(); return el ? { top: el.offsetTop, left: el.offsetLeft } : null; } html(content) { if (content === undefined) return this.first()?.innerHTML; return this.each((_, el) => { el.innerHTML = content; }); } text(content) { if (content === undefined) return this.first()?.textContent; return this.each((_, el) => { el.textContent = content; }); } val(value) { if (value === undefined) return this.first()?.value; return this.each((_, el) => { el.value = value; }); } append(content) { return this.each((_, el) => { if (typeof content === 'string') el.insertAdjacentHTML('beforeend', content); else if (content instanceof ZQueryCollection) content.each((__, c) => el.appendChild(c)); else if (content instanceof Node) el.appendChild(content); }); } prepend(content) { return this.each((_, el) => { if (typeof content === 'string') el.insertAdjacentHTML('afterbegin', content); else if (content instanceof Node) el.insertBefore(content, el.firstChild); }); } after(content) { return this.each((_, el) => { if (typeof content === 'string') el.insertAdjacentHTML('afterend', content); else if (content instanceof Node) el.parentNode.insertBefore(content, el.nextSibling); }); } before(content) { return this.each((_, el) => { if (typeof content === 'string') el.insertAdjacentHTML('beforebegin', content); else if (content instanceof Node) el.parentNode.insertBefore(content, el); }); } wrap(wrapper) { return this.each((_, el) => { const w = typeof wrapper === 'string' ? createFragment(wrapper).firstElementChild : wrapper.cloneNode(true); el.parentNode.insertBefore(w, el); w.appendChild(el); }); } remove() { return this.each((_, el) => el.remove()); } empty() { return this.each((_, el) => { el.innerHTML = ''; }); } clone(deep = true) { return new ZQueryCollection(this.elements.map(el => el.cloneNode(deep))); } replaceWith(content) { return this.each((_, el) => { if (typeof content === 'string') { el.insertAdjacentHTML('afterend', content); el.remove(); } else if (content instanceof Node) { el.parentNode.replaceChild(content, el); } }); } show(display = '') { return this.each((_, el) => { el.style.display = display; }); } hide() { return this.each((_, el) => { el.style.display = 'none'; }); } toggle(display = '') { return this.each((_, el) => { el.style.display = (el.style.display === 'none' || getComputedStyle(el).display === 'none') ? display : 'none'; }); } on(event, selectorOrHandler, handler) { const events = event.split(/\s+/); return this.each((_, el) => { events.forEach(evt => { if (typeof selectorOrHandler === 'function') { el.addEventListener(evt, selectorOrHandler); } else { el.addEventListener(evt, (e) => { const target = e.target.closest(selectorOrHandler); if (target && el.contains(target)) handler.call(target, e); }); } }); }); } off(event, handler) { const events = event.split(/\s+/); return this.each((_, el) => { events.forEach(evt => el.removeEventListener(evt, handler)); }); } one(event, handler) { return this.each((_, el) => { el.addEventListener(event, handler, { once: true }); }); } trigger(event, detail) { return this.each((_, el) => { el.dispatchEvent(new CustomEvent(event, { detail, bubbles: true, cancelable: true })); }); } click(fn) { return fn ? this.on('click', fn) : this.trigger('click'); } submit(fn) { return fn ? this.on('submit', fn) : this.trigger('submit'); } focus() { this.first()?.focus(); return this; } blur() { this.first()?.blur(); return this; } animate(props, duration = 300, easing = 'ease') { return new Promise(resolve => { const count = { done: 0 }; this.each((_, el) => { el.style.transition = `all ${duration}ms ${easing}`; requestAnimationFrame(() => { Object.assign(el.style, props); const onEnd = () => { el.removeEventListener('transitionend', onEnd); el.style.transition = ''; if (++count.done >= this.length) resolve(this); }; el.addEventListener('transitionend', onEnd); }); }); setTimeout(() => resolve(this), duration + 50); }); } fadeIn(duration = 300) { return this.css({ opacity: '0', display: '' }).animate({ opacity: '1' }, duration); } fadeOut(duration = 300) { return this.animate({ opacity: '0' }, duration).then(col => col.hide()); } slideToggle(duration = 300) { return this.each((_, el) => { if (el.style.display === 'none' || getComputedStyle(el).display === 'none') { el.style.display = ''; el.style.overflow = 'hidden'; const h = el.scrollHeight + 'px'; el.style.maxHeight = '0'; el.style.transition = `max-height ${duration}ms ease`; requestAnimationFrame(() => { el.style.maxHeight = h; }); setTimeout(() => { el.style.maxHeight = ''; el.style.overflow = ''; el.style.transition = ''; }, duration); } else { el.style.overflow = 'hidden'; el.style.maxHeight = el.scrollHeight + 'px'; el.style.transition = `max-height ${duration}ms ease`; requestAnimationFrame(() => { el.style.maxHeight = '0'; }); setTimeout(() => { el.style.display = 'none'; el.style.maxHeight = ''; el.style.overflow = ''; el.style.transition = ''; }, duration); } }); } serialize() { const form = this.first(); if (!form || form.tagName !== 'FORM') return ''; return new URLSearchParams(new FormData(form)).toString(); } serializeObject() { const form = this.first(); if (!form || form.tagName !== 'FORM') return {}; const obj = {}; new FormData(form).forEach((v, k) => { if (obj[k] !== undefined) { if (!Array.isArray(obj[k])) obj[k] = [obj[k]]; obj[k].push(v); } else { obj[k] = v; } }); return obj; }
|
|
10
|
+
class ZQueryCollection { constructor(elements) { this.elements = Array.isArray(elements) ? elements : [elements]; this.length = this.elements.length; this.elements.forEach((el, i) => { this[i] = el; }); } each(fn) { this.elements.forEach((el, i) => fn.call(el, i, el)); return this; } map(fn) { return this.elements.map((el, i) => fn.call(el, i, el)); } first() { return this.elements[0] || null; } last() { return this.elements[this.length - 1] || null; } eq(i) { return new ZQueryCollection(this.elements[i] ? [this.elements[i]] : []); } toArray(){ return [...this.elements]; } [Symbol.iterator]() { return this.elements[Symbol.iterator](); } find(selector) { const found = []; this.elements.forEach(el => found.push(...el.querySelectorAll(selector))); return new ZQueryCollection(found); } parent() { const parents = [...new Set(this.elements.map(el => el.parentElement).filter(Boolean))]; return new ZQueryCollection(parents); } closest(selector) { return new ZQueryCollection( this.elements.map(el => el.closest(selector)).filter(Boolean) ); } children(selector) { const kids = []; this.elements.forEach(el => { kids.push(...(selector ? el.querySelectorAll(`:scope > ${selector}`) : el.children)); }); return new ZQueryCollection([...kids]); } siblings() { const sibs = []; this.elements.forEach(el => { sibs.push(...[...el.parentElement.children].filter(c => c !== el)); }); return new ZQueryCollection(sibs); } next() { return new ZQueryCollection(this.elements.map(el => el.nextElementSibling).filter(Boolean)); } prev() { return new ZQueryCollection(this.elements.map(el => el.previousElementSibling).filter(Boolean)); } filter(selector) { if (typeof selector === 'function') { return new ZQueryCollection(this.elements.filter(selector)); } return new ZQueryCollection(this.elements.filter(el => el.matches(selector))); } not(selector) { if (typeof selector === 'function') { return new ZQueryCollection(this.elements.filter((el, i) => !selector.call(el, i, el))); } return new ZQueryCollection(this.elements.filter(el => !el.matches(selector))); } has(selector) { return new ZQueryCollection(this.elements.filter(el => el.querySelector(selector))); } addClass(...names) { const classes = names.flatMap(n => n.split(/\s+/)); return this.each((_, el) => el.classList.add(...classes)); } removeClass(...names) { const classes = names.flatMap(n => n.split(/\s+/)); return this.each((_, el) => el.classList.remove(...classes)); } toggleClass(name, force) { return this.each((_, el) => el.classList.toggle(name, force)); } hasClass(name) { return this.first()?.classList.contains(name) || false; } attr(name, value) { if (value === undefined) return this.first()?.getAttribute(name); return this.each((_, el) => el.setAttribute(name, value)); } removeAttr(name) { return this.each((_, el) => el.removeAttribute(name)); } prop(name, value) { if (value === undefined) return this.first()?.[name]; return this.each((_, el) => { el[name] = value; }); } data(key, value) { if (value === undefined) { if (key === undefined) return this.first()?.dataset; const raw = this.first()?.dataset[key]; try { return JSON.parse(raw); } catch { return raw; } } return this.each((_, el) => { el.dataset[key] = typeof value === 'object' ? JSON.stringify(value) : value; }); } css(props) { if (typeof props === 'string') { return getComputedStyle(this.first())[props]; } return this.each((_, el) => Object.assign(el.style, props)); } width() { return this.first()?.getBoundingClientRect().width; } height() { return this.first()?.getBoundingClientRect().height; } offset() { const r = this.first()?.getBoundingClientRect(); return r ? { top: r.top + window.scrollY, left: r.left + window.scrollX, width: r.width, height: r.height } : null; } position() { const el = this.first(); return el ? { top: el.offsetTop, left: el.offsetLeft } : null; } html(content) { if (content === undefined) return this.first()?.innerHTML; return this.each((_, el) => { el.innerHTML = content; }); } text(content) { if (content === undefined) return this.first()?.textContent; return this.each((_, el) => { el.textContent = content; }); } val(value) { if (value === undefined) return this.first()?.value; return this.each((_, el) => { el.value = value; }); } append(content) { return this.each((_, el) => { if (typeof content === 'string') el.insertAdjacentHTML('beforeend', content); else if (content instanceof ZQueryCollection) content.each((__, c) => el.appendChild(c)); else if (content instanceof Node) el.appendChild(content); }); } prepend(content) { return this.each((_, el) => { if (typeof content === 'string') el.insertAdjacentHTML('afterbegin', content); else if (content instanceof Node) el.insertBefore(content, el.firstChild); }); } after(content) { return this.each((_, el) => { if (typeof content === 'string') el.insertAdjacentHTML('afterend', content); else if (content instanceof Node) el.parentNode.insertBefore(content, el.nextSibling); }); } before(content) { return this.each((_, el) => { if (typeof content === 'string') el.insertAdjacentHTML('beforebegin', content); else if (content instanceof Node) el.parentNode.insertBefore(content, el); }); } wrap(wrapper) { return this.each((_, el) => { const w = typeof wrapper === 'string' ? createFragment(wrapper).firstElementChild : wrapper.cloneNode(true); el.parentNode.insertBefore(w, el); w.appendChild(el); }); } remove() { return this.each((_, el) => el.remove()); } empty() { return this.each((_, el) => { el.innerHTML = ''; }); } clone(deep = true) { return new ZQueryCollection(this.elements.map(el => el.cloneNode(deep))); } replaceWith(content) { return this.each((_, el) => { if (typeof content === 'string') { el.insertAdjacentHTML('afterend', content); el.remove(); } else if (content instanceof Node) { el.parentNode.replaceChild(content, el); } }); } show(display = '') { return this.each((_, el) => { el.style.display = display; }); } hide() { return this.each((_, el) => { el.style.display = 'none'; }); } toggle(display = '') { return this.each((_, el) => { el.style.display = (el.style.display === 'none' || getComputedStyle(el).display === 'none') ? display : 'none'; }); } on(event, selectorOrHandler, handler) { const events = event.split(/\s+/); return this.each((_, el) => { events.forEach(evt => { if (typeof selectorOrHandler === 'function') { el.addEventListener(evt, selectorOrHandler); } else { el.addEventListener(evt, (e) => { const target = e.target.closest(selectorOrHandler); if (target && el.contains(target)) handler.call(target, e); }); } }); }); } off(event, handler) { const events = event.split(/\s+/); return this.each((_, el) => { events.forEach(evt => el.removeEventListener(evt, handler)); }); } one(event, handler) { return this.each((_, el) => { el.addEventListener(event, handler, { once: true }); }); } trigger(event, detail) { return this.each((_, el) => { el.dispatchEvent(new CustomEvent(event, { detail, bubbles: true, cancelable: true })); }); } click(fn) { return fn ? this.on('click', fn) : this.trigger('click'); } submit(fn) { return fn ? this.on('submit', fn) : this.trigger('submit'); } focus() { this.first()?.focus(); return this; } blur() { this.first()?.blur(); return this; } animate(props, duration = 300, easing = 'ease') { return new Promise(resolve => { const count = { done: 0 }; this.each((_, el) => { el.style.transition = `all ${duration}ms ${easing}`; requestAnimationFrame(() => { Object.assign(el.style, props); const onEnd = () => { el.removeEventListener('transitionend', onEnd); el.style.transition = ''; if (++count.done >= this.length) resolve(this); }; el.addEventListener('transitionend', onEnd); }); }); setTimeout(() => resolve(this), duration + 50); }); } fadeIn(duration = 300) { return this.css({ opacity: '0', display: '' }).animate({ opacity: '1' }, duration); } fadeOut(duration = 300) { return this.animate({ opacity: '0' }, duration).then(col => col.hide()); } slideToggle(duration = 300) { return this.each((_, el) => { if (el.style.display === 'none' || getComputedStyle(el).display === 'none') { el.style.display = ''; el.style.overflow = 'hidden'; const h = el.scrollHeight + 'px'; el.style.maxHeight = '0'; el.style.transition = `max-height ${duration}ms ease`; requestAnimationFrame(() => { el.style.maxHeight = h; }); setTimeout(() => { el.style.maxHeight = ''; el.style.overflow = ''; el.style.transition = ''; }, duration); } else { el.style.overflow = 'hidden'; el.style.maxHeight = el.scrollHeight + 'px'; el.style.transition = `max-height ${duration}ms ease`; requestAnimationFrame(() => { el.style.maxHeight = '0'; }); setTimeout(() => { el.style.display = 'none'; el.style.maxHeight = ''; el.style.overflow = ''; el.style.transition = ''; }, duration); } }); } serialize() { const form = this.first(); if (!form || form.tagName !== 'FORM') return ''; return new URLSearchParams(new FormData(form)).toString(); } serializeObject() { const form = this.first(); if (!form || form.tagName !== 'FORM') return {}; const obj = {}; new FormData(form).forEach((v, k) => { if (obj[k] !== undefined) { if (!Array.isArray(obj[k])) obj[k] = [obj[k]]; obj[k].push(v); } else { obj[k] = v; } }); return obj; }
|
|
11
11
|
const _registry = new Map(); const _instances = new Map(); const _resourceCache = new Map(); let _uid = 0;
|
|
12
12
|
class Router { constructor(config = {}) { this._el = null; const isFile = typeof location !== 'undefined' && location.protocol === 'file:'; this._mode = isFile ? 'hash' : (config.mode || 'history'); let rawBase = config.base; if (rawBase == null) { rawBase = (typeof window !== 'undefined' && window.__ZQ_BASE) || ''; if (!rawBase && typeof document !== 'undefined') { const baseEl = document.querySelector('base'); if (baseEl) { try { rawBase = new URL(baseEl.href).pathname; } catch { rawBase = baseEl.getAttribute('href') || ''; } if (rawBase === '/') rawBase = ''; } } } this._base = String(rawBase).replace(/\/+$/, ''); if (this._base && !this._base.startsWith('/')) this._base = '/' + this._base; this._routes = []; this._fallback = config.fallback || null; this._current = null; this._guards = { before: [], after: [] }; this._listeners = new Set(); this._instance = null; this._resolving = false; if (config.el) { this._el = typeof config.el === 'string' ? document.querySelector(config.el) : config.el; } if (config.routes) { config.routes.forEach(r => this.add(r)); } if (this._mode === 'hash') { window.addEventListener('hashchange', () => this._resolve()); } else { window.addEventListener('popstate', () => this._resolve()); } document.addEventListener('click', (e) => { if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return; const link = e.target.closest('[z-link]'); if (!link) return; if (link.getAttribute('target') === '_blank') return; e.preventDefault(); this.navigate(link.getAttribute('z-link')); }); if (this._el) { queueMicrotask(() => this._resolve()); } } add(route) { const keys = []; const pattern = route.path .replace(/:(\w+)/g, (_, key) => { keys.push(key); return '([^/]+)'; }) .replace(/\*/g, '(.*)'); const regex = new RegExp(`^${pattern}$`); this._routes.push({ ...route, _regex: regex, _keys: keys }); if (route.fallback) { const fbKeys = []; const fbPattern = route.fallback .replace(/:(\w+)/g, (_, key) => { fbKeys.push(key); return '([^/]+)'; }) .replace(/\*/g, '(.*)'); const fbRegex = new RegExp(`^${fbPattern}$`); this._routes.push({ ...route, path: route.fallback, _regex: fbRegex, _keys: fbKeys }); } return this; } remove(path) { this._routes = this._routes.filter(r => r.path !== path); return this; } navigate(path, options = {}) { const [cleanPath, fragment] = (path || '').split('#'); let normalized = this._normalizePath(cleanPath); const hash = fragment ? '#' + fragment : ''; if (this._mode === 'hash') { if (fragment) window.__zqScrollTarget = fragment; window.location.hash = '#' + normalized; } else { window.history.pushState(options.state || {}, '', this._base + normalized + hash); this._resolve(); } return this; } replace(path, options = {}) { const [cleanPath, fragment] = (path || '').split('#'); let normalized = this._normalizePath(cleanPath); const hash = fragment ? '#' + fragment : ''; if (this._mode === 'hash') { if (fragment) window.__zqScrollTarget = fragment; window.location.replace('#' + normalized); } else { window.history.replaceState(options.state || {}, '', this._base + normalized + hash); this._resolve(); } return this; } _normalizePath(path) { let p = path && path.startsWith('/') ? path : (path ? `/${path}` : '/'); if (this._base) { if (p === this._base) return '/'; if (p.startsWith(this._base + '/')) p = p.slice(this._base.length) || '/'; } return p; } resolve(path) { const normalized = path && path.startsWith('/') ? path : (path ? `/${path}` : '/'); return this._base + normalized; } back() { window.history.back(); return this; } forward() { window.history.forward(); return this; } go(n) { window.history.go(n); return this; } beforeEach(fn) { this._guards.before.push(fn); return this; } afterEach(fn) { this._guards.after.push(fn); return this; } onChange(fn) { this._listeners.add(fn); return () => this._listeners.delete(fn); } get current() { return this._current; } get base() { return this._base; } get path() { if (this._mode === 'hash') { const raw = window.location.hash.slice(1) || '/'; if (raw && !raw.startsWith('/')) { window.__zqScrollTarget = raw; const fallbackPath = (this._current && this._current.path) || '/'; window.location.replace('#' + fallbackPath); return fallbackPath; } return raw; } let pathname = window.location.pathname || '/'; if (pathname.length > 1 && pathname.endsWith('/')) { pathname = pathname.slice(0, -1); } if (this._base) { if (pathname === this._base) return '/'; if (pathname.startsWith(this._base + '/')) { return pathname.slice(this._base.length) || '/'; } } return pathname; } get query() { const search = this._mode === 'hash' ? (window.location.hash.split('?')[1] || '') : window.location.search.slice(1); return Object.fromEntries(new URLSearchParams(search)); } async _resolve() { if (this._resolving) return; this._resolving = true; try { await this.__resolve(); } finally { this._resolving = false; } } async __resolve() { const fullPath = this.path; const [pathPart, queryString] = fullPath.split('?'); const path = pathPart || '/'; const query = Object.fromEntries(new URLSearchParams(queryString || '')); let matched = null; let params = {}; for (const route of this._routes) { const m = path.match(route._regex); if (m) { matched = route; route._keys.forEach((key, i) => { params[key] = m[i + 1]; }); break; } } if (!matched && this._fallback) { matched = { component: this._fallback, path: '*', _keys: [], _regex: /.*/ }; } if (!matched) return; const to = { route: matched, params, query, path }; const from = this._current; for (const guard of this._guards.before) { const result = await guard(to, from); if (result === false) return; if (typeof result === 'string') { return this.navigate(result); } } if (matched.load) { try { await matched.load(); } catch (err) { console.error(`zQuery Router: Failed to load module for "${matched.path}"`, err); return; } } this._current = to; if (this._el && matched.component) { if (this._instance) { this._instance.destroy(); this._instance = null; } this._el.innerHTML = ''; const props = { ...params, $route: to, $query: query, $params: params }; if (typeof matched.component === 'string') { const container = document.createElement(matched.component); this._el.appendChild(container); this._instance = mount(container, matched.component, props); } else if (typeof matched.component === 'function') { this._el.innerHTML = matched.component(to); } } for (const guard of this._guards.after) { await guard(to, from); } this._listeners.forEach(fn => fn(to, from)); } destroy() { if (this._instance) this._instance.destroy(); this._listeners.clear(); this._routes = []; this._guards = { before: [], after: [] }; }
|
|
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/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Lightweight modern frontend library — jQuery-like selectors, reactive
|
|
5
5
|
* components, SPA router, state management, HTTP client & utilities.
|
|
6
6
|
*
|
|
7
|
-
* @version 0.
|
|
7
|
+
* @version 0.5.2
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @see https://z-query.com/docs
|
|
10
10
|
*/
|
|
@@ -1086,6 +1086,12 @@ interface ZQueryStatic {
|
|
|
1086
1086
|
classes(name: string): Element[];
|
|
1087
1087
|
/** `document.getElementsByTagName(name)` as array. */
|
|
1088
1088
|
tag(name: string): Element[];
|
|
1089
|
+
/** `document.getElementsByName(name)` as array. */
|
|
1090
|
+
name(name: string): Element[];
|
|
1091
|
+
/** `document.querySelectorAll('[attr]')` or `[attr="value"]` as array. */
|
|
1092
|
+
attr(attr: string, value?: string): Element[];
|
|
1093
|
+
/** `document.querySelectorAll('[data-key]')` or `[data-key="value"]` as array. */
|
|
1094
|
+
data(key: string, value?: string): Element[];
|
|
1089
1095
|
/** Children of `#parentId` as array. */
|
|
1090
1096
|
children(parentId: string): Element[];
|
|
1091
1097
|
|
|
@@ -1117,6 +1123,7 @@ interface ZQueryStatic {
|
|
|
1117
1123
|
|
|
1118
1124
|
// -- Reactive ------------------------------------------------------------
|
|
1119
1125
|
reactive: typeof reactive;
|
|
1126
|
+
Signal: typeof Signal;
|
|
1120
1127
|
signal: typeof signal;
|
|
1121
1128
|
computed: typeof computed;
|
|
1122
1129
|
effect: typeof effect;
|
package/index.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { query, queryAll, ZQueryCollection } from './src/core.js';
|
|
13
|
-
import { reactive, signal, computed, effect } from './src/reactive.js';
|
|
13
|
+
import { reactive, Signal, signal, computed, effect } from './src/reactive.js';
|
|
14
14
|
import { component, mount, mountAll, getInstance, destroy, getRegistry, style } from './src/component.js';
|
|
15
15
|
import { createRouter, getRouter } from './src/router.js';
|
|
16
16
|
import { createStore, getStore } from './src/store.js';
|
|
@@ -49,11 +49,16 @@ function $(selector, context) {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
// --- Quick refs
|
|
52
|
+
// --- Quick refs (DOM selectors) --------------------------------------------
|
|
53
53
|
$.id = query.id;
|
|
54
54
|
$.class = query.class;
|
|
55
55
|
$.classes = query.classes;
|
|
56
56
|
$.tag = query.tag;
|
|
57
|
+
Object.defineProperty($, 'name', {
|
|
58
|
+
value: query.name, writable: true, configurable: true
|
|
59
|
+
});
|
|
60
|
+
$.attr = query.attr;
|
|
61
|
+
$.data = query.data;
|
|
57
62
|
$.children = query.children;
|
|
58
63
|
|
|
59
64
|
// --- Collection selector ---------------------------------------------------
|
|
@@ -82,6 +87,7 @@ $.fn = query.fn;
|
|
|
82
87
|
|
|
83
88
|
// --- Reactive primitives ---------------------------------------------------
|
|
84
89
|
$.reactive = reactive;
|
|
90
|
+
$.Signal = Signal;
|
|
85
91
|
$.signal = signal;
|
|
86
92
|
$.computed = computed;
|
|
87
93
|
$.effect = effect;
|
|
@@ -161,7 +167,7 @@ export {
|
|
|
161
167
|
$ as zQuery,
|
|
162
168
|
ZQueryCollection,
|
|
163
169
|
queryAll,
|
|
164
|
-
reactive, signal, computed, effect,
|
|
170
|
+
reactive, Signal, signal, computed, effect,
|
|
165
171
|
component, mount, mountAll, getInstance, destroy, getRegistry, style,
|
|
166
172
|
createRouter, getRouter,
|
|
167
173
|
createStore, getStore,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zero-query",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.2",
|
|
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",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dev": "node cli/index.js dev zquery-website",
|
|
22
22
|
"dev-lib": "node cli/index.js build --watch",
|
|
23
23
|
"bundle": "node cli/index.js bundle",
|
|
24
|
-
"bundle:app": "node cli/index.js bundle zquery-website"
|
|
24
|
+
"bundle:app": "node cli/index.js bundle zquery-website --minimal"
|
|
25
25
|
},
|
|
26
26
|
"keywords": [
|
|
27
27
|
"dom",
|
package/src/core.js
CHANGED
|
@@ -468,6 +468,16 @@ query.id = (id) => document.getElementById(id);
|
|
|
468
468
|
query.class = (name) => document.querySelector(`.${name}`);
|
|
469
469
|
query.classes = (name) => Array.from(document.getElementsByClassName(name));
|
|
470
470
|
query.tag = (name) => Array.from(document.getElementsByTagName(name));
|
|
471
|
+
Object.defineProperty(query, 'name', {
|
|
472
|
+
value: (name) => Array.from(document.getElementsByName(name)),
|
|
473
|
+
writable: true, configurable: true
|
|
474
|
+
});
|
|
475
|
+
query.attr = (attr, value) => Array.from(
|
|
476
|
+
document.querySelectorAll(value !== undefined ? `[${attr}="${value}"]` : `[${attr}]`)
|
|
477
|
+
);
|
|
478
|
+
query.data = (key, value) => Array.from(
|
|
479
|
+
document.querySelectorAll(value !== undefined ? `[data-${key}="${value}"]` : `[data-${key}]`)
|
|
480
|
+
);
|
|
471
481
|
query.children = (parentId) => {
|
|
472
482
|
const p = document.getElementById(parentId);
|
|
473
483
|
return p ? Array.from(p.children) : [];
|