zero-query 0.6.3 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/cli/commands/build.js +3 -3
- package/cli/commands/bundle.js +286 -8
- package/cli/commands/dev/index.js +2 -2
- package/cli/commands/dev/overlay.js +51 -2
- package/cli/commands/dev/server.js +34 -5
- package/cli/commands/dev/watcher.js +33 -0
- package/cli/scaffold/index.html +1 -0
- package/cli/scaffold/scripts/app.js +15 -22
- package/cli/scaffold/scripts/components/contacts/contacts.css +0 -7
- package/cli/scaffold/scripts/components/contacts/contacts.html +3 -3
- package/cli/scaffold/styles/styles.css +1 -0
- package/cli/utils.js +111 -6
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +379 -27
- package/dist/zquery.min.js +3 -16
- package/index.d.ts +127 -1290
- package/package.json +5 -5
- package/src/component.js +11 -1
- package/src/core.js +305 -10
- package/src/router.js +49 -2
- package/tests/component.test.js +304 -0
- package/tests/core.test.js +726 -0
- package/tests/diff.test.js +194 -0
- package/tests/errors.test.js +162 -0
- package/tests/expression.test.js +334 -0
- package/tests/http.test.js +181 -0
- package/tests/reactive.test.js +191 -0
- package/tests/router.test.js +332 -0
- package/tests/store.test.js +253 -0
- package/tests/utils.test.js +353 -0
- package/types/collection.d.ts +368 -0
- package/types/component.d.ts +210 -0
- package/types/errors.d.ts +103 -0
- package/types/http.d.ts +81 -0
- package/types/misc.d.ts +166 -0
- package/types/reactive.d.ts +76 -0
- package/types/router.d.ts +132 -0
- package/types/ssr.d.ts +49 -0
- package/types/store.d.ts +107 -0
- package/types/utils.d.ts +142 -0
|
@@ -27,7 +27,7 @@ const router = $.router({
|
|
|
27
27
|
// Highlight the active nav link on every route change
|
|
28
28
|
router.onChange((to) => {
|
|
29
29
|
$.all('.nav-link').removeClass('active');
|
|
30
|
-
|
|
30
|
+
$(`.nav-link[z-link="${to.path}"]`).addClass('active');
|
|
31
31
|
|
|
32
32
|
// Close mobile menu on navigate
|
|
33
33
|
closeMobileMenu();
|
|
@@ -36,26 +36,20 @@ router.onChange((to) => {
|
|
|
36
36
|
// ---------------------------------------------------------------------------
|
|
37
37
|
// Responsive sidebar toggle
|
|
38
38
|
// ---------------------------------------------------------------------------
|
|
39
|
-
const sidebar =
|
|
40
|
-
const overlay =
|
|
41
|
-
const toggle =
|
|
39
|
+
const $sidebar = $('#sidebar');
|
|
40
|
+
const $overlay = $('#overlay');
|
|
41
|
+
const $toggle = $('#menu-toggle');
|
|
42
42
|
|
|
43
|
-
function
|
|
44
|
-
sidebar.
|
|
45
|
-
overlay.
|
|
46
|
-
toggle.
|
|
43
|
+
function toggleMobileMenu(open) {
|
|
44
|
+
$sidebar.toggleClass('open', open);
|
|
45
|
+
$overlay.toggleClass('visible', open);
|
|
46
|
+
$toggle.toggleClass('active', open);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function closeMobileMenu() {
|
|
50
|
-
sidebar.classList.remove('open');
|
|
51
|
-
overlay.classList.remove('visible');
|
|
52
|
-
toggle.classList.remove('active');
|
|
53
|
-
}
|
|
49
|
+
function closeMobileMenu() { toggleMobileMenu(false); }
|
|
54
50
|
|
|
55
51
|
// $.on — global delegated event listeners
|
|
56
|
-
$.on('click', '#menu-toggle', () =>
|
|
57
|
-
sidebar.classList.contains('open') ? closeMobileMenu() : openMobileMenu();
|
|
58
|
-
});
|
|
52
|
+
$.on('click', '#menu-toggle', () => toggleMobileMenu(!$sidebar.hasClass('open')));
|
|
59
53
|
$.on('click', '#overlay', closeMobileMenu);
|
|
60
54
|
|
|
61
55
|
// Close sidebar on Escape key — using $.on direct (no selector needed)
|
|
@@ -69,14 +63,13 @@ $.on('keydown', (e) => {
|
|
|
69
63
|
// Any component can emit: $.bus.emit('toast', { message, type })
|
|
70
64
|
// Types: 'success', 'error', 'info'
|
|
71
65
|
$.bus.on('toast', ({ message, type = 'info' }) => {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
container.appendChild(toast);
|
|
66
|
+
const toast = $.create('div')
|
|
67
|
+
.addClass('toast', `toast-${type}`)
|
|
68
|
+
.text(message)
|
|
69
|
+
.appendTo('#toasts');
|
|
77
70
|
// Auto-remove after 3 seconds
|
|
78
71
|
setTimeout(() => {
|
|
79
|
-
toast.
|
|
72
|
+
toast.addClass('toast-exit');
|
|
80
73
|
setTimeout(() => toast.remove(), 300);
|
|
81
74
|
}, 3000);
|
|
82
75
|
});
|
|
@@ -216,13 +216,6 @@
|
|
|
216
216
|
font-weight: 500;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
@keyframes slide-in {
|
|
220
|
-
from { opacity: 0; transform: translateY(-6px); }
|
|
221
|
-
to { opacity: 1; transform: translateY(0); }
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/* -- Animation -- */
|
|
226
219
|
@keyframes slide-in {
|
|
227
220
|
from { opacity: 0; transform: translateY(-6px); }
|
|
228
221
|
to { opacity: 1; transform: translateY(0); }
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
</div>
|
|
69
69
|
|
|
70
70
|
<!-- Contact count -->
|
|
71
|
-
<div class="card" z-if="contacts.length > 0">
|
|
71
|
+
<div class="card" z-if="contacts.length > 0" z-key="contacts-list">
|
|
72
72
|
<!-- Contacts list — z-for renders each item -->
|
|
73
73
|
<ul class="contacts-list">
|
|
74
74
|
<li
|
|
@@ -102,14 +102,14 @@
|
|
|
102
102
|
</div>
|
|
103
103
|
|
|
104
104
|
<!-- Empty state -->
|
|
105
|
-
<div class="card" z-else>
|
|
105
|
+
<div class="card" z-else z-key="contacts-empty">
|
|
106
106
|
<div class="empty-state">
|
|
107
107
|
<p>No contacts yet — add one above!</p>
|
|
108
108
|
</div>
|
|
109
109
|
</div>
|
|
110
110
|
|
|
111
111
|
<!-- Selected contact detail panel — z-if conditional rendering -->
|
|
112
|
-
<div class="card contact-detail" z-if="selectedId !== null">
|
|
112
|
+
<div class="card contact-detail" z-if="selectedId !== null" z-key="contact-detail">
|
|
113
113
|
<div class="detail-header">
|
|
114
114
|
<div>
|
|
115
115
|
<h3 z-text="selectedName"></h3>
|
package/cli/utils.js
CHANGED
|
@@ -83,15 +83,120 @@ function stripComments(code) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// ---------------------------------------------------------------------------
|
|
86
|
-
// minify —
|
|
86
|
+
// minify — single-pass minification
|
|
87
|
+
// Strips comments, collapses whitespace to the minimum required,
|
|
88
|
+
// and preserves string / template-literal / regex content verbatim.
|
|
87
89
|
// ---------------------------------------------------------------------------
|
|
88
90
|
|
|
89
91
|
function minify(code, banner) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
return banner + '\n' + _minifyBody(code.replace(banner, ''));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Single-pass minifier: walks character-by-character, skips strings/regex,
|
|
97
|
+
* strips comments, and emits a space only when both neighbours are
|
|
98
|
+
* identifier-like characters (or when collapsing would create ++, --, // or /*).
|
|
99
|
+
*/
|
|
100
|
+
function _minifyBody(code) {
|
|
101
|
+
let out = '';
|
|
102
|
+
let i = 0;
|
|
103
|
+
|
|
104
|
+
while (i < code.length) {
|
|
105
|
+
const ch = code[i];
|
|
106
|
+
const nx = code[i + 1];
|
|
107
|
+
|
|
108
|
+
// ── String / template literal: copy verbatim ────────────────
|
|
109
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
110
|
+
const q = ch;
|
|
111
|
+
out += ch; i++;
|
|
112
|
+
while (i < code.length) {
|
|
113
|
+
if (code[i] === '\\') { out += code[i] + (code[i + 1] || ''); i += 2; continue; }
|
|
114
|
+
out += code[i];
|
|
115
|
+
if (code[i] === q) { i++; break; }
|
|
116
|
+
i++;
|
|
117
|
+
}
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Block comment: skip ─────────────────────────────────────
|
|
122
|
+
if (ch === '/' && nx === '*') {
|
|
123
|
+
i += 2;
|
|
124
|
+
while (i < code.length && !(code[i] === '*' && code[i + 1] === '/')) i++;
|
|
125
|
+
i += 2;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ── Line comment: skip ──────────────────────────────────────
|
|
130
|
+
if (ch === '/' && nx === '/') {
|
|
131
|
+
i += 2;
|
|
132
|
+
while (i < code.length && code[i] !== '\n') i++;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ── Regex literal: copy verbatim ────────────────────────────
|
|
137
|
+
if (ch === '/') {
|
|
138
|
+
if (_isRegexCtx(out)) {
|
|
139
|
+
out += ch; i++;
|
|
140
|
+
let inCC = false;
|
|
141
|
+
while (i < code.length) {
|
|
142
|
+
const rc = code[i];
|
|
143
|
+
if (rc === '\\') { out += rc + (code[i + 1] || ''); i += 2; continue; }
|
|
144
|
+
if (rc === '[') inCC = true;
|
|
145
|
+
if (rc === ']') inCC = false;
|
|
146
|
+
out += rc; i++;
|
|
147
|
+
if (rc === '/' && !inCC) {
|
|
148
|
+
while (i < code.length && /[gimsuy]/.test(code[i])) { out += code[i]; i++; }
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── Whitespace: collapse ────────────────────────────────────
|
|
157
|
+
if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r') {
|
|
158
|
+
while (i < code.length && (code[i] === ' ' || code[i] === '\t' || code[i] === '\n' || code[i] === '\r')) i++;
|
|
159
|
+
const before = out[out.length - 1];
|
|
160
|
+
const after = code[i];
|
|
161
|
+
if (_needsSpace(before, after)) out += ' ';
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
out += ch;
|
|
166
|
+
i++;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** True when removing the space between a and b would change semantics. */
|
|
173
|
+
function _needsSpace(a, b) {
|
|
174
|
+
if (!a || !b) return false;
|
|
175
|
+
const idA = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || (a >= '0' && a <= '9') || a === '_' || a === '$';
|
|
176
|
+
const idB = (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b === '_' || b === '$';
|
|
177
|
+
if (idA && idB) return true; // e.g. const x, return value
|
|
178
|
+
if (a === '+' && b === '+') return true; // prevent ++
|
|
179
|
+
if (a === '-' && b === '-') return true; // prevent --
|
|
180
|
+
if (a === '/' && (b === '/' || b === '*')) return true; // prevent // or /*
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Heuristic: is the next '/' a regex start (vs division)? */
|
|
185
|
+
function _isRegexCtx(out) {
|
|
186
|
+
let end = out.length - 1;
|
|
187
|
+
while (end >= 0 && out[end] === ' ') end--;
|
|
188
|
+
if (end < 0) return true;
|
|
189
|
+
const last = out[end];
|
|
190
|
+
if ('=({[,;:!&|?~+-*/%^>'.includes(last)) return true;
|
|
191
|
+
const tail = out.substring(Math.max(0, end - 7), end + 1);
|
|
192
|
+
const kws = ['return', 'typeof', 'case', 'in', 'delete', 'void', 'throw', 'new'];
|
|
193
|
+
for (const kw of kws) {
|
|
194
|
+
if (tail.endsWith(kw)) {
|
|
195
|
+
const pos = end - kw.length;
|
|
196
|
+
if (pos < 0 || !((out[pos] >= 'a' && out[pos] <= 'z') || (out[pos] >= 'A' && out[pos] <= 'Z') || (out[pos] >= '0' && out[pos] <= '9') || out[pos] === '_' || out[pos] === '$')) return true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
95
200
|
}
|
|
96
201
|
|
|
97
202
|
// ---------------------------------------------------------------------------
|
package/dist/zquery.dist.zip
CHANGED
|
Binary file
|