zero-query 0.9.8 → 1.0.0
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 +55 -31
- package/cli/args.js +1 -1
- package/cli/commands/build.js +2 -2
- package/cli/commands/bundle.js +15 -15
- package/cli/commands/create.js +41 -7
- package/cli/commands/dev/devtools/index.js +1 -1
- package/cli/commands/dev/devtools/js/core.js +14 -14
- package/cli/commands/dev/devtools/js/elements.js +4 -4
- package/cli/commands/dev/devtools/js/stats.js +1 -1
- package/cli/commands/dev/devtools/styles.css +2 -2
- package/cli/commands/dev/index.js +2 -2
- package/cli/commands/dev/logger.js +1 -1
- package/cli/commands/dev/overlay.js +21 -14
- package/cli/commands/dev/server.js +5 -5
- package/cli/commands/dev/validator.js +7 -7
- package/cli/commands/dev/watcher.js +6 -6
- package/cli/help.js +4 -2
- package/cli/index.js +2 -2
- package/cli/scaffold/default/app/app.js +17 -18
- package/cli/scaffold/default/app/components/about.js +9 -9
- package/cli/scaffold/default/app/components/api-demo.js +6 -6
- package/cli/scaffold/default/app/components/contact-card.js +4 -4
- package/cli/scaffold/default/app/components/contacts/contacts.css +2 -2
- package/cli/scaffold/default/app/components/contacts/contacts.html +3 -3
- package/cli/scaffold/default/app/components/contacts/contacts.js +11 -11
- package/cli/scaffold/default/app/components/counter.js +8 -8
- package/cli/scaffold/default/app/components/home.js +13 -13
- package/cli/scaffold/default/app/components/not-found.js +1 -1
- package/cli/scaffold/default/app/components/playground/playground.css +1 -1
- package/cli/scaffold/default/app/components/playground/playground.html +11 -11
- package/cli/scaffold/default/app/components/playground/playground.js +11 -11
- package/cli/scaffold/default/app/components/todos.js +8 -8
- package/cli/scaffold/default/app/components/toolkit/toolkit.css +1 -1
- package/cli/scaffold/default/app/components/toolkit/toolkit.html +4 -4
- package/cli/scaffold/default/app/components/toolkit/toolkit.js +7 -7
- package/cli/scaffold/default/app/routes.js +1 -1
- package/cli/scaffold/default/app/store.js +1 -1
- package/cli/scaffold/default/global.css +2 -2
- package/cli/scaffold/default/index.html +2 -2
- package/cli/scaffold/minimal/app/app.js +6 -7
- package/cli/scaffold/minimal/app/components/about.js +5 -5
- package/cli/scaffold/minimal/app/components/counter.js +6 -6
- package/cli/scaffold/minimal/app/components/home.js +8 -8
- package/cli/scaffold/minimal/app/components/not-found.js +1 -1
- package/cli/scaffold/minimal/app/routes.js +1 -1
- package/cli/scaffold/minimal/app/store.js +1 -1
- package/cli/scaffold/minimal/global.css +2 -2
- package/cli/scaffold/minimal/index.html +1 -1
- package/cli/scaffold/ssr/app/app.js +29 -0
- package/cli/scaffold/ssr/app/components/about.js +28 -0
- package/cli/scaffold/ssr/app/components/home.js +37 -0
- package/cli/scaffold/ssr/app/components/not-found.js +15 -0
- package/cli/scaffold/ssr/app/routes.js +6 -0
- package/cli/scaffold/ssr/global.css +113 -0
- package/cli/scaffold/ssr/index.html +31 -0
- package/cli/scaffold/ssr/package.json +8 -0
- package/cli/scaffold/ssr/server/index.js +118 -0
- package/cli/utils.js +6 -6
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +565 -228
- package/dist/zquery.min.js +2 -2
- package/index.d.ts +25 -12
- package/index.js +11 -7
- package/package.json +9 -3
- package/src/component.js +64 -63
- package/src/core.js +15 -15
- package/src/diff.js +38 -38
- package/src/errors.js +72 -18
- package/src/expression.js +15 -17
- package/src/http.js +4 -4
- package/src/package.json +1 -0
- package/src/reactive.js +75 -9
- package/src/router.js +104 -24
- package/src/ssr.js +133 -39
- package/src/store.js +103 -21
- package/src/utils.js +64 -12
- package/tests/audit.test.js +143 -15
- package/tests/cli.test.js +20 -20
- package/tests/component.test.js +121 -121
- package/tests/core.test.js +56 -56
- package/tests/diff.test.js +42 -42
- package/tests/errors.test.js +425 -147
- package/tests/expression.test.js +58 -53
- package/tests/http.test.js +20 -20
- package/tests/reactive.test.js +185 -24
- package/tests/router.test.js +501 -74
- package/tests/ssr.test.js +444 -10
- package/tests/store.test.js +264 -23
- package/tests/utils.test.js +163 -26
- package/types/collection.d.ts +2 -2
- package/types/component.d.ts +5 -5
- package/types/errors.d.ts +36 -4
- package/types/http.d.ts +3 -3
- package/types/misc.d.ts +9 -9
- package/types/reactive.d.ts +25 -3
- package/types/router.d.ts +10 -6
- package/types/ssr.d.ts +22 -2
- package/types/store.d.ts +40 -5
- package/types/utils.d.ts +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// not-found.js - 404 fallback component
|
|
2
|
+
|
|
3
|
+
export const notFound = {
|
|
4
|
+
render() {
|
|
5
|
+
return `
|
|
6
|
+
<div class="page-header" style="text-align:center; margin-top:4rem;">
|
|
7
|
+
<h1>404</h1>
|
|
8
|
+
<p class="subtitle">Page not found.</p>
|
|
9
|
+
<p style="margin-top:1rem;">
|
|
10
|
+
<a href="/" style="color:var(--accent);">← Home</a>
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
`;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/* global.css - SSR Scaffold Styles */
|
|
2
|
+
|
|
3
|
+
*, *::before, *::after {
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
margin: 0;
|
|
6
|
+
padding: 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
:root {
|
|
10
|
+
--bg: #0f0f0f;
|
|
11
|
+
--surface: #1a1a2e;
|
|
12
|
+
--text: #e0e0e0;
|
|
13
|
+
--muted: #888;
|
|
14
|
+
--accent: #00d4ff;
|
|
15
|
+
--accent-hover: #00b8d9;
|
|
16
|
+
--radius: 8px;
|
|
17
|
+
--gap: 1.5rem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
body {
|
|
21
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
22
|
+
background: var(--bg);
|
|
23
|
+
color: var(--text);
|
|
24
|
+
line-height: 1.6;
|
|
25
|
+
min-height: 100vh;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* -- Navbar -- */
|
|
29
|
+
.navbar {
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
justify-content: space-between;
|
|
33
|
+
padding: 1rem 2rem;
|
|
34
|
+
background: var(--surface);
|
|
35
|
+
border-bottom: 1px solid rgba(255,255,255,0.06);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.brand {
|
|
39
|
+
font-weight: 700;
|
|
40
|
+
font-size: 1.2rem;
|
|
41
|
+
color: var(--accent);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.nav-links { display: flex; gap: 1rem; }
|
|
45
|
+
|
|
46
|
+
.nav-link {
|
|
47
|
+
color: var(--muted);
|
|
48
|
+
text-decoration: none;
|
|
49
|
+
padding: 0.4rem 0.8rem;
|
|
50
|
+
border-radius: var(--radius);
|
|
51
|
+
transition: color 0.2s, background 0.2s;
|
|
52
|
+
}
|
|
53
|
+
.nav-link:hover, .nav-link.active {
|
|
54
|
+
color: var(--accent);
|
|
55
|
+
background: rgba(0, 212, 255, 0.08);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* -- Main content -- */
|
|
59
|
+
z-outlet {
|
|
60
|
+
max-width: 800px;
|
|
61
|
+
margin: 2rem auto;
|
|
62
|
+
padding: 0 1.5rem;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
[z-cloak] { display: none !important; }
|
|
66
|
+
|
|
67
|
+
.page-header {
|
|
68
|
+
margin-bottom: var(--gap);
|
|
69
|
+
}
|
|
70
|
+
.page-header h1 {
|
|
71
|
+
font-size: 2rem;
|
|
72
|
+
margin-bottom: 0.5rem;
|
|
73
|
+
}
|
|
74
|
+
.page-header .subtitle {
|
|
75
|
+
color: var(--muted);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.card {
|
|
79
|
+
background: var(--surface);
|
|
80
|
+
border: 1px solid rgba(255,255,255,0.06);
|
|
81
|
+
border-radius: var(--radius);
|
|
82
|
+
padding: 1.5rem;
|
|
83
|
+
margin-bottom: var(--gap);
|
|
84
|
+
}
|
|
85
|
+
.card h3 {
|
|
86
|
+
margin-bottom: 0.75rem;
|
|
87
|
+
color: var(--accent);
|
|
88
|
+
}
|
|
89
|
+
.card code {
|
|
90
|
+
background: rgba(255,255,255,0.06);
|
|
91
|
+
padding: 0.15em 0.4em;
|
|
92
|
+
border-radius: 4px;
|
|
93
|
+
font-size: 0.9em;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.badge {
|
|
97
|
+
display: inline-block;
|
|
98
|
+
padding: 2px 8px;
|
|
99
|
+
border-radius: 4px;
|
|
100
|
+
font-size: 0.8em;
|
|
101
|
+
font-weight: 600;
|
|
102
|
+
}
|
|
103
|
+
.badge-ssr { background: rgba(0,212,255,0.15); color: var(--accent); }
|
|
104
|
+
.badge-csr { background: rgba(255,165,0,0.15); color: #ffa500; }
|
|
105
|
+
|
|
106
|
+
/* -- Footer -- */
|
|
107
|
+
.footer {
|
|
108
|
+
text-align: center;
|
|
109
|
+
padding: 2rem;
|
|
110
|
+
color: var(--muted);
|
|
111
|
+
border-top: 1px solid rgba(255,255,255,0.06);
|
|
112
|
+
margin-top: 3rem;
|
|
113
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{NAME}}</title>
|
|
7
|
+
<base href="/">
|
|
8
|
+
<link rel="stylesheet" href="global.css">
|
|
9
|
+
<script src="zquery.min.js"></script>
|
|
10
|
+
<script type="module" src="app/app.js"></script>
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
|
|
14
|
+
<!-- Navigation -->
|
|
15
|
+
<nav class="navbar">
|
|
16
|
+
<span class="brand">⚡ {{NAME}}</span>
|
|
17
|
+
<div class="nav-links">
|
|
18
|
+
<a z-link="/" class="nav-link">Home</a>
|
|
19
|
+
<a z-link="/about" class="nav-link">About</a>
|
|
20
|
+
</div>
|
|
21
|
+
</nav>
|
|
22
|
+
|
|
23
|
+
<!-- Main content - SSR output is injected here by the server -->
|
|
24
|
+
<z-outlet z-cloak></z-outlet>
|
|
25
|
+
|
|
26
|
+
<footer class="footer">
|
|
27
|
+
<small>Built with zQuery · SSR Scaffold</small>
|
|
28
|
+
</footer>
|
|
29
|
+
|
|
30
|
+
</body>
|
|
31
|
+
</html>
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// server/index.js - SSR HTTP server
|
|
2
|
+
//
|
|
3
|
+
// Renders routes to HTML with zQuery SSR and serves them over HTTP.
|
|
4
|
+
// Components are imported from app/components/ - the same definitions
|
|
5
|
+
// the client uses.
|
|
6
|
+
//
|
|
7
|
+
// Usage:
|
|
8
|
+
// node server/index.js
|
|
9
|
+
// PORT=8080 node server/index.js
|
|
10
|
+
|
|
11
|
+
import { createServer } from 'node:http';
|
|
12
|
+
import { readFile } from 'node:fs/promises';
|
|
13
|
+
import { join, extname, resolve } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import { createSSRApp } from 'zero-query/ssr';
|
|
16
|
+
|
|
17
|
+
// Shared component definitions - same ones the client registers
|
|
18
|
+
import { homePage } from '../app/components/home.js';
|
|
19
|
+
import { aboutPage } from '../app/components/about.js';
|
|
20
|
+
import { notFound } from '../app/components/not-found.js';
|
|
21
|
+
import { routes } from '../app/routes.js';
|
|
22
|
+
|
|
23
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
24
|
+
const ROOT = join(__dirname, '..');
|
|
25
|
+
const PORT = parseInt(process.env.PORT || '3000', 10);
|
|
26
|
+
|
|
27
|
+
// --- SSR app ----------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
const app = createSSRApp();
|
|
30
|
+
app.component('home-page', homePage);
|
|
31
|
+
app.component('about-page', aboutPage);
|
|
32
|
+
app.component('not-found', notFound);
|
|
33
|
+
|
|
34
|
+
// --- Route matching ---------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
function matchRoute(pathname) {
|
|
37
|
+
const route = routes.find(r => r.path === pathname);
|
|
38
|
+
return route ? route.component : 'not-found';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// --- Render a full HTML page ------------------------------------------------
|
|
42
|
+
|
|
43
|
+
async function render(pathname) {
|
|
44
|
+
const component = matchRoute(pathname);
|
|
45
|
+
const body = await app.renderToString(component);
|
|
46
|
+
|
|
47
|
+
return `<!DOCTYPE html>
|
|
48
|
+
<html lang="en">
|
|
49
|
+
<head>
|
|
50
|
+
<meta charset="UTF-8">
|
|
51
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
52
|
+
<title>{{NAME}}</title>
|
|
53
|
+
<link rel="stylesheet" href="/global.css">
|
|
54
|
+
</head>
|
|
55
|
+
<body>
|
|
56
|
+
<nav class="navbar">
|
|
57
|
+
<span class="brand">⚡ {{NAME}}</span>
|
|
58
|
+
<div class="nav-links">
|
|
59
|
+
${routes.map(r =>
|
|
60
|
+
` <a href="${r.path}" class="nav-link${r.path === pathname ? ' active' : ''}">${
|
|
61
|
+
r.path === '/' ? 'Home' : r.path.slice(1)[0].toUpperCase() + r.path.slice(2)
|
|
62
|
+
}</a>`).join('\n')}
|
|
63
|
+
</div>
|
|
64
|
+
</nav>
|
|
65
|
+
<z-outlet>${body}</z-outlet>
|
|
66
|
+
<footer class="footer"><small>Built with zQuery · SSR</small></footer>
|
|
67
|
+
</body>
|
|
68
|
+
</html>`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// --- Static files -----------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
const MIME = {
|
|
74
|
+
'.css': 'text/css', '.js': 'text/javascript', '.json': 'application/json',
|
|
75
|
+
'.png': 'image/png', '.jpg': 'image/jpeg', '.svg': 'image/svg+xml',
|
|
76
|
+
'.ico': 'image/x-icon', '.woff2': 'font/woff2',
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
async function serveStatic(res, pathname) {
|
|
80
|
+
const ext = extname(pathname);
|
|
81
|
+
if (!MIME[ext]) return false;
|
|
82
|
+
|
|
83
|
+
const filePath = join(ROOT, pathname);
|
|
84
|
+
if (!resolve(filePath).startsWith(resolve(ROOT))) return false;
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const data = await readFile(filePath);
|
|
88
|
+
res.writeHead(200, { 'Content-Type': MIME[ext] });
|
|
89
|
+
res.end(data);
|
|
90
|
+
return true;
|
|
91
|
+
} catch { return false; }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// --- HTTP server ------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
createServer(async (req, res) => {
|
|
97
|
+
const { pathname } = new URL(req.url, `http://localhost:${PORT}`);
|
|
98
|
+
|
|
99
|
+
// Static assets (CSS, images, etc.)
|
|
100
|
+
if (pathname !== '/' && await serveStatic(res, pathname)) return;
|
|
101
|
+
|
|
102
|
+
// SSR route
|
|
103
|
+
try {
|
|
104
|
+
const html = await render(pathname);
|
|
105
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
106
|
+
res.end(html);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.error('SSR error:', err);
|
|
109
|
+
if (!res.headersSent) {
|
|
110
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
111
|
+
}
|
|
112
|
+
res.end('Internal Server Error');
|
|
113
|
+
}
|
|
114
|
+
}).listen(PORT, () => {
|
|
115
|
+
console.log(`\n ⚡ SSR server → http://localhost:${PORT}\n`);
|
|
116
|
+
routes.forEach(r => console.log(` ${r.path.padEnd(10)} → ${r.component}`));
|
|
117
|
+
console.log(` * → not-found\n`);
|
|
118
|
+
});
|
package/cli/utils.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* cli/utils.js
|
|
2
|
+
* cli/utils.js - shared utility functions
|
|
3
3
|
*
|
|
4
4
|
* Context-aware comment stripping, quick minification, size formatting,
|
|
5
5
|
* and recursive directory copying. These are consumed by both the
|
|
@@ -12,7 +12,7 @@ const fs = require('fs');
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
|
-
// _copyTemplateLiteral
|
|
15
|
+
// _copyTemplateLiteral - copy a template literal verbatim, tracking ${…}
|
|
16
16
|
// nesting so that `//` inside template text isn't mistaken for a comment.
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
18
18
|
|
|
@@ -49,7 +49,7 @@ function _copyTemplateLiteral(code, start) {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// ---------------------------------------------------------------------------
|
|
52
|
-
// stripComments
|
|
52
|
+
// stripComments - context-aware, skips strings / templates / regex
|
|
53
53
|
// ---------------------------------------------------------------------------
|
|
54
54
|
|
|
55
55
|
function stripComments(code) {
|
|
@@ -127,7 +127,7 @@ function stripComments(code) {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
// ---------------------------------------------------------------------------
|
|
130
|
-
// minify
|
|
130
|
+
// minify - single-pass minification
|
|
131
131
|
// Strips comments, collapses whitespace to the minimum required,
|
|
132
132
|
// and preserves string / template-literal / regex content verbatim.
|
|
133
133
|
// ---------------------------------------------------------------------------
|
|
@@ -251,7 +251,7 @@ function _isRegexCtx(out) {
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
// ---------------------------------------------------------------------------
|
|
254
|
-
// sizeKB
|
|
254
|
+
// sizeKB - human-readable file size
|
|
255
255
|
// ---------------------------------------------------------------------------
|
|
256
256
|
|
|
257
257
|
function sizeKB(buf) {
|
|
@@ -259,7 +259,7 @@ function sizeKB(buf) {
|
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
// ---------------------------------------------------------------------------
|
|
262
|
-
// copyDirSync
|
|
262
|
+
// copyDirSync - recursive directory copy
|
|
263
263
|
// ---------------------------------------------------------------------------
|
|
264
264
|
|
|
265
265
|
function copyDirSync(src, dest) {
|
package/dist/zquery.dist.zip
CHANGED
|
Binary file
|