diff --git a/Makefile b/Makefile index eb981bb85c..e72b1de273 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 -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) +ifneq ($(filter mux,$(MAKECMDGOALS)),) +%: + @: +endif + ## Help help: ## Show this help message @echo 'Usage: make [target]' @@ -117,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 @@ -140,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 @@ -151,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 @@ -163,13 +178,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/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 & 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))