From c65b7a9f1f2d9876fa51a2e720f86fc238dd3870 Mon Sep 17 00:00:00 2001 From: MattieTK Date: Sun, 11 Oct 2026 01:36:13 +0100 Subject: [PATCH] fix: analyse the project again after framework setup prepareProject returned the details detected before framework setup, so cf dev and cf build used pre-setup commands, environment, and mode support on their first run. Setup can change a project's tooling (for example autoconfig setting up Next.js with vinext), so analyse the configured project again, as the Wrangler-migration route already does. --- .changeset/refresh-project-after-setup.md | 7 ++++ .../lib/autoconfig-migration.test.ts | 39 ++++++++++++++++++- packages/cli/src/lib/autoconfig.ts | 26 ++++++++----- 3 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 .changeset/refresh-project-after-setup.md diff --git a/.changeset/refresh-project-after-setup.md b/.changeset/refresh-project-after-setup.md new file mode 100644 index 000000000..751fa452b --- /dev/null +++ b/.changeset/refresh-project-after-setup.md @@ -0,0 +1,7 @@ +--- +"cf": patch +--- + +Use the configured project's commands on the first `cf dev` or `cf build` after setup + +When `cf dev` or `cf build` set up a project, they kept the commands and environment detected before setup. If setup changes the project's tooling, for example by setting up a Next.js project with vinext, the first run would use the old commands. cf now analyses the project again after setup, as it already did after converting a Wrangler config. diff --git a/packages/cli/src/__tests__/lib/autoconfig-migration.test.ts b/packages/cli/src/__tests__/lib/autoconfig-migration.test.ts index a19a0b954..9b891fe9b 100644 --- a/packages/cli/src/__tests__/lib/autoconfig-migration.test.ts +++ b/packages/cli/src/__tests__/lib/autoconfig-migration.test.ts @@ -86,7 +86,42 @@ describe("project preparation", () => { outputDir: "dist", buildCommand: "npm run build", }; - mocks.getDetailsForAutoConfig.mockResolvedValue(unconfiguredDetails); + const configuredDetails = { + ...unconfiguredDetails, + configured: true, + devCommand: "npx vite dev", + env: { CONFIGURED: "true" }, + }; + mocks.getDetailsForAutoConfig + .mockResolvedValueOnce(unconfiguredDetails) + .mockResolvedValueOnce(configuredDetails); + mocks.maybeMigrateWranglerProject.mockResolvedValue(false); + mocks.runAutoConfig.mockResolvedValue(configuration); + + await expect(prepareProject("/project")).resolves.toEqual({ + details: configuredDetails, + configuration, + }); + expect(mocks.runAutoConfig).toHaveBeenCalledWith( + unconfiguredDetails, + expect.objectContaining({ runBuild: false }) + ); + expect(mocks.getDetailsForAutoConfig).toHaveBeenCalledTimes(2); + }); + + it("keeps the pre-setup details when the configured project cannot be analyzed", async () => { + const configuration = { scripts: {}, outputDir: "dist" }; + mocks.getDetailsForAutoConfig + .mockResolvedValueOnce(unconfiguredDetails) + .mockRejectedValueOnce( + new (await import("@cloudflare/autoconfig")).AutoConfigDetectionError( + "not detected", + { + telemetryMessage: "not detected", + configured: false, + } + ) + ); mocks.maybeMigrateWranglerProject.mockResolvedValue(false); mocks.runAutoConfig.mockResolvedValue(configuration); @@ -94,7 +129,6 @@ describe("project preparation", () => { details: unconfiguredDetails, configuration, }); - expect(mocks.runAutoConfig).toHaveBeenCalledOnce(); }); it("dry-runs framework setup when Wrangler config conversion did not run", async () => { @@ -112,6 +146,7 @@ describe("project preparation", () => { unconfiguredDetails, expect.objectContaining({ dryRun: true, runBuild: false }) ); + expect(mocks.getDetailsForAutoConfig).toHaveBeenCalledOnce(); expect(mocks.maybeMigrateWranglerProject).toHaveBeenCalledWith( "/project", expect.any(Function), diff --git a/packages/cli/src/lib/autoconfig.ts b/packages/cli/src/lib/autoconfig.ts index 15d9f6a85..d5563eb4b 100644 --- a/packages/cli/src/lib/autoconfig.ts +++ b/packages/cli/src/lib/autoconfig.ts @@ -100,8 +100,10 @@ export async function configureProject( /** * Choose project setup before a build: an accepted Wrangler config conversion - * takes precedence over framework setup. In a dry run, `setupNeeded` means - * a setup route was selected but not applied, so callers must skip the build. + * takes precedence over framework setup. After either route is applied, the + * returned details describe the configured project. In a dry run, + * `setupNeeded` means a setup route was selected but not applied, so callers + * must skip the build. */ export async function prepareProject( cwd: string, @@ -131,14 +133,20 @@ export async function prepareProject( return { details }; } + if (!details) { + return { details }; + } + + const configuration = await configureProject(details, options); + if (options.dryRun) { + return { details, configuration, setupNeeded: true }; + } + + // Setup can change which tools the project uses, so callers need the + // details of the configured project rather than the pre-setup analysis. return { - details, - ...(details - ? { - configuration: await configureProject(details, options), - ...(options.dryRun ? { setupNeeded: true as const } : {}), - } - : {}), + details: (await analyzeProject(cwd, options)) ?? details, + configuration, }; }