From 1dc4f781b715f4d742860b3bb875232d96ec7e64 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Fri, 5 Dec 2025 10:44:50 +0100 Subject: [PATCH 1/2] feat: add `make mux` target to run compiled CLI Adds a convenience target that runs the compiled CLI with trailing args: make mux server --port 4000 make mux -- --help Builds automatically if dist/cli/index.js is missing, otherwise instant. --- Makefile | 27 +++++++++++++++++++++++++-- src/cli/index.ts | 8 +++++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index eb981bb85c..4ff97d7ce7 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ include fmt.mk .PHONY: docs-server check-docs-links .PHONY: storybook storybook-build test-storybook chromatic .PHONY: benchmark-terminal -.PHONY: ensure-deps rebuild-native +.PHONY: ensure-deps rebuild-native mux .PHONY: check-eager-imports check-bundle-size check-startup # Build tools @@ -107,6 +107,17 @@ rebuild-native: node_modules/.installed ## Rebuild native modules (node-pty) for @npx @electron/rebuild -f -m node_modules/node-pty @echo "Native modules rebuilt successfully" +# Run compiled CLI with trailing arguments (builds only if missing) +mux: ## Run the compiled mux CLI (e.g., make mux server --port 3000) + @test -f dist/cli/index.js || $(MAKE) build-main + @node dist/cli/index.js $(filter-out $@,$(MAKECMDGOALS)) + +# Catch unknown targets passed to mux (prevents "No rule to make target" errors) +ifneq ($(filter mux,$(MAKECMDGOALS)),) +%: + @: +endif + ## Help help: ## Show this help message @echo 'Usage: make [target]' @@ -163,13 +174,25 @@ start: node_modules/.installed build-main build-preload build-static ## Build an ## Build targets (can run in parallel) build: node_modules/.installed src/version.ts build-renderer build-main build-preload build-icons build-static ## Build all targets -build-main: node_modules/.installed dist/cli/index.js ## Build main process +build-main: node_modules/.installed dist/cli/index.js dist/cli/api.mjs ## Build main process dist/cli/index.js: src/cli/index.ts src/desktop/main.ts src/cli/server.ts src/version.ts tsconfig.main.json tsconfig.json $(TS_SOURCES) @echo "Building main process..." @NODE_ENV=production $(TSGO) -p tsconfig.main.json @NODE_ENV=production bun x tsc-alias -p tsconfig.main.json +# Build API CLI as ESM bundle (trpc-cli requires ESM with top-level await) +dist/cli/api.mjs: src/cli/api.ts src/cli/proxifyOrpc.ts $(TS_SOURCES) + @echo "Building API CLI (ESM)..." + @bun x esbuild src/cli/api.ts \ + --bundle \ + --format=esm \ + --platform=node \ + --outfile=dist/cli/api.mjs \ + --external:zod \ + --external:commander \ + --external:@trpc/server + build-preload: node_modules/.installed dist/preload.js ## Build preload script dist/preload.js: src/desktop/preload.ts $(TS_SOURCES) diff --git a/src/cli/index.ts b/src/cli/index.ts index e518c9e50f..c7edc176eb 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -40,9 +40,11 @@ if (subcommand === "run") { require("./server"); } else if (subcommand === "api") { process.argv.splice(2, 1); - // Dynamic import required: trpc-cli is ESM-only and can't be require()'d - // eslint-disable-next-line no-restricted-syntax - void import("./api"); + // Must use native import() to load ESM module - trpc-cli requires ESM with top-level await. + // Using Function constructor prevents TypeScript from converting this to require(). + // The .mjs extension is critical for Node.js to treat it as ESM. + // eslint-disable-next-line @typescript-eslint/no-implied-eval, @typescript-eslint/no-unsafe-call + void new Function("return import('./api.mjs')")(); } else if ( subcommand === "desktop" || (isElectron && (subcommand === undefined || isElectronLaunchArg)) From 08186716430df2610028e1997a3a227325e13c84 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Fri, 5 Dec 2025 11:05:28 +0100 Subject: [PATCH 2/2] fix: build api CLI as ESM to support trpc-cli trpc-cli requires ESM with top-level await, which can't be loaded via CommonJS require(). Bundle api.ts separately using esbuild as ESM (.mjs) and use native import() via Function constructor to bypass TypeScript's conversion to require(). Also adds `make mux` target for convenient CLI invocation. --- Makefile | 8 ++++++-- package.json | 1 + scripts/smoke-test.sh | 9 +++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4ff97d7ce7..e72b1de273 100644 --- a/Makefile +++ b/Makefile @@ -109,7 +109,7 @@ rebuild-native: node_modules/.installed ## Rebuild native modules (node-pty) for # Run compiled CLI with trailing arguments (builds only if missing) mux: ## Run the compiled mux CLI (e.g., make mux server --port 3000) - @test -f dist/cli/index.js || $(MAKE) build-main + @test -f dist/cli/index.js -a -f dist/cli/api.mjs || $(MAKE) build-main @node dist/cli/index.js $(filter-out $@,$(MAKECMDGOALS)) # Catch unknown targets passed to mux (prevents "No rule to make target" errors) @@ -128,16 +128,18 @@ help: ## Show this help message ## Development ifeq ($(OS),Windows_NT) dev: node_modules/.installed build-main ## Start development server (Vite + nodemon watcher for Windows compatibility) - @echo "Starting dev mode (2 watchers: nodemon for main process, vite for renderer)..." + @echo "Starting dev mode (3 watchers: nodemon for main process, esbuild for api, vite for renderer)..." # On Windows, use npm run because bunx doesn't correctly pass arguments to concurrently # https://github.com/oven-sh/bun/issues/18275 @NODE_OPTIONS="--max-old-space-size=4096" npm x concurrently -k --raw \ "bun x nodemon --watch src --watch tsconfig.main.json --watch tsconfig.json --ext ts,tsx,json --ignore dist --ignore node_modules --exec node scripts/build-main-watch.js" \ + "npx esbuild src/cli/api.ts --bundle --format=esm --platform=node --outfile=dist/cli/api.mjs --external:zod --external:commander --external:@trpc/server --watch" \ "vite" else dev: node_modules/.installed build-main build-preload ## Start development server (Vite + tsgo watcher for 10x faster type checking) @bun x concurrently -k \ "bun x concurrently \"$(TSGO) -w -p tsconfig.main.json\" \"bun x tsc-alias -w -p tsconfig.main.json\"" \ + "bun x esbuild src/cli/api.ts --bundle --format=esm --platform=node --outfile=dist/cli/api.mjs --external:zod --external:commander --external:@trpc/server --watch" \ "vite" endif @@ -151,6 +153,7 @@ dev-server: node_modules/.installed build-main ## Start server mode with hot rel @# On Windows, use npm run because bunx doesn't correctly pass arguments @npmx concurrently -k \ "npmx nodemon --watch src --watch tsconfig.main.json --watch tsconfig.json --ext ts,tsx,json --ignore dist --ignore node_modules --exec node scripts/build-main-watch.js" \ + "npx esbuild src/cli/api.ts --bundle --format=esm --platform=node --outfile=dist/cli/api.mjs --external:zod --external:commander --external:@trpc/server --watch" \ "npmx nodemon --watch dist/cli/index.js --watch dist/cli/server.js --delay 500ms --exec \"node dist/cli/index.js server --host $(or $(BACKEND_HOST),localhost) --port $(or $(BACKEND_PORT),3000)\"" \ "$(SHELL) -lc \"MUX_VITE_HOST=$(or $(VITE_HOST),127.0.0.1) MUX_VITE_PORT=$(or $(VITE_PORT),5173) VITE_BACKEND_URL=http://$(or $(BACKEND_HOST),localhost):$(or $(BACKEND_PORT),3000) vite\"" else @@ -162,6 +165,7 @@ dev-server: node_modules/.installed build-main ## Start server mode with hot rel @echo "For remote access: make dev-server VITE_HOST=0.0.0.0 BACKEND_HOST=0.0.0.0" @bun x concurrently -k \ "bun x concurrently \"$(TSGO) -w -p tsconfig.main.json\" \"bun x tsc-alias -w -p tsconfig.main.json\"" \ + "bun x esbuild src/cli/api.ts --bundle --format=esm --platform=node --outfile=dist/cli/api.mjs --external:zod --external:commander --external:@trpc/server --watch" \ "bun x nodemon --watch dist/cli/index.js --watch dist/cli/server.js --delay 500ms --exec 'NODE_ENV=development node dist/cli/index.js server --host $(or $(BACKEND_HOST),localhost) --port $(or $(BACKEND_PORT),3000)'" \ "MUX_VITE_HOST=$(or $(VITE_HOST),127.0.0.1) MUX_VITE_PORT=$(or $(VITE_PORT),5173) VITE_BACKEND_URL=http://$(or $(BACKEND_HOST),localhost):$(or $(BACKEND_PORT),3000) vite" endif diff --git a/package.json b/package.json index 375273b2de..d8ee0e78c6 100644 --- a/package.json +++ b/package.json @@ -196,6 +196,7 @@ }, "files": [ "dist/**/*.js", + "dist/**/*.mjs", "dist/**/*.js.map", "dist/**/*.wasm", "dist/**/*.html", diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh index 7850f1d426..8b231ec06e 100755 --- a/scripts/smoke-test.sh +++ b/scripts/smoke-test.sh @@ -108,6 +108,15 @@ fi log_info "✅ mux binary found" +# Test that mux api subcommand works (requires ESM bundle api.mjs) +log_info "Testing mux api subcommand (ESM bundle)..." +if ! node_modules/.bin/mux api --help >/dev/null 2>&1; then + log_error "mux api --help failed - ESM bundle (api.mjs) may be missing from package" + exit 1 +fi + +log_info "✅ mux api subcommand works" + # Start the server in background log_info "Starting mux server on $SERVER_HOST:$SERVER_PORT..." node_modules/.bin/mux server --host "$SERVER_HOST" --port "$SERVER_PORT" >server.log 2>&1 &