muon-ui 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -12
- package/dist/cli.cjs +291 -126
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/native/linux-amd64/muon-bootstrap +0 -0
- package/dist/native/linux-amd64/muon-builder +0 -0
- package/dist/native/linux-arm64/muon-bootstrap +0 -0
- package/dist/native/linux-arm64/{muon-prepare → muon-builder} +0 -0
- package/dist/native/linux-armhf/muon-bootstrap +0 -0
- package/dist/native/linux-armhf/{muon-prepare → muon-builder} +0 -0
- package/dist/native/muon-256.png +0 -0
- package/dist/native/windows-amd64/muon-bootstrap.exe +0 -0
- package/dist/native/windows-amd64/muon-builder.exe +0 -0
- package/dist/native/windows-i686/muon-bootstrap.exe +0 -0
- package/dist/native/windows-i686/muon-builder.exe +0 -0
- package/dist/runtime/linux-amd64/CREDITS.md +24 -0
- package/dist/runtime/linux-amd64/libmuon-ui.so +0 -0
- package/dist/runtime/linux-amd64/muon-core +0 -0
- package/dist/runtime/linux-arm64/CREDITS.md +24 -0
- package/dist/runtime/linux-arm64/libmuon-ui.so +0 -0
- package/dist/runtime/linux-arm64/muon-core +0 -0
- package/dist/runtime/linux-armhf/CREDITS.md +24 -0
- package/dist/runtime/linux-armhf/libmuon-ui.so +0 -0
- package/dist/runtime/linux-armhf/muon-core +0 -0
- package/dist/runtime/windows-amd64/CREDITS.md +24 -0
- package/dist/runtime/windows-amd64/libcardio.dll +0 -0
- package/dist/runtime/windows-amd64/libmuon-ui.dll +0 -0
- package/dist/runtime/windows-amd64/muon-core.exe +0 -0
- package/dist/runtime/windows-i686/CREDITS.md +24 -0
- package/dist/runtime/windows-i686/libcardio.dll +0 -0
- package/dist/runtime/windows-i686/libmuon-ui.dll +0 -0
- package/dist/runtime/windows-i686/muon-core.exe +0 -0
- package/dist/{vite-options-FFh0NWUa.cjs → vite-internals-HXgH7b5M.cjs} +1460 -121
- package/dist/vite-internals-HXgH7b5M.cjs.map +1 -0
- package/dist/vite.cjs +452 -10
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.mjs +1552 -92
- package/dist/vite.mjs.map +1 -1
- package/muon.d.ts +141 -27
- package/package.json +14 -10
- package/vite.d.ts +276 -14
- package/dist/native/linux-amd64/muon-prepare +0 -0
- package/dist/native/windows-amd64/muon-prepare.exe +0 -0
- package/dist/native/windows-i686/muon-prepare.exe +0 -0
- package/dist/vite-options-FFh0NWUa.cjs.map +0 -1
package/dist/vite.mjs
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* name: muon-ui
|
|
3
|
-
* version: 0.
|
|
3
|
+
* version: 0.9.0
|
|
4
4
|
* description: A multi-platform GUI application framework that uses CEF as its backend
|
|
5
5
|
* author: Kouji Matsui (@kekyo@mi.kekyo.net)
|
|
6
6
|
* license: MIT
|
|
7
7
|
* repository.url: https://github.com/kekyo/muon-ui.git
|
|
8
|
-
* git.commit.hash:
|
|
8
|
+
* git.commit.hash: 3437b522a51d8772713b72722b109c5ff30fc711
|
|
9
9
|
*/
|
|
10
|
-
import { basename, dirname, isAbsolute, join, relative, resolve, sep, win32 } from "node:path";
|
|
11
|
-
import { constants, writeFileSync } from "node:fs";
|
|
10
|
+
import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep, win32 } from "node:path";
|
|
11
|
+
import { constants, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { access, chmod, copyFile, cp, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
13
13
|
import { createHash, randomBytes } from "node:crypto";
|
|
14
14
|
import { tmpdir } from "node:os";
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
16
|
import __screwUpDefaultImportModule0 from "adm-zip";
|
|
17
17
|
import { spawn } from "node:child_process";
|
|
18
|
+
import * as __screwUpDefaultImportModule0$1 from "sharp";
|
|
18
19
|
//#region \0rolldown/runtime.js
|
|
19
20
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
20
21
|
//#endregion
|
|
@@ -1292,8 +1293,10 @@ var encodeTaggedBytes = (tag, bytes) => Buffer.concat([
|
|
|
1292
1293
|
encodeVarUint(BigInt(bytes.length)),
|
|
1293
1294
|
Buffer.from(bytes)
|
|
1294
1295
|
]);
|
|
1295
|
-
var isJsonObject$
|
|
1296
|
+
var isJsonObject$5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1296
1297
|
var isPath = (path, first, second) => path.length === 2 && path[0] === first && path[1] === second;
|
|
1298
|
+
var isPluginSignaturePath = (path) => path.length === 3 && path[0] === "plugin" && path[1] === "plugins" && path[2] === "signature";
|
|
1299
|
+
var isPluginSaltPath = (path) => path.length === 3 && path[0] === "plugin" && path[1] === "plugins" && path[2] === "salt";
|
|
1297
1300
|
var isHexString = (value) => value.length % 2 === 0 && /^[0-9a-fA-F]*$/.test(value);
|
|
1298
1301
|
var decodeHexString = (value) => {
|
|
1299
1302
|
const bytes = Buffer.alloc(value.length / 2);
|
|
@@ -1303,6 +1306,8 @@ var decodeHexString = (value) => {
|
|
|
1303
1306
|
var encodeKnownBinaryString = (path, value) => {
|
|
1304
1307
|
if (isPath(path, "asset", "signature")) return value.length === 40 && isHexString(value) ? decodeHexString(value) : void 0;
|
|
1305
1308
|
if (isPath(path, "asset", "salt")) return isHexString(value) ? decodeHexString(value) : void 0;
|
|
1309
|
+
if (isPluginSignaturePath(path)) return value.length === 40 && isHexString(value) ? decodeHexString(value) : void 0;
|
|
1310
|
+
if (isPluginSaltPath(path)) return isHexString(value) ? decodeHexString(value) : void 0;
|
|
1306
1311
|
if (isPath(path, "browser", "backgroundColor")) {
|
|
1307
1312
|
const hex = value.startsWith("#") ? value.slice(1) : value;
|
|
1308
1313
|
return hex.length === 6 && isHexString(hex) ? decodeHexString(hex) : void 0;
|
|
@@ -1325,7 +1330,7 @@ var encodeTlvValue = (value, path) => {
|
|
|
1325
1330
|
encodeVarUint(BigInt(value.length)),
|
|
1326
1331
|
...value.map((entry) => encodeTlvValue(entry, path))
|
|
1327
1332
|
]);
|
|
1328
|
-
if (isJsonObject$
|
|
1333
|
+
if (isJsonObject$5(value)) {
|
|
1329
1334
|
const entries = Object.entries(value);
|
|
1330
1335
|
return Buffer.concat([
|
|
1331
1336
|
Buffer.from([tlvObjectTag]),
|
|
@@ -1350,8 +1355,8 @@ var encodeMuonConfigTlv = (config) => encodeTlvValue(config, []);
|
|
|
1350
1355
|
*/
|
|
1351
1356
|
var createMuonEmbeddedConfigSlot = (payload = void 0) => {
|
|
1352
1357
|
if (payload !== void 0 && payload.length > muonEmbeddedConfigPayloadCapacity) throw new Error(`Encoded muon config exceeds the embedded slot capacity: ${payload.length} > ${muonEmbeddedConfigPayloadCapacity}`);
|
|
1353
|
-
|
|
1354
|
-
|
|
1358
|
+
if (payload === void 0) return createEmptySlot();
|
|
1359
|
+
const slot = randomBytes(muonEmbeddedConfigSlotSize);
|
|
1355
1360
|
Buffer.from(payload).copy(slot, 0);
|
|
1356
1361
|
return slot;
|
|
1357
1362
|
};
|
|
@@ -1363,9 +1368,12 @@ var createMuonEmbeddedConfigSlot = (payload = void 0) => {
|
|
|
1363
1368
|
*/
|
|
1364
1369
|
var createMuonBootstrapEmbeddedConfigSlot = (payload = void 0) => {
|
|
1365
1370
|
if (payload !== void 0 && payload.length > muonBootstrapEmbeddedConfigPayloadCapacity) throw new Error(`Encoded muon config exceeds the embedded bootstrap slot capacity: ${payload.length} > ${muonBootstrapEmbeddedConfigPayloadCapacity}`);
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1371
|
+
if (payload === void 0) {
|
|
1372
|
+
const slot = Buffer.alloc(muonBootstrapEmbeddedConfigSlotSize, 0);
|
|
1373
|
+
muonBootstrapEmbeddedConfigEmptySlotMarker.copy(slot, 0);
|
|
1374
|
+
return slot;
|
|
1375
|
+
}
|
|
1376
|
+
const slot = randomBytes(muonBootstrapEmbeddedConfigSlotSize);
|
|
1369
1377
|
Buffer.from(payload).copy(slot, 0);
|
|
1370
1378
|
return slot;
|
|
1371
1379
|
};
|
|
@@ -1439,7 +1447,7 @@ var findMuonBootstrapEmbeddedConfigSlot = (content) => {
|
|
|
1439
1447
|
if (candidate === void 0) throw new Error("Embedded muon-bootstrap config slot was not found.");
|
|
1440
1448
|
return candidate;
|
|
1441
1449
|
};
|
|
1442
|
-
var fileExists$
|
|
1450
|
+
var fileExists$6 = async (path) => {
|
|
1443
1451
|
try {
|
|
1444
1452
|
return (await stat(path)).isFile();
|
|
1445
1453
|
} catch {
|
|
@@ -1455,10 +1463,10 @@ var resolveMuonConfigInputPath = async (configPath) => {
|
|
|
1455
1463
|
"muon.json"
|
|
1456
1464
|
]) {
|
|
1457
1465
|
const candidate = join(directory, fileName);
|
|
1458
|
-
if (await fileExists$
|
|
1466
|
+
if (await fileExists$6(candidate)) return candidate;
|
|
1459
1467
|
}
|
|
1460
1468
|
}
|
|
1461
|
-
if (!await fileExists$
|
|
1469
|
+
if (!await fileExists$6(configPath)) throw new Error(`muon config does not exist: ${configPath}`);
|
|
1462
1470
|
return configPath;
|
|
1463
1471
|
};
|
|
1464
1472
|
var readMuonConfigInput = async (configPath) => {
|
|
@@ -1571,7 +1579,7 @@ var targetDescriptors = {
|
|
|
1571
1579
|
cefTarget: "linux64",
|
|
1572
1580
|
os: "linux",
|
|
1573
1581
|
arch: "amd64",
|
|
1574
|
-
distributionDirectoryName: "dist-muon
|
|
1582
|
+
distributionDirectoryName: "dist-muon/linux-amd64",
|
|
1575
1583
|
runtimeExecutableName: "muon-core",
|
|
1576
1584
|
bootstrapExecutableName: "muon-bootstrap",
|
|
1577
1585
|
launcherExtension: "",
|
|
@@ -1586,7 +1594,7 @@ var targetDescriptors = {
|
|
|
1586
1594
|
cefTarget: "linuxarm",
|
|
1587
1595
|
os: "linux",
|
|
1588
1596
|
arch: "armhf",
|
|
1589
|
-
distributionDirectoryName: "dist-muon
|
|
1597
|
+
distributionDirectoryName: "dist-muon/linux-armhf",
|
|
1590
1598
|
runtimeExecutableName: "muon-core",
|
|
1591
1599
|
bootstrapExecutableName: "muon-bootstrap",
|
|
1592
1600
|
launcherExtension: "",
|
|
@@ -1601,7 +1609,7 @@ var targetDescriptors = {
|
|
|
1601
1609
|
cefTarget: "linuxarm64",
|
|
1602
1610
|
os: "linux",
|
|
1603
1611
|
arch: "arm64",
|
|
1604
|
-
distributionDirectoryName: "dist-muon
|
|
1612
|
+
distributionDirectoryName: "dist-muon/linux-arm64",
|
|
1605
1613
|
runtimeExecutableName: "muon-core",
|
|
1606
1614
|
bootstrapExecutableName: "muon-bootstrap",
|
|
1607
1615
|
launcherExtension: "",
|
|
@@ -1616,7 +1624,7 @@ var targetDescriptors = {
|
|
|
1616
1624
|
cefTarget: "windows32",
|
|
1617
1625
|
os: "windows",
|
|
1618
1626
|
arch: "i686",
|
|
1619
|
-
distributionDirectoryName: "dist-muon
|
|
1627
|
+
distributionDirectoryName: "dist-muon/windows-i686",
|
|
1620
1628
|
runtimeExecutableName: "muon-core.exe",
|
|
1621
1629
|
bootstrapExecutableName: "muon-bootstrap.exe",
|
|
1622
1630
|
launcherExtension: ".exe",
|
|
@@ -1636,7 +1644,7 @@ var targetDescriptors = {
|
|
|
1636
1644
|
cefTarget: "windows64",
|
|
1637
1645
|
os: "windows",
|
|
1638
1646
|
arch: "amd64",
|
|
1639
|
-
distributionDirectoryName: "dist-muon
|
|
1647
|
+
distributionDirectoryName: "dist-muon/windows-amd64",
|
|
1640
1648
|
runtimeExecutableName: "muon-core.exe",
|
|
1641
1649
|
bootstrapExecutableName: "muon-bootstrap.exe",
|
|
1642
1650
|
launcherExtension: ".exe",
|
|
@@ -1661,6 +1669,20 @@ Object.fromEntries(allMuonTargets.map((target) => [targetDescriptors[target].cef
|
|
|
1661
1669
|
*/
|
|
1662
1670
|
var getMuonTargetDescriptor = (target) => targetDescriptors[target];
|
|
1663
1671
|
/**
|
|
1672
|
+
* Returns the runtime app identifier embedded for one target distribution.
|
|
1673
|
+
*
|
|
1674
|
+
* @param appId Base sanitized application identifier.
|
|
1675
|
+
* @param target Public Muon target.
|
|
1676
|
+
* @returns Target runtime application identifier.
|
|
1677
|
+
*
|
|
1678
|
+
* @remarks Windows targets append their public architecture name so 32-bit and
|
|
1679
|
+
* 64-bit packages do not share runtime state or installer identities.
|
|
1680
|
+
*/
|
|
1681
|
+
var getMuonTargetRuntimeAppId = (appId, target) => {
|
|
1682
|
+
const descriptor = getMuonTargetDescriptor(target);
|
|
1683
|
+
return descriptor.os === "windows" ? `${appId}.${descriptor.arch}` : appId;
|
|
1684
|
+
};
|
|
1685
|
+
/**
|
|
1664
1686
|
* Normalizes a user supplied public Muon target.
|
|
1665
1687
|
*
|
|
1666
1688
|
* @param target Target value supplied by the user.
|
|
@@ -1707,8 +1729,8 @@ var getDefaultMuonPrepareTarget = (platform, architecture) => {
|
|
|
1707
1729
|
throw new Error(`Unsupported Muon prepare target: platform=${platform}, arch=${architecture}`);
|
|
1708
1730
|
}
|
|
1709
1731
|
};
|
|
1710
|
-
var
|
|
1711
|
-
var moduleDirectory$
|
|
1732
|
+
var getBuilderExecutableName = (platform) => platform === "win32" ? "muon-builder.exe" : "muon-builder";
|
|
1733
|
+
var moduleDirectory$4 = typeof __dirname === "string" ? __dirname : dirname(fileURLToPath(import.meta.url));
|
|
1712
1734
|
var canExecute = async (path) => {
|
|
1713
1735
|
try {
|
|
1714
1736
|
await access(path, constants.X_OK);
|
|
@@ -1717,12 +1739,12 @@ var canExecute = async (path) => {
|
|
|
1717
1739
|
return false;
|
|
1718
1740
|
}
|
|
1719
1741
|
};
|
|
1720
|
-
var
|
|
1721
|
-
const explicit = options.prepareExecutablePath ?? options.environment.
|
|
1742
|
+
var resolveMuonBuilderExecutable = async (options) => {
|
|
1743
|
+
const explicit = options.prepareExecutablePath ?? options.environment.MUON_BUILDER_PATH;
|
|
1722
1744
|
if (explicit !== void 0 && explicit !== "") return explicit;
|
|
1723
|
-
const executableName =
|
|
1745
|
+
const executableName = getBuilderExecutableName(process.platform);
|
|
1724
1746
|
const target = getDefaultMuonPrepareTarget(process.platform, process.arch);
|
|
1725
|
-
const candidates = [join(moduleDirectory$
|
|
1747
|
+
const candidates = [join(moduleDirectory$4, "native", target, executableName), join(moduleDirectory$4, "..", "dist", "native", target, executableName)];
|
|
1726
1748
|
for (const candidate of candidates) if (await canExecute(candidate)) return candidate;
|
|
1727
1749
|
return candidates[0] ?? executableName;
|
|
1728
1750
|
};
|
|
@@ -1741,14 +1763,100 @@ var createMuonPrepareArguments = (options) => {
|
|
|
1741
1763
|
if (options.quiet) args.push("--quiet");
|
|
1742
1764
|
return args;
|
|
1743
1765
|
};
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
var
|
|
1751
|
-
|
|
1766
|
+
var spinnerFrames = [
|
|
1767
|
+
"-",
|
|
1768
|
+
"\\",
|
|
1769
|
+
"|",
|
|
1770
|
+
"/"
|
|
1771
|
+
];
|
|
1772
|
+
var spinnerIntervalMilliseconds = 100;
|
|
1773
|
+
var terminalLineStart = "\r";
|
|
1774
|
+
var terminalClearLine = "\x1B[K";
|
|
1775
|
+
var isMuonPrepareStatusLine = (line) => {
|
|
1776
|
+
const trimmed = line.trimEnd();
|
|
1777
|
+
return trimmed.endsWith("...") || trimmed === "Failed to prepare CEF." || trimmed.startsWith("Downloading CEF binary:");
|
|
1778
|
+
};
|
|
1779
|
+
var shouldKeepMuonPrepareStatusAfterLine = (line) => line.startsWith("Muon files copied to staging:");
|
|
1780
|
+
var createPlainStderrForwarder = () => ({
|
|
1781
|
+
write: (chunk) => {
|
|
1782
|
+
process.stderr.write(chunk);
|
|
1783
|
+
},
|
|
1784
|
+
flush: () => {}
|
|
1785
|
+
});
|
|
1786
|
+
var createSpinnerStderrForwarder = () => {
|
|
1787
|
+
let pending = "";
|
|
1788
|
+
let activeStatus = void 0;
|
|
1789
|
+
let frameIndex = 0;
|
|
1790
|
+
let timer = void 0;
|
|
1791
|
+
const writeRaw = (chunk) => {
|
|
1792
|
+
process.stderr.write(chunk);
|
|
1793
|
+
};
|
|
1794
|
+
const stopTimer = () => {
|
|
1795
|
+
if (timer !== void 0) {
|
|
1796
|
+
clearInterval(timer);
|
|
1797
|
+
timer = void 0;
|
|
1798
|
+
}
|
|
1799
|
+
};
|
|
1800
|
+
const renderStatus = () => {
|
|
1801
|
+
if (activeStatus === void 0) return;
|
|
1802
|
+
writeRaw(`${terminalLineStart}${spinnerFrames[frameIndex] ?? spinnerFrames[0]} ${activeStatus}${terminalClearLine}`);
|
|
1803
|
+
frameIndex = (frameIndex + 1) % spinnerFrames.length;
|
|
1804
|
+
};
|
|
1805
|
+
const ensureTimer = () => {
|
|
1806
|
+
if (timer === void 0) timer = setInterval(renderStatus, spinnerIntervalMilliseconds);
|
|
1807
|
+
};
|
|
1808
|
+
const finishStatus = () => {
|
|
1809
|
+
if (activeStatus === void 0) return;
|
|
1810
|
+
const status = activeStatus;
|
|
1811
|
+
activeStatus = void 0;
|
|
1812
|
+
stopTimer();
|
|
1813
|
+
frameIndex = 0;
|
|
1814
|
+
writeRaw(`${terminalLineStart}${status}${terminalClearLine}\n`);
|
|
1815
|
+
};
|
|
1816
|
+
const startStatus = (status) => {
|
|
1817
|
+
if (activeStatus !== void 0 && activeStatus !== status) finishStatus();
|
|
1818
|
+
activeStatus = status;
|
|
1819
|
+
renderStatus();
|
|
1820
|
+
ensureTimer();
|
|
1821
|
+
};
|
|
1822
|
+
const writeLine = (line) => {
|
|
1823
|
+
const text = line.replace(/\r?\n$/, "");
|
|
1824
|
+
if (isMuonPrepareStatusLine(text)) {
|
|
1825
|
+
startStatus(text.trimEnd());
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1828
|
+
if (activeStatus !== void 0 && shouldKeepMuonPrepareStatusAfterLine(text)) {
|
|
1829
|
+
writeRaw(`${terminalLineStart}${terminalClearLine}${line.endsWith("\n") ? line : `${line}\n`}`);
|
|
1830
|
+
renderStatus();
|
|
1831
|
+
return;
|
|
1832
|
+
}
|
|
1833
|
+
finishStatus();
|
|
1834
|
+
writeRaw(line);
|
|
1835
|
+
};
|
|
1836
|
+
return {
|
|
1837
|
+
write: (chunk) => {
|
|
1838
|
+
pending += chunk;
|
|
1839
|
+
for (;;) {
|
|
1840
|
+
const newlineIndex = pending.indexOf("\n");
|
|
1841
|
+
if (newlineIndex < 0) break;
|
|
1842
|
+
const line = pending.slice(0, newlineIndex + 1);
|
|
1843
|
+
pending = pending.slice(newlineIndex + 1);
|
|
1844
|
+
writeLine(line);
|
|
1845
|
+
}
|
|
1846
|
+
},
|
|
1847
|
+
flush: () => {
|
|
1848
|
+
if (pending.length > 0) {
|
|
1849
|
+
const line = pending;
|
|
1850
|
+
pending = "";
|
|
1851
|
+
writeLine(line);
|
|
1852
|
+
}
|
|
1853
|
+
finishStatus();
|
|
1854
|
+
}
|
|
1855
|
+
};
|
|
1856
|
+
};
|
|
1857
|
+
var createMuonPrepareStderrForwarder = () => process.stderr.isTTY === true ? createSpinnerStderrForwarder() : createPlainStderrForwarder();
|
|
1858
|
+
var runMuonPrepareCommand = async (options) => {
|
|
1859
|
+
const child = spawn(await resolveMuonBuilderExecutable(options), [...options.args], {
|
|
1752
1860
|
cwd: options.cwd,
|
|
1753
1861
|
env: options.environment,
|
|
1754
1862
|
stdio: [
|
|
@@ -1761,22 +1869,46 @@ var runMuonPrepare = async (options) => {
|
|
|
1761
1869
|
let stderr = "";
|
|
1762
1870
|
child.stdout.setEncoding("utf8");
|
|
1763
1871
|
child.stderr.setEncoding("utf8");
|
|
1872
|
+
const stderrForwarder = options.quiet ? void 0 : createMuonPrepareStderrForwarder();
|
|
1764
1873
|
child.stdout.on("data", (chunk) => {
|
|
1765
1874
|
stdout += chunk;
|
|
1766
1875
|
});
|
|
1767
1876
|
child.stderr.on("data", (chunk) => {
|
|
1768
1877
|
stderr += chunk;
|
|
1769
|
-
|
|
1878
|
+
stderrForwarder?.write(chunk);
|
|
1770
1879
|
});
|
|
1771
|
-
const exitCode = await
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1880
|
+
const exitCode = await (async () => {
|
|
1881
|
+
try {
|
|
1882
|
+
return await new Promise((resolvePromise, reject) => {
|
|
1883
|
+
child.on("error", reject);
|
|
1884
|
+
child.on("close", (code) => {
|
|
1885
|
+
resolvePromise(code ?? 1);
|
|
1886
|
+
});
|
|
1887
|
+
});
|
|
1888
|
+
} finally {
|
|
1889
|
+
stderrForwarder?.flush();
|
|
1890
|
+
}
|
|
1891
|
+
})();
|
|
1892
|
+
if (exitCode !== 0) throw new Error(`muon-builder failed with exit code ${exitCode}.\n${stderr.trim()}`);
|
|
1893
|
+
return stdout;
|
|
1894
|
+
};
|
|
1895
|
+
/**
|
|
1896
|
+
* Invokes the native muon-builder executable and returns the prepared runtime.
|
|
1897
|
+
*
|
|
1898
|
+
* @param options Native prepare invocation options.
|
|
1899
|
+
* @returns Prepared runtime location.
|
|
1900
|
+
*/
|
|
1901
|
+
var runMuonPrepare = async (options) => {
|
|
1902
|
+
const args = createMuonPrepareArguments(options);
|
|
1903
|
+
const stdout = await runMuonPrepareCommand({
|
|
1904
|
+
prepareExecutablePath: options.prepareExecutablePath,
|
|
1905
|
+
environment: options.environment,
|
|
1906
|
+
cwd: options.cwd,
|
|
1907
|
+
quiet: options.quiet,
|
|
1908
|
+
args
|
|
1776
1909
|
});
|
|
1777
|
-
if (exitCode !== 0) throw new Error(`muon-prepare failed with exit code ${exitCode}.\n${stderr.trim()}`);
|
|
1778
1910
|
const result = JSON.parse(stdout);
|
|
1779
|
-
if (result.stagePath !== void 0 && typeof result.stagePath !== "string" || typeof result.muonPath !== "string" || typeof result.cefPath !== "string" || typeof result.cacheHit !== "boolean") throw new Error(`muon-
|
|
1911
|
+
if (result.stagePath !== void 0 && typeof result.stagePath !== "string" || typeof result.muonPath !== "string" || typeof result.cefPath !== "string" || typeof result.cacheHit !== "boolean") throw new Error(`muon-builder returned invalid JSON: ${stdout}`);
|
|
1780
1912
|
return {
|
|
1781
1913
|
...result.stagePath === void 0 ? {} : { stagePath: result.stagePath },
|
|
1782
1914
|
muonPath: result.muonPath,
|
|
@@ -1784,6 +1916,776 @@ var runMuonPrepare = async (options) => {
|
|
|
1784
1916
|
cacheHit: result.cacheHit
|
|
1785
1917
|
};
|
|
1786
1918
|
};
|
|
1919
|
+
/**
|
|
1920
|
+
* Invokes the native muon-builder executable to write Windows PE resources.
|
|
1921
|
+
*
|
|
1922
|
+
* @param options Resource update invocation options.
|
|
1923
|
+
*/
|
|
1924
|
+
var runMuonPrepareResourceUpdate = async (options) => {
|
|
1925
|
+
await runMuonPrepareCommand({
|
|
1926
|
+
prepareExecutablePath: options.prepareExecutablePath,
|
|
1927
|
+
environment: options.environment,
|
|
1928
|
+
cwd: options.cwd,
|
|
1929
|
+
quiet: options.quiet,
|
|
1930
|
+
args: [
|
|
1931
|
+
"resource",
|
|
1932
|
+
"--input",
|
|
1933
|
+
options.inputPath,
|
|
1934
|
+
"--updates-json",
|
|
1935
|
+
options.updatesJsonPath,
|
|
1936
|
+
"--output",
|
|
1937
|
+
options.outputPath,
|
|
1938
|
+
...options.quiet ? ["--quiet"] : []
|
|
1939
|
+
]
|
|
1940
|
+
});
|
|
1941
|
+
};
|
|
1942
|
+
//#endregion
|
|
1943
|
+
//#region src/windows-icon.ts
|
|
1944
|
+
var sharp = __resolveDefaultExport$1(__screwUpDefaultImportModule0$1, true);
|
|
1945
|
+
globalThis.__screwUpIsInCJS_27e672935538 = false;
|
|
1946
|
+
function __resolveDefaultExport$1(module, isESM) {
|
|
1947
|
+
const __isInCJS = typeof globalThis !== "undefined" && globalThis.__screwUpIsInCJS_27e672935538 === true;
|
|
1948
|
+
const maybe = module;
|
|
1949
|
+
const hasDefault = !!(maybe && typeof maybe === "object" && "default" in maybe);
|
|
1950
|
+
const unwrapNamespaceDefault = (value) => {
|
|
1951
|
+
if (!value || typeof value !== "object") return value;
|
|
1952
|
+
const inner = value;
|
|
1953
|
+
if ((inner.__esModule === true || typeof Symbol !== "undefined" && inner[Symbol.toStringTag] === "Module") && "default" in inner) return inner.default;
|
|
1954
|
+
return value;
|
|
1955
|
+
};
|
|
1956
|
+
const resolvedDefault = hasDefault ? unwrapNamespaceDefault(maybe.default) : void 0;
|
|
1957
|
+
if (__isInCJS) return hasDefault ? resolvedDefault ?? module : module;
|
|
1958
|
+
if (isESM) {
|
|
1959
|
+
if (hasDefault) return resolvedDefault;
|
|
1960
|
+
throw new Error("Default export not found.");
|
|
1961
|
+
}
|
|
1962
|
+
return hasDefault ? resolvedDefault ?? module : module;
|
|
1963
|
+
}
|
|
1964
|
+
var normalizedIconSize = 256;
|
|
1965
|
+
var windowsIconDibSizes = [
|
|
1966
|
+
16,
|
|
1967
|
+
24,
|
|
1968
|
+
32,
|
|
1969
|
+
48,
|
|
1970
|
+
64,
|
|
1971
|
+
128
|
|
1972
|
+
];
|
|
1973
|
+
var pngSignature = Buffer.from([
|
|
1974
|
+
137,
|
|
1975
|
+
80,
|
|
1976
|
+
78,
|
|
1977
|
+
71,
|
|
1978
|
+
13,
|
|
1979
|
+
10,
|
|
1980
|
+
26,
|
|
1981
|
+
10
|
|
1982
|
+
]);
|
|
1983
|
+
/**
|
|
1984
|
+
* Creates a normalized square PNG icon image.
|
|
1985
|
+
*
|
|
1986
|
+
* @param pngData Source PNG bytes.
|
|
1987
|
+
* @param source Diagnostic source label used in errors.
|
|
1988
|
+
* @param label User-facing diagnostic label.
|
|
1989
|
+
* @returns PNG bytes normalized to the Muon base icon size.
|
|
1990
|
+
* @remarks Non-square images are fitted into transparent padding without
|
|
1991
|
+
* stretching. The normalized PNG is the source for all platform-specific icon
|
|
1992
|
+
* derivatives.
|
|
1993
|
+
*/
|
|
1994
|
+
var createNormalizedIconPngData = async (pngData, source, label) => {
|
|
1995
|
+
await assertPngData(pngData, source, label);
|
|
1996
|
+
try {
|
|
1997
|
+
return await sharp(pngData).resize(normalizedIconSize, normalizedIconSize, {
|
|
1998
|
+
fit: "contain",
|
|
1999
|
+
background: {
|
|
2000
|
+
r: 0,
|
|
2001
|
+
g: 0,
|
|
2002
|
+
b: 0,
|
|
2003
|
+
alpha: 0
|
|
2004
|
+
}
|
|
2005
|
+
}).ensureAlpha().png().toBuffer();
|
|
2006
|
+
} catch {
|
|
2007
|
+
throw new Error(`${label} must be a valid PNG: ${source}`);
|
|
2008
|
+
}
|
|
2009
|
+
};
|
|
2010
|
+
/**
|
|
2011
|
+
* Creates a normalized square PNG icon image for Windows resources.
|
|
2012
|
+
*
|
|
2013
|
+
* @param pngData Source PNG bytes.
|
|
2014
|
+
* @param source Diagnostic source label used in errors.
|
|
2015
|
+
* @returns PNG bytes normalized to the Muon base icon size.
|
|
2016
|
+
*/
|
|
2017
|
+
var createNormalizedMuonIconPngData = async (pngData, source) => await createNormalizedIconPngData(pngData, source, "Windows resource icon");
|
|
2018
|
+
/**
|
|
2019
|
+
* Creates a Windows ICO file from PNG bytes.
|
|
2020
|
+
*
|
|
2021
|
+
* @param pngData Source PNG bytes.
|
|
2022
|
+
* @param source Diagnostic source label used in errors.
|
|
2023
|
+
* @returns ICO file bytes containing 16, 24, 32, 48, 64, 128, and 256 pixel images.
|
|
2024
|
+
* @remarks The 256px entry is PNG-compressed. Smaller entries are 32-bit DIB
|
|
2025
|
+
* images, matching the common Windows ICO layout recommended by Microsoft.
|
|
2026
|
+
*/
|
|
2027
|
+
var createWindowsIconBufferFromPngData = async (pngData, source) => {
|
|
2028
|
+
const normalizedPng = await createNormalizedMuonIconPngData(pngData, source);
|
|
2029
|
+
const images = [];
|
|
2030
|
+
for (const size of windowsIconDibSizes) {
|
|
2031
|
+
const rawImage = await resizePngToRawRgba(normalizedPng, size, source);
|
|
2032
|
+
images.push({
|
|
2033
|
+
data: createDibIconImage(rawImage),
|
|
2034
|
+
size,
|
|
2035
|
+
bitCount: 32
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
images.push({
|
|
2039
|
+
data: normalizedPng,
|
|
2040
|
+
size: normalizedIconSize,
|
|
2041
|
+
bitCount: 32
|
|
2042
|
+
});
|
|
2043
|
+
return createIco(images);
|
|
2044
|
+
};
|
|
2045
|
+
/**
|
|
2046
|
+
* Creates a Windows ICO file from a PNG file.
|
|
2047
|
+
*
|
|
2048
|
+
* @param sourcePath Source PNG file path.
|
|
2049
|
+
* @param outputPath Output ICO file path.
|
|
2050
|
+
* @returns Promise resolved when the ICO file has been written.
|
|
2051
|
+
*/
|
|
2052
|
+
var createWindowsIconFromPngFile = async (sourcePath, outputPath) => {
|
|
2053
|
+
const icon = await createWindowsIconBufferFromPngData(await readFile(sourcePath), sourcePath);
|
|
2054
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
2055
|
+
await writeFile(outputPath, icon);
|
|
2056
|
+
};
|
|
2057
|
+
var assertPngData = async (data, source, label) => {
|
|
2058
|
+
if (data.length === 0) throw new Error(`${label} PNG must not be empty: ${source}`);
|
|
2059
|
+
if (!data.subarray(0, pngSignature.length).equals(pngSignature)) throw new Error(`${label} must be a valid PNG: ${source}`);
|
|
2060
|
+
try {
|
|
2061
|
+
if ((await sharp(data).metadata()).format !== "png") throw new Error();
|
|
2062
|
+
} catch {
|
|
2063
|
+
throw new Error(`${label} must be a valid PNG: ${source}`);
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
var resizePngToRawRgba = async (pngData, size, source) => {
|
|
2067
|
+
try {
|
|
2068
|
+
const { data, info } = await sharp(pngData).resize(size, size, {
|
|
2069
|
+
fit: "fill",
|
|
2070
|
+
kernel: "lanczos3"
|
|
2071
|
+
}).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
2072
|
+
if (info.width !== size || info.height !== size || info.channels !== 4) throw new Error();
|
|
2073
|
+
return {
|
|
2074
|
+
data,
|
|
2075
|
+
width: info.width,
|
|
2076
|
+
height: info.height
|
|
2077
|
+
};
|
|
2078
|
+
} catch {
|
|
2079
|
+
throw new Error(`Failed to resize Windows resource icon PNG: ${source}`);
|
|
2080
|
+
}
|
|
2081
|
+
};
|
|
2082
|
+
var createDibIconImage = (image) => {
|
|
2083
|
+
const pixelBytes = image.width * image.height * 4;
|
|
2084
|
+
const maskBytes = Math.ceil(image.width / 32) * 4 * image.height;
|
|
2085
|
+
const output = Buffer.alloc(40 + pixelBytes + maskBytes);
|
|
2086
|
+
output.writeUInt32LE(40, 0);
|
|
2087
|
+
output.writeInt32LE(image.width, 4);
|
|
2088
|
+
output.writeInt32LE(image.height * 2, 8);
|
|
2089
|
+
output.writeUInt16LE(1, 12);
|
|
2090
|
+
output.writeUInt16LE(32, 14);
|
|
2091
|
+
output.writeUInt32LE(0, 16);
|
|
2092
|
+
output.writeUInt32LE(pixelBytes + maskBytes, 20);
|
|
2093
|
+
output.writeInt32LE(0, 24);
|
|
2094
|
+
output.writeInt32LE(0, 28);
|
|
2095
|
+
output.writeUInt32LE(0, 32);
|
|
2096
|
+
output.writeUInt32LE(0, 36);
|
|
2097
|
+
for (let y = 0; y < image.height; y += 1) {
|
|
2098
|
+
const sourceY = image.height - 1 - y;
|
|
2099
|
+
for (let x = 0; x < image.width; x += 1) {
|
|
2100
|
+
const sourceOffset = (sourceY * image.width + x) * 4;
|
|
2101
|
+
const outputOffset = 40 + (y * image.width + x) * 4;
|
|
2102
|
+
output[outputOffset] = image.data[sourceOffset + 2];
|
|
2103
|
+
output[outputOffset + 1] = image.data[sourceOffset + 1];
|
|
2104
|
+
output[outputOffset + 2] = image.data[sourceOffset];
|
|
2105
|
+
output[outputOffset + 3] = image.data[sourceOffset + 3];
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
return output;
|
|
2109
|
+
};
|
|
2110
|
+
var createIco = (images) => {
|
|
2111
|
+
const directorySize = 6 + images.length * 16;
|
|
2112
|
+
let imageOffset = directorySize;
|
|
2113
|
+
const header = Buffer.alloc(directorySize);
|
|
2114
|
+
header.writeUInt16LE(0, 0);
|
|
2115
|
+
header.writeUInt16LE(1, 2);
|
|
2116
|
+
header.writeUInt16LE(images.length, 4);
|
|
2117
|
+
const imageData = [];
|
|
2118
|
+
images.forEach((image, index) => {
|
|
2119
|
+
const entryOffset = 6 + index * 16;
|
|
2120
|
+
header[entryOffset] = image.size === 256 ? 0 : image.size;
|
|
2121
|
+
header[entryOffset + 1] = image.size === 256 ? 0 : image.size;
|
|
2122
|
+
header[entryOffset + 2] = 0;
|
|
2123
|
+
header[entryOffset + 3] = 0;
|
|
2124
|
+
header.writeUInt16LE(1, entryOffset + 4);
|
|
2125
|
+
header.writeUInt16LE(image.bitCount, entryOffset + 6);
|
|
2126
|
+
header.writeUInt32LE(image.data.length, entryOffset + 8);
|
|
2127
|
+
header.writeUInt32LE(imageOffset, entryOffset + 12);
|
|
2128
|
+
imageOffset += image.data.length;
|
|
2129
|
+
imageData.push(image.data);
|
|
2130
|
+
});
|
|
2131
|
+
return Buffer.concat([header, ...imageData]);
|
|
2132
|
+
};
|
|
2133
|
+
//#endregion
|
|
2134
|
+
//#region src/app-icon.ts
|
|
2135
|
+
/**
|
|
2136
|
+
* ZIP entry used for the generated static application icon asset.
|
|
2137
|
+
*/
|
|
2138
|
+
var appIconAssetEntryName = "main/.muon/app-icon.png";
|
|
2139
|
+
/**
|
|
2140
|
+
* Runtime asset URL injected as the generated initial title bar icon.
|
|
2141
|
+
*/
|
|
2142
|
+
var appIconAssetUrl = "asset://main/.muon/app-icon.png";
|
|
2143
|
+
/**
|
|
2144
|
+
* Creates a static application icon source from API or CLI options.
|
|
2145
|
+
*/
|
|
2146
|
+
var createAppIconOptionsSource = (iconPath, directory, label = "iconPath") => {
|
|
2147
|
+
if (iconPath === void 0) return;
|
|
2148
|
+
if (typeof iconPath !== "string") throw new Error(`${label} must be a string when present.`);
|
|
2149
|
+
if (iconPath.trim() === "") return;
|
|
2150
|
+
return {
|
|
2151
|
+
iconPath: iconPath.trim(),
|
|
2152
|
+
directory
|
|
2153
|
+
};
|
|
2154
|
+
};
|
|
2155
|
+
/**
|
|
2156
|
+
* Reads the top-level `iconPath` from a Muon config object.
|
|
2157
|
+
*/
|
|
2158
|
+
var readConfigAppIconSource = (config, directory, label) => {
|
|
2159
|
+
const value = config.iconPath;
|
|
2160
|
+
if (value === void 0) return;
|
|
2161
|
+
if (typeof value !== "string") throw new Error(`${label} iconPath must be a string when present.`);
|
|
2162
|
+
return createAppIconOptionsSource(value, directory);
|
|
2163
|
+
};
|
|
2164
|
+
/**
|
|
2165
|
+
* Reads the top-level `iconPath` from project metadata.
|
|
2166
|
+
*/
|
|
2167
|
+
var readProjectAppIconSource = (projectJson, directory) => {
|
|
2168
|
+
const value = projectJson.iconPath;
|
|
2169
|
+
return typeof value === "string" ? createAppIconOptionsSource(value, directory) : void 0;
|
|
2170
|
+
};
|
|
2171
|
+
/**
|
|
2172
|
+
* Resolves and validates the first available static application icon source.
|
|
2173
|
+
*/
|
|
2174
|
+
var resolveMuonAppIconPath = async (sources) => {
|
|
2175
|
+
for (const source of sources) {
|
|
2176
|
+
const iconPath = resolveResourcePath$2(source.directory, source.iconPath);
|
|
2177
|
+
await assertMuonAppIconPath(iconPath);
|
|
2178
|
+
return iconPath;
|
|
2179
|
+
}
|
|
2180
|
+
};
|
|
2181
|
+
var assertMuonAppIconPath = async (iconPath) => {
|
|
2182
|
+
if (extname(iconPath).toLowerCase() !== ".png") throw new Error(`Muon app icon must be a .png file: ${iconPath}`);
|
|
2183
|
+
if (!await fileExists$5(iconPath)) throw new Error(`Muon app icon does not exist: ${iconPath}`);
|
|
2184
|
+
await createNormalizedIconPngData(await readFile(iconPath), iconPath, "Muon app icon");
|
|
2185
|
+
};
|
|
2186
|
+
var resolveResourcePath$2 = (directory, value) => isAbsolute(value) ? value : resolve(directory, value);
|
|
2187
|
+
var fileExists$5 = async (path) => {
|
|
2188
|
+
try {
|
|
2189
|
+
await access(path, constants.F_OK);
|
|
2190
|
+
return true;
|
|
2191
|
+
} catch {
|
|
2192
|
+
return false;
|
|
2193
|
+
}
|
|
2194
|
+
};
|
|
2195
|
+
//#endregion
|
|
2196
|
+
//#region src/windows-resource.ts
|
|
2197
|
+
var defaultLanguage = 1033;
|
|
2198
|
+
var defaultCodePage = 1200;
|
|
2199
|
+
var moduleDirectory$3 = typeof __dirname === "string" ? __dirname : dirname(fileURLToPath(import.meta.url));
|
|
2200
|
+
/**
|
|
2201
|
+
* Merges Windows resource options while preserving field-level fallback.
|
|
2202
|
+
*/
|
|
2203
|
+
var mergeMuonWindowsResourceOptions = (primary, fallback) => {
|
|
2204
|
+
if (primary === void 0) return fallback;
|
|
2205
|
+
if (fallback === void 0) return primary;
|
|
2206
|
+
const merged = { ...fallback };
|
|
2207
|
+
if (primary.iconPath !== void 0) merged.iconPath = primary.iconPath;
|
|
2208
|
+
if (primary.productName !== void 0) merged.productName = primary.productName;
|
|
2209
|
+
if (primary.fileDescription !== void 0) merged.fileDescription = primary.fileDescription;
|
|
2210
|
+
if (primary.companyName !== void 0) merged.companyName = primary.companyName;
|
|
2211
|
+
if (primary.version !== void 0) merged.version = primary.version;
|
|
2212
|
+
if (primary.copyright !== void 0) merged.copyright = primary.copyright;
|
|
2213
|
+
if (primary.language !== void 0) merged.language = primary.language;
|
|
2214
|
+
if (primary.codePage !== void 0) merged.codePage = primary.codePage;
|
|
2215
|
+
return merged;
|
|
2216
|
+
};
|
|
2217
|
+
/**
|
|
2218
|
+
* Resolves Windows resource metadata from options, config files, and package metadata.
|
|
2219
|
+
*/
|
|
2220
|
+
var resolveMuonWindowsResource = async (input) => {
|
|
2221
|
+
const projectJson = await readProjectJson$1(input.root);
|
|
2222
|
+
const windowsSources = [
|
|
2223
|
+
createOptionsSource$1(input.options, input.root, "windowsResource"),
|
|
2224
|
+
readConfigWindowsResourceSource(input.muonConfig, input.muonConfigDirectory, "muon.json"),
|
|
2225
|
+
readConfigWindowsResourceSource(projectJson, input.root, "project.json")
|
|
2226
|
+
].filter((source) => source !== void 0);
|
|
2227
|
+
const sources = [
|
|
2228
|
+
...windowsSources,
|
|
2229
|
+
createProjectSource(projectJson, input.root),
|
|
2230
|
+
createPackageSource(input.packageJson, input.root)
|
|
2231
|
+
].filter((source) => source !== void 0);
|
|
2232
|
+
const appIconSources = [
|
|
2233
|
+
createAppIconOptionsSource(input.appIconPath, input.root),
|
|
2234
|
+
readConfigAppIconSource(input.muonConfig, input.muonConfigDirectory, "muon.json"),
|
|
2235
|
+
readProjectAppIconSource(projectJson, input.root)
|
|
2236
|
+
].filter((source) => source !== void 0);
|
|
2237
|
+
const productName = resolveStringField$1(sources, "productName", input.defaults.productName);
|
|
2238
|
+
const fileDescription = resolveStringField$1(sources, "fileDescription", input.defaults.fileDescription);
|
|
2239
|
+
const companyName = resolveStringField$1(sources, "companyName", input.defaults.companyName);
|
|
2240
|
+
const version = resolveStringField$1(sources, "version", input.defaults.version);
|
|
2241
|
+
const copyright = resolveOptionalStringField(sources, "copyright", input.defaults.copyright);
|
|
2242
|
+
const language = resolveNumericField(sources, "language", defaultLanguage);
|
|
2243
|
+
const codePage = resolveNumericField(sources, "codePage", defaultCodePage);
|
|
2244
|
+
const fixedVersion = normalizeWindowsVersion(version);
|
|
2245
|
+
return {
|
|
2246
|
+
iconPath: await resolveWindowsIconPath(windowsSources, appIconSources, await resolveDefaultWindowsIcon(input.packageDirectory)),
|
|
2247
|
+
productName,
|
|
2248
|
+
fileDescription,
|
|
2249
|
+
companyName,
|
|
2250
|
+
version,
|
|
2251
|
+
fixedVersion,
|
|
2252
|
+
copyright,
|
|
2253
|
+
language,
|
|
2254
|
+
codePage
|
|
2255
|
+
};
|
|
2256
|
+
};
|
|
2257
|
+
/**
|
|
2258
|
+
* Removes build-only Windows resource settings from runtime config.
|
|
2259
|
+
*/
|
|
2260
|
+
var stripBuildOnlyWindowsResourceConfig = (sourceConfig) => {
|
|
2261
|
+
const sourceWindows = sourceConfig.windows;
|
|
2262
|
+
if (!isJsonObject$4(sourceWindows)) return sourceConfig;
|
|
2263
|
+
const windowsConfig = {};
|
|
2264
|
+
for (const [key, value] of Object.entries(sourceWindows)) if (key !== "resource") windowsConfig[key] = value;
|
|
2265
|
+
const output = {};
|
|
2266
|
+
for (const [key, value] of Object.entries(sourceConfig)) if (key !== "windows") output[key] = value;
|
|
2267
|
+
if (Object.keys(windowsConfig).length > 0) output.windows = windowsConfig;
|
|
2268
|
+
return output;
|
|
2269
|
+
};
|
|
2270
|
+
/**
|
|
2271
|
+
* Returns true when the file has a Windows PE header.
|
|
2272
|
+
*/
|
|
2273
|
+
var isWindowsPeExecutable = async (path) => {
|
|
2274
|
+
const content = await readFile(path);
|
|
2275
|
+
if (content.length < 64 || content.toString("ascii", 0, 2) !== "MZ") return false;
|
|
2276
|
+
const peOffset = content.readUInt32LE(60);
|
|
2277
|
+
return peOffset + 4 <= content.length && content.toString("ascii", peOffset, peOffset + 4) === "PE\0\0";
|
|
2278
|
+
};
|
|
2279
|
+
/**
|
|
2280
|
+
* Applies resolved resource metadata to a Windows PE executable in place.
|
|
2281
|
+
*/
|
|
2282
|
+
var updateWindowsPeResources = async (input) => {
|
|
2283
|
+
return await updateWindowsPeResourceFile(input, createEngraverUpdate);
|
|
2284
|
+
};
|
|
2285
|
+
/**
|
|
2286
|
+
* Applies only resolved icon metadata to a Windows PE executable in place.
|
|
2287
|
+
*/
|
|
2288
|
+
var updateWindowsPeIconResource = async (input) => {
|
|
2289
|
+
return await updateWindowsPeResourceFile(input, createEngraverIconUpdate);
|
|
2290
|
+
};
|
|
2291
|
+
var updateWindowsPeResourceFile = async (input, createUpdate) => {
|
|
2292
|
+
if (!await isWindowsPeExecutable(input.executablePath)) return false;
|
|
2293
|
+
const mode = (await stat(input.executablePath)).mode & 511;
|
|
2294
|
+
const tempDirectory = await mkdtemp(join(tmpdir(), "muon-windows-resource-"));
|
|
2295
|
+
const updatesJsonPath = join(tempDirectory, "updates.json");
|
|
2296
|
+
const outputPath = join(tempDirectory, "output.exe");
|
|
2297
|
+
const iconPath = join(tempDirectory, "icon.ico");
|
|
2298
|
+
try {
|
|
2299
|
+
await createWindowsIconFromPngFile(input.resource.iconPath, iconPath);
|
|
2300
|
+
await writeFile(updatesJsonPath, `${JSON.stringify(createUpdate(input.resource, iconPath), null, 2)}\n`);
|
|
2301
|
+
await runMuonPrepareResourceUpdate({
|
|
2302
|
+
inputPath: input.executablePath,
|
|
2303
|
+
updatesJsonPath,
|
|
2304
|
+
outputPath,
|
|
2305
|
+
quiet: true,
|
|
2306
|
+
prepareExecutablePath: void 0,
|
|
2307
|
+
environment: input.environment,
|
|
2308
|
+
cwd: input.cwd
|
|
2309
|
+
});
|
|
2310
|
+
await copyFile(outputPath, input.executablePath);
|
|
2311
|
+
await chmod(input.executablePath, mode === 0 ? 493 : mode);
|
|
2312
|
+
return true;
|
|
2313
|
+
} finally {
|
|
2314
|
+
await rm(tempDirectory, {
|
|
2315
|
+
recursive: true,
|
|
2316
|
+
force: true
|
|
2317
|
+
});
|
|
2318
|
+
}
|
|
2319
|
+
};
|
|
2320
|
+
var createEngraverUpdate = (resource, iconPath) => {
|
|
2321
|
+
const strings = {
|
|
2322
|
+
CompanyName: resource.companyName,
|
|
2323
|
+
FileDescription: resource.fileDescription,
|
|
2324
|
+
FileVersion: resource.version,
|
|
2325
|
+
ProductName: resource.productName,
|
|
2326
|
+
ProductVersion: resource.version
|
|
2327
|
+
};
|
|
2328
|
+
if (resource.copyright !== void 0) strings.LegalCopyright = resource.copyright;
|
|
2329
|
+
return {
|
|
2330
|
+
version: {
|
|
2331
|
+
language: resource.language,
|
|
2332
|
+
codePage: resource.codePage,
|
|
2333
|
+
fixed: {
|
|
2334
|
+
fileVersion: resource.fixedVersion,
|
|
2335
|
+
productVersion: resource.fixedVersion,
|
|
2336
|
+
fileOS: "windows32",
|
|
2337
|
+
fileType: "app"
|
|
2338
|
+
},
|
|
2339
|
+
strings
|
|
2340
|
+
},
|
|
2341
|
+
icons: createEngraverIcons(resource, iconPath)
|
|
2342
|
+
};
|
|
2343
|
+
};
|
|
2344
|
+
var createEngraverIconUpdate = (resource, iconPath) => ({ icons: createEngraverIcons(resource, iconPath) });
|
|
2345
|
+
var createEngraverIcons = (resource, iconPath) => [{
|
|
2346
|
+
id: 1,
|
|
2347
|
+
language: resource.language,
|
|
2348
|
+
path: iconPath
|
|
2349
|
+
}];
|
|
2350
|
+
var readProjectJson$1 = async (root) => {
|
|
2351
|
+
const projectJsonPath = join(root, "project.json");
|
|
2352
|
+
if (!await fileExists$4(projectJsonPath)) return {};
|
|
2353
|
+
return await readJsonObjectFile$3(projectJsonPath, "project.json");
|
|
2354
|
+
};
|
|
2355
|
+
var readJsonObjectFile$3 = async (filePath, label) => {
|
|
2356
|
+
const parsed = (0, import_dist.parse)(await readFile(filePath, "utf8"));
|
|
2357
|
+
if (!isJsonObject$4(parsed)) throw new Error(`${label} must contain a JSON object: ${filePath}`);
|
|
2358
|
+
return parsed;
|
|
2359
|
+
};
|
|
2360
|
+
var createOptionsSource$1 = (options, directory, label) => {
|
|
2361
|
+
if (options === void 0) return;
|
|
2362
|
+
return {
|
|
2363
|
+
options: validateWindowsResourceOptions(options, label),
|
|
2364
|
+
directory
|
|
2365
|
+
};
|
|
2366
|
+
};
|
|
2367
|
+
var readConfigWindowsResourceSource = (config, directory, label) => {
|
|
2368
|
+
const windows = config.windows;
|
|
2369
|
+
if (windows === void 0) return;
|
|
2370
|
+
if (!isJsonObject$4(windows)) throw new Error(`${label} windows must be an object when present.`);
|
|
2371
|
+
const resource = windows.resource;
|
|
2372
|
+
if (resource === void 0) return;
|
|
2373
|
+
return createOptionsSource$1(validateWindowsResourceOptions(resource, `${label} windows.resource`), directory, `${label} windows.resource`);
|
|
2374
|
+
};
|
|
2375
|
+
var createProjectSource = (projectJson, directory) => {
|
|
2376
|
+
const topLevel = {};
|
|
2377
|
+
copyStringField(projectJson, "productName", topLevel, "productName");
|
|
2378
|
+
copyStringField(projectJson, "name", topLevel, "productName");
|
|
2379
|
+
copyStringField(projectJson, "fileDescription", topLevel, "fileDescription");
|
|
2380
|
+
copyStringField(projectJson, "description", topLevel, "fileDescription");
|
|
2381
|
+
copyStringField(projectJson, "companyName", topLevel, "companyName");
|
|
2382
|
+
copyStringField(projectJson, "version", topLevel, "version");
|
|
2383
|
+
copyStringField(projectJson, "copyright", topLevel, "copyright");
|
|
2384
|
+
const author = stringifyAuthor(projectJson.author);
|
|
2385
|
+
if (author !== void 0 && topLevel.companyName === void 0) topLevel.companyName = author;
|
|
2386
|
+
const options = mergeMuonWindowsResourceOptions(readConfigWindowsResourceSource(projectJson, directory, "project.json")?.options, topLevel);
|
|
2387
|
+
if (options === void 0 || Object.keys(options).length === 0) return;
|
|
2388
|
+
return {
|
|
2389
|
+
options,
|
|
2390
|
+
directory
|
|
2391
|
+
};
|
|
2392
|
+
};
|
|
2393
|
+
var createPackageSource = (packageJson, directory) => {
|
|
2394
|
+
const options = {};
|
|
2395
|
+
copyStringField(packageJson, "name", options, "productName");
|
|
2396
|
+
copyStringField(packageJson, "description", options, "fileDescription");
|
|
2397
|
+
copyStringField(packageJson, "version", options, "version");
|
|
2398
|
+
copyStringField(packageJson, "copyright", options, "copyright");
|
|
2399
|
+
const author = stringifyAuthor(packageJson.author);
|
|
2400
|
+
if (author !== void 0) options.companyName = author;
|
|
2401
|
+
return Object.keys(options).length === 0 ? void 0 : {
|
|
2402
|
+
options,
|
|
2403
|
+
directory
|
|
2404
|
+
};
|
|
2405
|
+
};
|
|
2406
|
+
var copyStringField = (source, sourceKey, target, targetKey) => {
|
|
2407
|
+
const value = source[sourceKey];
|
|
2408
|
+
if (typeof value === "string" && value.trim() !== "") target[targetKey] = value.trim();
|
|
2409
|
+
};
|
|
2410
|
+
var validateWindowsResourceOptions = (value, label) => {
|
|
2411
|
+
if (!isJsonObject$4(value)) throw new Error(`${label} must be an object.`);
|
|
2412
|
+
const output = {};
|
|
2413
|
+
copyOptionalStringResourceField(value, "iconPath", output);
|
|
2414
|
+
copyOptionalStringResourceField(value, "productName", output);
|
|
2415
|
+
copyOptionalStringResourceField(value, "fileDescription", output);
|
|
2416
|
+
copyOptionalStringResourceField(value, "companyName", output);
|
|
2417
|
+
copyOptionalStringResourceField(value, "version", output);
|
|
2418
|
+
copyOptionalStringResourceField(value, "copyright", output);
|
|
2419
|
+
copyOptionalNumberResourceField(value, "language", output, label);
|
|
2420
|
+
copyOptionalNumberResourceField(value, "codePage", output, label);
|
|
2421
|
+
return output;
|
|
2422
|
+
};
|
|
2423
|
+
var copyOptionalStringResourceField = (source, key, target) => {
|
|
2424
|
+
const value = source[key];
|
|
2425
|
+
if (value === void 0) return;
|
|
2426
|
+
if (typeof value !== "string") throw new Error(`${key} must be a string when present.`);
|
|
2427
|
+
if (value.trim() !== "") target[key] = value.trim();
|
|
2428
|
+
};
|
|
2429
|
+
var copyOptionalNumberResourceField = (source, key, target, label) => {
|
|
2430
|
+
const value = source[key];
|
|
2431
|
+
if (value === void 0) return;
|
|
2432
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value > 65535) throw new Error(`${label} ${key} must be an integer from 0 to 65535.`);
|
|
2433
|
+
target[key] = value;
|
|
2434
|
+
};
|
|
2435
|
+
var resolveStringField$1 = (sources, field, fallback) => {
|
|
2436
|
+
return resolveOptionalStringField(sources, field, fallback) ?? fallback;
|
|
2437
|
+
};
|
|
2438
|
+
var resolveOptionalStringField = (sources, field, fallback) => {
|
|
2439
|
+
for (const source of sources) {
|
|
2440
|
+
const value = source.options[field];
|
|
2441
|
+
if (value !== void 0 && value.trim() !== "") return value.trim();
|
|
2442
|
+
}
|
|
2443
|
+
return fallback;
|
|
2444
|
+
};
|
|
2445
|
+
var resolveNumericField = (sources, field, fallback) => {
|
|
2446
|
+
for (const source of sources) {
|
|
2447
|
+
const value = source.options[field];
|
|
2448
|
+
if (value !== void 0) return value;
|
|
2449
|
+
}
|
|
2450
|
+
return fallback;
|
|
2451
|
+
};
|
|
2452
|
+
var resolveWindowsIconPath = async (windowsSources, appIconSources, defaultIconPath) => {
|
|
2453
|
+
for (const source of windowsSources) {
|
|
2454
|
+
const value = source.options.iconPath;
|
|
2455
|
+
if (value !== void 0 && value.trim() !== "") {
|
|
2456
|
+
const iconPath = resolveResourcePath$1(source.directory, value);
|
|
2457
|
+
await assertIconPath$1(iconPath);
|
|
2458
|
+
return iconPath;
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
const appIconPath = await resolveMuonAppIconPath(appIconSources);
|
|
2462
|
+
if (appIconPath !== void 0) return appIconPath;
|
|
2463
|
+
await assertIconPath$1(defaultIconPath);
|
|
2464
|
+
return defaultIconPath;
|
|
2465
|
+
};
|
|
2466
|
+
var assertIconPath$1 = async (iconPath) => {
|
|
2467
|
+
if (extname(iconPath).toLowerCase() !== ".png") throw new Error(`Windows resource icon must be a .png file: ${iconPath}`);
|
|
2468
|
+
if (!await fileExists$4(iconPath)) throw new Error(`Windows resource icon does not exist: ${iconPath}`);
|
|
2469
|
+
await createNormalizedMuonIconPngData(await readFile(iconPath), iconPath);
|
|
2470
|
+
};
|
|
2471
|
+
var resolveDefaultWindowsIcon = async (packageDirectory) => {
|
|
2472
|
+
const sourceIconPath = join(moduleDirectory$3, "..", "..", "images", "muon-256.png");
|
|
2473
|
+
const candidates = [
|
|
2474
|
+
join(resolve(packageDirectory), "native", "muon-256.png"),
|
|
2475
|
+
join(moduleDirectory$3, "native", "muon-256.png"),
|
|
2476
|
+
join(moduleDirectory$3, "..", "dist", "native", "muon-256.png"),
|
|
2477
|
+
sourceIconPath
|
|
2478
|
+
];
|
|
2479
|
+
for (const candidate of candidates) if (await fileExists$4(candidate)) return candidate;
|
|
2480
|
+
return sourceIconPath;
|
|
2481
|
+
};
|
|
2482
|
+
var resolveResourcePath$1 = (directory, path) => {
|
|
2483
|
+
return isAbsolute(path) ? path : resolve(directory, path);
|
|
2484
|
+
};
|
|
2485
|
+
var normalizeWindowsVersion = (version) => {
|
|
2486
|
+
const normalized = version.trim();
|
|
2487
|
+
const match = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?(?:$|[-+])/.exec(normalized);
|
|
2488
|
+
if (match === null) throw new Error(`Windows resource version must start with numeric parts: ${version}`);
|
|
2489
|
+
const values = [
|
|
2490
|
+
match[1],
|
|
2491
|
+
match[2],
|
|
2492
|
+
match[3],
|
|
2493
|
+
match[4]
|
|
2494
|
+
].map((value) => value === void 0 ? 0 : Number.parseInt(value, 10));
|
|
2495
|
+
for (const value of values) if (!Number.isInteger(value) || value < 0 || value > 65535) throw new Error(`Windows resource version part must be from 0 to 65535: ${version}`);
|
|
2496
|
+
return values.join(".");
|
|
2497
|
+
};
|
|
2498
|
+
var stringifyAuthor = (value) => {
|
|
2499
|
+
if (typeof value === "string" && value.trim() !== "") return value.trim();
|
|
2500
|
+
if (!isJsonObject$4(value) || typeof value.name !== "string") return;
|
|
2501
|
+
const name = value.name.trim();
|
|
2502
|
+
if (name === "") return;
|
|
2503
|
+
return value.email === void 0 || typeof value.email !== "string" ? name : `${name} <${value.email}>`;
|
|
2504
|
+
};
|
|
2505
|
+
var fileExists$4 = async (path) => {
|
|
2506
|
+
try {
|
|
2507
|
+
await access(path, constants.F_OK);
|
|
2508
|
+
return true;
|
|
2509
|
+
} catch {
|
|
2510
|
+
return false;
|
|
2511
|
+
}
|
|
2512
|
+
};
|
|
2513
|
+
var isJsonObject$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2514
|
+
//#endregion
|
|
2515
|
+
//#region src/linux-desktop.ts
|
|
2516
|
+
var defaultIconFileName = "muon-desktop-icon.png";
|
|
2517
|
+
var defaultDesktopConfigFileName = "muon-desktop.json";
|
|
2518
|
+
var moduleDirectory$2 = typeof __dirname === "string" ? __dirname : dirname(fileURLToPath(import.meta.url));
|
|
2519
|
+
/**
|
|
2520
|
+
* Resolves Linux desktop integration metadata from options, config files, and
|
|
2521
|
+
* package metadata.
|
|
2522
|
+
*/
|
|
2523
|
+
var resolveMuonLinuxDesktop = async (input) => {
|
|
2524
|
+
const projectJson = await readProjectJson(input.root);
|
|
2525
|
+
const sources = [createOptionsSource(input.options, input.root, "linuxDesktop"), readConfigLinuxDesktopSource(input.muonConfig, input.muonConfigDirectory, "muon.json")].filter((source) => source !== void 0);
|
|
2526
|
+
const appIconSources = [
|
|
2527
|
+
createAppIconOptionsSource(input.appIconPath, input.root),
|
|
2528
|
+
readConfigAppIconSource(input.muonConfig, input.muonConfigDirectory, "muon.json"),
|
|
2529
|
+
readProjectAppIconSource(projectJson, input.root)
|
|
2530
|
+
].filter((source) => source !== void 0);
|
|
2531
|
+
return {
|
|
2532
|
+
desktopId: sanitizeDesktopId(resolveStringField(sources, "desktopId", input.defaults.desktopId)),
|
|
2533
|
+
name: resolveStringField(sources, "name", input.defaults.name),
|
|
2534
|
+
comment: resolveStringField(sources, "comment", input.defaults.comment),
|
|
2535
|
+
categories: resolveCategories(sources, input.defaults.categories),
|
|
2536
|
+
startupNotify: resolveBooleanField(sources, "startupNotify", input.defaults.startupNotify),
|
|
2537
|
+
iconPath: await resolveLinuxIconPath(sources, appIconSources, await resolveDefaultLinuxDesktopIcon(input.packageDirectory)),
|
|
2538
|
+
iconFileName: defaultIconFileName,
|
|
2539
|
+
configFileName: defaultDesktopConfigFileName
|
|
2540
|
+
};
|
|
2541
|
+
};
|
|
2542
|
+
/**
|
|
2543
|
+
* Removes build-only Linux desktop settings from runtime config.
|
|
2544
|
+
*/
|
|
2545
|
+
var stripBuildOnlyLinuxDesktopConfig = (sourceConfig) => {
|
|
2546
|
+
const sourceLinux = sourceConfig.linux;
|
|
2547
|
+
if (!isJsonObject$3(sourceLinux)) return sourceConfig;
|
|
2548
|
+
const linuxConfig = {};
|
|
2549
|
+
for (const [key, value] of Object.entries(sourceLinux)) if (key !== "desktop") linuxConfig[key] = value;
|
|
2550
|
+
const output = {};
|
|
2551
|
+
for (const [key, value] of Object.entries(sourceConfig)) if (key !== "linux") output[key] = value;
|
|
2552
|
+
if (Object.keys(linuxConfig).length > 0) output.linux = linuxConfig;
|
|
2553
|
+
return output;
|
|
2554
|
+
};
|
|
2555
|
+
/**
|
|
2556
|
+
* Writes normalized Linux desktop sidecar files into a target distribution.
|
|
2557
|
+
*/
|
|
2558
|
+
var writeLinuxDesktopDistributionFiles = async (outputPath, desktop) => {
|
|
2559
|
+
const iconData = await createNormalizedIconPngData(await readFile(desktop.iconPath), desktop.iconPath, "Linux desktop icon");
|
|
2560
|
+
await writeFile(join(outputPath, desktop.iconFileName), iconData);
|
|
2561
|
+
await writeFile(join(outputPath, desktop.configFileName), `${JSON.stringify({
|
|
2562
|
+
desktopId: desktop.desktopId,
|
|
2563
|
+
name: desktop.name,
|
|
2564
|
+
comment: desktop.comment,
|
|
2565
|
+
categories: desktop.categories,
|
|
2566
|
+
startupNotify: desktop.startupNotify,
|
|
2567
|
+
iconFileName: desktop.iconFileName
|
|
2568
|
+
}, void 0, 2)}\n`);
|
|
2569
|
+
};
|
|
2570
|
+
var readJsonObjectFile$2 = async (filePath, label) => {
|
|
2571
|
+
const parsed = (0, import_dist.parse)(await readFile(filePath, "utf8"));
|
|
2572
|
+
if (!isJsonObject$3(parsed)) throw new Error(`${label} must contain a JSON object: ${filePath}`);
|
|
2573
|
+
return parsed;
|
|
2574
|
+
};
|
|
2575
|
+
var readProjectJson = async (root) => {
|
|
2576
|
+
const projectJsonPath = join(root, "project.json");
|
|
2577
|
+
if (!await fileExists$3(projectJsonPath)) return {};
|
|
2578
|
+
return await readJsonObjectFile$2(projectJsonPath, "project.json");
|
|
2579
|
+
};
|
|
2580
|
+
var createOptionsSource = (options, directory, label) => {
|
|
2581
|
+
if (options === void 0) return;
|
|
2582
|
+
return {
|
|
2583
|
+
options: validateLinuxDesktopOptions(options, label),
|
|
2584
|
+
directory
|
|
2585
|
+
};
|
|
2586
|
+
};
|
|
2587
|
+
var readConfigLinuxDesktopSource = (config, directory, label) => {
|
|
2588
|
+
const linux = config.linux;
|
|
2589
|
+
if (linux === void 0) return;
|
|
2590
|
+
if (!isJsonObject$3(linux)) throw new Error(`${label} linux must be an object when present.`);
|
|
2591
|
+
const desktop = linux.desktop;
|
|
2592
|
+
if (desktop === void 0) return;
|
|
2593
|
+
return createOptionsSource(validateLinuxDesktopOptions(desktop, `${label} linux.desktop`), directory, `${label} linux.desktop`);
|
|
2594
|
+
};
|
|
2595
|
+
var validateLinuxDesktopOptions = (value, label) => {
|
|
2596
|
+
if (!isJsonObject$3(value)) throw new Error(`${label} must be an object.`);
|
|
2597
|
+
const output = {};
|
|
2598
|
+
copyOptionalStringDesktopField(value, "desktopId", output);
|
|
2599
|
+
copyOptionalStringDesktopField(value, "name", output);
|
|
2600
|
+
copyOptionalStringDesktopField(value, "comment", output);
|
|
2601
|
+
copyOptionalStringDesktopField(value, "iconPath", output);
|
|
2602
|
+
copyOptionalCategoriesField(value, output, label);
|
|
2603
|
+
copyOptionalBooleanDesktopField(value, "startupNotify", output, label);
|
|
2604
|
+
return output;
|
|
2605
|
+
};
|
|
2606
|
+
var copyOptionalStringDesktopField = (source, key, target) => {
|
|
2607
|
+
const value = source[key];
|
|
2608
|
+
if (value === void 0) return;
|
|
2609
|
+
if (typeof value !== "string") throw new Error(`${key} must be a string when present.`);
|
|
2610
|
+
if (value.trim() !== "") target[key] = value.trim();
|
|
2611
|
+
};
|
|
2612
|
+
var copyOptionalCategoriesField = (source, target, label) => {
|
|
2613
|
+
const value = source.categories;
|
|
2614
|
+
if (value === void 0) return;
|
|
2615
|
+
if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string" && entry.trim() !== "" && !entry.includes(";") && !entry.includes("\n") && !entry.includes("\r"))) throw new Error(`${label} categories must be a string array without semicolons when present.`);
|
|
2616
|
+
target.categories = value.map((entry) => entry.trim());
|
|
2617
|
+
};
|
|
2618
|
+
var copyOptionalBooleanDesktopField = (source, key, target, label) => {
|
|
2619
|
+
const value = source[key];
|
|
2620
|
+
if (value === void 0) return;
|
|
2621
|
+
if (typeof value !== "boolean") throw new Error(`${label} ${key} must be a boolean when present.`);
|
|
2622
|
+
target[key] = value;
|
|
2623
|
+
};
|
|
2624
|
+
var resolveStringField = (sources, field, fallback) => {
|
|
2625
|
+
for (const source of sources) {
|
|
2626
|
+
const value = source.options[field];
|
|
2627
|
+
if (value !== void 0 && value.trim() !== "") return value.trim();
|
|
2628
|
+
}
|
|
2629
|
+
return fallback;
|
|
2630
|
+
};
|
|
2631
|
+
var resolveCategories = (sources, fallback) => {
|
|
2632
|
+
for (const source of sources) if (source.options.categories !== void 0) return source.options.categories;
|
|
2633
|
+
return fallback;
|
|
2634
|
+
};
|
|
2635
|
+
var resolveBooleanField = (sources, field, fallback) => {
|
|
2636
|
+
for (const source of sources) {
|
|
2637
|
+
const value = source.options[field];
|
|
2638
|
+
if (value !== void 0) return value;
|
|
2639
|
+
}
|
|
2640
|
+
return fallback;
|
|
2641
|
+
};
|
|
2642
|
+
var resolveLinuxIconPath = async (sources, appIconSources, defaultIconPath) => {
|
|
2643
|
+
for (const source of sources) {
|
|
2644
|
+
const value = source.options.iconPath;
|
|
2645
|
+
if (value !== void 0 && value.trim() !== "") {
|
|
2646
|
+
const iconPath = resolveResourcePath(source.directory, value);
|
|
2647
|
+
await assertIconPath(iconPath, true);
|
|
2648
|
+
return iconPath;
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
const appIconPath = await resolveMuonAppIconPath(appIconSources);
|
|
2652
|
+
if (appIconPath !== void 0) return appIconPath;
|
|
2653
|
+
if (defaultIconPath !== void 0 && await fileExists$3(defaultIconPath)) {
|
|
2654
|
+
await assertIconPath(defaultIconPath, false);
|
|
2655
|
+
return defaultIconPath;
|
|
2656
|
+
}
|
|
2657
|
+
throw new Error("Linux desktop icon does not exist.");
|
|
2658
|
+
};
|
|
2659
|
+
var assertIconPath = async (iconPath, required) => {
|
|
2660
|
+
if (extname(iconPath).toLowerCase() !== ".png") throw new Error(`Linux desktop icon must be a .png file: ${iconPath}`);
|
|
2661
|
+
if (required && !await fileExists$3(iconPath)) throw new Error(`Linux desktop icon does not exist: ${iconPath}`);
|
|
2662
|
+
if (await fileExists$3(iconPath)) await createNormalizedIconPngData(await readFile(iconPath), iconPath, "Linux desktop icon");
|
|
2663
|
+
};
|
|
2664
|
+
var resolveDefaultLinuxDesktopIcon = async (packageDirectory) => {
|
|
2665
|
+
const candidates = [
|
|
2666
|
+
join(resolve(packageDirectory), "native", "muon-256.png"),
|
|
2667
|
+
join(moduleDirectory$2, "native", "muon-256.png"),
|
|
2668
|
+
join(moduleDirectory$2, "..", "dist", "native", "muon-256.png"),
|
|
2669
|
+
join(moduleDirectory$2, "..", "..", "images", "muon-256.png")
|
|
2670
|
+
];
|
|
2671
|
+
for (const candidate of candidates) if (await fileExists$3(candidate)) return candidate;
|
|
2672
|
+
};
|
|
2673
|
+
var sanitizeDesktopId = (value) => {
|
|
2674
|
+
const sanitized = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[.-]+/g, "").replace(/[.-]+$/g, "");
|
|
2675
|
+
return sanitized.length > 0 ? sanitized : "muon-app";
|
|
2676
|
+
};
|
|
2677
|
+
var resolveResourcePath = (directory, path) => {
|
|
2678
|
+
return isAbsolute(path) ? path : resolve(directory, path);
|
|
2679
|
+
};
|
|
2680
|
+
var fileExists$3 = async (path) => {
|
|
2681
|
+
try {
|
|
2682
|
+
await access(path, constants.F_OK);
|
|
2683
|
+
return true;
|
|
2684
|
+
} catch {
|
|
2685
|
+
return false;
|
|
2686
|
+
}
|
|
2687
|
+
};
|
|
2688
|
+
var isJsonObject$3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1787
2689
|
//#endregion
|
|
1788
2690
|
//#region src/build.ts
|
|
1789
2691
|
var AdmZip = __resolveDefaultExport(__screwUpDefaultImportModule0, false);
|
|
@@ -1806,7 +2708,7 @@ function __resolveDefaultExport(module, isESM) {
|
|
|
1806
2708
|
}
|
|
1807
2709
|
return hasDefault ? resolvedDefault ?? module : module;
|
|
1808
2710
|
}
|
|
1809
|
-
var defaultConfigFileNames = [
|
|
2711
|
+
var defaultConfigFileNames$1 = [
|
|
1810
2712
|
"muon.json5",
|
|
1811
2713
|
"muon.jsonc",
|
|
1812
2714
|
"muon.json"
|
|
@@ -1843,18 +2745,58 @@ var buildMuonApp = async (options = {}) => {
|
|
|
1843
2745
|
const appName = resolveAppName(packageJson, options.appName);
|
|
1844
2746
|
const appId = resolveAppId(packageJson, options.appId);
|
|
1845
2747
|
const buildConfig = await readBuildConfig(root, options.configPath);
|
|
1846
|
-
const
|
|
2748
|
+
const sourceConfig = applyRuntimePluginConfig(buildConfig.config, options.runtimePluginConfig ?? { mode: "simple" });
|
|
2749
|
+
assertNoUserInitialTitleBarIcon(sourceConfig);
|
|
2750
|
+
const resolvedBuildConfig = {
|
|
2751
|
+
...buildConfig,
|
|
2752
|
+
config: sourceConfig
|
|
2753
|
+
};
|
|
2754
|
+
const assetInput = resolveAssetInput(root, options.assetSourcePath, options.assetPrefix, resolvedBuildConfig);
|
|
2755
|
+
const windowsResource = await resolveMuonWindowsResource({
|
|
2756
|
+
root,
|
|
2757
|
+
packageDirectory,
|
|
2758
|
+
packageJson,
|
|
2759
|
+
muonConfig: sourceConfig,
|
|
2760
|
+
muonConfigDirectory: buildConfig.directory,
|
|
2761
|
+
options: options.windowsResource,
|
|
2762
|
+
appIconPath: options.iconPath,
|
|
2763
|
+
defaults: {
|
|
2764
|
+
productName: appName,
|
|
2765
|
+
fileDescription: appName,
|
|
2766
|
+
companyName: "Unknown",
|
|
2767
|
+
version: "0.0.0",
|
|
2768
|
+
copyright: void 0
|
|
2769
|
+
}
|
|
2770
|
+
});
|
|
2771
|
+
const linuxDesktop = await resolveMuonLinuxDesktop({
|
|
2772
|
+
root,
|
|
2773
|
+
packageDirectory,
|
|
2774
|
+
muonConfig: sourceConfig,
|
|
2775
|
+
muonConfigDirectory: buildConfig.directory,
|
|
2776
|
+
options: options.linuxDesktop,
|
|
2777
|
+
appIconPath: options.iconPath,
|
|
2778
|
+
defaults: {
|
|
2779
|
+
desktopId: appId,
|
|
2780
|
+
name: resolveLinuxDesktopDefaultName(packageJson, appName),
|
|
2781
|
+
comment: resolvePackageDescription(packageJson),
|
|
2782
|
+
categories: ["Utility"],
|
|
2783
|
+
startupNotify: true
|
|
2784
|
+
}
|
|
2785
|
+
});
|
|
1847
2786
|
const salt = Buffer.from(options.assetSalt ?? randomBytes(assetSaltByteLength));
|
|
1848
2787
|
const results = [];
|
|
1849
2788
|
for (const target of targets) {
|
|
1850
2789
|
const result = await buildMuonTarget({
|
|
1851
2790
|
packageDirectory,
|
|
2791
|
+
root,
|
|
1852
2792
|
outputRoot,
|
|
1853
2793
|
appName,
|
|
1854
2794
|
appId,
|
|
1855
2795
|
target,
|
|
1856
2796
|
assetInput,
|
|
1857
|
-
sourceConfig
|
|
2797
|
+
sourceConfig,
|
|
2798
|
+
windowsResource,
|
|
2799
|
+
linuxDesktop,
|
|
1858
2800
|
salt
|
|
1859
2801
|
});
|
|
1860
2802
|
results.push(result);
|
|
@@ -1889,8 +2831,8 @@ var normalizeZipPrefix = (prefix) => {
|
|
|
1889
2831
|
};
|
|
1890
2832
|
var readPackageJson = async (root) => {
|
|
1891
2833
|
const packageJsonPath = join(root, "package.json");
|
|
1892
|
-
if (!await fileExists$
|
|
1893
|
-
return await readJsonObjectFile(packageJsonPath, "package.json");
|
|
2834
|
+
if (!await fileExists$2(packageJsonPath)) return {};
|
|
2835
|
+
return await readJsonObjectFile$1(packageJsonPath, "package.json");
|
|
1894
2836
|
};
|
|
1895
2837
|
var resolvePackageName = (packageJson) => {
|
|
1896
2838
|
return typeof packageJson.name === "string" ? packageJson.name : defaultAppName;
|
|
@@ -1912,6 +2854,8 @@ var sanitizeAppId = (value) => {
|
|
|
1912
2854
|
const sanitized = (value.startsWith("@") ? value.slice(1) : value).trim().toLowerCase().replace("/", ".").replace(/[^a-z0-9._-]+/g, ".").replace(/^[.]+/g, "").replace(/[.]+$/g, "");
|
|
1913
2855
|
return sanitized.length > 0 ? sanitized : defaultAppId;
|
|
1914
2856
|
};
|
|
2857
|
+
var resolveLinuxDesktopDefaultName = (packageJson, appName) => typeof packageJson.name === "string" && packageJson.name.trim() !== "" ? packageJson.name.trim() : appName;
|
|
2858
|
+
var resolvePackageDescription = (packageJson) => typeof packageJson.description === "string" ? packageJson.description.trim() : "";
|
|
1915
2859
|
var readBuildConfig = async (root, configPath) => {
|
|
1916
2860
|
const resolvedConfigPath = await resolveConfigPath(root, configPath);
|
|
1917
2861
|
if (resolvedConfigPath === void 0) return {
|
|
@@ -1919,14 +2863,27 @@ var readBuildConfig = async (root, configPath) => {
|
|
|
1919
2863
|
directory: root
|
|
1920
2864
|
};
|
|
1921
2865
|
return {
|
|
1922
|
-
config: await readJsonObjectFile(resolvedConfigPath, "Muon config file"),
|
|
2866
|
+
config: await readJsonObjectFile$1(resolvedConfigPath, "Muon config file"),
|
|
1923
2867
|
directory: dirname(resolvedConfigPath)
|
|
1924
2868
|
};
|
|
1925
2869
|
};
|
|
2870
|
+
var applyRuntimePluginConfig = (sourceConfig, runtimePluginConfig) => {
|
|
2871
|
+
if (runtimePluginConfig === void 0) return sourceConfig;
|
|
2872
|
+
const sourcePlugin = sourceConfig.plugin;
|
|
2873
|
+
if (sourcePlugin !== void 0 && !isJsonObject$2(sourcePlugin)) throw new Error("muon.json plugin must be an object when runtime plugin config is applied.");
|
|
2874
|
+
const plugin = sourcePlugin ?? {};
|
|
2875
|
+
return {
|
|
2876
|
+
...sourceConfig,
|
|
2877
|
+
plugin: {
|
|
2878
|
+
...plugin,
|
|
2879
|
+
...runtimePluginConfig
|
|
2880
|
+
}
|
|
2881
|
+
};
|
|
2882
|
+
};
|
|
1926
2883
|
var readConfigAssetSourcePath = (sourceConfig) => {
|
|
1927
2884
|
const sourceAsset = sourceConfig.asset;
|
|
1928
2885
|
if (sourceAsset === void 0) return;
|
|
1929
|
-
if (!isJsonObject$
|
|
2886
|
+
if (!isJsonObject$2(sourceAsset)) throw new Error("muon.json asset must be an object when present.");
|
|
1930
2887
|
const sourceAssetPath = sourceAsset.sourcePath;
|
|
1931
2888
|
if (sourceAssetPath === void 0) return;
|
|
1932
2889
|
if (typeof sourceAssetPath !== "string") throw new Error("muon.json asset.sourcePath must be a string when present.");
|
|
@@ -1935,28 +2892,28 @@ var readConfigAssetSourcePath = (sourceConfig) => {
|
|
|
1935
2892
|
var resolveConfigPath = async (root, configPath) => {
|
|
1936
2893
|
if (configPath !== void 0) {
|
|
1937
2894
|
const resolvedPath = resolve(root, configPath);
|
|
1938
|
-
if (await fileExists$
|
|
2895
|
+
if (await fileExists$2(resolvedPath)) return resolvedPath;
|
|
1939
2896
|
throw new Error(`Muon config file does not exist: ${resolvedPath}`);
|
|
1940
2897
|
}
|
|
1941
|
-
for (const fileName of defaultConfigFileNames) {
|
|
2898
|
+
for (const fileName of defaultConfigFileNames$1) {
|
|
1942
2899
|
const candidatePath = join(root, fileName);
|
|
1943
|
-
if (await fileExists$
|
|
2900
|
+
if (await fileExists$2(candidatePath)) return candidatePath;
|
|
1944
2901
|
}
|
|
1945
2902
|
};
|
|
1946
|
-
var readJsonObjectFile = async (filePath, label) => {
|
|
2903
|
+
var readJsonObjectFile$1 = async (filePath, label) => {
|
|
1947
2904
|
let content;
|
|
1948
2905
|
try {
|
|
1949
2906
|
content = await readFile(filePath, "utf8");
|
|
1950
2907
|
} catch (error) {
|
|
1951
|
-
throw new Error(`${label} could not be read: ${filePath}: ${getErrorMessage$
|
|
2908
|
+
throw new Error(`${label} could not be read: ${filePath}: ${getErrorMessage$3(error)}`);
|
|
1952
2909
|
}
|
|
1953
2910
|
let parsed;
|
|
1954
2911
|
try {
|
|
1955
2912
|
parsed = (0, import_dist.parse)(content);
|
|
1956
2913
|
} catch (error) {
|
|
1957
|
-
throw new Error(`${label} could not be parsed: ${filePath}: ${getErrorMessage$
|
|
2914
|
+
throw new Error(`${label} could not be parsed: ${filePath}: ${getErrorMessage$3(error)}`);
|
|
1958
2915
|
}
|
|
1959
|
-
if (!isJsonObject$
|
|
2916
|
+
if (!isJsonObject$2(parsed)) throw new Error(`${label} must contain a JSON object: ${filePath}`);
|
|
1960
2917
|
return parsed;
|
|
1961
2918
|
};
|
|
1962
2919
|
var buildMuonTarget = async (input) => {
|
|
@@ -1966,6 +2923,8 @@ var buildMuonTarget = async (input) => {
|
|
|
1966
2923
|
const outputPath = join(input.outputRoot, descriptor.distributionDirectoryName);
|
|
1967
2924
|
const launcherPath = join(outputPath, getLauncherFileName(input.appName, descriptor));
|
|
1968
2925
|
const assetZipPath = join(outputPath, "assets.zip");
|
|
2926
|
+
const runtimeAppId = getMuonTargetRuntimeAppId(input.appId, input.target);
|
|
2927
|
+
const appIconPath = descriptor.os === "windows" ? input.windowsResource.iconPath : input.linuxDesktop.iconPath;
|
|
1969
2928
|
await verifyTargetInputs({
|
|
1970
2929
|
sourceRuntimePath,
|
|
1971
2930
|
sourceBootstrapPath,
|
|
@@ -1984,8 +2943,11 @@ var buildMuonTarget = async (input) => {
|
|
|
1984
2943
|
await chmod(join(outputPath, descriptor.runtimeExecutableName), executableMode);
|
|
1985
2944
|
await copyFile(sourceBootstrapPath, launcherPath);
|
|
1986
2945
|
await chmod(launcherPath, executableMode);
|
|
1987
|
-
const asset = await writeAssetArchive(input.assetInput, assetZipPath, input.salt
|
|
1988
|
-
|
|
2946
|
+
const asset = await writeAssetArchive(input.assetInput, assetZipPath, input.salt, [{
|
|
2947
|
+
name: appIconAssetEntryName,
|
|
2948
|
+
data: await readFile(appIconPath)
|
|
2949
|
+
}]);
|
|
2950
|
+
const embeddedConfig = createEmbeddedConfig(input.sourceConfig, asset, runtimeAppId, input.linuxDesktop.desktopId, appIconAssetUrl);
|
|
1989
2951
|
await withTemporaryConfig(embeddedConfig, async (configPath) => {
|
|
1990
2952
|
await embedMuonConfigInRuntime({
|
|
1991
2953
|
runtimePath: outputPath,
|
|
@@ -1998,13 +2960,29 @@ var buildMuonTarget = async (input) => {
|
|
|
1998
2960
|
outputPath: void 0
|
|
1999
2961
|
});
|
|
2000
2962
|
});
|
|
2963
|
+
if (descriptor.os === "windows") {
|
|
2964
|
+
await updateWindowsPeIconResource({
|
|
2965
|
+
executablePath: join(outputPath, descriptor.runtimeExecutableName),
|
|
2966
|
+
resource: input.windowsResource,
|
|
2967
|
+
environment: process.env,
|
|
2968
|
+
cwd: input.root
|
|
2969
|
+
});
|
|
2970
|
+
await updateWindowsPeResources({
|
|
2971
|
+
executablePath: launcherPath,
|
|
2972
|
+
resource: input.windowsResource,
|
|
2973
|
+
environment: process.env,
|
|
2974
|
+
cwd: input.root
|
|
2975
|
+
});
|
|
2976
|
+
} else if (descriptor.os === "linux") await writeLinuxDesktopDistributionFiles(outputPath, input.linuxDesktop);
|
|
2001
2977
|
return {
|
|
2002
2978
|
target: input.target,
|
|
2003
2979
|
distributionDirectoryName: descriptor.distributionDirectoryName,
|
|
2004
2980
|
outputPath,
|
|
2005
2981
|
launcherPath,
|
|
2006
2982
|
asset,
|
|
2007
|
-
|
|
2983
|
+
runtimeAppId,
|
|
2984
|
+
embeddedConfig,
|
|
2985
|
+
...descriptor.os === "linux" ? { linuxDesktop: input.linuxDesktop } : {}
|
|
2008
2986
|
};
|
|
2009
2987
|
};
|
|
2010
2988
|
var verifyTargetInputs = async (input) => {
|
|
@@ -2025,10 +3003,10 @@ var copyRuntimeFiles = async (sourceRuntimePath, outputPath, descriptor) => {
|
|
|
2025
3003
|
}
|
|
2026
3004
|
await copyFile(join(sourceRuntimePath, muonLicenseFileName), join(outputPath, muonLicenseFileName));
|
|
2027
3005
|
};
|
|
2028
|
-
var writeAssetArchive = async (input, outputPath, salt) => {
|
|
3006
|
+
var writeAssetArchive = async (input, outputPath, salt, extraEntries) => {
|
|
2029
3007
|
const sourceStats = await statOrUndefined(input.sourcePath);
|
|
2030
3008
|
if (sourceStats === void 0) throw new Error(`Muon asset source does not exist: ${input.sourcePath}`);
|
|
2031
|
-
const archive = sourceStats.isDirectory() ? await createAssetArchiveFromDirectory(input) : sourceStats.isFile() ? await
|
|
3009
|
+
const archive = sourceStats.isDirectory() ? await createAssetArchiveFromDirectory(input, extraEntries) : sourceStats.isFile() ? await createAssetArchiveFromZipFile(input.sourcePath, extraEntries) : void 0;
|
|
2032
3010
|
if (archive === void 0) throw new Error(`Muon asset source is not a directory or file: ${input.sourcePath}`);
|
|
2033
3011
|
await writeFile(outputPath, archive);
|
|
2034
3012
|
return {
|
|
@@ -2038,10 +3016,30 @@ var writeAssetArchive = async (input, outputPath, salt) => {
|
|
|
2038
3016
|
entryCount: sourceStats.isDirectory() ? readZipEntryCount(archive, outputPath) : readZipEntryCount(archive, input.sourcePath)
|
|
2039
3017
|
};
|
|
2040
3018
|
};
|
|
2041
|
-
var createAssetArchiveFromDirectory = async (input) => {
|
|
3019
|
+
var createAssetArchiveFromDirectory = async (input, extraEntries) => {
|
|
2042
3020
|
const entries = await collectZipEntries(input.sourcePath, input.prefix);
|
|
2043
3021
|
if (entries.length === 0) throw new Error(`Muon asset source has no files: ${input.sourcePath}`);
|
|
2044
|
-
return createZipArchive(entries);
|
|
3022
|
+
return createZipArchive(appendZipEntries(entries, extraEntries));
|
|
3023
|
+
};
|
|
3024
|
+
var createAssetArchiveFromZipFile = async (sourcePath, extraEntries) => {
|
|
3025
|
+
const zip = new AdmZip(await readFile(sourcePath));
|
|
3026
|
+
for (const entry of extraEntries) {
|
|
3027
|
+
assertSafeZipEntryName(entry.name);
|
|
3028
|
+
if (zip.getEntry(entry.name) !== null) throw new Error(`Muon app icon asset entry already exists: ${entry.name}`);
|
|
3029
|
+
zip.addFile(entry.name, entry.data);
|
|
3030
|
+
}
|
|
3031
|
+
return zip.toBuffer();
|
|
3032
|
+
};
|
|
3033
|
+
var appendZipEntries = (entries, extraEntries) => {
|
|
3034
|
+
const output = [...entries];
|
|
3035
|
+
const names = new Set(entries.map((entry) => entry.name));
|
|
3036
|
+
for (const entry of extraEntries) {
|
|
3037
|
+
assertSafeZipEntryName(entry.name);
|
|
3038
|
+
if (names.has(entry.name)) throw new Error(`Muon app icon asset entry already exists: ${entry.name}`);
|
|
3039
|
+
names.add(entry.name);
|
|
3040
|
+
output.push(entry);
|
|
3041
|
+
}
|
|
3042
|
+
return output;
|
|
2045
3043
|
};
|
|
2046
3044
|
var readZipEntryCount = (archive, sourcePath) => {
|
|
2047
3045
|
const endSignature = 101010256;
|
|
@@ -2079,13 +3077,20 @@ var createZipArchive = (entries) => {
|
|
|
2079
3077
|
for (const entry of entries) zip.addFile(entry.name, entry.data);
|
|
2080
3078
|
return zip.toBuffer();
|
|
2081
3079
|
};
|
|
2082
|
-
var createEmbeddedConfig = (sourceConfig, asset, appId) => {
|
|
3080
|
+
var createEmbeddedConfig = (sourceConfig, asset, appId, desktopId, initialTitleBarIcon) => {
|
|
2083
3081
|
const sourceAsset = sourceConfig.asset;
|
|
2084
|
-
if (sourceAsset !== void 0 && !isJsonObject$
|
|
3082
|
+
if (sourceAsset !== void 0 && !isJsonObject$2(sourceAsset)) throw new Error("muon.json asset must be an object when present.");
|
|
2085
3083
|
const sourceBootstrap = sourceConfig.bootstrap;
|
|
2086
|
-
if (sourceBootstrap !== void 0 && !isJsonObject$
|
|
3084
|
+
if (sourceBootstrap !== void 0 && !isJsonObject$2(sourceBootstrap)) throw new Error("muon.json bootstrap must be an object when present.");
|
|
3085
|
+
const runtimeConfig = stripBuildOnlyAppIconConfig(stripBuildOnlyLinuxDesktopConfig(stripBuildOnlyWindowsResourceConfig(sourceConfig)));
|
|
3086
|
+
const sourceBrowser = runtimeConfig.browser;
|
|
3087
|
+
if (sourceBrowser !== void 0 && !isJsonObject$2(sourceBrowser)) throw new Error("muon.json browser must be an object when present.");
|
|
2087
3088
|
return {
|
|
2088
|
-
...
|
|
3089
|
+
...runtimeConfig,
|
|
3090
|
+
browser: {
|
|
3091
|
+
...sourceBrowser ?? {},
|
|
3092
|
+
initialTitleBarIcon
|
|
3093
|
+
},
|
|
2089
3094
|
asset: {
|
|
2090
3095
|
...sourceAsset ?? {},
|
|
2091
3096
|
sourcePath: appConfigSourcePath,
|
|
@@ -2094,10 +3099,22 @@ var createEmbeddedConfig = (sourceConfig, asset, appId) => {
|
|
|
2094
3099
|
},
|
|
2095
3100
|
bootstrap: {
|
|
2096
3101
|
...sourceBootstrap ?? {},
|
|
2097
|
-
appId
|
|
3102
|
+
appId,
|
|
3103
|
+
desktopId
|
|
2098
3104
|
}
|
|
2099
3105
|
};
|
|
2100
3106
|
};
|
|
3107
|
+
var assertNoUserInitialTitleBarIcon = (sourceConfig) => {
|
|
3108
|
+
const sourceBrowser = sourceConfig.browser;
|
|
3109
|
+
if (sourceBrowser === void 0) return;
|
|
3110
|
+
if (!isJsonObject$2(sourceBrowser)) throw new Error("muon.json browser must be an object when present.");
|
|
3111
|
+
if (sourceBrowser.initialTitleBarIcon !== void 0) throw new Error("muon.json browser.initialTitleBarIcon is generated by muon build; use top-level iconPath instead.");
|
|
3112
|
+
};
|
|
3113
|
+
var stripBuildOnlyAppIconConfig = (sourceConfig) => {
|
|
3114
|
+
const output = {};
|
|
3115
|
+
for (const [key, value] of Object.entries(sourceConfig)) if (key !== "iconPath") output[key] = value;
|
|
3116
|
+
return output;
|
|
3117
|
+
};
|
|
2101
3118
|
var withTemporaryConfig = async (config, callback) => {
|
|
2102
3119
|
const tempDirectory = await mkdtemp(join(tmpdir(), "muon-build-config-"));
|
|
2103
3120
|
const configPath = join(tempDirectory, "muon.json");
|
|
@@ -2126,7 +3143,7 @@ var statOrUndefined = async (path) => {
|
|
|
2126
3143
|
return;
|
|
2127
3144
|
}
|
|
2128
3145
|
};
|
|
2129
|
-
var fileExists$
|
|
3146
|
+
var fileExists$2 = async (path) => {
|
|
2130
3147
|
try {
|
|
2131
3148
|
await access(path, constants.F_OK);
|
|
2132
3149
|
return true;
|
|
@@ -2134,15 +3151,420 @@ var fileExists$1 = async (path) => {
|
|
|
2134
3151
|
return false;
|
|
2135
3152
|
}
|
|
2136
3153
|
};
|
|
2137
|
-
var isJsonObject$
|
|
3154
|
+
var isJsonObject$2 = (value) => {
|
|
2138
3155
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2139
3156
|
};
|
|
2140
|
-
var getErrorMessage$
|
|
3157
|
+
var getErrorMessage$3 = (error) => error instanceof Error ? error.message : String(error);
|
|
3158
|
+
//#endregion
|
|
3159
|
+
//#region src/capability.ts
|
|
3160
|
+
var virtualModulePrefix = "\0muon-capability:";
|
|
3161
|
+
var moduleNamePattern = /^[A-Za-z_$][A-Za-z0-9_$]*:[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*$/u;
|
|
3162
|
+
var jsIdentifierPattern = /^[A-Za-z_$][A-Za-z0-9_$]*$/u;
|
|
3163
|
+
var normalizePath = (path) => (path.split("?")[0] ?? "").replaceAll("\\", "/");
|
|
3164
|
+
var hashCapabilityRule = (index, rule) => `cap-${createHash("sha1").update(JSON.stringify({
|
|
3165
|
+
index,
|
|
3166
|
+
pluginName: rule.pluginName,
|
|
3167
|
+
sources: [...rule.sources ?? []],
|
|
3168
|
+
packages: [...rule.packages ?? []],
|
|
3169
|
+
allow: [...rule.allow]
|
|
3170
|
+
})).digest("hex").slice(0, 16)}`;
|
|
3171
|
+
var parseCapabilitySpecifier = (source) => {
|
|
3172
|
+
if (!moduleNamePattern.test(source)) return;
|
|
3173
|
+
const separator = source.indexOf(":");
|
|
3174
|
+
return {
|
|
3175
|
+
moduleName: source,
|
|
3176
|
+
namespace: `${source.slice(0, separator)}.${source.slice(separator + 1)}`
|
|
3177
|
+
};
|
|
3178
|
+
};
|
|
3179
|
+
var escapeRegExp = (source) => source.replace(/[\\^$+?.()|[\]{}]/gu, "\\$&");
|
|
3180
|
+
var globToRegExp = (glob, separator) => {
|
|
3181
|
+
let source = "^";
|
|
3182
|
+
for (let index = 0; index < glob.length; index += 1) {
|
|
3183
|
+
const current = glob[index] ?? "";
|
|
3184
|
+
const next = glob[index + 1];
|
|
3185
|
+
if (current === "*" && next === "*") {
|
|
3186
|
+
source += ".*";
|
|
3187
|
+
index += 1;
|
|
3188
|
+
continue;
|
|
3189
|
+
}
|
|
3190
|
+
if (current === "*") {
|
|
3191
|
+
source += `[^${escapeRegExp(separator)}]*`;
|
|
3192
|
+
continue;
|
|
3193
|
+
}
|
|
3194
|
+
source += escapeRegExp(current);
|
|
3195
|
+
}
|
|
3196
|
+
source += "$";
|
|
3197
|
+
return new RegExp(source, "u");
|
|
3198
|
+
};
|
|
3199
|
+
var isGlobMatch = (glob, value, separator) => globToRegExp(glob, separator).test(value);
|
|
3200
|
+
var toRootRelativeImporter = (root, importer) => normalizePath(relative(root, importer));
|
|
3201
|
+
var readPackageName = (packageJsonPath) => {
|
|
3202
|
+
try {
|
|
3203
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
3204
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && typeof parsed.name === "string" ? parsed.name : void 0;
|
|
3205
|
+
} catch {
|
|
3206
|
+
return;
|
|
3207
|
+
}
|
|
3208
|
+
};
|
|
3209
|
+
var getImporterPackageName = (root, importer) => {
|
|
3210
|
+
const normalizedRoot = normalizePath(root);
|
|
3211
|
+
const rootPackageJsonPath = join(root, "package.json");
|
|
3212
|
+
let directory = dirname(normalizePath(importer));
|
|
3213
|
+
for (;;) {
|
|
3214
|
+
const packageJsonPath = join(directory, "package.json");
|
|
3215
|
+
if (packageJsonPath !== rootPackageJsonPath && existsSync(packageJsonPath)) return readPackageName(packageJsonPath);
|
|
3216
|
+
if (normalizePath(directory) === normalizedRoot) return;
|
|
3217
|
+
const parent = dirname(directory);
|
|
3218
|
+
if (parent === directory) return;
|
|
3219
|
+
directory = parent;
|
|
3220
|
+
}
|
|
3221
|
+
};
|
|
3222
|
+
var matchesCapabilityRuleImporter = (rule, root, importer) => {
|
|
3223
|
+
const relativeImporter = toRootRelativeImporter(root, importer);
|
|
3224
|
+
if (rule.sources?.some((pattern) => isGlobMatch(normalizePath(pattern), relativeImporter, "/")) ?? false) return true;
|
|
3225
|
+
const packageName = getImporterPackageName(root, importer);
|
|
3226
|
+
return packageName === void 0 ? false : rule.packages?.includes(packageName) ?? false;
|
|
3227
|
+
};
|
|
3228
|
+
var hasNamespaceFunctionAllow = (namespace, allow) => allow.some((entry) => {
|
|
3229
|
+
if (entry.startsWith(`${namespace}.`)) return true;
|
|
3230
|
+
return isGlobMatch(entry, `${namespace}.placeholder`, ".");
|
|
3231
|
+
});
|
|
3232
|
+
var getExactExportName = (namespace, functionPath) => {
|
|
3233
|
+
const prefix = `${namespace}.`;
|
|
3234
|
+
if (!functionPath.startsWith(prefix)) return;
|
|
3235
|
+
const name = functionPath.slice(prefix.length);
|
|
3236
|
+
if (!jsIdentifierPattern.test(name)) return;
|
|
3237
|
+
return name;
|
|
3238
|
+
};
|
|
3239
|
+
var getExportedFunctions = (namespace, allow) => allow.flatMap((functionPath) => {
|
|
3240
|
+
const exportName = getExactExportName(namespace, functionPath);
|
|
3241
|
+
return exportName === void 0 ? [] : [[exportName, functionPath]];
|
|
3242
|
+
});
|
|
3243
|
+
var createRuleRuntimeEntry = (rule, index) => ({
|
|
3244
|
+
id: hashCapabilityRule(index, rule),
|
|
3245
|
+
allow: [...rule.allow]
|
|
3246
|
+
});
|
|
3247
|
+
var validateCapabilityRule = (rule) => {
|
|
3248
|
+
if ((rule.sources === void 0 || rule.sources.length === 0) && (rule.packages === void 0 || rule.packages.length === 0)) throw new Error("Muon capability import rule requires sources or packages.");
|
|
3249
|
+
};
|
|
3250
|
+
var createModuleVirtualId = (rule) => `${virtualModulePrefix}${rule.index}:${rule.moduleName}`;
|
|
3251
|
+
var parseModuleVirtualId = (id) => {
|
|
3252
|
+
if (!id.startsWith(virtualModulePrefix)) return;
|
|
3253
|
+
const payload = id.slice(17);
|
|
3254
|
+
const separator = payload.indexOf(":");
|
|
3255
|
+
if (separator <= 0) return;
|
|
3256
|
+
const index = Number.parseInt(payload.slice(0, separator), 10);
|
|
3257
|
+
if (!Number.isInteger(index) || index < 0) return;
|
|
3258
|
+
return {
|
|
3259
|
+
index,
|
|
3260
|
+
moduleName: payload.slice(separator + 1)
|
|
3261
|
+
};
|
|
3262
|
+
};
|
|
3263
|
+
var createExecutorSpawnExport = (capabilityId, functionPath) => `export const spawn = async (options = {}) => {
|
|
3264
|
+
const source = await __muonCall(${JSON.stringify(capabilityId)}, ${JSON.stringify(functionPath)}, [JSON.stringify(options ?? {})]);
|
|
3265
|
+
return JSON.parse(source);
|
|
3266
|
+
};`;
|
|
3267
|
+
var createGenericFunctionExport = (exportName, capabilityId, functionPath) => `export const ${exportName} = async (...args) =>
|
|
3268
|
+
await __muonCall(${JSON.stringify(capabilityId)}, ${JSON.stringify(functionPath)}, args);`;
|
|
3269
|
+
var createModuleSource = (rule) => {
|
|
3270
|
+
const exportedFunctions = getExportedFunctions(rule.namespace, rule.rule.allow);
|
|
3271
|
+
if (exportedFunctions.length === 0) throw new Error(`Muon capability import has no exact function exports: ${rule.moduleName}`);
|
|
3272
|
+
return `const __muonCall = globalThis.__muon_plugin_call;
|
|
3273
|
+
if (typeof __muonCall !== "function") {
|
|
3274
|
+
throw new Error("Muon plugin capability bridge is not available.");
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
${exportedFunctions.map(([exportName, functionPath]) => rule.namespace === "muon.executor" && exportName === "spawn" ? createExecutorSpawnExport(rule.id, functionPath) : createGenericFunctionExport(exportName, rule.id, functionPath)).join("\n\n")}
|
|
3278
|
+
`;
|
|
3279
|
+
};
|
|
3280
|
+
/**
|
|
3281
|
+
* Creates a capability virtual module resolver for a bundler adapter.
|
|
3282
|
+
*
|
|
3283
|
+
* @param root Bundler project root.
|
|
3284
|
+
* @param options Capability import options.
|
|
3285
|
+
*/
|
|
3286
|
+
var createMuonCapabilityModuleResolver = (root, options) => {
|
|
3287
|
+
const rules = [...options?.imports ?? []];
|
|
3288
|
+
for (const rule of rules) validateCapabilityRule(rule);
|
|
3289
|
+
const runtimeEntries = rules.map((rule, index) => createRuleRuntimeEntry(rule, index));
|
|
3290
|
+
const resolveRule = (source, importer) => {
|
|
3291
|
+
const parsed = parseCapabilitySpecifier(source);
|
|
3292
|
+
if (parsed === void 0) return;
|
|
3293
|
+
if (!rules.some((rule) => hasNamespaceFunctionAllow(parsed.namespace, rule.allow))) return;
|
|
3294
|
+
if (importer === void 0) throw new Error(`Muon capability import is not allowed without an importer: ${source}`);
|
|
3295
|
+
const relativeImporter = toRootRelativeImporter(root, importer);
|
|
3296
|
+
for (let index = 0; index < rules.length; index += 1) {
|
|
3297
|
+
const rule = rules[index];
|
|
3298
|
+
if (rule === void 0 || !matchesCapabilityRuleImporter(rule, root, importer) || !hasNamespaceFunctionAllow(parsed.namespace, rule.allow)) continue;
|
|
3299
|
+
return {
|
|
3300
|
+
index,
|
|
3301
|
+
id: runtimeEntries[index]?.id ?? hashCapabilityRule(index, rule),
|
|
3302
|
+
rule,
|
|
3303
|
+
namespace: parsed.namespace,
|
|
3304
|
+
moduleName: parsed.moduleName
|
|
3305
|
+
};
|
|
3306
|
+
}
|
|
3307
|
+
throw new Error(`Muon capability import is not allowed: ${source} from ${relativeImporter}`);
|
|
3308
|
+
};
|
|
3309
|
+
return {
|
|
3310
|
+
resolveId: (source, importer) => {
|
|
3311
|
+
const rule = resolveRule(source, importer);
|
|
3312
|
+
return rule === void 0 ? void 0 : { id: createModuleVirtualId(rule) };
|
|
3313
|
+
},
|
|
3314
|
+
load: (id) => {
|
|
3315
|
+
const parsed = parseModuleVirtualId(id);
|
|
3316
|
+
if (parsed === void 0) return;
|
|
3317
|
+
const source = parseCapabilitySpecifier(parsed.moduleName);
|
|
3318
|
+
const rule = rules[parsed.index];
|
|
3319
|
+
const runtimeEntry = runtimeEntries[parsed.index];
|
|
3320
|
+
if (source === void 0 || rule === void 0 || runtimeEntry === void 0) return;
|
|
3321
|
+
return createModuleSource({
|
|
3322
|
+
index: parsed.index,
|
|
3323
|
+
id: runtimeEntry.id,
|
|
3324
|
+
rule,
|
|
3325
|
+
namespace: source.namespace,
|
|
3326
|
+
moduleName: source.moduleName
|
|
3327
|
+
});
|
|
3328
|
+
},
|
|
3329
|
+
getRuntimePluginConfig: () => ({
|
|
3330
|
+
mode: "validate",
|
|
3331
|
+
capabilities: runtimeEntries
|
|
3332
|
+
})
|
|
3333
|
+
};
|
|
3334
|
+
};
|
|
3335
|
+
//#endregion
|
|
3336
|
+
//#region src/plugin-access.ts
|
|
3337
|
+
var defaultConfigFileNames = [
|
|
3338
|
+
"muon.json5",
|
|
3339
|
+
"muon.jsonc",
|
|
3340
|
+
"muon.json"
|
|
3341
|
+
];
|
|
3342
|
+
var defaultPluginPages = ["asset://main/**"];
|
|
3343
|
+
var isJsonObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3344
|
+
var isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
3345
|
+
var isSha1HexString = (value) => value.length === 40 && /^[0-9a-fA-F]+$/.test(value);
|
|
3346
|
+
var isHexByteString = (value) => value.length % 2 === 0 && /^[0-9a-fA-F]*$/.test(value);
|
|
3347
|
+
var getErrorMessage$2 = (error) => error instanceof Error ? error.message : String(error);
|
|
3348
|
+
var fileExists$1 = async (path) => {
|
|
3349
|
+
try {
|
|
3350
|
+
await access(path, constants.F_OK);
|
|
3351
|
+
return true;
|
|
3352
|
+
} catch {
|
|
3353
|
+
return false;
|
|
3354
|
+
}
|
|
3355
|
+
};
|
|
3356
|
+
var resolveMuonConfigPath = async (root, configPath) => {
|
|
3357
|
+
if (configPath !== void 0) {
|
|
3358
|
+
const resolvedPath = resolve(root, configPath);
|
|
3359
|
+
if (await fileExists$1(resolvedPath)) return resolvedPath;
|
|
3360
|
+
throw new Error(`Muon config file does not exist: ${resolvedPath}`);
|
|
3361
|
+
}
|
|
3362
|
+
for (const fileName of defaultConfigFileNames) {
|
|
3363
|
+
const candidatePath = join(root, fileName);
|
|
3364
|
+
if (await fileExists$1(candidatePath)) return candidatePath;
|
|
3365
|
+
}
|
|
3366
|
+
};
|
|
3367
|
+
var readJsonObjectFile = async (path, label) => {
|
|
3368
|
+
let content;
|
|
3369
|
+
try {
|
|
3370
|
+
content = await readFile(path, "utf8");
|
|
3371
|
+
} catch (error) {
|
|
3372
|
+
throw new Error(`${label} could not be read: ${path}: ${getErrorMessage$2(error)}`);
|
|
3373
|
+
}
|
|
3374
|
+
let parsed;
|
|
3375
|
+
try {
|
|
3376
|
+
parsed = (0, import_dist.parse)(content);
|
|
3377
|
+
} catch (error) {
|
|
3378
|
+
throw new Error(`${label} could not be parsed: ${path}: ${getErrorMessage$2(error)}`);
|
|
3379
|
+
}
|
|
3380
|
+
if (!isJsonObject$1(parsed)) throw new Error(`${label} must contain a JSON object: ${path}`);
|
|
3381
|
+
return parsed;
|
|
3382
|
+
};
|
|
3383
|
+
var readOptionalStringArray = (object, key, configPath) => {
|
|
3384
|
+
const value = object[key];
|
|
3385
|
+
if (value === void 0) return;
|
|
3386
|
+
if (!isStringArray(value)) throw new Error(`muon.json ${configPath} must be an array of strings.`);
|
|
3387
|
+
return value;
|
|
3388
|
+
};
|
|
3389
|
+
var readPluginAccessImportOptions = (value, configPath) => {
|
|
3390
|
+
if (!isJsonObject$1(value)) throw new Error(`muon.json ${configPath} must be an object.`);
|
|
3391
|
+
const sources = readOptionalStringArray(value, "sources", `${configPath}.sources`);
|
|
3392
|
+
const packages = readOptionalStringArray(value, "packages", `${configPath}.packages`);
|
|
3393
|
+
const allow = readOptionalStringArray(value, "allow", `${configPath}.allow`);
|
|
3394
|
+
const options = {};
|
|
3395
|
+
if (sources !== void 0) options.sources = sources;
|
|
3396
|
+
if (packages !== void 0) options.packages = packages;
|
|
3397
|
+
if (allow !== void 0) options.allow = allow;
|
|
3398
|
+
return options;
|
|
3399
|
+
};
|
|
3400
|
+
var readPluginAccessEntryOptions = (value, index) => {
|
|
3401
|
+
const configPath = `plugin.plugins[${index}]`;
|
|
3402
|
+
if (!isJsonObject$1(value)) throw new Error(`muon.json ${configPath} must be an object.`);
|
|
3403
|
+
if (typeof value.name !== "string") throw new Error(`muon.json ${configPath}.name must be a string.`);
|
|
3404
|
+
if (value.signature !== void 0) {
|
|
3405
|
+
if (typeof value.signature !== "string") throw new Error(`muon.json ${configPath}.signature must be a string.`);
|
|
3406
|
+
if (!isSha1HexString(value.signature)) throw new Error(`muon.json ${configPath}.signature must be a 40-character SHA-1 hex string.`);
|
|
3407
|
+
}
|
|
3408
|
+
if (value.salt !== void 0) {
|
|
3409
|
+
if (typeof value.salt !== "string") throw new Error(`muon.json ${configPath}.salt must be a string.`);
|
|
3410
|
+
if (!isHexByteString(value.salt)) throw new Error(`muon.json ${configPath}.salt must be a hexadecimal byte string.`);
|
|
3411
|
+
}
|
|
3412
|
+
const allow = readOptionalStringArray(value, "allow", `${configPath}.allow`);
|
|
3413
|
+
const rawImports = value.imports;
|
|
3414
|
+
if (rawImports !== void 0 && !Array.isArray(rawImports)) throw new Error(`muon.json ${configPath}.imports must be an array.`);
|
|
3415
|
+
const imports = rawImports === void 0 ? void 0 : rawImports.map((entry, importIndex) => readPluginAccessImportOptions(entry, `${configPath}.imports[${importIndex}]`));
|
|
3416
|
+
const options = { name: value.name };
|
|
3417
|
+
if (value.signature !== void 0) options.signature = value.signature;
|
|
3418
|
+
if (value.salt !== void 0) options.salt = value.salt;
|
|
3419
|
+
if (allow !== void 0) options.allow = allow;
|
|
3420
|
+
if (imports !== void 0) options.imports = imports;
|
|
3421
|
+
return options;
|
|
3422
|
+
};
|
|
3423
|
+
var readPluginAccessOptions = (config) => {
|
|
3424
|
+
const browser = config.browser;
|
|
3425
|
+
if (isJsonObject$1(browser) && browser.plugin !== void 0) throw new Error("muon.json browser.plugin is no longer supported; use plugin.mode and plugin.pages instead.");
|
|
3426
|
+
const plugin = config.plugin;
|
|
3427
|
+
if (plugin === void 0) return {};
|
|
3428
|
+
if (!isJsonObject$1(plugin)) throw new Error("muon.json plugin must be an object.");
|
|
3429
|
+
const options = {};
|
|
3430
|
+
if (plugin.path !== void 0) {
|
|
3431
|
+
if (typeof plugin.path !== "string") throw new Error("muon.json plugin.path must be a string.");
|
|
3432
|
+
if (plugin.path.length === 0) throw new Error("muon.json plugin.path must not be empty.");
|
|
3433
|
+
options.path = plugin.path;
|
|
3434
|
+
}
|
|
3435
|
+
if (plugin.mode !== void 0) {
|
|
3436
|
+
if (typeof plugin.mode !== "string") throw new Error("muon.json plugin.mode must be a string.");
|
|
3437
|
+
if (plugin.mode.length === 0) throw new Error("muon.json plugin.mode must not be empty.");
|
|
3438
|
+
if (plugin.mode !== "simple" && plugin.mode !== "validate") throw new Error("muon.json plugin.mode has unknown value.");
|
|
3439
|
+
options.mode = plugin.mode;
|
|
3440
|
+
}
|
|
3441
|
+
const pages = readOptionalStringArray(plugin, "pages", "plugin.pages");
|
|
3442
|
+
if (pages !== void 0) options.pages = pages;
|
|
3443
|
+
const rawPlugins = plugin.plugins;
|
|
3444
|
+
if (rawPlugins !== void 0) {
|
|
3445
|
+
if (!Array.isArray(rawPlugins)) throw new Error("muon.json plugin.plugins must be an array.");
|
|
3446
|
+
options.plugins = rawPlugins.map((entry, index) => readPluginAccessEntryOptions(entry, index));
|
|
3447
|
+
}
|
|
3448
|
+
return options;
|
|
3449
|
+
};
|
|
3450
|
+
var hasImportSourceSelector = (entry) => entry.sources !== void 0 && entry.sources.length > 0 || entry.packages !== void 0 && entry.packages.length > 0;
|
|
3451
|
+
var validatePluginAccessOptions = (resolved) => {
|
|
3452
|
+
for (const [pluginIndex, plugin] of resolved.plugins.entries()) {
|
|
3453
|
+
const pluginPath = `plugin.plugins[${pluginIndex}]`;
|
|
3454
|
+
if (plugin.name === "internal" && plugin.signature !== void 0) throw new Error(`muon.json ${pluginPath}.signature is not supported for internal plugins.`);
|
|
3455
|
+
if (plugin.name === "internal" && plugin.salt !== void 0) throw new Error(`muon.json ${pluginPath}.salt is not supported for internal plugins.`);
|
|
3456
|
+
if (plugin.signature !== void 0 && plugin.salt === void 0) throw new Error(`muon.json ${pluginPath}.signature requires ${pluginPath}.salt.`);
|
|
3457
|
+
if (resolved.mode === "validate") {
|
|
3458
|
+
if (plugin.allow !== void 0) throw new Error(`muon.json ${pluginPath}.allow is only supported in simple mode; use ${pluginPath}.imports[].allow in validate mode.`);
|
|
3459
|
+
if (plugin.imports === void 0) throw new Error(`muon.json ${pluginPath}.imports is required in validate mode.`);
|
|
3460
|
+
if (plugin.imports.length === 0) throw new Error(`muon.json ${pluginPath}.imports must not be empty in validate mode.`);
|
|
3461
|
+
for (const [importIndex, entry] of plugin.imports.entries()) {
|
|
3462
|
+
const importPath = `${pluginPath}.imports[${importIndex}]`;
|
|
3463
|
+
if (!hasImportSourceSelector(entry)) throw new Error(`muon.json ${importPath} requires sources or packages.`);
|
|
3464
|
+
if (entry.allow === void 0) throw new Error(`muon.json ${importPath}.allow is required in validate mode.`);
|
|
3465
|
+
if (entry.allow.length === 0) throw new Error(`muon.json ${importPath}.allow must not be empty in validate mode.`);
|
|
3466
|
+
}
|
|
3467
|
+
continue;
|
|
3468
|
+
}
|
|
3469
|
+
if (plugin.allow === void 0) throw new Error(`muon.json ${pluginPath}.allow is required in simple mode.`);
|
|
3470
|
+
if (plugin.imports !== void 0) throw new Error(`muon.json ${pluginPath}.imports is only supported in validate mode.`);
|
|
3471
|
+
}
|
|
3472
|
+
return resolved;
|
|
3473
|
+
};
|
|
3474
|
+
var getValidateImportAllow = (entry, pluginIndex, importIndex) => {
|
|
3475
|
+
if (entry.allow === void 0 || entry.allow.length === 0) throw new Error(`muon.json plugin.plugins[${pluginIndex}].imports[${importIndex}].allow is required in validate mode.`);
|
|
3476
|
+
return entry.allow;
|
|
3477
|
+
};
|
|
3478
|
+
var getSimplePluginAllow = (plugin, pluginIndex) => {
|
|
3479
|
+
if (plugin.allow === void 0) throw new Error(`muon.json plugin.plugins[${pluginIndex}].allow is required in simple mode.`);
|
|
3480
|
+
return plugin.allow;
|
|
3481
|
+
};
|
|
3482
|
+
var toCapabilityImports = (plugins) => plugins.flatMap((plugin, pluginIndex) => (plugin.imports ?? []).map((entry, importIndex) => {
|
|
3483
|
+
if (!hasImportSourceSelector(entry)) throw new Error(`muon.json plugin.plugins[${pluginIndex}].imports[${importIndex}] requires sources or packages.`);
|
|
3484
|
+
const rule = {
|
|
3485
|
+
allow: getValidateImportAllow(entry, pluginIndex, importIndex),
|
|
3486
|
+
pluginName: plugin.name
|
|
3487
|
+
};
|
|
3488
|
+
if (entry.sources !== void 0) rule.sources = entry.sources;
|
|
3489
|
+
if (entry.packages !== void 0) rule.packages = entry.packages;
|
|
3490
|
+
return rule;
|
|
3491
|
+
}));
|
|
3492
|
+
var toValidateRuntimePluginEntries = (plugins) => plugins.map((plugin, pluginIndex) => {
|
|
3493
|
+
const allow = [];
|
|
3494
|
+
const seenAllow = /* @__PURE__ */ new Set();
|
|
3495
|
+
for (const [importIndex, entry] of (plugin.imports ?? []).entries()) for (const functionPath of getValidateImportAllow(entry, pluginIndex, importIndex)) if (!seenAllow.has(functionPath)) {
|
|
3496
|
+
seenAllow.add(functionPath);
|
|
3497
|
+
allow.push(functionPath);
|
|
3498
|
+
}
|
|
3499
|
+
return {
|
|
3500
|
+
name: plugin.name,
|
|
3501
|
+
...plugin.signature === void 0 ? {} : { signature: plugin.signature },
|
|
3502
|
+
...plugin.salt === void 0 ? {} : { salt: plugin.salt },
|
|
3503
|
+
allow
|
|
3504
|
+
};
|
|
3505
|
+
});
|
|
3506
|
+
var toSimpleRuntimePluginEntries = (plugins) => plugins.map((plugin, pluginIndex) => ({
|
|
3507
|
+
name: plugin.name,
|
|
3508
|
+
...plugin.signature === void 0 ? {} : { signature: plugin.signature },
|
|
3509
|
+
...plugin.salt === void 0 ? {} : { salt: plugin.salt },
|
|
3510
|
+
allow: [...getSimplePluginAllow(plugin, pluginIndex)]
|
|
3511
|
+
}));
|
|
3512
|
+
var shouldOverlayRuntimePlugins = (resolved, override) => override !== false && (override?.plugins !== void 0 || resolved.mode === "validate" && resolved.plugins.length > 0);
|
|
3513
|
+
var toRuntimePluginEntries = (resolved) => resolved.mode === "validate" ? toValidateRuntimePluginEntries(resolved.plugins) : toSimpleRuntimePluginEntries(resolved.plugins);
|
|
3514
|
+
var mergePluginAccessOptions = (base, override) => ({
|
|
3515
|
+
...override?.path === void 0 ? {} : { path: override.path },
|
|
3516
|
+
mode: override?.mode ?? base.mode ?? "validate",
|
|
3517
|
+
pages: override?.pages ?? base.pages ?? defaultPluginPages,
|
|
3518
|
+
plugins: override?.plugins ?? base.plugins ?? []
|
|
3519
|
+
});
|
|
3520
|
+
var createRuntimeOverlay = (root, resolved, override) => {
|
|
3521
|
+
if (override === false) return {};
|
|
3522
|
+
const path = override?.path !== void 0 ? resolve(root, override.path) : void 0;
|
|
3523
|
+
const plugins = shouldOverlayRuntimePlugins(resolved, override) ? toRuntimePluginEntries(resolved) : void 0;
|
|
3524
|
+
return {
|
|
3525
|
+
pages: resolved.pages,
|
|
3526
|
+
...path === void 0 ? {} : { path },
|
|
3527
|
+
...plugins === void 0 ? {} : { plugins }
|
|
3528
|
+
};
|
|
3529
|
+
};
|
|
3530
|
+
/**
|
|
3531
|
+
* Resolves public plugin access config from muon.json and Vite overrides.
|
|
3532
|
+
*
|
|
3533
|
+
* @param input Resolution inputs.
|
|
3534
|
+
* @returns Effective plugin access config for bundler integrations.
|
|
3535
|
+
*/
|
|
3536
|
+
var resolveMuonPluginAccessOptions = async ({ root, configPath, pluginAccess, onConfigReadError }) => {
|
|
3537
|
+
const resolvedConfigPath = await resolveMuonConfigPath(root, configPath);
|
|
3538
|
+
let base = {};
|
|
3539
|
+
if (resolvedConfigPath !== void 0) try {
|
|
3540
|
+
base = readPluginAccessOptions(await readJsonObjectFile(resolvedConfigPath, "Muon config file"));
|
|
3541
|
+
} catch (error) {
|
|
3542
|
+
if (onConfigReadError === void 0) throw error;
|
|
3543
|
+
onConfigReadError(error);
|
|
3544
|
+
}
|
|
3545
|
+
if (pluginAccess === false) {
|
|
3546
|
+
const resolved = validatePluginAccessOptions(mergePluginAccessOptions(base, { mode: "simple" }));
|
|
3547
|
+
return {
|
|
3548
|
+
mode: "simple",
|
|
3549
|
+
pages: resolved.pages,
|
|
3550
|
+
capabilityOptions: { imports: [] },
|
|
3551
|
+
runtimeOverlay: createRuntimeOverlay(root, resolved, pluginAccess)
|
|
3552
|
+
};
|
|
3553
|
+
}
|
|
3554
|
+
const resolved = validatePluginAccessOptions(mergePluginAccessOptions(base, pluginAccess));
|
|
3555
|
+
const capabilityOptions = { imports: resolved.mode === "validate" ? toCapabilityImports(resolved.plugins) : [] };
|
|
3556
|
+
return {
|
|
3557
|
+
mode: resolved.mode,
|
|
3558
|
+
pages: resolved.pages,
|
|
3559
|
+
capabilityOptions,
|
|
3560
|
+
runtimeOverlay: createRuntimeOverlay(root, resolved, pluginAccess)
|
|
3561
|
+
};
|
|
3562
|
+
};
|
|
2141
3563
|
//#endregion
|
|
2142
3564
|
//#region src/gitignore.ts
|
|
2143
3565
|
var muonGitignoreEntries = [
|
|
2144
3566
|
".muon/",
|
|
2145
|
-
"dist-muon
|
|
3567
|
+
"dist-muon/",
|
|
2146
3568
|
"artifacts/"
|
|
2147
3569
|
];
|
|
2148
3570
|
var muonGitignoreEntryAliases = {
|
|
@@ -2152,11 +3574,13 @@ var muonGitignoreEntryAliases = {
|
|
|
2152
3574
|
".muon",
|
|
2153
3575
|
"/.muon"
|
|
2154
3576
|
],
|
|
2155
|
-
"dist-muon
|
|
2156
|
-
"dist-muon
|
|
2157
|
-
"/dist-muon
|
|
2158
|
-
"dist-muon
|
|
2159
|
-
"/dist-muon
|
|
3577
|
+
"dist-muon/": [
|
|
3578
|
+
"dist-muon/",
|
|
3579
|
+
"/dist-muon/",
|
|
3580
|
+
"dist-muon",
|
|
3581
|
+
"/dist-muon",
|
|
3582
|
+
"dist-muon/*",
|
|
3583
|
+
"/dist-muon/*"
|
|
2160
3584
|
],
|
|
2161
3585
|
"artifacts/": [
|
|
2162
3586
|
"artifacts/",
|
|
@@ -2299,7 +3723,7 @@ var getWebSocketOrigin = (startUrl) => {
|
|
|
2299
3723
|
else if (url.protocol === "http:") url.protocol = "ws:";
|
|
2300
3724
|
return url.origin;
|
|
2301
3725
|
};
|
|
2302
|
-
var createMuonOverrideConfig = (startUrl, enableDebugger) => {
|
|
3726
|
+
var createMuonOverrideConfig = (startUrl, enableDebugger, runtimePluginConfig) => {
|
|
2303
3727
|
const origin = new URL(startUrl).origin;
|
|
2304
3728
|
return {
|
|
2305
3729
|
...enableDebugger ? { cdp: { enable: true } } : {},
|
|
@@ -2308,19 +3732,22 @@ var createMuonOverrideConfig = (startUrl, enableDebugger) => {
|
|
|
2308
3732
|
...enableDebugger ? { keybind: {
|
|
2309
3733
|
devtools: "f12",
|
|
2310
3734
|
recycle: "ctrl+f12"
|
|
2311
|
-
} } : {}
|
|
2312
|
-
|
|
3735
|
+
} } : {}
|
|
3736
|
+
},
|
|
3737
|
+
plugin: {
|
|
3738
|
+
...runtimePluginConfig,
|
|
3739
|
+
pages: [`${origin}/**`]
|
|
2313
3740
|
},
|
|
2314
3741
|
network: { allow: [`${origin}/**`, `${getWebSocketOrigin(startUrl)}/**`] }
|
|
2315
3742
|
};
|
|
2316
3743
|
};
|
|
2317
|
-
var writeMuonOverrideConfig = (server, overrideConfigPath,
|
|
3744
|
+
var writeMuonOverrideConfig = (server, overrideConfigPath, pluginOptions, getRuntimePluginConfig) => {
|
|
2318
3745
|
const startUrl = getBaseUrl(server);
|
|
2319
3746
|
if (startUrl === void 0) {
|
|
2320
3747
|
server.config.logger.warn("Muon Vite plugin could not resolve a Vite URL.");
|
|
2321
3748
|
return false;
|
|
2322
3749
|
}
|
|
2323
|
-
writeFileSync(overrideConfigPath, `${JSON.stringify(createMuonOverrideConfig(startUrl, enableDebugger), null, 2)}\n`);
|
|
3750
|
+
writeFileSync(overrideConfigPath, `${JSON.stringify(createMuonOverrideConfig(startUrl, pluginOptions.enableDebugger !== false, getRuntimePluginConfig()), null, 2)}\n`);
|
|
2324
3751
|
return true;
|
|
2325
3752
|
};
|
|
2326
3753
|
var createRuntimePaths = async (server, stagePath, platform, projectConfigPath) => {
|
|
@@ -2342,7 +3769,7 @@ var fileExists = async (path) => {
|
|
|
2342
3769
|
}
|
|
2343
3770
|
};
|
|
2344
3771
|
var isJsonObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2345
|
-
var getErrorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
3772
|
+
var getErrorMessage$1 = (error) => error instanceof Error ? error.message : String(error);
|
|
2346
3773
|
var resolveProjectConfigPath = async (server) => {
|
|
2347
3774
|
for (const fileName of defaultProjectConfigFileNames) {
|
|
2348
3775
|
const candidatePath = join(server.config.root, fileName);
|
|
@@ -2351,7 +3778,7 @@ var resolveProjectConfigPath = async (server) => {
|
|
|
2351
3778
|
if (!isJsonObject((0, import_dist.parse)(await readFile(candidatePath, "utf8")))) throw new Error("muon config root must be an object");
|
|
2352
3779
|
return candidatePath;
|
|
2353
3780
|
} catch (error) {
|
|
2354
|
-
server.config.logger.warn(`Muon project config will be ignored because it could not be read or parsed: ${candidatePath}: ${getErrorMessage(error)}`);
|
|
3781
|
+
server.config.logger.warn(`Muon project config will be ignored because it could not be read or parsed: ${candidatePath}: ${getErrorMessage$1(error)}`);
|
|
2355
3782
|
return;
|
|
2356
3783
|
}
|
|
2357
3784
|
}
|
|
@@ -2379,11 +3806,11 @@ var launchMuon = (paths, platform, server) => {
|
|
|
2379
3806
|
windowsHide: false
|
|
2380
3807
|
});
|
|
2381
3808
|
child.once("error", (error) => {
|
|
2382
|
-
server.config.logger.warn(`Muon startup failed: ${getErrorMessage(error)}`);
|
|
3809
|
+
server.config.logger.warn(`Muon startup failed: ${getErrorMessage$1(error)}`);
|
|
2383
3810
|
});
|
|
2384
3811
|
child.unref();
|
|
2385
3812
|
};
|
|
2386
|
-
var startMuonViteBrowserBridge = async ({ server, pluginOptions, platform, architecture, environment }) => {
|
|
3813
|
+
var startMuonViteBrowserBridge = async ({ server, pluginOptions, getRuntimePluginConfig, platform, architecture, environment }) => {
|
|
2387
3814
|
await ensureMuonGitignoreEntry(server.config.root);
|
|
2388
3815
|
if (pluginOptions.open === false || server.httpServer === null) return;
|
|
2389
3816
|
const target = getDefaultMuonPrepareTarget(platform, architecture);
|
|
@@ -2403,7 +3830,7 @@ var startMuonViteBrowserBridge = async ({ server, pluginOptions, platform, archi
|
|
|
2403
3830
|
environment,
|
|
2404
3831
|
cwd: server.config.root
|
|
2405
3832
|
});
|
|
2406
|
-
if (preparedRuntime.stagePath === void 0) throw new Error("muon-
|
|
3833
|
+
if (preparedRuntime.stagePath === void 0) throw new Error("muon-builder did not return a staged runtime path.");
|
|
2407
3834
|
const paths = await createRuntimePaths(server, preparedRuntime.stagePath, platform, await resolveProjectConfigPath(server));
|
|
2408
3835
|
await writeLaunchScript(paths, platform);
|
|
2409
3836
|
let cleanupPromise = void 0;
|
|
@@ -2429,7 +3856,7 @@ var startMuonViteBrowserBridge = async ({ server, pluginOptions, platform, archi
|
|
|
2429
3856
|
cleanup();
|
|
2430
3857
|
});
|
|
2431
3858
|
server.httpServer.once("listening", () => {
|
|
2432
|
-
if (writeMuonOverrideConfig(server, paths.overrideConfigPath, pluginOptions
|
|
3859
|
+
if (writeMuonOverrideConfig(server, paths.overrideConfigPath, pluginOptions, getRuntimePluginConfig)) launchMuon(paths, platform, server);
|
|
2433
3860
|
});
|
|
2434
3861
|
};
|
|
2435
3862
|
//#endregion
|
|
@@ -2456,28 +3883,43 @@ var attachMuonVitePluginOptions = (plugin, options) => {
|
|
|
2456
3883
|
};
|
|
2457
3884
|
//#endregion
|
|
2458
3885
|
//#region src/vite.ts
|
|
2459
|
-
var
|
|
3886
|
+
var getErrorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
2460
3887
|
/**
|
|
2461
3888
|
* Creates a Vite plugin that launches Muon during Vite dev startup.
|
|
2462
3889
|
*
|
|
2463
3890
|
* @param options Muon plugin options used for development startup and build.
|
|
2464
3891
|
* @returns Vite plugin instance.
|
|
3892
|
+
* @defaultValue `options` defaults to `{}`.
|
|
2465
3893
|
*/
|
|
2466
3894
|
var muon = (options = {}) => {
|
|
2467
3895
|
let resolvedConfig = void 0;
|
|
3896
|
+
let capabilityResolver = void 0;
|
|
3897
|
+
let resolvedPluginAccess = void 0;
|
|
2468
3898
|
return attachMuonVitePluginOptions({
|
|
2469
3899
|
name: "muon",
|
|
2470
3900
|
config: (config) => {
|
|
2471
3901
|
if (config.server?.watch === null) return null;
|
|
2472
3902
|
return { server: { watch: createMuonWatchOptions(config.server?.watch) } };
|
|
2473
3903
|
},
|
|
2474
|
-
configResolved: (config) => {
|
|
3904
|
+
configResolved: async (config) => {
|
|
2475
3905
|
resolvedConfig = config;
|
|
3906
|
+
resolvedPluginAccess = await resolveMuonPluginAccessOptions({
|
|
3907
|
+
root: config.root,
|
|
3908
|
+
configPath: resolveMuonConfigPathForViteCommand(config, options),
|
|
3909
|
+
pluginAccess: options.pluginAccess,
|
|
3910
|
+
...config.command === "serve" ? { onConfigReadError: (error) => {
|
|
3911
|
+
config.logger.warn(`Muon project config will be ignored because it could not be read or parsed: ${getErrorMessage(error)}`);
|
|
3912
|
+
} } : {}
|
|
3913
|
+
});
|
|
3914
|
+
capabilityResolver = resolvedPluginAccess.mode === "validate" ? createMuonCapabilityModuleResolver(config.root, resolvedPluginAccess.capabilityOptions) : void 0;
|
|
2476
3915
|
},
|
|
3916
|
+
resolveId: (source, importer) => capabilityResolver?.resolveId(source, importer)?.id,
|
|
3917
|
+
load: (id) => capabilityResolver?.load(id),
|
|
2477
3918
|
configureServer: async (server) => {
|
|
2478
3919
|
await startMuonViteBrowserBridge({
|
|
2479
3920
|
server,
|
|
2480
3921
|
pluginOptions: options,
|
|
3922
|
+
getRuntimePluginConfig: () => resolveMuonRuntimePluginConfig(capabilityResolver, resolvedPluginAccess),
|
|
2481
3923
|
platform: process.platform,
|
|
2482
3924
|
architecture: process.arch,
|
|
2483
3925
|
environment: process.env
|
|
@@ -2485,10 +3927,10 @@ var muon = (options = {}) => {
|
|
|
2485
3927
|
},
|
|
2486
3928
|
closeBundle: async () => {
|
|
2487
3929
|
if (resolvedConfig === void 0 || resolvedConfig.command !== "build") return;
|
|
2488
|
-
if (process.env[
|
|
3930
|
+
if (process.env["MUON_SUPPRESS_VITE_MUON_BUILD"] === "1") return;
|
|
2489
3931
|
if (options.build === false) return;
|
|
2490
3932
|
const buildOptions = typeof options.build === "object" ? options.build : {};
|
|
2491
|
-
await buildMuonApp(createMuonBuildOptions(resolvedConfig, buildOptions));
|
|
3933
|
+
await buildMuonApp(createMuonBuildOptions(resolvedConfig, buildOptions, resolveMuonRuntimePluginConfig(capabilityResolver, resolvedPluginAccess)));
|
|
2492
3934
|
}
|
|
2493
3935
|
}, options);
|
|
2494
3936
|
};
|
|
@@ -2505,7 +3947,7 @@ var createMuonWatchOptions = (watch) => ({
|
|
|
2505
3947
|
...watch ?? {},
|
|
2506
3948
|
ignored: mergeMuonWatchIgnored(watch?.ignored)
|
|
2507
3949
|
});
|
|
2508
|
-
var createMuonBuildOptions = (config, buildOptions) => {
|
|
3950
|
+
var createMuonBuildOptions = (config, buildOptions, runtimePluginConfig) => {
|
|
2509
3951
|
const outDir = isAbsolute(config.build.outDir) ? config.build.outDir : resolve(config.root, config.build.outDir);
|
|
2510
3952
|
const options = {
|
|
2511
3953
|
root: config.root,
|
|
@@ -2518,10 +3960,28 @@ var createMuonBuildOptions = (config, buildOptions) => {
|
|
|
2518
3960
|
if (buildOptions.appId !== void 0) options.appId = buildOptions.appId;
|
|
2519
3961
|
if (buildOptions.outputRoot !== void 0) options.outputRoot = buildOptions.outputRoot;
|
|
2520
3962
|
if (buildOptions.configPath !== void 0) options.configPath = buildOptions.configPath;
|
|
3963
|
+
if (buildOptions.iconPath !== void 0) options.iconPath = buildOptions.iconPath;
|
|
3964
|
+
if (buildOptions.windowsResource !== void 0) options.windowsResource = buildOptions.windowsResource;
|
|
3965
|
+
if (buildOptions.linuxDesktop !== void 0) options.linuxDesktop = buildOptions.linuxDesktop;
|
|
2521
3966
|
if (buildOptions.packageDirectory !== void 0) options.packageDirectory = buildOptions.packageDirectory;
|
|
2522
3967
|
if (buildOptions.assetSalt !== void 0) options.assetSalt = buildOptions.assetSalt;
|
|
3968
|
+
options.runtimePluginConfig = runtimePluginConfig;
|
|
2523
3969
|
return options;
|
|
2524
3970
|
};
|
|
3971
|
+
var resolveMuonConfigPathForViteCommand = (config, options) => {
|
|
3972
|
+
if (config.command !== "build" || typeof options.build !== "object") return;
|
|
3973
|
+
return options.build.configPath;
|
|
3974
|
+
};
|
|
3975
|
+
var resolveMuonRuntimePluginConfig = (capabilityResolver, pluginAccess) => pluginAccess?.mode === "simple" ? {
|
|
3976
|
+
mode: "simple",
|
|
3977
|
+
...pluginAccess.runtimeOverlay
|
|
3978
|
+
} : {
|
|
3979
|
+
...capabilityResolver?.getRuntimePluginConfig() ?? {
|
|
3980
|
+
mode: "validate",
|
|
3981
|
+
capabilities: []
|
|
3982
|
+
},
|
|
3983
|
+
...pluginAccess?.runtimeOverlay ?? {}
|
|
3984
|
+
};
|
|
2525
3985
|
//#endregion
|
|
2526
3986
|
export { muon as default };
|
|
2527
3987
|
|