Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: bun as packageManager option #1309

Merged
merged 16 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from 8 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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ yarn.lock
# test files
/test/temp_package*.json
/test/.ncurc.json

# we're using npm, so we want this to be ignored
yarn.lock
pnpm-lock.yaml
bun.lockb
ImBIOS marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
- sensible defaults
- lots of options for custom behavior
- CLI and module usage
- compatible with `npm`, `yarn`, and `pnpm`
- compatible with `npm`, `yarn`, `pnpm`, `bun`

![npm-check-updates-screenshot](https://github.com/raineorshine/npm-check-updates/blob/main/.github/screenshot.png?raw=true)

Expand Down Expand Up @@ -273,7 +273,7 @@ Options that take no arguments can be negated by prefixing them with `--no-`, e.
</tr>
<tr>
<td>-p, --packageManager <s></td>
<td>npm, yarn, pnpm, deno, staticRegistry (default: npm).</td>
<td>npm, yarn, pnpm, deno, bun, staticRegistry (default: npm).</td>
</tr>
<tr>
<td>--peer</td>
Expand Down Expand Up @@ -498,6 +498,7 @@ Specifies the package manager to use when looking up version numbers.
<tr><td>npm</td><td>System-installed npm. Default.</td></tr>
<tr><td>yarn</td><td>System-installed yarn. Automatically used if yarn.lock is present.</td></tr>
<tr><td>pnpm</td><td>System-installed pnpm. Automatically used if pnpm-lock.yaml is present.</td></tr>
<tr><td>bun</td><td>System-installed bun. Automatically used if bun.lockb is present.</td></tr>
<tr><td>staticRegistry</td><td>Checks versions from a static file. Must include the `--registry` option with the path to a JSON registry file.

Example:
Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"author": "Tomas Junnonen <[email protected]>",
"license": "Apache-2.0",
"contributors": [
"Raine Revere (https://github.com/raineorshine)"
"Raine Revere (https://github.com/raineorshine)",
"Imamuzzaki Abu Salam <[email protected]>"
],
"description": "Find newer versions of dependencies than what your package.json allows",
"keywords": [
Expand All @@ -19,7 +20,10 @@
"updater",
"version",
"management",
"ncu"
"ncu",
"bun",
"yarn",
"pnpm"
],
"engines": {
"node": ">=14.14"
Expand Down
5 changes: 3 additions & 2 deletions src/cli-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ const extendedHelpPackageManager: ExtendedHelp = ({ markdown }) => {
['npm', `System-installed npm. Default.`],
['yarn', `System-installed yarn. Automatically used if yarn.lock is present.`],
['pnpm', `System-installed pnpm. Automatically used if pnpm-lock.yaml is present.`],
['bun', `System-installed bun. Automatically used if bun.lockb is present.`],
[
'staticRegistry',
`Checks versions from a static file. Must include the \`--registry\` option with the path to a JSON registry file.
Expand Down Expand Up @@ -558,9 +559,9 @@ const cliOptions: CLIOption[] = [
long: 'packageManager',
short: 'p',
arg: 's',
description: 'npm, yarn, pnpm, deno, staticRegistry (default: npm).',
description: 'npm, yarn, pnpm, deno, bun, staticRegistry (default: npm).',
help: extendedHelpPackageManager,
type: `'npm' | 'yarn' | 'pnpm' | 'deno' | 'staticRegistry'`,
type: `'npm' | 'yarn' | 'pnpm' | 'deno' | 'bun' | 'staticRegistry'`,
},
{
long: 'peer',
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function checkIfVolta(options: Options): void {
}
}

/** Returns the package manager that should be used to install packages after running "ncu -u". Detects pnpm via pnpm-lock.yarn. This is the one place that pnpm needs to be detected, since otherwise it is backwards compatible with npm. */
/** Returns the package manager that should be used to install packages after running "ncu -u". Detects pnpm via pnpm-lock.yaml. This is the one place that pnpm needs to be detected, since otherwise it is backwards compatible with npm. */
const getPackageManagerForInstall = async (options: Options, pkgFile: string) => {
if (options.packageManager !== 'npm') return options.packageManager
const cwd = options.cwd ?? pkgFile ? `${pkgFile}/..` : process.cwd()
Expand Down
1 change: 1 addition & 0 deletions src/lib/determinePackageManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const packageManagerLockfileMap: Index<PackageManagerName> = {
yarn: 'yarn',
'pnpm-lock': 'pnpm',
deno: 'deno',
bun: 'bun',
}

/**
Expand Down
29 changes: 21 additions & 8 deletions src/lib/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from 'fs/promises'
import { rimraf } from 'rimraf'
import spawn from 'spawn-please'
import { printUpgrades } from '../lib/logging'
import spawnBun from '../package-managers/bun'
import spawnNpm from '../package-managers/npm'
import spawnPnpm from '../package-managers/pnpm'
import spawnYarn from '../package-managers/yarn'
Expand All @@ -17,7 +18,7 @@ import upgradePackageData from './upgradePackageData'

type Run = (options?: Options) => Promise<PackageFile | Index<VersionSpec> | void>

/** Run npm, yarn, or pnpm. */
/** Run npm, yarn, pnpm, or bun. */
const npm = (
args: string[],
options: Options,
Expand Down Expand Up @@ -45,11 +46,15 @@ const npm = (
...(options.prefix ? { prefix: options.prefix } : null),
}

return (options.packageManager === 'pnpm' ? spawnPnpm : options.packageManager === 'yarn' ? spawnYarn : spawnNpm)(
args,
npmOptions,
spawnOptionsMerged as any,
)
return (
options.packageManager === 'pnpm'
? spawnPnpm
: options.packageManager === 'yarn'
? spawnYarn
: options.packageManager === 'bun'
? spawnBun
: spawnNpm
)(args, npmOptions, spawnOptionsMerged as any)
}

/** Load and validate package file and tests. */
Expand Down Expand Up @@ -91,6 +96,8 @@ const doctor = async (run: Run, options: Options): Promise<void> => {
? 'yarn.lock'
: options.packageManager === 'pnpm'
? 'pnpm-lock.yaml'
: options.packageManager === 'bun'
? 'bun.lockb'
: 'package-lock.json'
const { pkg, pkgFile }: PackageInfo = await loadPackageFileForDoctor(options)

Expand Down Expand Up @@ -256,7 +263,9 @@ const doctor = async (run: Run, options: Options): Promise<void> => {
// install single dependency
await npm(
[
...(options.packageManager === 'yarn' || options.packageManager === 'pnpm'
...(options.packageManager === 'yarn' ||
options.packageManager === 'pnpm' ||
options.packageManager === 'bun'
? ['add']
: ['install', '--no-save']),
`${name}@${version}`,
Expand Down Expand Up @@ -299,7 +308,11 @@ const doctor = async (run: Run, options: Options): Promise<void> => {
await fs.writeFile(lockFileName, lockFile)

// restore package.json since yarn and pnpm do not have the --no-save option
if (options.packageManager === 'yarn' || options.packageManager === 'pnpm') {
if (
options.packageManager === 'yarn' ||
options.packageManager === 'pnpm' ||
options.packageManager === 'bun'
) {
await fs.writeFile('package.json', lastPkgFile)
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/lib/findLockfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path from 'path'
import { Options } from '../types/Options'

/**
* Goes up the filesystem tree until it finds a package-lock.json or yarn.lock.
* Goes up the filesystem tree until it finds a package-lock.json, yarn.lock, pnpm-lock.yaml, deno.json, deno.jsonc, or bun.lockb file.
*
* @param readdir This is only a parameter so that it can be used in tests.
* @returns The path of the directory that contains the lockfile and the
Expand Down Expand Up @@ -33,6 +33,8 @@ export default async function findLockfile(
return { directoryPath: currentPath, filename: 'deno.json' }
} else if (files.includes('deno.jsonc')) {
return { directoryPath: currentPath, filename: 'deno.jsonc' }
} else if (files.includes('bun.lockb')) {
return { directoryPath: currentPath, filename: 'bun.lockb' }
}

const pathParent = path.resolve(currentPath, '..')
Expand Down
2 changes: 2 additions & 0 deletions src/lib/runGlobal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ async function runGlobal(options: Options): Promise<Index<string> | void> {
? 'yarn global upgrade'
: options.packageManager === 'pnpm'
? 'pnpm -g add'
: options.packageManager === 'bun'
? 'bun add -g'
: 'npm -g install'

print(
Expand Down
4 changes: 4 additions & 0 deletions src/package-managers/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Package Managers

## How to add a new package manager

To add support for another package manager, drop in a module with the following interface.

```js
Expand Down
42 changes: 42 additions & 0 deletions src/package-managers/bun.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import spawn from 'spawn-please'
import { Index } from '../types/IndexType'
import { NpmOptions } from '../types/NpmOptions'
import { latest as npmLatest, list as npmList } from './npm'

/**
* (Bun) Fetches the list of all installed packages.
*/
export const list = npmList
Copy link
Owner

Choose a reason for hiding this comment

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

You should spawn bun for the list command, as npm may have a different global install directory than bun.

Hopefully bun has a --json option, otherwise you'll have to parse the results.


/**
* (Bun) Fetches the latest version of a package.
*/
export const latest = npmLatest

/**
* Spawn bun.
*
* @param args
* @param [bunOptions={}]
* @param [spawnOptions={}]
* @returns
*/
async function spawnBun(
args: string | string[],
bunOptions: NpmOptions = {},
spawnOptions: Index<any> = {},
): Promise<any> {
// Bun not yet supported on Windows.
// @see https://github.com/oven-sh/bun/issues/43
if (process.platform === 'win32') {
throw new Error('Bun not yet supported on Windows')
ImBIOS marked this conversation as resolved.
Show resolved Hide resolved
}

const cmd = 'bun'
args = Array.isArray(args) ? args : [args]

const fullArgs = args.concat('--global', bunOptions.prefix ? `--prefix=${bunOptions.prefix}` : [])
return spawn(cmd, fullArgs, spawnOptions)
}

export default spawnBun
2 changes: 2 additions & 0 deletions src/package-managers/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Index } from '../types/IndexType'
import { PackageManager } from '../types/PackageManager'
import * as bun from './bun'
import * as gitTags from './gitTags'
import * as npm from './npm'
import * as pnpm from './pnpm'
Expand All @@ -10,6 +11,7 @@ export default {
npm,
pnpm,
yarn,
bun,
gitTags,
staticRegistry,
} as Index<PackageManager>
2 changes: 1 addition & 1 deletion src/types/PackageManagerName.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/** A valid package manager. */
export type PackageManagerName = 'npm' | 'yarn' | 'pnpm' | 'deno' | 'staticRegistry'
export type PackageManagerName = 'npm' | 'yarn' | 'pnpm' | 'deno' | 'bun' | 'staticRegistry'
4 changes: 2 additions & 2 deletions src/types/RunOptions.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,8 @@
"type": "string"
},
"packageManager": {
"description": "npm, yarn, pnpm, deno, staticRegistry (default: npm). Run \"ncu --help --packageManager\" for details.",
"enum": ["deno", "npm", "pnpm", "staticRegistry", "yarn"],
"description": "npm, yarn, pnpm, deno, bun, staticRegistry (default: npm). Run \"ncu --help --packageManager\" for details.",
"enum": ["bun", "deno", "npm", "pnpm", "staticRegistry", "yarn"],
"type": "string"
},
"peer": {
Expand Down
4 changes: 2 additions & 2 deletions src/types/RunOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ export interface RunOptions {
/** Package file(s) location. (default: ./package.json) */
packageFile?: string

/** npm, yarn, pnpm, deno, staticRegistry (default: npm). Run "ncu --help --packageManager" for details. */
packageManager?: 'npm' | 'yarn' | 'pnpm' | 'deno' | 'staticRegistry'
/** npm, yarn, pnpm, deno, bun, staticRegistry (default: npm). Run "ncu --help --packageManager" for details. */
packageManager?: 'npm' | 'yarn' | 'pnpm' | 'deno' | 'bun' | 'staticRegistry'

/** Check peer dependencies of installed packages and filter updates to compatible versions. Run "ncu --help --peer" for details. */
peer?: boolean
Expand Down
48 changes: 48 additions & 0 deletions test/determinePackageManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,54 @@ chai.should()
const isWindows = process.platform === 'win32'

describe('determinePackageManager', () => {
it('returns bun if bun.lockb exists in cwd', async () => {
/** Mock for filesystem calls. */
function readdirMock(path: string): Promise<string[]> {
switch (path) {
case '/home/test-repo':
case 'C:\\home\\test-repo':
return Promise.resolve(['bun.lockb'])
}

throw new Error(`Mock cannot handle path: ${path}.`)
}

const packageManager = await determinePackageManager(
{
cwd: isWindows ? 'C:\\home\\test-repo' : '/home/test-repo',
},
readdirMock,
)
packageManager.should.equal('bun')
})

it('returns bun if bun.lockb exists in an ancestor directory', async () => {
/** Mock for filesystem calls. */
function readdirMock(path: string): Promise<string[]> {
switch (path) {
case '/home/test-repo/packages/package-a':
case 'C:\\home\\test-repo\\packages\\package-a':
return Promise.resolve(['index.ts'])
case '/home/test-repo/packages':
case 'C:\\home\\test-repo\\packages':
return Promise.resolve([])
case '/home/test-repo':
case 'C:\\home\\test-repo':
return Promise.resolve(['bun.lockb'])
}

throw new Error(`Mock cannot handle path: ${path}.`)
}

const packageManager = await determinePackageManager(
{
cwd: isWindows ? 'C:\\home\\test-repo\\packages\\package-a' : '/home/test-repo/packages/package-a',
},
readdirMock,
)
packageManager.should.equal('bun')
})

it('returns yarn if yarn.lock exists in cwd', async () => {
/** Mock for filesystem calls. */
function readdirMock(path: string): Promise<string[]> {
Expand Down
Loading
Loading