@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 +25 -0
- package/native/app/app.zc +43 -18
- package/native/build.zc +2 -1
- package/package.json +9 -2
- package/src/config.ts +1 -0
- package/src/init.ts +66 -20
- package/src/native.ts +28 -8
- package/src/workers.ts +7 -5
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
|
|
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:
|
|
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
|
-
//
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
199
|
-
wm.webContentInspectable = true;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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:
|
|
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.
|
|
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.
|
|
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(
|
|
27
|
-
|
|
28
|
-
stdout: "inherit",
|
|
29
|
-
|
|
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:
|
|
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"), `
|
|
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.
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
|
|
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(`
|
|
162
|
-
process.stdout.write(`
|
|
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: "
|
|
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
|
-
|
|
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 —
|
|
85
|
-
const backendPath =
|
|
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(
|
|
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
|
|
105
|
+
// No backend file — that's fine, backend is optional
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
return workers.length;
|