Skip to content

Instantly share code, notes, and snippets.

@jrson83
Created August 14, 2023 01:01
Show Gist options
  • Save jrson83/b19c9f5dd05051b7b72ab993ddeb9b76 to your computer and use it in GitHub Desktop.
Save jrson83/b19c9f5dd05051b7b72ab993ddeb9b76 to your computer and use it in GitHub Desktop.
detect package manager
import fs from 'node:fs/promises'
import path from 'node:path'
import { exec } from 'node:child_process'
import { promisify } from 'node:util'
export const execAsync = promisify(exec)
export interface PackageJson {
name: string
version?: string
license?: string
scripts?: ScriptsMap
dependencies?: DependencyMap
packageManager?: string
}
export interface ScriptsMap {
[scriptName: string]: string
}
export interface DependencyMap {
[depName: string]: string
}
/**
* Available package managers
*/
export const packageManager = ['npm', 'pnpm', 'yarn'] as const
export const packageManagerSpecs = [
{
name: 'npm',
lockfile: 'package-lock.json',
},
{
name: 'pnpm',
lockfile: 'pnpm-lock.yaml',
},
{
name: 'yarn',
lockfile: 'yarn.lock',
},
] as const
export type PackageManagerName = typeof packageManager[number]
export type PackageManagerVersion = string | undefined
export type PackageManagerResult = {
name: PackageManagerName
version: PackageManagerVersion
filePath: string
}
/**
* Lockfiles which belong to the available package managers
*/
export const lockFiles = ['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock']
/**
* Normalizes a given path.
* @param filePath - path to search
* @returns Returns if a path exists
*/
const normalizePath = (dir: string, file?: string): string => {
return path.normalize(path.resolve(dir, file ? file : ''))
}
/**
* Checks if a given path exists.
* @param filePath - path to search
* @returns Returns if a path exists
*/
export const pathExists = (filePath: string): Promise<boolean> =>
fs
.access(filePath)
.then(() => true)
.catch(() => false)
export async function findFile(
filename: string,
startdir: string = process.cwd()
): Promise<string | undefined> {
if (!filename)
throw new Error('Error: No filename was specified for findFile()')
const filePath = normalizePath(startdir, filename)
return (await pathExists(filePath)) ? filePath : undefined
}
export async function detectFromPackageJson(
startdir: string = process.cwd()
): Promise<PackageManagerResult | undefined> {
const packageJsonPath = await findFile('package.json', startdir)
if (!packageJsonPath) {
return
}
const { packageManager }: PackageJson = JSON.parse(
await fs.readFile(packageJsonPath, {
encoding: 'utf8',
})
)
if (!packageManager) {
return
}
const [name, version] = packageManager.split('@') as [
PackageManagerName,
PackageManagerVersion
]
return {
name,
version,
filePath: packageJsonPath,
}
}
export async function detectFromLockfile(startdir: string = process.cwd()) {
return (
await Promise.all(
packageManagerSpecs.map(async ({ name, lockfile }) => {
return {
name,
lockfile,
exists: await pathExists(normalizePath(startdir, lockfile)),
version: '',
}
})
)
).filter(({ exists }) => {
return exists === true
})
}
export async function detectPackageManager(
startdir: string = process.cwd(),
prefered: PackageManagerName = 'npm'
) {
const detectedPackageManager = await detectFromPackageJson(startdir)
if (detectedPackageManager) {
return detectedPackageManager
}
const detectedPackageManagerFromLockfile = await detectFromLockfile(startdir)
if (
Array.isArray(detectedPackageManagerFromLockfile) &&
!detectedPackageManagerFromLockfile.length
) {
throw new Error('Error: Could not detect package manager')
}
if (
Array.isArray(detectedPackageManagerFromLockfile) &&
detectedPackageManagerFromLockfile.length > 1
) {
/* throw new Error('Error: Multiple package manager detected') */
return detectedPackageManagerFromLockfile.filter(
({ name }) => name === prefered
)
}
const childProcess = await execAsync(
`${detectedPackageManagerFromLockfile[0].name} --version`
)
const version = childProcess.stdout.trim().replace(/^v/i, '')
detectedPackageManagerFromLockfile[0].version = version
return detectedPackageManagerFromLockfile[0]
}
console.log(await detectPackageManager())
/* function getObjectProperty<T extends Object, K extends keyof T>(
o: T,
propertyName: K
): T[K] | undefined {
return Object.hasOwn(o, propertyName) ? o[propertyName] : undefined
}
const packageJsonPath = await findFile(startdir, 'package.json')
if (!packageJsonPath) {
return
}
const packageJsonData: PackageJson = JSON.parse(
await fs.readFile(packageJsonPath, {
encoding: 'utf8',
})
)
console.log(getObjectProperty(packageJsonData, 'packageManager'))
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment