Skip to content
Open
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
1 change: 1 addition & 0 deletions packages/data-context/schemas/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1692,6 +1692,7 @@ enum OverLimitActionTypeEnum {
}

enum PackageManagerEnum {
bun
npm
pnpm
yarn
Expand Down
4 changes: 4 additions & 0 deletions packages/data-context/src/data/ProjectLifecycleManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ export class ProjectLifecycleManager {
return 'pnpm'
}

if (fs.existsSync(path.join(projectRoot, 'bun.lock'))) {
return 'bun'
}

return 'npm'
}

Expand Down
1 change: 1 addition & 0 deletions packages/data-context/src/sources/WizardDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export class WizardDataSource {
'npm': 'npm install -D',
'pnpm': 'pnpm add -D',
'yarn': 'yarn add -D',
'bun': 'bun add -D',
} as const

const deps = (await this.ctx.wizard.packagesToInstall())
Expand Down
2 changes: 1 addition & 1 deletion packages/types/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const CODE_LANGUAGES = [

export type CodeLanguage = typeof CODE_LANGUAGES[number]

export const PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm'] as const
export const PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm', 'bun'] as const

export type PackageManager = typeof PACKAGE_MANAGERS[number]

Expand Down
21 changes: 21 additions & 0 deletions system-tests/lib/dep-installer/bun.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import path from 'path'
import tempDir from 'temp-dir'
import { homedir } from 'os'

export function getBunCommand (opts: {
updateLockFile: boolean
isCI: boolean
runScripts: boolean
}): string {
let cmd = 'bun install'

if (!opts.runScripts) cmd += ' --ignore-scripts'

if (!opts.updateLockFile) cmd += ' --frozen-lockfile'

// Bun uses different cache structure than npm/yarn
if (opts.isCI) cmd += ` --cache=${homedir()}/.bun/install/cache`
else cmd += ` --cache=${path.join(tempDir, 'cy-system-tests-bun-cache', String(Date.now()))}`

return cmd
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Missing Parameter Causes TypeScript Error

The getBunCommand function's signature doesn't include the yarnV311 parameter, which is passed by the caller. This causes a TypeScript compilation error. It also silently ignores the _cyYarnV311 flag, unlike other package managers that explicitly error when this unsupported option is present.

Additional Locations (1)

Fix in Cursor Fix in Web

20 changes: 17 additions & 3 deletions system-tests/lib/dep-installer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import execa from 'execa'
import { cyTmpDir, projectPath, projects, root } from '../fixtures'
import { getYarnCommand } from './yarn'
import { getNpmCommand } from './npm'
import { getBunCommand } from './bun'
import { getPnpmCommand } from './pnpm'

type Dependencies = Record<string, string>

Expand Down Expand Up @@ -83,8 +85,18 @@ async function copyNodeModulesFromCache (tmpNodeModulesDir: string, cacheDir: st
async function getLockFilename (dir: string) {
const hasYarnLock = !!await fs.stat(path.join(dir, 'yarn.lock')).catch(() => false)
const hasNpmLock = !!await fs.stat(path.join(dir, 'package-lock.json')).catch(() => false)
const hasPnpmLock = !!await fs.stat(path.join(dir, 'pnpm-lock.yaml')).catch(() => false)
const hasBunLock = !!await fs.stat(path.join(dir, 'bun.lock')).catch(() => false)

if (hasYarnLock && hasNpmLock) throw new Error(`The example project at '${dir}' has conflicting lockfiles. Only use one package manager's lockfile per project.`)
const lockfileCount = [hasYarnLock, hasNpmLock, hasPnpmLock, hasBunLock].filter(Boolean).length

if (lockfileCount > 1) {
throw new Error(`The example project at '${dir}' has conflicting lockfiles. Only use one package manager's lockfile per project.`)
}

if (hasBunLock) return 'bun.lock'

if (hasPnpmLock) return 'pnpm-lock.yaml'

if (hasNpmLock) return 'package-lock.json'

Expand Down Expand Up @@ -197,6 +209,8 @@ export async function scaffoldProjectNodeModules ({

const lockFilename = await getLockFilename(projectDir)
const hasYarnLock = lockFilename === 'yarn.lock'
const hasPnpmLock = lockFilename === 'pnpm-lock.yaml'
const hasBunLock = lockFilename === 'bun.lock'

// 1. Ensure there is a cache directory set up for this test project's `node_modules`.
await ensureCacheDir(cacheNodeModulesDir)
Expand Down Expand Up @@ -226,8 +240,8 @@ export async function scaffoldProjectNodeModules ({
log(`Writing ${lockFilename} with fixed relative paths to temp dir`)
await restoreLockFileRelativePaths({ projectDir, lockFilePath, relativePathToMonorepoRoot })

// 5. Run `yarn/npm install`.
const getCommandFn = hasYarnLock ? getYarnCommand : getNpmCommand
// 5. Run `yarn/npm/bun/pnpm install`.
const getCommandFn = hasBunLock ? getBunCommand : (hasYarnLock ? getYarnCommand : (hasPnpmLock ? getPnpmCommand : getNpmCommand))
const cmd = getCommandFn({
updateLockFile,
yarnV311: pkgJson._cyYarnV311,
Expand Down
23 changes: 23 additions & 0 deletions system-tests/lib/dep-installer/pnpm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import path from 'path'
import tempDir from 'temp-dir'
import { homedir } from 'os'

export function getPnpmCommand (opts: {
yarnV311: boolean
updateLockFile: boolean
isCI: boolean
runScripts: boolean
}): string {
let cmd = 'pnpm install'

if (opts.yarnV311) throw new Error('_cyYarnV311 is not supported with PNPM.')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Irrelevant Parameter Causes Command Failures

The yarnV311 parameter, meant for Yarn, is passed to getPnpmCommand and getBunCommand. getPnpmCommand errors if yarnV311 is true, while getBunCommand's interface omits it, causing type issues or silent ignoring. This inconsistent handling of an irrelevant parameter leads to failures or unexpected behavior for PNPM and Bun projects.

Additional Locations (1)

Fix in Cursor Fix in Web


if (!opts.runScripts) cmd += ' --ignore-scripts'

if (!opts.updateLockFile) cmd += ' --frozen-lockfile'

if (opts.isCI) cmd += ` --store-dir=${homedir()}/.pnpm-store`
else cmd += ` --store-dir=${path.join(tempDir, 'cy-system-tests-pnpm-store', String(Date.now()))}`

return cmd
}
38 changes: 38 additions & 0 deletions system-tests/projects/bun-component-testing/bun.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "bun-component-testing",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
},
"devDependencies": {
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"typescript": "^5.6.3",
},
},
},
"packages": {
"@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="],

"@types/react": ["@types/react@18.3.24", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A=="],

"@types/react-dom": ["@types/react-dom@18.3.7", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="],

"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],

"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],

"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],

"react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],

"react-dom": ["react-dom@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="],

"scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],

"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
}
}
13 changes: 13 additions & 0 deletions system-tests/projects/bun-component-testing/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineConfig } from 'cypress'

export default defineConfig({
component: {
devServer: {
framework: 'react',
bundler: 'webpack',
},
},
e2e: {
baseUrl: 'http://localhost:5000',
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react'
import { mount } from '@cypress/react'

describe('Bun Component Test', () => {
it('should render a simple React component', () => {
const TestComponent = () => (
<div>
<h1>Hello from Bun!</h1>
<p>This component is tested using Bun package manager</p>
</div>
)

mount(<TestComponent />)
cy.contains('Hello from Bun!').should('be.visible')
cy.contains('This component is tested using Bun package manager').should('be.visible')
})

it('should work with TypeScript', () => {
interface Props {
message: string
}

const TypeScriptComponent: React.FC<Props> = ({ message }) => (
<div>
<h2>TypeScript Component</h2>
<p>{message}</p>
</div>
)

mount(<TypeScriptComponent message="Bun + TypeScript + Cypress" />)
cy.contains('TypeScript Component').should('be.visible')
cy.contains('Bun + TypeScript + Cypress').should('be.visible')
})
})
21 changes: 21 additions & 0 deletions system-tests/projects/bun-component-testing/cypress/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "es6"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"**/*"
]
}
13 changes: 13 additions & 0 deletions system-tests/projects/bun-component-testing/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "bun-component-testing",
"version": "0.0.0-test",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"typescript": "^5.6.3"
}
}
21 changes: 21 additions & 0 deletions system-tests/projects/bun-component-testing/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "es6"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"cypress/**/*"
]
}
22 changes: 22 additions & 0 deletions system-tests/projects/bun-with-deps/bun.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "bun-with-deps",
"dependencies": {
"lodash": "^4.17.21",
},
"devDependencies": {
"@types/lodash": "^4.14.225",
"typescript": "^5.6.3",
},
},
},
"packages": {
"@types/lodash": ["@types/lodash@4.17.20", "", {}, "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA=="],

"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],

"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
}
}
15 changes: 15 additions & 0 deletions system-tests/projects/bun-with-deps/cypress/e2e/bun-test.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
describe('Bun E2E Test', () => {
it('should work with bun package manager', () => {
cy.visit('/cypress/fixtures/bun-test.html')
cy.contains('Bun Test Page').should('be.visible')
})

it('should be able to use lodash imported via bun', () => {
// This test verifies that dependencies installed via bun are available
const _ = require('lodash')

expect(_.isArray).to.be.a('function')
expect(_.isArray([])).to.be.true
expect(_.isArray({})).to.be.false
})
})
10 changes: 10 additions & 0 deletions system-tests/projects/bun-with-deps/cypress/fixtures/bun-test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Bun Test Page</title>
</head>
<body>
<h1>Bun Test Page</h1>
<p>This page is used for testing Bun package manager integration with Cypress.</p>
</body>
</html>
11 changes: 11 additions & 0 deletions system-tests/projects/bun-with-deps/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "bun-with-deps",
"version": "0.0.0-test",
"dependencies": {
"lodash": "^4.17.21"
},
"devDependencies": {
"@types/lodash": "^4.14.225",
"typescript": "^5.6.3"
}
}
37 changes: 37 additions & 0 deletions system-tests/projects/bun-workspace/bun.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "bun-workspace",
"devDependencies": {
"typescript": "^5.6.3",
},
},
"packages/app": {
"name": "@bun-workspace/app",
"version": "0.0.0-test",
"dependencies": {
"@bun-workspace/shared": "workspace:*",
},
"devDependencies": {
"typescript": "^5.6.3",
},
},
"packages/shared": {
"name": "@bun-workspace/shared",
"version": "0.0.0-test",
"dependencies": {
"lodash": "^4.17.21",
},
},
},
"packages": {
"@bun-workspace/app": ["@bun-workspace/app@workspace:packages/app"],

"@bun-workspace/shared": ["@bun-workspace/shared@workspace:packages/shared"],

"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],

"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
describe('Bun Workspace Test', () => {
it('should work with bun workspace dependencies', () => {
cy.visit('/cypress/fixtures/workspace-test.html')
cy.contains('Workspace Test Page').should('be.visible')
})

it('should be able to import workspace packages', () => {
// This test verifies that workspace dependencies are properly resolved
const { sharedFunction, useLodash } = require('@bun-workspace/shared')

expect(sharedFunction).to.be.a('function')
expect(sharedFunction()).to.equal('Hello from shared package!')
expect(useLodash).to.be.a('function')
expect(useLodash()).to.equal('Lodash is available')
})
})
Loading
Loading