-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathesbuild.config.mjs
More file actions
261 lines (220 loc) · 7.89 KB
/
esbuild.config.mjs
File metadata and controls
261 lines (220 loc) · 7.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/**
* ScriptVault — esbuild build configuration
*
* Replaces build-background.sh with a cross-platform Node.js build.
* Concatenates background source modules in the correct dependency order
* and optionally copies Monaco editor files for local bundling.
*
* Usage:
* node esbuild.config.mjs # full build (background + monaco)
* node esbuild.config.mjs --bg-only # background.js only
* node esbuild.config.mjs --monaco-only # copy monaco only
* node esbuild.config.mjs --watch # rebuild background.js on changes
* node esbuild.config.mjs --prod # minified production build
*/
import { readFileSync, writeFileSync, mkdirSync, cpSync, existsSync, readdirSync } from "node:fs";
import { join, resolve } from "node:path";
import { context, build } from "esbuild";
const ROOT = resolve(import.meta.dirname || ".");
const args = process.argv.slice(2);
const bgOnly = args.includes("--bg-only");
const monacoOnly = args.includes("--monaco-only");
const watchMode = args.includes("--watch");
const production = args.includes("--prod");
const typeCheck = args.includes("--typecheck");
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function readVersion() {
const manifest = JSON.parse(readFileSync(join(ROOT, "manifest.json"), "utf-8"));
return manifest.version;
}
function readFile(rel) {
return readFileSync(join(ROOT, rel), "utf-8");
}
function readJson(rel) {
return JSON.parse(readFile(rel));
}
/**
* Return sorted .js filenames inside a directory (alphabetical, matching
* the glob expansion order used by the original bash build).
*/
function jsFilesIn(dir) {
const abs = join(ROOT, dir);
if (!existsSync(abs)) return [];
return readdirSync(abs)
.filter((f) => f.endsWith(".js"))
.sort()
.map((f) => `${dir}/${f}`);
}
// ---------------------------------------------------------------------------
// Build background.js — concatenation approach
// ---------------------------------------------------------------------------
async function buildBackground() {
const version = readVersion();
const settingsDefaults = readJson("src/config/settings-defaults.json");
console.log(`Building background.js v${version}${production ? " (production)" : ""}...`);
const separator = "\n";
// Ordered list — matches build-background.sh exactly
const parts = [
// Banner
[
`// ScriptVault v${version} - Background Service Worker`,
"// Comprehensive userscript manager with cloud sync and auto-updates",
"// NOTE: This file is built from source modules. Edit the individual files in",
"// shared/, modules/, and lib/, then run `npm run build` to regenerate.",
"",
].join("\n"),
readFile("shared/utils.js"),
[
"// ============================================================================",
"// SHARED SETTINGS DEFAULTS",
"// Generated from src/config/settings-defaults.json",
"// ============================================================================",
`const SCRIPTVAULT_SETTINGS_DEFAULTS = ${JSON.stringify(settingsDefaults, null, 2)};`,
"",
].join("\n"),
readFile("lib/fflate.js"),
readFile("modules/sync-providers.js"),
readFile("modules/i18n.js"),
[
"// ============================================================================",
"// END INLINED MODULES",
"// ============================================================================",
"",
].join("\n"),
readFile("modules/storage.js"),
readFile("modules/xhr.js"),
readFile("modules/resources.js"),
// v2.0 modules (conditionally included if they exist)
...["npm-resolve", "error-log", "notifications", "sync-easycloud",
"backup-scheduler", "userstyles", "public-api", "migration", "quota-manager"]
.map(m => { try { return readFile(`modules/${m}.js`); } catch { return ""; } })
.filter(Boolean),
// bg/*.js — alphabetical order
...jsFilesIn("bg").map((f) => readFile(f)),
readFile("background.core.js"),
];
let code = parts.join(separator);
if (production) {
// Use esbuild to minify the concatenated bundle
const result = await build({
stdin: { contents: code, loader: "js" },
write: false,
minify: true,
target: "chrome120",
format: "iife",
});
code = result.outputFiles[0].text;
}
const outPath = join(ROOT, "background.js");
writeFileSync(outPath, code, "utf-8");
const lines = code.split("\n").length;
console.log(`Done: background.js (${lines} lines)`);
}
// ---------------------------------------------------------------------------
// Copy Monaco editor from node_modules to lib/monaco
// ---------------------------------------------------------------------------
function copyMonaco() {
const src = join(ROOT, "node_modules", "monaco-editor", "min");
const dest = join(ROOT, "lib", "monaco");
if (!existsSync(src)) {
console.warn("Warning: monaco-editor not found in node_modules. Run `npm install` first.");
return;
}
console.log("Copying Monaco editor to lib/monaco/...");
mkdirSync(dest, { recursive: true });
cpSync(src, dest, { recursive: true, force: true });
console.log("Done: lib/monaco/ updated.");
}
// ---------------------------------------------------------------------------
// Watch mode
// ---------------------------------------------------------------------------
async function startWatch() {
const version = readVersion();
console.log(`Watching for changes (v${version})...`);
// Directories to watch for changes
const watchDirs = ["shared", "modules", "lib", "bg"];
const watchFiles = ["background.core.js", "manifest.json"];
// Use esbuild's context for efficient rebuilds via a dummy entrypoint
// that triggers our concat build
const ctx = await context({
entryPoints: [],
write: false,
logLevel: "silent",
plugins: [
{
name: "scriptvault-watch",
setup(pluginBuild) {
// Watch source files for changes
pluginBuild.onStart(() => {
buildBackground().catch(console.error);
});
},
},
],
});
// We do a manual FS poll approach since we are concatenating, not bundling
const { watch: fsWatch } = await import("node:fs");
const rebuild = debounce(() => {
buildBackground().catch(console.error);
}, 200);
for (const dir of watchDirs) {
const abs = join(ROOT, dir);
if (existsSync(abs)) {
fsWatch(abs, { recursive: true }, rebuild);
}
}
for (const file of watchFiles) {
const abs = join(ROOT, file);
if (existsSync(abs)) {
fsWatch(abs, rebuild);
}
}
// Initial build
await buildBackground();
console.log("Watching for changes. Press Ctrl+C to stop.");
}
function debounce(fn, ms) {
let timer;
return (...a) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...a), ms);
};
}
// ---------------------------------------------------------------------------
// TypeScript type-check (runs tsc --noEmit)
// ---------------------------------------------------------------------------
async function runTypeCheck() {
const { execSync } = await import("node:child_process");
console.log("Running TypeScript type-check...");
try {
execSync("npx tsc --noEmit", { cwd: ROOT, stdio: "inherit" });
console.log("Type-check passed.");
} catch {
console.error("Type-check failed.");
process.exit(1);
}
}
// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
async function main() {
if (typeCheck) {
await runTypeCheck();
}
if (watchMode) {
await startWatch();
return;
}
if (!monacoOnly) {
await buildBackground();
}
if (!bgOnly) {
copyMonaco();
}
}
main().catch((err) => {
console.error(err);
process.exit(1);
});