diff --git a/.changeset/cli-dev-without-project.md b/.changeset/cli-dev-without-project.md new file mode 100644 index 0000000000..4e5b74a7f6 --- /dev/null +++ b/.changeset/cli-dev-without-project.md @@ -0,0 +1,5 @@ +--- +"trigger.dev": patch +--- + +Running a CLI command like `dev`, `deploy`, `preview`, or `update` before initializing a project no longer crashes with a raw `Cannot find matching package.json` stack trace. The CLI now detects the missing project and points you to `npx trigger.dev@latest init` instead. diff --git a/packages/cli-v3/src/commands/update.ts b/packages/cli-v3/src/commands/update.ts index f94718213f..b7df5837f2 100644 --- a/packages/cli-v3/src/commands/update.ts +++ b/packages/cli-v3/src/commands/update.ts @@ -66,13 +66,35 @@ export async function updateTriggerPackages( const projectPath = resolve(process.cwd(), dir); - const { packageJson, readonlyPackageJson, packageJsonPath } = await getPackageJson(projectPath); + let packageJsonResult: Awaited> | undefined; + + try { + packageJsonResult = await getPackageJson(projectPath); + } catch (error) { + // resolvePackageJSON throws when there's no package.json in projectPath or any parent + // directory — usually because the command ran before the project was set up. Don't crash + // with a raw stack trace; fall through to the actionable guidance below. + logger.debug("Failed to resolve package.json for update check", { projectPath, error }); + } + + if (!packageJsonResult?.packageJson) { + prettyError( + "No package.json found", + `Couldn't find a package.json in ${projectPath} or any parent directory.`, + "Run `npx trigger.dev@latest init` to set up your project, then try again." + ); + + // When embedded in another command (e.g. `dev`), there's nothing to run without a project, + // so stop here with a clean exit instead of letting the caller fail again downstream. + if (embedded) { + process.exit(1); + } - if (!packageJson) { - log.error("Failed to load package.json. Try to re-run with `-l debug` to see what's going on."); return false; } + const { packageJson, readonlyPackageJson, packageJsonPath } = packageJsonResult; + const newCliVersion = await updateCheck(); if (newCliVersion && !cliVersion.startsWith("0.0.0")) { diff --git a/packages/cli-v3/src/config.ts b/packages/cli-v3/src/config.ts index af5623e017..1421cafe06 100644 --- a/packages/cli-v3/src/config.ts +++ b/packages/cli-v3/src/config.ts @@ -152,18 +152,9 @@ async function resolveConfig( overrides?: Partial, warn = true ): Promise { - const packageJsonPath = await resolvePackageJSON(cwd); - const tsconfigPath = await safeResolveTsConfig(cwd); - const lockfilePath = await resolveLockfile(cwd); - const workspaceDir = await findWorkspaceDir(cwd); - - const workingDir = result.configFile - ? dirname(result.configFile) - : packageJsonPath - ? dirname(packageJsonPath) - : cwd; - - // `trigger.config` is the fallback value set by c12 + // `trigger.config` is the fallback value set by c12. Bail out with actionable guidance before + // touching the filesystem: the pkg-types resolvers below throw raw errors when run outside a + // project (e.g. `dev` before `init`), which would mask this message. const missingConfigFile = !result.configFile || result.configFile === "trigger.config"; if (missingConfigFile) { @@ -178,6 +169,17 @@ async function resolveConfig( ); } + const packageJsonPath = await resolvePackageJSON(cwd); + const tsconfigPath = await safeResolveTsConfig(cwd); + const lockfilePath = await resolveLockfile(cwd); + const workspaceDir = await findWorkspaceDir(cwd); + + const workingDir = result.configFile + ? dirname(result.configFile) + : packageJsonPath + ? dirname(packageJsonPath) + : cwd; + const config = "config" in result.config ? (result.config.config as TriggerConfig) : result.config;