Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/twelve-signs-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": patch
---

Skip lock file warning for static projects during autoconfig

Previously, running autoconfig on a static project (one with no framework detected) would emit a misleading warning about a missing lock file, suggesting the project might be in a workspace. Since static projects don't require a lock file, this warning is now suppressed for them.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { writeFile } from "node:fs/promises";
import { seed } from "@cloudflare/workers-utils/test-helpers";
import { describe, it } from "vitest";
import { detectFramework } from "../../../../autoconfig/details/framework-detection";
import { runInTempDir } from "../../../helpers/run-in-tmp";

describe("detectFramework() / basic framework detection", () => {
runInTempDir();

it("defaults to the static framework when no framework is detected", async ({
expect,
}) => {
await writeFile(
"package-lock.json",
JSON.stringify({ lockfileVersion: 3 })
);

const result = await detectFramework(process.cwd());

expect(result.detectedFramework.framework.id).toBe("static");
});

it("detects astro when astro is in dependencies", async ({ expect }) => {
await seed({
"package.json": JSON.stringify({ dependencies: { astro: "5" } }),
"package-lock.json": JSON.stringify({ lockfileVersion: 3 }),
});

const result = await detectFramework(process.cwd());

expect(result.detectedFramework?.framework.id).toBe("astro");
expect(result.detectedFramework?.framework.name).toBe("Astro");
});

it("includes buildCommand in detectedFramework when available", async ({
expect,
}) => {
await seed({
"package.json": JSON.stringify({ dependencies: { astro: "5" } }),
"package-lock.json": JSON.stringify({ lockfileVersion: 3 }),
});

const result = await detectFramework(process.cwd());

expect(result.detectedFramework?.buildCommand).toBeDefined();
expect(result.detectedFramework?.buildCommand).toContain("astro build");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { seed } from "@cloudflare/workers-utils/test-helpers";
import { describe, it } from "vitest";
import { detectFramework } from "../../../../autoconfig/details/framework-detection";
import { mockConsoleMethods } from "../../../helpers/mock-console";
import { runInTempDir } from "../../../helpers/run-in-tmp";

const noLockFileWarning =
"No lock file has been detected in the current working directory. This might indicate that the project is part of a workspace.";

describe("detectFramework() / lock file warning", () => {
runInTempDir();
const std = mockConsoleMethods();

it("warns when no lock file is detected", async ({ expect }) => {
await seed({
"package.json": JSON.stringify({ dependencies: { astro: "5" } }),
});

await detectFramework(process.cwd());

expect(std.warn).toContain(noLockFileWarning);
});

it("does not warn for static projects without a lock file", async ({
expect,
}) => {
await seed({
"package.json": JSON.stringify({}),
});

await detectFramework(process.cwd());

expect(std.warn).not.toContain(noLockFileWarning);
});

it("does not warn when a lock file exists", async ({ expect }) => {
await seed({
"package.json": JSON.stringify({ dependencies: { astro: "5" } }),
"package-lock.json": JSON.stringify({ lockfileVersion: 3 }),
});

await detectFramework(process.cwd());

expect(std.warn).not.toContain(noLockFileWarning);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { seed } from "@cloudflare/workers-utils/test-helpers";
import { afterEach, beforeEach, describe, it, vi } from "vitest";
import { detectFramework } from "../../../../autoconfig/details/framework-detection";
import * as isInteractiveModule from "../../../../is-interactive";
import { runInTempDir } from "../../../helpers/run-in-tmp";
import type { MockInstance } from "vitest";

describe("detectFramework() / multiple frameworks detected", () => {
runInTempDir();
let isNonInteractiveOrCISpy: MockInstance;

beforeEach(() => {
isNonInteractiveOrCISpy = vi
.spyOn(isInteractiveModule, "isNonInteractiveOrCI")
.mockReturnValue(false);
});

afterEach(() => {
vi.unstubAllGlobals();
isNonInteractiveOrCISpy.mockRestore();
});

describe("non-CI environment", () => {
beforeEach(() => {
isNonInteractiveOrCISpy.mockReturnValue(false);
});

it("returns the known framework when multiple are detected but only one is known", async ({
expect,
}) => {
// gatsby is not in allKnownFrameworks, only astro is
await seed({
"package.json": JSON.stringify({
dependencies: { astro: "5", gatsby: "5" },
}),
"package-lock.json": JSON.stringify({ lockfileVersion: 3 }),
});

const result = await detectFramework(process.cwd());

expect(result.detectedFramework?.framework.id).toBe("astro");
});

it("filters out Vite and returns the other known framework", async ({
expect,
}) => {
await seed({
"package.json": JSON.stringify({
dependencies: { next: "14", vite: "5" },
}),
"package-lock.json": JSON.stringify({ lockfileVersion: 3 }),
});

const result = await detectFramework(process.cwd());

expect(result.detectedFramework?.framework.id).toBe("next");
});

it("returns Waku (not Hono) when both Waku and Hono are detected", async ({
expect,
}) => {
await seed({
"package.json": JSON.stringify({
dependencies: { waku: "0.21", hono: "4" },
}),
"package-lock.json": JSON.stringify({ lockfileVersion: 3 }),
});

const result = await detectFramework(process.cwd());

expect(result.detectedFramework?.framework.id).toBe("waku");
});
Comment thread
dario-piotrowicz marked this conversation as resolved.

it("returns first framework without throwing when multiple unknown frameworks are detected", async ({
expect,
}) => {
// Both gatsby and gridsome are unknown to wrangler
await seed({
"package.json": JSON.stringify({
dependencies: { gatsby: "5", gridsome: "1" },
}),
"package-lock.json": JSON.stringify({ lockfileVersion: 3 }),
});

// Should not throw even with multiple unknowns in non-CI mode
await expect(detectFramework(process.cwd())).resolves.toBeDefined();
});
});

describe("CI environment", () => {
beforeEach(() => {
isNonInteractiveOrCISpy.mockReturnValue(true);
});

it("throws MultipleFrameworksCIError when multiple known frameworks are detected", async ({
expect,
}) => {
await seed({
"package.json": JSON.stringify({
dependencies: { astro: "5", nuxt: "3" },
}),
"package-lock.json": JSON.stringify({ lockfileVersion: 3 }),
});

await expect(
detectFramework(process.cwd())
).rejects.toThrowErrorMatchingInlineSnapshot(
`
[Error: Wrangler was unable to automatically configure your project to work with Cloudflare, since multiple frameworks were found: Astro, Nuxt.

To fix this issue either:
- check your project's configuration to make sure that the target framework
is the only configured one and try again
- run \`wrangler setup\` locally to get an interactive user experience where
you can specify what framework you want to target
]
`
);
});

it("throws MultipleFrameworksCIError when multiple unknown frameworks are detected", async ({
expect,
}) => {
await seed({
"package.json": JSON.stringify({
dependencies: { gatsby: "5", gridsome: "1" },
}),
"package-lock.json": JSON.stringify({ lockfileVersion: 3 }),
});

await expect(detectFramework(process.cwd())).rejects.toThrowError(
/Wrangler was unable to automatically configure your project to work with Cloudflare, since multiple frameworks were found/
);
});

it("does not throw when Vite and another known framework are detected (Vite is filtered out)", async ({
expect,
}) => {
await seed({
"package.json": JSON.stringify({
dependencies: { astro: "5", vite: "5" },
}),
"package-lock.json": JSON.stringify({ lockfileVersion: 3 }),
});

const result = await detectFramework(process.cwd());

expect(result.detectedFramework?.framework.id).toBe("astro");
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { seed } from "@cloudflare/workers-utils/test-helpers";
import { describe, it } from "vitest";
import { detectFramework } from "../../../../autoconfig/details/framework-detection";
import {
BunPackageManager,
NpmPackageManager,
PnpmPackageManager,
YarnPackageManager,
} from "../../../../package-manager";
import { runInTempDir } from "../../../helpers/run-in-tmp";

describe("detectFramework() / package manager detection", () => {
runInTempDir();

it("detects npm when package-lock.json is present", async ({ expect }) => {
await seed({
"package.json": JSON.stringify({ dependencies: { astro: "5" } }),
"package-lock.json": JSON.stringify({ lockfileVersion: 3 }),
});

const result = await detectFramework(process.cwd());

expect(result.packageManager).toStrictEqual(NpmPackageManager);
});

it("detects pnpm when pnpm-lock.yaml is present", async ({ expect }) => {
await seed({
"package.json": JSON.stringify({ dependencies: { astro: "5" } }),
"pnpm-lock.yaml": "lockfileVersion: '6.0'\n",
});

const result = await detectFramework(process.cwd());

expect(result.packageManager).toStrictEqual(PnpmPackageManager);
});

it("detects yarn when yarn.lock is present", async ({ expect }) => {
await seed({
"package.json": JSON.stringify({ dependencies: { astro: "5" } }),
"yarn.lock": "# yarn lockfile v1\n",
});

const result = await detectFramework(process.cwd());

expect(result.packageManager).toStrictEqual(YarnPackageManager);
});

it("detects bun when bun.lock is present", async ({ expect }) => {
await seed({
"package.json": JSON.stringify({ dependencies: { astro: "5" } }),
"bun.lock": "",
});

const result = await detectFramework(process.cwd());

expect(result.packageManager).toStrictEqual(BunPackageManager);
});

it("falls back to npm when no package manager lock file is present", async ({
expect,
}) => {
await seed({
"package.json": JSON.stringify({ dependencies: { astro: "5" } }),
});

const result = await detectFramework(process.cwd());

expect(result.packageManager).toStrictEqual(NpmPackageManager);
});
});
Loading
Loading