muon-ui 0.1.0 → 0.3.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.
Files changed (43) hide show
  1. package/README.md +1 -1
  2. package/dist/{build-CCuZpajl.cjs → build-xwfaxosJ.cjs} +121 -28
  3. package/dist/build-xwfaxosJ.cjs.map +1 -0
  4. package/dist/cli.cjs +19 -10
  5. package/dist/cli.cjs.map +1 -1
  6. package/dist/index.cjs +2 -2
  7. package/dist/index.mjs +2 -2
  8. package/dist/native/linux64/muon-bootstrap +0 -0
  9. package/dist/native/linux64/muon-prepare +0 -0
  10. package/dist/native/linuxarm/muon-bootstrap +0 -0
  11. package/dist/native/linuxarm/muon-prepare +0 -0
  12. package/dist/native/linuxarm64/muon-bootstrap +0 -0
  13. package/dist/native/linuxarm64/muon-prepare +0 -0
  14. package/dist/native/windows32/muon-bootstrap.exe +0 -0
  15. package/dist/native/windows32/muon-prepare.exe +0 -0
  16. package/dist/native/windows64/muon-bootstrap.exe +0 -0
  17. package/dist/native/windows64/muon-prepare.exe +0 -0
  18. package/dist/runtime/{linuxarm64/THIRD_PARTY_NOTICES.md → linux64/LICENSE_muon} +156 -16
  19. package/dist/runtime/linux64/libmuon-ui.so +0 -0
  20. package/dist/runtime/linux64/muon-core +0 -0
  21. package/dist/runtime/{windows32/THIRD_PARTY_NOTICES.md → linuxarm/LICENSE_muon} +156 -16
  22. package/dist/runtime/linuxarm/libmuon-ui.so +0 -0
  23. package/dist/runtime/linuxarm/muon-core +0 -0
  24. package/dist/runtime/{linux64/THIRD_PARTY_NOTICES.md → linuxarm64/LICENSE_muon} +156 -16
  25. package/dist/runtime/linuxarm64/libmuon-ui.so +0 -0
  26. package/dist/runtime/linuxarm64/muon-core +0 -0
  27. package/dist/runtime/{linuxarm/THIRD_PARTY_NOTICES.md → windows32/LICENSE_muon} +156 -16
  28. package/dist/runtime/windows32/libcardio.dll +0 -0
  29. package/dist/runtime/windows32/libmuon-ui.dll +0 -0
  30. package/dist/runtime/windows32/muon-core.exe +0 -0
  31. package/dist/runtime/windows64/LICENSE_muon +363 -0
  32. package/dist/runtime/windows64/libcardio.dll +0 -0
  33. package/dist/runtime/windows64/libmuon-ui.dll +0 -0
  34. package/dist/runtime/windows64/muon-core.exe +0 -0
  35. package/dist/vite.cjs +90 -41
  36. package/dist/vite.cjs.map +1 -1
  37. package/dist/vite.mjs +197 -72
  38. package/dist/vite.mjs.map +1 -1
  39. package/muon.d.ts +17 -0
  40. package/package.json +8 -8
  41. package/vite.d.ts +18 -2
  42. package/dist/build-CCuZpajl.cjs.map +0 -1
  43. package/dist/runtime/windows64/THIRD_PARTY_NOTICES.md +0 -223
package/dist/vite.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  /*!
2
2
  * name: muon-ui
3
- * version: 0.1.0
3
+ * version: 0.3.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: 35f9658786642562593735d3b589c1d7ab270ecf
8
+ * git.commit.hash: e3c2c87d50dddb0bf6c2b4d5ebf0a37ab2d3b433
9
9
  */
10
10
  import { basename, dirname, isAbsolute, join, relative, resolve, sep, win32 } from "node:path";
11
11
  import { constants, writeFileSync } from "node:fs";
@@ -1292,7 +1292,7 @@ var encodeTaggedBytes = (tag, bytes) => Buffer.concat([
1292
1292
  encodeVarUint(BigInt(bytes.length)),
1293
1293
  Buffer.from(bytes)
1294
1294
  ]);
1295
- var isJsonObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1295
+ var isJsonObject$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1296
1296
  var isPath = (path, first, second) => path.length === 2 && path[0] === first && path[1] === second;
1297
1297
  var isHexString = (value) => value.length % 2 === 0 && /^[0-9a-fA-F]*$/.test(value);
1298
1298
  var decodeHexString = (value) => {
@@ -1325,7 +1325,7 @@ var encodeTlvValue = (value, path) => {
1325
1325
  encodeVarUint(BigInt(value.length)),
1326
1326
  ...value.map((entry) => encodeTlvValue(entry, path))
1327
1327
  ]);
