zero-query 1.2.3 → 1.2.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 CHANGED
@@ -43,21 +43,32 @@
43
43
 
44
44
  The fastest way to develop with zQuery is via the built-in **CLI dev server** with **live-reload**. It serves your ES modules as-is and automatically resolves the library - no manual downloads required.
45
45
 
46
+ Pick a scaffold flavor — every variant auto-installs, auto-starts, and opens your browser:
47
+
46
48
  ```bash
47
- # Install (per-project or globally)
48
- npm install zero-query # or: npm install zero-query -g
49
+ # Default: full sidebar layout, router, demo components, responsive styles
50
+ npx zero-query create my-app # http://localhost:3100
51
+
52
+ # Minimal: lightweight 3-page starter
53
+ npx zero-query create my-app --minimal # → http://localhost:3100 (alias: -m)
54
+
55
+ # SSR: Node.js server-side rendering project
56
+ npx zero-query create my-app --ssr # → http://localhost:3000 (alias: -s)
57
+
58
+ # WebRTC: one-page video room backed by zero-server
59
+ npx zero-query create my-app --webrtc-demo # → http://localhost:3000 (alias: -w)
49
60
  ```
50
61
 
51
- Scaffold a new project and start the server:
62
+ That's it. One command scaffolds the project, installs dependencies, starts the server, and opens the browser. To restart later:
52
63
 
53
64
  ```bash
54
- npx zquery create my-app
55
- npx zquery dev my-app
65
+ cd my-app
66
+ npm run dev # or: npm start
56
67
  ```
57
68
 
58
- > **Tip:** Stay in the project root (where `node_modules` lives) instead of `cd`-ing into `my-app`. This keeps `index.d.ts` accessible to your IDE for full type/intellisense support.
69
+ > **Tip:** For the default and minimal variants, you can stay in the project root (where `node_modules` lives) instead of `cd`-ing into `my-app`. This keeps `index.d.ts` accessible to your IDE for full type/intellisense support.
59
70
 
60
- The `create` command generates a ready-to-run project with a sidebar layout, router, multiple components (including folder components with external templates and styles), and responsive styles. Use `--minimal` (or `-m`) to scaffold a lightweight 3-page starter instead. Use `--ssr` (or `-s`) to scaffold a project with a Node.js server-side rendering example. The dev server watches for file changes, hot-swaps CSS in-place, full-reloads on other changes, and handles SPA fallback routing.
71
+ The default scaffold ships a sidebar layout, router, multiple components (including folder components with external templates and styles), and responsive styles. The SSR variant runs a Node.js server that renders pages to HTML strings. The WebRTC variant is wired to [zero-server](https://github.com/tonywied17/zero-server) for signaling + TURN. The dev server watches for file changes, hot-swaps CSS in-place, full-reloads on other changes, and handles SPA fallback routing.
61
72
 
62
73
  #### Error Overlay
63
74
 
@@ -220,9 +220,17 @@ function htmlToMarkdown(html) {
220
220
  md = md.replace(/<div class="file-tree">[\s\S]*?<\/div>(?:\s*<\/div>)*/g, '');
221
221
 
222
222
  // ---- Pass 5: Convert headings ----
223
- md = md.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/g, (_, c) => '\n## ' + unesc(inlineMd(c)).trim() + '\n');
224
- md = md.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/g, (_, c) => '\n### ' + unesc(inlineMd(c)).trim() + '\n');
225
- md = md.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/g, (_, c) => '\n#### ' + unesc(inlineMd(c)).trim() + '\n');
223
+ // Preserve `id` attributes by emitting an explicit HTML anchor before the
224
+ // heading so cross-section duplicate titles (e.g. "Overview") still resolve
225
+ // to unique anchors in the rendered Markdown TOC.
226
+ const headingRepl = (level) => (_, attrs, c) => {
227
+ const idMatch = /\bid="([^"]+)"/.exec(attrs || '');
228
+ const anchor = idMatch ? `<a id="${idMatch[1]}"></a>\n` : '';
229
+ return '\n' + anchor + '#'.repeat(level) + ' ' + unesc(inlineMd(c)).trim() + '\n';
230
+ };
231
+ md = md.replace(/<h2([^>]*)>([\s\S]*?)<\/h2>/g, headingRepl(2));
232
+ md = md.replace(/<h3([^>]*)>([\s\S]*?)<\/h3>/g, headingRepl(3));
233
+ md = md.replace(/<h4([^>]*)>([\s\S]*?)<\/h4>/g, headingRepl(4));
226
234
 
