Skip to content

Commit ac3b481

Browse files
committed
chore: wip
1 parent d195819 commit ac3b481

33 files changed

+1542
-874
lines changed

packages/launchpad/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "@stacksjs/launchpad",
33
"type": "module",
44
"version": "0.6.4",
5+
"packageManager": "bun",
56
"description": "Like Homebrew, but faster.",
67
"author": "Chris Breuer <[email protected]>",
78
"license": "MIT",

packages/launchpad/src/dev/sniff.ts

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -614,31 +614,21 @@ export default async function sniff(dir: SimplePath | { string: string }): Promi
614614
// Collect all dependencies from different sources
615615
const allDependencies: Record<string, string> = {}
616616

617-
// Process engines
618-
if (json?.engines) {
619-
if (json.engines.node)
620-
allDependencies['nodejs.org'] = json.engines.node
621-
if (json.engines.npm)
622-
allDependencies['npmjs.com'] = json.engines.npm
623-
if (json.engines.yarn)
624-
allDependencies['yarnpkg.com'] = json.engines.yarn
625-
if (json.engines.pnpm) {
626-
allDependencies['pnpm.io'] = json.engines.pnpm
627-
detected_pnpm_usage = true
628-
}
629-
}
630-
631-
// Process packageManager (corepack)
617+
// First check packageManager to determine if we should skip Node.js-specific processing
618+
let isUsingBun = false
632619
if (json?.packageManager) {
633-
const match = json.packageManager.match(
620+
// Support both "pkg@version" and "pkg" formats
621+
const matchWithVersion = json.packageManager.match(
634622
/^(?<pkg>[^@]+)@(?<version>[^+]+)/,
635623
)
624+
const matchWithoutVersion = json.packageManager.match(
625+
/^(?<pkg>[^@]+)$/,
626+
)
636627

628+
const match = matchWithVersion || matchWithoutVersion
637629
if (match) {
638-
const { pkg, version } = match.groups as {
639-
pkg: string
640-
version: string
641-
}
630+
const { pkg } = match.groups as { pkg: string }
631+
const version = matchWithVersion?.groups?.version || '*' // Default to latest
642632

643633
switch (pkg) {
644634
case 'npm':
@@ -651,19 +641,37 @@ export default async function sniff(dir: SimplePath | { string: string }): Promi
651641
detected_pnpm_usage = true
652642
allDependencies['pnpm.io'] = version
653643
break
644+
case 'bun':
645+
allDependencies['bun.sh'] = version
646+
isUsingBun = true
647+
break
654648
}
655649
}
656650
}
657651

658-
// Process volta
652+
// Process engines (but skip Node.js ecosystem if packageManager is bun)
653+
if (json?.engines) {
654+
if (json.engines.node && !isUsingBun)
655+
allDependencies['nodejs.org'] = json.engines.node
656+
if (json.engines.npm && !isUsingBun)
657+
allDependencies['npmjs.com'] = json.engines.npm
658+
if (json.engines.yarn && !isUsingBun)
659+
allDependencies['yarnpkg.com'] = json.engines.yarn
660+
if (json.engines.pnpm && !isUsingBun) {
661+
allDependencies['pnpm.io'] = json.engines.pnpm
662+
detected_pnpm_usage = true
663+
}
664+
}
665+
666+
// Process volta (but skip Node.js ecosystem if packageManager is bun)
659667
if (json?.volta) {
660-
if (json.volta.node)
668+
if (json.volta.node && !isUsingBun)
661669
allDependencies['nodejs.org'] = json.volta.node
662-
if (json.volta.npm)
670+
if (json.volta.npm && !isUsingBun)
663671
allDependencies['npmjs.com'] = json.volta.npm
664-
if (json.volta.yarn)
672+
if (json.volta.yarn && !isUsingBun)
665673
allDependencies['yarnpkg.com'] = json.volta.yarn
666-
if (json.volta.pnpm) {
674+
if (json.volta.pnpm && !isUsingBun) {
667675
allDependencies['pnpm.io'] = json.volta.pnpm
668676
detected_pnpm_usage = true
669677
}

packages/launchpad/src/install-helpers.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,26 @@ exec "${binaryPath}" "$@"
390390
}
391391
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2))
392392

393+
// Create node_modules directory structure
394+
const nodeModulesDir = path.join(globalDir, 'node_modules')
395+
fs.mkdirSync(nodeModulesDir, { recursive: true })
396+
397+
// Create empty bun.lock file to make Bun recognize this as a valid package
398+
const lockFilePath = path.join(globalDir, 'bun.lock')
399+
if (!fs.existsSync(lockFilePath)) {
400+
try {
401+
fs.writeFileSync(lockFilePath, '')
402+
if (config.verbose) {
403+
console.warn(`Created empty bun.lock file in global directory`)
404+
}
405+
}
406+
catch (error) {
407+
if (config.verbose) {
408+
console.warn(`Failed to create bun.lock file: ${error instanceof Error ? error.message : String(error)}`)
409+
}
410+
}
411+
}
412+
393413
// Create bin directory in the official Bun directory
394414
const officialBinDir = path.join(officialBunDir, 'bin')
395415
fs.mkdirSync(officialBinDir, { recursive: true })
@@ -410,6 +430,28 @@ exec "${binaryPath}" "$@"
410430
fs.symlinkSync(binaryPath, officialBunBinPath)
411431
fs.symlinkSync(officialBunBinPath, officialBunxBinPath)
412432

