UNPKG

@deskthing/cli

Version:
1,213 lines (1,180 loc) 40.9 kB
#!/usr/bin/env node var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // package.json var require_package = __commonJS({ "package.json"(exports, module) { module.exports = { name: "@deskthing/cli", version: "0.11.12", description: "An emulator for the DeskThing Server", keywords: [ "Emulator", "DeskThing", "Development", "App", "Development", "DeskThing", "Development" ], exports: { ".": { import: "./dist/index.js", types: "./dist/index.d.ts" } }, homepage: "https://github.com/itsriprod/deskthing-emulator#readme", bugs: { url: "https://github.com/itsriprod/deskthing-emulator/issues" }, repository: { type: "git", url: "git+https://github.com/itsriprod/deskthing-emulator.git" }, license: "ISC", author: "Riprod", type: "module", main: "./dist/index.js", bin: { deskthing: "dist/cli.js" }, scripts: { build: "npm run build:emulator-client && npm run build:emulator && npm run build:cli && npm run build:emulator-thread && npm run build:index", "build:cli": "esbuild src/cli.ts --bundle --platform=node --outdir=dist --format=esm --packages=external", "build:emulator": "esbuild src/emulator/index.ts --bundle --platform=node --outdir=dist/emulator --format=esm --external:ws", "build:emulator-thread": "esbuild src/emulator/server/serverProcess.ts --bundle --platform=node --outdir=dist/emulator --format=esm --external:ts-node", "build:emulator-client": "vite build", dev: "vite", "build:index": "esbuild src/index.ts --bundle --platform=node --outdir=dist --format=esm --packages=external && tsc src/index.ts --declaration --emitDeclarationOnly --outDir dist --skipLibCheck --isolatedModules --module ESNext", typecheck: "tsc --noEmit" }, dependencies: { "@tailwindcss/vite": "^4.1.10", "@vitejs/plugin-legacy": "^6.0.2", react: "^19.0.0", "react-dom": "^19.0.0", tailwindcss: "^4.1.10", vite: "^6.2.2", ws: "^8.18.0", yargs: "^17.7.2", "zip-lib": "^1.0.5", zustand: "^5.0.5" }, devDependencies: { "@deskthing/types": "^0.11.16", "@types/node": "^22.13.17", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@types/ws": "^8.5.14", "@types/yargs": "^17.0.33", "@vitejs/plugin-react": "^4.3.4", esbuild: "^0.25.1", "import-meta-resolve": "^4.1.0", typescript: "^5.7.3" }, peerDependencies: { tsm: "^2.3.0" } }; } }); // src/cli.ts import yargs from "yargs"; import { hideBin } from "yargs/helpers"; import { fileURLToPath } from "url"; import { dirname, join as join9 } from "path"; import { execSync } from "child_process"; // src/package/package.ts import { join as join2, resolve } from "path"; import zl from "zip-lib"; import { readdir, stat, cp, rm, mkdir } from "fs/promises"; // src/package/config.ts import { join } from "path"; import { readFileSync } from "fs"; var loadConfigs = () => { const packageJsonPath = join(process.cwd(), "package.json"); const manifestJsonPath = join(process.cwd(), "deskthing/manifest.json"); let packageJson; let manifestJson; try { packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8")); } catch (error) { throw new Error(`\x1B[31mFailed to load package.json: ${error.message}\x1B[0m`); } try { manifestJson = JSON.parse(readFileSync(manifestJsonPath, "utf8")); } catch (error) { try { console.log("\x1B[33m\u274C Failed to load manifest.json from deskthing/manifest.json, trying public/manifest.json\x1B[0m"); const oldmanifestJsonPath = join(process.cwd(), "public/manifest.json"); manifestJson = JSON.parse(readFileSync(oldmanifestJsonPath, "utf8")); console.log("\x1B[32m\u2705 Successfully loaded manifest.json from public/manifest.json\x1B[0m"); } catch (err2) { throw new Error(`\x1B[31mFailed to load manifest.json from both locations: ${error.message}, ${err2.message}\x1B[0m`); } } return { packageJson, manifestJson }; }; // src/package/package.ts import { build as buildEsbuild } from "esbuild"; import { build as buildVite } from "vite"; import viteLegacyPlugin from "@vitejs/plugin-legacy"; import { exec } from "child_process"; async function buildServer() { await buildEsbuild({ entryPoints: ["server/index.ts"], bundle: true, platform: "node", outfile: "dist/server/index.js", target: "ESNext", format: "esm", external: ["node:*", "fs", "path", "child_process"], resolveExtensions: [".ts", ".js"], sourcemap: true, banner: { js: ` // ESM shims for Node.js built-in modules import { createRequire as DeskThingCreateRequire } from 'module'; import { fileURLToPath as DeskThingFileURLToPath } from 'url'; import { dirname as DeskThingDirname } from 'node:path'; const require = DeskThingCreateRequire(import.meta.url); const __filename = DeskThingFileURLToPath(import.meta.url); const __dirname = DeskThingDirname(__filename); ` } }); } async function buildWorkers() { try { await stat("server/workers"); } catch (e) { console.warn("\x1B[35mUnable to find workers file\x1B[0m"); console.warn("\x1B[90m(Can be ignored if you do not have workers)\x1B[0m"); return; } try { const workersDir = "server/workers"; const files = await readdir(workersDir); const tsFiles = files.filter((file) => file.endsWith(".ts")).map((file) => join2(workersDir, file)); if (tsFiles.length === 0) { console.warn("\x1B[35mNo TypeScript files found in workers directory\x1B[0m"); return; } await buildEsbuild({ entryPoints: tsFiles, bundle: true, platform: "node", outdir: "dist/server/workers", target: "ESNext", format: "esm", resolveExtensions: [".ts", ".js"], sourcemap: true, banner: { js: ` // ESM shims for Node.js built-in modules import { createRequire as DeskThingCreateRequire } from 'module'; import { fileURLToPath as DeskThingFileURLToPath } from 'url'; import { dirname as DeskThingDirname } from 'node:path'; const require = DeskThingCreateRequire(import.meta.url); const __filename = DeskThingFileURLToPath(import.meta.url); const __dirname = DeskThingDirname(__filename); ` } }); } catch (error) { console.error("\x1B[31mError building workers:\x1B[0m", error); } } var buildPostinstall = async () => { try { await stat("postinstall"); } catch (e) { console.warn("\x1B[35mUnable to find postinstall file\x1B[0m"); console.warn("\x1B[90m(Can be ignored if you do not have a postinstall script)\x1B[0m"); return; } try { await buildEsbuild({ entryPoints: ["postinstall/index.ts"], bundle: true, platform: "node", target: "ESNext", format: "esm", resolveExtensions: [".ts", ".js"], sourcemap: false, outfile: "dist/postinstall.mjs", // ensure that it is a module minify: true }); } catch (error) { console.error("\x1B[31mError building postinstall:\x1B[0m", error); } }; async function buildClient() { await buildVite({ configFile: "vite.config.ts", base: "./", plugins: [viteLegacyPlugin({ targets: ["Chrome 69"] })], build: { outDir: "dist/client", rollupOptions: { output: { assetFileNames: "[name]-[hash][extname]", chunkFileNames: "[name]-[hash].js", entryFileNames: "[name]-[hash].js" } } } }); } async function copyDeskThing() { const deskthingPath = resolve("deskthing"); const publicPath = resolve("public"); const distFile = resolve("dist"); const manifestFile = join2(deskthingPath, "manifest.json"); const oldmanifestFile = join2(publicPath, "manifest.json"); if (await stat(manifestFile).catch(() => false)) { await cp(deskthingPath, join2(distFile), { recursive: true }); } else if (await stat(oldmanifestFile).catch(() => false)) { console.log( "Using old manifest.json. Please move this to /deskthing or run `deskthing update`" ); await cp(publicPath, join2(distFile), { recursive: true }); } else { throw new Error("No manifest.json found in either /deskthing or /public"); } } async function addFilesToArchive(archive, folderPath, baseFolder = "") { const exists = await stat(folderPath).catch(() => false); if (!exists) return; const files = await readdir(folderPath); for (const file of files) { const filePath = join2(folderPath, file); const stats = await stat(filePath); if (stats.isDirectory()) { await addFilesToArchive(archive, filePath, join2(baseFolder, file)); } else { await archive.addFile(filePath, join2(baseFolder, file)); } } } async function createPackage() { const { packageJson, manifestJson } = loadConfigs(); const packageName = packageJson.name; const version = manifestJson.version || packageJson.version; const distPath = resolve("dist"); const outputFile = join2(distPath, `${packageName}-v${version}.zip`); console.log("\x1B[36m%s\x1B[0m", "Zipping to " + outputFile); const archive = new zl.Zip(); console.log("\x1B[33m%s\x1B[0m", "\u{1F4E6} Adding files to archive..."); await addFilesToArchive(archive, distPath); console.log("\x1B[33m%s\x1B[0m", "\u{1F4DD} Writing archive to file..."); await archive.archive(outputFile); console.log("\x1B[32m%s\x1B[0m", "\u2705 Archive written successfully!"); } async function clean() { const distPath = resolve("dist"); try { await mkdir(distPath, { recursive: true }); const files = await readdir(distPath); for (const file of files) { const filePath = join2(distPath, file); await rm(filePath, { recursive: true, force: true }); } } catch (error) { await mkdir(distPath, { recursive: true }); } } async function ensureNpmBuilt() { const nodeModulesPath = resolve("node_modules"); const nodeModulesExists = await stat(nodeModulesPath).catch(() => false); if (!nodeModulesExists) { console.log("\x1B[33m%s\x1B[0m", "\u{1F4E6} Running npm install..."); await new Promise((resolve4, reject) => { exec("npm install", (error, stdout, stderr) => { if (error) { reject(error); return; } resolve4(stdout); }); }); } else { console.log("\x1B[32m%s\x1B[0m", "\u2705 NPM is already built!"); } } async function buildAll() { console.log("\x1B[33m%s\x1B[0m", "\u{1F9F9} Clearing dist folder..."); await clean(); console.log("\x1B[33m%s\x1B[0m", "\u{1F4E6} Ensuring NPM has been built..."); await ensureNpmBuilt(); console.log("\x1B[33m%s\x1B[0m", "\u{1F3D7}\uFE0F Building Client..."); await buildClient(); console.log("\x1B[33m%s\x1B[0m", "\u{1F3D7}\uFE0F Building Server..."); await buildServer(); console.log("\x1B[33m%s\x1B[0m", "\u{1F3D7}\uFE0F Building Workers..."); await buildWorkers(); console.log("\x1B[33m%s\x1B[0m", "\u{1F3D7}\uFE0F Building Postinstall Script..."); await buildPostinstall(); console.log("\x1B[33m%s\x1B[0m", "\u{1F3D7}\uFE0F Copying Manifest..."); await copyDeskThing(); console.log("\x1B[33m%s\x1B[0m", "\u{1F4E6} Creating package..."); await createPackage(); console.log("\x1B[32m%s\x1B[0m", "\u2705 Build completed successfully!"); } // src/package/releaseMeta.ts import { createHash } from "crypto"; import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs"; import { join as join4 } from "path"; import { writeFile } from "fs/promises"; // src/utils/filePaths.ts import { existsSync } from "node:fs"; import { join as join3 } from "node:path"; var getReleaseFilePath = async (fileId) => { const distPath = join3(process.cwd(), "dist"); const defaultPath = join3(distPath, "latest.json"); const appSpecificPath = join3(distPath, `${fileId}.json`); return existsSync(defaultPath) ? appSpecificPath : defaultPath; }; // src/utils/validateRepoUrl.ts async function validateRepoUrl(url) { try { const parsedUrl = new URL(url); if (parsedUrl.hostname === "github.com") { const response2 = await fetch(url, { method: "HEAD" }); return response2.ok; } const response = await fetch(url, { method: "HEAD" }); return response.ok; } catch (error) { return false; } } // src/package/releaseMeta.ts var getLatestReleasesFromGithubURLs = (releaseAssetId, potentialUrls) => { for (const url of potentialUrls) { try { const githubMatch = url.match(/github\.com\/([^\/]+)\/([^\/]+)/); if (githubMatch) { const [, owner, repo] = githubMatch; const cleanRepo = repo.replace(".git", ""); return `https://github.com/${owner}/${cleanRepo}/releases/latest/download/${releaseAssetId}`; } } catch (error) { console.error("\x1B[90m%s\x1B[0m", "Error processing github URL:", error); } } return ""; }; var generateRelease = async () => { console.log("\x1B[33m%s\x1B[0m", "Reading package.json and manifest.json..."); const { packageJson, manifestJson } = loadConfigs(); console.log("\x1B[33m%s\x1B[0m", "Calculating package hash..."); const packageName = packageJson.name; const version = manifestJson.version || packageJson.version; const distPath = join4(process.cwd(), "dist"); const zipPath = join4(distPath, `${packageName}-v${version}.zip`); const iconPath = join4(distPath, "icons", `${manifestJson.id}.svg`); const iconPathAlt = join4(distPath, "images", `${manifestJson.id}.svg`); const iconFilePath = existsSync2(iconPath) ? iconPath : existsSync2(iconPathAlt) ? iconPathAlt : null; const icon = iconFilePath ? `data:image/svg+xml;base64,${readFileSync2(iconFilePath, "base64")}` : ""; if (icon) { console.log("\x1B[33m%s\x1B[0m", "Icon found, adding to release metadata..."); } const updateUrl = getLatestReleasesFromGithubURLs( `${packageName}-v${version}.zip`, [ manifestJson.repository, packageJson.repository?.url ] ); let fileHash = ""; let fileSize = -1; try { const fileBuffer = readFileSync2(zipPath); const hashSum = createHash("sha512"); hashSum.update(fileBuffer); fileHash = hashSum.digest("hex"); fileSize = fileBuffer.length; } catch (err) { console.warn("\x1B[33m%s\x1B[0m", "Could not generate hash for package file"); } console.log("\x1B[33m%s\x1B[0m", "Generating release metadata..."); const isValidUrl = await validateRepoUrl(manifestJson.repository); if (isValidUrl) { console.log("\x1B[32m%s\x1B[0m", "\u2705 Url is valid"); } else { console.error("\x1B[31m%s\x1B[0m", `\u26A0\uFE0F Url ${manifestJson.repository} is not valid! You may need to provide a valid GitHub repository URL in manifest.json. If you ignore, DeskThing will be unable to download your app`); } const release = { meta_version: "0.11.8", // latest version of the release meta_type: "app", repository: manifestJson.repository || packageJson.repository?.url, icon, size: fileSize, appManifest: manifestJson, updateUrl, hash: fileHash, hashAlgorithm: "sha512", updatedAt: Date.now(), createdAt: Date.now(), downloads: 0 }; return release; }; async function createReleaseFile() { console.log("\x1B[33m%s\x1B[0m", "Creating release file..."); try { const release = await generateRelease(); const releaseFilePath = await getReleaseFilePath(release.appManifest.id); await writeFile(releaseFilePath, JSON.stringify(release, null, 2)); console.log("\x1B[32m%s\x1B[0m", "Release file created successfully"); } catch (err) { console.error("\x1B[31m%s\x1B[0m", "Failed to create release file:", err); } } // src/package/index.ts var packageApp = async ({ guided, noRelease, onlyRelease }) => { if (guided) { console.log("Guided mode is not implemented yet."); return; } if (!onlyRelease) { await buildAll(); } else { console.log("\x1B[34m%s\x1B[0m", "\u23ED\uFE0F Skipping build step."); } if (!noRelease) { await createReleaseFile(); } else { console.log("\x1B[34m%s\x1B[0m", "\u23ED\uFE0F Skipping release file creation step."); } }; // src/packageClient/package.ts import { join as join6, resolve as resolve2 } from "path"; import zl2 from "zip-lib"; import { readdir as readdir2, stat as stat2, rm as rm2, mkdir as mkdir2 } from "fs/promises"; // src/packageClient/config.ts import { join as join5 } from "path"; import { readFileSync as readFileSync3 } from "fs"; var loadConfigs2 = () => { const packageJsonPath = join5(process.cwd(), "package.json"); const manifestJsPath = join5(process.cwd(), "public/manifest.json"); let packageJson; let manifestJson; try { packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8")); } catch (error) { throw new Error(`\x1B[31mFailed to load package.json: ${error.message}\x1B[0m`); } try { const manifestContent = readFileSync3(manifestJsPath, "utf8"); manifestJson = JSON.parse(manifestContent); } catch (error) { throw new Error(`\x1B[33m\u274C Failed to load manifest.json from ${manifestJsPath}. Does it exist?\x1B[0m`); } return { packageJson, manifestJson }; }; // src/packageClient/package.ts import { build as buildVite2 } from "vite"; async function buildClient2() { await buildVite2({ configFile: "vite.config.ts", base: "./" }); } async function addFilesToArchive2(archive, folderPath, baseFolder = "") { const exists = await stat2(folderPath).catch(() => false); if (!exists) return; const files = await readdir2(folderPath); for (const file of files) { const filePath = join6(folderPath, file); const stats = await stat2(filePath); if (stats.isDirectory()) { await addFilesToArchive2(archive, filePath, join6(baseFolder, file)); } else { await archive.addFile(filePath, join6(baseFolder, file)); } } } async function createPackage2() { const { packageJson, manifestJson } = loadConfigs2(); const packageName = packageJson.name; const version = (manifestJson.version || packageJson.version).replaceAll( "v", "" ); const distPath = resolve2("dist"); const distExists = await stat2(distPath).catch(() => false); if (!distExists) { await mkdir2(distPath, { recursive: true }); } const outputFile = join6(distPath, `${packageName}-v${version}.zip`); console.log("\x1B[36m%s\x1B[0m", "Zipping to " + outputFile); const archive = new zl2.Zip(); console.log("\x1B[33m%s\x1B[0m", "\u{1F4E6} Adding files to archive..."); await addFilesToArchive2(archive, distPath); console.log("\x1B[33m%s\x1B[0m", "\u{1F4DD} Writing archive to file..."); await archive.archive(outputFile); console.log("\x1B[32m%s\x1B[0m", "\u2705 Archive written successfully!"); } async function clean2() { const distPath = resolve2("dist"); const files = await readdir2(distPath); for (const file of files) { const filePath = join6(distPath, file); await rm2(filePath, { recursive: true, force: true }); } } async function buildAll2() { console.log("\x1B[33m%s\x1B[0m", "\u{1F9F9} Clearing dist folder..."); await clean2(); console.log("\x1B[33m%s\x1B[0m", "\u{1F3D7}\uFE0F Building Client..."); await buildClient2(); console.log("\x1B[33m%s\x1B[0m", "\u{1F4E6} Creating package..."); await createPackage2(); console.log("\x1B[32m%s\x1B[0m", "\u2705 Build completed successfully!"); } // src/packageClient/releaseMeta.ts import { createHash as createHash2 } from "crypto"; import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs"; import { join as join7 } from "path"; // src/packageClient/sanitizeData.ts var sanitizeClient = async () => { console.log("\x1B[33m%s\x1B[0m", "\u{1F9F9} Client Sanitization not yet implemented..."); console.log("\x1B[32m%s\x1B[0m", "\u2705 DeskThing sanitization completed successfully!"); }; // src/packageClient/releaseMeta.ts var getLatestReleasesFromGithubURLs2 = (releaseAssetId, potentialUrls) => { for (const url of potentialUrls) { try { const githubMatch = url.match(/github\.com\/([^\/]+)\/([^\/]+)/); if (githubMatch) { const [, owner, repo] = githubMatch; const cleanRepo = repo.replace(".git", ""); return `https://github.com/${owner}/${cleanRepo}/releases/latest/download/${releaseAssetId}`; } } catch (error) { console.error("\x1B[90m%s\x1B[0m", "Error processing github URL:", error); } } return ""; }; var generateRelease2 = async () => { console.log("\x1B[33m%s\x1B[0m", "Reading package.json and manifest.json..."); const { packageJson, manifestJson } = loadConfigs2(); console.log("\x1B[33m%s\x1B[0m", "Calculating package hash..."); const packageName = packageJson.name; const version = (manifestJson.version || packageJson.version).replaceAll( "v", "" ); const distPath = join7(process.cwd(), "dist"); const zipPath = join7(distPath, `${packageName}-v${version}.zip`); const iconPath = join7(distPath, "icons", `${manifestJson.id}.svg`); const iconPathAlt = join7(distPath, "images", `${manifestJson.id}.svg`); const iconFilePath = existsSync3(iconPath) ? iconPath : existsSync3(iconPathAlt) ? iconPathAlt : null; const icon = iconFilePath ? `data:image/svg+xml;base64,${readFileSync4(iconFilePath, "base64")}` : ""; if (icon) { console.log( "\x1B[33m%s\x1B[0m", "Icon found, adding to release metadata..." ); } const updateUrl = getLatestReleasesFromGithubURLs2( `${packageName}-v${version}.zip`, [manifestJson.repository, packageJson.repository] ); let fileHash = ""; let fileSize = -1; try { const fileBuffer = readFileSync4(zipPath); const hashSum = createHash2("sha512"); hashSum.update(fileBuffer); fileHash = hashSum.digest("hex"); fileSize = fileBuffer.length; } catch (err) { console.warn( "\x1B[33m%s\x1B[0m", "Could not generate hash for package file" ); } console.log("\x1B[33m%s\x1B[0m", "Generating release metadata..."); console.log("\x1B[33m%s\x1B[0m", "Validating Url..."); const isValidUrl = await validateRepoUrl(manifestJson.repository); if (isValidUrl) { console.log("\x1B[32m%s\x1B[0m", "\u2705 Url is valid"); } else { console.error("\x1B[31m%s\x1B[0m", "\u26A0\uFE0F Url is not valid! You may need to provide a valid GitHub repository URL in manifest.json. If you ignore, DeskThing will be unable to download your app"); } const release = { meta_version: "0.11.8", meta_type: "client", repository: manifestJson.repository || packageJson.repository?.url, icon, clientManifest: manifestJson, updateUrl, hash: fileHash, hashAlgorithm: "sha512", size: fileSize, updatedAt: Date.now(), createdAt: Date.now(), downloads: 0 }; return release; }; async function createReleaseFile2() { console.log("\x1B[33m%s\x1B[0m", "Creating release file..."); try { await sanitizeClient(); const release = await generateRelease2(); const releaseFilePath = await getReleaseFilePath(release.clientManifest.id); writeFileSync2(releaseFilePath, JSON.stringify(release, null, 2)); console.log("\x1B[32m%s\x1B[0m", "Release file created successfully"); } catch (err) { console.error("\x1B[31m%s\x1B[0m", "Failed to create release file:", err); } } // src/packageClient/index.ts var packageClient = async () => { await buildAll2(); await createReleaseFile2(); }; // src/cli.ts import { stat as stat4, writeFile as writeFile2 } from "fs/promises"; // src/config/deskthing.config.ts import { join as join8, resolve as resolve3 } from "path"; import { createRequire } from "module"; import { readFile } from "fs/promises"; import { pathToFileURL } from "url"; import { existsSync as existsSync4 } from "fs"; var defaultConfig = { development: { logging: { level: "info", prefix: "[DeskThing Server]" }, client: { logging: { level: "info", prefix: "[DeskThing Client]", enableRemoteLogging: true }, clientPort: 3e3, viteLocation: "http://localhost", vitePort: 5173, linkPort: 8080 }, server: { editCooldownMs: 1e3, refreshInterval: 0 } } }; async function loadTsConfig(path, debug = false) { try { const require2 = createRequire(import.meta.url); try { require2("ts-node/register"); } catch (e) { if (debug) console.log(`ts-node not available, continuing anyway...`); } try { const configModule = require2(path); return configModule.default || configModule; } catch (e) { if (debug) console.error( "\x1B[91mError loading TypeScript config:", path, e, "\x1B[0m" ); throw e; } } catch (error) { if (debug) console.error("\x1B[91mError loading config:", error, "\x1B[0m"); throw error; } } var manuallyParseConfig = async (path, debug = false) => { try { if (debug) console.log( `(debug mode enabled) Manually loading config from ${path} file and parsing manually (may cause errors)` ); const fileContent = await readFile(path, "utf-8"); const configMatch = fileContent.match(/defineConfig\s*\(\s*({[\s\S]*?})\s*\)/); if (!configMatch) { if (debug) console.log("Could not find config definition in file using regex"); const objectMatch = fileContent.match(/\{[\s\S]*development[\s\S]*\}/); if (objectMatch) { try { let jsonStr = objectMatch[0].replace(/process\.env\.[A-Z_]+/g, '"ENV_VARIABLE"').replace(/'/g, '"').replace(/,(\s*[}\]])/g, "$1").replace(/\/\/.*$/gm, ""); jsonStr = jsonStr.replace( /process\.env\.[A-Z_]+/g, '"ENV_PLACEHOLDER"' ); const config = JSON.parse(jsonStr); if (debug) console.log("Successfully extracted config using fallback method"); return config; } catch (e) { if (debug) console.log("Failed to parse extracted object:", e); } } throw new Error("Could not find or parse config definition in file"); } try { const configStr = configMatch[1]?.trim() || configMatch[2]?.trim(); if (debug) console.log("Found config string, attempting to evaluate safely"); if (debug) console.log(configStr); const cleanConfigStr = configStr?.replace(/process\.env\.[A-Z_]+/g, '"ENV_VARIABLE"').replace(/,(\s*[}\]])/g, "$1").replace(/\/\/.*$/gm, ""); const config = JSON.parse(cleanConfigStr); return config; } catch (parseError) { if (debug) console.error("\x1B[91mError parsing config:", parseError, "\x1B[0m"); throw parseError; } } catch (e) { if (debug) console.error( "\x1B[91m(debug mode enabled) Error loading config:", e, "\x1B[0m" ); throw e; } }; async function parseTypeScriptConfig(filePath, debug = false) { try { const content = await readFile(filePath, "utf-8"); const configMatch = content.match(/defineConfig\s*\(\s*({[\s\S]*?})\s*\)/); if (!configMatch || !configMatch[1]) { return null; } let configStr = configMatch[1].replace(/process\.env\.[A-Z_]+/g, '"ENV_VARIABLE"').replace(/,(\s*[}\]])/g, "$1").replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, ""); try { const fn = new Function(`return ${configStr}`); return fn(); } catch (evalError) { if (debug) console.log("Failed to evaluate config as JavaScript:", evalError); try { configStr = configStr.replace(/'/g, '"'); return JSON.parse(configStr); } catch (jsonError) { if (debug) console.log("Failed to parse as JSON:", jsonError); return null; } } } catch (error) { if (debug) console.log("Error reading or parsing config file:", error); return null; } } var directConfigImport = async (path, debug = false) => { try { if (debug) console.log( `(debug mode enabled) Loading config from ${path} file...` ); const configModule = await import(`${path}`); if (debug) console.log(`Config loaded successfully from ${path}`); return configModule.default || configModule; } catch (error) { if (debug) console.log("Direct import failed:", error); throw error; } }; var directConfigImportUrl = async (path, debug = false) => { try { const tsConfigPath = pathToFileURL(path).href; if (debug) console.log( `(debug mode enabled) Loading config from ${tsConfigPath} file...` ); const configModule = await import(tsConfigPath); if (debug) console.log(`Config loaded successfully from ${tsConfigPath}`); return configModule.default || configModule; } catch (error) { if (debug) console.log("Direct import failed:", error); throw error; } }; var getConfigFromFile = async (debug = false) => { try { let rootUrl = resolve3(process.cwd(), "deskthing.config.ts"); if (!existsSync4(rootUrl)) { if (debug) console.log( "deskthing.config.ts not found, trying deskthing.config.js" ); rootUrl = resolve3(process.cwd(), "deskthing.config.js"); if (!existsSync4(rootUrl)) { throw new Error("No config file found (tried both TS and JS files)"); } } try { const config = await directConfigImport(rootUrl, debug); if (config) { if (debug) console.log("Config loaded successfully from TS file"); return config; } } catch (importError) { if (debug) console.log(`Direct import failed, trying alternative method...`); } try { const config = await directConfigImportUrl(rootUrl, debug); if (config) { if (debug) console.log("Config loaded successfully from TS file"); return config; } } catch (importError) { if (debug) console.log(`Direct import failed, trying alternative method...`); } try { const config = await loadTsConfig(rootUrl, debug); if (config) { if (debug) console.log("Config loaded successfully from TS file"); return config; } } catch (e) { if (debug) console.error("\x1B[91mError loading TS config:", e, "\x1B[0m"); } if (debug) console.log("Trying to parse config manually..."); try { const config = await manuallyParseConfig(rootUrl, debug); return config; } catch (e) { if (debug) console.error("\x1B[91mError parsing config manually:", e, "\x1B[0m"); } if (debug) console.log("Trying to parse config and run as js..."); try { const config = await parseTypeScriptConfig(rootUrl, debug); return config; } catch (error) { if (debug) console.log("Error running as js:", error); } } catch (e) { if (debug) console.error( "\x1B[91m(debug mode) Error loading config. Does it exist? :", e, "\x1B[0m" ); return defaultConfig; } }; var DeskThingConfig = defaultConfig; function isObject(item) { return item && typeof item === "object" && !Array.isArray(item); } function deepmerge(target, source) { if (!isObject(target) || !isObject(source)) { return source; } const output = { ...target }; for (const key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { if (isObject(source[key]) && isObject(target[key])) { output[key] = deepmerge(target[key], source[key]); } else { output[key] = source[key]; } } } return output; } var initConfig = async (options = { silent: false, debug: false }) => { try { const userConfig = await getConfigFromFile(options.debug); DeskThingConfig = deepmerge(defaultConfig, userConfig || {}); if (!options.silent || options.debug) { if (userConfig) { console.log(` \x1B[32m\u2705 Config Loaded\x1B[0m `); } else { console.log(` \x1B[32m\u2705 No Config Found, Using Default\x1B[0m`); if (options.debug) { console.log( `\x1B[3m\x1B[90mPath Checked: ${join8( process.cwd(), "deskthing.config.ts" )}\x1B[0m ` ); } } } } catch (e) { console.warn("\x1B[93mWarning: Error loading config, using defaults\x1B[0m"); if (options.debug) console.error("\x1B[91mError loading config:", e, "\x1B[0m"); DeskThingConfig = defaultConfig; } }; var serverConfig = { ...defaultConfig }; // src/cli.ts await initConfig({ silent: true }); var __dirname = dirname(fileURLToPath(import.meta.url)); var thisPackage = await Promise.resolve().then(() => __toESM(require_package(), 1)).catch(() => ({ default: { version: "0.0.0" } })).then((module) => module?.default) || { version: "0.0.0" }; console.log(` ------------------------------------------------ _ _ _ _ \x1B[92m_\x1B[0m | | | | | | | | \x1B[92m(_)\x1B[0m __| | ___ ___| | _| |_| |__ _ _ __ __ _ / _\` |/ _ \\/ __| |/ / __| '_ \\| | '_ \\ / _\` | | (_| | __/\\__ \\ <| |_| | | | | | | | (_| | \\__,_|\\___||___/_|\\_\\\\__|_| |_|_|_| |_|\\__, | __/ | |___/ Version ${thisPackage?.version || "0.11.0"}`); yargs(hideBin(process.argv)).scriptName("deskthing").command( "dev", "Start development server", (yargs2) => { return yargs2.option("debug", { type: "boolean", default: false, description: "Enable debug mode" }); }, async (argv) => { console.log( `------- \x1B[1mdev\x1B[0m -- init -- update -- package --------` ); console.log("\n\n\x1B[1m\u{1F680} Starting development server...\x1B[0m\n\n"); const tsmPath = join9(process.cwd(), "node_modules", "tsm"); try { await stat4(tsmPath); console.log("\x1B[1mtsm is installed\x1B[0m\n"); } catch (e) { console.log("\n\x1B[1mInstalling tsm dependency...\x1B[0m\n"); execSync("npm install tsm --no-save --legacy-peer-deps", { stdio: "inherit" }); } const indexPath = join9(__dirname, "./emulator/index.js"); const fileUrl = `file://${indexPath.replace(/\\/g, "/")}`; const { startDevelopment } = await import(fileUrl); await startDevelopment({ debug: argv.debug }); } ).command( "update", "Update dependencies and configurations", (yargs2) => { return yargs2.option("force", { type: "boolean", default: false, description: "Force update all dependencies" }).option("no-overwrite", { type: "boolean", default: false, description: "Do not overwrite existing files" }).option("debug", { type: "boolean", default: false, description: "Add verbose debugging" }).option("silent", { type: "boolean", default: false, description: "Make it so there is no output to the console" }); }, async (argv) => { if (!argv.silent) console.log( `------- dev -- init -- \x1B[1mupdate\x1B[0m -- package --------` ); if (!argv.silent) console.log("Updating dependencies and configurations..."); const args = ["create-deskthing@latest", "--update"]; if (argv["no-overwrite"]) { if (!argv.silent) console.log("\x1B[32m\u2713 No Overwrite Enabled\x1B[0m"); args.push("--no-overwrite"); } if (argv.force) { if (!argv.silent) console.log("\x1B[32m\u2713 Force Enabled\x1B[0m"); args.push("--force"); } if (argv.debug) { if (!argv.silent) console.log("\x1B[32m\u2713 Debug Enabled\x1B[0m"); args.push("--debug"); } if (argv.silent) { console.log("\x1B[32m\u2713 Silent Enabled\x1B[0m"); args.push("--silent"); } execSync(`npx ${args.join(" ")}`, { stdio: "inherit" }); } ).command( "package", "Package and zip up your app. Also generates needed manifest files", (yargs2) => { return yargs2.option("guided", { type: "boolean", default: false, description: "Force update all dependencies" }).option("no-release", { type: "boolean", default: false, description: "Skip generating release metadata files" }).option("only-release", { type: "boolean", default: false, description: "Only generate release metadata files" }); }, async (argv) => { console.log( `------- dev -- init -- update -- \x1B[1mpackage\x1B[0m --------` ); console.log("Packaging app..."); await packageApp({ guided: argv.guided, noRelease: argv.noRelease, onlyRelease: argv.onlyRelease }); } ).command( "package-client", "Package and zip up your client. Also generates needed manifest files", (yargs2) => { return yargs2; }, async () => { console.log(`------- dev -- init -- update -- package -------- `); console.log( `------- \x1B[1mpackaging client\x1B[0m -------- ` ); await packageClient(); } ).command( "init", "Setup the DeskThing template", (yargs2) => { return yargs2; }, async () => { console.log( `------- dev -- \x1B[1minit\x1B[0m -- update -- package --------` ); console.log("Setting up the DeskThing template..."); execSync("npm create deskthing@latest --create", { stdio: "inherit" }); } ).command("$0", "Show available commands", () => { console.log(`------- dev -- init -- update -- package --------`); console.log("Available commands:"); console.log(" dev Start development server"); console.log(" update Update dependencies and configurations"); console.log(" package Package and zip up your app"); console.log(" init Setup the DeskThing template"); console.log( "\nRun `deskthing <command> --help` for more information about a command." ); }).command( "init-config", "Create a typed configuration file", (yargs2) => { return yargs2; }, async () => { console.log( ` ------- \x1B[1mCreating typed configuration file\x1B[0m -------- ` ); const configTemplate = ` // @ts-check // version ${thisPackage?.version || "0.11.0"} import { defineConfig } from '@deskthing/cli'; export default defineConfig({ development: { logging: { level: "info", prefix: "[DeskThing Server]", }, client: { logging: { level: "info", prefix: "[DeskThing Client]", enableRemoteLogging: true, }, clientPort: 3000, viteLocation: "http://localhost", vitePort: 5173, linkPort: 8080, }, server: { editCooldownMs: 1000, }, } }); `; const configPath = join9(process.cwd(), "deskthing.config.js"); try { await writeFile2(configPath, configTemplate); console.log(` \x1B[32m\u2705 File created at ${configPath}\x1B[0m `); } catch (error) { console.error( "\x1B[31m\u274C Failed to create configuration file:\x1B[0m", error ); } } ).parse();