1328
- if (isJsonObject$1(value)) {
1328
+ if (isJsonObject$2(value)) {
1329
1329
  const entries = Object.entries(value);
1330
1330
  return Buffer.concat([
1331
1331
  Buffer.from([tlvObjectTag]),
@@ -1439,7 +1439,7 @@ var findMuonBootstrapEmbeddedConfigSlot = (content) => {
1439
1439
  if (candidate === void 0) throw new Error("Embedded muon-bootstrap config slot was not found.");
1440
1440
  return candidate;
1441
1441
  };
1442
- var fileExists$1 = async (path) => {
1442
+ var fileExists$2 = async (path) => {
1443
1443
  try {
1444
1444
  return (await stat(path)).isFile();
1445
1445
  } catch {
@@ -1455,10 +1455,10 @@ var resolveMuonConfigInputPath = async (configPath) => {
1455
1455
  "muon.json"
1456
1456
  ]) {
1457
1457
  const candidate = join(directory, fileName);
1458
- if (await fileExists$1(candidate)) return candidate;
1458
+ if (await fileExists$2(candidate)) return candidate;
1459
1459
  }
1460
1460
  }
1461
- if (!await fileExists$1(configPath)) throw new Error(`muon config does not exist: ${configPath}`);
1461
+ if (!await fileExists$2(configPath)) throw new Error(`muon config does not exist: ${configPath}`);
1462
1462
  return configPath;
1463
1463
  };
1464
1464
  var readMuonConfigInput = async (configPath) => {
@@ -1769,8 +1769,9 @@ var defaultConfigFileNames = [
1769
1769
  "muon.jsonc",
1770
1770
  "muon.json"
1771
1771
  ];
1772
- var appConfigFromPath = "./assets.zip";
1772
+ var appConfigSourcePath = "./assets.zip";
1773
1773
  var defaultAppName = "muon-app";
1774
+ var muonLicenseFileName = "LICENSE_muon";
1774
1775
  var directoryMode = 493;
1775
1776
  var executableMode = 493;
1776
1777
  var assetSaltByteLength = 16;
@@ -1800,8 +1801,8 @@ var buildMuonApp = async (options = {}) => {
1800
1801
  const targets = resolveBuildTargets(options);
1801
1802
  const outputRoot = resolve(root, options.outputRoot ?? ".");
1802
1803
  const appName = await resolveAppName(root, options.appName);
1803
- const assetInput = resolveAssetInput(root, options.assetSourcePath, options.assetPrefix);
1804
- const sourceConfig = await readBuildConfig(root, options.configPath);
1804
+ const buildConfig = await readBuildConfig(root, options.configPath);
1805
+ const assetInput = resolveAssetInput(root, options.assetSourcePath, options.assetPrefix, buildConfig);
1805
1806
  const salt = Buffer.from(options.assetSalt ?? randomBytes(assetSaltByteLength));
1806
1807
  const results = [];
1807
1808
  for (const target of targets) {
@@ -1811,7 +1812,7 @@ var buildMuonApp = async (options = {}) => {
1811
1812
  appName,
1812
1813
  target,
1813
1814
  assetInput,
1814
- sourceConfig,
1815
+ sourceConfig: buildConfig.config,
1815
1816
  salt
1816
1817
  });
1817
1818
  results.push(result);
@@ -1831,9 +1832,10 @@ var resolveBuildTargets = (options) => {
1831
1832
  if (options.targets !== void 0 && options.targets.length > 0) return [...new Set(options.targets.map((target) => normalizeMuonBuildTarget(target)))];
1832
1833
  return [getDefaultMuonBuildTarget()];
1833
1834
  };
1834
- var resolveAssetInput = (root, assetSourcePath, assetPrefix) => {
1835
+ var resolveAssetInput = (root, assetSourcePath, assetPrefix, buildConfig) => {
1836
+ const configuredAssetSourcePath = assetSourcePath === void 0 ? readConfigAssetSourcePath(buildConfig.config) : void 0;
1835
1837
  return {
1836
- sourcePath: resolve(root, assetSourcePath ?? "assets"),
1838
+ sourcePath: assetSourcePath !== void 0 ? resolve(root, assetSourcePath) : configuredAssetSourcePath !== void 0 ? resolve(buildConfig.directory, configuredAssetSourcePath) : resolve(root, "assets"),
1837
1839
  prefix: normalizeZipPrefix(assetPrefix ?? "")
1838
1840
  };
1839
1841
  };
@@ -1843,7 +1845,7 @@ var normalizeZipPrefix = (prefix) => {
1843
1845
  };
1844
1846
  var resolveAppName = async (root, appName) => {
1845
1847
  if (appName !== void 0) return sanitizeAppName(appName);
1846
- const packageJson = await readJsonObjectFile(join(root, "package.json"));
1848
+ const packageJson = await readJsonObjectFile(join(root, "package.json"), "package.json");
1847
1849
  const packageName = typeof packageJson.name === "string" ? packageJson.name : defaultAppName;
1848
1850
  return sanitizeAppName(packageName.startsWith("@") ? packageName.slice(packageName.indexOf("/") + 1) : packageName);
1849
1851
  };
@@ -1853,23 +1855,49 @@ var sanitizeAppName = (name) => {
1853
1855
  };
1854
1856
  var readBuildConfig = async (root, configPath) => {
1855
1857
  const resolvedConfigPath = await resolveConfigPath(root, configPath);
1856
- if (resolvedConfigPath === void 0) return {};
1857
- return readJsonObjectFile(resolvedConfigPath);
1858
+ if (resolvedConfigPath === void 0) return {
1859
+ config: {},
1860
+ directory: root
1861
+ };
1862
+ return {
1863
+ config: await readJsonObjectFile(resolvedConfigPath, "Muon config file"),
1864
+ directory: dirname(resolvedConfigPath)
1865
+ };
1866
+ };
1867
+ var readConfigAssetSourcePath = (sourceConfig) => {
1868
+ const sourceAsset = sourceConfig.asset;
1869
+ if (sourceAsset === void 0) return;
1870
+ if (!isJsonObject$1(sourceAsset)) throw new Error("muon.json asset must be an object when present.");
1871
+ const sourceAssetPath = sourceAsset.sourcePath;
1872
+ if (sourceAssetPath === void 0) return;
1873
+ if (typeof sourceAssetPath !== "string") throw new Error("muon.json asset.sourcePath must be a string when present.");
1874
+ return sourceAssetPath;
1858
1875
  };
1859
1876
  var resolveConfigPath = async (root, configPath) => {
1860
1877
  if (configPath !== void 0) {
1861
1878
  const resolvedPath = resolve(root, configPath);
1862
- if (await fileExists(resolvedPath)) return resolvedPath;
1879
+ if (await fileExists$1(resolvedPath)) return resolvedPath;
1863
1880
  throw new Error(`Muon config file does not exist: ${resolvedPath}`);
1864
1881
  }
1865
1882
  for (const fileName of defaultConfigFileNames) {
1866
1883
  const candidatePath = join(root, fileName);
1867
- if (await fileExists(candidatePath)) return candidatePath;
1884
+ if (await fileExists$1(candidatePath)) return candidatePath;
1868
1885
  }
1869
1886
  };
1870
- var readJsonObjectFile = async (filePath) => {
1871
- const parsed = (0, import_dist.parse)(await readFile(filePath, "utf8"));
1872
- if (!isJsonObject(parsed)) throw new Error(`Expected JSON object in ${filePath}`);
1887
+ var readJsonObjectFile = async (filePath, label) => {
1888
+ let content;
1889
+ try {
1890
+ content = await readFile(filePath, "utf8");
1891
+ } catch (error) {
1892
+ throw new Error(`${label} could not be read: ${filePath}: ${getErrorMessage$1(error)}`);
1893
+ }
1894
+ let parsed;
1895
+ try {
1896
+ parsed = (0, import_dist.parse)(content);
1897
+ } catch (error) {
1898
+ throw new Error(`${label} could not be parsed: ${filePath}: ${getErrorMessage$1(error)}`);
1899
+ }
1900
+ if (!isJsonObject$1(parsed)) throw new Error(`${label} must contain a JSON object: ${filePath}`);
1873
1901
  return parsed;
1874
1902
  };
1875
1903
  var buildMuonTarget = async (input) => {
@@ -1924,7 +1952,7 @@ var verifyTargetInputs = async (input) => {
1924
1952
  await assertDirectory(input.sourceRuntimePath, `Muon runtime for ${input.target}`);
1925
1953
  await assertFile(input.sourceBootstrapPath, `Muon bootstrap for ${input.target}`);
1926
1954
  for (const fileName of input.descriptor.runtimeFiles) await assertFile(join(input.sourceRuntimePath, fileName), `Muon runtime file ${fileName} for ${input.target}`);
1927
- await assertFile(join(input.sourceRuntimePath, "THIRD_PARTY_NOTICES.md"), `Muon third-party notices for ${input.target}`);
1955
+ await assertFile(join(input.sourceRuntimePath, muonLicenseFileName), `Muon license file for ${input.target}`);
1928
1956
  };
1929
1957
  var getLauncherFileName = (appName, descriptor) => {
1930
1958
  if (descriptor.launcherExtension.length > 0 && !appName.endsWith(descriptor.launcherExtension)) return `${appName}${descriptor.launcherExtension}`;
@@ -1932,21 +1960,33 @@ var getLauncherFileName = (appName, descriptor) => {
1932
1960
  };
1933
1961
  var copyRuntimeFiles = async (sourceRuntimePath, outputPath, descriptor) => {
1934
1962
  for (const fileName of descriptor.runtimeFiles) await copyFile(join(sourceRuntimePath, fileName), join(outputPath, fileName));
1935
- await copyFile(join(sourceRuntimePath, "THIRD_PARTY_NOTICES.md"), join(outputPath, "THIRD_PARTY_NOTICES.md"));
1963
+ await copyFile(join(sourceRuntimePath, muonLicenseFileName), join(outputPath, muonLicenseFileName));
1936
1964
  };
1937
1965
  var writeAssetArchive = async (input, outputPath, salt) => {
1938
- await assertDirectory(input.sourcePath, "Muon asset source");
1939
- const entries = await collectZipEntries(input.sourcePath, input.prefix);
1940
- if (entries.length === 0) throw new Error(`Muon asset source has no files: ${input.sourcePath}`);
1941
- const archive = createZipArchive(entries);
1966
+ const sourceStats = await statOrUndefined(input.sourcePath);
1967
+ if (sourceStats === void 0) throw new Error(`Muon asset source does not exist: ${input.sourcePath}`);
1968
+ const archive = sourceStats.isDirectory() ? await createAssetArchiveFromDirectory(input) : sourceStats.isFile() ? await readFile(input.sourcePath) : void 0;
1969
+ if (archive === void 0) throw new Error(`Muon asset source is not a directory or file: ${input.sourcePath}`);
1942
1970
  await writeFile(outputPath, archive);
1943
1971
  return {
1944
1972
  path: outputPath,
1945
1973
  signature: createHash("sha1").update(archive).update(salt).digest("hex"),
1946
1974
  salt: salt.toString("hex"),
1947
- entryCount: entries.length
1975
+ entryCount: sourceStats.isDirectory() ? readZipEntryCount(archive, outputPath) : readZipEntryCount(archive, input.sourcePath)
1948
1976
  };
1949
1977
  };
1978
+ var createAssetArchiveFromDirectory = async (input) => {
1979
+ const entries = await collectZipEntries(input.sourcePath, input.prefix);
1980
+ if (entries.length === 0) throw new Error(`Muon asset source has no files: ${input.sourcePath}`);
1981
+ return createZipArchive(entries);
1982
+ };
1983
+ var readZipEntryCount = (archive, sourcePath) => {
1984
+ const endSignature = 101010256;
1985
+ const lastPossibleOffset = archive.length - 22;
1986
+ const firstPossibleOffset = Math.max(0, lastPossibleOffset - 65535);
1987
+ for (let offset = lastPossibleOffset; offset >= firstPossibleOffset; offset -= 1) if (archive.readUInt32LE(offset) === endSignature) return archive.readUInt16LE(offset + 10);
1988
+ throw new Error(`Muon asset ZIP could not be read: ${sourcePath}`);
1989
+ };
1950
1990
  var collectZipEntries = async (sourcePath, prefix) => {
1951
1991
  const entries = [];
1952
1992
  const walk = async (directoryPath) => {
@@ -1978,12 +2018,12 @@ var createZipArchive = (entries) => {
1978
2018
  };
1979
2019
  var createEmbeddedConfig = (sourceConfig, asset) => {
1980
2020
  const sourceAsset = sourceConfig.asset;
1981
- if (sourceAsset !== void 0 && !isJsonObject(sourceAsset)) throw new Error("muon.json asset must be an object when present.");
2021
+ if (sourceAsset !== void 0 && !isJsonObject$1(sourceAsset)) throw new Error("muon.json asset must be an object when present.");
1982
2022
  return {
1983
2023
  ...sourceConfig,
1984
2024
  asset: {
1985
2025
  ...sourceAsset ?? {},
1986
- from: appConfigFromPath,
2026
+ sourcePath: appConfigSourcePath,
1987
2027
  signature: asset.signature,
1988
2028
  salt: asset.salt
1989
2029
  }
@@ -2003,14 +2043,21 @@ var withTemporaryConfig = async (config, callback) => {
2003
2043
  }
2004
2044
  };
2005
2045
  var assertDirectory = async (path, label) => {
2006
- const stats = await stat(path).catch(() => void 0);
2046
+ const stats = await statOrUndefined(path);
2007
2047
  if (stats === void 0 || !stats.isDirectory()) throw new Error(`${label} directory does not exist: ${path}`);
2008
2048
  };
2009
2049
  var assertFile = async (path, label) => {
2010
- const stats = await stat(path).catch(() => void 0);
2050
+ const stats = await statOrUndefined(path);
2011
2051
  if (stats === void 0 || !stats.isFile()) throw new Error(`${label} file does not exist: ${path}`);
2012
2052
  };
2013
- var fileExists = async (path) => {
2053
+ var statOrUndefined = async (path) => {
2054
+ try {
2055
+ return await stat(path);
2056
+ } catch {
2057
+ return;
2058
+ }
2059
+ };
2060
+ var fileExists$1 = async (path) => {
2014
2061
  try {
2015
2062
  await access(path, constants.F_OK);
2016
2063
  return true;
@@ -2018,17 +2065,49 @@ var fileExists = async (path) => {
2018
2065
  return false;
2019
2066
  }
2020
2067
  };
2021
- var isJsonObject = (value) => {
2068
+ var isJsonObject$1 = (value) => {
2022
2069
  return typeof value === "object" && value !== null && !Array.isArray(value);
2023
2070
  };
2071
+ var getErrorMessage$1 = (error) => error instanceof Error ? error.message : String(error);
2024
2072
  //#endregion
2025
- //#region src/vite-internals.ts
2026
- var getServerOpenValue = (server) => {
2027
- const open = server.config.server.open;
2028
- return open === true || typeof open === "string" ? open : false;
2073
+ //#region src/gitignore.ts
2074
+ var muonGitignoreEntry = ".muon/";
2075
+ var isMissingFileError = (error) => error instanceof Error && error.code === "ENOENT";
2076
+ var hasMuonGitignoreEntry = (content) => content.split(/\r?\n/).map((line) => line.trim()).some((line) => line === muonGitignoreEntry || line === `/${muonGitignoreEntry}` || line === ".muon" || line === "/.muon");
2077
+ /**
2078
+ * Adds the Muon staging directory to a project .gitignore file.
2079
+ *
2080
+ * @param root Project root containing the .gitignore file.
2081
+ * @returns Gitignore update result.
2082
+ */
2083
+ var ensureMuonGitignoreEntry = async (root) => {
2084
+ const gitignorePath = join(root, ".gitignore");
2085
+ let content = "";
2086
+ try {
2087
+ content = await readFile(gitignorePath, "utf8");
2088
+ } catch (error) {
2089
+ if (!isMissingFileError(error)) throw error;
2090
+ }
2091
+ if (hasMuonGitignoreEntry(content)) return {
2092
+ gitignorePath,
2093
+ changed: false
2094
+ };
2095
+ const separator = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
2096
+ await writeFile(gitignorePath, `${content}${separator}${muonGitignoreEntry}\n`);
2097
+ return {
2098
+ gitignorePath,
2099
+ changed: true
2100
+ };
2029
2101
  };
2102
+ //#endregion
2103
+ //#region src/vite-internals.ts
2030
2104
  var resolveFromRoot = (root, path) => isAbsolute(path) ? path : resolve(root, path);
2031
2105
  var moduleDirectory = typeof __dirname === "string" ? __dirname : dirname(fileURLToPath(import.meta.url));
2106
+ var defaultProjectConfigFileNames = [
2107
+ "muon.json5",
2108
+ "muon.jsonc",
2109
+ "muon.json"
2110
+ ];
2032
2111
  /**
2033
2112
  * Resolves the muon-core runtime directory used by the Vite plugin.
2034
2113
  *
@@ -2040,16 +2119,21 @@ var getMuonExecutablePath = (runtimePath, platform) => join(runtimePath, platfor
2040
2119
  var getLaunchScriptFileName = (platform) => platform === "win32" ? "open-muon.cmd" : "open-muon.sh";
2041
2120
  var quotePosix = (value) => `'${value.replaceAll("'", "'\\''")}'`;
2042
2121
  var getPlatformDirectoryName = (path, platform) => platform === "win32" ? win32.dirname(path) : dirname(path);
2122
+ var getOptionalPosixValue = (value) => value === void 0 ? "''" : quotePosix(value);
2043
2123
  var createPosixMuonLaunchScript = ({ muonExecutablePath, projectConfigPath, overrideConfigPath }) => `#!/usr/bin/env bash
2044
2124
  set -euo pipefail
2045
2125
  MUON_EXECUTABLE=${quotePosix(muonExecutablePath)}
2046
2126
  MUON_EXECUTABLE_DIRECTORY=${quotePosix(getPlatformDirectoryName(muonExecutablePath, "linux"))}
2047
- MUON_PROJECT_CONFIG=${quotePosix(projectConfigPath)}
2127
+ MUON_PROJECT_CONFIG=${getOptionalPosixValue(projectConfigPath)}
2048
2128
  MUON_OVERRIDE_CONFIG=${quotePosix(overrideConfigPath)}
2049
2129
 
2050
- if [[ ! -f "$MUON_PROJECT_CONFIG" ]]; then
2051
- echo "Muon startup failed: project config does not exist: $MUON_PROJECT_CONFIG" >&2
2052
- exit 1
2130
+ MUON_CONFIG_ARGS=()
2131
+ if [[ -n "$MUON_PROJECT_CONFIG" ]]; then
2132
+ if [[ ! -f "$MUON_PROJECT_CONFIG" ]]; then
2133
+ echo "Muon startup failed: project config does not exist: $MUON_PROJECT_CONFIG" >&2
2134
+ exit 1
2135
+ fi
2136
+ MUON_CONFIG_ARGS+=("-c" "$MUON_PROJECT_CONFIG")
2053
2137
  fi
2054
2138
 
2055
2139
  if [[ ! -x "$MUON_EXECUTABLE" ]]; then
@@ -2061,23 +2145,20 @@ if [[ ! -f "$MUON_OVERRIDE_CONFIG" ]]; then
2061
2145
  echo "Muon startup failed: generated override config does not exist: $MUON_OVERRIDE_CONFIG" >&2
2062
2146
  exit 1
2063
2147
  fi
2148
+ MUON_CONFIG_ARGS+=("-c" "$MUON_OVERRIDE_CONFIG")
2064
2149
 
2065
2150
  cd "$MUON_EXECUTABLE_DIRECTORY"
2066
- exec "$MUON_EXECUTABLE" -c "$MUON_PROJECT_CONFIG" -c "$MUON_OVERRIDE_CONFIG"
2151
+ exec "$MUON_EXECUTABLE" "\${MUON_CONFIG_ARGS[@]}"
2067
2152
  `;
2068
2153
  var escapeWindowsCmdValue = (value) => value.replaceAll("%", "%%").replaceAll("\r", "").replaceAll("\n", "");
2154
+ var getOptionalWindowsCmdValue = (value) => value === void 0 ? "" : escapeWindowsCmdValue(value);
2069
2155
  var createWindowsMuonLaunchScript = ({ muonExecutablePath, projectConfigPath, overrideConfigPath }) => `@echo off
2070
2156
  setlocal
2071
2157
  set "MUON_EXECUTABLE=${escapeWindowsCmdValue(muonExecutablePath)}"
2072
2158
  set "MUON_EXECUTABLE_DIRECTORY=${escapeWindowsCmdValue(getPlatformDirectoryName(muonExecutablePath, "win32"))}"
2073
- set "MUON_PROJECT_CONFIG=${escapeWindowsCmdValue(projectConfigPath)}"
2159
+ set "MUON_PROJECT_CONFIG=${getOptionalWindowsCmdValue(projectConfigPath)}"
2074
2160
  set "MUON_OVERRIDE_CONFIG=${escapeWindowsCmdValue(overrideConfigPath)}"
2075
2161
 
2076
- if not exist "%MUON_PROJECT_CONFIG%" (
2077
- echo Muon startup failed: project config does not exist: %MUON_PROJECT_CONFIG% 1>&2
2078
- exit /b 1
2079
- )
2080
-
2081
2162
  if not exist "%MUON_EXECUTABLE%" (
2082
2163
  echo Muon startup failed: executable does not exist: %MUON_EXECUTABLE% 1>&2
2083
2164
  exit /b 1
@@ -2089,52 +2170,83 @@ if not exist "%MUON_OVERRIDE_CONFIG%" (
2089
2170
  )
2090
2171
 
2091
2172
  pushd "%MUON_EXECUTABLE_DIRECTORY%"
2092
- "%MUON_EXECUTABLE%" -c "%MUON_PROJECT_CONFIG%" -c "%MUON_OVERRIDE_CONFIG%"
2173
+ if defined MUON_PROJECT_CONFIG (
2174
+ if not exist "%MUON_PROJECT_CONFIG%" (
2175
+ echo Muon startup failed: project config does not exist: %MUON_PROJECT_CONFIG% 1>&2
2176
+ popd
2177
+ exit /b 1
2178
+ )
2179
+ "%MUON_EXECUTABLE%" -c "%MUON_PROJECT_CONFIG%" -c "%MUON_OVERRIDE_CONFIG%"
2180
+ ) else (
2181
+ "%MUON_EXECUTABLE%" -c "%MUON_OVERRIDE_CONFIG%"
2182
+ )
2093
2183
  set "MUON_EXIT_CODE=%ERRORLEVEL%"
2094
2184
  popd
2095
2185
  exit /b %MUON_EXIT_CODE%
2096
2186
  `;
2097
2187
  var createMuonLaunchScript = (options) => options.platform === "win32" ? createWindowsMuonLaunchScript(options) : createPosixMuonLaunchScript(options);
2098
2188
  var getBaseUrl = (server) => server.resolvedUrls?.local[0] ?? server.resolvedUrls?.network[0];
2099
- var getStartUrl = (server, openValue) => {
2100
- const baseUrl = getBaseUrl(server);
2101
- if (baseUrl === void 0) return;
2102
- return typeof openValue === "string" ? new URL(openValue, baseUrl).href : baseUrl;
2103
- };
2104
2189
  var getWebSocketOrigin = (startUrl) => {
2105
2190
  const url = new URL(startUrl);
2106
2191
  if (url.protocol === "https:") url.protocol = "wss:";
2107
2192
  else if (url.protocol === "http:") url.protocol = "ws:";
2108
2193
  return url.origin;
2109
2194
  };
2110
- var createMuonOverrideConfig = (startUrl) => {
2195
+ var createMuonOverrideConfig = (startUrl, enableDebugger) => {
2111
2196
  const origin = new URL(startUrl).origin;
2112
2197
  return {
2198
+ ...enableDebugger ? { cdp: { enable: true } } : {},
2113
2199
  browser: {
2114
2200
  startPage: startUrl,
2201
+ ...enableDebugger ? { keybind: { devtools: "f12" } } : {},
2115
2202
  plugin: { allow: [`${origin}/**`] }
2116
2203
  },
2117
2204
  network: { allow: [`${origin}/**`, `${getWebSocketOrigin(startUrl)}/**`] }
2118
2205
  };
2119
2206
  };
2120
- var writeMuonOverrideConfig = (server, openValue, overrideConfigPath) => {
2121
- const startUrl = getStartUrl(server, openValue);
2207
+ var writeMuonOverrideConfig = (server, overrideConfigPath, enableDebugger) => {
2208
+ const startUrl = getBaseUrl(server);
2122
2209
  if (startUrl === void 0) {
2123
2210
  server.config.logger.warn("Muon Vite plugin could not resolve a Vite URL.");
2124
- return;
2211
+ return false;
2125
2212
  }
2126
- writeFileSync(overrideConfigPath, `${JSON.stringify(createMuonOverrideConfig(startUrl), null, 2)}\n`);
2213
+ writeFileSync(overrideConfigPath, `${JSON.stringify(createMuonOverrideConfig(startUrl, enableDebugger), null, 2)}\n`);
2214
+ return true;
2127
2215
  };
2128
- var createRuntimePaths = async (server, stagePath, platform) => {
2216
+ var createRuntimePaths = async (server, stagePath, platform, projectConfigPath) => {
2129
2217
  const temporaryDirectory = await mkdtemp(join(tmpdir(), "muon-vite-"));
2130
2218
  return {
2131
2219
  temporaryDirectory,
2132
2220
  launchScriptPath: join(temporaryDirectory, getLaunchScriptFileName(platform)),
2133
2221
  overrideConfigPath: join(temporaryDirectory, "muon.vite.json"),
2134
- projectConfigPath: join(server.config.root, "muon.json"),
2222
+ projectConfigPath,
2135
2223
  muonExecutablePath: getMuonExecutablePath(stagePath, platform)
2136
2224
  };
2137
2225
  };
2226
+ var fileExists = async (path) => {
2227
+ try {
2228
+ await access(path, constants.F_OK);
2229
+ return true;
2230
+ } catch {
2231
+ return false;
2232
+ }
2233
+ };
2234
+ var isJsonObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2235
+ var getErrorMessage = (error) => error instanceof Error ? error.message : String(error);
2236
+ var resolveProjectConfigPath = async (server) => {
2237
+ for (const fileName of defaultProjectConfigFileNames) {
2238
+ const candidatePath = join(server.config.root, fileName);
2239
+ if (!await fileExists(candidatePath)) continue;
2240
+ try {
2241
+ if (!isJsonObject((0, import_dist.parse)(await readFile(candidatePath, "utf8")))) throw new Error("muon config root must be an object");
2242
+ return candidatePath;
2243
+ } catch (error) {
2244
+ server.config.logger.warn(`Muon project config will be ignored because it could not be read or parsed: ${candidatePath}: ${getErrorMessage(error)}`);
2245
+ return;
2246
+ }
2247
+ }
2248
+ server.config.logger.warn(`Muon project config was not found in ${server.config.root}; launching with generated Vite config only.`);
2249
+ };
2138
2250
  var writeLaunchScript = async (paths, platform) => {
2139
2251
  await writeFile(paths.launchScriptPath, createMuonLaunchScript({
2140
2252
  muonExecutablePath: paths.muonExecutablePath,
@@ -2144,9 +2256,26 @@ var writeLaunchScript = async (paths, platform) => {
2144
2256
  }));
2145
2257
  if (platform !== "win32") await chmod(paths.launchScriptPath, 448);
2146
2258
  };
2259
+ var quoteWindowsCommandArgument = (value) => `"${value.replaceAll("\"", "\\\"")}"`;
2260
+ var launchMuon = (paths, platform, server) => {
2261
+ const child = spawn(platform === "win32" ? "cmd.exe" : paths.launchScriptPath, platform === "win32" ? [
2262
+ "/d",
2263
+ "/s",
2264
+ "/c",
2265
+ quoteWindowsCommandArgument(paths.launchScriptPath)
2266
+ ] : [], {
2267
+ detached: true,
2268
+ stdio: "ignore",
2269
+ windowsHide: false
2270
+ });
2271
+ child.once("error", (error) => {
2272
+ server.config.logger.warn(`Muon startup failed: ${getErrorMessage(error)}`);
2273
+ });
2274
+ child.unref();
2275
+ };
2147
2276
  var startMuonViteBrowserBridge = async ({ server, pluginOptions, platform, architecture, environment }) => {
2148
- const openValue = getServerOpenValue(server);
2149
- if (openValue === false || server.httpServer === null) return;
2277
+ await ensureMuonGitignoreEntry(server.config.root);
2278
+ if (pluginOptions.open === false || server.httpServer === null) return;
2150
2279
  const target = getDefaultMuonPrepareTarget(platform, architecture);
2151
2280
  const preparedRuntime = await runMuonPrepare({
2152
2281
  muonPath: resolveMuonRuntimePath({
@@ -2165,16 +2294,12 @@ var startMuonViteBrowserBridge = async ({ server, pluginOptions, platform, archi
2165
2294
  cwd: server.config.root
2166
2295
  });
2167
2296
  if (preparedRuntime.stagePath === void 0) throw new Error("muon-prepare did not return a staged runtime path.");
2168
- const paths = await createRuntimePaths(server, preparedRuntime.stagePath, platform);
2297
+ const paths = await createRuntimePaths(server, preparedRuntime.stagePath, platform, await resolveProjectConfigPath(server));
2169
2298
  await writeLaunchScript(paths, platform);
2170
- const previousBrowser = environment.BROWSER;
2171
- environment.BROWSER = paths.launchScriptPath;
2172
2299
  let cleanupPromise = void 0;
2173
2300
  const cleanup = async () => {
2174
2301
  if (cleanupPromise !== void 0) return cleanupPromise;
2175
2302
  cleanupPromise = (async () => {
2176
- if (previousBrowser === void 0) delete environment.BROWSER;
2177
- else environment.BROWSER = previousBrowser;
2178
2303
  await rm(paths.temporaryDirectory, {
2179
2304
  recursive: true,
2180
2305
  force: true
@@ -2194,15 +2319,15 @@ var startMuonViteBrowserBridge = async ({ server, pluginOptions, platform, archi
2194
2319
  cleanup();
2195
2320
  });
2196
2321
  server.httpServer.once("listening", () => {
2197
- writeMuonOverrideConfig(server, openValue, paths.overrideConfigPath);
2322
+ if (writeMuonOverrideConfig(server, paths.overrideConfigPath, pluginOptions.enableDebugger !== false)) launchMuon(paths, platform, server);
2198
2323
  });
2199
2324
  };
2200
2325
  //#endregion
2201
2326
  //#region src/vite.ts
2202
2327
  /**
2203
- * Creates a Vite plugin that launches Muon through Vite's server.open flow.
2328
+ * Creates a Vite plugin that launches Muon during Vite dev startup.
2204
2329
  *
2205
- * @param options Muon runtime location used for development startup.
2330
+ * @param options Muon plugin options used for development startup and build.
2206
2331
  * @returns Vite plugin instance.
2207
2332
  */
2208
2333
  var muon = (options = {}) => {