Skip to content

Commit d6d7bea

Browse files
committed
chore: wip
1 parent 8f20e4b commit d6d7bea

File tree

8 files changed

+433
-96
lines changed

8 files changed

+433
-96
lines changed

packages/launchpad/bin/cli.ts

Lines changed: 67 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,52 +1803,85 @@ cli
18031803
const pkgsDir = path.join(installPrefix, 'pkgs')
18041804
const binDir = path.join(installPrefix, 'bin')
18051805

1806-
if (!fs.existsSync(pkgsDir))
1807-
return binaries
1806+
// Method 1: Use metadata if available
1807+
if (fs.existsSync(pkgsDir)) {
1808+
try {
1809+
const domains = fs.readdirSync(pkgsDir, { withFileTypes: true })
1810+
.filter(dirent => dirent.isDirectory())
18081811

1809-
try {
1810-
const domains = fs.readdirSync(pkgsDir, { withFileTypes: true })
1811-
.filter(dirent => dirent.isDirectory())
1812+
for (const domain of domains) {
1813+
const domainPath = path.join(pkgsDir, domain.name)
1814+
const versions = fs.readdirSync(domainPath, { withFileTypes: true })
1815+
.filter(dirent => dirent.isDirectory())
18121816

1813-
for (const domain of domains) {
1814-
const domainPath = path.join(pkgsDir, domain.name)
1815-
const versions = fs.readdirSync(domainPath, { withFileTypes: true })
1816-
.filter(dirent => dirent.isDirectory())
1817+
for (const version of versions) {
1818+
const versionPath = path.join(domainPath, version.name)
1819+
const metadataPath = path.join(versionPath, 'metadata.json')
18171820

1818-
for (const version of versions) {
1819-
const versionPath = path.join(domainPath, version.name)
1820-
const metadataPath = path.join(versionPath, 'metadata.json')
1821-
1822-
if (fs.existsSync(metadataPath)) {
1823-
try {
1824-
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'))
1825-
if (metadata.binaries && Array.isArray(metadata.binaries)) {
1826-
for (const binary of metadata.binaries) {
1827-
const binaryPath = path.join(binDir, binary)
1828-
if (fs.existsSync(binaryPath)) {
1829-
// Skip global dependencies if --keep-global is enabled
1830-
if (options?.keepGlobal && globalDeps.has(domain.name)) {
1831-
continue
1832-
}
1821+
if (fs.existsSync(metadataPath)) {
1822+
try {
1823+
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'))
1824+
if (metadata.binaries && Array.isArray(metadata.binaries)) {
1825+
for (const binary of metadata.binaries) {
1826+
const binaryPath = path.join(binDir, binary)
1827+
if (fs.existsSync(binaryPath)) {
1828+
// Skip global dependencies if --keep-global is enabled
1829+
if (options?.keepGlobal && globalDeps.has(domain.name)) {
1830+
continue
1831+
}
18331832

1834-
binaries.push({
1835-
binary,
1836-
package: `${domain.name}@${version.name.slice(1)}`, // Remove 'v' prefix
1837-
fullPath: binaryPath,
1838-
})
1833+
binaries.push({
1834+
binary,
1835+
package: `${domain.name}@${version.name.slice(1)}`, // Remove 'v' prefix
1836+
fullPath: binaryPath,
1837+
})
1838+
}
18391839
}
18401840
}
18411841
}
1842-
}
1843-
catch {
1844-
// Ignore invalid metadata files
1842+
catch {
1843+
// Ignore invalid metadata files
1844+
}
18451845
}
18461846
}
18471847
}
18481848
}
1849+
catch {
1850+
// Ignore errors reading package directory
1851+
}
18491852
}
1850-
catch {
1851-
// Ignore errors reading package directory
1853+
1854+
// Method 2: Additional scan - always check bin directory for any remaining Launchpad shims
1855+
if (fs.existsSync(binDir)) {
1856+
try {
1857+
const binFiles = fs.readdirSync(binDir, { withFileTypes: true })
1858+
.filter(dirent => dirent.isFile())
1859+
1860+
for (const file of binFiles) {
1861+
const filePath = path.join(binDir, file.name)
1862+
try {
1863+
// Read first few lines to check if it's a Launchpad shim
1864+
const content = fs.readFileSync(filePath, 'utf8')
1865+
if (content.includes('Launchpad shim')) {
1866+
// Check if we already have this binary from metadata
1867+
const alreadyTracked = binaries.some(b => b.binary === file.name)
1868+
if (!alreadyTracked) {
1869+
binaries.push({
1870+
binary: file.name,
1871+
package: 'unknown', // We don't have metadata, so package is unknown
1872+
fullPath: filePath,
1873+
})
1874+
}
1875+
}
1876+
}
1877+
catch {
1878+
// Ignore files we can't read
1879+
}
1880+
}
1881+
}
1882+
catch {
1883+
// Ignore errors reading bin directory
1884+
}
18521885
}
18531886

18541887
return binaries

packages/launchpad/src/dev/dump.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,15 +214,20 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
214214
try {
215215
// Find dependency file
216216
const dependencyFile = findDependencyFile(dir)
217-
if (!dependencyFile) {
217+
218+
// If no dependency file found, check for package.json to enable auto-detection
219+
const packageJsonPath = path.join(dir, 'package.json')
220+
const hasPackageJson = fs.existsSync(packageJsonPath)
221+
222+
if (!dependencyFile && !hasPackageJson) {
218223
if (!quiet && !shellOutput) {
219224
console.log('No dependency file found')
220225
}
221226
return
222227
}
223228

224229
// For shell output mode, prioritize speed with aggressive optimizations
225-
const projectDir = path.dirname(dependencyFile)
230+
const projectDir = dependencyFile ? path.dirname(dependencyFile) : dir
226231

227232
// Ultra-fast path for shell output: check if environments exist and use cached data
228233
if (shellOutput) {

packages/launchpad/src/dev/shellcode.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ __launchpad_find_deps_file() {
413413
__launchpad_cache_timestamp=0
414414
415415
while [[ "$dir" != "/" ]]; do
416-
# Check multiple file patterns efficiently
416+
# Check dependency files first
417417
# Supported files: dependencies.yaml, dependencies.yml, deps.yaml, deps.yml,
418418
# pkgx.yaml, pkgx.yml, launchpad.yaml, launchpad.yml
419419
for pattern in "dependencies" "deps" "pkgx" "launchpad"; do
@@ -427,6 +427,15 @@ __launchpad_find_deps_file() {
427427
fi
428428
done
429429
done
430+
431+
# Check for package.json if no dependency files found
432+
if [[ -f "$dir/package.json" ]]; then
433+
__launchpad_cache_dir="$dir"
434+
__launchpad_cache_timestamp=$current_time
435+
echo "$dir"
436+
return 0
437+
fi
438+
430439
dir="$(/usr/bin/dirname "$dir")"
431440
done
432441

packages/launchpad/src/dev/sniff.ts

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,9 @@ export default async function sniff(dir: SimplePath | { string: string }): Promi
298298

299299
const constraint = new SemverRange('*')
300300
let has_package_json = false
301+
let has_bun_lock = false
302+
let has_deps_file = false
303+
let detected_pnpm_usage = false
301304

302305
const pkgs: PackageRequirement[] = []
303306
const env: Record<string, string> = {}
@@ -371,9 +374,11 @@ export default async function sniff(dir: SimplePath | { string: string }): Promi
371374
break
372375
case 'bun.lock':
373376
case 'bun.lockb':
377+
has_bun_lock = true
374378
pkgs.push({ project: 'bun.sh', constraint: new SemverRange('>=1'), source: 'inferred' })
375379
break
376380
case 'pnpm-lock.yaml':
381+
detected_pnpm_usage = true
377382
pkgs.push({ project: 'pnpm.io', constraint, source: 'inferred' })
378383
break
379384
case 'pixi.toml':
@@ -382,20 +387,23 @@ export default async function sniff(dir: SimplePath | { string: string }): Promi
382387
break
383388
case 'pkgx.yml':
384389
case 'pkgx.yaml':
385-
case '.pkgx.yml':
386-
case '.pkgx.yaml':
387-
case 'launchpad.yml':
388-
case 'launchpad.yaml':
389-
case '.launchpad.yml':
390-
case '.launchpad.yaml':
391-
case 'dependencies.yml':
392390
case 'dependencies.yaml':
391+
case 'dependencies.yml':
393392
case '.dependencies.yml':
394393
case '.dependencies.yaml':
395394
case 'deps.yml':
396395
case 'deps.yaml':
397396
case '.deps.yml':
398397
case '.deps.yaml':
398+
case 'pkgx.yaml':
399+
case 'pkgx.yml':
400+
case '.pkgx.yml':
401+
case '.pkgx.yaml':
402+
case 'launchpad.yaml':
403+
case 'launchpad.yml':
404+
case '.launchpad.yml':
405+
case '.launchpad.yaml':
406+
has_deps_file = true
399407
await parse_well_formatted_node(await path.readYAML())
400408
break
401409
case 'cdk.json':
@@ -431,6 +439,11 @@ export default async function sniff(dir: SimplePath | { string: string }): Promi
431439
}
432440
}
433441

442+
// Auto-install pnpm if detected usage but not explicitly installed
443+
if (detected_pnpm_usage && !pkgs.some(pkg => pkg.project === 'pnpm.io' || pkg.project === 'pnpm')) {
444+
pkgs.push({ project: 'pnpm.io', constraint, source: 'inferred' })
445+
}
446+
434447
// Only auto-add nodejs.org if we have a package.json but no JS runtime is explicitly specified
435448
// This should not interfere with explicit dependencies defined in deps.yaml files
436449
const hasAnyJSRuntime = pkgs.some((pkg) => {
@@ -448,9 +461,15 @@ export default async function sniff(dir: SimplePath | { string: string }): Promi
448461
|| pkg.project.includes('deno')
449462
})
450463

451-
// Only auto-infer nodejs if we have package.json but no explicit JS runtime was specified
452-
if (has_package_json && !hasAnyJSRuntime) {
453-
pkgs.push({ project: 'nodejs.org', constraint, source: 'inferred' })
464+
// Auto-infer nodejs.org only if:
465+
// 1. We have package.json
466+
// 2. No explicit JS runtime was specified
467+
// 3. No bun.lock is present (indicating Bun usage)
468+
// 4. No deps files are present (user controls dependencies explicitly)
469+
if (has_package_json && !hasAnyJSRuntime && !has_bun_lock && !has_deps_file) {
470+
// Use Node.js LTS (v22) for better compatibility with older OpenSSL versions
471+
const nodeConstraint = new SemverRange('^22')
472+
pkgs.push({ project: 'nodejs.org', constraint: nodeConstraint, source: 'inferred' })
454473
}
455474

456475
// Optimized deduplication with source-aware priority
@@ -597,8 +616,10 @@ export default async function sniff(dir: SimplePath | { string: string }): Promi
597616
allDependencies['npmjs.com'] = json.engines.npm
598617
if (json.engines.yarn)
599618
allDependencies['yarnpkg.com'] = json.engines.yarn
600-
if (json.engines.pnpm)
619+
if (json.engines.pnpm) {
601620
allDependencies['pnpm.io'] = json.engines.pnpm
621+
detected_pnpm_usage = true
622+
}
602623
}
603624

604625
// Process packageManager (corepack)
@@ -621,6 +642,7 @@ export default async function sniff(dir: SimplePath | { string: string }): Promi
621642
allDependencies['yarnpkg.com'] = version
622643
break
623644
case 'pnpm':
645+
detected_pnpm_usage = true
624646
allDependencies['pnpm.io'] = version
625647
break
626648
}
@@ -635,8 +657,10 @@ export default async function sniff(dir: SimplePath | { string: string }): Promi
635657
allDependencies['npmjs.com'] = json.volta.npm
636658
if (json.volta.yarn)
637659
allDependencies['yarnpkg.com'] = json.volta.yarn
638-
if (json.volta.pnpm)
660+
if (json.volta.pnpm) {
639661
allDependencies['pnpm.io'] = json.volta.pnpm
662+
detected_pnpm_usage = true
663+
}
640664
}
641665

642666
// Process pkgx section

packages/launchpad/src/install.ts

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -25,51 +25,6 @@ export function resetInstalledTracker(): void {
2525
globalCompletedPackages.clear()
2626
}
2727

28-
// Collect all dependencies (direct and transitive) for the given packages
29-
async function _collectAllDependencies(packages: string[]): Promise<string[]> {
30-
const allDeps = new Set<string>()
31-
32-
// Helper function to recursively collect dependencies
33-
function collectDepsRecursive(packageName: string, visited = new Set<string>()): void {
34-
if (visited.has(packageName))
35-
return // Prevent circular dependencies
36-
visited.add(packageName)
37-
38-
const packageInfo = getPackageInfo(packageName)
39-
if (!packageInfo || !packageInfo.dependencies)
40-
return
41-
42-
for (const dep of packageInfo.dependencies) {
43-
const { name: depName } = parsePackageSpec(dep)
44-
const _depDomain = resolvePackageName(depName)
45-
46-
// Skip platform-specific dependencies that don't match current platform
47-
if (depName.includes(':')) {
48-
const [platformPrefix] = depName.split(':', 2)
49-
const currentPlatform = getPlatform()
50-
if ((platformPrefix === 'linux' && currentPlatform !== 'linux')
51-
|| (platformPrefix === 'darwin' && currentPlatform !== 'darwin')) {
52-
continue
53-
}
54-
}
55-
56-
// Add this dependency
57-
allDeps.add(dep)
58-
59-
// Recursively collect its dependencies
60-
collectDepsRecursive(depName, visited)
61-
}
62-
}
63-
64-
// Collect dependencies for all packages
65-
for (const pkg of packages) {
66-
const { name: packageName } = parsePackageSpec(pkg)
67-
collectDepsRecursive(packageName)
68-
}
69-
70-
return Array.from(allDeps)
71-
}
72-
7328
// Use ts-pkgx API to resolve all dependencies with proper version conflict resolution
7429
export async function resolveAllDependencies(packages: string[]): Promise<string[]> {
7530
try {

packages/launchpad/test/dev.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,9 +429,11 @@ describe('Dev Commands', () => {
429429
const fixturePath = path.join(fixturesDir, 'package.json', 'std')
430430
if (fs.existsSync(fixturePath)) {
431431
const result = await testFixture(fixturePath)
432-
// package.json is not currently recognized as a dependency file by Launchpad
432+
// package.json is now recognized as a dependency source by Launchpad
433433
expect(result.exitCode).toBe(0)
434-
expect(result.stdout).toContain('No dependency file found')
434+
// Should install zlib.net from pkgx section and auto-infer nodejs.org
435+
expect(result.stdout).toContain('zlib.net')
436+
expect(result.stdout).toContain('nodejs.org')
435437
}
436438
}, 60000)
437439

0 commit comments

Comments
 (0)