zero-query 1.0.5 → 1.0.9

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 CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  </p>
17
17
 
18
- > **Lightweight, zero-dependency frontend library that combines jQuery-style DOM manipulation with a modern reactive component system, SPA router, global state management, HTTP client, and utility toolkit - all in a single ~91 KB minified browser bundle. Works out of the box with ES modules. An optional CLI bundler is available for single-file production builds.**
18
+ > **Lightweight, zero-dependency frontend library that combines jQuery-style DOM manipulation with a modern reactive component system, SPA router, global state management, HTTP client, and utility toolkit - all in a single ~108 KB minified browser bundle. Works out of the box with ES modules. An optional CLI bundler is available for single-file production builds.**
19
19
 
20
20
  ## Features
21
21
 
@@ -77,7 +77,7 @@ If you prefer **zero tooling**, download `dist/zquery.min.js` from the [dist/ fo
77
77
  git clone https://github.com/tonywied17/zero-query.git
78
78
  cd zero-query
79
79
  npx zquery build
80
- # → dist/zquery.min.js (~91 KB)
80
+ # → dist/zquery.min.js (~108 KB)
81
81
  ```
82
82
 
83
83
  ### Include in HTML
@@ -363,7 +363,7 @@ $.router({ base: '/my-app', routes });
363
363
  | `$.storage` `$.session` | Storage wrappers |
364
364
  | `$.EventBus` `$.bus` | Event bus |
365
365
  | `$.onError` `$.ZQueryError` `$.ErrorCode` `$.guardCallback` `$.guardAsync` `$.formatError` `$.validate` | Error handling |
366
- | `$.version` | Library version |\n| `$.libSize` | Minified bundle size string (e.g. `\"~91 KB\"`) |
366
+ | `$.version` | Library version |\n| `$.libSize` | Minified bundle size string (e.g. `\"~108 KB\"`) |
367
367
  | `$.unitTests` | Build-time test results `{ passed, failed, total, suites, duration, ok }` |
368
368
  | `$.meta` | Build metadata (populated by CLI bundler) |
369
369
  | `$.noConflict` | Release `$` global |
@@ -87,14 +87,137 @@ function walkImportGraph(entry) {
87
87
  return order;
88
88
  }
89
89
 
90
- /** Strip ES module import/export syntax, keeping declarations. */
90
+ /**
91
+ * Strip ES module import/export syntax, keeping declarations.
92
+ * Exported const/let are converted to var so they hoist past the per-module
93
+ * block scope and remain accessible to downstream modules.
94
+ * Exported function/class are converted to var assignments for the same reason.
95
+ * Non-exported declarations stay as-is and remain block-scoped (private).
96
+ *
97
+ * Template literals are temporarily hidden via a character-level scanner
98
+ * (handles nested backtick expressions) so that code examples inside
99
+ * backtick strings aren't accidentally rewritten.
100
+ */
91
101
  function stripModuleSyntax(code) {
102
+ // -- Hide template literals (supports nesting) -------------------------
103
+ const templates = [];
104
+
105
+ function scanTemplateLiteral(str, start) {
106
+ let i = start + 1; // skip opening backtick
107
+ while (i < str.length) {
108
+ const ch = str[i];
109
+ if (ch === '\\') { i += 2; continue; }
110
+ if (ch === '`') { return i + 1; } // end of template
111
+ if (ch === '$' && str[i + 1] === '{') {
112
+ i += 2; // skip ${
113
+ let depth = 1;
114
+ while (i < str.length && depth > 0) {
115
+ const c = str[i];
116
+ if (c === '{') { depth++; i++; }
117
+ else if (c === '}') { depth--; i++; }
118
+ else if (c === '`') { i = scanTemplateLiteral(str, i); }
119
+ else if (c === "'" || c === '"') { i = skipString(str, i); }
120
+ else if (c === '/' && str[i + 1] === '/') { while (i < str.length && str[i] !== '\n') i++; }
121
+ else if (c === '/' && str[i + 1] === '*') { i += 2; while (i < str.length - 1 && !(str[i] === '*' && str[i + 1] === '/')) i++; i += 2; }
122
+ else { i++; }
123
+ }
124
+ continue;
125
+ }
126
+ i++;
127
+ }
128
+ return i;
129
+ }
130
+
131
+ function skipString(str, start) {
132
+ const q = str[start];
133
+ let i = start + 1;
134
+ while (i < str.length) {
135
+ if (str[i] === '\\') { i += 2; continue; }
136
+ if (str[i] === q) { return i + 1; }
137
+ i++;
138
+ }
139
+ return i;
140
+ }
141
+
142
+ let hidden = '';
143
+ let pos = 0;
144
+ while (pos < code.length) {
145
+ const ch = code[pos];
146
+ if (ch === "'" || ch === '"') {
147
+ const end = skipString(code, pos);
148
+ hidden += code.substring(pos, end);
149
+ pos = end;
150
+ } else if (ch === '/' && code[pos + 1] === '/') {
151
+ let end = pos;
152
+ while (end < code.length && code[end] !== '\n') end++;
153
+ hidden += code.substring(pos, end);
154
+ pos = end;
155
+ } else if (ch === '/' && code[pos + 1] === '*') {
156
+ let end = pos + 2;
157
+ while (end < code.length - 1 && !(code[end] === '*' && code[end + 1] === '/')) end++;
158
+ end += 2;
159
+ hidden += code.substring(pos, end);
160
+ pos = end;
161
+ } else if (ch === '`') {
162
+ const end = scanTemplateLiteral(code, pos);
163
+ templates.push(code.substring(pos, end));
164
+ hidden += `__TPL_${templates.length - 1}__`;
165
+ pos = end;
166
+ } else {
167
+ hidden += ch;
168
+ pos++;
169
+ }
170
+ }
171
+
172
+ // -- Apply import / export transforms -----------------------------------
173
+ code = hidden;
92
174
  code = code.replace(/^\s*import\s+[\s\S]*?from\s+['"].*?['"];?\s*$/gm, '');
93
175
  code = code.replace(/^\s*import\s+['"].*?['"];?\s*$/gm, '');
94
176
  code = code.replace(/^(\s*)export\s+default\s+/gm, '$1');
95
- code = code.replace(/^(\s*)export\s+(const|let|var|function|class|async\s+function)\s/gm, '$1$2 ');
96
- code = code.replace(/^\s*export\s*\{[\s\S]*?\};?\s*$/gm, '');
97
- return code;
177
+ // Convert exported const/let/var to var (hoists past block scope)
178
+ code = code.replace(/^(\s*)export\s+(const|let|var)\s/gm, '$1var ');
179
+ // Convert exported function/async function to var assignment (hoists past block scope)
180
+ code = code.replace(/^(\s*)export\s+async\s+function\s+(\w+)/gm, '$1var $2 = async function $2');
181
+ code = code.replace(/^(\s*)export\s+function\s+(\w+)/gm, '$1var $2 = function $2');
182
+ // Convert exported class to var assignment
183
+ code = code.replace(/^(\s*)export\s+class\s+(\w+)/gm, '$1var $2 = class $2');
184
+ // Collect names from bare export blocks: export { a, b, c };
185
+ // These names are declared elsewhere (function/const/let) and need to be
186
+ // hoisted past the per-module block scope. We collect them here and
187
+ // the caller converts their declarations to var-based forms.
188
+ const bareExportNames = [];
189
+ code = code.replace(/^\s*export\s*\{([^}]+)\};?\s*$/gm, (_, names) => {
190
+ for (const n of names.split(',')) {
191
+ const parts = n.trim().split(/\s+as\s+/);
192
+ if (parts[0]) bareExportNames.push({ local: parts[0].trim(), exported: (parts[1] || parts[0]).trim() });
193
+ }
194
+ return '';
195
+ });
196
+
197
+ // For every bare-exported name, convert its block-scoped declaration to a
198
+ // var-based form so it hoists past the per-module { } wrapper:
199
+ // function foo(…) { … } → var foo = function foo(…) { … }
200
+ // async function foo(…) → var foo = async function foo(…)
201
+ // const/let foo = … → var foo = …
202
+ for (const { local } of bareExportNames) {
203
+ const fnRe = new RegExp(`^(\\s*)function\\s+${local}\\s*\\(`, 'gm');
204
+ code = code.replace(fnRe, `$1var ${local} = function ${local}(`);
205
+ const asyncFnRe = new RegExp(`^(\\s*)async\\s+function\\s+${local}\\s*\\(`, 'gm');
206
+ code = code.replace(asyncFnRe, `$1var ${local} = async function ${local}(`);
207
+ const constLetRe = new RegExp(`^(\\s*)(const|let)\\s+${local}\\b`, 'gm');
208
+ code = code.replace(constLetRe, `$1var ${local}`);
209
+ }
210
+
211
+ // Create aliases for "export { local as exported }" where the names differ
212
+ for (const { local, exported } of bareExportNames) {
213
+ if (exported !== local) {
214
+ code += `\nvar ${exported} = ${local};`;
215
+ }
216
+ }
217
+
218
+ // -- Restore template literals ------------------------------------------
219
+ code = code.replace(/__TPL_(\d+)__/g, (_, i) => templates[i]);
220
+ return { code, bareExportNames };
98
221
  }
99
222
 
100
223
  /** Replace import.meta.url with a runtime equivalent. */
@@ -381,8 +504,8 @@ function collectInlineResources(files, projectRoot) {
381
504
 
382
505
  // styleUrl:
383
506
  const styleUrlRe = /styleUrl\s*:\s*['"]([^'"]+)['"]/g;
384
- const styleMatch = styleUrlRe.exec(code);
385
- if (styleMatch) {
507
+ let styleMatch;
508
+ while ((styleMatch = styleUrlRe.exec(code)) !== null) {
386
509
  const stylePath = path.join(fileDir, styleMatch[1]);
387
510
  if (fs.existsSync(stylePath)) {
388
511
  const relKey = path.relative(projectRoot, stylePath).replace(/\\/g, '/');
@@ -877,12 +1000,13 @@ function bundleApp() {
877
1000
 
878
1001
  const sections = files.map(file => {
879
1002
  let code = fs.readFileSync(file, 'utf-8');
880
- code = stripModuleSyntax(code);
1003
+ const stripped = stripModuleSyntax(code);
1004
+ code = stripped.code;
881
1005
  code = replaceImportMeta(code, file, projectRoot);
882
1006
  code = rewriteResourceUrls(code, file, projectRoot);
883
1007
  code = minifyTemplateLiterals(code);
884
1008
  const rel = path.relative(projectRoot, file);
885
- return `// --- ${rel} ${'-'.repeat(Math.max(1, 60 - rel.length))}\n${code.trim()}`;
1009
+ return `// --- ${rel} ${'-'.repeat(Math.max(1, 60 - rel.length))}\n{\n${code.trim()}\n}`;
886
1010
  });
887
1011
 
888
1012
  // Embed zquery.min.js
@@ -1056,3 +1180,4 @@ function bundleApp() {
1056
1180
  }
1057
1181
 
1058
1182
  module.exports = bundleApp;
1183
+ module.exports.stripModuleSyntax = stripModuleSyntax;
@@ -92,7 +92,8 @@ async function createServer({ root, htmlEntry, port, noIntercept }) {
92
92
  zeroHttp = require('zero-http');
93
93
  }
94
94
 
95
- const { createApp, static: serveStatic } = zeroHttp;
95
+ const { createApp, static: serveStatic, debug } = zeroHttp;
96
+ debug.level('silent');
96
97
 
97
98
  const app = createApp();
98
99
  const pool = new SSEPool();
package/cli/utils.js CHANGED
@@ -206,9 +206,18 @@ function _minifyBody(code) {
206
206
 
207
207
  // ── Whitespace: collapse ────────────────────────────────────
208
208
  if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r') {
209
- while (i < code.length && (code[i] === ' ' || code[i] === '\t' || code[i] === '\n' || code[i] === '\r')) i++;
209
+ let hasNewline = ch === '\n' || ch === '\r';
210
+ while (i < code.length && (code[i] === ' ' || code[i] === '\t' || code[i] === '\n' || code[i] === '\r')) {
211
+ if (code[i] === '\n' || code[i] === '\r') hasNewline = true;
212
+ i++;
213
+ }
210
214
  const before = out[out.length - 1];
211
215
  const after = code[i];
216
+ // After '}', a newline may be needed for ASI (e.g. var x=function(){}⏎var y).
217
+ // A space alone doesn't trigger ASI, so preserve ';\n' when '}' precedes
218
+ // an identifier-start character and the original whitespace had a newline.
219
+ const afterIsId = after && ((after >= 'a' && after <= 'z') || (after >= 'A' && after <= 'Z') || after === '_' || after === '$');
220
+ if (before === '}' && afterIsId && hasNewline) { out += '\n'; continue; }
212
221
  if (_needsSpace(before, after)) out += ' ';
213
222
  continue;
214
223
  }
Binary file
package/dist/zquery.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * zQuery (zeroQuery) v1.0.5
2
+ * zQuery (zeroQuery) v1.0.9
3
3
  * Lightweight Frontend Library
4
4
  * https://github.com/tonywied17/zero-query
5
5
  * (c) 2026 Anthony Wiedman - MIT License
@@ -6254,9 +6254,9 @@ $.validate = validate;
6254
6254
  $.formatError = formatError;
6255
6255
 
6256
6256
  // --- Meta ------------------------------------------------------------------
6257
- $.version = '1.0.5';
6258
- $.libSize = '~107 KB';
6259
- $.unitTests = {"passed":1931,"failed":0,"total":1931,"suites":523,"duration":3815,"ok":true};
6257
+ $.version = '1.0.9';
6258
+ $.libSize = '~108 KB';
6259
+ $.unitTests = {"passed":1965,"failed":0,"total":1965,"suites":525,"duration":3752,"ok":true};
6260
6260
  $.meta = {}; // populated at build time by CLI bundler
6261
6261
 
6262
6262
  $.noConflict = () => {