@zappdev/cli 0.5.0-alpha.1 → 0.5.0-alpha.10

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/dist/config.js ADDED
@@ -0,0 +1,25 @@
1
+ // src/config.ts
2
+ import path from "node:path";
3
+ function defineConfig(config) {
4
+ return config;
5
+ }
6
+ async function loadConfig(root) {
7
+ const configPath = path.join(root, "zapp.config.ts");
8
+ try {
9
+ const mod = await import(configPath);
10
+ const config = typeof mod.default === "function" ? mod.default() : mod.default;
11
+ return {
12
+ ...config,
13
+ assetDir: config.assetDir ?? "./dist"
14
+ };
15
+ } catch {
16
+ return {
17
+ name: path.basename(root),
18
+ assetDir: "./dist"
19
+ };
20
+ }
21
+ }
22
+ export {
23
+ loadConfig,
24
+ defineConfig
25
+ };
package/native/app/app.zc CHANGED
@@ -38,7 +38,12 @@ fn app_get_bootstrap_name() -> string {
38
38
 
39
39
  fn app_get_bootstrap_web_content_inspectable() -> bool {
40
40
  let app = (App*)app_get_active();
41
- if app != NULL { return app.config.webContentInspectable > 0; }
41
+ if app == NULL { return false; }
42
+ match app.config.webContentInspectable {
43
+ ZappInspectable::On => { return true; },
44
+ ZappInspectable::Off => { return false; },
45
+ ZappInspectable::Auto => { return zapp_build_dev_tools_default() > 0; },
46
+ }
42
47
  return false;
43
48
  }
44
49
 
@@ -161,24 +166,45 @@ fn zapp_handle_message_from_window(app_ptr: void*, msg: string, window_id: int)
161
166
 
162
167
  // Service bindings — provided by service/service.zc (imported via router.zc)
163
168
 
169
+ // Whether the webview inspector (devtools) is available.
170
+ // Auto — inherit from build mode (dev → on, prod → off)
171
+ // On — always available (useful for debugging)
172
+ // Off — never available (hardened production)
173
+ enum ZappInspectable {
174
+ Auto,
175
+ On,
176
+ Off,
177
+ }
178
+
164
179
  struct AppConfig {
165
180
  name: string;
166
181
  applicationShouldTerminateAfterLastWindowClosed: bool;
167
- webContentInspectable: int; // -1 = auto (dev → on, prod → off), 0 = off, 1 = on
182
+ webContentInspectable: ZappInspectable;
168
183
  maxWorkers: int;
169
184
  qjsStackSize: int;
170
185
  backend: bool; // enable backend worker (src/backend.ts)
171
186
  }
172
187
 
173
- // Named toggle values — use as AppConfig fields instead of raw int literals.
174
- // webContentInspectable: Inspectable.auto,
175
- // webContentInspectable: Inspectable.on,
176
- // webContentInspectable: Inspectable.off,
177
- struct Inspectable {}
178
- impl Inspectable {
179
- def auto: int = -1;
180
- def off: int = 0;
181
- def on: int = 1;
188
+ // ---------------------------------------------------------------------------
189
+ // Zapp namespace — convenience accessors for framework enums and values.
190
+ //
191
+ // Usage:
192
+ // webContentInspectable: Zapp::inspectable_auto(),
193
+ // webContentInspectable: Zapp::inspectable_on(),
194
+ // webContentInspectable: Zapp::inspectable_off(),
195
+ //
196
+ // These return the proper enum type so you get match exhaustiveness and
197
+ // type checking. The enum constructors also work directly if you prefer:
198
+ // webContentInspectable: ZappInspectable::Auto(),
199
+ //
200
+ // As Zapp adds more framework-level options (log levels, security modes,
201
+ // etc.), new accessors will appear here under the same Zapp:: namespace.
202
+ // ---------------------------------------------------------------------------
203
+ struct Zapp {}
204
+ impl Zapp {
205
+ fn inspectable_auto() -> ZappInspectable { return ZappInspectable::Auto(); }
206
+ fn inspectable_on() -> ZappInspectable { return ZappInspectable::On(); }
207
+ fn inspectable_off() -> ZappInspectable { return ZappInspectable::Off(); }
182
208
  }
183
209
 
184
210
  struct App {
@@ -195,13 +221,12 @@ struct App {
195
221
  impl App {
196
222
  fn new(config: AppConfig) -> App {
197
223
  let wm = WindowManager::new();
198
- if config.webContentInspectable > 0 {
199
- wm.webContentInspectable = true;
200
- } else if config.webContentInspectable == 0 {
201
- wm.webContentInspectable = false;
202
- } else {
203
- // -1 = inherit from build mode (dev → on, prod → off)
204
- wm.webContentInspectable = zapp_build_dev_tools_default() > 0;
224
+ match config.webContentInspectable {
225
+ ZappInspectable::On => { wm.webContentInspectable = true; },
226
+ ZappInspectable::Off => { wm.webContentInspectable = false; },
227
+ ZappInspectable::Auto => {
228
+ wm.webContentInspectable = zapp_build_dev_tools_default() > 0;
229
+ },
205
230
  }
206
231
 
207
232
  platform_init(config.name);
package/native/build.zc CHANGED
@@ -53,9 +53,10 @@ fn main() -> int {
53
53
  let config = AppConfig{
54
54
  name: "Zapp v2 Test",
55
55
  applicationShouldTerminateAfterLastWindowClosed: true,
56
- webContentInspectable: 1,
56
+ webContentInspectable: Zapp::inspectable_on(),
57
57
  maxWorkers: 0,
58
58
  qjsStackSize: 0,
59
+ backend: false,
59
60
  };
60
61
  let app = App::new(config);
61
62
 
package/package.json CHANGED
@@ -1,19 +1,26 @@
1
1
  {
2
2
  "name": "@zappdev/cli",
3
- "version": "0.5.0-alpha.1",
3
+ "version": "0.5.0-alpha.10",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "zapp": "./src/zapp-cli.ts"
7
7
  },
8
+ "exports": {
9
+ "./config": {
10
+ "types": "./src/config.ts",
11
+ "default": "./dist/config.js"
12
+ }
13
+ },
8
14
  "files": [
9
15
  "src/",
16
+ "dist/",
10
17
  "native/",
11
18
  "bootstrap/",
12
19
  "assets/",
13
20
  "vendor/webview2/"
14
21
  ],
15
22
  "scripts": {
16
- "prepack": "cp -r ../native ./native && cp -r ../bootstrap ./bootstrap && cp -r ../assets ./assets && mkdir -p ./vendor && cp -r ../vendor/webview2 ./vendor/webview2",
23
+ "prepack": "bun build src/config.ts --outdir dist --format esm --target node && cp -r ../native ./native && cp -r ../bootstrap ./bootstrap && cp -r ../assets ./assets && mkdir -p ./vendor && cp -r ../vendor/webview2 ./vendor/webview2",
17
24
  "postpack": "rm -rf ./native ./bootstrap ./assets ./vendor"
18
25
  },
19
26
  "dependencies": {
package/src/config.ts CHANGED
@@ -26,6 +26,7 @@ export interface ZappConfig {
26
26
  version?: string;
27
27
  assetDir?: string; // Default: "./dist" (Vite), configurable for static sites
28
28
  devPort?: number; // Default: 5173
29
+ backend?: string; // Path to backend worker source (e.g. "src/server.ts"). Omit for no backend.
29
30
  deepLinkSchemes?: string[]; // e.g. ["myapp"] → registers myapp:// URL scheme
30
31
  macos?: MacOSConfig;
31
32
  security?: SecurityConfig;
package/src/init.ts CHANGED
@@ -21,13 +21,15 @@ export async function runInit(opts: InitOptions) {
21
21
  process.exit(1);
22
22
  }
23
23
 
24
- // 1. Create Vite project
24
+ // 1. Scaffold Vite project files (no install, no dev server).
25
+ // create-vite has an --immediate flag that auto-installs + starts the dev
26
+ // server, and an interactive prompt that does the same if confirmed.
27
+ // --no-interactive skips the prompt and defaults to no install.
25
28
  process.stdout.write(`[zapp] creating ${name} with template ${template}...\n`);
26
- const viteProc = Bun.spawn(["bun", "create", "vite", name, "--template", template], {
27
- cwd: root,
28
- stdout: "inherit",
29
- stderr: "inherit",
30
- });
29
+ const viteProc = Bun.spawn(
30
+ ["bunx", "create-vite@latest", name, "--template", template, "--no-interactive"],
31
+ { cwd: root, stdout: "inherit", stderr: "inherit" },
32
+ );
31
33
  if ((await viteProc.exited) !== 0) {
32
34
  process.stderr.write("[zapp] vite scaffold failed\n");
33
35
  process.exit(1);
@@ -51,7 +53,7 @@ fn run_app() -> int {
51
53
  let config = AppConfig{
52
54
  name: "${name}",
53
55
  applicationShouldTerminateAfterLastWindowClosed: true,
54
- webContentInspectable: Inspectable.auto,
56
+ webContentInspectable: Zapp::inspectable_auto(),
55
57
  maxWorkers: 0,
56
58
  qjsStackSize: 0,
57
59
  backend: false,
@@ -100,13 +102,15 @@ fn main() -> int {
100
102
  }
101
103
  `);
102
104
 
103
- // 3. Add zapp.config.ts
105
+ // 3. Add zapp.config.ts — typed via defineConfig for autocomplete
104
106
  const identifier = `com.zapp.${name.toLowerCase().replace(/[^a-z0-9]/g, "")}`;
105
- await Bun.write(path.join(projectDir, "zapp.config.ts"), `export default {
107
+ await Bun.write(path.join(projectDir, "zapp.config.ts"), `import { defineConfig } from "@zappdev/cli/config";
108
+
109
+ export default defineConfig({
106
110
  name: "${name}",
107
111
  identifier: "${identifier}",
108
112
  version: "0.1.0",
109
- };
113
+ });
110
114
  `);
111
115
 
112
116
  // 4. Update package.json — add deps and scripts
@@ -117,31 +121,72 @@ fn main() -> int {
117
121
  ...pkgObj.scripts,
118
122
  "dev": "zapp dev",
119
123
  "build": "zapp build",
124
+ "package": "zapp package",
120
125
  "generate": "zapp generate",
121
126
  };
122
127
 
123
128
  pkgObj.dependencies = {
124
129
  ...(pkgObj.dependencies ?? {}),
125
- "@zappdev/runtime": "^0.5.0",
130
+ "@zappdev/runtime": "^0.5.0-alpha.0",
126
131
  };
127
132
  pkgObj.devDependencies = {
128
133
  ...(pkgObj.devDependencies ?? {}),
129
- "@zappdev/cli": "^0.5.0",
130
- "@zappdev/vite": "^0.5.0",
134
+ "@zappdev/cli": "^0.5.0-alpha.0",
135
+ "@zappdev/vite": "^0.5.0-alpha.0",
131
136
  };
132
137
 
133
138
  await Bun.write(pkgPath, JSON.stringify(pkgObj, null, 2));
134
139
 
135
- // 5. Overwrite vite.config.ts with Zapp plugin wiring
136
- // The Vite scaffold creates a minimal one, but we need the zappWorkers()
137
- // plugin for worker bundling support.
138
- await Bun.write(path.join(projectDir, "vite.config.ts"), `import { defineConfig } from "vite";
140
+ // 5. Inject zappWorkers() into the template's vite.config.ts
141
+ // Templates like svelte-ts ship their own vite.config.ts with framework
142
+ // plugins (e.g. svelte()). We must preserve those and append ours.
143
+ const viteConfigPath = path.join(projectDir, "vite.config.ts");
144
+ // Also check .js — some templates use vite.config.js
145
+ const viteConfigJsPath = path.join(projectDir, "vite.config.js");
146
+ const configPath = existsSync(viteConfigPath) ? viteConfigPath
147
+ : existsSync(viteConfigJsPath) ? viteConfigJsPath
148
+ : null;
149
+
150
+ if (configPath) {
151
+ let viteConfig = await Bun.file(configPath).text();
152
+
153
+ // Add our imports at the top (after existing imports)
154
+ const importLines = [
155
+ `import { zappWorkers } from "@zappdev/vite";`,
156
+ `import zappConfig from "./zapp.config";`,
157
+ ];
158
+ for (const importLine of importLines) {
159
+ const pkg = importLine.match(/from ["'](.+?)["']/)?.[1] ?? "";
160
+ if (viteConfig.includes(pkg)) continue;
161
+ const lastImportIdx = viteConfig.lastIndexOf("\nimport ");
162
+ if (lastImportIdx >= 0) {
163
+ const endOfLine = viteConfig.indexOf("\n", lastImportIdx + 1);
164
+ viteConfig = viteConfig.slice(0, endOfLine + 1) + importLine + "\n" + viteConfig.slice(endOfLine + 1);
165
+ } else {
166
+ viteConfig = importLine + "\n" + viteConfig;
167
+ }
168
+ }
169
+
170
+ // Append zappWorkers() with config to the plugins array
171
+ if (!viteConfig.includes("zappWorkers(")) {
172
+ viteConfig = viteConfig.replace(
173
+ /plugins:\s*\[/,
174
+ "plugins: [zappWorkers({ backend: zappConfig.backend }), "
175
+ );
176
+ }
177
+
178
+ await Bun.write(configPath, viteConfig);
179
+ } else {
180
+ // No vite.config found — create a minimal one
181
+ await Bun.write(viteConfigPath, `import { defineConfig } from "vite";
139
182
  import { zappWorkers } from "@zappdev/vite";
183
+ import zappConfig from "./zapp.config";
140
184
 
141
185
  export default defineConfig({
142
- plugins: [zappWorkers()],
186
+ plugins: [zappWorkers({ backend: zappConfig.backend })],
143
187
  });
144
188
  `);
189
+ }
145
190
 
146
191
  // 6. Add .zapp/ to .gitignore
147
192
  const gitignorePath = path.join(projectDir, ".gitignore");
@@ -158,6 +203,7 @@ export default defineConfig({
158
203
  process.stdout.write(` Then add to your entry file (e.g. src/main.ts):\n\n`);
159
204
  process.stdout.write(` import { Window, WindowEvent, Services } from "@zappdev/runtime";\n\n`);
160
205
  process.stdout.write(` Run:\n`);
161
- process.stdout.write(` zapp dev # development with Vite HMR\n`);
162
- process.stdout.write(` zapp build # production build\n\n`);
206
+ process.stdout.write(` bun run dev # development with Vite HMR\n`);
207
+ process.stdout.write(` bun run build # production build\n`);
208
+ process.stdout.write(` bun run package # .app bundle (macOS)\n\n`);
163
209
  }
package/src/native.ts CHANGED
@@ -103,6 +103,8 @@ export async function compileNative(opts: CompileOptions): Promise<void> {
103
103
  const { generatePlatformConfig } = await import("./build-config");
104
104
  const platformFile = await generatePlatformConfig(root);
105
105
 
106
+ const verbose = process.argv.includes("--verbose") || process.argv.includes("-v");
107
+
106
108
  const zcArgs = [
107
109
  "build",
108
110
  buildFile,
@@ -112,21 +114,39 @@ export async function compileNative(opts: CompileOptions): Promise<void> {
112
114
  ...(assetsFile ? [assetsFile] : []),
113
115
  "-I", nativeDir,
114
116
  "-o", output,
117
+ // Suppress C compiler warnings by default. The framework and zc stdlib
118
+ // generate ~200 warnings (parentheses-equality, incompatible-pointer-types,
119
+ // etc.) that are pure noise for end users and bury any actual error.
120
+ // Pass --verbose to see them.
121
+ ...(verbose ? [] : ["-w"]),
115
122
  ];
116
123
 
117
- // Size optimizations deferred for v2 baseline.
118
- // When re-introduced: -Oz, -flto, --no-debug, strip
119
-
120
- // Debug: uncomment to see zc invocation
121
- // process.stderr.write(`[zapp] zc ${zcArgs.join(" ")}\n`);
122
124
  const proc = Bun.spawn(["zc", ...zcArgs], {
123
125
  cwd: root,
124
- stdout: "inherit",
125
- stderr: "inherit",
126
+ stdout: verbose ? "inherit" : "pipe",
127
+ stderr: "pipe",
126
128
  });
129
+
130
+ const [stdoutText, stderrText] = await Promise.all([
131
+ verbose ? "" : new Response(proc.stdout).text(),
132
+ new Response(proc.stderr).text(),
133
+ ]);
134
+
127
135
  const exitCode = await proc.exited;
128
136
  if (exitCode !== 0) {
129
- throw new Error(`[zapp] zc compilation failed (exit ${exitCode})`);
137
+ // Show only error lines (not warnings) unless verbose.
138
+ if (!verbose && stderrText) {
139
+ const errors = stderrText.split("\n").filter(
140
+ line => line.includes("error:") || line.includes("error :") || line.startsWith(" ")
141
+ );
142
+ if (errors.length > 0) {
143
+ process.stderr.write(errors.join("\n") + "\n");
144
+ } else {
145
+ // Fallback: show everything if we couldn't filter.
146
+ process.stderr.write(stderrText);
147
+ }
148
+ }
149
+ throw new Error(`[zapp] compilation failed (exit ${exitCode}). Run with --verbose for full output.`);
130
150
  }
131
151
 
132
152
  // Strip deferred for v2 baseline
package/src/workers.ts CHANGED
@@ -45,7 +45,7 @@ async function discoverWorkers(srcDir: string): Promise<WorkerEntry[]> {
45
45
  return [...found.entries()].map(([entryPath, specifier]) => ({ entryPath, specifier }));
46
46
  }
47
47
 
48
- export async function bundleWorkers(root: string): Promise<number> {
48
+ export async function bundleWorkers(root: string, backendConfig?: string): Promise<number> {
49
49
  const srcDir = path.join(root, "src");
50
50
  const outDir = path.join(root, ".zapp", "workers");
51
51
  await mkdir(outDir, { recursive: true });
@@ -81,8 +81,10 @@ export async function bundleWorkers(root: string): Promise<number> {
81
81
  }
82
82
  }
83
83
 
84
- // Backend worker — convention: src/backend.ts
85
- const backendPath = path.join(root, "src", "backend.ts");
84
+ // Backend worker — from config or fall back to src/backend.ts convention
85
+ const backendPath = backendConfig
86
+ ? path.resolve(root, backendConfig)
87
+ : path.join(root, "src", "backend.ts");
86
88
  try {
87
89
  await stat(backendPath);
88
90
  const outPath = path.join(outDir, "backend.mjs");
@@ -95,12 +97,12 @@ export async function bundleWorkers(root: string): Promise<number> {
95
97
  minify: false,
96
98
  });
97
99
  if (result.success) {
98
- process.stdout.write("[zapp] backend worker detected: src/backend.ts\n");
100
+ process.stdout.write(`[zapp] backend worker: ${path.relative(root, backendPath)}\n`);
99
101
  } else {
100
102
  process.stderr.write("[zapp] backend bundle failed\n");
101
103
  }
102
104
  } catch {
103
- // No backend.ts — that's fine, backend is optional
105
+ // No backend file — that's fine, backend is optional
104
106
  }
105
107
 
106
108
  return workers.length;