Skip to content

Commit 3d5cffc

Browse files
authored
fix(cli): point to init when dev or update runs without a project (#3929)
## Summary Running `trigger.dev dev` before setting up a project crashed with a raw `Cannot find matching package.json` stack trace from a transitive dependency, instead of telling the user what to do next. It happens whenever `dev` (or `update`) runs in a directory with no `package.json` in it or any parent directory, for example right after creating an empty project folder, or when `init` was exited before it scaffolded anything. The CLI now detects the missing project and prints actionable guidance pointing at `init`. ## Fix `dev` runs an embedded package-version check before it loads any project config. That check resolved `package.json` through a helper that throws when nothing is found up the tree, and nothing caught it. It is now wrapped, so a missing `package.json` produces a clear "run init" message and a clean exit. The config loader had the same latent crash on the `--skip-update-check` path. Its resolvers for `package.json`, the lockfile, and the workspace root all ran before the friendly "couldn't find your trigger.config.ts" check, so any of them throwing masked it. That check now runs first and short-circuits before the resolvers touch the filesystem. Verified live: in an empty directory, `dev`, `dev --skip-update-check`, and `update` all print a "run init" message and exit cleanly; in a configured project, `dev` still resolves config and boots normally.
1 parent 43b4936 commit 3d5cffc

3 files changed

Lines changed: 44 additions & 15 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"trigger.dev": patch
3+
---
4+
5+
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.

packages/cli-v3/src/commands/update.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,35 @@ export async function updateTriggerPackages(
6666

6767
const projectPath = resolve(process.cwd(), dir);
6868

69-
const { packageJson, readonlyPackageJson, packageJsonPath } = await getPackageJson(projectPath);
69+
let packageJsonResult: Awaited<ReturnType<typeof getPackageJson>> | undefined;
70+
71+
try {
72+
packageJsonResult = await getPackageJson(projectPath);
73+
} catch (error) {
74+
// resolvePackageJSON throws when there's no package.json in projectPath or any parent
75+
// directory — usually because the command ran before the project was set up. Don't crash
76+
// with a raw stack trace; fall through to the actionable guidance below.
77+
logger.debug("Failed to resolve package.json for update check", { projectPath, error });
78+
}
79+
80+
if (!packageJsonResult?.packageJson) {
81+
prettyError(
82+
"No package.json found",
83+
`Couldn't find a package.json in ${projectPath} or any parent directory.`,
84+
"Run `npx trigger.dev@latest init` to set up your project, then try again."
85+
);
86+
87+
// When embedded in another command (e.g. `dev`), there's nothing to run without a project,
88+
// so stop here with a clean exit instead of letting the caller fail again downstream.
89+
if (embedded) {
90+
process.exit(1);
91+
}
7092

71-
if (!packageJson) {
72-
log.error("Failed to load package.json. Try to re-run with `-l debug` to see what's going on.");
7393
return false;
7494
}
7595

96+
const { packageJson, readonlyPackageJson, packageJsonPath } = packageJsonResult;
97+
7698
const newCliVersion = await updateCheck();
7799

78100
if (newCliVersion && !cliVersion.startsWith("0.0.0")) {

packages/cli-v3/src/config.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -152,18 +152,9 @@ async function resolveConfig(
152152
overrides?: Partial<TriggerConfig>,
153153
warn = true
154154
): Promise<ResolvedConfig> {
155-
const packageJsonPath = await resolvePackageJSON(cwd);
156-
const tsconfigPath = await safeResolveTsConfig(cwd);
157-
const lockfilePath = await resolveLockfile(cwd);
158-
const workspaceDir = await findWorkspaceDir(cwd);
159-
160-
const workingDir = result.configFile
161-
? dirname(result.configFile)
162-
: packageJsonPath
163-
? dirname(packageJsonPath)
164-
: cwd;
165-
166-
// `trigger.config` is the fallback value set by c12
155+
// `trigger.config` is the fallback value set by c12. Bail out with actionable guidance before
156+
// touching the filesystem: the pkg-types resolvers below throw raw errors when run outside a
157+
// project (e.g. `dev` before `init`), which would mask this message.
167158
const missingConfigFile = !result.configFile || result.configFile === "trigger.config";
168159

169160
if (missingConfigFile) {
@@ -178,6 +169,17 @@ async function resolveConfig(
178169
);
179170
}
180171

172+
const packageJsonPath = await resolvePackageJSON(cwd);
173+
const tsconfigPath = await safeResolveTsConfig(cwd);
174+
const lockfilePath = await resolveLockfile(cwd);
175+
const workspaceDir = await findWorkspaceDir(cwd);
176+
177+
const workingDir = result.configFile
178+
? dirname(result.configFile)
179+
: packageJsonPath
180+
? dirname(packageJsonPath)
181+
: cwd;
182+
181183
const config =
182184
"config" in result.config ? (result.config.config as TriggerConfig) : result.config;
183185

0 commit comments

Comments
 (0)