@zappdev/vite 0.1.0 → 0.5.0-alpha.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/package.json +10 -17
- package/src/index.ts +246 -0
- package/README.md +0 -43
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -237
package/package.json
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zappdev/vite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-alpha.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"types": "src/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts"
|
|
9
|
+
},
|
|
8
10
|
"files": [
|
|
9
|
-
"
|
|
11
|
+
"src/"
|
|
10
12
|
],
|
|
11
|
-
"
|
|
12
|
-
"
|
|
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"
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"vite": ">=5.0.0"
|
|
15
|
+
}
|
|
23
16
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zapp Vite Plugin — bundles workers and backend scripts.
|
|
3
|
+
*
|
|
4
|
+
* Discovers `new Worker("./path")` and `new SharedWorker("./path")` patterns,
|
|
5
|
+
* bundles each as a separate entry, outputs to dist/_workers/.
|
|
6
|
+
* Also handles the backend worker convention (src/backend.ts).
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { zappWorkers } from "@zappdev/vite";
|
|
11
|
+
* export default defineConfig({ plugins: [zappWorkers()] });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { Plugin, ViteDevServer } from "vite";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { existsSync } from "node:fs";
|
|
18
|
+
import { mkdir, readdir, stat, readFile } from "node:fs/promises";
|
|
19
|
+
|
|
20
|
+
const WORKER_PATTERN =
|
|
21
|
+
/new\s+(?:SharedWorker|Worker)\s*\(\s*(?:new\s+URL\(\s*["'`](.+?)["'`]\s*,\s*import\.meta\.url\s*\)|["'`](.+?)["'`])/g;
|
|
22
|
+
|
|
23
|
+
interface WorkerEntry {
|
|
24
|
+
/** Original specifier from source: "./worker.ts" */
|
|
25
|
+
specifier: string;
|
|
26
|
+
/** Absolute path to source file */
|
|
27
|
+
sourcePath: string;
|
|
28
|
+
/** Output name: "worker.mjs" */
|
|
29
|
+
outputName: string;
|
|
30
|
+
/** Output URL path: "/_workers/worker.mjs" */
|
|
31
|
+
outputUrl: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Recursively scan for source files. */
|
|
35
|
+
async function scanDir(dir: string): Promise<string[]> {
|
|
36
|
+
const results: string[] = [];
|
|
37
|
+
try {
|
|
38
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "zapp") continue;
|
|
41
|
+
const full = path.join(dir, entry.name);
|
|
42
|
+
if (entry.isDirectory()) {
|
|
43
|
+
results.push(...await scanDir(full));
|
|
44
|
+
} else if (/\.(ts|tsx|js|jsx|mjs)$/.test(entry.name)) {
|
|
45
|
+
results.push(full);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
} catch {}
|
|
49
|
+
return results;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Discover worker entries by scanning source files for new Worker() patterns. */
|
|
53
|
+
async function discoverWorkers(srcDir: string): Promise<WorkerEntry[]> {
|
|
54
|
+
const files = await scanDir(srcDir);
|
|
55
|
+
const found = new Map<string, WorkerEntry>();
|
|
56
|
+
|
|
57
|
+
for (const file of files) {
|
|
58
|
+
const content = await readFile(file, "utf-8");
|
|
59
|
+
let match;
|
|
60
|
+
WORKER_PATTERN.lastIndex = 0;
|
|
61
|
+
while ((match = WORKER_PATTERN.exec(content)) !== null) {
|
|
62
|
+
const spec = match[1] || match[2];
|
|
63
|
+
if (!spec || found.has(spec)) continue;
|
|
64
|
+
const sourcePath = path.resolve(path.dirname(file), spec);
|
|
65
|
+
const baseName = path.basename(spec).replace(/\.[^.]+$/, "");
|
|
66
|
+
found.set(spec, {
|
|
67
|
+
specifier: spec,
|
|
68
|
+
sourcePath,
|
|
69
|
+
outputName: `${baseName}.mjs`,
|
|
70
|
+
outputUrl: `/_workers/${baseName}.mjs`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return [...found.values()];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Bundle a single worker entry using Vite's build API (Rolldown in Vite 8). */
|
|
79
|
+
async function bundleWorker(entry: WorkerEntry, outDir: string, aliases: Record<string, string>): Promise<boolean> {
|
|
80
|
+
try {
|
|
81
|
+
const vite = await import("vite");
|
|
82
|
+
await vite.build({
|
|
83
|
+
configFile: false,
|
|
84
|
+
logLevel: "silent",
|
|
85
|
+
build: {
|
|
86
|
+
outDir,
|
|
87
|
+
emptyOutDir: false,
|
|
88
|
+
minify: true,
|
|
89
|
+
rollupOptions: {
|
|
90
|
+
input: entry.sourcePath,
|
|
91
|
+
output: {
|
|
92
|
+
format: "es",
|
|
93
|
+
entryFileNames: entry.outputName,
|
|
94
|
+
dir: outDir,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
resolve: { alias: aliases },
|
|
99
|
+
});
|
|
100
|
+
return true;
|
|
101
|
+
} catch (e) {
|
|
102
|
+
console.error(`[zapp] worker bundle failed: ${entry.specifier}`, e);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function zappWorkers(): Plugin {
|
|
108
|
+
let root = "";
|
|
109
|
+
let srcDir = "";
|
|
110
|
+
let workers: WorkerEntry[] = [];
|
|
111
|
+
let backendEntry: WorkerEntry | null = null;
|
|
112
|
+
let aliases: Record<string, string> = {};
|
|
113
|
+
let isDev = false;
|
|
114
|
+
let outDir = "";
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
name: "zapp-workers",
|
|
118
|
+
enforce: "pre",
|
|
119
|
+
|
|
120
|
+
configResolved(config) {
|
|
121
|
+
root = config.root;
|
|
122
|
+
srcDir = path.join(root, "src");
|
|
123
|
+
outDir = path.join(root, "dist", "_workers");
|
|
124
|
+
isDev = config.command === "serve";
|
|
125
|
+
|
|
126
|
+
// Extract alias paths for worker bundling
|
|
127
|
+
const resolvedAlias = config.resolve?.alias;
|
|
128
|
+
if (resolvedAlias && typeof resolvedAlias === "object" && !Array.isArray(resolvedAlias)) {
|
|
129
|
+
aliases = resolvedAlias as Record<string, string>;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
async buildStart() {
|
|
134
|
+
// Discover workers from source
|
|
135
|
+
workers = await discoverWorkers(srcDir);
|
|
136
|
+
|
|
137
|
+
// Check for backend worker (convention: src/backend.ts)
|
|
138
|
+
const backendPath = path.join(srcDir, "backend.ts");
|
|
139
|
+
if (existsSync(backendPath)) {
|
|
140
|
+
backendEntry = {
|
|
141
|
+
specifier: "./backend.ts",
|
|
142
|
+
sourcePath: backendPath,
|
|
143
|
+
outputName: "backend.mjs",
|
|
144
|
+
outputUrl: "/_workers/backend.mjs",
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (workers.length > 0) {
|
|
149
|
+
console.log(`[zapp] discovered ${workers.length} worker(s)`);
|
|
150
|
+
}
|
|
151
|
+
if (backendEntry) {
|
|
152
|
+
console.log("[zapp] backend worker: src/backend.ts");
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
// Rewrite new Worker("./worker.ts") → new Worker("/_workers/worker.mjs")
|
|
157
|
+
transform(code, id) {
|
|
158
|
+
if (!id.endsWith(".ts") && !id.endsWith(".tsx") && !id.endsWith(".js") && !id.endsWith(".jsx")) return null;
|
|
159
|
+
if (id.includes("node_modules")) return null;
|
|
160
|
+
|
|
161
|
+
let modified = false;
|
|
162
|
+
let result = code;
|
|
163
|
+
|
|
164
|
+
for (const entry of workers) {
|
|
165
|
+
// Replace the specifier in new Worker("./worker.ts") with the output URL
|
|
166
|
+
const escaped = entry.specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
167
|
+
const regex = new RegExp(`(new\\s+(?:SharedWorker|Worker)\\s*\\(\\s*["'\`])${escaped}(["'\`])`, "g");
|
|
168
|
+
const replaced = result.replace(regex, `$1${entry.outputUrl}$2`);
|
|
169
|
+
if (replaced !== result) {
|
|
170
|
+
result = replaced;
|
|
171
|
+
modified = true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return modified ? { code: result, map: null } : null;
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
// Dev: bundle workers eagerly so they exist on disk before the native
|
|
179
|
+
// binary launches. The backend worker is loaded directly by native code
|
|
180
|
+
// and never goes through HTTP, so a lazy-on-request middleware would never
|
|
181
|
+
// bundle it. Eager bundling also lets native fall back to filesystem.
|
|
182
|
+
//
|
|
183
|
+
// configureServer runs before buildStart, so we re-discover workers here
|
|
184
|
+
// (buildStart's results aren't yet available).
|
|
185
|
+
async configureServer(server: ViteDevServer) {
|
|
186
|
+
const devOutDir = path.join(root, ".zapp", "workers");
|
|
187
|
+
await mkdir(devOutDir, { recursive: true });
|
|
188
|
+
|
|
189
|
+
workers = await discoverWorkers(srcDir);
|
|
190
|
+
const backendPath = path.join(srcDir, "backend.ts");
|
|
191
|
+
if (existsSync(backendPath)) {
|
|
192
|
+
backendEntry = {
|
|
193
|
+
specifier: "./backend.ts",
|
|
194
|
+
sourcePath: backendPath,
|
|
195
|
+
outputName: "backend.mjs",
|
|
196
|
+
outputUrl: "/_workers/backend.mjs",
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const allEntries = [...workers];
|
|
201
|
+
if (backendEntry) allEntries.push(backendEntry);
|
|
202
|
+
|
|
203
|
+
for (const entry of allEntries) {
|
|
204
|
+
const ok = await bundleWorker(entry, devOutDir, aliases);
|
|
205
|
+
if (ok) console.log(`[zapp] dev-bundled worker: ${entry.outputName}`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Still expose middleware so WebView fetch of /_workers/<name>.mjs works
|
|
209
|
+
// (e.g. native engines that pull via dev URL like jsc.m).
|
|
210
|
+
server.middlewares.use(async (req, res, next) => {
|
|
211
|
+
if (!req.url?.startsWith("/_workers/")) return next();
|
|
212
|
+
|
|
213
|
+
const fileName = req.url.slice("/_workers/".length);
|
|
214
|
+
const filePath = path.join(devOutDir, fileName);
|
|
215
|
+
|
|
216
|
+
if (existsSync(filePath)) {
|
|
217
|
+
const content = await readFile(filePath, "utf-8");
|
|
218
|
+
res.setHeader("Content-Type", "application/javascript");
|
|
219
|
+
res.end(content);
|
|
220
|
+
} else {
|
|
221
|
+
res.statusCode = 404;
|
|
222
|
+
res.end("Worker not found");
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
// Prod: bundle all workers to dist/_workers/
|
|
228
|
+
async generateBundle() {
|
|
229
|
+
if (isDev) return;
|
|
230
|
+
|
|
231
|
+
await mkdir(outDir, { recursive: true });
|
|
232
|
+
|
|
233
|
+
const allEntries = [...workers];
|
|
234
|
+
if (backendEntry) allEntries.push(backendEntry);
|
|
235
|
+
|
|
236
|
+
for (const entry of allEntries) {
|
|
237
|
+
const ok = await bundleWorker(entry, outDir, aliases);
|
|
238
|
+
if (ok) {
|
|
239
|
+
console.log(`[zapp] bundled worker: ${entry.outputName}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export default zappWorkers;
|
package/README.md
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
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
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED
|
@@ -1,237 +0,0 @@
|
|
|
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;
|