mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-11 01:41:37 +00:00
fix(cli): update template handling for pnpm 11 (#10659)
* fix(cli): allow esbuild builds in Vite templates * fix(cli): extend pnpm 11 build-script allowlists across app templates - Add packages: [] to single-app pnpm-workspace.yaml so pnpm 9 does not reject the file with "packages field missing or empty". - Add astro-app, react-router-app, start-app, next-app workspace yamls with the build-script allowlist each template needs (esbuild, sharp, unrs-resolver as applicable). - Set msw: false across all app allowlists so the registry component install runs cleanly under pnpm 11 without executing msw's service-worker postinstall. - Add a scaffold test pinning the packages:[] + allowBuilds shape so the parser keeps treating it as single-app. * chore: changeset * fix(templates): allow monorepo pnpm builds * ci(templates): validate app workspace conversion --------- Co-authored-by: shadcn <m@shadcn.com>
This commit is contained in:
committed by
GitHub
parent
360e8a19c3
commit
07900769d9
5
.changeset/grumpy-moles-ring.md
Normal file
5
.changeset/grumpy-moles-ring.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"shadcn": patch
|
||||
---
|
||||
|
||||
update template handling
|
||||
68
.github/workflows/templates.yml
vendored
68
.github/workflows/templates.yml
vendored
@@ -131,6 +131,7 @@ jobs:
|
||||
local package_manager="$1"
|
||||
local project_path="$2"
|
||||
local check_workspace_protocol="$3"
|
||||
local is_monorepo="$4"
|
||||
|
||||
cd "$project_path"
|
||||
test ! -f pnpm-workspace.yaml
|
||||
@@ -138,6 +139,7 @@ jobs:
|
||||
|
||||
EXPECTED_PACKAGE_MANAGER="$package_manager" \
|
||||
CHECK_WORKSPACE_PROTOCOL="$check_workspace_protocol" \
|
||||
IS_MONOREPO="$is_monorepo" \
|
||||
node <<'NODE'
|
||||
const fs = require("node:fs")
|
||||
const path = require("node:path")
|
||||
@@ -145,27 +147,41 @@ jobs:
|
||||
const expectedPackageManager = process.env.EXPECTED_PACKAGE_MANAGER
|
||||
const checkWorkspaceProtocol =
|
||||
process.env.CHECK_WORKSPACE_PROTOCOL === "true"
|
||||
const isMonorepo = process.env.IS_MONOREPO === "true"
|
||||
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"))
|
||||
const workspaces = pkg.workspaces ?? []
|
||||
|
||||
if (!Array.isArray(workspaces)) {
|
||||
throw new Error("Expected package.json workspaces to be an array.")
|
||||
}
|
||||
if (isMonorepo) {
|
||||
const workspaces = pkg.workspaces ?? []
|
||||
|
||||
if (workspaces.length === 0) {
|
||||
throw new Error("Expected package.json workspaces to have entries.")
|
||||
}
|
||||
|
||||
for (const workspace of ["sharp", "unrs-resolver", "esbuild"]) {
|
||||
if (workspaces.includes(workspace)) {
|
||||
throw new Error(`Unexpected workspace entry: ${workspace}`)
|
||||
if (!Array.isArray(workspaces)) {
|
||||
throw new Error("Expected package.json workspaces to be an array.")
|
||||
}
|
||||
}
|
||||
|
||||
if (!pkg.packageManager?.startsWith(`${expectedPackageManager}@`)) {
|
||||
throw new Error(
|
||||
`Expected packageManager to use ${expectedPackageManager}, got ${pkg.packageManager}`
|
||||
)
|
||||
if (workspaces.length === 0) {
|
||||
throw new Error("Expected package.json workspaces to have entries.")
|
||||
}
|
||||
|
||||
for (const workspace of ["sharp", "unrs-resolver", "esbuild"]) {
|
||||
if (workspaces.includes(workspace)) {
|
||||
throw new Error(`Unexpected workspace entry: ${workspace}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (!pkg.packageManager?.startsWith(`${expectedPackageManager}@`)) {
|
||||
throw new Error(
|
||||
`Expected packageManager to use ${expectedPackageManager}, got ${pkg.packageManager}`
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (pkg.workspaces !== undefined) {
|
||||
throw new Error("Did not expect package.json workspaces for app template.")
|
||||
}
|
||||
|
||||
if (pkg.packageManager !== undefined) {
|
||||
throw new Error(
|
||||
`Did not expect packageManager for app template, got ${pkg.packageManager}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (checkWorkspaceProtocol) {
|
||||
@@ -213,8 +229,10 @@ jobs:
|
||||
|
||||
if [ "$mode" = "monorepo" ]; then
|
||||
args+=(--monorepo)
|
||||
is_monorepo="true"
|
||||
else
|
||||
args+=(--no-monorepo)
|
||||
is_monorepo="false"
|
||||
fi
|
||||
|
||||
case "$TEMPLATE_PACKAGE_MANAGER" in
|
||||
@@ -238,7 +256,11 @@ jobs:
|
||||
bunx --bun --package "$GITHUB_WORKSPACE/packages/shadcn" \
|
||||
shadcn "${args[@]}"
|
||||
)
|
||||
validate_non_pnpm_project "bun" "$project_path" "false"
|
||||
validate_non_pnpm_project \
|
||||
"bun" \
|
||||
"$project_path" \
|
||||
"false" \
|
||||
"$is_monorepo"
|
||||
;;
|
||||
npm)
|
||||
(
|
||||
@@ -249,7 +271,11 @@ jobs:
|
||||
npx --yes --package "$GITHUB_WORKSPACE/packages/shadcn" \
|
||||
shadcn "${args[@]}"
|
||||
)
|
||||
validate_non_pnpm_project "npm" "$project_path" "true"
|
||||
validate_non_pnpm_project \
|
||||
"npm" \
|
||||
"$project_path" \
|
||||
"true" \
|
||||
"$is_monorepo"
|
||||
;;
|
||||
yarn)
|
||||
(
|
||||
@@ -261,7 +287,11 @@ jobs:
|
||||
yarn dlx --package "$GITHUB_WORKSPACE/packages/shadcn" \
|
||||
shadcn "${args[@]}"
|
||||
)
|
||||
validate_non_pnpm_project "yarn" "$project_path" "false"
|
||||
validate_non_pnpm_project \
|
||||
"yarn" \
|
||||
"$project_path" \
|
||||
"false" \
|
||||
"$is_monorepo"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
@@ -127,7 +127,11 @@ async function adaptWorkspaceConfig(
|
||||
await fs.remove(lockFilePath)
|
||||
}
|
||||
|
||||
const isMonorepo = fs.existsSync(pnpmWorkspacePath)
|
||||
const hasPnpmWorkspaceConfig = fs.existsSync(pnpmWorkspacePath)
|
||||
const workspacePatterns = hasPnpmWorkspaceConfig
|
||||
? parsePnpmWorkspacePackages(await fs.readFile(pnpmWorkspacePath, "utf8"))
|
||||
: []
|
||||
const isMonorepo = workspacePatterns.length > 0
|
||||
|
||||
// Update root package.json: update "packageManager" field for the
|
||||
// target package manager, and add "workspaces" for npm/bun/yarn.
|
||||
@@ -145,12 +149,7 @@ async function adaptWorkspaceConfig(
|
||||
}
|
||||
|
||||
if (isMonorepo) {
|
||||
// Read workspace patterns from pnpm-workspace.yaml.
|
||||
const workspaceContent = await fs.readFile(pnpmWorkspacePath, "utf8")
|
||||
const patterns = parsePnpmWorkspacePackages(workspaceContent)
|
||||
|
||||
packageJson.workspaces = patterns
|
||||
await fs.remove(pnpmWorkspacePath)
|
||||
packageJson.workspaces = workspacePatterns
|
||||
}
|
||||
|
||||
await fs.writeFile(
|
||||
@@ -159,6 +158,10 @@ async function adaptWorkspaceConfig(
|
||||
)
|
||||
}
|
||||
|
||||
if (hasPnpmWorkspaceConfig) {
|
||||
await fs.remove(pnpmWorkspacePath)
|
||||
}
|
||||
|
||||
// Rewrite workspace: protocol references in nested package.json files.
|
||||
// npm does not support workspace: protocol; bun and yarn do, so only
|
||||
// rewrite for npm monorepo templates.
|
||||
|
||||
@@ -338,6 +338,84 @@ describe("defaultScaffold", () => {
|
||||
expect(written.packageManager).toBe("bun@1.2.0")
|
||||
})
|
||||
|
||||
it("should remove pnpm-only workspace config for non-pnpm templates", async () => {
|
||||
vi.mocked(fs.existsSync).mockImplementation((p: any) => {
|
||||
const s = p.toString()
|
||||
return s.includes("pnpm-workspace.yaml") || s.includes("package.json")
|
||||
})
|
||||
|
||||
vi.mocked(fs.readFile).mockImplementation(((filePath: string) => {
|
||||
if (filePath.includes("pnpm-workspace.yaml")) {
|
||||
return Promise.resolve("allowBuilds:\n esbuild: true\n")
|
||||
}
|
||||
return Promise.resolve(
|
||||
JSON.stringify({ name: "my-app", packageManager: "pnpm@9.0.0" })
|
||||
)
|
||||
}) as any)
|
||||
|
||||
const template = createTestTemplate()
|
||||
|
||||
await template.scaffold({
|
||||
projectPath: "/test/my-app",
|
||||
packageManager: "bun",
|
||||
cwd: "/test",
|
||||
})
|
||||
|
||||
expect(vi.mocked(fs.remove)).toHaveBeenCalledWith(
|
||||
path.join("/test/my-app", "pnpm-workspace.yaml")
|
||||
)
|
||||
|
||||
const writeCalls = vi.mocked(fs.writeFile).mock.calls
|
||||
const adaptCall = writeCalls.find(
|
||||
(call) => call[0] === path.join("/test/my-app", "package.json")
|
||||
)
|
||||
expect(adaptCall).toBeDefined()
|
||||
const written = JSON.parse(adaptCall![1] as string)
|
||||
expect(written.packageManager).toBeUndefined()
|
||||
expect(written.workspaces).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should treat single-app workspace yaml (packages:[] + allowBuilds) as non-monorepo", async () => {
|
||||
vi.mocked(fs.existsSync).mockImplementation((p: any) => {
|
||||
const s = p.toString()
|
||||
return s.includes("pnpm-workspace.yaml") || s.includes("package.json")
|
||||
})
|
||||
|
||||
vi.mocked(fs.readFile).mockImplementation(((filePath: string) => {
|
||||
if (filePath.includes("pnpm-workspace.yaml")) {
|
||||
return Promise.resolve(
|
||||
"packages: []\n\nallowBuilds:\n esbuild: true\n"
|
||||
)
|
||||
}
|
||||
return Promise.resolve(
|
||||
JSON.stringify({ name: "my-app", packageManager: "pnpm@9.0.0" })
|
||||
)
|
||||
}) as any)
|
||||
|
||||
const template = createTestTemplate()
|
||||
|
||||
await template.scaffold({
|
||||
projectPath: "/test/my-app",
|
||||
packageManager: "npm",
|
||||
cwd: "/test",
|
||||
})
|
||||
|
||||
// Inline empty packages array must not be parsed as a monorepo;
|
||||
// the yaml is stripped and no workspaces array is added.
|
||||
expect(vi.mocked(fs.remove)).toHaveBeenCalledWith(
|
||||
path.join("/test/my-app", "pnpm-workspace.yaml")
|
||||
)
|
||||
|
||||
const writeCalls = vi.mocked(fs.writeFile).mock.calls
|
||||
const adaptCall = writeCalls.find(
|
||||
(call) => call[0] === path.join("/test/my-app", "package.json")
|
||||
)
|
||||
expect(adaptCall).toBeDefined()
|
||||
const written = JSON.parse(adaptCall![1] as string)
|
||||
expect(written.packageManager).toBeUndefined()
|
||||
expect(written.workspaces).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should rewrite workspace: protocol refs to * for npm monorepo", async () => {
|
||||
vi.mocked(fs.existsSync).mockImplementation((p: any) => {
|
||||
const s = p.toString()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
packages:
|
||||
- "."
|
||||
packages: []
|
||||
|
||||
ignoredBuiltDependencies:
|
||||
- esbuild
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
sharp: true
|
||||
msw: false
|
||||
|
||||
@@ -2,6 +2,7 @@ packages:
|
||||
- "apps/*"
|
||||
- "packages/*"
|
||||
|
||||
ignoredBuiltDependencies:
|
||||
- esbuild
|
||||
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
sharp: true
|
||||
msw: false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
packages:
|
||||
- "."
|
||||
packages: []
|
||||
|
||||
ignoredBuiltDependencies:
|
||||
- sharp
|
||||
- unrs-resolver
|
||||
allowBuilds:
|
||||
sharp: true
|
||||
unrs-resolver: true
|
||||
msw: false
|
||||
|
||||
@@ -2,7 +2,7 @@ packages:
|
||||
- "apps/*"
|
||||
- "packages/*"
|
||||
|
||||
ignoredBuiltDependencies:
|
||||
- sharp
|
||||
- unrs-resolver
|
||||
|
||||
allowBuilds:
|
||||
sharp: true
|
||||
unrs-resolver: true
|
||||
msw: false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
packages:
|
||||
- "."
|
||||
packages: []
|
||||
|
||||
ignoredBuiltDependencies:
|
||||
- esbuild
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
msw: false
|
||||
|
||||
@@ -2,6 +2,6 @@ packages:
|
||||
- "apps/*"
|
||||
- "packages/*"
|
||||
|
||||
ignoredBuiltDependencies:
|
||||
- esbuild
|
||||
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
msw: false
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
packages:
|
||||
- "."
|
||||
packages: []
|
||||
|
||||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
- lightningcss
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
lightningcss: true
|
||||
unrs-resolver: true
|
||||
msw: false
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
packages:
|
||||
- "apps/*"
|
||||
- "packages/*"
|
||||
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
lightningcss: true
|
||||
unrs-resolver: true
|
||||
msw: false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
packages:
|
||||
- "."
|
||||
packages: []
|
||||
|
||||
ignoredBuiltDependencies:
|
||||
- esbuild
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
msw: false
|
||||
|
||||
@@ -2,6 +2,6 @@ packages:
|
||||
- "apps/*"
|
||||
- "packages/*"
|
||||
|
||||
ignoredBuiltDependencies:
|
||||
- esbuild
|
||||
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
msw: false
|
||||
|
||||
Reference in New Issue
Block a user