Skip to content

Commit 3119f94

Browse files
authored
feat: only show npm flag for react 18 deps (#7573)
* feat: only show npm flag for react 18 deps * tests: update
1 parent 057d97d commit 3119f94

File tree

4 files changed

+173
-143
lines changed

4 files changed

+173
-143
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"shadcn": patch
3+
---
4+
5+
update npm flag

packages/shadcn/src/utils/updaters/update-dependencies.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export async function updateDependencies(
3434

3535
// Offer to use --force or --legacy-peer-deps if using React 19 with npm.
3636
let flag = ""
37-
if (isUsingReact19(config) && packageManager === "npm") {
37+
if (shouldPromptForNpmFlag(config) && packageManager === "npm") {
3838
if (options.silent) {
3939
flag = "force"
4040
} else {
@@ -98,12 +98,18 @@ export async function updateDependencies(
9898
dependenciesSpinner?.succeed()
9999
}
100100

101-
function isUsingReact19(config: Config) {
101+
function shouldPromptForNpmFlag(config: Config) {
102102
const packageInfo = getPackageInfo(config.resolvedPaths.cwd, false)
103103

104104
if (!packageInfo?.dependencies?.react) {
105105
return false
106106
}
107107

108-
return /^(?:\^|~)?19(?:\.\d+)*(?:-.*)?$/.test(packageInfo.dependencies.react)
108+
const hasReact19 = /^(?:\^|~)?19(?:\.\d+)*(?:-.*)?$/.test(
109+
packageInfo.dependencies.react
110+
)
111+
const hasReactDayPicker8 =
112+
packageInfo.dependencies["react-day-picker"]?.startsWith("8")
113+
114+
return hasReact19 && hasReactDayPicker8
109115
}

packages/shadcn/test/fixtures/project-npm-react19/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"author": "shadcn",
66
"license": "MIT",
77
"dependencies": {
8-
"react": "19.0.0"
8+
"react": "19.0.0",
9+
"react-day-picker": "8.0.0"
910
}
1011
}
Lines changed: 157 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1,139 +1,157 @@
1-
import { vi, describe, afterEach, test, expect } from "vitest"
2-
import { execa } from "execa"
3-
import prompts from "prompts"
4-
import { updateDependencies } from "../../../src/utils/updaters/update-dependencies"
5-
import path from "path"
6-
7-
vi.mock("execa")
8-
vi.mock("prompts")
9-
10-
describe("updateDependencies", () => {
11-
afterEach(() => {
12-
vi.restoreAllMocks()
13-
})
14-
15-
test.each([
16-
{
17-
description: "npm without react 19 includes no additional flags",
18-
options: { silent: true },
19-
dependencies: ["first", "second", "third"],
20-
devDependencies: ["fourth"],
21-
config: {
22-
resolvedPaths: {
23-
cwd: path.resolve(__dirname, "../../fixtures/project-npm")
24-
}
25-
},
26-
expectedPackageManager: "npm",
27-
expectedArgs: ["install", "first", "second", "third"],
28-
expectedDevArgs: ["install", "-D", "fourth"]
29-
},
30-
{
31-
description: "npm with react 19 applies force prompt when silent",
32-
options: { silent: true },
33-
dependencies: ["first", "second", "third"],
34-
devDependencies: ["fourth"],
35-
config: {
36-
resolvedPaths: {
37-
cwd: path.resolve(__dirname, "../../fixtures/project-npm-react19")
38-
}
39-
},
40-
expectedPackageManager: "npm",
41-
expectedArgs: ["install", "--force", "first", "second", "third"],
42-
expectedDevArgs: ["install", "--force", "-D", "fourth"]
43-
},
44-
{
45-
description: "npm with react 19 prompts for flag when not silent",
46-
flagPrompt: "legacy-peer-deps",
47-
dependencies: ["first", "second", "third"],
48-
devDependencies: ["fourth"],
49-
config: {
50-
resolvedPaths: {
51-
cwd: path.resolve(__dirname, "../../fixtures/project-npm-react19")
52-
}
53-
},
54-
expectedPackageManager: "npm",
55-
expectedArgs: ["install", "--legacy-peer-deps", "first", "second", "third"],
56-
expectedDevArgs: ["install", "--legacy-peer-deps", "-D", "fourth"]
57-
},
58-
{
59-
description: "deno uses npm: package prefix",
60-
dependencies: ["first", "second", "third"],
61-
devDependencies: ["fourth"],
62-
config: {
63-
resolvedPaths: {
64-
cwd: path.resolve(__dirname, "../../fixtures/project-deno")
65-
}
66-
},
67-
expectedPackageManager: "deno",
68-
expectedArgs: ["add", "npm:first", "npm:second", "npm:third"],
69-
expectedDevArgs: ["add", "-D", "npm:fourth"]
70-
},
71-
{
72-
description: "bun uses bun",
73-
dependencies: ["first", "second", "third"],
74-
devDependencies: ["fourth"],
75-
config: {
76-
resolvedPaths: {
77-
cwd: path.resolve(__dirname, "../../fixtures/project-bun")
78-
}
79-
},
80-
expectedPackageManager: "bun",
81-
expectedArgs: ["add", "first", "second", "third"],
82-
expectedDevArgs: ["add", "-D", "fourth"]
83-
},
84-
{
85-
description: "pnpm uses pnpm",
86-
dependencies: ["first", "second", "third"],
87-
devDependencies: ["fourth"],
88-
config: {
89-
resolvedPaths: {
90-
cwd: path.resolve(__dirname, "../../fixtures/project-pnpm")
91-
}
92-
},
93-
expectedPackageManager: "pnpm",
94-
expectedArgs: ["add", "first", "second", "third"],
95-
expectedDevArgs: ["add", "-D", "fourth"]
96-
},
97-
{
98-
description: "deduplicates input dependencies",
99-
options: { silent: true },
100-
dependencies: ["first", "first"],
101-
devDependencies: ["second", "second"],
102-
config: {
103-
resolvedPaths: {
104-
cwd: path.resolve(__dirname, "../../fixtures/project-npm")
105-
}
106-
},
107-
expectedPackageManager: "npm",
108-
expectedArgs: ["install", "first"],
109-
expectedDevArgs: ["install", "-D", "second"]
110-
}
111-
])("$description", async ({ options, flagPrompt, config, dependencies, devDependencies, expectedPackageManager, expectedArgs, expectedDevArgs }) => {
112-
113-
vi.mocked(prompts).mockResolvedValue({ flag: flagPrompt })
114-
115-
await updateDependencies(
116-
dependencies,
117-
devDependencies,
118-
config,
119-
options ?? {}
120-
)
121-
122-
if (flagPrompt) {
123-
expect(prompts).toHaveBeenCalled()
124-
}
125-
126-
127-
expect(execa).toHaveBeenCalledWith(
128-
expectedPackageManager,
129-
expectedArgs,
130-
{ cwd: config?.resolvedPaths.cwd }
131-
)
132-
133-
expect(execa).toHaveBeenCalledWith(
134-
expectedPackageManager,
135-
expectedDevArgs,
136-
{ cwd: config?.resolvedPaths.cwd }
137-
)
138-
})
139-
})
1+
import path from "path"
2+
import { execa } from "execa"
3+
import prompts from "prompts"
4+
import { afterEach, describe, expect, test, vi } from "vitest"
5+
6+
import { updateDependencies } from "../../../src/utils/updaters/update-dependencies"
7+
8+
vi.mock("execa")
9+
vi.mock("prompts")
10+
11+
describe("updateDependencies", () => {
12+
afterEach(() => {
13+
vi.restoreAllMocks()
14+
})
15+
16+
test.each([
17+
{
18+
description:
19+
"npm without react-day-picker v8 includes no additional flags",
20+
options: { silent: true },
21+
dependencies: ["first", "second", "third"],
22+
devDependencies: ["fourth"],
23+
config: {
24+
resolvedPaths: {
25+
cwd: path.resolve(__dirname, "../../fixtures/project-npm"),
26+
},
27+
},
28+
expectedPackageManager: "npm",
29+
expectedArgs: ["install", "first", "second", "third"],
30+
expectedDevArgs: ["install", "-D", "fourth"],
31+
},
32+
{
33+
description:
34+
"npm with react-day-picker v8 applies force prompt when silent",
35+
options: { silent: true },
36+
dependencies: ["first", "second", "third"],
37+
devDependencies: ["fourth"],
38+
config: {
39+
resolvedPaths: {
40+
cwd: path.resolve(__dirname, "../../fixtures/project-npm-react19"),
41+
},
42+
},
43+
expectedPackageManager: "npm",
44+
expectedArgs: ["install", "--force", "first", "second", "third"],
45+
expectedDevArgs: ["install", "--force", "-D", "fourth"],
46+
},
47+
{
48+
description:
49+
"npm with react-day-picker v8 prompts for flag when not silent",
50+
flagPrompt: "legacy-peer-deps",
51+
dependencies: ["first", "second", "third"],
52+
devDependencies: ["fourth"],
53+
config: {
54+
resolvedPaths: {
55+
cwd: path.resolve(__dirname, "../../fixtures/project-npm-react19"),
56+
},
57+
},
58+
expectedPackageManager: "npm",
59+
expectedArgs: [
60+
"install",
61+
"--legacy-peer-deps",
62+
"first",
63+
"second",
64+
"third",
65+
],
66+
expectedDevArgs: ["install", "--legacy-peer-deps", "-D", "fourth"],
67+
},
68+
{
69+
description: "deno uses npm: package prefix",
70+
dependencies: ["first", "second", "third"],
71+
devDependencies: ["fourth"],
72+
config: {
73+
resolvedPaths: {
74+
cwd: path.resolve(__dirname, "../../fixtures/project-deno"),
75+
},
76+
},
77+
expectedPackageManager: "deno",
78+
expectedArgs: ["add", "npm:first", "npm:second", "npm:third"],
79+
expectedDevArgs: ["add", "-D", "npm:fourth"],
80+
},
81+
{
82+
description: "bun uses bun",
83+
dependencies: ["first", "second", "third"],
84+
devDependencies: ["fourth"],
85+
config: {
86+
resolvedPaths: {
87+
cwd: path.resolve(__dirname, "../../fixtures/project-bun"),
88+
},
89+
},
90+
expectedPackageManager: "bun",
91+
expectedArgs: ["add", "first", "second", "third"],
92+
expectedDevArgs: ["add", "-D", "fourth"],
93+
},
94+
{
95+
description: "pnpm uses pnpm",
96+
dependencies: ["first", "second", "third"],
97+
devDependencies: ["fourth"],
98+
config: {
99+
resolvedPaths: {
100+
cwd: path.resolve(__dirname, "../../fixtures/project-pnpm"),
101+
},
102+
},
103+
expectedPackageManager: "pnpm",
104+
expectedArgs: ["add", "first", "second", "third"],
105+
expectedDevArgs: ["add", "-D", "fourth"],
106+
},
107+
{
108+
description: "deduplicates input dependencies",
109+
options: { silent: true },
110+
dependencies: ["first", "first"],
111+
devDependencies: ["second", "second"],
112+
config: {
113+
resolvedPaths: {
114+
cwd: path.resolve(__dirname, "../../fixtures/project-npm"),
115+
},
116+
},
117+
expectedPackageManager: "npm",
118+
expectedArgs: ["install", "first"],
119+
expectedDevArgs: ["install", "-D", "second"],
120+
},
121+
])(
122+
"$description",
123+
async ({
124+
options,
125+
flagPrompt,
126+
config,
127+
dependencies,
128+
devDependencies,
129+
expectedPackageManager,
130+
expectedArgs,
131+
expectedDevArgs,
132+
}) => {
133+
vi.mocked(prompts).mockResolvedValue({ flag: flagPrompt })
134+
135+
await updateDependencies(
136+
dependencies,
137+
devDependencies,
138+
config,
139+
options ?? {}
140+
)
141+
142+
if (flagPrompt) {
143+
expect(prompts).toHaveBeenCalled()
144+
}
145+
146+
expect(execa).toHaveBeenCalledWith(expectedPackageManager, expectedArgs, {
147+
cwd: config?.resolvedPaths.cwd,
148+
})
149+
150+
expect(execa).toHaveBeenCalledWith(
151+
expectedPackageManager,
152+
expectedDevArgs,
153+
{ cwd: config?.resolvedPaths.cwd }
154+
)
155+
}
156+
)
157+
})

0 commit comments

Comments
 (0)