Skip to content

Commit 443d1f1

Browse files
fix: ensure that pinned versions are not converted to ranges when using npm (#10575)
1 parent dac302c commit 443d1f1

File tree

3 files changed

+100
-5
lines changed

3 files changed

+100
-5
lines changed

.changeset/cool-rocks-bet.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"create-cloudflare": patch
3+
---
4+
5+
fix: ensure that pinned versions are not converted to ranges when using npm

packages/create-cloudflare/src/helpers/__tests__/packages.test.ts

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
1-
import { existsSync } from "fs";
1+
import { existsSync } from "node:fs";
2+
import { resolve } from "node:path";
23
import { runCommand } from "helpers/command";
34
import { installPackages, installWrangler, npmInstall } from "helpers/packages";
45
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
56
import whichPMRuns from "which-pm-runs";
67
import { createTestContext } from "../../__tests__/helpers";
8+
import * as files from "../files";
79
import { mockPackageManager } from "./mocks";
810

911
vi.mock("fs");
1012
vi.mock("which-pm-runs");
1113
vi.mock("which-pm-runs");
1214
vi.mock("helpers/command");
15+
vi.mock("../files", () => ({
16+
readJSON: vi.fn(),
17+
writeJSON: vi.fn(),
18+
}));
19+
const mockReadJSON = vi.mocked(files.readJSON);
20+
const mockWriteJSON = vi.mocked(files.writeJSON);
1321

1422
describe("Package Helpers", () => {
1523
beforeEach(() => {
1624
vi.mocked(whichPMRuns).mockReturnValue({ name: "npm", version: "8.3.1" });
1725
vi.mocked(existsSync).mockImplementation(() => false);
1826
});
1927

20-
afterEach(() => {});
28+
afterEach(() => {
29+
vi.clearAllMocks();
30+
});
2131

2232
describe("npmInstall", () => {
2333
test("npm", async () => {
@@ -62,13 +72,37 @@ describe("Package Helpers", () => {
6272
"with $pm",
6373
async ({ pm, initialArgs, additionalArgs }) => {
6474
mockPackageManager(pm);
65-
const packages = ["foo", "bar", "baz"];
75+
mockReadJSON.mockReturnValue({
76+
["dependencies"]: {
77+
foo: "^1.0.0",
78+
bar: "^2.0.0",
79+
baz: "^1.2.3",
80+
},
81+
});
82+
const packages = ["foo", "bar@latest", "[email protected]"];
6683
await installPackages(packages);
6784

6885
expect(vi.mocked(runCommand)).toHaveBeenCalledWith(
6986
[...initialArgs, ...packages, ...(additionalArgs ?? [])],
7087
expect.anything(),
7188
);
89+
90+
if (pm === "npm") {
91+
// Check that package.json was updated for npm
92+
expect(mockReadJSON).toHaveBeenCalledWith(
93+
resolve(process.cwd(), "package.json"),
94+
);
95+
expect(mockWriteJSON).toHaveBeenCalledWith(
96+
resolve(process.cwd(), "package.json"),
97+
expect.objectContaining({
98+
["dependencies"]: {
99+
foo: "^1.0.0",
100+
bar: "^2.0.0",
101+
baz: "1.2.3",
102+
},
103+
}),
104+
);
105+
}
72106
},
73107
);
74108

@@ -87,18 +121,47 @@ describe("Package Helpers", () => {
87121
"with $pm (dev = true)",
88122
async ({ pm, initialArgs, additionalArgs }) => {
89123
mockPackageManager(pm);
90-
const packages = ["foo", "bar", "baz"];
124+
mockReadJSON.mockReturnValue({
125+
["devDependencies"]: {
126+
foo: "^1.0.0",
127+
bar: "^2.0.0",
128+
baz: "^1.2.3",
129+
},
130+
});
131+
const packages = ["foo", "bar@latest", "[email protected]"];
91132
await installPackages(packages, { dev: true });
92133

93134
expect(vi.mocked(runCommand)).toHaveBeenCalledWith(
94135
[...initialArgs, ...packages, ...(additionalArgs ?? [])],
95136
expect.anything(),
96137
);
138+
139+
if (pm === "npm") {
140+
// Check that package.json was updated for npm
141+
expect(mockReadJSON).toHaveBeenCalledWith(
142+
resolve(process.cwd(), "package.json"),
143+
);
144+
expect(mockWriteJSON).toHaveBeenCalledWith(
145+
resolve(process.cwd(), "package.json"),
146+
expect.objectContaining({
147+
["devDependencies"]: {
148+
foo: "^1.0.0",
149+
bar: "^2.0.0",
150+
baz: "1.2.3",
151+
},
152+
}),
153+
);
154+
}
97155
},
98156
);
99157
});
100158

101159
test("installWrangler", async () => {
160+
mockReadJSON.mockReturnValue({
161+
["devDependencies"]: {
162+
wrangler: "^4.0.0",
163+
},
164+
});
102165
await installWrangler();
103166

104167
expect(vi.mocked(runCommand)).toHaveBeenCalledWith(

packages/create-cloudflare/src/helpers/packages.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import assert from "assert";
12
import { existsSync } from "fs";
23
import path from "path";
34
import { brandColor, dim } from "@cloudflare/cli/colors";
45
import { fetch } from "undici";
56
import { runCommand } from "./command";
7+
import { readJSON, writeJSON } from "./files";
68
import { detectPackageManager } from "./packageManagers";
7-
import type { C3Context } from "types";
9+
import type { C3Context, PackageJson } from "types";
810

911
type InstallConfig = {
1012
startText?: string;
@@ -24,6 +26,10 @@ export const installPackages = async (
2426
packages: string[],
2527
config: InstallConfig = {},
2628
) => {
29+
if (packages.length === 0) {
30+
return;
31+
}
32+
2733
const { npm } = detectPackageManager();
2834

2935
let saveFlag;
@@ -61,6 +67,27 @@ export const installPackages = async (
6167
silent: true,
6268
},
6369
);
70+
71+
if (npm === "npm") {
72+
// Npm install will update the package.json with a hat-range rather than the exact version/range we asked for.
73+
// So let's just fix that up now by rewriting the package.json.
74+
const pkgJsonPath = path.join(process.cwd(), "package.json");
75+
const pkgJson = readJSON(pkgJsonPath) as PackageJson;
76+
const deps = config.dev ? pkgJson.devDependencies : pkgJson.dependencies;
77+
assert(deps, "dependencies should be defined");
78+
for (const pkg of packages) {
79+
const versionMarker = pkg.lastIndexOf("@");
80+
if (versionMarker > 0) {
81+
// (if versionMarker was 0 then this would indicate a scoped package with no version)
82+
const pkgName = pkg.slice(0, versionMarker);
83+
const pkgVersion = pkg.slice(versionMarker + 1);
84+
if (pkgVersion !== "latest") {
85+
deps[pkgName] = pkgVersion;
86+
}
87+
}
88+
}
89+
writeJSON(pkgJsonPath, pkgJson);
90+
}
6491
};
6592

6693
/**

0 commit comments

Comments
 (0)