@cordy/electro-cli 1.0.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/dist/index.d.mts +1 -0
- package/dist/index.mjs +3362 -0
- package/package.json +66 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,3362 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { builtinModules } from "node:module";
|
|
3
|
+
import { EventEmitter } from "events";
|
|
4
|
+
import { access, mkdir, readFile, rm, unlink, writeFile } from "node:fs/promises";
|
|
5
|
+
import { basename, dirname, extname, join, relative, resolve } from "node:path";
|
|
6
|
+
import { generate, scan } from "@cordy/electro-generator";
|
|
7
|
+
import { build, createLogger, createServer, mergeConfig, version } from "vite";
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { spawn } from "node:child_process";
|
|
11
|
+
|
|
12
|
+
//#region ../../node_modules/.bun/cac@6.7.14/node_modules/cac/dist/index.mjs
|
|
13
|
+
function toArr(any) {
|
|
14
|
+
return any == null ? [] : Array.isArray(any) ? any : [any];
|
|
15
|
+
}
|
|
16
|
+
function toVal(out, key, val, opts) {
|
|
17
|
+
var x, old = out[key], nxt = !!~opts.string.indexOf(key) ? val == null || val === true ? "" : String(val) : typeof val === "boolean" ? val : !!~opts.boolean.indexOf(key) ? val === "false" ? false : val === "true" || (out._.push((x = +val, x * 0 === 0) ? x : val), !!val) : (x = +val, x * 0 === 0) ? x : val;
|
|
18
|
+
out[key] = old == null ? nxt : Array.isArray(old) ? old.concat(nxt) : [old, nxt];
|
|
19
|
+
}
|
|
20
|
+
function mri2(args, opts) {
|
|
21
|
+
args = args || [];
|
|
22
|
+
opts = opts || {};
|
|
23
|
+
var k, arr, arg, name, val, out = { _: [] };
|
|
24
|
+
var i = 0, j = 0, idx = 0, len = args.length;
|
|
25
|
+
const alibi = opts.alias !== void 0;
|
|
26
|
+
const strict = opts.unknown !== void 0;
|
|
27
|
+
const defaults = opts.default !== void 0;
|
|
28
|
+
opts.alias = opts.alias || {};
|
|
29
|
+
opts.string = toArr(opts.string);
|
|
30
|
+
opts.boolean = toArr(opts.boolean);
|
|
31
|
+
if (alibi) for (k in opts.alias) {
|
|
32
|
+
arr = opts.alias[k] = toArr(opts.alias[k]);
|
|
33
|
+
for (i = 0; i < arr.length; i++) (opts.alias[arr[i]] = arr.concat(k)).splice(i, 1);
|
|
34
|
+
}
|
|
35
|
+
for (i = opts.boolean.length; i-- > 0;) {
|
|
36
|
+
arr = opts.alias[opts.boolean[i]] || [];
|
|
37
|
+
for (j = arr.length; j-- > 0;) opts.boolean.push(arr[j]);
|
|
38
|
+
}
|
|
39
|
+
for (i = opts.string.length; i-- > 0;) {
|
|
40
|
+
arr = opts.alias[opts.string[i]] || [];
|
|
41
|
+
for (j = arr.length; j-- > 0;) opts.string.push(arr[j]);
|
|
42
|
+
}
|
|
43
|
+
if (defaults) for (k in opts.default) {
|
|
44
|
+
name = typeof opts.default[k];
|
|
45
|
+
arr = opts.alias[k] = opts.alias[k] || [];
|
|
46
|
+
if (opts[name] !== void 0) {
|
|
47
|
+
opts[name].push(k);
|
|
48
|
+
for (i = 0; i < arr.length; i++) opts[name].push(arr[i]);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const keys = strict ? Object.keys(opts.alias) : [];
|
|
52
|
+
for (i = 0; i < len; i++) {
|
|
53
|
+
arg = args[i];
|
|
54
|
+
if (arg === "--") {
|
|
55
|
+
out._ = out._.concat(args.slice(++i));
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
for (j = 0; j < arg.length; j++) if (arg.charCodeAt(j) !== 45) break;
|
|
59
|
+
if (j === 0) out._.push(arg);
|
|
60
|
+
else if (arg.substring(j, j + 3) === "no-") {
|
|
61
|
+
name = arg.substring(j + 3);
|
|
62
|
+
if (strict && !~keys.indexOf(name)) return opts.unknown(arg);
|
|
63
|
+
out[name] = false;
|
|
64
|
+
} else {
|
|
65
|
+
for (idx = j + 1; idx < arg.length; idx++) if (arg.charCodeAt(idx) === 61) break;
|
|
66
|
+
name = arg.substring(j, idx);
|
|
67
|
+
val = arg.substring(++idx) || i + 1 === len || ("" + args[i + 1]).charCodeAt(0) === 45 || args[++i];
|
|
68
|
+
arr = j === 2 ? [name] : name;
|
|
69
|
+
for (idx = 0; idx < arr.length; idx++) {
|
|
70
|
+
name = arr[idx];
|
|
71
|
+
if (strict && !~keys.indexOf(name)) return opts.unknown("-".repeat(j) + name);
|
|
72
|
+
toVal(out, name, idx + 1 < arr.length || val, opts);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (defaults) {
|
|
77
|
+
for (k in opts.default) if (out[k] === void 0) out[k] = opts.default[k];
|
|
78
|
+
}
|
|
79
|
+
if (alibi) for (k in out) {
|
|
80
|
+
arr = opts.alias[k] || [];
|
|
81
|
+
while (arr.length > 0) out[arr.shift()] = out[k];
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
const removeBrackets = (v) => v.replace(/[<[].+/, "").trim();
|
|
86
|
+
const findAllBrackets = (v) => {
|
|
87
|
+
const ANGLED_BRACKET_RE_GLOBAL = /<([^>]+)>/g;
|
|
88
|
+
const SQUARE_BRACKET_RE_GLOBAL = /\[([^\]]+)\]/g;
|
|
89
|
+
const res = [];
|
|
90
|
+
const parse = (match) => {
|
|
91
|
+
let variadic = false;
|
|
92
|
+
let value = match[1];
|
|
93
|
+
if (value.startsWith("...")) {
|
|
94
|
+
value = value.slice(3);
|
|
95
|
+
variadic = true;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
required: match[0].startsWith("<"),
|
|
99
|
+
value,
|
|
100
|
+
variadic
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
let angledMatch;
|
|
104
|
+
while (angledMatch = ANGLED_BRACKET_RE_GLOBAL.exec(v)) res.push(parse(angledMatch));
|
|
105
|
+
let squareMatch;
|
|
106
|
+
while (squareMatch = SQUARE_BRACKET_RE_GLOBAL.exec(v)) res.push(parse(squareMatch));
|
|
107
|
+
return res;
|
|
108
|
+
};
|
|
109
|
+
const getMriOptions = (options) => {
|
|
110
|
+
const result = {
|
|
111
|
+
alias: {},
|
|
112
|
+
boolean: []
|
|
113
|
+
};
|
|
114
|
+
for (const [index, option] of options.entries()) {
|
|
115
|
+
if (option.names.length > 1) result.alias[option.names[0]] = option.names.slice(1);
|
|
116
|
+
if (option.isBoolean) if (option.negated) {
|
|
117
|
+
if (!options.some((o, i) => {
|
|
118
|
+
return i !== index && o.names.some((name) => option.names.includes(name)) && typeof o.required === "boolean";
|
|
119
|
+
})) result.boolean.push(option.names[0]);
|
|
120
|
+
} else result.boolean.push(option.names[0]);
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
};
|
|
124
|
+
const findLongest = (arr) => {
|
|
125
|
+
return arr.sort((a, b) => {
|
|
126
|
+
return a.length > b.length ? -1 : 1;
|
|
127
|
+
})[0];
|
|
128
|
+
};
|
|
129
|
+
const padRight = (str, length) => {
|
|
130
|
+
return str.length >= length ? str : `${str}${" ".repeat(length - str.length)}`;
|
|
131
|
+
};
|
|
132
|
+
const camelcase = (input) => {
|
|
133
|
+
return input.replace(/([a-z])-([a-z])/g, (_, p1, p2) => {
|
|
134
|
+
return p1 + p2.toUpperCase();
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
const setDotProp = (obj, keys, val) => {
|
|
138
|
+
let i = 0;
|
|
139
|
+
let length = keys.length;
|
|
140
|
+
let t = obj;
|
|
141
|
+
let x;
|
|
142
|
+
for (; i < length; ++i) {
|
|
143
|
+
x = t[keys[i]];
|
|
144
|
+
t = t[keys[i]] = i === length - 1 ? val : x != null ? x : !!~keys[i + 1].indexOf(".") || !(+keys[i + 1] > -1) ? {} : [];
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const setByType = (obj, transforms) => {
|
|
148
|
+
for (const key of Object.keys(transforms)) {
|
|
149
|
+
const transform = transforms[key];
|
|
150
|
+
if (transform.shouldTransform) {
|
|
151
|
+
obj[key] = Array.prototype.concat.call([], obj[key]);
|
|
152
|
+
if (typeof transform.transformFunction === "function") obj[key] = obj[key].map(transform.transformFunction);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const getFileName = (input) => {
|
|
157
|
+
const m = /([^\\\/]+)$/.exec(input);
|
|
158
|
+
return m ? m[1] : "";
|
|
159
|
+
};
|
|
160
|
+
const camelcaseOptionName = (name) => {
|
|
161
|
+
return name.split(".").map((v, i) => {
|
|
162
|
+
return i === 0 ? camelcase(v) : v;
|
|
163
|
+
}).join(".");
|
|
164
|
+
};
|
|
165
|
+
var CACError = class extends Error {
|
|
166
|
+
constructor(message) {
|
|
167
|
+
super(message);
|
|
168
|
+
this.name = this.constructor.name;
|
|
169
|
+
if (typeof Error.captureStackTrace === "function") Error.captureStackTrace(this, this.constructor);
|
|
170
|
+
else this.stack = new Error(message).stack;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
var Option = class {
|
|
174
|
+
constructor(rawName, description, config) {
|
|
175
|
+
this.rawName = rawName;
|
|
176
|
+
this.description = description;
|
|
177
|
+
this.config = Object.assign({}, config);
|
|
178
|
+
rawName = rawName.replace(/\.\*/g, "");
|
|
179
|
+
this.negated = false;
|
|
180
|
+
this.names = removeBrackets(rawName).split(",").map((v) => {
|
|
181
|
+
let name = v.trim().replace(/^-{1,2}/, "");
|
|
182
|
+
if (name.startsWith("no-")) {
|
|
183
|
+
this.negated = true;
|
|
184
|
+
name = name.replace(/^no-/, "");
|
|
185
|
+
}
|
|
186
|
+
return camelcaseOptionName(name);
|
|
187
|
+
}).sort((a, b) => a.length > b.length ? 1 : -1);
|
|
188
|
+
this.name = this.names[this.names.length - 1];
|
|
189
|
+
if (this.negated && this.config.default == null) this.config.default = true;
|
|
190
|
+
if (rawName.includes("<")) this.required = true;
|
|
191
|
+
else if (rawName.includes("[")) this.required = false;
|
|
192
|
+
else this.isBoolean = true;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
const processArgs = process.argv;
|
|
196
|
+
const platformInfo = `${process.platform}-${process.arch} node-${process.version}`;
|
|
197
|
+
var Command = class {
|
|
198
|
+
constructor(rawName, description, config = {}, cli) {
|
|
199
|
+
this.rawName = rawName;
|
|
200
|
+
this.description = description;
|
|
201
|
+
this.config = config;
|
|
202
|
+
this.cli = cli;
|
|
203
|
+
this.options = [];
|
|
204
|
+
this.aliasNames = [];
|
|
205
|
+
this.name = removeBrackets(rawName);
|
|
206
|
+
this.args = findAllBrackets(rawName);
|
|
207
|
+
this.examples = [];
|
|
208
|
+
}
|
|
209
|
+
usage(text) {
|
|
210
|
+
this.usageText = text;
|
|
211
|
+
return this;
|
|
212
|
+
}
|
|
213
|
+
allowUnknownOptions() {
|
|
214
|
+
this.config.allowUnknownOptions = true;
|
|
215
|
+
return this;
|
|
216
|
+
}
|
|
217
|
+
ignoreOptionDefaultValue() {
|
|
218
|
+
this.config.ignoreOptionDefaultValue = true;
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
version(version, customFlags = "-v, --version") {
|
|
222
|
+
this.versionNumber = version;
|
|
223
|
+
this.option(customFlags, "Display version number");
|
|
224
|
+
return this;
|
|
225
|
+
}
|
|
226
|
+
example(example) {
|
|
227
|
+
this.examples.push(example);
|
|
228
|
+
return this;
|
|
229
|
+
}
|
|
230
|
+
option(rawName, description, config) {
|
|
231
|
+
const option = new Option(rawName, description, config);
|
|
232
|
+
this.options.push(option);
|
|
233
|
+
return this;
|
|
234
|
+
}
|
|
235
|
+
alias(name) {
|
|
236
|
+
this.aliasNames.push(name);
|
|
237
|
+
return this;
|
|
238
|
+
}
|
|
239
|
+
action(callback) {
|
|
240
|
+
this.commandAction = callback;
|
|
241
|
+
return this;
|
|
242
|
+
}
|
|
243
|
+
isMatched(name) {
|
|
244
|
+
return this.name === name || this.aliasNames.includes(name);
|
|
245
|
+
}
|
|
246
|
+
get isDefaultCommand() {
|
|
247
|
+
return this.name === "" || this.aliasNames.includes("!");
|
|
248
|
+
}
|
|
249
|
+
get isGlobalCommand() {
|
|
250
|
+
return this instanceof GlobalCommand;
|
|
251
|
+
}
|
|
252
|
+
hasOption(name) {
|
|
253
|
+
name = name.split(".")[0];
|
|
254
|
+
return this.options.find((option) => {
|
|
255
|
+
return option.names.includes(name);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
outputHelp() {
|
|
259
|
+
const { name, commands } = this.cli;
|
|
260
|
+
const { versionNumber, options: globalOptions, helpCallback } = this.cli.globalCommand;
|
|
261
|
+
let sections = [{ body: `${name}${versionNumber ? `/${versionNumber}` : ""}` }];
|
|
262
|
+
sections.push({
|
|
263
|
+
title: "Usage",
|
|
264
|
+
body: ` $ ${name} ${this.usageText || this.rawName}`
|
|
265
|
+
});
|
|
266
|
+
if ((this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0) {
|
|
267
|
+
const longestCommandName = findLongest(commands.map((command) => command.rawName));
|
|
268
|
+
sections.push({
|
|
269
|
+
title: "Commands",
|
|
270
|
+
body: commands.map((command) => {
|
|
271
|
+
return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
|
|
272
|
+
}).join("\n")
|
|
273
|
+
});
|
|
274
|
+
sections.push({
|
|
275
|
+
title: `For more info, run any command with the \`--help\` flag`,
|
|
276
|
+
body: commands.map((command) => ` $ ${name}${command.name === "" ? "" : ` ${command.name}`} --help`).join("\n")
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
let options = this.isGlobalCommand ? globalOptions : [...this.options, ...globalOptions || []];
|
|
280
|
+
if (!this.isGlobalCommand && !this.isDefaultCommand) options = options.filter((option) => option.name !== "version");
|
|
281
|
+
if (options.length > 0) {
|
|
282
|
+
const longestOptionName = findLongest(options.map((option) => option.rawName));
|
|
283
|
+
sections.push({
|
|
284
|
+
title: "Options",
|
|
285
|
+
body: options.map((option) => {
|
|
286
|
+
return ` ${padRight(option.rawName, longestOptionName.length)} ${option.description} ${option.config.default === void 0 ? "" : `(default: ${option.config.default})`}`;
|
|
287
|
+
}).join("\n")
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
if (this.examples.length > 0) sections.push({
|
|
291
|
+
title: "Examples",
|
|
292
|
+
body: this.examples.map((example) => {
|
|
293
|
+
if (typeof example === "function") return example(name);
|
|
294
|
+
return example;
|
|
295
|
+
}).join("\n")
|
|
296
|
+
});
|
|
297
|
+
if (helpCallback) sections = helpCallback(sections) || sections;
|
|
298
|
+
console.log(sections.map((section) => {
|
|
299
|
+
return section.title ? `${section.title}:
|
|
300
|
+
${section.body}` : section.body;
|
|
301
|
+
}).join("\n\n"));
|
|
302
|
+
}
|
|
303
|
+
outputVersion() {
|
|
304
|
+
const { name } = this.cli;
|
|
305
|
+
const { versionNumber } = this.cli.globalCommand;
|
|
306
|
+
if (versionNumber) console.log(`${name}/${versionNumber} ${platformInfo}`);
|
|
307
|
+
}
|
|
308
|
+
checkRequiredArgs() {
|
|
309
|
+
const minimalArgsCount = this.args.filter((arg) => arg.required).length;
|
|
310
|
+
if (this.cli.args.length < minimalArgsCount) throw new CACError(`missing required args for command \`${this.rawName}\``);
|
|
311
|
+
}
|
|
312
|
+
checkUnknownOptions() {
|
|
313
|
+
const { options, globalCommand } = this.cli;
|
|
314
|
+
if (!this.config.allowUnknownOptions) {
|
|
315
|
+
for (const name of Object.keys(options)) if (name !== "--" && !this.hasOption(name) && !globalCommand.hasOption(name)) throw new CACError(`Unknown option \`${name.length > 1 ? `--${name}` : `-${name}`}\``);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
checkOptionValue() {
|
|
319
|
+
const { options: parsedOptions, globalCommand } = this.cli;
|
|
320
|
+
const options = [...globalCommand.options, ...this.options];
|
|
321
|
+
for (const option of options) {
|
|
322
|
+
const value = parsedOptions[option.name.split(".")[0]];
|
|
323
|
+
if (option.required) {
|
|
324
|
+
const hasNegated = options.some((o) => o.negated && o.names.includes(option.name));
|
|
325
|
+
if (value === true || value === false && !hasNegated) throw new CACError(`option \`${option.rawName}\` value is missing`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
var GlobalCommand = class extends Command {
|
|
331
|
+
constructor(cli) {
|
|
332
|
+
super("@@global@@", "", {}, cli);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
var __assign = Object.assign;
|
|
336
|
+
var CAC = class extends EventEmitter {
|
|
337
|
+
constructor(name = "") {
|
|
338
|
+
super();
|
|
339
|
+
this.name = name;
|
|
340
|
+
this.commands = [];
|
|
341
|
+
this.rawArgs = [];
|
|
342
|
+
this.args = [];
|
|
343
|
+
this.options = {};
|
|
344
|
+
this.globalCommand = new GlobalCommand(this);
|
|
345
|
+
this.globalCommand.usage("<command> [options]");
|
|
346
|
+
}
|
|
347
|
+
usage(text) {
|
|
348
|
+
this.globalCommand.usage(text);
|
|
349
|
+
return this;
|
|
350
|
+
}
|
|
351
|
+
command(rawName, description, config) {
|
|
352
|
+
const command = new Command(rawName, description || "", config, this);
|
|
353
|
+
command.globalCommand = this.globalCommand;
|
|
354
|
+
this.commands.push(command);
|
|
355
|
+
return command;
|
|
356
|
+
}
|
|
357
|
+
option(rawName, description, config) {
|
|
358
|
+
this.globalCommand.option(rawName, description, config);
|
|
359
|
+
return this;
|
|
360
|
+
}
|
|
361
|
+
help(callback) {
|
|
362
|
+
this.globalCommand.option("-h, --help", "Display this message");
|
|
363
|
+
this.globalCommand.helpCallback = callback;
|
|
364
|
+
this.showHelpOnExit = true;
|
|
365
|
+
return this;
|
|
366
|
+
}
|
|
367
|
+
version(version, customFlags = "-v, --version") {
|
|
368
|
+
this.globalCommand.version(version, customFlags);
|
|
369
|
+
this.showVersionOnExit = true;
|
|
370
|
+
return this;
|
|
371
|
+
}
|
|
372
|
+
example(example) {
|
|
373
|
+
this.globalCommand.example(example);
|
|
374
|
+
return this;
|
|
375
|
+
}
|
|
376
|
+
outputHelp() {
|
|
377
|
+
if (this.matchedCommand) this.matchedCommand.outputHelp();
|
|
378
|
+
else this.globalCommand.outputHelp();
|
|
379
|
+
}
|
|
380
|
+
outputVersion() {
|
|
381
|
+
this.globalCommand.outputVersion();
|
|
382
|
+
}
|
|
383
|
+
setParsedInfo({ args, options }, matchedCommand, matchedCommandName) {
|
|
384
|
+
this.args = args;
|
|
385
|
+
this.options = options;
|
|
386
|
+
if (matchedCommand) this.matchedCommand = matchedCommand;
|
|
387
|
+
if (matchedCommandName) this.matchedCommandName = matchedCommandName;
|
|
388
|
+
return this;
|
|
389
|
+
}
|
|
390
|
+
unsetMatchedCommand() {
|
|
391
|
+
this.matchedCommand = void 0;
|
|
392
|
+
this.matchedCommandName = void 0;
|
|
393
|
+
}
|
|
394
|
+
parse(argv = processArgs, { run = true } = {}) {
|
|
395
|
+
this.rawArgs = argv;
|
|
396
|
+
if (!this.name) this.name = argv[1] ? getFileName(argv[1]) : "cli";
|
|
397
|
+
let shouldParse = true;
|
|
398
|
+
for (const command of this.commands) {
|
|
399
|
+
const parsed = this.mri(argv.slice(2), command);
|
|
400
|
+
const commandName = parsed.args[0];
|
|
401
|
+
if (command.isMatched(commandName)) {
|
|
402
|
+
shouldParse = false;
|
|
403
|
+
const parsedInfo = __assign(__assign({}, parsed), { args: parsed.args.slice(1) });
|
|
404
|
+
this.setParsedInfo(parsedInfo, command, commandName);
|
|
405
|
+
this.emit(`command:${commandName}`, command);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (shouldParse) {
|
|
409
|
+
for (const command of this.commands) if (command.name === "") {
|
|
410
|
+
shouldParse = false;
|
|
411
|
+
const parsed = this.mri(argv.slice(2), command);
|
|
412
|
+
this.setParsedInfo(parsed, command);
|
|
413
|
+
this.emit(`command:!`, command);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (shouldParse) {
|
|
417
|
+
const parsed = this.mri(argv.slice(2));
|
|
418
|
+
this.setParsedInfo(parsed);
|
|
419
|
+
}
|
|
420
|
+
if (this.options.help && this.showHelpOnExit) {
|
|
421
|
+
this.outputHelp();
|
|
422
|
+
run = false;
|
|
423
|
+
this.unsetMatchedCommand();
|
|
424
|
+
}
|
|
425
|
+
if (this.options.version && this.showVersionOnExit && this.matchedCommandName == null) {
|
|
426
|
+
this.outputVersion();
|
|
427
|
+
run = false;
|
|
428
|
+
this.unsetMatchedCommand();
|
|
429
|
+
}
|
|
430
|
+
const parsedArgv = {
|
|
431
|
+
args: this.args,
|
|
432
|
+
options: this.options
|
|
433
|
+
};
|
|
434
|
+
if (run) this.runMatchedCommand();
|
|
435
|
+
if (!this.matchedCommand && this.args[0]) this.emit("command:*");
|
|
436
|
+
return parsedArgv;
|
|
437
|
+
}
|
|
438
|
+
mri(argv, command) {
|
|
439
|
+
const cliOptions = [...this.globalCommand.options, ...command ? command.options : []];
|
|
440
|
+
const mriOptions = getMriOptions(cliOptions);
|
|
441
|
+
let argsAfterDoubleDashes = [];
|
|
442
|
+
const doubleDashesIndex = argv.indexOf("--");
|
|
443
|
+
if (doubleDashesIndex > -1) {
|
|
444
|
+
argsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1);
|
|
445
|
+
argv = argv.slice(0, doubleDashesIndex);
|
|
446
|
+
}
|
|
447
|
+
let parsed = mri2(argv, mriOptions);
|
|
448
|
+
parsed = Object.keys(parsed).reduce((res, name) => {
|
|
449
|
+
return __assign(__assign({}, res), { [camelcaseOptionName(name)]: parsed[name] });
|
|
450
|
+
}, { _: [] });
|
|
451
|
+
const args = parsed._;
|
|
452
|
+
const options = { "--": argsAfterDoubleDashes };
|
|
453
|
+
const ignoreDefault = command && command.config.ignoreOptionDefaultValue ? command.config.ignoreOptionDefaultValue : this.globalCommand.config.ignoreOptionDefaultValue;
|
|
454
|
+
let transforms = Object.create(null);
|
|
455
|
+
for (const cliOption of cliOptions) {
|
|
456
|
+
if (!ignoreDefault && cliOption.config.default !== void 0) for (const name of cliOption.names) options[name] = cliOption.config.default;
|
|
457
|
+
if (Array.isArray(cliOption.config.type)) {
|
|
458
|
+
if (transforms[cliOption.name] === void 0) {
|
|
459
|
+
transforms[cliOption.name] = Object.create(null);
|
|
460
|
+
transforms[cliOption.name]["shouldTransform"] = true;
|
|
461
|
+
transforms[cliOption.name]["transformFunction"] = cliOption.config.type[0];
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
for (const key of Object.keys(parsed)) if (key !== "_") {
|
|
466
|
+
setDotProp(options, key.split("."), parsed[key]);
|
|
467
|
+
setByType(options, transforms);
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
args,
|
|
471
|
+
options
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
runMatchedCommand() {
|
|
475
|
+
const { args, options, matchedCommand: command } = this;
|
|
476
|
+
if (!command || !command.commandAction) return;
|
|
477
|
+
command.checkUnknownOptions();
|
|
478
|
+
command.checkOptionValue();
|
|
479
|
+
command.checkRequiredArgs();
|
|
480
|
+
const actionArgs = [];
|
|
481
|
+
command.args.forEach((arg, index) => {
|
|
482
|
+
if (arg.variadic) actionArgs.push(args.slice(index));
|
|
483
|
+
else actionArgs.push(args[index]);
|
|
484
|
+
});
|
|
485
|
+
actionArgs.push(options);
|
|
486
|
+
return command.commandAction.apply(this, actionArgs);
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
const cac = (name = "") => new CAC(name);
|
|
490
|
+
|
|
491
|
+
//#endregion
|
|
492
|
+
//#region package.json
|
|
493
|
+
var version$1 = "1.0.0";
|
|
494
|
+
|
|
495
|
+
//#endregion
|
|
496
|
+
//#region src/dev/logger.ts
|
|
497
|
+
const yellow$1 = "\x1B[33m";
|
|
498
|
+
const green = "\x1B[32m";
|
|
499
|
+
const cyan = "\x1B[36m";
|
|
500
|
+
const red$1 = "\x1B[31m";
|
|
501
|
+
const magenta = "\x1B[35m";
|
|
502
|
+
const dim$1 = "\x1B[90m";
|
|
503
|
+
const bold = "\x1B[1m";
|
|
504
|
+
const reset$1 = "\x1B[0m";
|
|
505
|
+
const levels = {
|
|
506
|
+
info: 0,
|
|
507
|
+
warn: 1,
|
|
508
|
+
error: 2,
|
|
509
|
+
silent: 3
|
|
510
|
+
};
|
|
511
|
+
let currentLevel = "info";
|
|
512
|
+
function setLogLevel(level) {
|
|
513
|
+
currentLevel = level;
|
|
514
|
+
}
|
|
515
|
+
function info(msg) {
|
|
516
|
+
if (levels[currentLevel] > levels.info) return;
|
|
517
|
+
console.log(` ${dim$1}▸${reset$1} ${msg}`);
|
|
518
|
+
}
|
|
519
|
+
function warn(msg) {
|
|
520
|
+
if (levels[currentLevel] > levels.warn) return;
|
|
521
|
+
console.log(` ${yellow$1}⚠ ${msg}${reset$1}`);
|
|
522
|
+
}
|
|
523
|
+
function error(msg) {
|
|
524
|
+
if (levels[currentLevel] > levels.error) return;
|
|
525
|
+
console.error(` ${red$1}✗ ${msg}${reset$1}`);
|
|
526
|
+
}
|
|
527
|
+
function note(msg) {
|
|
528
|
+
if (levels[currentLevel] > levels.info) return;
|
|
529
|
+
console.log(` ${dim$1}${msg}${reset$1}`);
|
|
530
|
+
}
|
|
531
|
+
function startTimer() {
|
|
532
|
+
const startedAt = Date.now();
|
|
533
|
+
return () => formatDuration(Date.now() - startedAt);
|
|
534
|
+
}
|
|
535
|
+
function formatDuration(ms) {
|
|
536
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
537
|
+
const seconds = ms / 1e3;
|
|
538
|
+
if (seconds < 60) return `${seconds.toFixed(1)}s`;
|
|
539
|
+
return `${Math.floor(seconds / 60)}m${Math.round(seconds % 60).toString().padStart(2, "0")}s`;
|
|
540
|
+
}
|
|
541
|
+
const STEP_WIDTH = 26;
|
|
542
|
+
function step(label, duration, extra) {
|
|
543
|
+
if (levels[currentLevel] > levels.info) return;
|
|
544
|
+
const dotsLen = Math.max(2, STEP_WIDTH - label.length - 1);
|
|
545
|
+
const dots = "·".repeat(dotsLen);
|
|
546
|
+
const dur = duration.padStart(5);
|
|
547
|
+
const suffix = extra ? ` ${dim$1}${extra}${reset$1}` : "";
|
|
548
|
+
console.log(` ${label} ${dim$1}${dots}${reset$1} ${green}✓${reset$1} ${green}${dur}${reset$1}${suffix}`);
|
|
549
|
+
}
|
|
550
|
+
function stepFail(label, message) {
|
|
551
|
+
const dotsLen = Math.max(2, STEP_WIDTH - label.length - 1);
|
|
552
|
+
const dots = "·".repeat(dotsLen);
|
|
553
|
+
console.error(` ${label} ${dim$1}${dots}${reset$1} ${red$1}✗ ${message}${reset$1}`);
|
|
554
|
+
}
|
|
555
|
+
function colorRuntimeMessage(msg) {
|
|
556
|
+
if (msg.startsWith("hmr update")) return `${green}${msg}${reset$1}`;
|
|
557
|
+
if (msg.startsWith("page reload")) return `${yellow$1}page reload${reset$1}${msg.slice(11)}`;
|
|
558
|
+
if (msg.startsWith("rebuild")) return `${cyan}rebuild${reset$1}${msg.slice(7)}`;
|
|
559
|
+
if (msg.startsWith("generated")) return `${magenta}generated${reset$1}`;
|
|
560
|
+
if (msg.startsWith("crashed")) return `${red$1}${msg}${reset$1}`;
|
|
561
|
+
if (msg.startsWith("exited")) return `${dim$1}exited${reset$1}`;
|
|
562
|
+
return msg;
|
|
563
|
+
}
|
|
564
|
+
function formatTime(d) {
|
|
565
|
+
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}:${String(d.getSeconds()).padStart(2, "0")}`;
|
|
566
|
+
}
|
|
567
|
+
function clearProgressLine() {
|
|
568
|
+
if (process.stdout.isTTY) {
|
|
569
|
+
process.stdout.clearLine(0);
|
|
570
|
+
process.stdout.cursorTo(0);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
function runtimeLog(scope, msg, changedFile) {
|
|
574
|
+
if (levels[currentLevel] > levels.info) return;
|
|
575
|
+
clearProgressLine();
|
|
576
|
+
const time = formatTime(/* @__PURE__ */ new Date());
|
|
577
|
+
const filePart = changedFile ? ` ${dim$1}${changedFile}${reset$1}` : "";
|
|
578
|
+
console.log(`${dim$1}${time}${reset$1} ${yellow$1}[electro]${reset$1} ${dim$1}(${scope})${reset$1} ${colorRuntimeMessage(msg)}${filePart}`);
|
|
579
|
+
}
|
|
580
|
+
function footer(message, url) {
|
|
581
|
+
if (levels[currentLevel] > levels.info) return;
|
|
582
|
+
console.log(`\n ${bold}${green}✓ ${message}${reset$1}`);
|
|
583
|
+
if (url) console.log(` ${yellow$1}→ ${url}${reset$1}`);
|
|
584
|
+
console.log("");
|
|
585
|
+
}
|
|
586
|
+
function toProjectRelative(root, absolutePath) {
|
|
587
|
+
return relative(root, absolutePath) || ".";
|
|
588
|
+
}
|
|
589
|
+
function session(meta) {
|
|
590
|
+
if (levels[currentLevel] > levels.info) return;
|
|
591
|
+
const isBuild = meta.mode === "build";
|
|
592
|
+
const projectName = basename(meta.root);
|
|
593
|
+
const mainEntry = toProjectRelative(meta.root, meta.main);
|
|
594
|
+
const preloadEntry = meta.preload ? toProjectRelative(meta.root, meta.preload) : `${dim$1}(none)${reset$1}`;
|
|
595
|
+
const rendererEntry = meta.renderer ? toProjectRelative(meta.root, meta.renderer) : `${dim$1}(none)${reset$1}`;
|
|
596
|
+
const command = isBuild ? "build" : "dev";
|
|
597
|
+
console.log(`\n${bold}${yellow$1}⚡ electro ${command}${reset$1} → ${cyan}${projectName}${reset$1}\n`);
|
|
598
|
+
const mainMode = isBuild ? "build" : "watch";
|
|
599
|
+
const preloadMode = isBuild ? "build" : "watch";
|
|
600
|
+
const rendererMode = isBuild ? "build" : "dev server";
|
|
601
|
+
const entryWidth = Math.max(14, mainEntry.length, preloadEntry.length, rendererEntry.length) + 2;
|
|
602
|
+
console.log(` ${dim$1}Scope ${"Entry".padEnd(entryWidth)}Mode${reset$1}`);
|
|
603
|
+
console.log(` ${cyan}main${reset$1} ${mainEntry.padEnd(entryWidth)}${dim$1}${mainMode}${reset$1}`);
|
|
604
|
+
console.log(` ${yellow$1}preload${reset$1} ${preloadEntry.padEnd(entryWidth)}${dim$1}${preloadMode}${reset$1}`);
|
|
605
|
+
console.log(` ${green}renderer${reset$1} ${rendererEntry.padEnd(entryWidth)}${dim$1}${rendererMode}${reset$1}`);
|
|
606
|
+
if (meta.windows && meta.windows.length > 0) {
|
|
607
|
+
console.log("");
|
|
608
|
+
console.log(` ${dim$1}Windows${reset$1} ${meta.windows.length} configured`);
|
|
609
|
+
for (const win of meta.windows) {
|
|
610
|
+
const winEntry = toProjectRelative(meta.root, win.entry);
|
|
611
|
+
console.log(` ${win.name.padEnd(10)} ${dim$1}${winEntry}${reset$1}`);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
console.log("");
|
|
615
|
+
}
|
|
616
|
+
const ANSI_RE = new RegExp(String.raw`\x1b\[[0-9;]*m`, "g");
|
|
617
|
+
const VITE_TAG_RE = new RegExp(String.raw`(?:\x1b\[[0-9;]*m)*\[(vite(?:-plugin-[^\]]+)?)\](?:\x1b\[[0-9;]*m)*`, "g");
|
|
618
|
+
function retagMessage(msg) {
|
|
619
|
+
return msg.replace(VITE_TAG_RE, `[${bold}${yellow$1}electro${reset$1}]`);
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Patch a Vite logger to rebrand `[vite]` → `[electro]`,
|
|
623
|
+
* suppress startup noise ("ready in", "➜"), and extract
|
|
624
|
+
* HMR/reload targets for cleaner output.
|
|
625
|
+
*/
|
|
626
|
+
function patchLogger(logger, scope) {
|
|
627
|
+
const origInfo = logger.info.bind(logger);
|
|
628
|
+
logger.info = (msg, options) => {
|
|
629
|
+
if (typeof msg !== "string") return origInfo(msg, options);
|
|
630
|
+
const clean = msg.replace(ANSI_RE, "").trim();
|
|
631
|
+
if (!clean || clean.includes("ready in") || clean.includes("➜")) return;
|
|
632
|
+
if (clean.includes("hmr update")) {
|
|
633
|
+
const target = extractTarget(clean, "hmr update");
|
|
634
|
+
runtimeLog(scope, `hmr update${target ? ` ${target}` : ""}`);
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
if (clean.includes("hmr invalidate")) {
|
|
638
|
+
const target = extractTarget(clean, "hmr invalidate");
|
|
639
|
+
runtimeLog(scope, `hmr invalidate${target ? ` ${target}` : ""}`);
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (clean.includes("page reload")) {
|
|
643
|
+
const target = extractTarget(clean, "page reload");
|
|
644
|
+
runtimeLog(scope, `page reload${target ? ` ${target}` : ""}`);
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
origInfo(retagMessage(msg), options);
|
|
648
|
+
};
|
|
649
|
+
const origWarn = logger.warn.bind(logger);
|
|
650
|
+
logger.warn = (msg, options) => {
|
|
651
|
+
if (typeof msg !== "string") return origWarn(msg, options);
|
|
652
|
+
origWarn(retagMessage(msg), options);
|
|
653
|
+
};
|
|
654
|
+
const origError = logger.error.bind(logger);
|
|
655
|
+
logger.error = (msg, options) => {
|
|
656
|
+
if (typeof msg !== "string") return origError(msg, options);
|
|
657
|
+
origError(retagMessage(msg), options);
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
function extractTarget(message, event) {
|
|
661
|
+
return message.match(new RegExp(`\\b${event}\\b\\s+(.+)$`))?.[1]?.trim() ?? null;
|
|
662
|
+
}
|
|
663
|
+
/** Print a scope header before a production build step. */
|
|
664
|
+
function buildScope(scope) {
|
|
665
|
+
if (levels[currentLevel] > levels.info) return;
|
|
666
|
+
const color = scope === "main" ? cyan : scope === "preload" ? yellow$1 : green;
|
|
667
|
+
console.log(`\n ${color}${scope}${reset$1}`);
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Create a Vite Logger for production builds.
|
|
671
|
+
* Suppresses the "vite vX building..." header (we print our own banner)
|
|
672
|
+
* and rebrands `[vite]` → `[electro]`. Everything else — transforming
|
|
673
|
+
* progress, file listing with sizes, "built in" — passes through.
|
|
674
|
+
*/
|
|
675
|
+
function createBuildLogger() {
|
|
676
|
+
const logger = createLogger(currentLevel, { allowClearScreen: false });
|
|
677
|
+
const origInfo = logger.info.bind(logger);
|
|
678
|
+
logger.info = (msg, options) => {
|
|
679
|
+
if (typeof msg !== "string") return origInfo(msg, options);
|
|
680
|
+
const clean = msg.replace(ANSI_RE, "").trim();
|
|
681
|
+
if (/^vite v[\d.]+/.test(clean) && clean.includes("building")) return;
|
|
682
|
+
if (!clean) return;
|
|
683
|
+
origInfo(retagMessage(msg), options);
|
|
684
|
+
};
|
|
685
|
+
const origWarn = logger.warn.bind(logger);
|
|
686
|
+
logger.warn = (msg, options) => {
|
|
687
|
+
if (typeof msg !== "string") return origWarn(msg, options);
|
|
688
|
+
origWarn(retagMessage(msg), options);
|
|
689
|
+
};
|
|
690
|
+
const origError = logger.error.bind(logger);
|
|
691
|
+
logger.error = (msg, options) => {
|
|
692
|
+
if (typeof msg !== "string") return origError(msg, options);
|
|
693
|
+
origError(retagMessage(msg), options);
|
|
694
|
+
};
|
|
695
|
+
return logger;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
//#endregion
|
|
699
|
+
//#region src/validate.ts
|
|
700
|
+
const VALID_SOURCEMAP_VALUES = [
|
|
701
|
+
"linked",
|
|
702
|
+
"inline",
|
|
703
|
+
"external",
|
|
704
|
+
"none"
|
|
705
|
+
];
|
|
706
|
+
const MIN_VITE_MAJOR = 8;
|
|
707
|
+
/**
|
|
708
|
+
* Validate the loaded ElectroConfig for structural issues.
|
|
709
|
+
* Exits on fatal problems, warns on non-fatal ones.
|
|
710
|
+
*/
|
|
711
|
+
function validateConfig(config) {
|
|
712
|
+
const mainEntry = resolve(dirname(config.runtime.__source), config.runtime.entry);
|
|
713
|
+
if (!existsSync(mainEntry)) {
|
|
714
|
+
error(`Main entry not found: ${mainEntry}`);
|
|
715
|
+
process.exit(1);
|
|
716
|
+
}
|
|
717
|
+
const windows = config.windows ?? [];
|
|
718
|
+
const names = /* @__PURE__ */ new Set();
|
|
719
|
+
for (const win of windows) {
|
|
720
|
+
if (names.has(win.name)) {
|
|
721
|
+
error(`Duplicate window name "${win.name}". Window names must be unique.`);
|
|
722
|
+
process.exit(1);
|
|
723
|
+
}
|
|
724
|
+
names.add(win.name);
|
|
725
|
+
}
|
|
726
|
+
for (const win of windows) {
|
|
727
|
+
const winEntry = resolve(dirname(win.__source), win.entry);
|
|
728
|
+
if (!existsSync(winEntry)) {
|
|
729
|
+
error(`Window "${win.name}" entry not found: ${winEntry}`);
|
|
730
|
+
process.exit(1);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
for (const win of windows) if (win.features && win.features.length === 0) warn(`Window "${win.name}" has an empty features array — it won't have access to any services.`);
|
|
734
|
+
}
|
|
735
|
+
/** Validate --sourcemap CLI value. Warns on unrecognized values. */
|
|
736
|
+
function validateSourcemap(value) {
|
|
737
|
+
if (!VALID_SOURCEMAP_VALUES.includes(value)) warn(`Unknown --sourcemap value "${value}". Valid values: ${VALID_SOURCEMAP_VALUES.join(", ")}. Defaulting to "linked".`);
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Validate merged Vite config for a Node scope (main/preload).
|
|
741
|
+
* Called after mergeConfig but before the config is used.
|
|
742
|
+
* Throws on invalid settings that would break electro invariants.
|
|
743
|
+
*/
|
|
744
|
+
function validateMergedNodeConfig(config, scope) {
|
|
745
|
+
const target = config.build?.target;
|
|
746
|
+
if (target) {
|
|
747
|
+
const targets = Array.isArray(target) ? target : [target];
|
|
748
|
+
for (const t of targets) if (typeof t === "string" && t !== "esnext") throw new Error(`[electro] ${scope}: invalid target "${t}". Electro requires target: "esnext" (Electron 40+). Remove the target override from your config.`);
|
|
749
|
+
}
|
|
750
|
+
const rolldownOpts = config.build?.rolldownOptions;
|
|
751
|
+
if (rolldownOpts && typeof rolldownOpts === "object" && !Array.isArray(rolldownOpts)) {
|
|
752
|
+
const output = rolldownOpts.output;
|
|
753
|
+
if (output && typeof output === "object" && !Array.isArray(output)) {
|
|
754
|
+
const fmt = output.format;
|
|
755
|
+
if (fmt && fmt !== "es") throw new Error(`[electro] ${scope}: invalid output format "${fmt}". Electro is ESM-only (format: "es"). Remove the format override from your config.`);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (config.ssr === false || config.build?.ssr === false) throw new Error(`[electro] ${scope}: SSR cannot be disabled in Node scopes. SSR mode is required for correct Node.js module resolution.`);
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Enforce electro invariants after merge — normalization pass.
|
|
762
|
+
* Silently corrects values that must be fixed for correct operation.
|
|
763
|
+
* Called AFTER validateMergedNodeConfig (which throws on user errors).
|
|
764
|
+
*/
|
|
765
|
+
function enforceMergedNodeConfig(config, _scope) {
|
|
766
|
+
const build = config.build;
|
|
767
|
+
if (!build) return;
|
|
768
|
+
if (build.target !== "esnext") build.target = "esnext";
|
|
769
|
+
const rolldownOpts = build.rolldownOptions;
|
|
770
|
+
if (rolldownOpts) {
|
|
771
|
+
const output = rolldownOpts.output;
|
|
772
|
+
if (output && output.format !== "es") output.format = "es";
|
|
773
|
+
}
|
|
774
|
+
if (!build.ssr) build.ssr = true;
|
|
775
|
+
}
|
|
776
|
+
/** Validate that installed Vite version is within the supported range. */
|
|
777
|
+
function validateViteVersion(viteVersion) {
|
|
778
|
+
const majorRaw = viteVersion.split(".", 1)[0];
|
|
779
|
+
const major = Number.parseInt(majorRaw ?? "", 10);
|
|
780
|
+
if (!Number.isFinite(major) || major < MIN_VITE_MAJOR) {
|
|
781
|
+
error(`Unsupported Vite version: ${viteVersion}. @cordy/electro requires Vite ${MIN_VITE_MAJOR}+.`);
|
|
782
|
+
process.exit(1);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
//#endregion
|
|
787
|
+
//#region src/dev/config-loader.ts
|
|
788
|
+
async function loadConfig(configPath) {
|
|
789
|
+
const absolutePath = resolve(process.cwd(), configPath);
|
|
790
|
+
const root = dirname(absolutePath);
|
|
791
|
+
if (!existsSync(absolutePath)) throw new Error(`Config file not found: ${absolutePath}`);
|
|
792
|
+
const config = (await import(absolutePath)).default;
|
|
793
|
+
if (!config) throw new Error(`${configPath} must have a default export`);
|
|
794
|
+
if (!config.runtime) throw new Error(`${configPath} must define a runtime via defineRuntime()`);
|
|
795
|
+
validateConfig(config);
|
|
796
|
+
return {
|
|
797
|
+
config,
|
|
798
|
+
configPath: absolutePath,
|
|
799
|
+
root
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
//#endregion
|
|
804
|
+
//#region src/dev/externals.ts
|
|
805
|
+
/**
|
|
806
|
+
* Resolve externals for Node scope builds (main/preload).
|
|
807
|
+
*
|
|
808
|
+
* Auto-externalizes: electron, Node builtins (bare + node: prefixed),
|
|
809
|
+
* package.json dependencies + optionalDependencies, and deep imports (pkg/subpath).
|
|
810
|
+
*/
|
|
811
|
+
async function resolveExternals(root) {
|
|
812
|
+
const pkgPath = resolve(root, "package.json");
|
|
813
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
814
|
+
const deps = new Set([
|
|
815
|
+
"electron",
|
|
816
|
+
...Object.keys(pkg.dependencies ?? {}),
|
|
817
|
+
...Object.keys(pkg.optionalDependencies ?? {})
|
|
818
|
+
]);
|
|
819
|
+
const builtins = builtinModules.flatMap((m) => [m, `node:${m}`]);
|
|
820
|
+
const depsArray = [...deps];
|
|
821
|
+
const deepPattern = depsArray.length > 0 ? new RegExp(`^(${depsArray.map(escapeRegExp).join("|")})/.+`) : null;
|
|
822
|
+
return deepPattern ? [
|
|
823
|
+
...depsArray,
|
|
824
|
+
...builtins,
|
|
825
|
+
deepPattern
|
|
826
|
+
] : [...depsArray, ...builtins];
|
|
827
|
+
}
|
|
828
|
+
function escapeRegExp(str) {
|
|
829
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
//#endregion
|
|
833
|
+
//#region src/dev/vite-node-config.ts
|
|
834
|
+
function resolveSourcemap$1(mode) {
|
|
835
|
+
if (!mode || mode === "linked" || mode === "external") return true;
|
|
836
|
+
if (mode === "inline") return "inline";
|
|
837
|
+
if (mode === "none") return false;
|
|
838
|
+
return true;
|
|
839
|
+
}
|
|
840
|
+
function createNodeConfig(opts) {
|
|
841
|
+
const resolveConditions = opts.scope === "preload" ? [
|
|
842
|
+
"node",
|
|
843
|
+
"import",
|
|
844
|
+
"default"
|
|
845
|
+
] : ["node", "import"];
|
|
846
|
+
const envPrefix = opts.scope === "main" ? ["MAIN_VITE_", "VITE_"] : ["PRELOAD_VITE_", "VITE_"];
|
|
847
|
+
const config = {
|
|
848
|
+
configFile: false,
|
|
849
|
+
root: opts.root,
|
|
850
|
+
plugins: opts.plugins ?? [],
|
|
851
|
+
customLogger: opts.customLogger,
|
|
852
|
+
envPrefix,
|
|
853
|
+
define: {
|
|
854
|
+
"process.env": "process.env",
|
|
855
|
+
...opts.define
|
|
856
|
+
},
|
|
857
|
+
build: {
|
|
858
|
+
ssr: opts.entry,
|
|
859
|
+
ssrEmitAssets: true,
|
|
860
|
+
outDir: opts.outDir,
|
|
861
|
+
emptyOutDir: true,
|
|
862
|
+
rolldownOptions: {
|
|
863
|
+
output: {
|
|
864
|
+
format: "es",
|
|
865
|
+
entryFileNames: "index.mjs"
|
|
866
|
+
},
|
|
867
|
+
external: opts.externals
|
|
868
|
+
},
|
|
869
|
+
target: "esnext",
|
|
870
|
+
sourcemap: resolveSourcemap$1(opts.sourcemap),
|
|
871
|
+
minify: false,
|
|
872
|
+
modulePreload: false,
|
|
873
|
+
watch: opts.watch ? {} : null,
|
|
874
|
+
reportCompressedSize: !opts.watch
|
|
875
|
+
},
|
|
876
|
+
ssr: {
|
|
877
|
+
target: "node",
|
|
878
|
+
noExternal: []
|
|
879
|
+
},
|
|
880
|
+
resolve: { conditions: resolveConditions },
|
|
881
|
+
logLevel: opts.logLevel ?? "warn",
|
|
882
|
+
clearScreen: opts.clearScreen
|
|
883
|
+
};
|
|
884
|
+
if (opts.scope === "main") {
|
|
885
|
+
const resourcesDir = resolve(opts.root, "resources");
|
|
886
|
+
if (existsSync(resourcesDir)) config.publicDir = resourcesDir;
|
|
887
|
+
}
|
|
888
|
+
if (opts.userViteConfig) {
|
|
889
|
+
const merged = mergeConfig(config, opts.userViteConfig);
|
|
890
|
+
validateMergedNodeConfig(merged, opts.scope);
|
|
891
|
+
enforceMergedNodeConfig(merged, opts.scope);
|
|
892
|
+
return merged;
|
|
893
|
+
}
|
|
894
|
+
return config;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
//#endregion
|
|
898
|
+
//#region src/dev/vite-renderer-config.ts
|
|
899
|
+
function resolveSourcemap(mode) {
|
|
900
|
+
if (!mode || mode === "linked" || mode === "external") return true;
|
|
901
|
+
if (mode === "inline") return "inline";
|
|
902
|
+
if (mode === "none") return false;
|
|
903
|
+
return true;
|
|
904
|
+
}
|
|
905
|
+
function createRendererConfig(opts) {
|
|
906
|
+
const input = {};
|
|
907
|
+
for (const win of opts.windows) {
|
|
908
|
+
const sourceDir = dirname(win.__source);
|
|
909
|
+
input[win.name] = resolve(sourceDir, win.entry);
|
|
910
|
+
}
|
|
911
|
+
const isBuild = !!opts.outDir;
|
|
912
|
+
const config = {
|
|
913
|
+
configFile: false,
|
|
914
|
+
root: opts.root,
|
|
915
|
+
customLogger: opts.customLogger,
|
|
916
|
+
envPrefix: ["RENDERER_VITE_", "VITE_"],
|
|
917
|
+
...!isBuild && { server: { strictPort: false } },
|
|
918
|
+
...isBuild && { base: "./" },
|
|
919
|
+
build: {
|
|
920
|
+
rolldownOptions: { input },
|
|
921
|
+
...isBuild && {
|
|
922
|
+
outDir: opts.outDir,
|
|
923
|
+
emptyOutDir: true,
|
|
924
|
+
minify: opts.minify ?? true,
|
|
925
|
+
sourcemap: resolveSourcemap(opts.sourcemap),
|
|
926
|
+
reportCompressedSize: true,
|
|
927
|
+
modulePreload: { polyfill: false }
|
|
928
|
+
}
|
|
929
|
+
},
|
|
930
|
+
logLevel: opts.logLevel ?? "info",
|
|
931
|
+
clearScreen: opts.clearScreen
|
|
932
|
+
};
|
|
933
|
+
if (opts.userViteConfigs?.length) {
|
|
934
|
+
let merged = config;
|
|
935
|
+
for (const userConfig of opts.userViteConfigs) merged = mergeConfig(merged, userConfig);
|
|
936
|
+
return merged;
|
|
937
|
+
}
|
|
938
|
+
return config;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
//#endregion
|
|
942
|
+
//#region ../../node_modules/.bun/@jridgewell+sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs
|
|
943
|
+
var comma = ",".charCodeAt(0);
|
|
944
|
+
var semicolon = ";".charCodeAt(0);
|
|
945
|
+
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
946
|
+
var intToChar = new Uint8Array(64);
|
|
947
|
+
var charToInt = new Uint8Array(128);
|
|
948
|
+
for (let i = 0; i < chars.length; i++) {
|
|
949
|
+
const c = chars.charCodeAt(i);
|
|
950
|
+
intToChar[i] = c;
|
|
951
|
+
charToInt[c] = i;
|
|
952
|
+
}
|
|
953
|
+
function encodeInteger(builder, num, relative) {
|
|
954
|
+
let delta = num - relative;
|
|
955
|
+
delta = delta < 0 ? -delta << 1 | 1 : delta << 1;
|
|
956
|
+
do {
|
|
957
|
+
let clamped = delta & 31;
|
|
958
|
+
delta >>>= 5;
|
|
959
|
+
if (delta > 0) clamped |= 32;
|
|
960
|
+
builder.write(intToChar[clamped]);
|
|
961
|
+
} while (delta > 0);
|
|
962
|
+
return num;
|
|
963
|
+
}
|
|
964
|
+
var bufLength = 1024 * 16;
|
|
965
|
+
var td = typeof TextDecoder !== "undefined" ? /* @__PURE__ */ new TextDecoder() : typeof Buffer !== "undefined" ? { decode(buf) {
|
|
966
|
+
return Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength).toString();
|
|
967
|
+
} } : { decode(buf) {
|
|
968
|
+
let out = "";
|
|
969
|
+
for (let i = 0; i < buf.length; i++) out += String.fromCharCode(buf[i]);
|
|
970
|
+
return out;
|
|
971
|
+
} };
|
|
972
|
+
var StringWriter = class {
|
|
973
|
+
constructor() {
|
|
974
|
+
this.pos = 0;
|
|
975
|
+
this.out = "";
|
|
976
|
+
this.buffer = new Uint8Array(bufLength);
|
|
977
|
+
}
|
|
978
|
+
write(v) {
|
|
979
|
+
const { buffer } = this;
|
|
980
|
+
buffer[this.pos++] = v;
|
|
981
|
+
if (this.pos === bufLength) {
|
|
982
|
+
this.out += td.decode(buffer);
|
|
983
|
+
this.pos = 0;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
flush() {
|
|
987
|
+
const { buffer, out, pos } = this;
|
|
988
|
+
return pos > 0 ? out + td.decode(buffer.subarray(0, pos)) : out;
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
function encode(decoded) {
|
|
992
|
+
const writer = new StringWriter();
|
|
993
|
+
let sourcesIndex = 0;
|
|
994
|
+
let sourceLine = 0;
|
|
995
|
+
let sourceColumn = 0;
|
|
996
|
+
let namesIndex = 0;
|
|
997
|
+
for (let i = 0; i < decoded.length; i++) {
|
|
998
|
+
const line = decoded[i];
|
|
999
|
+
if (i > 0) writer.write(semicolon);
|
|
1000
|
+
if (line.length === 0) continue;
|
|
1001
|
+
let genColumn = 0;
|
|
1002
|
+
for (let j = 0; j < line.length; j++) {
|
|
1003
|
+
const segment = line[j];
|
|
1004
|
+
if (j > 0) writer.write(comma);
|
|
1005
|
+
genColumn = encodeInteger(writer, segment[0], genColumn);
|
|
1006
|
+
if (segment.length === 1) continue;
|
|
1007
|
+
sourcesIndex = encodeInteger(writer, segment[1], sourcesIndex);
|
|
1008
|
+
sourceLine = encodeInteger(writer, segment[2], sourceLine);
|
|
1009
|
+
sourceColumn = encodeInteger(writer, segment[3], sourceColumn);
|
|
1010
|
+
if (segment.length === 4) continue;
|
|
1011
|
+
namesIndex = encodeInteger(writer, segment[4], namesIndex);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
return writer.flush();
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
//#endregion
|
|
1018
|
+
//#region ../../node_modules/.bun/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.mjs
|
|
1019
|
+
var BitSet = class BitSet {
|
|
1020
|
+
constructor(arg) {
|
|
1021
|
+
this.bits = arg instanceof BitSet ? arg.bits.slice() : [];
|
|
1022
|
+
}
|
|
1023
|
+
add(n) {
|
|
1024
|
+
this.bits[n >> 5] |= 1 << (n & 31);
|
|
1025
|
+
}
|
|
1026
|
+
has(n) {
|
|
1027
|
+
return !!(this.bits[n >> 5] & 1 << (n & 31));
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
var Chunk = class Chunk {
|
|
1031
|
+
constructor(start, end, content) {
|
|
1032
|
+
this.start = start;
|
|
1033
|
+
this.end = end;
|
|
1034
|
+
this.original = content;
|
|
1035
|
+
this.intro = "";
|
|
1036
|
+
this.outro = "";
|
|
1037
|
+
this.content = content;
|
|
1038
|
+
this.storeName = false;
|
|
1039
|
+
this.edited = false;
|
|
1040
|
+
this.previous = null;
|
|
1041
|
+
this.next = null;
|
|
1042
|
+
}
|
|
1043
|
+
appendLeft(content) {
|
|
1044
|
+
this.outro += content;
|
|
1045
|
+
}
|
|
1046
|
+
appendRight(content) {
|
|
1047
|
+
this.intro = this.intro + content;
|
|
1048
|
+
}
|
|
1049
|
+
clone() {
|
|
1050
|
+
const chunk = new Chunk(this.start, this.end, this.original);
|
|
1051
|
+
chunk.intro = this.intro;
|
|
1052
|
+
chunk.outro = this.outro;
|
|
1053
|
+
chunk.content = this.content;
|
|
1054
|
+
chunk.storeName = this.storeName;
|
|
1055
|
+
chunk.edited = this.edited;
|
|
1056
|
+
return chunk;
|
|
1057
|
+
}
|
|
1058
|
+
contains(index) {
|
|
1059
|
+
return this.start < index && index < this.end;
|
|
1060
|
+
}
|
|
1061
|
+
eachNext(fn) {
|
|
1062
|
+
let chunk = this;
|
|
1063
|
+
while (chunk) {
|
|
1064
|
+
fn(chunk);
|
|
1065
|
+
chunk = chunk.next;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
eachPrevious(fn) {
|
|
1069
|
+
let chunk = this;
|
|
1070
|
+
while (chunk) {
|
|
1071
|
+
fn(chunk);
|
|
1072
|
+
chunk = chunk.previous;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
edit(content, storeName, contentOnly) {
|
|
1076
|
+
this.content = content;
|
|
1077
|
+
if (!contentOnly) {
|
|
1078
|
+
this.intro = "";
|
|
1079
|
+
this.outro = "";
|
|
1080
|
+
}
|
|
1081
|
+
this.storeName = storeName;
|
|
1082
|
+
this.edited = true;
|
|
1083
|
+
return this;
|
|
1084
|
+
}
|
|
1085
|
+
prependLeft(content) {
|
|
1086
|
+
this.outro = content + this.outro;
|
|
1087
|
+
}
|
|
1088
|
+
prependRight(content) {
|
|
1089
|
+
this.intro = content + this.intro;
|
|
1090
|
+
}
|
|
1091
|
+
reset() {
|
|
1092
|
+
this.intro = "";
|
|
1093
|
+
this.outro = "";
|
|
1094
|
+
if (this.edited) {
|
|
1095
|
+
this.content = this.original;
|
|
1096
|
+
this.storeName = false;
|
|
1097
|
+
this.edited = false;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
split(index) {
|
|
1101
|
+
const sliceIndex = index - this.start;
|
|
1102
|
+
const originalBefore = this.original.slice(0, sliceIndex);
|
|
1103
|
+
const originalAfter = this.original.slice(sliceIndex);
|
|
1104
|
+
this.original = originalBefore;
|
|
1105
|
+
const newChunk = new Chunk(index, this.end, originalAfter);
|
|
1106
|
+
newChunk.outro = this.outro;
|
|
1107
|
+
this.outro = "";
|
|
1108
|
+
this.end = index;
|
|
1109
|
+
if (this.edited) {
|
|
1110
|
+
newChunk.edit("", false);
|
|
1111
|
+
this.content = "";
|
|
1112
|
+
} else this.content = originalBefore;
|
|
1113
|
+
newChunk.next = this.next;
|
|
1114
|
+
if (newChunk.next) newChunk.next.previous = newChunk;
|
|
1115
|
+
newChunk.previous = this;
|
|
1116
|
+
this.next = newChunk;
|
|
1117
|
+
return newChunk;
|
|
1118
|
+
}
|
|
1119
|
+
toString() {
|
|
1120
|
+
return this.intro + this.content + this.outro;
|
|
1121
|
+
}
|
|
1122
|
+
trimEnd(rx) {
|
|
1123
|
+
this.outro = this.outro.replace(rx, "");
|
|
1124
|
+
if (this.outro.length) return true;
|
|
1125
|
+
const trimmed = this.content.replace(rx, "");
|
|
1126
|
+
if (trimmed.length) {
|
|
1127
|
+
if (trimmed !== this.content) {
|
|
1128
|
+
this.split(this.start + trimmed.length).edit("", void 0, true);
|
|
1129
|
+
if (this.edited) this.edit(trimmed, this.storeName, true);
|
|
1130
|
+
}
|
|
1131
|
+
return true;
|
|
1132
|
+
} else {
|
|
1133
|
+
this.edit("", void 0, true);
|
|
1134
|
+
this.intro = this.intro.replace(rx, "");
|
|
1135
|
+
if (this.intro.length) return true;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
trimStart(rx) {
|
|
1139
|
+
this.intro = this.intro.replace(rx, "");
|
|
1140
|
+
if (this.intro.length) return true;
|
|
1141
|
+
const trimmed = this.content.replace(rx, "");
|
|
1142
|
+
if (trimmed.length) {
|
|
1143
|
+
if (trimmed !== this.content) {
|
|
1144
|
+
const newChunk = this.split(this.end - trimmed.length);
|
|
1145
|
+
if (this.edited) newChunk.edit(trimmed, this.storeName, true);
|
|
1146
|
+
this.edit("", void 0, true);
|
|
1147
|
+
}
|
|
1148
|
+
return true;
|
|
1149
|
+
} else {
|
|
1150
|
+
this.edit("", void 0, true);
|
|
1151
|
+
this.outro = this.outro.replace(rx, "");
|
|
1152
|
+
if (this.outro.length) return true;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
function getBtoa() {
|
|
1157
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.btoa === "function") return (str) => globalThis.btoa(unescape(encodeURIComponent(str)));
|
|
1158
|
+
else if (typeof Buffer === "function") return (str) => Buffer.from(str, "utf-8").toString("base64");
|
|
1159
|
+
else return () => {
|
|
1160
|
+
throw new Error("Unsupported environment: `window.btoa` or `Buffer` should be supported.");
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
const btoa = /* @__PURE__ */ getBtoa();
|
|
1164
|
+
var SourceMap = class {
|
|
1165
|
+
constructor(properties) {
|
|
1166
|
+
this.version = 3;
|
|
1167
|
+
this.file = properties.file;
|
|
1168
|
+
this.sources = properties.sources;
|
|
1169
|
+
this.sourcesContent = properties.sourcesContent;
|
|
1170
|
+
this.names = properties.names;
|
|
1171
|
+
this.mappings = encode(properties.mappings);
|
|
1172
|
+
if (typeof properties.x_google_ignoreList !== "undefined") this.x_google_ignoreList = properties.x_google_ignoreList;
|
|
1173
|
+
if (typeof properties.debugId !== "undefined") this.debugId = properties.debugId;
|
|
1174
|
+
}
|
|
1175
|
+
toString() {
|
|
1176
|
+
return JSON.stringify(this);
|
|
1177
|
+
}
|
|
1178
|
+
toUrl() {
|
|
1179
|
+
return "data:application/json;charset=utf-8;base64," + btoa(this.toString());
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
function guessIndent(code) {
|
|
1183
|
+
const lines = code.split("\n");
|
|
1184
|
+
const tabbed = lines.filter((line) => /^\t+/.test(line));
|
|
1185
|
+
const spaced = lines.filter((line) => /^ {2,}/.test(line));
|
|
1186
|
+
if (tabbed.length === 0 && spaced.length === 0) return null;
|
|
1187
|
+
if (tabbed.length >= spaced.length) return " ";
|
|
1188
|
+
const min = spaced.reduce((previous, current) => {
|
|
1189
|
+
const numSpaces = /^ +/.exec(current)[0].length;
|
|
1190
|
+
return Math.min(numSpaces, previous);
|
|
1191
|
+
}, Infinity);
|
|
1192
|
+
return new Array(min + 1).join(" ");
|
|
1193
|
+
}
|
|
1194
|
+
function getRelativePath(from, to) {
|
|
1195
|
+
const fromParts = from.split(/[/\\]/);
|
|
1196
|
+
const toParts = to.split(/[/\\]/);
|
|
1197
|
+
fromParts.pop();
|
|
1198
|
+
while (fromParts[0] === toParts[0]) {
|
|
1199
|
+
fromParts.shift();
|
|
1200
|
+
toParts.shift();
|
|
1201
|
+
}
|
|
1202
|
+
if (fromParts.length) {
|
|
1203
|
+
let i = fromParts.length;
|
|
1204
|
+
while (i--) fromParts[i] = "..";
|
|
1205
|
+
}
|
|
1206
|
+
return fromParts.concat(toParts).join("/");
|
|
1207
|
+
}
|
|
1208
|
+
const toString = Object.prototype.toString;
|
|
1209
|
+
function isObject(thing) {
|
|
1210
|
+
return toString.call(thing) === "[object Object]";
|
|
1211
|
+
}
|
|
1212
|
+
function getLocator(source) {
|
|
1213
|
+
const originalLines = source.split("\n");
|
|
1214
|
+
const lineOffsets = [];
|
|
1215
|
+
for (let i = 0, pos = 0; i < originalLines.length; i++) {
|
|
1216
|
+
lineOffsets.push(pos);
|
|
1217
|
+
pos += originalLines[i].length + 1;
|
|
1218
|
+
}
|
|
1219
|
+
return function locate(index) {
|
|
1220
|
+
let i = 0;
|
|
1221
|
+
let j = lineOffsets.length;
|
|
1222
|
+
while (i < j) {
|
|
1223
|
+
const m = i + j >> 1;
|
|
1224
|
+
if (index < lineOffsets[m]) j = m;
|
|
1225
|
+
else i = m + 1;
|
|
1226
|
+
}
|
|
1227
|
+
const line = i - 1;
|
|
1228
|
+
return {
|
|
1229
|
+
line,
|
|
1230
|
+
column: index - lineOffsets[line]
|
|
1231
|
+
};
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
const wordRegex = /\w/;
|
|
1235
|
+
var Mappings = class {
|
|
1236
|
+
constructor(hires) {
|
|
1237
|
+
this.hires = hires;
|
|
1238
|
+
this.generatedCodeLine = 0;
|
|
1239
|
+
this.generatedCodeColumn = 0;
|
|
1240
|
+
this.raw = [];
|
|
1241
|
+
this.rawSegments = this.raw[this.generatedCodeLine] = [];
|
|
1242
|
+
this.pending = null;
|
|
1243
|
+
}
|
|
1244
|
+
addEdit(sourceIndex, content, loc, nameIndex) {
|
|
1245
|
+
if (content.length) {
|
|
1246
|
+
const contentLengthMinusOne = content.length - 1;
|
|
1247
|
+
let contentLineEnd = content.indexOf("\n", 0);
|
|
1248
|
+
let previousContentLineEnd = -1;
|
|
1249
|
+
while (contentLineEnd >= 0 && contentLengthMinusOne > contentLineEnd) {
|
|
1250
|
+
const segment = [
|
|
1251
|
+
this.generatedCodeColumn,
|
|
1252
|
+
sourceIndex,
|
|
1253
|
+
loc.line,
|
|
1254
|
+
loc.column
|
|
1255
|
+
];
|
|
1256
|
+
if (nameIndex >= 0) segment.push(nameIndex);
|
|
1257
|
+
this.rawSegments.push(segment);
|
|
1258
|
+
this.generatedCodeLine += 1;
|
|
1259
|
+
this.raw[this.generatedCodeLine] = this.rawSegments = [];
|
|
1260
|
+
this.generatedCodeColumn = 0;
|
|
1261
|
+
previousContentLineEnd = contentLineEnd;
|
|
1262
|
+
contentLineEnd = content.indexOf("\n", contentLineEnd + 1);
|
|
1263
|
+
}
|
|
1264
|
+
const segment = [
|
|
1265
|
+
this.generatedCodeColumn,
|
|
1266
|
+
sourceIndex,
|
|
1267
|
+
loc.line,
|
|
1268
|
+
loc.column
|
|
1269
|
+
];
|
|
1270
|
+
if (nameIndex >= 0) segment.push(nameIndex);
|
|
1271
|
+
this.rawSegments.push(segment);
|
|
1272
|
+
this.advance(content.slice(previousContentLineEnd + 1));
|
|
1273
|
+
} else if (this.pending) {
|
|
1274
|
+
this.rawSegments.push(this.pending);
|
|
1275
|
+
this.advance(content);
|
|
1276
|
+
}
|
|
1277
|
+
this.pending = null;
|
|
1278
|
+
}
|
|
1279
|
+
addUneditedChunk(sourceIndex, chunk, original, loc, sourcemapLocations) {
|
|
1280
|
+
let originalCharIndex = chunk.start;
|
|
1281
|
+
let first = true;
|
|
1282
|
+
let charInHiresBoundary = false;
|
|
1283
|
+
while (originalCharIndex < chunk.end) {
|
|
1284
|
+
if (original[originalCharIndex] === "\n") {
|
|
1285
|
+
loc.line += 1;
|
|
1286
|
+
loc.column = 0;
|
|
1287
|
+
this.generatedCodeLine += 1;
|
|
1288
|
+
this.raw[this.generatedCodeLine] = this.rawSegments = [];
|
|
1289
|
+
this.generatedCodeColumn = 0;
|
|
1290
|
+
first = true;
|
|
1291
|
+
charInHiresBoundary = false;
|
|
1292
|
+
} else {
|
|
1293
|
+
if (this.hires || first || sourcemapLocations.has(originalCharIndex)) {
|
|
1294
|
+
const segment = [
|
|
1295
|
+
this.generatedCodeColumn,
|
|
1296
|
+
sourceIndex,
|
|
1297
|
+
loc.line,
|
|
1298
|
+
loc.column
|
|
1299
|
+
];
|
|
1300
|
+
if (this.hires === "boundary") if (wordRegex.test(original[originalCharIndex])) {
|
|
1301
|
+
if (!charInHiresBoundary) {
|
|
1302
|
+
this.rawSegments.push(segment);
|
|
1303
|
+
charInHiresBoundary = true;
|
|
1304
|
+
}
|
|
1305
|
+
} else {
|
|
1306
|
+
this.rawSegments.push(segment);
|
|
1307
|
+
charInHiresBoundary = false;
|
|
1308
|
+
}
|
|
1309
|
+
else this.rawSegments.push(segment);
|
|
1310
|
+
}
|
|
1311
|
+
loc.column += 1;
|
|
1312
|
+
this.generatedCodeColumn += 1;
|
|
1313
|
+
first = false;
|
|
1314
|
+
}
|
|
1315
|
+
originalCharIndex += 1;
|
|
1316
|
+
}
|
|
1317
|
+
this.pending = null;
|
|
1318
|
+
}
|
|
1319
|
+
advance(str) {
|
|
1320
|
+
if (!str) return;
|
|
1321
|
+
const lines = str.split("\n");
|
|
1322
|
+
if (lines.length > 1) {
|
|
1323
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
1324
|
+
this.generatedCodeLine++;
|
|
1325
|
+
this.raw[this.generatedCodeLine] = this.rawSegments = [];
|
|
1326
|
+
}
|
|
1327
|
+
this.generatedCodeColumn = 0;
|
|
1328
|
+
}
|
|
1329
|
+
this.generatedCodeColumn += lines[lines.length - 1].length;
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
const n = "\n";
|
|
1333
|
+
const warned = {
|
|
1334
|
+
insertLeft: false,
|
|
1335
|
+
insertRight: false,
|
|
1336
|
+
storeName: false
|
|
1337
|
+
};
|
|
1338
|
+
var MagicString = class MagicString {
|
|
1339
|
+
constructor(string, options = {}) {
|
|
1340
|
+
const chunk = new Chunk(0, string.length, string);
|
|
1341
|
+
Object.defineProperties(this, {
|
|
1342
|
+
original: {
|
|
1343
|
+
writable: true,
|
|
1344
|
+
value: string
|
|
1345
|
+
},
|
|
1346
|
+
outro: {
|
|
1347
|
+
writable: true,
|
|
1348
|
+
value: ""
|
|
1349
|
+
},
|
|
1350
|
+
intro: {
|
|
1351
|
+
writable: true,
|
|
1352
|
+
value: ""
|
|
1353
|
+
},
|
|
1354
|
+
firstChunk: {
|
|
1355
|
+
writable: true,
|
|
1356
|
+
value: chunk
|
|
1357
|
+
},
|
|
1358
|
+
lastChunk: {
|
|
1359
|
+
writable: true,
|
|
1360
|
+
value: chunk
|
|
1361
|
+
},
|
|
1362
|
+
lastSearchedChunk: {
|
|
1363
|
+
writable: true,
|
|
1364
|
+
value: chunk
|
|
1365
|
+
},
|
|
1366
|
+
byStart: {
|
|
1367
|
+
writable: true,
|
|
1368
|
+
value: {}
|
|
1369
|
+
},
|
|
1370
|
+
byEnd: {
|
|
1371
|
+
writable: true,
|
|
1372
|
+
value: {}
|
|
1373
|
+
},
|
|
1374
|
+
filename: {
|
|
1375
|
+
writable: true,
|
|
1376
|
+
value: options.filename
|
|
1377
|
+
},
|
|
1378
|
+
indentExclusionRanges: {
|
|
1379
|
+
writable: true,
|
|
1380
|
+
value: options.indentExclusionRanges
|
|
1381
|
+
},
|
|
1382
|
+
sourcemapLocations: {
|
|
1383
|
+
writable: true,
|
|
1384
|
+
value: new BitSet()
|
|
1385
|
+
},
|
|
1386
|
+
storedNames: {
|
|
1387
|
+
writable: true,
|
|
1388
|
+
value: {}
|
|
1389
|
+
},
|
|
1390
|
+
indentStr: {
|
|
1391
|
+
writable: true,
|
|
1392
|
+
value: void 0
|
|
1393
|
+
},
|
|
1394
|
+
ignoreList: {
|
|
1395
|
+
writable: true,
|
|
1396
|
+
value: options.ignoreList
|
|
1397
|
+
},
|
|
1398
|
+
offset: {
|
|
1399
|
+
writable: true,
|
|
1400
|
+
value: options.offset || 0
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
this.byStart[0] = chunk;
|
|
1404
|
+
this.byEnd[string.length] = chunk;
|
|
1405
|
+
}
|
|
1406
|
+
addSourcemapLocation(char) {
|
|
1407
|
+
this.sourcemapLocations.add(char);
|
|
1408
|
+
}
|
|
1409
|
+
append(content) {
|
|
1410
|
+
if (typeof content !== "string") throw new TypeError("outro content must be a string");
|
|
1411
|
+
this.outro += content;
|
|
1412
|
+
return this;
|
|
1413
|
+
}
|
|
1414
|
+
appendLeft(index, content) {
|
|
1415
|
+
index = index + this.offset;
|
|
1416
|
+
if (typeof content !== "string") throw new TypeError("inserted content must be a string");
|
|
1417
|
+
this._split(index);
|
|
1418
|
+
const chunk = this.byEnd[index];
|
|
1419
|
+
if (chunk) chunk.appendLeft(content);
|
|
1420
|
+
else this.intro += content;
|
|
1421
|
+
return this;
|
|
1422
|
+
}
|
|
1423
|
+
appendRight(index, content) {
|
|
1424
|
+
index = index + this.offset;
|
|
1425
|
+
if (typeof content !== "string") throw new TypeError("inserted content must be a string");
|
|
1426
|
+
this._split(index);
|
|
1427
|
+
const chunk = this.byStart[index];
|
|
1428
|
+
if (chunk) chunk.appendRight(content);
|
|
1429
|
+
else this.outro += content;
|
|
1430
|
+
return this;
|
|
1431
|
+
}
|
|
1432
|
+
clone() {
|
|
1433
|
+
const cloned = new MagicString(this.original, {
|
|
1434
|
+
filename: this.filename,
|
|
1435
|
+
offset: this.offset
|
|
1436
|
+
});
|
|
1437
|
+
let originalChunk = this.firstChunk;
|
|
1438
|
+
let clonedChunk = cloned.firstChunk = cloned.lastSearchedChunk = originalChunk.clone();
|
|
1439
|
+
while (originalChunk) {
|
|
1440
|
+
cloned.byStart[clonedChunk.start] = clonedChunk;
|
|
1441
|
+
cloned.byEnd[clonedChunk.end] = clonedChunk;
|
|
1442
|
+
const nextOriginalChunk = originalChunk.next;
|
|
1443
|
+
const nextClonedChunk = nextOriginalChunk && nextOriginalChunk.clone();
|
|
1444
|
+
if (nextClonedChunk) {
|
|
1445
|
+
clonedChunk.next = nextClonedChunk;
|
|
1446
|
+
nextClonedChunk.previous = clonedChunk;
|
|
1447
|
+
clonedChunk = nextClonedChunk;
|
|
1448
|
+
}
|
|
1449
|
+
originalChunk = nextOriginalChunk;
|
|
1450
|
+
}
|
|
1451
|
+
cloned.lastChunk = clonedChunk;
|
|
1452
|
+
if (this.indentExclusionRanges) cloned.indentExclusionRanges = this.indentExclusionRanges.slice();
|
|
1453
|
+
cloned.sourcemapLocations = new BitSet(this.sourcemapLocations);
|
|
1454
|
+
cloned.intro = this.intro;
|
|
1455
|
+
cloned.outro = this.outro;
|
|
1456
|
+
return cloned;
|
|
1457
|
+
}
|
|
1458
|
+
generateDecodedMap(options) {
|
|
1459
|
+
options = options || {};
|
|
1460
|
+
const sourceIndex = 0;
|
|
1461
|
+
const names = Object.keys(this.storedNames);
|
|
1462
|
+
const mappings = new Mappings(options.hires);
|
|
1463
|
+
const locate = getLocator(this.original);
|
|
1464
|
+
if (this.intro) mappings.advance(this.intro);
|
|
1465
|
+
this.firstChunk.eachNext((chunk) => {
|
|
1466
|
+
const loc = locate(chunk.start);
|
|
1467
|
+
if (chunk.intro.length) mappings.advance(chunk.intro);
|
|
1468
|
+
if (chunk.edited) mappings.addEdit(sourceIndex, chunk.content, loc, chunk.storeName ? names.indexOf(chunk.original) : -1);
|
|
1469
|
+
else mappings.addUneditedChunk(sourceIndex, chunk, this.original, loc, this.sourcemapLocations);
|
|
1470
|
+
if (chunk.outro.length) mappings.advance(chunk.outro);
|
|
1471
|
+
});
|
|
1472
|
+
if (this.outro) mappings.advance(this.outro);
|
|
1473
|
+
return {
|
|
1474
|
+
file: options.file ? options.file.split(/[/\\]/).pop() : void 0,
|
|
1475
|
+
sources: [options.source ? getRelativePath(options.file || "", options.source) : options.file || ""],
|
|
1476
|
+
sourcesContent: options.includeContent ? [this.original] : void 0,
|
|
1477
|
+
names,
|
|
1478
|
+
mappings: mappings.raw,
|
|
1479
|
+
x_google_ignoreList: this.ignoreList ? [sourceIndex] : void 0
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
generateMap(options) {
|
|
1483
|
+
return new SourceMap(this.generateDecodedMap(options));
|
|
1484
|
+
}
|
|
1485
|
+
_ensureindentStr() {
|
|
1486
|
+
if (this.indentStr === void 0) this.indentStr = guessIndent(this.original);
|
|
1487
|
+
}
|
|
1488
|
+
_getRawIndentString() {
|
|
1489
|
+
this._ensureindentStr();
|
|
1490
|
+
return this.indentStr;
|
|
1491
|
+
}
|
|
1492
|
+
getIndentString() {
|
|
1493
|
+
this._ensureindentStr();
|
|
1494
|
+
return this.indentStr === null ? " " : this.indentStr;
|
|
1495
|
+
}
|
|
1496
|
+
indent(indentStr, options) {
|
|
1497
|
+
const pattern = /^[^\r\n]/gm;
|
|
1498
|
+
if (isObject(indentStr)) {
|
|
1499
|
+
options = indentStr;
|
|
1500
|
+
indentStr = void 0;
|
|
1501
|
+
}
|
|
1502
|
+
if (indentStr === void 0) {
|
|
1503
|
+
this._ensureindentStr();
|
|
1504
|
+
indentStr = this.indentStr || " ";
|
|
1505
|
+
}
|
|
1506
|
+
if (indentStr === "") return this;
|
|
1507
|
+
options = options || {};
|
|
1508
|
+
const isExcluded = {};
|
|
1509
|
+
if (options.exclude) (typeof options.exclude[0] === "number" ? [options.exclude] : options.exclude).forEach((exclusion) => {
|
|
1510
|
+
for (let i = exclusion[0]; i < exclusion[1]; i += 1) isExcluded[i] = true;
|
|
1511
|
+
});
|
|
1512
|
+
let shouldIndentNextCharacter = options.indentStart !== false;
|
|
1513
|
+
const replacer = (match) => {
|
|
1514
|
+
if (shouldIndentNextCharacter) return `${indentStr}${match}`;
|
|
1515
|
+
shouldIndentNextCharacter = true;
|
|
1516
|
+
return match;
|
|
1517
|
+
};
|
|
1518
|
+
this.intro = this.intro.replace(pattern, replacer);
|
|
1519
|
+
let charIndex = 0;
|
|
1520
|
+
let chunk = this.firstChunk;
|
|
1521
|
+
while (chunk) {
|
|
1522
|
+
const end = chunk.end;
|
|
1523
|
+
if (chunk.edited) {
|
|
1524
|
+
if (!isExcluded[charIndex]) {
|
|
1525
|
+
chunk.content = chunk.content.replace(pattern, replacer);
|
|
1526
|
+
if (chunk.content.length) shouldIndentNextCharacter = chunk.content[chunk.content.length - 1] === "\n";
|
|
1527
|
+
}
|
|
1528
|
+
} else {
|
|
1529
|
+
charIndex = chunk.start;
|
|
1530
|
+
while (charIndex < end) {
|
|
1531
|
+
if (!isExcluded[charIndex]) {
|
|
1532
|
+
const char = this.original[charIndex];
|
|
1533
|
+
if (char === "\n") shouldIndentNextCharacter = true;
|
|
1534
|
+
else if (char !== "\r" && shouldIndentNextCharacter) {
|
|
1535
|
+
shouldIndentNextCharacter = false;
|
|
1536
|
+
if (charIndex === chunk.start) chunk.prependRight(indentStr);
|
|
1537
|
+
else {
|
|
1538
|
+
this._splitChunk(chunk, charIndex);
|
|
1539
|
+
chunk = chunk.next;
|
|
1540
|
+
chunk.prependRight(indentStr);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
charIndex += 1;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
charIndex = chunk.end;
|
|
1548
|
+
chunk = chunk.next;
|
|
1549
|
+
}
|
|
1550
|
+
this.outro = this.outro.replace(pattern, replacer);
|
|
1551
|
+
return this;
|
|
1552
|
+
}
|
|
1553
|
+
insert() {
|
|
1554
|
+
throw new Error("magicString.insert(...) is deprecated. Use prependRight(...) or appendLeft(...)");
|
|
1555
|
+
}
|
|
1556
|
+
insertLeft(index, content) {
|
|
1557
|
+
if (!warned.insertLeft) {
|
|
1558
|
+
console.warn("magicString.insertLeft(...) is deprecated. Use magicString.appendLeft(...) instead");
|
|
1559
|
+
warned.insertLeft = true;
|
|
1560
|
+
}
|
|
1561
|
+
return this.appendLeft(index, content);
|
|
1562
|
+
}
|
|
1563
|
+
insertRight(index, content) {
|
|
1564
|
+
if (!warned.insertRight) {
|
|
1565
|
+
console.warn("magicString.insertRight(...) is deprecated. Use magicString.prependRight(...) instead");
|
|
1566
|
+
warned.insertRight = true;
|
|
1567
|
+
}
|
|
1568
|
+
return this.prependRight(index, content);
|
|
1569
|
+
}
|
|
1570
|
+
move(start, end, index) {
|
|
1571
|
+
start = start + this.offset;
|
|
1572
|
+
end = end + this.offset;
|
|
1573
|
+
index = index + this.offset;
|
|
1574
|
+
if (index >= start && index <= end) throw new Error("Cannot move a selection inside itself");
|
|
1575
|
+
this._split(start);
|
|
1576
|
+
this._split(end);
|
|
1577
|
+
this._split(index);
|
|
1578
|
+
const first = this.byStart[start];
|
|
1579
|
+
const last = this.byEnd[end];
|
|
1580
|
+
const oldLeft = first.previous;
|
|
1581
|
+
const oldRight = last.next;
|
|
1582
|
+
const newRight = this.byStart[index];
|
|
1583
|
+
if (!newRight && last === this.lastChunk) return this;
|
|
1584
|
+
const newLeft = newRight ? newRight.previous : this.lastChunk;
|
|
1585
|
+
if (oldLeft) oldLeft.next = oldRight;
|
|
1586
|
+
if (oldRight) oldRight.previous = oldLeft;
|
|
1587
|
+
if (newLeft) newLeft.next = first;
|
|
1588
|
+
if (newRight) newRight.previous = last;
|
|
1589
|
+
if (!first.previous) this.firstChunk = last.next;
|
|
1590
|
+
if (!last.next) {
|
|
1591
|
+
this.lastChunk = first.previous;
|
|
1592
|
+
this.lastChunk.next = null;
|
|
1593
|
+
}
|
|
1594
|
+
first.previous = newLeft;
|
|
1595
|
+
last.next = newRight || null;
|
|
1596
|
+
if (!newLeft) this.firstChunk = first;
|
|
1597
|
+
if (!newRight) this.lastChunk = last;
|
|
1598
|
+
return this;
|
|
1599
|
+
}
|
|
1600
|
+
overwrite(start, end, content, options) {
|
|
1601
|
+
options = options || {};
|
|
1602
|
+
return this.update(start, end, content, {
|
|
1603
|
+
...options,
|
|
1604
|
+
overwrite: !options.contentOnly
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
update(start, end, content, options) {
|
|
1608
|
+
start = start + this.offset;
|
|
1609
|
+
end = end + this.offset;
|
|
1610
|
+
if (typeof content !== "string") throw new TypeError("replacement content must be a string");
|
|
1611
|
+
if (this.original.length !== 0) {
|
|
1612
|
+
while (start < 0) start += this.original.length;
|
|
1613
|
+
while (end < 0) end += this.original.length;
|
|
1614
|
+
}
|
|
1615
|
+
if (end > this.original.length) throw new Error("end is out of bounds");
|
|
1616
|
+
if (start === end) throw new Error("Cannot overwrite a zero-length range – use appendLeft or prependRight instead");
|
|
1617
|
+
this._split(start);
|
|
1618
|
+
this._split(end);
|
|
1619
|
+
if (options === true) {
|
|
1620
|
+
if (!warned.storeName) {
|
|
1621
|
+
console.warn("The final argument to magicString.overwrite(...) should be an options object. See https://github.com/rich-harris/magic-string");
|
|
1622
|
+
warned.storeName = true;
|
|
1623
|
+
}
|
|
1624
|
+
options = { storeName: true };
|
|
1625
|
+
}
|
|
1626
|
+
const storeName = options !== void 0 ? options.storeName : false;
|
|
1627
|
+
const overwrite = options !== void 0 ? options.overwrite : false;
|
|
1628
|
+
if (storeName) {
|
|
1629
|
+
const original = this.original.slice(start, end);
|
|
1630
|
+
Object.defineProperty(this.storedNames, original, {
|
|
1631
|
+
writable: true,
|
|
1632
|
+
value: true,
|
|
1633
|
+
enumerable: true
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
const first = this.byStart[start];
|
|
1637
|
+
const last = this.byEnd[end];
|
|
1638
|
+
if (first) {
|
|
1639
|
+
let chunk = first;
|
|
1640
|
+
while (chunk !== last) {
|
|
1641
|
+
if (chunk.next !== this.byStart[chunk.end]) throw new Error("Cannot overwrite across a split point");
|
|
1642
|
+
chunk = chunk.next;
|
|
1643
|
+
chunk.edit("", false);
|
|
1644
|
+
}
|
|
1645
|
+
first.edit(content, storeName, !overwrite);
|
|
1646
|
+
} else {
|
|
1647
|
+
const newChunk = new Chunk(start, end, "").edit(content, storeName);
|
|
1648
|
+
last.next = newChunk;
|
|
1649
|
+
newChunk.previous = last;
|
|
1650
|
+
}
|
|
1651
|
+
return this;
|
|
1652
|
+
}
|
|
1653
|
+
prepend(content) {
|
|
1654
|
+
if (typeof content !== "string") throw new TypeError("outro content must be a string");
|
|
1655
|
+
this.intro = content + this.intro;
|
|
1656
|
+
return this;
|
|
1657
|
+
}
|
|
1658
|
+
prependLeft(index, content) {
|
|
1659
|
+
index = index + this.offset;
|
|
1660
|
+
if (typeof content !== "string") throw new TypeError("inserted content must be a string");
|
|
1661
|
+
this._split(index);
|
|
1662
|
+
const chunk = this.byEnd[index];
|
|
1663
|
+
if (chunk) chunk.prependLeft(content);
|
|
1664
|
+
else this.intro = content + this.intro;
|
|
1665
|
+
return this;
|
|
1666
|
+
}
|
|
1667
|
+
prependRight(index, content) {
|
|
1668
|
+
index = index + this.offset;
|
|
1669
|
+
if (typeof content !== "string") throw new TypeError("inserted content must be a string");
|
|
1670
|
+
this._split(index);
|
|
1671
|
+
const chunk = this.byStart[index];
|
|
1672
|
+
if (chunk) chunk.prependRight(content);
|
|
1673
|
+
else this.outro = content + this.outro;
|
|
1674
|
+
return this;
|
|
1675
|
+
}
|
|
1676
|
+
remove(start, end) {
|
|
1677
|
+
start = start + this.offset;
|
|
1678
|
+
end = end + this.offset;
|
|
1679
|
+
if (this.original.length !== 0) {
|
|
1680
|
+
while (start < 0) start += this.original.length;
|
|
1681
|
+
while (end < 0) end += this.original.length;
|
|
1682
|
+
}
|
|
1683
|
+
if (start === end) return this;
|
|
1684
|
+
if (start < 0 || end > this.original.length) throw new Error("Character is out of bounds");
|
|
1685
|
+
if (start > end) throw new Error("end must be greater than start");
|
|
1686
|
+
this._split(start);
|
|
1687
|
+
this._split(end);
|
|
1688
|
+
let chunk = this.byStart[start];
|
|
1689
|
+
while (chunk) {
|
|
1690
|
+
chunk.intro = "";
|
|
1691
|
+
chunk.outro = "";
|
|
1692
|
+
chunk.edit("");
|
|
1693
|
+
chunk = end > chunk.end ? this.byStart[chunk.end] : null;
|
|
1694
|
+
}
|
|
1695
|
+
return this;
|
|
1696
|
+
}
|
|
1697
|
+
reset(start, end) {
|
|
1698
|
+
start = start + this.offset;
|
|
1699
|
+
end = end + this.offset;
|
|
1700
|
+
if (this.original.length !== 0) {
|
|
1701
|
+
while (start < 0) start += this.original.length;
|
|
1702
|
+
while (end < 0) end += this.original.length;
|
|
1703
|
+
}
|
|
1704
|
+
if (start === end) return this;
|
|
1705
|
+
if (start < 0 || end > this.original.length) throw new Error("Character is out of bounds");
|
|
1706
|
+
if (start > end) throw new Error("end must be greater than start");
|
|
1707
|
+
this._split(start);
|
|
1708
|
+
this._split(end);
|
|
1709
|
+
let chunk = this.byStart[start];
|
|
1710
|
+
while (chunk) {
|
|
1711
|
+
chunk.reset();
|
|
1712
|
+
chunk = end > chunk.end ? this.byStart[chunk.end] : null;
|
|
1713
|
+
}
|
|
1714
|
+
return this;
|
|
1715
|
+
}
|
|
1716
|
+
lastChar() {
|
|
1717
|
+
if (this.outro.length) return this.outro[this.outro.length - 1];
|
|
1718
|
+
let chunk = this.lastChunk;
|
|
1719
|
+
do {
|
|
1720
|
+
if (chunk.outro.length) return chunk.outro[chunk.outro.length - 1];
|
|
1721
|
+
if (chunk.content.length) return chunk.content[chunk.content.length - 1];
|
|
1722
|
+
if (chunk.intro.length) return chunk.intro[chunk.intro.length - 1];
|
|
1723
|
+
} while (chunk = chunk.previous);
|
|
1724
|
+
if (this.intro.length) return this.intro[this.intro.length - 1];
|
|
1725
|
+
return "";
|
|
1726
|
+
}
|
|
1727
|
+
lastLine() {
|
|
1728
|
+
let lineIndex = this.outro.lastIndexOf(n);
|
|
1729
|
+
if (lineIndex !== -1) return this.outro.substr(lineIndex + 1);
|
|
1730
|
+
let lineStr = this.outro;
|
|
1731
|
+
let chunk = this.lastChunk;
|
|
1732
|
+
do {
|
|
1733
|
+
if (chunk.outro.length > 0) {
|
|
1734
|
+
lineIndex = chunk.outro.lastIndexOf(n);
|
|
1735
|
+
if (lineIndex !== -1) return chunk.outro.substr(lineIndex + 1) + lineStr;
|
|
1736
|
+
lineStr = chunk.outro + lineStr;
|
|
1737
|
+
}
|
|
1738
|
+
if (chunk.content.length > 0) {
|
|
1739
|
+
lineIndex = chunk.content.lastIndexOf(n);
|
|
1740
|
+
if (lineIndex !== -1) return chunk.content.substr(lineIndex + 1) + lineStr;
|
|
1741
|
+
lineStr = chunk.content + lineStr;
|
|
1742
|
+
}
|
|
1743
|
+
if (chunk.intro.length > 0) {
|
|
1744
|
+
lineIndex = chunk.intro.lastIndexOf(n);
|
|
1745
|
+
if (lineIndex !== -1) return chunk.intro.substr(lineIndex + 1) + lineStr;
|
|
1746
|
+
lineStr = chunk.intro + lineStr;
|
|
1747
|
+
}
|
|
1748
|
+
} while (chunk = chunk.previous);
|
|
1749
|
+
lineIndex = this.intro.lastIndexOf(n);
|
|
1750
|
+
if (lineIndex !== -1) return this.intro.substr(lineIndex + 1) + lineStr;
|
|
1751
|
+
return this.intro + lineStr;
|
|
1752
|
+
}
|
|
1753
|
+
slice(start = 0, end = this.original.length - this.offset) {
|
|
1754
|
+
start = start + this.offset;
|
|
1755
|
+
end = end + this.offset;
|
|
1756
|
+
if (this.original.length !== 0) {
|
|
1757
|
+
while (start < 0) start += this.original.length;
|
|
1758
|
+
while (end < 0) end += this.original.length;
|
|
1759
|
+
}
|
|
1760
|
+
let result = "";
|
|
1761
|
+
let chunk = this.firstChunk;
|
|
1762
|
+
while (chunk && (chunk.start > start || chunk.end <= start)) {
|
|
1763
|
+
if (chunk.start < end && chunk.end >= end) return result;
|
|
1764
|
+
chunk = chunk.next;
|
|
1765
|
+
}
|
|
1766
|
+
if (chunk && chunk.edited && chunk.start !== start) throw new Error(`Cannot use replaced character ${start} as slice start anchor.`);
|
|
1767
|
+
const startChunk = chunk;
|
|
1768
|
+
while (chunk) {
|
|
1769
|
+
if (chunk.intro && (startChunk !== chunk || chunk.start === start)) result += chunk.intro;
|
|
1770
|
+
const containsEnd = chunk.start < end && chunk.end >= end;
|
|
1771
|
+
if (containsEnd && chunk.edited && chunk.end !== end) throw new Error(`Cannot use replaced character ${end} as slice end anchor.`);
|
|
1772
|
+
const sliceStart = startChunk === chunk ? start - chunk.start : 0;
|
|
1773
|
+
const sliceEnd = containsEnd ? chunk.content.length + end - chunk.end : chunk.content.length;
|
|
1774
|
+
result += chunk.content.slice(sliceStart, sliceEnd);
|
|
1775
|
+
if (chunk.outro && (!containsEnd || chunk.end === end)) result += chunk.outro;
|
|
1776
|
+
if (containsEnd) break;
|
|
1777
|
+
chunk = chunk.next;
|
|
1778
|
+
}
|
|
1779
|
+
return result;
|
|
1780
|
+
}
|
|
1781
|
+
snip(start, end) {
|
|
1782
|
+
const clone = this.clone();
|
|
1783
|
+
clone.remove(0, start);
|
|
1784
|
+
clone.remove(end, clone.original.length);
|
|
1785
|
+
return clone;
|
|
1786
|
+
}
|
|
1787
|
+
_split(index) {
|
|
1788
|
+
if (this.byStart[index] || this.byEnd[index]) return;
|
|
1789
|
+
let chunk = this.lastSearchedChunk;
|
|
1790
|
+
let previousChunk = chunk;
|
|
1791
|
+
const searchForward = index > chunk.end;
|
|
1792
|
+
while (chunk) {
|
|
1793
|
+
if (chunk.contains(index)) return this._splitChunk(chunk, index);
|
|
1794
|
+
chunk = searchForward ? this.byStart[chunk.end] : this.byEnd[chunk.start];
|
|
1795
|
+
if (chunk === previousChunk) return;
|
|
1796
|
+
previousChunk = chunk;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
_splitChunk(chunk, index) {
|
|
1800
|
+
if (chunk.edited && chunk.content.length) {
|
|
1801
|
+
const loc = getLocator(this.original)(index);
|
|
1802
|
+
throw new Error(`Cannot split a chunk that has already been edited (${loc.line}:${loc.column} – "${chunk.original}")`);
|
|
1803
|
+
}
|
|
1804
|
+
const newChunk = chunk.split(index);
|
|
1805
|
+
this.byEnd[index] = chunk;
|
|
1806
|
+
this.byStart[index] = newChunk;
|
|
1807
|
+
this.byEnd[newChunk.end] = newChunk;
|
|
1808
|
+
if (chunk === this.lastChunk) this.lastChunk = newChunk;
|
|
1809
|
+
this.lastSearchedChunk = chunk;
|
|
1810
|
+
return true;
|
|
1811
|
+
}
|
|
1812
|
+
toString() {
|
|
1813
|
+
let str = this.intro;
|
|
1814
|
+
let chunk = this.firstChunk;
|
|
1815
|
+
while (chunk) {
|
|
1816
|
+
str += chunk.toString();
|
|
1817
|
+
chunk = chunk.next;
|
|
1818
|
+
}
|
|
1819
|
+
return str + this.outro;
|
|
1820
|
+
}
|
|
1821
|
+
isEmpty() {
|
|
1822
|
+
let chunk = this.firstChunk;
|
|
1823
|
+
do
|
|
1824
|
+
if (chunk.intro.length && chunk.intro.trim() || chunk.content.length && chunk.content.trim() || chunk.outro.length && chunk.outro.trim()) return false;
|
|
1825
|
+
while (chunk = chunk.next);
|
|
1826
|
+
return true;
|
|
1827
|
+
}
|
|
1828
|
+
length() {
|
|
1829
|
+
let chunk = this.firstChunk;
|
|
1830
|
+
let length = 0;
|
|
1831
|
+
do
|
|
1832
|
+
length += chunk.intro.length + chunk.content.length + chunk.outro.length;
|
|
1833
|
+
while (chunk = chunk.next);
|
|
1834
|
+
return length;
|
|
1835
|
+
}
|
|
1836
|
+
trimLines() {
|
|
1837
|
+
return this.trim("[\\r\\n]");
|
|
1838
|
+
}
|
|
1839
|
+
trim(charType) {
|
|
1840
|
+
return this.trimStart(charType).trimEnd(charType);
|
|
1841
|
+
}
|
|
1842
|
+
trimEndAborted(charType) {
|
|
1843
|
+
const rx = new RegExp((charType || "\\s") + "+$");
|
|
1844
|
+
this.outro = this.outro.replace(rx, "");
|
|
1845
|
+
if (this.outro.length) return true;
|
|
1846
|
+
let chunk = this.lastChunk;
|
|
1847
|
+
do {
|
|
1848
|
+
const end = chunk.end;
|
|
1849
|
+
const aborted = chunk.trimEnd(rx);
|
|
1850
|
+
if (chunk.end !== end) {
|
|
1851
|
+
if (this.lastChunk === chunk) this.lastChunk = chunk.next;
|
|
1852
|
+
this.byEnd[chunk.end] = chunk;
|
|
1853
|
+
this.byStart[chunk.next.start] = chunk.next;
|
|
1854
|
+
this.byEnd[chunk.next.end] = chunk.next;
|
|
1855
|
+
}
|
|
1856
|
+
if (aborted) return true;
|
|
1857
|
+
chunk = chunk.previous;
|
|
1858
|
+
} while (chunk);
|
|
1859
|
+
return false;
|
|
1860
|
+
}
|
|
1861
|
+
trimEnd(charType) {
|
|
1862
|
+
this.trimEndAborted(charType);
|
|
1863
|
+
return this;
|
|
1864
|
+
}
|
|
1865
|
+
trimStartAborted(charType) {
|
|
1866
|
+
const rx = new RegExp("^" + (charType || "\\s") + "+");
|
|
1867
|
+
this.intro = this.intro.replace(rx, "");
|
|
1868
|
+
if (this.intro.length) return true;
|
|
1869
|
+
let chunk = this.firstChunk;
|
|
1870
|
+
do {
|
|
1871
|
+
const end = chunk.end;
|
|
1872
|
+
const aborted = chunk.trimStart(rx);
|
|
1873
|
+
if (chunk.end !== end) {
|
|
1874
|
+
if (chunk === this.lastChunk) this.lastChunk = chunk.next;
|
|
1875
|
+
this.byEnd[chunk.end] = chunk;
|
|
1876
|
+
this.byStart[chunk.next.start] = chunk.next;
|
|
1877
|
+
this.byEnd[chunk.next.end] = chunk.next;
|
|
1878
|
+
}
|
|
1879
|
+
if (aborted) return true;
|
|
1880
|
+
chunk = chunk.next;
|
|
1881
|
+
} while (chunk);
|
|
1882
|
+
return false;
|
|
1883
|
+
}
|
|
1884
|
+
trimStart(charType) {
|
|
1885
|
+
this.trimStartAborted(charType);
|
|
1886
|
+
return this;
|
|
1887
|
+
}
|
|
1888
|
+
hasChanged() {
|
|
1889
|
+
return this.original !== this.toString();
|
|
1890
|
+
}
|
|
1891
|
+
_replaceRegexp(searchValue, replacement) {
|
|
1892
|
+
function getReplacement(match, str) {
|
|
1893
|
+
if (typeof replacement === "string") return replacement.replace(/\$(\$|&|\d+)/g, (_, i) => {
|
|
1894
|
+
if (i === "$") return "$";
|
|
1895
|
+
if (i === "&") return match[0];
|
|
1896
|
+
if (+i < match.length) return match[+i];
|
|
1897
|
+
return `$${i}`;
|
|
1898
|
+
});
|
|
1899
|
+
else return replacement(...match, match.index, str, match.groups);
|
|
1900
|
+
}
|
|
1901
|
+
function matchAll(re, str) {
|
|
1902
|
+
let match;
|
|
1903
|
+
const matches = [];
|
|
1904
|
+
while (match = re.exec(str)) matches.push(match);
|
|
1905
|
+
return matches;
|
|
1906
|
+
}
|
|
1907
|
+
if (searchValue.global) matchAll(searchValue, this.original).forEach((match) => {
|
|
1908
|
+
if (match.index != null) {
|
|
1909
|
+
const replacement = getReplacement(match, this.original);
|
|
1910
|
+
if (replacement !== match[0]) this.overwrite(match.index, match.index + match[0].length, replacement);
|
|
1911
|
+
}
|
|
1912
|
+
});
|
|
1913
|
+
else {
|
|
1914
|
+
const match = this.original.match(searchValue);
|
|
1915
|
+
if (match && match.index != null) {
|
|
1916
|
+
const replacement = getReplacement(match, this.original);
|
|
1917
|
+
if (replacement !== match[0]) this.overwrite(match.index, match.index + match[0].length, replacement);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
return this;
|
|
1921
|
+
}
|
|
1922
|
+
_replaceString(string, replacement) {
|
|
1923
|
+
const { original } = this;
|
|
1924
|
+
const index = original.indexOf(string);
|
|
1925
|
+
if (index !== -1) {
|
|
1926
|
+
if (typeof replacement === "function") replacement = replacement(string, index, original);
|
|
1927
|
+
if (string !== replacement) this.overwrite(index, index + string.length, replacement);
|
|
1928
|
+
}
|
|
1929
|
+
return this;
|
|
1930
|
+
}
|
|
1931
|
+
replace(searchValue, replacement) {
|
|
1932
|
+
if (typeof searchValue === "string") return this._replaceString(searchValue, replacement);
|
|
1933
|
+
return this._replaceRegexp(searchValue, replacement);
|
|
1934
|
+
}
|
|
1935
|
+
_replaceAllString(string, replacement) {
|
|
1936
|
+
const { original } = this;
|
|
1937
|
+
const stringLength = string.length;
|
|
1938
|
+
for (let index = original.indexOf(string); index !== -1; index = original.indexOf(string, index + stringLength)) {
|
|
1939
|
+
const previous = original.slice(index, index + stringLength);
|
|
1940
|
+
let _replacement = replacement;
|
|
1941
|
+
if (typeof replacement === "function") _replacement = replacement(previous, index, original);
|
|
1942
|
+
if (previous !== _replacement) this.overwrite(index, index + stringLength, _replacement);
|
|
1943
|
+
}
|
|
1944
|
+
return this;
|
|
1945
|
+
}
|
|
1946
|
+
replaceAll(searchValue, replacement) {
|
|
1947
|
+
if (typeof searchValue === "string") return this._replaceAllString(searchValue, replacement);
|
|
1948
|
+
if (!searchValue.global) throw new TypeError("MagicString.prototype.replaceAll called with a non-global RegExp argument");
|
|
1949
|
+
return this._replaceRegexp(searchValue, replacement);
|
|
1950
|
+
}
|
|
1951
|
+
};
|
|
1952
|
+
|
|
1953
|
+
//#endregion
|
|
1954
|
+
//#region src/plugins/utils.ts
|
|
1955
|
+
/** Strip query and hash from a URL/path. */
|
|
1956
|
+
function cleanUrl(url) {
|
|
1957
|
+
return url.split("?")[0].split("#")[0];
|
|
1958
|
+
}
|
|
1959
|
+
/** Get a relative path from an importer chunk to a target file, always prefixed with `.` */
|
|
1960
|
+
function toRelativePath(from, to) {
|
|
1961
|
+
const rel = relative(dirname(from), to);
|
|
1962
|
+
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
//#endregion
|
|
1966
|
+
//#region src/plugins/asset.ts
|
|
1967
|
+
const ASSET_QUERY_RE = /[?&]asset(?:&|$)/;
|
|
1968
|
+
const ASSET_IMPORT_RE = /(?:[?&]asset(?:&|$)|\.node$)/;
|
|
1969
|
+
const ASSET_PLACEHOLDER_RE = /__ELECTRO_ASSET__([\w$]+)__/g;
|
|
1970
|
+
function assetPlugin() {
|
|
1971
|
+
const assetCache = /* @__PURE__ */ new Map();
|
|
1972
|
+
return {
|
|
1973
|
+
name: "electro:asset",
|
|
1974
|
+
apply: "build",
|
|
1975
|
+
async load(id) {
|
|
1976
|
+
if (id.startsWith("\0") || !ASSET_IMPORT_RE.test(id)) return;
|
|
1977
|
+
const file = cleanUrl(id);
|
|
1978
|
+
let referenceId;
|
|
1979
|
+
const cached = assetCache.get(file);
|
|
1980
|
+
if (cached) referenceId = cached;
|
|
1981
|
+
else {
|
|
1982
|
+
const source = await readFile(file);
|
|
1983
|
+
referenceId = `__ELECTRO_ASSET__${this.emitFile({
|
|
1984
|
+
type: "asset",
|
|
1985
|
+
name: basename(file),
|
|
1986
|
+
source
|
|
1987
|
+
})}__`;
|
|
1988
|
+
assetCache.set(file, referenceId);
|
|
1989
|
+
}
|
|
1990
|
+
if (ASSET_QUERY_RE.test(id)) return [`import { join } from "node:path"`, `export default join(import.meta.dirname, ${referenceId})`].join("\n");
|
|
1991
|
+
if (id.endsWith(".node")) return [`const __require = process.getBuiltinModule("module").createRequire(import.meta.url)`, `export default __require(new URL(${referenceId}, import.meta.url).pathname)`].join("\n");
|
|
1992
|
+
},
|
|
1993
|
+
renderChunk(code, chunk) {
|
|
1994
|
+
ASSET_PLACEHOLDER_RE.lastIndex = 0;
|
|
1995
|
+
let match = ASSET_PLACEHOLDER_RE.exec(code);
|
|
1996
|
+
if (!match) return null;
|
|
1997
|
+
const s = new MagicString(code);
|
|
1998
|
+
while (match) {
|
|
1999
|
+
const [full, hash] = match;
|
|
2000
|
+
const filename = this.getFileName(hash);
|
|
2001
|
+
const replacement = JSON.stringify(toRelativePath(chunk.fileName, filename));
|
|
2002
|
+
s.overwrite(match.index, match.index + full.length, replacement, { contentOnly: true });
|
|
2003
|
+
match = ASSET_PLACEHOLDER_RE.exec(code);
|
|
2004
|
+
}
|
|
2005
|
+
return {
|
|
2006
|
+
code: s.toString(),
|
|
2007
|
+
map: s.generateMap({ hires: "boundary" })
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
};
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
//#endregion
|
|
2014
|
+
//#region src/dev/electron-launcher.ts
|
|
2015
|
+
async function fileExists(path) {
|
|
2016
|
+
try {
|
|
2017
|
+
await access(path);
|
|
2018
|
+
return true;
|
|
2019
|
+
} catch {
|
|
2020
|
+
return false;
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
/**
|
|
2024
|
+
* Find the Electron binary. Resolution order:
|
|
2025
|
+
* 1. ELECTRON_EXEC_PATH env var
|
|
2026
|
+
* 2. electron/path.txt (npm package convention)
|
|
2027
|
+
* 3. node_modules/.bin/electron symlink
|
|
2028
|
+
*/
|
|
2029
|
+
async function findElectronBin(root) {
|
|
2030
|
+
if (process.env.ELECTRON_EXEC_PATH) return process.env.ELECTRON_EXEC_PATH;
|
|
2031
|
+
try {
|
|
2032
|
+
const electronDir = resolve(root, "node_modules/electron");
|
|
2033
|
+
const pathTxtPath = resolve(electronDir, "path.txt");
|
|
2034
|
+
if (await fileExists(pathTxtPath)) {
|
|
2035
|
+
const resolved = resolve(electronDir, (await readFile(pathTxtPath, "utf-8")).trim());
|
|
2036
|
+
if (await fileExists(resolved)) return resolved;
|
|
2037
|
+
}
|
|
2038
|
+
} catch {}
|
|
2039
|
+
const binSymlink = resolve(root, "node_modules/.bin/electron");
|
|
2040
|
+
if (await fileExists(binSymlink)) return binSymlink;
|
|
2041
|
+
throw new Error("Could not find Electron binary. Install electron: npm add -D electron");
|
|
2042
|
+
}
|
|
2043
|
+
const yellow = "\x1B[33m";
|
|
2044
|
+
const red = "\x1B[31m";
|
|
2045
|
+
const dim = "\x1B[90m";
|
|
2046
|
+
const reset = "\x1B[0m";
|
|
2047
|
+
const DIAG_RE = new RegExp(String.raw`^(\d{2}:\d{2}:\d{2}) \[(electro|warn|error)\] (.+?) \u2192 (.+)$`);
|
|
2048
|
+
function colorDiagnosticLine(line) {
|
|
2049
|
+
const match = line.match(DIAG_RE);
|
|
2050
|
+
if (!match) return line;
|
|
2051
|
+
const [, time, tag, code, message] = match;
|
|
2052
|
+
let coloredTag;
|
|
2053
|
+
if (tag === "error") coloredTag = `${red}[${tag}]${reset}`;
|
|
2054
|
+
else if (tag === "warn") coloredTag = `${yellow}[${tag}]${reset}`;
|
|
2055
|
+
else coloredTag = `${yellow}[${tag}]${reset}`;
|
|
2056
|
+
return `${dim}${time}${reset} ${coloredTag} ${dim}${code}${reset} \u2192 ${message}`;
|
|
2057
|
+
}
|
|
2058
|
+
function pipeWithColoring(stream, target) {
|
|
2059
|
+
let buffer = "";
|
|
2060
|
+
stream.on("data", (chunk) => {
|
|
2061
|
+
buffer += chunk.toString();
|
|
2062
|
+
const lines = buffer.split("\n");
|
|
2063
|
+
buffer = lines.pop() ?? "";
|
|
2064
|
+
for (const line of lines) target.write(`${colorDiagnosticLine(line)}\n`);
|
|
2065
|
+
});
|
|
2066
|
+
stream.on("end", () => {
|
|
2067
|
+
if (buffer) target.write(`${colorDiagnosticLine(buffer)}\n`);
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
async function launchElectron(opts) {
|
|
2071
|
+
const proc = spawn(await findElectronBin(opts.root), [opts.entry], {
|
|
2072
|
+
cwd: opts.root,
|
|
2073
|
+
stdio: [
|
|
2074
|
+
"ignore",
|
|
2075
|
+
"pipe",
|
|
2076
|
+
"pipe"
|
|
2077
|
+
],
|
|
2078
|
+
env: {
|
|
2079
|
+
...process.env,
|
|
2080
|
+
...opts.env
|
|
2081
|
+
}
|
|
2082
|
+
});
|
|
2083
|
+
if (proc.stdout) pipeWithColoring(proc.stdout, process.stdout);
|
|
2084
|
+
if (proc.stderr) pipeWithColoring(proc.stderr, process.stderr);
|
|
2085
|
+
return {
|
|
2086
|
+
kill: () => proc.kill(),
|
|
2087
|
+
exited: new Promise((resolve) => {
|
|
2088
|
+
proc.on("exit", (code) => resolve(code));
|
|
2089
|
+
proc.on("error", () => resolve(null));
|
|
2090
|
+
})
|
|
2091
|
+
};
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
//#endregion
|
|
2095
|
+
//#region src/plugins/bytecode.ts
|
|
2096
|
+
const BYTECODE_LOADER_FILE = "bytecode-loader.cjs";
|
|
2097
|
+
/**
|
|
2098
|
+
* CJS module that registers `.jsc` file extension handler.
|
|
2099
|
+
* Loaded at runtime via createRequire before bytecode modules are required.
|
|
2100
|
+
*/
|
|
2101
|
+
const BYTECODE_LOADER_CODE = [
|
|
2102
|
+
`"use strict";`,
|
|
2103
|
+
`const fs = require("fs");`,
|
|
2104
|
+
`const path = require("path");`,
|
|
2105
|
+
`const vm = require("vm");`,
|
|
2106
|
+
`const v8 = require("v8");`,
|
|
2107
|
+
`const Module = require("module");`,
|
|
2108
|
+
``,
|
|
2109
|
+
`v8.setFlagsFromString("--no-lazy");`,
|
|
2110
|
+
`v8.setFlagsFromString("--no-flush-bytecode");`,
|
|
2111
|
+
``,
|
|
2112
|
+
`const FLAG_HASH_OFFSET = 12;`,
|
|
2113
|
+
`const SOURCE_HASH_OFFSET = 8;`,
|
|
2114
|
+
``,
|
|
2115
|
+
`let dummyBytecode;`,
|
|
2116
|
+
`function fixFlagHash(buffer) {`,
|
|
2117
|
+
` if (!dummyBytecode) {`,
|
|
2118
|
+
` dummyBytecode = new vm.Script("", { produceCachedData: true }).createCachedData();`,
|
|
2119
|
+
` }`,
|
|
2120
|
+
` dummyBytecode.copy(buffer, FLAG_HASH_OFFSET, FLAG_HASH_OFFSET, FLAG_HASH_OFFSET + 4);`,
|
|
2121
|
+
`}`,
|
|
2122
|
+
``,
|
|
2123
|
+
`Module._extensions[".jsc"] = function(module, filename) {`,
|
|
2124
|
+
` const bytecode = fs.readFileSync(filename);`,
|
|
2125
|
+
` fixFlagHash(bytecode);`,
|
|
2126
|
+
` const sourceLength = bytecode.readUInt32LE(SOURCE_HASH_OFFSET);`,
|
|
2127
|
+
` const dummyCode = sourceLength > 1 ? '"' + "\\u200b".repeat(sourceLength - 2) + '"' : "";`,
|
|
2128
|
+
` const script = new vm.Script(dummyCode, { filename, cachedData: bytecode });`,
|
|
2129
|
+
` if (script.cachedDataRejected) {`,
|
|
2130
|
+
` throw new Error("Bytecode cache rejected (V8 version mismatch?): " + filename);`,
|
|
2131
|
+
` }`,
|
|
2132
|
+
` const wrapper = script.runInThisContext({ filename });`,
|
|
2133
|
+
` const dir = path.dirname(filename);`,
|
|
2134
|
+
` wrapper.apply(module.exports, [module.exports, module.require.bind(module), module, filename, dir]);`,
|
|
2135
|
+
`};`,
|
|
2136
|
+
``
|
|
2137
|
+
].join("\n");
|
|
2138
|
+
/**
|
|
2139
|
+
* Script executed inside Electron (ELECTRON_RUN_AS_NODE=1) to compile JS to V8 bytecode.
|
|
2140
|
+
* Reads CJS code from stdin, writes bytecode buffer to stdout.
|
|
2141
|
+
*/
|
|
2142
|
+
const COMPILER_SCRIPT = [
|
|
2143
|
+
`"use strict";`,
|
|
2144
|
+
`const vm = require("vm");`,
|
|
2145
|
+
`const v8 = require("v8");`,
|
|
2146
|
+
`const Module = require("module");`,
|
|
2147
|
+
``,
|
|
2148
|
+
`v8.setFlagsFromString("--no-lazy");`,
|
|
2149
|
+
`v8.setFlagsFromString("--no-flush-bytecode");`,
|
|
2150
|
+
``,
|
|
2151
|
+
`let code = "";`,
|
|
2152
|
+
`process.stdin.setEncoding("utf-8");`,
|
|
2153
|
+
`process.stdin.on("data", chunk => { code += chunk; });`,
|
|
2154
|
+
`process.stdin.on("end", () => {`,
|
|
2155
|
+
` const wrapped = Module.wrap(code);`,
|
|
2156
|
+
` const script = new vm.Script(wrapped, { produceCachedData: true });`,
|
|
2157
|
+
` const bytecode = script.createCachedData();`,
|
|
2158
|
+
` process.stdout.write(bytecode);`,
|
|
2159
|
+
`});`
|
|
2160
|
+
].join("\n");
|
|
2161
|
+
/**
|
|
2162
|
+
* Compile Node scope output to V8 bytecode for source code protection.
|
|
2163
|
+
*
|
|
2164
|
+
* Architecture:
|
|
2165
|
+
* 1. ESM chunk code → CJS transform (via Bun.build)
|
|
2166
|
+
* 2. CJS code → V8 bytecode (via Electron subprocess)
|
|
2167
|
+
* 3. Entry .js → thin ESM loader stub that requires the .jsc bytecode
|
|
2168
|
+
* 4. bytecode-loader.cjs → registers Module._extensions[".jsc"]
|
|
2169
|
+
*
|
|
2170
|
+
* Only active in production mode. Not applicable to renderer scope.
|
|
2171
|
+
*/
|
|
2172
|
+
function bytecodePlugin(options = {}) {
|
|
2173
|
+
const { chunkAlias = [], keepSource = false, protectedStrings = [] } = options;
|
|
2174
|
+
const compileAll = chunkAlias.length === 0;
|
|
2175
|
+
const protectedSet = new Set(protectedStrings);
|
|
2176
|
+
let logger;
|
|
2177
|
+
let isProduction = false;
|
|
2178
|
+
let projectRoot = "";
|
|
2179
|
+
function shouldCompile(chunkName) {
|
|
2180
|
+
return compileAll || chunkAlias.includes(chunkName);
|
|
2181
|
+
}
|
|
2182
|
+
return {
|
|
2183
|
+
name: "electro:bytecode",
|
|
2184
|
+
apply: "build",
|
|
2185
|
+
enforce: "post",
|
|
2186
|
+
configResolved(config) {
|
|
2187
|
+
logger = config.logger;
|
|
2188
|
+
isProduction = config.isProduction;
|
|
2189
|
+
projectRoot = config.root;
|
|
2190
|
+
},
|
|
2191
|
+
renderChunk(code, chunk, opts) {
|
|
2192
|
+
if (!isProduction || !shouldCompile(chunk.name) || protectedSet.size === 0) return null;
|
|
2193
|
+
const s = obfuscateStrings(code, protectedSet);
|
|
2194
|
+
if (!s) return null;
|
|
2195
|
+
const sourcemap = typeof opts === "object" && "sourcemap" in opts ? opts.sourcemap : false;
|
|
2196
|
+
return {
|
|
2197
|
+
code: s.toString(),
|
|
2198
|
+
map: sourcemap ? s.generateMap({ hires: "boundary" }) : void 0
|
|
2199
|
+
};
|
|
2200
|
+
},
|
|
2201
|
+
async generateBundle(_outputOptions, output) {
|
|
2202
|
+
if (!isProduction) return;
|
|
2203
|
+
let electronPath;
|
|
2204
|
+
try {
|
|
2205
|
+
electronPath = await findElectronBin(projectRoot);
|
|
2206
|
+
} catch {
|
|
2207
|
+
try {
|
|
2208
|
+
electronPath = await findElectronBin(join(projectRoot, "../.."));
|
|
2209
|
+
} catch {}
|
|
2210
|
+
}
|
|
2211
|
+
if (!electronPath) {
|
|
2212
|
+
logger.warn("[electro:bytecode] Electron binary not found — skipping bytecode compilation");
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
2215
|
+
const compilerPath = join(tmpdir(), `electro-bc-compiler-${process.pid}.cjs`);
|
|
2216
|
+
await Bun.write(compilerPath, COMPILER_SCRIPT);
|
|
2217
|
+
const chunks = [];
|
|
2218
|
+
for (const item of Object.values(output)) if (item.type === "chunk" && item.isEntry && shouldCompile(item.name)) chunks.push(item);
|
|
2219
|
+
if (chunks.length === 0) {
|
|
2220
|
+
await cleanup(compilerPath);
|
|
2221
|
+
return;
|
|
2222
|
+
}
|
|
2223
|
+
let compiledCount = 0;
|
|
2224
|
+
for (const chunk of chunks) try {
|
|
2225
|
+
const bytecode = await compileToBytecode(await esmToCjs(chunk.code), electronPath, compilerPath);
|
|
2226
|
+
const jscFileName = `${chunk.fileName}c`;
|
|
2227
|
+
this.emitFile({
|
|
2228
|
+
type: "asset",
|
|
2229
|
+
fileName: jscFileName,
|
|
2230
|
+
source: bytecode
|
|
2231
|
+
});
|
|
2232
|
+
if (keepSource) this.emitFile({
|
|
2233
|
+
type: "asset",
|
|
2234
|
+
fileName: `_${chunk.fileName}`,
|
|
2235
|
+
source: chunk.code
|
|
2236
|
+
});
|
|
2237
|
+
chunk.code = generateLoaderStub(relativeChunkPath(BYTECODE_LOADER_FILE, chunk.fileName), relativeChunkPath(jscFileName, chunk.fileName), chunk.exports);
|
|
2238
|
+
compiledCount++;
|
|
2239
|
+
} catch (e) {
|
|
2240
|
+
logger.error(`[electro:bytecode] Failed to compile ${chunk.fileName}: ${e instanceof Error ? e.message : e}`);
|
|
2241
|
+
}
|
|
2242
|
+
if (compiledCount > 0) {
|
|
2243
|
+
if (!Object.values(output).some((a) => a.type === "asset" && a.fileName === BYTECODE_LOADER_FILE)) this.emitFile({
|
|
2244
|
+
type: "asset",
|
|
2245
|
+
fileName: BYTECODE_LOADER_FILE,
|
|
2246
|
+
source: BYTECODE_LOADER_CODE
|
|
2247
|
+
});
|
|
2248
|
+
logger.info(`\x1b[32m\u2713\x1b[0m ${compiledCount} chunk(s) compiled to bytecode`);
|
|
2249
|
+
}
|
|
2250
|
+
await cleanup(compilerPath);
|
|
2251
|
+
}
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
/** Transform ESM code to CJS using Bun.build (packages stay external). */
|
|
2255
|
+
async function esmToCjs(code) {
|
|
2256
|
+
const tmpFile = join(tmpdir(), `electro-bc-${process.pid}-${Date.now()}.mjs`);
|
|
2257
|
+
await Bun.write(tmpFile, code);
|
|
2258
|
+
try {
|
|
2259
|
+
const result = await Bun.build({
|
|
2260
|
+
entrypoints: [tmpFile],
|
|
2261
|
+
format: "cjs",
|
|
2262
|
+
target: "node",
|
|
2263
|
+
packages: "external",
|
|
2264
|
+
minify: false
|
|
2265
|
+
});
|
|
2266
|
+
if (!result.success) {
|
|
2267
|
+
const msgs = result.logs.map((l) => l.message).join("\n");
|
|
2268
|
+
throw new Error(`ESM\u2192CJS transform failed:\n${msgs}`);
|
|
2269
|
+
}
|
|
2270
|
+
return await result.outputs[0].text();
|
|
2271
|
+
} finally {
|
|
2272
|
+
await cleanup(tmpFile);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
/** Compile CJS code to V8 bytecode via Electron subprocess. */
|
|
2276
|
+
async function compileToBytecode(code, electronPath, compilerPath) {
|
|
2277
|
+
const proc = Bun.spawn([electronPath, compilerPath], {
|
|
2278
|
+
env: {
|
|
2279
|
+
...process.env,
|
|
2280
|
+
ELECTRON_RUN_AS_NODE: "1"
|
|
2281
|
+
},
|
|
2282
|
+
stdin: new Blob([code]),
|
|
2283
|
+
stdout: "pipe",
|
|
2284
|
+
stderr: "pipe"
|
|
2285
|
+
});
|
|
2286
|
+
const [stdoutBuf, stderrText, exitCode] = await Promise.all([
|
|
2287
|
+
new Response(proc.stdout).arrayBuffer(),
|
|
2288
|
+
new Response(proc.stderr).text(),
|
|
2289
|
+
proc.exited
|
|
2290
|
+
]);
|
|
2291
|
+
if (exitCode !== 0) throw new Error(`Bytecode compilation failed (exit ${exitCode}): ${stderrText}`);
|
|
2292
|
+
const buf = Buffer.from(stdoutBuf);
|
|
2293
|
+
if (buf.length === 0) throw new Error("Bytecode compilation returned empty buffer");
|
|
2294
|
+
return buf;
|
|
2295
|
+
}
|
|
2296
|
+
/** Generate ESM entry stub that loads bytecode via createRequire. */
|
|
2297
|
+
function generateLoaderStub(loaderPath, jscPath, exports) {
|
|
2298
|
+
const lines = [
|
|
2299
|
+
`const __require = process.getBuiltinModule("module").createRequire(import.meta.url);`,
|
|
2300
|
+
`__require(${JSON.stringify(loaderPath)});`,
|
|
2301
|
+
`const __mod = __require(${JSON.stringify(jscPath)});`
|
|
2302
|
+
];
|
|
2303
|
+
if (exports.includes("default")) lines.push(`export default __mod["default"] ?? __mod;`);
|
|
2304
|
+
const named = exports.filter((e) => e !== "default");
|
|
2305
|
+
if (named.length > 0) lines.push(`export const { ${named.join(", ")} } = __mod;`);
|
|
2306
|
+
return `${lines.join("\n")}\n`;
|
|
2307
|
+
}
|
|
2308
|
+
/**
|
|
2309
|
+
* Obfuscate specific string literals via String.fromCharCode.
|
|
2310
|
+
*
|
|
2311
|
+
* Context-aware: skips strings that appear in unsafe positions:
|
|
2312
|
+
* - import/export specifiers: `import "pkg"`, `from "pkg"`
|
|
2313
|
+
* - require() arguments: `require("pkg")`
|
|
2314
|
+
* - computed member expressions: `obj["key"]`
|
|
2315
|
+
* - object literal keys: `{ "key": value }`
|
|
2316
|
+
*/
|
|
2317
|
+
function obfuscateStrings(code, strings) {
|
|
2318
|
+
if (strings.size === 0) return null;
|
|
2319
|
+
let s;
|
|
2320
|
+
const stringRE = /"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)'/g;
|
|
2321
|
+
let match;
|
|
2322
|
+
match = stringRE.exec(code);
|
|
2323
|
+
while (match) {
|
|
2324
|
+
const value = match[1] ?? match[2];
|
|
2325
|
+
if (value && strings.has(value) && !isUnsafeContext(code, match.index, match[0].length)) {
|
|
2326
|
+
s ??= new MagicString(code);
|
|
2327
|
+
const charCodes = Array.from(value).map((c) => c.charCodeAt(0)).join(",");
|
|
2328
|
+
s.overwrite(match.index, match.index + match[0].length, `String.fromCharCode(${charCodes})`, { contentOnly: true });
|
|
2329
|
+
}
|
|
2330
|
+
match = stringRE.exec(code);
|
|
2331
|
+
}
|
|
2332
|
+
return s ?? null;
|
|
2333
|
+
}
|
|
2334
|
+
/** Check if a string literal at the given position is in an unsafe context for obfuscation. */
|
|
2335
|
+
function isUnsafeContext(code, matchStart, matchLen) {
|
|
2336
|
+
const before = code.slice(Math.max(0, matchStart - 80), matchStart).trimEnd();
|
|
2337
|
+
if (/\bimport\s*$/.test(before) || /\bfrom\s*$/.test(before)) return true;
|
|
2338
|
+
if (/\brequire\s*\(\s*$/.test(before)) return true;
|
|
2339
|
+
if (before.endsWith("[")) return true;
|
|
2340
|
+
if (code.slice(matchStart + matchLen, matchStart + matchLen + 20).trimStart().startsWith(":")) {
|
|
2341
|
+
if (!before.endsWith("?")) return true;
|
|
2342
|
+
}
|
|
2343
|
+
return false;
|
|
2344
|
+
}
|
|
2345
|
+
/** Compute relative path from `from` file to `target` file. */
|
|
2346
|
+
function relativeChunkPath(target, from) {
|
|
2347
|
+
let rel = relative(dirname(from), target);
|
|
2348
|
+
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
2349
|
+
return rel;
|
|
2350
|
+
}
|
|
2351
|
+
/** Silent cleanup of temp files. */
|
|
2352
|
+
async function cleanup(filePath) {
|
|
2353
|
+
try {
|
|
2354
|
+
await Bun.file(filePath).delete();
|
|
2355
|
+
} catch {}
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
//#endregion
|
|
2359
|
+
//#region src/plugins/isolate-entries.ts
|
|
2360
|
+
const VIRTUAL_ENTRY_ID = "\0electro:isolate-entries";
|
|
2361
|
+
/**
|
|
2362
|
+
* Isolates multiple entry points into separate sub-builds.
|
|
2363
|
+
* Dormant (no-op) if only one entry detected.
|
|
2364
|
+
*/
|
|
2365
|
+
function isolateEntriesPlugin(subBuildConfig) {
|
|
2366
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
2367
|
+
let entries = null;
|
|
2368
|
+
return {
|
|
2369
|
+
name: "electro:isolate-entries",
|
|
2370
|
+
apply: "build",
|
|
2371
|
+
options(opts) {
|
|
2372
|
+
const { input } = opts;
|
|
2373
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return;
|
|
2374
|
+
if (Object.keys(input).length <= 1) return;
|
|
2375
|
+
entries = Object.entries(input).map(([k, v]) => ({ [k]: v }));
|
|
2376
|
+
opts.input = VIRTUAL_ENTRY_ID;
|
|
2377
|
+
},
|
|
2378
|
+
resolveId(id) {
|
|
2379
|
+
if (id === VIRTUAL_ENTRY_ID) return id;
|
|
2380
|
+
return null;
|
|
2381
|
+
},
|
|
2382
|
+
async load(id) {
|
|
2383
|
+
if (id !== VIRTUAL_ENTRY_ID || !entries) return;
|
|
2384
|
+
for (const entry of entries) {
|
|
2385
|
+
const config = mergeConfig(subBuildConfig, {
|
|
2386
|
+
build: {
|
|
2387
|
+
write: false,
|
|
2388
|
+
watch: null
|
|
2389
|
+
},
|
|
2390
|
+
plugins: [{
|
|
2391
|
+
name: "electro:entry-file-name",
|
|
2392
|
+
outputOptions(output) {
|
|
2393
|
+
if (output.entryFileNames && typeof output.entryFileNames === "string") output.entryFileNames = `[name]${extname(output.entryFileNames)}`;
|
|
2394
|
+
}
|
|
2395
|
+
}],
|
|
2396
|
+
logLevel: "warn",
|
|
2397
|
+
configFile: false
|
|
2398
|
+
});
|
|
2399
|
+
if (config.build) config.build.rolldownOptions = {
|
|
2400
|
+
...config.build.rolldownOptions,
|
|
2401
|
+
input: entry
|
|
2402
|
+
};
|
|
2403
|
+
const result = await build(config);
|
|
2404
|
+
for (const chunk of result.output) {
|
|
2405
|
+
if (emitted.has(chunk.fileName)) continue;
|
|
2406
|
+
const source = chunk.type === "chunk" ? chunk.code : chunk.source;
|
|
2407
|
+
if (source == null) continue;
|
|
2408
|
+
this.emitFile({
|
|
2409
|
+
type: "asset",
|
|
2410
|
+
fileName: chunk.fileName,
|
|
2411
|
+
source
|
|
2412
|
+
});
|
|
2413
|
+
emitted.add(chunk.fileName);
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
return "// virtual entry — removed in generateBundle";
|
|
2417
|
+
},
|
|
2418
|
+
generateBundle(_, bundle) {
|
|
2419
|
+
for (const name in bundle) if (name.includes("isolate-entries")) delete bundle[name];
|
|
2420
|
+
}
|
|
2421
|
+
};
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
//#endregion
|
|
2425
|
+
//#region src/plugins/module-path.ts
|
|
2426
|
+
const MODULE_PATH_RE = /__ELECTRO_MODULE_PATH__([\w$]+)__/g;
|
|
2427
|
+
/** Plugins to exclude from sub-build configs to prevent recursion. */
|
|
2428
|
+
const EXCLUDED_PLUGINS = new Set([
|
|
2429
|
+
"electro:module-path",
|
|
2430
|
+
"electro:isolate-entries",
|
|
2431
|
+
"electro:bytecode"
|
|
2432
|
+
]);
|
|
2433
|
+
/**
|
|
2434
|
+
* Handles `?modulePath` imports in main/preload scopes.
|
|
2435
|
+
*
|
|
2436
|
+
* Builds the referenced file as a fully isolated bundle via `viteBuild()`
|
|
2437
|
+
* and returns its runtime filesystem path. Guarantees self-contained output
|
|
2438
|
+
* with no shared chunks — safe for `child_process.fork()`.
|
|
2439
|
+
*
|
|
2440
|
+
* Usage:
|
|
2441
|
+
* import taskPath from './task.ts?modulePath'
|
|
2442
|
+
* fork(taskPath)
|
|
2443
|
+
*/
|
|
2444
|
+
function modulePathPlugin() {
|
|
2445
|
+
const assetCache = /* @__PURE__ */ new Set();
|
|
2446
|
+
let resolvedConfig;
|
|
2447
|
+
return {
|
|
2448
|
+
name: "electro:module-path",
|
|
2449
|
+
apply: "build",
|
|
2450
|
+
enforce: "pre",
|
|
2451
|
+
configResolved(config) {
|
|
2452
|
+
resolvedConfig = config;
|
|
2453
|
+
},
|
|
2454
|
+
buildStart() {
|
|
2455
|
+
assetCache.clear();
|
|
2456
|
+
},
|
|
2457
|
+
async load(id) {
|
|
2458
|
+
if (!id.endsWith("?modulePath")) return;
|
|
2459
|
+
const result = await bundleModulePath(cleanUrl(id), createSubBuildConfig(resolvedConfig), this.meta.watchMode);
|
|
2460
|
+
const [mainChunk, ...otherChunks] = result.output;
|
|
2461
|
+
const refId = this.emitFile({
|
|
2462
|
+
type: "asset",
|
|
2463
|
+
fileName: mainChunk.fileName,
|
|
2464
|
+
source: mainChunk.code ?? ""
|
|
2465
|
+
});
|
|
2466
|
+
for (const chunk of otherChunks) {
|
|
2467
|
+
if (assetCache.has(chunk.fileName)) continue;
|
|
2468
|
+
const source = chunk.type === "chunk" ? chunk.code : chunk.source;
|
|
2469
|
+
if (source == null) continue;
|
|
2470
|
+
this.emitFile({
|
|
2471
|
+
type: "asset",
|
|
2472
|
+
fileName: chunk.fileName,
|
|
2473
|
+
source
|
|
2474
|
+
});
|
|
2475
|
+
assetCache.add(chunk.fileName);
|
|
2476
|
+
}
|
|
2477
|
+
for (const watchFile of result.watchFiles) this.addWatchFile(watchFile);
|
|
2478
|
+
return [`import { join } from 'path'`, `export default join(import.meta.dirname, ${`__ELECTRO_MODULE_PATH__${refId}__`})`].join("\n");
|
|
2479
|
+
},
|
|
2480
|
+
renderChunk(code, chunk, opts) {
|
|
2481
|
+
MODULE_PATH_RE.lastIndex = 0;
|
|
2482
|
+
let match = MODULE_PATH_RE.exec(code);
|
|
2483
|
+
if (!match) return null;
|
|
2484
|
+
const sourcemap = typeof opts === "object" && "sourcemap" in opts ? opts.sourcemap : false;
|
|
2485
|
+
const s = new MagicString(code);
|
|
2486
|
+
while (match) {
|
|
2487
|
+
const [full, hash] = match;
|
|
2488
|
+
const filename = this.getFileName(hash);
|
|
2489
|
+
const replacement = JSON.stringify(toRelativePath(chunk.fileName, filename));
|
|
2490
|
+
s.overwrite(match.index, match.index + full.length, replacement, { contentOnly: true });
|
|
2491
|
+
match = MODULE_PATH_RE.exec(code);
|
|
2492
|
+
}
|
|
2493
|
+
return {
|
|
2494
|
+
code: s.toString(),
|
|
2495
|
+
map: sourcemap ? s.generateMap({ hires: "boundary" }) : null
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
};
|
|
2499
|
+
}
|
|
2500
|
+
/** Create a sub-build config from resolved config, stripping recursive plugins. */
|
|
2501
|
+
function createSubBuildConfig(config) {
|
|
2502
|
+
const plugins = (config.plugins ?? []).filter((p) => !EXCLUDED_PLUGINS.has(p.name));
|
|
2503
|
+
return {
|
|
2504
|
+
configFile: false,
|
|
2505
|
+
root: config.root,
|
|
2506
|
+
mode: config.mode,
|
|
2507
|
+
envDir: config.envDir,
|
|
2508
|
+
envPrefix: config.envPrefix,
|
|
2509
|
+
plugins,
|
|
2510
|
+
resolve: config.resolve,
|
|
2511
|
+
define: config.define,
|
|
2512
|
+
build: {
|
|
2513
|
+
ssr: config.build.ssr,
|
|
2514
|
+
outDir: config.build.outDir,
|
|
2515
|
+
rolldownOptions: {
|
|
2516
|
+
output: config.build.rolldownOptions.output,
|
|
2517
|
+
external: config.build.rolldownOptions.external
|
|
2518
|
+
},
|
|
2519
|
+
target: config.build.target,
|
|
2520
|
+
sourcemap: config.build.sourcemap,
|
|
2521
|
+
minify: config.build.minify
|
|
2522
|
+
},
|
|
2523
|
+
ssr: config.ssr,
|
|
2524
|
+
logLevel: "warn"
|
|
2525
|
+
};
|
|
2526
|
+
}
|
|
2527
|
+
/** Collect source files from a sub-build for watch-mode tracking. */
|
|
2528
|
+
function watchCollectorPlugin(watchFiles) {
|
|
2529
|
+
return {
|
|
2530
|
+
name: "electro:module-path-watch",
|
|
2531
|
+
buildEnd() {
|
|
2532
|
+
for (const id of this.getModuleIds()) {
|
|
2533
|
+
if (id.includes("node_modules") || id.startsWith("\0")) continue;
|
|
2534
|
+
if (this.getModuleInfo(id)?.code != null) watchFiles.push(id);
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
};
|
|
2538
|
+
}
|
|
2539
|
+
/** Build a ?modulePath import as a fully isolated bundle. */
|
|
2540
|
+
async function bundleModulePath(input, config, watch) {
|
|
2541
|
+
const watchFiles = [];
|
|
2542
|
+
const subConfig = mergeConfig(config, {
|
|
2543
|
+
build: {
|
|
2544
|
+
write: false,
|
|
2545
|
+
watch: false
|
|
2546
|
+
},
|
|
2547
|
+
plugins: [{
|
|
2548
|
+
name: "electro:module-path-entry-name",
|
|
2549
|
+
outputOptions(output) {
|
|
2550
|
+
if (typeof output.entryFileNames !== "function" && output.entryFileNames) output.entryFileNames = `[name]-[hash]${extname(output.entryFileNames)}`;
|
|
2551
|
+
}
|
|
2552
|
+
}, ...watch ? [watchCollectorPlugin(watchFiles)] : []],
|
|
2553
|
+
logLevel: "warn",
|
|
2554
|
+
configFile: false
|
|
2555
|
+
});
|
|
2556
|
+
if (subConfig.build) subConfig.build.rolldownOptions = {
|
|
2557
|
+
...subConfig.build?.rolldownOptions,
|
|
2558
|
+
input,
|
|
2559
|
+
plugins: [...subConfig.build?.rolldownOptions?.plugins ?? []]
|
|
2560
|
+
};
|
|
2561
|
+
return {
|
|
2562
|
+
output: (await build(subConfig)).output,
|
|
2563
|
+
watchFiles
|
|
2564
|
+
};
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
//#endregion
|
|
2568
|
+
//#region src/plugins/worker.ts
|
|
2569
|
+
const WORKER_PLACEHOLDER_RE = /__ELECTRO_WORKER__([\w$]+)__/g;
|
|
2570
|
+
const WORKER_QUERY_RE = /\?nodeWorker(?:&|$)/;
|
|
2571
|
+
const WORKER_IMPORTER_RE = /\?nodeWorker&importer=([^&]+)(?:&|$)/;
|
|
2572
|
+
/**
|
|
2573
|
+
* Handles `?nodeWorker` imports in main/preload scopes.
|
|
2574
|
+
*
|
|
2575
|
+
* Builds the referenced file as a separate chunk and returns
|
|
2576
|
+
* a factory function that creates a `worker_threads` Worker.
|
|
2577
|
+
*
|
|
2578
|
+
* Usage:
|
|
2579
|
+
* import createWorker from './heavy-task.ts?nodeWorker'
|
|
2580
|
+
* const worker = createWorker({ workerData: { ... } })
|
|
2581
|
+
*/
|
|
2582
|
+
function workerPlugin() {
|
|
2583
|
+
return {
|
|
2584
|
+
name: "electro:worker",
|
|
2585
|
+
apply: "build",
|
|
2586
|
+
enforce: "pre",
|
|
2587
|
+
resolveId(id, importer) {
|
|
2588
|
+
if (id.endsWith("?nodeWorker")) return `${id}&importer=${importer}`;
|
|
2589
|
+
},
|
|
2590
|
+
load(id) {
|
|
2591
|
+
if (!WORKER_QUERY_RE.test(id)) return;
|
|
2592
|
+
const match = WORKER_IMPORTER_RE.exec(id);
|
|
2593
|
+
if (!match) return;
|
|
2594
|
+
return [
|
|
2595
|
+
`import { Worker } from 'worker_threads';`,
|
|
2596
|
+
`export default function createWorker(options) {`,
|
|
2597
|
+
` return new Worker(new URL(${`__ELECTRO_WORKER__${this.emitFile({
|
|
2598
|
+
type: "chunk",
|
|
2599
|
+
id: cleanUrl(id),
|
|
2600
|
+
importer: match[1]
|
|
2601
|
+
})}__`}, import.meta.url), options);`,
|
|
2602
|
+
`};`
|
|
2603
|
+
].join("\n");
|
|
2604
|
+
},
|
|
2605
|
+
renderChunk(code, chunk, opts) {
|
|
2606
|
+
WORKER_PLACEHOLDER_RE.lastIndex = 0;
|
|
2607
|
+
let match = WORKER_PLACEHOLDER_RE.exec(code);
|
|
2608
|
+
if (!match) return null;
|
|
2609
|
+
const sourcemap = typeof opts === "object" && "sourcemap" in opts ? opts.sourcemap : false;
|
|
2610
|
+
const s = new MagicString(code);
|
|
2611
|
+
while (match) {
|
|
2612
|
+
const [full, hash] = match;
|
|
2613
|
+
const filename = this.getFileName(hash);
|
|
2614
|
+
const replacement = JSON.stringify(toRelativePath(chunk.fileName, filename));
|
|
2615
|
+
s.overwrite(match.index, match.index + full.length, replacement, { contentOnly: true });
|
|
2616
|
+
match = WORKER_PLACEHOLDER_RE.exec(code);
|
|
2617
|
+
}
|
|
2618
|
+
return {
|
|
2619
|
+
code: s.toString(),
|
|
2620
|
+
map: sourcemap ? s.generateMap({ hires: "boundary" }) : null
|
|
2621
|
+
};
|
|
2622
|
+
}
|
|
2623
|
+
};
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
//#endregion
|
|
2627
|
+
//#region src/commands/build.ts
|
|
2628
|
+
async function build$1(options) {
|
|
2629
|
+
if (options.sourcemap) validateSourcemap(options.sourcemap);
|
|
2630
|
+
if (options.logLevel) setLogLevel(options.logLevel);
|
|
2631
|
+
const totalTimer = startTimer();
|
|
2632
|
+
validateViteVersion(version);
|
|
2633
|
+
const loaded = await loadConfig(options.config);
|
|
2634
|
+
const config = loaded.config;
|
|
2635
|
+
const root = loaded.root;
|
|
2636
|
+
const outDir = resolve(root, options.outDir);
|
|
2637
|
+
const codegenDir = resolve(root, ".electro");
|
|
2638
|
+
const windows = config.windows ?? [];
|
|
2639
|
+
const srcDir = resolve(root, "src");
|
|
2640
|
+
session({
|
|
2641
|
+
root,
|
|
2642
|
+
main: resolve(dirname(config.runtime.__source), config.runtime.entry),
|
|
2643
|
+
preload: windows.length > 0 ? resolve(codegenDir, "generated/preload") : null,
|
|
2644
|
+
renderer: windows.length > 0 ? resolve(root, dirname(relative(root, windows[0].__source))) : null,
|
|
2645
|
+
mode: "build",
|
|
2646
|
+
windows: windows.map((w) => ({
|
|
2647
|
+
name: w.name,
|
|
2648
|
+
entry: resolve(dirname(w.__source), w.entry)
|
|
2649
|
+
}))
|
|
2650
|
+
});
|
|
2651
|
+
const codegenTimer = startTimer();
|
|
2652
|
+
try {
|
|
2653
|
+
const { files, envTypes } = generate({
|
|
2654
|
+
scanResult: await scan(srcDir),
|
|
2655
|
+
windows,
|
|
2656
|
+
outputDir: codegenDir,
|
|
2657
|
+
srcDir
|
|
2658
|
+
});
|
|
2659
|
+
await mkdir(codegenDir, { recursive: true });
|
|
2660
|
+
for (const file of files) {
|
|
2661
|
+
const filePath = resolve(codegenDir, file.path);
|
|
2662
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
2663
|
+
await writeFile(filePath, file.content);
|
|
2664
|
+
}
|
|
2665
|
+
const envTypesPath = resolve(srcDir, envTypes.path);
|
|
2666
|
+
await mkdir(dirname(envTypesPath), { recursive: true });
|
|
2667
|
+
await writeFile(envTypesPath, envTypes.content);
|
|
2668
|
+
step("codegen", codegenTimer());
|
|
2669
|
+
} catch (err) {
|
|
2670
|
+
stepFail("codegen", err instanceof Error ? err.message : String(err));
|
|
2671
|
+
process.exit(1);
|
|
2672
|
+
}
|
|
2673
|
+
const externals = await resolveExternals(root);
|
|
2674
|
+
const logger = createBuildLogger();
|
|
2675
|
+
try {
|
|
2676
|
+
buildScope("main");
|
|
2677
|
+
await buildMain({
|
|
2678
|
+
config,
|
|
2679
|
+
root,
|
|
2680
|
+
outDir,
|
|
2681
|
+
externals,
|
|
2682
|
+
sourcemap: options.sourcemap,
|
|
2683
|
+
logger,
|
|
2684
|
+
bytecode: options.bytecode
|
|
2685
|
+
});
|
|
2686
|
+
} catch (err) {
|
|
2687
|
+
stepFail("main", err instanceof Error ? err.message : String(err));
|
|
2688
|
+
process.exit(1);
|
|
2689
|
+
}
|
|
2690
|
+
if (windows.length > 0) try {
|
|
2691
|
+
buildScope("preload");
|
|
2692
|
+
await buildPreload({
|
|
2693
|
+
config,
|
|
2694
|
+
root,
|
|
2695
|
+
outDir,
|
|
2696
|
+
codegenDir,
|
|
2697
|
+
externals,
|
|
2698
|
+
sourcemap: options.sourcemap,
|
|
2699
|
+
logger,
|
|
2700
|
+
bytecode: options.bytecode
|
|
2701
|
+
});
|
|
2702
|
+
} catch (err) {
|
|
2703
|
+
stepFail("preload", err instanceof Error ? err.message : String(err));
|
|
2704
|
+
process.exit(1);
|
|
2705
|
+
}
|
|
2706
|
+
if (windows.length > 0) try {
|
|
2707
|
+
buildScope("renderer");
|
|
2708
|
+
const userViteConfigs = windows.filter((w) => w.vite).map((w) => w.vite);
|
|
2709
|
+
await build(createRendererConfig({
|
|
2710
|
+
root,
|
|
2711
|
+
windows,
|
|
2712
|
+
userViteConfigs: userViteConfigs.length > 0 ? userViteConfigs : void 0,
|
|
2713
|
+
logLevel: "info",
|
|
2714
|
+
customLogger: logger,
|
|
2715
|
+
outDir: resolve(outDir, "renderer"),
|
|
2716
|
+
minify: options.minify,
|
|
2717
|
+
sourcemap: options.sourcemap
|
|
2718
|
+
}));
|
|
2719
|
+
await flattenRendererOutput(resolve(outDir, "renderer"), windows, root);
|
|
2720
|
+
} catch (err) {
|
|
2721
|
+
stepFail("renderer", err instanceof Error ? err.message : String(err));
|
|
2722
|
+
process.exit(1);
|
|
2723
|
+
}
|
|
2724
|
+
footer(`Built in ${totalTimer()}`, outDir);
|
|
2725
|
+
}
|
|
2726
|
+
async function buildMain(args) {
|
|
2727
|
+
const runtimeEntry = args.config.runtime.entry;
|
|
2728
|
+
const entry = resolve(dirname(args.config.runtime.__source), runtimeEntry);
|
|
2729
|
+
const windowDefs = (args.config.windows ?? []).map((w) => ({
|
|
2730
|
+
name: w.name,
|
|
2731
|
+
type: w.type,
|
|
2732
|
+
lifecycle: w.lifecycle,
|
|
2733
|
+
autoShow: w.autoShow,
|
|
2734
|
+
behavior: w.behavior,
|
|
2735
|
+
window: w.window
|
|
2736
|
+
}));
|
|
2737
|
+
await build(createNodeConfig({
|
|
2738
|
+
scope: "main",
|
|
2739
|
+
root: args.root,
|
|
2740
|
+
entry,
|
|
2741
|
+
externals: args.externals,
|
|
2742
|
+
outDir: resolve(args.outDir, "main"),
|
|
2743
|
+
watch: false,
|
|
2744
|
+
plugins: [
|
|
2745
|
+
assetPlugin(),
|
|
2746
|
+
workerPlugin(),
|
|
2747
|
+
modulePathPlugin(),
|
|
2748
|
+
...args.bytecode ? [bytecodePlugin()] : []
|
|
2749
|
+
],
|
|
2750
|
+
userViteConfig: args.config.runtime.vite,
|
|
2751
|
+
sourcemap: args.sourcemap,
|
|
2752
|
+
customLogger: args.logger,
|
|
2753
|
+
logLevel: "info",
|
|
2754
|
+
define: { __ELECTRO_WINDOW_DEFINITIONS__: JSON.stringify(windowDefs) }
|
|
2755
|
+
}));
|
|
2756
|
+
}
|
|
2757
|
+
async function buildPreload(args) {
|
|
2758
|
+
const windows = args.config.windows ?? [];
|
|
2759
|
+
const input = {};
|
|
2760
|
+
for (const win of windows) input[win.name] = resolve(args.codegenDir, `generated/preload/${win.name}.gen.ts`);
|
|
2761
|
+
const firstEntry = Object.values(input)[0];
|
|
2762
|
+
const preloadOutDir = resolve(args.outDir, "preload");
|
|
2763
|
+
const preloadPlugins = [
|
|
2764
|
+
assetPlugin(),
|
|
2765
|
+
workerPlugin(),
|
|
2766
|
+
modulePathPlugin(),
|
|
2767
|
+
...args.bytecode ? [bytecodePlugin()] : []
|
|
2768
|
+
];
|
|
2769
|
+
const baseConfig = createNodeConfig({
|
|
2770
|
+
scope: "preload",
|
|
2771
|
+
root: args.root,
|
|
2772
|
+
entry: firstEntry,
|
|
2773
|
+
externals: args.externals,
|
|
2774
|
+
outDir: preloadOutDir,
|
|
2775
|
+
watch: false,
|
|
2776
|
+
plugins: preloadPlugins,
|
|
2777
|
+
sourcemap: args.sourcemap,
|
|
2778
|
+
customLogger: args.logger,
|
|
2779
|
+
logLevel: "info"
|
|
2780
|
+
});
|
|
2781
|
+
if (Object.keys(input).length > 1) {
|
|
2782
|
+
const subBuildConfig = createNodeConfig({
|
|
2783
|
+
scope: "preload",
|
|
2784
|
+
root: args.root,
|
|
2785
|
+
entry: firstEntry,
|
|
2786
|
+
externals: args.externals,
|
|
2787
|
+
outDir: preloadOutDir,
|
|
2788
|
+
watch: false,
|
|
2789
|
+
plugins: [
|
|
2790
|
+
assetPlugin(),
|
|
2791
|
+
workerPlugin(),
|
|
2792
|
+
modulePathPlugin()
|
|
2793
|
+
],
|
|
2794
|
+
sourcemap: args.sourcemap,
|
|
2795
|
+
customLogger: args.logger,
|
|
2796
|
+
logLevel: "info"
|
|
2797
|
+
});
|
|
2798
|
+
baseConfig.plugins.push(isolateEntriesPlugin(subBuildConfig));
|
|
2799
|
+
}
|
|
2800
|
+
if (baseConfig.build) baseConfig.build.rolldownOptions = {
|
|
2801
|
+
...baseConfig.build.rolldownOptions,
|
|
2802
|
+
input
|
|
2803
|
+
};
|
|
2804
|
+
await build(baseConfig);
|
|
2805
|
+
}
|
|
2806
|
+
/**
|
|
2807
|
+
* Flatten renderer HTML output from source-relative paths
|
|
2808
|
+
* (e.g., `src/windows/main/index.html`) to `{name}/index.html`.
|
|
2809
|
+
* Adjusts relative asset references to match the new depth.
|
|
2810
|
+
*/
|
|
2811
|
+
async function flattenRendererOutput(rendererDir, windows, root) {
|
|
2812
|
+
const dirsToClean = /* @__PURE__ */ new Set();
|
|
2813
|
+
for (const win of windows) {
|
|
2814
|
+
const relPath = relative(root, resolve(dirname(win.__source), win.entry));
|
|
2815
|
+
const oldHtmlPath = resolve(rendererDir, relPath);
|
|
2816
|
+
const newHtmlPath = resolve(rendererDir, win.name, "index.html");
|
|
2817
|
+
if (oldHtmlPath === newHtmlPath) continue;
|
|
2818
|
+
let html = await readFile(oldHtmlPath, "utf-8");
|
|
2819
|
+
const depthDiff = relPath.split("/").length - 1 - 1;
|
|
2820
|
+
if (depthDiff > 0) html = html.replace(/(["'(])((?:\.\.\/)+)/g, (_, prefix, dots) => {
|
|
2821
|
+
const levels = (dots.match(/\.\.\//g) || []).length;
|
|
2822
|
+
const adjusted = Math.max(0, levels - depthDiff);
|
|
2823
|
+
return prefix + (adjusted > 0 ? "../".repeat(adjusted) : "./");
|
|
2824
|
+
});
|
|
2825
|
+
await mkdir(dirname(newHtmlPath), { recursive: true });
|
|
2826
|
+
await writeFile(newHtmlPath, html);
|
|
2827
|
+
await unlink(oldHtmlPath);
|
|
2828
|
+
const topDir = relPath.split("/")[0];
|
|
2829
|
+
if (topDir !== win.name) dirsToClean.add(resolve(rendererDir, topDir));
|
|
2830
|
+
}
|
|
2831
|
+
for (const dir of dirsToClean) await rm(dir, {
|
|
2832
|
+
recursive: true,
|
|
2833
|
+
force: true
|
|
2834
|
+
});
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2837
|
+
//#endregion
|
|
2838
|
+
//#region src/dev/dev-server.ts
|
|
2839
|
+
const MAIN_RESTART_DEBOUNCE_MS = 80;
|
|
2840
|
+
const CONFIG_DEBOUNCE_MS = 300;
|
|
2841
|
+
var DevServer = class {
|
|
2842
|
+
rendererServer = null;
|
|
2843
|
+
electronProcess = null;
|
|
2844
|
+
config = null;
|
|
2845
|
+
root = "";
|
|
2846
|
+
lastScanResult = null;
|
|
2847
|
+
shuttingDown = false;
|
|
2848
|
+
cleanedUp = false;
|
|
2849
|
+
restartInFlight = false;
|
|
2850
|
+
restartQueued = false;
|
|
2851
|
+
restartQueuedFile = null;
|
|
2852
|
+
mainRestartQueued = false;
|
|
2853
|
+
mainRestartReason = null;
|
|
2854
|
+
mainRestartFlushTimer = null;
|
|
2855
|
+
mainRestartFlushInFlight = false;
|
|
2856
|
+
mainWatch = null;
|
|
2857
|
+
preloadWatch = null;
|
|
2858
|
+
outputDir = "";
|
|
2859
|
+
logLevel;
|
|
2860
|
+
clearScreen;
|
|
2861
|
+
rendererOnly;
|
|
2862
|
+
sourcemap;
|
|
2863
|
+
outDirOverride;
|
|
2864
|
+
configPaths = /* @__PURE__ */ new Set();
|
|
2865
|
+
configDebounce = null;
|
|
2866
|
+
onRestart = null;
|
|
2867
|
+
rendererUrl = null;
|
|
2868
|
+
constructor(configPath, opts) {
|
|
2869
|
+
this.configPath = configPath;
|
|
2870
|
+
this.logLevel = opts?.logLevel;
|
|
2871
|
+
this.clearScreen = opts?.clearScreen;
|
|
2872
|
+
this.rendererOnly = opts?.rendererOnly ?? false;
|
|
2873
|
+
this.sourcemap = opts?.sourcemap;
|
|
2874
|
+
this.outDirOverride = opts?.outDir;
|
|
2875
|
+
if (this.logLevel) setLogLevel(this.logLevel);
|
|
2876
|
+
}
|
|
2877
|
+
async start() {
|
|
2878
|
+
this.cleanedUp = false;
|
|
2879
|
+
this.shuttingDown = false;
|
|
2880
|
+
validateViteVersion(version);
|
|
2881
|
+
const totalTimer = startTimer();
|
|
2882
|
+
const loaded = await loadConfig(this.configPath);
|
|
2883
|
+
this.config = loaded.config;
|
|
2884
|
+
this.root = loaded.root;
|
|
2885
|
+
this.outputDir = this.outDirOverride ? resolve(this.root, this.outDirOverride) : resolve(this.root, ".electro");
|
|
2886
|
+
this.configPaths.add(loaded.configPath);
|
|
2887
|
+
for (const win of this.config.windows ?? []) this.configPaths.add(win.__source);
|
|
2888
|
+
const windows = this.config.windows ?? [];
|
|
2889
|
+
const srcDir = resolve(this.root, "src");
|
|
2890
|
+
const mainEntry = resolve(dirname(this.config.runtime.__source), this.config.runtime.entry);
|
|
2891
|
+
session({
|
|
2892
|
+
root: this.root,
|
|
2893
|
+
main: mainEntry,
|
|
2894
|
+
preload: windows.length > 0 ? resolve(this.outputDir, "generated/preload") : null,
|
|
2895
|
+
renderer: windows.length > 0 ? resolve(this.root, dirname(relative(this.root, windows[0].__source))) : null,
|
|
2896
|
+
windows: windows.map((w) => ({
|
|
2897
|
+
name: w.name,
|
|
2898
|
+
entry: resolve(dirname(w.__source), w.entry)
|
|
2899
|
+
}))
|
|
2900
|
+
});
|
|
2901
|
+
const codegenTimer = startTimer();
|
|
2902
|
+
try {
|
|
2903
|
+
await this.runCodegen(this.outputDir, srcDir);
|
|
2904
|
+
step("codegen", codegenTimer());
|
|
2905
|
+
} catch (err) {
|
|
2906
|
+
stepFail("codegen", err instanceof Error ? err.message : String(err));
|
|
2907
|
+
throw err;
|
|
2908
|
+
}
|
|
2909
|
+
if (windows.length > 0) {
|
|
2910
|
+
const rendererTimer = startTimer();
|
|
2911
|
+
try {
|
|
2912
|
+
await this.startRenderer();
|
|
2913
|
+
step("renderer", rendererTimer());
|
|
2914
|
+
} catch (err) {
|
|
2915
|
+
stepFail("renderer", err instanceof Error ? err.message : String(err));
|
|
2916
|
+
throw err;
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
if (this.rendererOnly) {
|
|
2920
|
+
note("Renderer-only mode — skipping main, preload, Electron");
|
|
2921
|
+
footer(`Ready in ${totalTimer()}`, this.rendererUrl ?? void 0);
|
|
2922
|
+
this.attachConfigWatcher();
|
|
2923
|
+
return;
|
|
2924
|
+
}
|
|
2925
|
+
const externals = await resolveExternals(this.root);
|
|
2926
|
+
if (windows.length > 0) {
|
|
2927
|
+
const preloadTimer = startTimer();
|
|
2928
|
+
try {
|
|
2929
|
+
await this.buildPreload(externals);
|
|
2930
|
+
step("preload", preloadTimer());
|
|
2931
|
+
} catch (err) {
|
|
2932
|
+
stepFail("preload", err instanceof Error ? err.message : String(err));
|
|
2933
|
+
throw err;
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
const mainBuildTimer = startTimer();
|
|
2937
|
+
try {
|
|
2938
|
+
await this.buildMain(externals);
|
|
2939
|
+
step("main", mainBuildTimer());
|
|
2940
|
+
} catch (err) {
|
|
2941
|
+
stepFail("main", err instanceof Error ? err.message : String(err));
|
|
2942
|
+
throw err;
|
|
2943
|
+
}
|
|
2944
|
+
const electronTimer = startTimer();
|
|
2945
|
+
try {
|
|
2946
|
+
await this.attachElectronProcess();
|
|
2947
|
+
step("electron", electronTimer());
|
|
2948
|
+
} catch (err) {
|
|
2949
|
+
stepFail("electron", err instanceof Error ? err.message : String(err));
|
|
2950
|
+
throw err;
|
|
2951
|
+
}
|
|
2952
|
+
footer(`Ready in ${totalTimer()}`, this.rendererUrl ?? void 0);
|
|
2953
|
+
this.attachConfigWatcher();
|
|
2954
|
+
}
|
|
2955
|
+
/** Register a callback for config-triggered restarts. */
|
|
2956
|
+
setOnRestart(fn) {
|
|
2957
|
+
this.onRestart = fn;
|
|
2958
|
+
}
|
|
2959
|
+
/** Clean shutdown — idempotent, safe to call from any context. */
|
|
2960
|
+
stop() {
|
|
2961
|
+
if (this.cleanedUp) return;
|
|
2962
|
+
this.cleanedUp = true;
|
|
2963
|
+
this.shuttingDown = true;
|
|
2964
|
+
if (this.configDebounce) {
|
|
2965
|
+
clearTimeout(this.configDebounce);
|
|
2966
|
+
this.configDebounce = null;
|
|
2967
|
+
}
|
|
2968
|
+
if (this.rendererServer?.watcher) for (const configPath of this.configPaths) this.rendererServer.watcher.unwatch(configPath);
|
|
2969
|
+
this.configPaths.clear();
|
|
2970
|
+
this.mainWatch?.close();
|
|
2971
|
+
this.mainWatch = null;
|
|
2972
|
+
this.preloadWatch?.close();
|
|
2973
|
+
this.preloadWatch = null;
|
|
2974
|
+
if (this.mainRestartFlushTimer) {
|
|
2975
|
+
clearTimeout(this.mainRestartFlushTimer);
|
|
2976
|
+
this.mainRestartFlushTimer = null;
|
|
2977
|
+
}
|
|
2978
|
+
if (this.rendererServer) {
|
|
2979
|
+
this.rendererServer.close();
|
|
2980
|
+
this.rendererServer = null;
|
|
2981
|
+
}
|
|
2982
|
+
if (this.electronProcess) {
|
|
2983
|
+
const proc = this.electronProcess;
|
|
2984
|
+
this.electronProcess = null;
|
|
2985
|
+
proc.kill();
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
async runCodegen(outputDir, srcDir) {
|
|
2989
|
+
const scanResult = await scan(srcDir);
|
|
2990
|
+
this.lastScanResult = scanResult;
|
|
2991
|
+
const { files, envTypes } = generate({
|
|
2992
|
+
scanResult,
|
|
2993
|
+
windows: this.config.windows ?? [],
|
|
2994
|
+
outputDir,
|
|
2995
|
+
srcDir
|
|
2996
|
+
});
|
|
2997
|
+
await mkdir(outputDir, { recursive: true });
|
|
2998
|
+
for (const file of files) {
|
|
2999
|
+
const filePath = resolve(outputDir, file.path);
|
|
3000
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
3001
|
+
await writeFile(filePath, file.content);
|
|
3002
|
+
}
|
|
3003
|
+
const envTypesPath = resolve(srcDir, envTypes.path);
|
|
3004
|
+
await mkdir(dirname(envTypesPath), { recursive: true });
|
|
3005
|
+
await writeFile(envTypesPath, envTypes.content);
|
|
3006
|
+
}
|
|
3007
|
+
async startRenderer() {
|
|
3008
|
+
const windows = this.config.windows ?? [];
|
|
3009
|
+
const userViteConfigs = windows.filter((w) => w.vite).map((w) => w.vite);
|
|
3010
|
+
this.rendererServer = await createServer(createRendererConfig({
|
|
3011
|
+
root: this.root,
|
|
3012
|
+
windows,
|
|
3013
|
+
userViteConfigs: userViteConfigs.length > 0 ? userViteConfigs : void 0,
|
|
3014
|
+
logLevel: this.logLevel,
|
|
3015
|
+
clearScreen: this.clearScreen
|
|
3016
|
+
}));
|
|
3017
|
+
patchLogger(this.rendererServer.config.logger, "renderer");
|
|
3018
|
+
await this.rendererServer.listen();
|
|
3019
|
+
const addr = this.rendererServer.httpServer?.address();
|
|
3020
|
+
this.rendererUrl = `http://localhost:${typeof addr === "object" && addr ? addr.port : 5173}`;
|
|
3021
|
+
}
|
|
3022
|
+
async buildPreload(externals) {
|
|
3023
|
+
const windows = this.config.windows ?? [];
|
|
3024
|
+
const preloadOutDir = resolve(this.outputDir, "preload");
|
|
3025
|
+
const input = {};
|
|
3026
|
+
for (const win of windows) input[win.name] = resolve(this.outputDir, `generated/preload/${win.name}.gen.ts`);
|
|
3027
|
+
const firstEntry = Object.values(input)[0];
|
|
3028
|
+
const baseConfig = createNodeConfig({
|
|
3029
|
+
scope: "preload",
|
|
3030
|
+
root: this.root,
|
|
3031
|
+
entry: firstEntry,
|
|
3032
|
+
externals,
|
|
3033
|
+
outDir: preloadOutDir,
|
|
3034
|
+
watch: true,
|
|
3035
|
+
plugins: [
|
|
3036
|
+
assetPlugin(),
|
|
3037
|
+
workerPlugin(),
|
|
3038
|
+
modulePathPlugin()
|
|
3039
|
+
],
|
|
3040
|
+
logLevel: this.logLevel,
|
|
3041
|
+
clearScreen: this.clearScreen,
|
|
3042
|
+
sourcemap: this.sourcemap
|
|
3043
|
+
});
|
|
3044
|
+
if (Object.keys(input).length > 1) {
|
|
3045
|
+
const subBuildConfig = createNodeConfig({
|
|
3046
|
+
scope: "preload",
|
|
3047
|
+
root: this.root,
|
|
3048
|
+
entry: firstEntry,
|
|
3049
|
+
externals,
|
|
3050
|
+
outDir: preloadOutDir,
|
|
3051
|
+
watch: false,
|
|
3052
|
+
plugins: [
|
|
3053
|
+
assetPlugin(),
|
|
3054
|
+
workerPlugin(),
|
|
3055
|
+
modulePathPlugin()
|
|
3056
|
+
],
|
|
3057
|
+
logLevel: this.logLevel,
|
|
3058
|
+
clearScreen: this.clearScreen,
|
|
3059
|
+
sourcemap: this.sourcemap
|
|
3060
|
+
});
|
|
3061
|
+
baseConfig.plugins.push(isolateEntriesPlugin(subBuildConfig));
|
|
3062
|
+
}
|
|
3063
|
+
if (baseConfig.build) baseConfig.build.rolldownOptions = {
|
|
3064
|
+
...baseConfig.build.rolldownOptions,
|
|
3065
|
+
input
|
|
3066
|
+
};
|
|
3067
|
+
let firstBuild = true;
|
|
3068
|
+
const self = this;
|
|
3069
|
+
baseConfig.plugins.push({
|
|
3070
|
+
name: "electro:preload-watch",
|
|
3071
|
+
apply: "build",
|
|
3072
|
+
watchChange(id) {
|
|
3073
|
+
if (!firstBuild) runtimeLog("preload", "rebuild → page reload", relative(self.root, id));
|
|
3074
|
+
},
|
|
3075
|
+
closeBundle() {
|
|
3076
|
+
if (firstBuild) {
|
|
3077
|
+
firstBuild = false;
|
|
3078
|
+
return;
|
|
3079
|
+
}
|
|
3080
|
+
if (self.rendererServer) self.rendererServer.ws.send({ type: "full-reload" });
|
|
3081
|
+
}
|
|
3082
|
+
});
|
|
3083
|
+
this.preloadWatch = await build(baseConfig);
|
|
3084
|
+
}
|
|
3085
|
+
async buildMain(externals) {
|
|
3086
|
+
const runtimeEntry = this.config.runtime.entry;
|
|
3087
|
+
const entry = resolve(dirname(this.config.runtime.__source), runtimeEntry);
|
|
3088
|
+
const windowDefs = (this.config.windows ?? []).map((w) => ({
|
|
3089
|
+
name: w.name,
|
|
3090
|
+
type: w.type,
|
|
3091
|
+
lifecycle: w.lifecycle,
|
|
3092
|
+
autoShow: w.autoShow,
|
|
3093
|
+
behavior: w.behavior,
|
|
3094
|
+
window: w.window
|
|
3095
|
+
}));
|
|
3096
|
+
const mainConfig = createNodeConfig({
|
|
3097
|
+
scope: "main",
|
|
3098
|
+
root: this.root,
|
|
3099
|
+
entry,
|
|
3100
|
+
externals,
|
|
3101
|
+
outDir: resolve(this.outputDir, "main"),
|
|
3102
|
+
watch: true,
|
|
3103
|
+
plugins: [
|
|
3104
|
+
assetPlugin(),
|
|
3105
|
+
workerPlugin(),
|
|
3106
|
+
modulePathPlugin()
|
|
3107
|
+
],
|
|
3108
|
+
logLevel: this.logLevel,
|
|
3109
|
+
clearScreen: this.clearScreen,
|
|
3110
|
+
userViteConfig: this.config.runtime.vite,
|
|
3111
|
+
sourcemap: this.sourcemap,
|
|
3112
|
+
define: { __ELECTRO_WINDOW_DEFINITIONS__: JSON.stringify(windowDefs) }
|
|
3113
|
+
});
|
|
3114
|
+
const self = this;
|
|
3115
|
+
let firstBuild = true;
|
|
3116
|
+
let changedFile = null;
|
|
3117
|
+
mainConfig.plugins.push({
|
|
3118
|
+
name: "electro:main-watch",
|
|
3119
|
+
apply: "build",
|
|
3120
|
+
watchChange(id) {
|
|
3121
|
+
changedFile = changedFile ?? id;
|
|
3122
|
+
},
|
|
3123
|
+
async closeBundle() {
|
|
3124
|
+
if (firstBuild) {
|
|
3125
|
+
firstBuild = false;
|
|
3126
|
+
changedFile = null;
|
|
3127
|
+
return;
|
|
3128
|
+
}
|
|
3129
|
+
const currentChanged = changedFile;
|
|
3130
|
+
changedFile = null;
|
|
3131
|
+
const srcDir = resolve(self.root, "src");
|
|
3132
|
+
const newScan = await scan(srcDir);
|
|
3133
|
+
if (JSON.stringify(newScan) !== JSON.stringify(self.lastScanResult)) {
|
|
3134
|
+
runtimeLog("main", "generated");
|
|
3135
|
+
await self.runCodegen(self.outputDir, srcDir);
|
|
3136
|
+
}
|
|
3137
|
+
self.queueMainRestart(currentChanged);
|
|
3138
|
+
}
|
|
3139
|
+
});
|
|
3140
|
+
this.mainWatch = await build(mainConfig);
|
|
3141
|
+
}
|
|
3142
|
+
async attachElectronProcess() {
|
|
3143
|
+
const mainEntry = resolve(this.outputDir, "main/index.mjs");
|
|
3144
|
+
const env = { ELECTRO_DEV: "true" };
|
|
3145
|
+
if (this.rendererServer) {
|
|
3146
|
+
const addr = this.rendererServer.httpServer?.address();
|
|
3147
|
+
const port = typeof addr === "object" && addr ? addr.port : 5173;
|
|
3148
|
+
env.ELECTRO_RENDERER_BASE = `http://localhost:${port}`;
|
|
3149
|
+
for (const win of this.config.windows ?? []) {
|
|
3150
|
+
const entryPath = resolve(dirname(win.__source), win.entry);
|
|
3151
|
+
const relPath = relative(this.root, entryPath);
|
|
3152
|
+
env[`ELECTRO_DEV_URL_${win.name}`] = `http://localhost:${port}/${relPath}`;
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
const proc = await launchElectron({
|
|
3156
|
+
root: this.root,
|
|
3157
|
+
entry: mainEntry,
|
|
3158
|
+
env
|
|
3159
|
+
});
|
|
3160
|
+
this.electronProcess = proc;
|
|
3161
|
+
proc.exited.then((code) => {
|
|
3162
|
+
if (this.electronProcess !== proc) return;
|
|
3163
|
+
if (this.shuttingDown) return;
|
|
3164
|
+
if (code === 0) runtimeLog("main", "exited");
|
|
3165
|
+
else runtimeLog("main", `crashed (exit ${code})`);
|
|
3166
|
+
this.stop();
|
|
3167
|
+
process.exit(code === 0 ? 0 : 1);
|
|
3168
|
+
});
|
|
3169
|
+
}
|
|
3170
|
+
/**
|
|
3171
|
+
* Restart Electron — handles queued restarts if another rebuild
|
|
3172
|
+
* arrives while restart is in flight.
|
|
3173
|
+
*/
|
|
3174
|
+
async restartElectron(changedFile) {
|
|
3175
|
+
if (this.restartInFlight) {
|
|
3176
|
+
this.restartQueued = true;
|
|
3177
|
+
this.restartQueuedFile = changedFile ?? this.restartQueuedFile;
|
|
3178
|
+
return;
|
|
3179
|
+
}
|
|
3180
|
+
this.restartInFlight = true;
|
|
3181
|
+
let nextChanged = changedFile;
|
|
3182
|
+
do {
|
|
3183
|
+
this.restartQueued = false;
|
|
3184
|
+
runtimeLog("main", "rebuild → restart", nextChanged ? relative(this.root, nextChanged) : null);
|
|
3185
|
+
nextChanged = null;
|
|
3186
|
+
if (this.electronProcess) {
|
|
3187
|
+
const prev = this.electronProcess;
|
|
3188
|
+
this.electronProcess = null;
|
|
3189
|
+
prev.kill();
|
|
3190
|
+
await prev.exited;
|
|
3191
|
+
}
|
|
3192
|
+
await this.attachElectronProcess();
|
|
3193
|
+
if (this.restartQueued) {
|
|
3194
|
+
nextChanged = this.restartQueuedFile;
|
|
3195
|
+
this.restartQueuedFile = null;
|
|
3196
|
+
}
|
|
3197
|
+
} while (this.restartQueued);
|
|
3198
|
+
this.restartInFlight = false;
|
|
3199
|
+
}
|
|
3200
|
+
queueMainRestart(changedFile) {
|
|
3201
|
+
if (changedFile) this.mainRestartReason = changedFile;
|
|
3202
|
+
else if (!this.mainRestartReason) this.mainRestartReason = changedFile;
|
|
3203
|
+
this.mainRestartQueued = true;
|
|
3204
|
+
this.scheduleMainRestartFlush();
|
|
3205
|
+
}
|
|
3206
|
+
scheduleMainRestartFlush() {
|
|
3207
|
+
if (this.mainRestartFlushTimer) clearTimeout(this.mainRestartFlushTimer);
|
|
3208
|
+
this.mainRestartFlushTimer = setTimeout(() => {
|
|
3209
|
+
this.mainRestartFlushTimer = null;
|
|
3210
|
+
this.flushMainRestartQueue();
|
|
3211
|
+
}, MAIN_RESTART_DEBOUNCE_MS);
|
|
3212
|
+
}
|
|
3213
|
+
async flushMainRestartQueue() {
|
|
3214
|
+
if (this.mainRestartFlushInFlight || !this.mainRestartQueued) return;
|
|
3215
|
+
this.mainRestartFlushInFlight = true;
|
|
3216
|
+
try {
|
|
3217
|
+
const reason = this.mainRestartReason;
|
|
3218
|
+
this.mainRestartQueued = false;
|
|
3219
|
+
this.mainRestartReason = null;
|
|
3220
|
+
await this.restartElectron(reason);
|
|
3221
|
+
} finally {
|
|
3222
|
+
this.mainRestartFlushInFlight = false;
|
|
3223
|
+
if (this.mainRestartQueued && !this.mainRestartFlushTimer) this.scheduleMainRestartFlush();
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
attachConfigWatcher() {
|
|
3227
|
+
if (!this.rendererServer?.watcher) return;
|
|
3228
|
+
const watcher = this.rendererServer.watcher;
|
|
3229
|
+
for (const configPath of this.configPaths) watcher.add(configPath);
|
|
3230
|
+
watcher.on("change", (changedPath) => {
|
|
3231
|
+
if (!this.configPaths.has(changedPath)) return;
|
|
3232
|
+
if (this.shuttingDown) return;
|
|
3233
|
+
if (this.configDebounce) clearTimeout(this.configDebounce);
|
|
3234
|
+
this.configDebounce = setTimeout(() => {
|
|
3235
|
+
this.configDebounce = null;
|
|
3236
|
+
info("Config file changed, restarting...");
|
|
3237
|
+
this.stop();
|
|
3238
|
+
this.onRestart?.();
|
|
3239
|
+
}, CONFIG_DEBOUNCE_MS);
|
|
3240
|
+
});
|
|
3241
|
+
}
|
|
3242
|
+
};
|
|
3243
|
+
|
|
3244
|
+
//#endregion
|
|
3245
|
+
//#region src/commands/dev.ts
|
|
3246
|
+
async function dev(options) {
|
|
3247
|
+
if (options.sourcemap) validateSourcemap(options.sourcemap);
|
|
3248
|
+
const createServer = () => new DevServer(options.config, {
|
|
3249
|
+
logLevel: options.logLevel,
|
|
3250
|
+
clearScreen: options.clearScreen,
|
|
3251
|
+
rendererOnly: options.rendererOnly,
|
|
3252
|
+
sourcemap: options.sourcemap,
|
|
3253
|
+
outDir: options.outDir
|
|
3254
|
+
});
|
|
3255
|
+
let server = createServer();
|
|
3256
|
+
const shutdown = () => {
|
|
3257
|
+
server.stop();
|
|
3258
|
+
process.exit(0);
|
|
3259
|
+
};
|
|
3260
|
+
process.on("SIGINT", shutdown);
|
|
3261
|
+
process.on("SIGTERM", shutdown);
|
|
3262
|
+
const startWithRestart = async () => {
|
|
3263
|
+
server.setOnRestart(() => {
|
|
3264
|
+
server = createServer();
|
|
3265
|
+
startWithRestart();
|
|
3266
|
+
});
|
|
3267
|
+
try {
|
|
3268
|
+
await server.start();
|
|
3269
|
+
} catch (err) {
|
|
3270
|
+
error(`Failed to start dev server: ${err instanceof Error ? err.message : String(err)}`);
|
|
3271
|
+
server.stop();
|
|
3272
|
+
process.exit(1);
|
|
3273
|
+
}
|
|
3274
|
+
};
|
|
3275
|
+
await startWithRestart();
|
|
3276
|
+
await new Promise(() => {});
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
//#endregion
|
|
3280
|
+
//#region src/commands/generate.ts
|
|
3281
|
+
async function generate$1(options) {
|
|
3282
|
+
const configPath = resolve(process.cwd(), options.config);
|
|
3283
|
+
const outputDir = resolve(process.cwd(), options.output);
|
|
3284
|
+
const config = (await import(configPath)).default;
|
|
3285
|
+
if (!config) {
|
|
3286
|
+
console.error("Error: electro.config.ts must have a default export");
|
|
3287
|
+
process.exit(1);
|
|
3288
|
+
}
|
|
3289
|
+
const windows = config.windows ?? [];
|
|
3290
|
+
console.log(`Loaded config with ${windows.length} window(s)`);
|
|
3291
|
+
const srcDir = resolve(process.cwd(), "src");
|
|
3292
|
+
console.log(`Scanning ${srcDir}...`);
|
|
3293
|
+
const scanResult = await scan(srcDir);
|
|
3294
|
+
console.log(`Found ${scanResult.features.length} feature(s)`);
|
|
3295
|
+
const { files, envTypes } = generate({
|
|
3296
|
+
scanResult,
|
|
3297
|
+
windows,
|
|
3298
|
+
outputDir,
|
|
3299
|
+
srcDir
|
|
3300
|
+
});
|
|
3301
|
+
console.log(`Generating ${files.length + 1} file(s)...`);
|
|
3302
|
+
for (const file of files) {
|
|
3303
|
+
const fullPath = resolve(outputDir, file.path);
|
|
3304
|
+
await mkdir(dirname(fullPath), { recursive: true });
|
|
3305
|
+
await writeFile(fullPath, file.content);
|
|
3306
|
+
console.log(` .electro/${file.path}`);
|
|
3307
|
+
}
|
|
3308
|
+
const envTypesPath = resolve(srcDir, envTypes.path);
|
|
3309
|
+
await mkdir(dirname(envTypesPath), { recursive: true });
|
|
3310
|
+
await writeFile(envTypesPath, envTypes.content);
|
|
3311
|
+
console.log(` src/${envTypes.path}`);
|
|
3312
|
+
console.log("Done.");
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3315
|
+
//#endregion
|
|
3316
|
+
//#region src/commands/preview.ts
|
|
3317
|
+
async function preview(options) {
|
|
3318
|
+
if (options.sourcemap) validateSourcemap(options.sourcemap);
|
|
3319
|
+
if (options.logLevel) setLogLevel(options.logLevel);
|
|
3320
|
+
const totalTimer = startTimer();
|
|
3321
|
+
if (!options.skipBuild) await build$1(options);
|
|
3322
|
+
else note("Skipped build (--skip-build)");
|
|
3323
|
+
const root = process.cwd();
|
|
3324
|
+
const mainEntry = resolve(resolve(root, options.outDir), "main/index.mjs");
|
|
3325
|
+
const launchTimer = startTimer();
|
|
3326
|
+
try {
|
|
3327
|
+
const proc = await launchElectron({
|
|
3328
|
+
root,
|
|
3329
|
+
entry: mainEntry
|
|
3330
|
+
});
|
|
3331
|
+
step("electron", launchTimer());
|
|
3332
|
+
footer(`Preview ready in ${totalTimer()}`);
|
|
3333
|
+
const onSignal = () => proc.kill();
|
|
3334
|
+
process.on("SIGINT", onSignal);
|
|
3335
|
+
process.on("SIGTERM", onSignal);
|
|
3336
|
+
const code = await proc.exited ?? 0;
|
|
3337
|
+
process.off("SIGINT", onSignal);
|
|
3338
|
+
process.off("SIGTERM", onSignal);
|
|
3339
|
+
if (code === 0) runtimeLog("main", "exited");
|
|
3340
|
+
else {
|
|
3341
|
+
runtimeLog("main", `crashed (exit ${code})`);
|
|
3342
|
+
process.exit(1);
|
|
3343
|
+
}
|
|
3344
|
+
} catch (err) {
|
|
3345
|
+
stepFail("electron", err instanceof Error ? err.message : String(err));
|
|
3346
|
+
process.exit(1);
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
//#endregion
|
|
3351
|
+
//#region src/index.ts
|
|
3352
|
+
const cli = cac("electro");
|
|
3353
|
+
cli.command("generate", "Generate preload scripts, bridge types, and context types").option("-c, --config <path>", "Path to electro.config.ts", { default: "electro.config.ts" }).option("-o, --output <dir>", "Output directory", { default: ".electro" }).action(generate$1);
|
|
3354
|
+
cli.command("build", "Build for production").option("-c, --config <path>", "Path to electro.config.ts", { default: "electro.config.ts" }).option("-o, --outDir <dir>", "Output directory", { default: "dist" }).option("--sourcemap <mode>", "Sourcemap mode (linked | inline | external | none)").option("--minify", "Minify output (default: true)", { default: true }).option("--no-minify", "Disable minification").option("--bytecode", "Compile main/preload to V8 bytecode for source protection").option("-l, --logLevel <level>", "Log level (info | warn | error | silent)").action(build$1);
|
|
3355
|
+
cli.command("preview", "Build and preview in Electron").option("-c, --config <path>", "Path to electro.config.ts", { default: "electro.config.ts" }).option("-o, --outDir <dir>", "Output directory", { default: "dist" }).option("--sourcemap <mode>", "Sourcemap mode (linked | inline | external | none)").option("--minify", "Minify output (default: true)", { default: true }).option("--no-minify", "Disable minification").option("--bytecode", "Compile main/preload to V8 bytecode for source protection").option("--skip-build", "Skip build step and launch from existing output").option("-l, --logLevel <level>", "Log level (info | warn | error | silent)").action(preview);
|
|
3356
|
+
cli.command("dev", "Start development server with Electron").option("-c, --config <path>", "Path to electro.config.ts", { default: "electro.config.ts" }).option("--clearScreen", "Clear screen on rebuild", { default: true }).option("-l, --logLevel <level>", "Log level (info | warn | error | silent)").option("--renderer-only", "Start only the renderer dev server (no Electron)").option("--sourcemap <mode>", "Sourcemap mode (linked | inline | external | none)").option("--outDir <dir>", "Output directory override (default: .electro)").action(dev);
|
|
3357
|
+
cli.help();
|
|
3358
|
+
cli.version(version$1);
|
|
3359
|
+
cli.parse();
|
|
3360
|
+
|
|
3361
|
+
//#endregion
|
|
3362
|
+
export { };
|