@zappdev/vite 0.1.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 ADDED
@@ -0,0 +1,43 @@
1
+ # @zapp/vite
2
+
3
+ Vite plugin for Zapp desktop apps. Handles worker discovery, source bundling, and output configuration so your Vite-based frontend integrates seamlessly with the Zapp native runtime.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ bun add -D @zapp/vite
9
+ ```
10
+
11
+ ```sh
12
+ npm install -D @zapp/vite
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```ts
18
+ // vite.config.ts
19
+ import { defineConfig } from "vite";
20
+ import zapp from "@zapp/vite";
21
+
22
+ export default defineConfig({
23
+ plugins: [
24
+ zapp({
25
+ outDir: "dist",
26
+ sourceRoot: "src",
27
+ minify: true,
28
+ }),
29
+ ],
30
+ });
31
+ ```
32
+
33
+ ## Options
34
+
35
+ | Option | Type | Description |
36
+ | ------------ | --------- | ---------------------------------- |
37
+ | `outDir` | `string` | Output directory for bundled files |
38
+ | `sourceRoot` | `string` | Root directory of source files |
39
+ | `minify` | `boolean` | Enable minification |
40
+
41
+ ## License
42
+
43
+ MIT
@@ -0,0 +1,8 @@
1
+ import type { Plugin } from "vite";
2
+ export interface ZappOptions {
3
+ outDir?: string;
4
+ sourceRoot?: string;
5
+ minify?: boolean;
6
+ }
7
+ export declare const zapp: (options?: ZappOptions) => Plugin;
8
+ export default zapp;
package/dist/index.js ADDED
@@ -0,0 +1,237 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ const WORKER_PATTERN = /new\s+(?:SharedWorker|Worker)\s*\(\s*(?:new\s+URL\(\s*["'`](.+?)["'`]\s*,\s*import\.meta\.url\s*\)|["'`](.+?)["'`])/g;
4
+ const scanSourceFiles = async (rootDir, dir = rootDir, out = []) => {
5
+ const entries = await fs.readdir(dir, { withFileTypes: true });
6
+ for (const entry of entries) {
7
+ if (entry.name.startsWith("."))
8
+ continue;
9
+ const full = path.join(dir, entry.name);
10
+ if (entry.isDirectory()) {
11
+ if (entry.name === "node_modules")
12
+ continue;
13
+ await scanSourceFiles(rootDir, full, out);
14
+ continue;
15
+ }
16
+ if (!/\.(ts|tsx|js|jsx|mjs|cjs)$/.test(entry.name))
17
+ continue;
18
+ out.push(full);
19
+ }
20
+ return out;
21
+ };
22
+ const discoverWorkerEntries = async (srcRoot) => {
23
+ const sourceFiles = await scanSourceFiles(srcRoot);
24
+ const found = new Map();
25
+ for (const file of sourceFiles) {
26
+ const content = await fs.readFile(file, "utf8");
27
+ let match;
28
+ while ((match = WORKER_PATTERN.exec(content)) != null) {
29
+ const spec = match[1] ?? match[2];
30
+ if (!spec || !/\.(ts|tsx|js|jsx|mjs)$/.test(spec))
31
+ continue;
32
+ const entryPath = path.resolve(path.dirname(file), spec);
33
+ found.set(entryPath, spec);
34
+ }
35
+ }
36
+ return [...found.entries()].map(([entryPath, sourceSpec]) => ({ entryPath, sourceSpec }));
37
+ };
38
+ const normalizeOutName = (entryPath, used) => {
39
+ const stem = path.basename(entryPath).replace(/\.[^.]+$/, "");
40
+ let candidate = `${stem}.mjs`;
41
+ let suffix = 1;
42
+ while (used.has(candidate)) {
43
+ candidate = `${stem}-${suffix}.mjs`;
44
+ suffix += 1;
45
+ }
46
+ used.add(candidate);
47
+ return candidate;
48
+ };
49
+ const writeIfChanged = async (filePath, content) => {
50
+ try {
51
+ const existing = await fs.readFile(filePath, "utf8");
52
+ if (existing === content)
53
+ return false;
54
+ }
55
+ catch { }
56
+ await fs.writeFile(filePath, content);
57
+ return true;
58
+ };
59
+ export const zapp = (options = {}) => {
60
+ const configuredOutDir = options.outDir ?? ".zapp/workers";
61
+ const sourceRoot = options.sourceRoot ?? "src";
62
+ const minify = options.minify ?? false;
63
+ let root = process.cwd();
64
+ let devServer = false;
65
+ let viteAliases = {};
66
+ let timer = null;
67
+ let building = false;
68
+ const hasBunBuild = typeof globalThis.Bun !== "undefined" &&
69
+ globalThis.Bun != null &&
70
+ typeof globalThis.Bun.build === "function";
71
+ const getOutDir = () => path.resolve(root, configuredOutDir);
72
+ const bundleWorker = async (entryPath, outFile) => {
73
+ const tmpFile = outFile + ".tmp";
74
+ if (hasBunBuild) {
75
+ const result = await globalThis.Bun.build({
76
+ entrypoints: [entryPath],
77
+ outdir: path.dirname(tmpFile),
78
+ naming: path.basename(tmpFile),
79
+ target: "browser",
80
+ format: "esm",
81
+ sourcemap: devServer ? "inline" : "none",
82
+ minify: devServer ? false : minify,
83
+ plugins: [{
84
+ name: "zapp-alias",
85
+ setup(build) {
86
+ for (const [find, replacement] of Object.entries(viteAliases)) {
87
+ build.onResolve({ filter: new RegExp(`^${find}$`) }, () => ({ path: replacement }));
88
+ build.onResolve({ filter: new RegExp(`^${find}/(.*)`) }, (args) => ({
89
+ path: path.join(replacement, args.path.slice(find.length + 1))
90
+ }));
91
+ }
92
+ }
93
+ }],
94
+ });
95
+ if (!result.success) {
96
+ const lines = (result.logs ?? [])
97
+ .map((log) => log?.message)
98
+ .filter(Boolean)
99
+ .join("\n");
100
+ throw new Error(lines || `bun build failed for ${entryPath}`);
101
+ }
102
+ }
103
+ else {
104
+ const { build: esbuild } = await import("esbuild");
105
+ await esbuild({
106
+ entryPoints: [entryPath],
107
+ bundle: true,
108
+ format: "esm",
109
+ platform: "browser",
110
+ target: "es2022",
111
+ sourcemap: devServer ? "inline" : false,
112
+ minify: devServer ? false : minify,
113
+ outfile: tmpFile,
114
+ alias: viteAliases,
115
+ });
116
+ }
117
+ try {
118
+ const built = await fs.readFile(tmpFile, "utf8");
119
+ await writeIfChanged(outFile, built);
120
+ }
121
+ finally {
122
+ await fs.unlink(tmpFile).catch(() => { });
123
+ }
124
+ };
125
+ const buildWorkers = async () => {
126
+ if (building)
127
+ return;
128
+ building = true;
129
+ try {
130
+ const srcRoot = path.resolve(root, sourceRoot);
131
+ const outDir = getOutDir();
132
+ await fs.mkdir(outDir, { recursive: true });
133
+ const entries = await discoverWorkerEntries(srcRoot);
134
+ const usedNames = new Set();
135
+ const manifest = {};
136
+ for (const { entryPath, sourceSpec } of entries) {
137
+ const outName = normalizeOutName(entryPath, usedNames);
138
+ const outFile = path.join(outDir, outName);
139
+ await bundleWorker(entryPath, outFile);
140
+ manifest[sourceSpec] = `/zapp-workers/${outName}`;
141
+ manifest[path.basename(sourceSpec)] = `/zapp-workers/${outName}`;
142
+ }
143
+ const manifestContent = JSON.stringify({ v: 1, workers: manifest }, null, 2);
144
+ await writeIfChanged(path.join(outDir, "manifest.json"), manifestContent);
145
+ return manifest;
146
+ }
147
+ finally {
148
+ building = false;
149
+ }
150
+ };
151
+ return {
152
+ name: "zapp-workers",
153
+ configResolved(config) {
154
+ root = config.root;
155
+ devServer = config.command === "serve";
156
+ if (Array.isArray(config.resolve?.alias)) {
157
+ for (const a of config.resolve.alias) {
158
+ if (typeof a.find === "string" && typeof a.replacement === "string") {
159
+ viteAliases[a.find] = a.replacement;
160
+ }
161
+ }
162
+ }
163
+ else if (config.resolve?.alias) {
164
+ Object.assign(viteAliases, config.resolve.alias);
165
+ }
166
+ },
167
+ async buildStart() {
168
+ await buildWorkers();
169
+ },
170
+ async transformIndexHtml(html) {
171
+ const outDir = getOutDir();
172
+ const manifestPath = path.join(outDir, "manifest.json");
173
+ let workers = {};
174
+ try {
175
+ const raw = await fs.readFile(manifestPath, "utf8");
176
+ workers = JSON.parse(raw).workers ?? {};
177
+ }
178
+ catch {
179
+ workers = {};
180
+ }
181
+ const serialized = JSON.stringify(workers).replace(/</g, "\\u003c");
182
+ const script = `<script>(function(){globalThis[Symbol.for('zapp.workerManifest')]=${serialized};})();</script>`;
183
+ return html.includes("</head>") ? html.replace("</head>", `${script}</head>`) : `${script}${html}`;
184
+ },
185
+ configureServer(server) {
186
+ devServer = true;
187
+ const outDir = getOutDir();
188
+ const srcRoot = path.resolve(root, sourceRoot);
189
+ const srcRootNorm = srcRoot.replace(/\\/g, "/");
190
+ server.middlewares.use((req, res, next) => {
191
+ const url = req.url ?? "";
192
+ if (!url.startsWith("/zapp-workers/"))
193
+ return next();
194
+ const fileName = url.slice("/zapp-workers/".length).split("?")[0];
195
+ if (!fileName)
196
+ return next();
197
+ const filePath = path.join(outDir, fileName);
198
+ fs.readFile(filePath, "utf8").then((content) => {
199
+ res.setHeader("Content-Type", "application/javascript; charset=utf-8");
200
+ res.setHeader("Cache-Control", "no-cache");
201
+ res.end(content);
202
+ }).catch(() => next());
203
+ });
204
+ const rebuild = (filePath) => {
205
+ const norm = path.resolve(filePath).replace(/\\/g, "/");
206
+ if (!norm.startsWith(srcRootNorm + "/"))
207
+ return;
208
+ if (timer)
209
+ clearTimeout(timer);
210
+ timer = setTimeout(() => {
211
+ buildWorkers().catch((error) => {
212
+ server.config.logger.error(`[zapp-workers] ${error.message}`);
213
+ });
214
+ }, 200);
215
+ };
216
+ server.watcher.on("add", rebuild);
217
+ server.watcher.on("change", rebuild);
218
+ server.watcher.on("unlink", rebuild);
219
+ },
220
+ async writeBundle(outputOptions) {
221
+ if (devServer)
222
+ return;
223
+ const workerDir = getOutDir();
224
+ const distDir = outputOptions.dir ?? path.join(root, "dist");
225
+ const workerDistDir = path.join(distDir, "zapp-workers");
226
+ await fs.mkdir(workerDistDir, { recursive: true });
227
+ try {
228
+ const files = await fs.readdir(workerDir);
229
+ for (const file of files) {
230
+ await fs.copyFile(path.join(workerDir, file), path.join(workerDistDir, file));
231
+ }
232
+ }
233
+ catch { }
234
+ },
235
+ };
236
+ };
237
+ export default zapp;
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@zappdev/vite",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Vite plugin for Zapp desktop apps",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist/"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc"
13
+ },
14
+ "dependencies": {
15
+ "esbuild": "^0.25.10"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^22.0.0",
19
+ "typescript": "^5.0.0",
20
+ "vite": "^6.0.0"
21
+ },
22
+ "license": "MIT"
23
+ }