diff --git a/.changeset/rude-geese-remain.md b/.changeset/rude-geese-remain.md new file mode 100644 index 00000000000..a2b43df3f62 --- /dev/null +++ b/.changeset/rude-geese-remain.md @@ -0,0 +1,5 @@ +--- +"@remix-run/dev": patch +--- + +better error message when `remix-serve` is not found diff --git a/packages/remix-dev/cli/commands.ts b/packages/remix-dev/cli/commands.ts index 95bec84c03e..8df2b505024 100644 --- a/packages/remix-dev/cli/commands.ts +++ b/packages/remix-dev/cli/commands.ts @@ -25,7 +25,6 @@ import { TaskError } from "../codemod/utils/task"; import { transpile as convertFileToJS } from "./useJavascript"; import { warnOnce } from "../warnOnce"; import type { Options } from "../compiler/options"; -import { getAppDependencies } from "../dependencies"; export async function create({ appTemplate, @@ -502,7 +501,7 @@ let resolveDevOrigin = async ( }; type DevServeFlags = DevOrigin & { - command: string; + command?: string; restart: boolean; }; let resolveDevServe = async ( @@ -518,25 +517,6 @@ let resolveDevServe = async ( let command = flags.command ?? (dev === true ? undefined : dev.command) - if (!command) { - command = `remix-serve ${path.relative( - process.cwd(), - config.serverBuildPath - )}`; - - let usingRemixAppServer = - getAppDependencies(config, true)["@remix-run/serve"] !== undefined; - if (!usingRemixAppServer) { - console.error( - [ - `Remix dev server command defaulted to '${command}', but @remix-run/serve is not installed.`, - "If you are using another server, specify how to run it with `-c` or `--command` flag.", - "For example, `remix dev -c 'node ./server.js'`", - ].join("\n") - ); - process.exit(1); - } - } let restart = flags.restart ?? (dev === true ? undefined : dev.restart) ?? true; diff --git a/packages/remix-dev/devServer_unstable/index.ts b/packages/remix-dev/devServer_unstable/index.ts index 01c29d8b71e..fbaa2d4dd61 100644 --- a/packages/remix-dev/devServer_unstable/index.ts +++ b/packages/remix-dev/devServer_unstable/index.ts @@ -3,6 +3,7 @@ import * as stream from "node:stream"; import * as http from "node:http"; import fs from "fs-extra"; import prettyMs from "pretty-ms"; +import type { ExecaChildProcess } from "execa"; import execa from "execa"; import express from "express"; @@ -18,6 +19,7 @@ import { detectPackageManager } from "../cli/detectPackageManager"; import * as HDR from "./hdr"; import type { Result } from "../result"; import { err, ok } from "../result"; +import invariant from "../invariant"; type Origin = { scheme: string; @@ -41,7 +43,7 @@ let detectBin = async (): Promise => { export let serve = async ( initialConfig: RemixConfig, options: { - command: string; + command?: string; scheme: string; host: string; port: number; @@ -83,19 +85,47 @@ export let serve = async ( }; let bin = await detectBin(); - let startAppServer = (command: string) => { - console.log(`> ${command}`); - let newAppServer = execa.command(command, { - stdio: "pipe", - env: { - NODE_ENV: "development", - PATH: - bin + (process.platform === "win32" ? ";" : ":") + process.env.PATH, - REMIX_DEV_HTTP_ORIGIN: stringifyOrigin(origin), - }, - // https://github.com/sindresorhus/execa/issues/433 - windowsHide: false, - }); + let startAppServer = (command?: string) => { + let cmd = + command ?? + `remix-serve ${path.relative( + process.cwd(), + initialConfig.serverBuildPath + )}`; + console.log(`> ${cmd}`); + let newAppServer = execa + .command(cmd, { + stdio: "pipe", + env: { + NODE_ENV: "development", + PATH: + bin + (process.platform === "win32" ? ";" : ":") + process.env.PATH, + REMIX_DEV_HTTP_ORIGIN: stringifyOrigin(origin), + }, + // https://github.com/sindresorhus/execa/issues/433 + windowsHide: false, + }) + .on("error", (e) => { + // patch execa error types + invariant("errno" in e && typeof e.errno === "number", "errno missing"); + invariant("code" in e && typeof e.code === "string", "code missing"); + invariant("path" in e && typeof e.path === "string", "path missing"); + + if (command === undefined) { + console.error( + [ + "", + `┏ [error] command not found: ${e.path}`, + `┃ \`remix dev\` did not receive \`--command\` nor \`-c\`, defaulting to \`${cmd}\`.`, + "┃ You probably meant to use `-c` for your app server command.", + "┗ For example: `remix dev -c 'node ./server.js'`", + "", + ].join("\n") + ); + process.exit(1); + } + throw e; + }); if (newAppServer.stdin) process.stdin.pipe(newAppServer.stdin, { end: true }); @@ -164,10 +194,7 @@ export let serve = async ( try { console.log(`Waiting for app server (${state.manifest?.version})`); let start = Date.now(); - if ( - options.command && - (state.appServer === undefined || options.restart) - ) { + if (state.appServer === undefined || options.restart) { await kill(state.appServer); state.appServer = startAppServer(options.command); }