227
235
  // ---- Pass 6: Convert lists ----
228
236
  md = md.replace(/<ul>([\s\S]*?)<\/ul>/g, (_, content) => {
@@ -298,14 +306,19 @@ function slugify(text) {
298
306
  function buildToc(sections) {
299
307
  const lines = [];
300
308
  for (const section of sections) {
301
- // Get the section title from the first <h2> in content
309
+ // Get the section title from the first <h2> in content, preferring an
310
+ // explicit `id` attribute when present so the TOC link is stable and
311
+ // unique across sections.
302
312
  const html = section.content();
303
- const h2Match = html.match(/<h2[^>]*>([\s\S]*?)<\/h2>/);
304
- const title = h2Match ? unesc(inlineMd(h2Match[1])).trim() : section.label;
305
- lines.push(`- [${title}](#${slugify(title)})`);
313
+ const h2Match = html.match(/<h2([^>]*)>([\s\S]*?)<\/h2>/);
314
+ const title = h2Match ? unesc(inlineMd(h2Match[2])).trim() : section.label;
315
+ const h2Id = h2Match && /\bid="([^"]+)"/.exec(h2Match[1] || '');
316
+ const anchor = h2Id ? h2Id[1] : (section.id || slugify(title));
317
+ lines.push(`- [${title}](#${anchor})`);
306
318
  for (const h of section.headings) {
307
319
  const hText = unesc(h.text);
308
- lines.push(` - [${hText}](#${slugify(hText)})`);
320
+ const hAnchor = h.id || slugify(hText);
321
+ lines.push(` - [${hText}](#${hAnchor})`);
309
322
  }
310
323
  }
311
324
  return lines.join('\n');
@@ -1,6 +1,6 @@
1
1
  // cli/commands/create.js - scaffold a new zQuery project
2
2
  //
3
- // Templates live in cli/scaffold/<variant>/ (default or minimal).
3
+ // Templates live in cli/scaffold/<variant>/ (default, minimal, ssr, webrtc).
4
4
  // Reads template files, replaces {{NAME}} with the project name,
5
5
  // and writes them into the target directory.
6
6
 
@@ -25,6 +25,28 @@ function walkDir(dir, prefix = '') {
25
25
  return entries;
26
26
  }
27
27
 
28
+ /**
29
+ * Open the given URL in the user's default browser. Best-effort, silent on
30
+ * failure (the server still prints the URL).
31
+ */
32
+ function openBrowser(url) {
33
+ const cmd = process.platform === 'win32' ? 'start ""'
34
+ : process.platform === 'darwin' ? 'open' : 'xdg-open';
35
+ try { execSync(`${cmd} ${url}`, { stdio: 'ignore' }); } catch { /* ignore */ }
36
+ }
37
+
38
+ /**
39
+ * Spawn a long-running child process, wire SIGINT/SIGTERM to terminate it,
40
+ * and exit when it does. Used by every scaffold variant to launch the dev /
41
+ * SSR / signaling server right after install.
42
+ */
43
+ function runAndExit(cmd, cmdArgs, cwd) {
44
+ const child = spawn(cmd, cmdArgs, { cwd, stdio: 'inherit', shell: process.platform === 'win32' });
45
+ process.on('SIGINT', () => { child.kill(); process.exit(); });
46
+ process.on('SIGTERM', () => { child.kill(); process.exit(); });
47
+ child.on('exit', (code) => process.exit(code || 0));
48
+ }
49
+
28
50
  function createProject(args) {
29
51
  // First positional arg after "create" is the target dir (skip flags)
30
52
  const dirArg = args.slice(1).find(a => !a.startsWith('-'));
@@ -38,7 +60,7 @@ function createProject(args) {
38
60
  : 'default';
39
61
 
40
62
  // Guard: refuse to overwrite existing files
41
- const checkFiles = ['index.html', 'global.css', 'app', 'assets'];
63
+ const checkFiles = ['index.html', 'global.css', 'app', 'assets', 'package.json'];
42
64
  if (variant === 'ssr' || variant === 'webrtc') checkFiles.push('server');
43
65
  const conflicts = checkFiles.filter(f =>
44
66
  fs.existsSync(path.join(target, f))
@@ -76,79 +98,53 @@ function createProject(args) {
76
98
  console.log(` ✓ ${rel}`);
77
99
  }
78
100
 
79
- const devCmd = `npx zquery dev${target !== process.cwd() ? ` ${dirArg}` : ''}`;
80
-
81
101
  // Copy zquery.min.js from the package's pre-built dist into the project root
82
102
  // so file:// previews and the dev/SSR servers all serve the same minified
83
103
  // bundle that ships with the installed zero-query package. The dev server
84
104
  // also intercepts requests for "zquery.min.js" and will fall back to the
85
105
  // package copy if the file is missing, so this is a convenience for direct
86
106
  // file access (Open in Browser, etc.).
107
+ const zqRoot = path.resolve(__dirname, '..', '..');
87
108
  {
88
- const zqRoot = path.resolve(__dirname, '..', '..');
89
- const zqMin = path.join(zqRoot, 'dist', 'zquery.min.js');
109
+ const zqMin = path.join(zqRoot, 'dist', 'zquery.min.js');
90
110
  if (fs.existsSync(zqMin)) {
91
111
  fs.copyFileSync(zqMin, path.join(target, 'zquery.min.js'));
92
112
  console.log(` ✓ zquery.min.js`);
93
113
  }
94
114
  }
95
115
 
96
- if (variant === 'ssr') {
97
- console.log(`\n Installing dependencies...\n`);
98
- // Install zero-query from the same package that provides this CLI
99
- // (works both in local dev and when installed from npm)
100
- const zqRoot = path.resolve(__dirname, '..', '..');
101
- try {
102
- execSync(`npm install "${zqRoot}"`, { cwd: target, stdio: 'inherit' });
103
- } catch {
104
- console.error(`\n ✗ npm install failed. Run it manually:\n\n cd ${dirArg || '.'}\n npm install\n node server/index.js\n`);
105
- process.exit(1);
106
- }
107
-
108
- // Copy zquery.min.js into the project root so the SSR server can serve it
109
- const zqMin = path.join(target, 'node_modules', 'zero-query', 'dist', 'zquery.min.js');
110
- if (fs.existsSync(zqMin)) {
111
- fs.copyFileSync(zqMin, path.join(target, 'zquery.min.js'));
112
- console.log(` ✓ zquery.min.js`);
113
- }
114
-
115
- console.log(`\n Starting SSR server...\n`);
116
- const child = spawn('node', ['server/index.js'], { cwd: target, stdio: 'inherit' });
117
-
118
- setTimeout(() => {
119
- const open = process.platform === 'win32' ? 'start'
120
- : process.platform === 'darwin' ? 'open' : 'xdg-open';
121
- try { execSync(`${open} http://localhost:3000`, { stdio: 'ignore' }); } catch {}
122
- }, 500);
123
-
124
- process.on('SIGINT', () => { child.kill(); process.exit(); });
125
- process.on('SIGTERM', () => { child.kill(); process.exit(); });
126
- child.on('exit', (code) => process.exit(code || 0));
127
- return; // keep process alive for the server
128
- } else if (variant === 'webrtc') {
129
- console.log(`\n Installing dependencies...\n`);
116
+ // ---- Install dependencies (all variants ship a package.json) ----
117
+ console.log(`\n Installing dependencies...\n`);
118
+ try {
130
119
  // Install zero-query from the same package that provides this CLI so
131
- // local-dev and published-npm both work. Dev deps (@zero-server/sdk +
132
- // @zero-server/webrtc) are declared in the scaffold's package.json and
133
- // get installed by the same `npm install`. The server has its own
134
- // install-prompt as a safety net.
135
- const zqRoot = path.resolve(__dirname, '..', '..');
136
- try {
137
- execSync(`npm install "${zqRoot}"`, { cwd: target, stdio: 'inherit' });
138
- execSync(`npm install`, { cwd: target, stdio: 'inherit' });
139
- } catch {
140
- console.error(`\n ✗ npm install failed. Run it manually:\n\n cd ${dirArg || '.'}\n npm install\n npm start\n`);
141
- process.exit(1);
142
- }
120
+ // local-dev and published-npm both work. Any extra devDependencies in
121
+ // the scaffold's package.json (e.g. @zero-server/* for webrtc) come in
122
+ // on the follow-up plain `npm install`.
123
+ execSync(`npm install "${zqRoot}"`, { cwd: target, stdio: 'inherit' });
124
+ execSync(`npm install`, { cwd: target, stdio: 'inherit' });
125
+ } catch {
126
+ console.error(`\n ✗ npm install failed. Run it manually:\n\n cd ${dirArg || '.'}\n npm install\n npm start\n`);
127
+ process.exit(1);
128
+ }
143
129
 
144
- // Refresh zquery.min.js from the installed package (preferred over the
145
- // pre-copied one above so post-install rebuilds win).
130
+ // Refresh zquery.min.js from the freshly installed package (preferred
131
+ // over the pre-copied one above so post-install rebuilds win).
132
+ {
146
133
  const zqMin = path.join(target, 'node_modules', 'zero-query', 'dist', 'zquery.min.js');
147
134
  if (fs.existsSync(zqMin)) {
148
135
  fs.copyFileSync(zqMin, path.join(target, 'zquery.min.js'));
149
- console.log(` ✓ zquery.min.js`);
150
136
  }
137
+ }
151
138
 
139
+ // ---- Launch the right server for the variant ----
140
+ if (variant === 'ssr') {
141
+ console.log(`\n Starting SSR server on http://localhost:3000 ...\n`);
142
+ setTimeout(() => openBrowser('http://localhost:3000'), 500);
143
+ runAndExit('node', ['server/index.js'], target);
144
+ return;
145
+ }
146
+
147
+ if (variant === 'webrtc') {
152
148
  console.log(`
153
149
  Camera, microphone, and screen-share are OFF by default - users opt in
154
150
  from inside the room. Optional env vars:
@@ -157,26 +153,19 @@ function createProject(args) {
157
153
 
158
154
  Starting WebRTC signaling + static server on http://localhost:3000 ...
159
155
  `);
160
-
161
- const child = spawn('node', ['server/index.js'], { cwd: target, stdio: 'inherit' });
162
-
163
- setTimeout(() => {
164
- const open = process.platform === 'win32' ? 'start'
165
- : process.platform === 'darwin' ? 'open' : 'xdg-open';
166
- try { execSync(`${open} http://localhost:3000`, { stdio: 'ignore' }); } catch {}
167
- }, 500);
168
-
169
- process.on('SIGINT', () => { child.kill(); process.exit(); });
170
- process.on('SIGTERM', () => { child.kill(); process.exit(); });
171
- child.on('exit', (code) => process.exit(code || 0));
172
- return; // keep process alive for the server
173
- } else {
174
- console.log(`
175
- Done! Next steps:
176
-
177
- ${devCmd}
178
- `);
156
+ setTimeout(() => openBrowser('http://localhost:3000'), 500);
157
+ runAndExit('node', ['server/index.js'], target);
158
+ return;
179
159
  }
160
+
161
+ // default / minimal: invoke the local zquery CLI's dev command. We point
162
+ // it at this same package's CLI entry so it works regardless of whether
163
+ // node_modules/.bin/zquery resolved a shim correctly (Windows .cmd quirks
164
+ // and all).
165
+ console.log(`\n Starting dev server on http://localhost:3100 ...\n`);
166
+ setTimeout(() => openBrowser('http://localhost:3100'), 800);
167
+ const cliEntry = path.resolve(__dirname, '..', 'index.js');
168
+ runAndExit('node', [cliEntry, 'dev', target], target);
180
169
  }
181
170
 
182
171
  module.exports = createProject;
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "{{NAME}}",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "start": "npx zquery dev",
7
+ "dev": "npx zquery dev",
8
+ "build": "npx zquery build",
9
+ "bundle": "npx zquery bundle"
10
+ },
11
+ "dependencies": {
12
+ "zero-query": "latest"
13
+ }
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "{{NAME}}",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "start": "npx zquery dev",
7
+ "dev": "npx zquery dev",
8
+ "build": "npx zquery build",
9
+ "bundle": "npx zquery bundle"
10
+ },
11
+ "dependencies": {
12
+ "zero-query": "latest"
13
+ }
14
+ }
@@ -2,6 +2,10 @@
2
2
  "name": "{{NAME}}",
3
3
  "private": true,
4
4
  "type": "module",
5
+ "scripts": {
6
+ "start": "node server/index.js",
7
+ "dev": "node server/index.js"
8
+ },
5
9
  "dependencies": {
6
10
  "zero-query": "latest"
7
11
  }