433+
// Create a node symlink to bun to handle packages that expect node
434+
// Only create if no node binary/shim already exists (avoids conflict with actual Node.js)
435+
const nodeShimPath = path.join(targetShimDir, 'node')
436+
if (!fs.existsSync(nodeShimPath)) {
437+
try {
438+
fs.symlinkSync('bun', nodeShimPath)
439+
if (config.verbose) {
440+
console.warn(`Created node symlink: node -> bun (to handle packages that expect node)`)
441+
}
442+
installedBinaries.push('node')
443+
}
444+
catch (error) {
445+
if (config.verbose) {
446+
console.warn(`Failed to create node symlink: ${error instanceof Error ? error.message : String(error)}`)
447+
}
448+
// Don't fail the installation if node symlink creation fails
449+
}
450+
}
451+
else if (config.verbose) {
452+
console.warn(`Skipped creating node symlink: node binary/shim already exists`)
453+
}
454+
413455
// Create a custom shim for Bun with BUN_INSTALL environment variable
414456
const customShimContent = `#!/bin/sh
415457
# Launchpad shim for ${binary} (${domain} v${version})

packages/launchpad/test-envs/README.md

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,28 @@ This directory contains comprehensive test environments for Launchpad's global/l
7373
- **Expected**: All packages install locally (default behavior)
7474
- **Use Case**: Traditional project-isolated installation
7575

76+
### Bun Package Manager Tests
77+
78+
#### `bun-package-manager-basic/` 🆕
79+
- **Pattern**: `packageManager: "bun"` (no version)
80+
- **Expected**: Bun latest installed, NO Node.js, proper environment setup
81+
- **Use Case**: Basic Bun project without version constraints
82+
83+
#### `bun-package-manager-versioned/` 🆕
84+
- **Pattern**: `packageManager: "[email protected]"` (specific version)
85+
- **Expected**: Exact Bun version, TypeScript/ESLint support, NO Node.js
86+
- **Use Case**: Project requiring specific Bun version for stability
87+
88+
#### `bun-package-manager-with-deps/` 🆕
89+
- **Pattern**: `packageManager: "bun@latest"` + additional tools from dependencies.yaml
90+
- **Expected**: Bun + additional tools, complex npm packages working, NO Node.js
91+
- **Use Case**: Full-stack project using Bun with additional development tools
92+
93+
#### `bun-vs-node-engines/` 🆕
94+
- **Pattern**: `packageManager: "bun"` + conflicting `engines.node` + `volta.node`
95+
- **Expected**: Bun prioritized over Node.js engines/volta, node symlink for compatibility
96+
- **Use Case**: Testing package manager priority over other Node.js configurations
97+
7698
### Existing Test Environments
7799

78100
#### `complex-deps/`
@@ -133,6 +155,9 @@ launchpad env:list
133155
| `no-global-flag` | None | None | All local (default) |
134156
| `team-standard` | None | Mixed | Mixed per individual flags |
135157
| `fullstack-mixed` | None | Mixed | Runtimes global, tools local |
158+
| `bun-package-manager-basic` | N/A | `packageManager: "bun"` | Only Bun, NO Node.js |
159+
| `bun-package-manager-versioned` | N/A | `packageManager: "[email protected]"` | Bun v1.2.20, NO Node.js |
160+
| `bun-vs-node-engines` | N/A | Bun vs engines conflict | Bun wins, NO Node.js |
136161

137162
## Key Test Cases Covered
138163

@@ -145,5 +170,12 @@ launchpad env:list
145170
✅ Complex real-world scenarios (team, fullstack, dev machine)
146171
✅ String array format with top-level global
147172
✅ Object format with version and global flags
148-
149-
These test environments comprehensively validate Launchpad's global/local installation functionality across all supported configuration patterns.
173+
🆕 **Bun Package Manager Support**:
174+
✅ `packageManager: "bun"` without version (defaults to latest)
175+
`packageManager: "bun@version"` with specific version
176+
✅ Bun prioritized over Node.js engines/volta configurations
177+
✅ Node.js compatibility via symlink creation
178+
✅ Complex npm packages working with Bun runtime
179+
✅ ESLint/Prettier/Next.js compatibility testing
180+
181+
These test environments comprehensively validate Launchpad's global/local installation functionality and Bun package manager support across all supported configuration patterns.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Bun Package Manager Basic Test Environment
2+
3+
This test environment verifies that Launchpad correctly handles `packageManager: "bun"` without a version specification.
4+
5+
## Expected Behavior
6+
7+
When running `launchpad install` in this directory:
8+
9+
1. **Should install Bun** (latest version)
10+
2. **Should NOT install Node.js** (since Bun is the package manager)
11+
3. **Should create proper Bun environment** with:
12+
- `bunx` symlink to `bun`
13+
- `node` symlink to `bun` (for package compatibility)
14+
- `.bun/install/global/` directory structure
15+
- `BUN_INSTALL` environment variable in shim
16+
17+
## Key Features Tested
18+
19+
- ✅ Package manager detection without version
20+
- ✅ Node.js exclusion when Bun is package manager
21+
- ✅ Bun environment setup
22+
- ✅ Compatibility shims creation
23+
24+
## Test Commands
25+
26+
```bash
27+
# Install packages
28+
launchpad install --verbose
29+
30+
# Verify Bun is installed and working
31+
bunx --version
32+
bun --version
33+
34+
# Verify Node.js compatibility
35+
node --version # Should actually call Bun
36+
37+
# Test package management
38+
bunx eslint --version # Should work without "env: node" error
39+
```

0 commit comments

Comments
 (0)