Skip to content

Commit fcc491a

Browse files
Recognize Hydrogen as a known unsupported framework in autoconfig (#13187)
Co-authored-by: Somhairle MacLeòid <smacleod@cloudflare.com>
1 parent 02ab1b4 commit fcc491a

10 files changed

Lines changed: 115 additions & 45 deletions

File tree

.changeset/lemon-hoops-stick.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
Recognize Hydrogen as a known unsupported framework in autoconfig
6+
7+
Previously, Hydrogen projects were incorrectly identified as React Router (since Hydrogen uses React Router under the hood), leading to a confusing autoconfig experience. Hydrogen is now recognized as a distinct unsupported framework, so users see a clear message that Hydrogen is not yet supported instead of being guided through React Router configuration.

packages/wrangler/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
"@cloudflare/workers-utils": "workspace:*",
8989
"@cloudflare/workflows-shared": "workspace:*",
9090
"@cspotcode/source-map-support": "0.8.1",
91-
"@netlify/build-info": "^10.2.0",
91+
"@netlify/build-info": "^10.5.1",
9292
"@sentry/node": "^7.86.0",
9393
"@sentry/types": "^7.86.0",
9494
"@sentry/utils": "^7.86.0",

packages/wrangler/src/__tests__/autoconfig/details/framework-detection/multiple-frameworks-detected.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,25 @@ describe("detectFramework() / multiple frameworks detected", () => {
7171
expect(result.detectedFramework?.framework.id).toBe("waku");
7272
});
7373

74+
it("returns Hydrogen (not React Router) when both Hydrogen and React Router are detected", async ({
75+
expect,
76+
}) => {
77+
await seed({
78+
"package.json": JSON.stringify({
79+
dependencies: {
80+
"@shopify/hydrogen": "2024",
81+
"@react-router/dev": "7",
82+
"react-router": "7",
83+
},
84+
}),
85+
"package-lock.json": JSON.stringify({ lockfileVersion: 3 }),
86+
});
87+
88+
const result = await detectFramework(process.cwd());
89+
90+
expect(result.detectedFramework?.framework.id).toBe("hydrogen");
91+
});
92+
7493
it("returns first framework without throwing when multiple unknown frameworks are detected", async ({
7594
expect,
7695
}) => {
@@ -133,6 +152,25 @@ describe("detectFramework() / multiple frameworks detected", () => {
133152
);
134153
});
135154

155+
it("does not throw when Hydrogen and React Router are detected (Hydrogen is selected)", async ({
156+
expect,
157+
}) => {
158+
await seed({
159+
"package.json": JSON.stringify({
160+
dependencies: {
161+
"@shopify/hydrogen": "2024",
162+
"@react-router/dev": "7",
163+
"react-router": "7",
164+
},
165+
}),
166+
"package-lock.json": JSON.stringify({ lockfileVersion: 3 }),
167+
});
168+
169+
const result = await detectFramework(process.cwd());
170+
171+
expect(result.detectedFramework?.framework.id).toBe("hydrogen");
172+
});
173+
136174
it("does not throw when Vite and another known framework are detected (Vite is filtered out)", async ({
137175
expect,
138176
}) => {
Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,37 @@
11
import { describe, it } from "vitest";
2-
import { getFrameworkClass } from "../../../autoconfig/frameworks";
2+
import { getFrameworkClassInstance } from "../../../autoconfig/frameworks";
33
import { NextJs } from "../../../autoconfig/frameworks/next";
4+
import { NoOpFramework } from "../../../autoconfig/frameworks/no-op";
45
import { Static } from "../../../autoconfig/frameworks/static";
56

6-
describe("getFrameworkClass()", () => {
7+
describe("getFrameworkClassInstance()", () => {
78
it("should return a Static framework when frameworkId is unknown", ({
89
expect,
910
}) => {
10-
const framework = getFrameworkClass("unknown-framework");
11+
const framework = getFrameworkClassInstance("unknown-framework");
1112

1213
expect(framework).toBeInstanceOf(Static);
1314
expect(framework.id).toBe("static");
1415
expect(framework.name).toBe("Static");
1516
});
1617

17-
it("should return a target framework when frameworkId is known", ({
18+
it("should return a target framework when frameworkId is known and supported", ({
1819
expect,
1920
}) => {
20-
const framework = getFrameworkClass("next");
21+
const framework = getFrameworkClassInstance("next");
2122

2223
expect(framework).toBeInstanceOf(NextJs);
2324
expect(framework.id).toBe("next");
2425
expect(framework.name).toBe("Next.js");
2526
});
27+
28+
it("should return a NoOpFramework for an unsupported framework (hono)", ({
29+
expect,
30+
}) => {
31+
const framework = getFrameworkClassInstance("hono");
32+
33+
expect(framework).toBeInstanceOf(NoOpFramework);
34+
expect(framework.id).toBe("hono");
35+
expect(framework.name).toBe("Hono");
36+
});
2637
});

packages/wrangler/src/autoconfig/details/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { confirm, prompt, select } from "../../dialogs";
1414
import { logger } from "../../logger";
1515
import { sendMetricsEvent } from "../../metrics";
1616
import { NpmPackageManager } from "../../package-manager";
17-
import { getFrameworkClass } from "../frameworks";
17+
import { getFrameworkClassInstance } from "../frameworks";
1818
import {
1919
allFrameworksInfos,
2020
staticFramework,
@@ -158,7 +158,7 @@ export async function getDetailsForAutoConfig({
158158
const { detectedFramework, packageManager, isWorkspaceRoot } =
159159
await detectFramework(projectPath, wranglerConfig);
160160

161-
const framework = getFrameworkClass(detectedFramework.framework.id);
161+
const framework = getFrameworkClassInstance(detectedFramework.framework.id);
162162
const packageJsonPath = resolve(projectPath, "package.json");
163163

164164
let packageJson: PackageJSON | undefined;
@@ -445,7 +445,7 @@ export async function confirmAutoConfigDetails(
445445
}
446446
);
447447

448-
updatedAutoConfigDetails.framework = getFrameworkClass(frameworkId);
448+
updatedAutoConfigDetails.framework = getFrameworkClassInstance(frameworkId);
449449

450450
const outputDir = await prompt(
451451
"What directory contains your applications' output/asset files?",

packages/wrangler/src/autoconfig/frameworks/all-frameworks.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ import assert from "node:assert";
22
import { Analog } from "./analog";
33
import { Angular } from "./angular";
44
import { Astro } from "./astro";
5-
import { Hono } from "./hono";
65
import { NextJs } from "./next";
76
import { Nuxt } from "./nuxt";
8-
import { CloudflarePages } from "./pages";
97
import { Qwik } from "./qwik";
108
import { ReactRouter } from "./react-router";
119
import { SolidStart } from "./solid-start";
@@ -67,7 +65,6 @@ export const allKnownFrameworks = [
6765
{
6866
id: "hono",
6967
name: "Hono",
70-
class: Hono,
7168
supported: false,
7269
},
7370
{
@@ -211,20 +208,25 @@ export const allKnownFrameworks = [
211208
{
212209
id: "cloudflare-pages",
213210
name: "Cloudflare Pages",
214-
class: CloudflarePages,
215211
// Autoconfiguring a Pages project into a Workers one is not yet supported
216212
supported: false,
217213
},
214+
{
215+
id: "hydrogen",
216+
name: "Hydrogen",
217+
supported: false,
218+
},
218219
] as const satisfies FrameworkInfo[];
219220

220221
/**
221222
* Type specific for the "static" framework.
222223
*
223224
* It is supported by autoconfig but, unlike all other frameworks, it doesn't have a package associated to it
224225
*/
225-
type StaticFrameworkInfo = Omit<FrameworkInfo, "frameworkPackageInfo"> & {
226-
supported: true;
227-
};
226+
type StaticFrameworkInfo = Omit<
227+
Extract<FrameworkInfo, { supported: true }>,
228+
"frameworkPackageInfo"
229+
>;
228230

229231
export const staticFramework = {
230232
id: "static",

packages/wrangler/src/autoconfig/frameworks/index.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import assert from "node:assert";
22
import { allFrameworksInfos, staticFramework } from "./all-frameworks";
3+
import { NoOpFramework } from "./no-op";
34
import type { Framework } from "./framework-class";
45

56
export type { Framework, PackageJsonScriptsOverrides } from "./framework-class";
@@ -20,17 +21,22 @@ export function isKnownFramework(frameworkId: string): boolean {
2021
}
2122

2223
/**
23-
* Gets the class for a framework based on its id
24+
* Gets a class instance for a framework based on its id
2425
*
2526
* @param frameworkId The target framework's id
2627
* @returns The class for the framework, defaulting to the static framework is the id is not recognized
2728
*/
28-
export function getFrameworkClass(frameworkId: FrameworkInfo["id"]): Framework {
29+
export function getFrameworkClassInstance(
30+
frameworkId: FrameworkInfo["id"]
31+
): Framework {
2932
const targetedFramework = allFrameworksInfos.find(
3033
(framework) => framework.id === frameworkId
3134
);
3235
const framework = targetedFramework ?? staticFramework;
33-
return new framework.class({ id: framework.id, name: framework.name });
36+
const targetClass =
37+
framework.supported === false ? NoOpFramework : framework.class;
38+
39+
return new targetClass({ id: framework.id, name: framework.name });
3440
}
3541

3642
/**
@@ -55,11 +61,11 @@ export function isFrameworkSupported(
5561
export type FrameworkInfo = {
5662
id: string;
5763
name: string;
58-
class: typeof Framework;
5964
} & (
6065
| { supported: false }
6166
| {
6267
supported: true;
68+
class: typeof Framework;
6369
frameworkPackageInfo: AutoConfigFrameworkPackageInfo;
6470
}
6571
);

packages/wrangler/src/autoconfig/frameworks/hono.ts renamed to packages/wrangler/src/autoconfig/frameworks/no-op.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Framework } from "./framework-class";
22
import type { ConfigurationResults } from "./framework-class";
33

4-
export class Hono extends Framework {
4+
export class NoOpFramework extends Framework {
55
async configure(): Promise<ConfigurationResults> {
66
return {
77
wranglerConfig: {},

packages/wrangler/src/autoconfig/frameworks/pages.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

pnpm-lock.yaml

Lines changed: 30 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)