Showing preview only (814K chars total). Download the full file or copy to clipboard to get everything.
Repository: raineorshine/npm-check-updates
Branch: main
Commit: cf1001bed2d5
Files: 238
Total size: 755.2 KB
Directory structure:
gitextract_0r1ho66r/
├── .editorconfig
├── .eslintrc.js
├── .gitattributes
├── .github/
│ ├── CONTRIBUTING.md
│ ├── ISSUE_TEMPLATE/
│ │ └── bug_report.md
│ └── workflows/
│ ├── codeql.yml
│ ├── lint.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .hooks/
│ ├── post-commit
│ └── pre-push
├── .markdownlint.js
├── .ncurc.js
├── .npmrc
├── .prettierignore
├── .prettierrc.json
├── .vscode/
│ └── settings.json
├── CHANGELOG.md
├── Dockerfile
├── LICENSE
├── README.md
├── deploy.md
├── package.json
├── src/
│ ├── bin/
│ │ └── cli.ts
│ ├── cli-options.ts
│ ├── index.ts
│ ├── lib/
│ │ ├── cache.ts
│ │ ├── chalk.ts
│ │ ├── defineConfig.ts
│ │ ├── determinePackageManager.ts
│ │ ├── doctor.ts
│ │ ├── exists.ts
│ │ ├── figgy-pudding/
│ │ │ ├── LICENSE.md
│ │ │ ├── README.md
│ │ │ └── index.js
│ │ ├── filterAndReject.ts
│ │ ├── filterObject.ts
│ │ ├── findLockfile.ts
│ │ ├── findPackage.ts
│ │ ├── getAllPackages.ts
│ │ ├── getCurrentDependencies.ts
│ │ ├── getEnginesNodeFromRegistry.ts
│ │ ├── getIgnoredUpgradesDueToEnginesNode.ts
│ │ ├── getIgnoredUpgradesDueToPeerDeps.ts
│ │ ├── getInstalledPackages.ts
│ │ ├── getNcuRc.ts
│ │ ├── getPackageJson.ts
│ │ ├── getPackageManager.ts
│ │ ├── getPackageVersion.ts
│ │ ├── getPeerDependenciesFromRegistry.ts
│ │ ├── getPreferredWildcard.ts
│ │ ├── getRepoUrl.ts
│ │ ├── initOptions.ts
│ │ ├── isUpgradeable.ts
│ │ ├── keyValueBy.ts
│ │ ├── libnpmconfig/
│ │ │ ├── LICENSE
│ │ │ ├── README.md
│ │ │ └── index.js
│ │ ├── loadPackageInfoFromFile.ts
│ │ ├── logging.ts
│ │ ├── mergeOptions.ts
│ │ ├── parseCooldown.ts
│ │ ├── pick.ts
│ │ ├── programError.ts
│ │ ├── queryVersions.ts
│ │ ├── resolveDepSections.ts
│ │ ├── runGlobal.ts
│ │ ├── runLocal.ts
│ │ ├── sortBy.ts
│ │ ├── spawnCommand.ts
│ │ ├── table.ts
│ │ ├── upgradeDependencies.ts
│ │ ├── upgradeJsonCatalogDependencies.ts
│ │ ├── upgradePackageData.ts
│ │ ├── upgradePackageDefinitions.ts
│ │ ├── upgradeYamlCatalogDependencies.ts
│ │ ├── utils/
│ │ │ └── parseJson.ts
│ │ ├── version-util.ts
│ │ └── wrap.ts
│ ├── package-managers/
│ │ ├── README.md
│ │ ├── bun.ts
│ │ ├── filters.ts
│ │ ├── gitTags.ts
│ │ ├── index.ts
│ │ ├── npm.ts
│ │ ├── pnpm.ts
│ │ ├── staticRegistry.ts
│ │ └── yarn.ts
│ ├── scripts/
│ │ ├── build-options.ts
│ │ └── install-hooks
│ └── types/
│ ├── CLIOption.ts
│ ├── Cacher.ts
│ ├── CatalogConfig.ts
│ ├── CooldownFunction.ts
│ ├── DependencyGroup.ts
│ ├── ExtendedHelp.ts
│ ├── FilterFunction.ts
│ ├── FilterPattern.ts
│ ├── FilterResultsFunction.ts
│ ├── GetVersion.ts
│ ├── GroupFunction.ts
│ ├── IgnoredUpgradeDueToEnginesNode.ts
│ ├── IgnoredUpgradeDueToPeerDeps.ts
│ ├── IndexType.ts
│ ├── Maybe.ts
│ ├── MockedVersions.ts
│ ├── NpmConfig.ts
│ ├── NpmOptions.ts
│ ├── Options.ts
│ ├── PackageFile.ts
│ ├── PackageFileRepository.ts
│ ├── PackageInfo.ts
│ ├── PackageManager.ts
│ ├── PackageManagerName.ts
│ ├── Packument.ts
│ ├── RcOptions.ts
│ ├── RunOptions.json
│ ├── RunOptions.ts
│ ├── SpawnOptions.ts
│ ├── SpawnPleaseOptions.ts
│ ├── StaticRegistry.ts
│ ├── Target.ts
│ ├── TargetFunction.ts
│ ├── UpgradeGroup.ts
│ ├── Version.ts
│ ├── VersionLevel.ts
│ ├── VersionResult.ts
│ ├── VersionSpec.ts
│ ├── libnpmconfig.d.ts
│ └── prompts-ncu.d.ts
├── tea.yaml
├── test/
│ ├── bin.test.ts
│ ├── bun/
│ │ ├── bun.lockb
│ │ ├── index.test.ts
│ │ └── package.json
│ ├── bun-install.sh
│ ├── cache.test.ts
│ ├── cli-options.test.ts
│ ├── cooldown.test.ts
│ ├── deep.test.ts
│ ├── dep.test.ts
│ ├── determinePackageManager.test.ts
│ ├── doctor.test.ts
│ ├── e2e/
│ │ ├── cjs/
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── esm/
│ │ ├── index.js
│ │ └── package.json
│ ├── e2e.sh
│ ├── enginesNode.test.ts
│ ├── filter.test.ts
│ ├── filterResults.test.ts
│ ├── filterVersion.test.ts
│ ├── format.test.ts
│ ├── getAllPackages.test.ts
│ ├── getCurrentDependencies.test.ts
│ ├── getEnginesNodeFromRegistry.test.ts
│ ├── getIgnoredUpgradesDueToEnginesNode.test.ts
│ ├── getIgnoredUpgradesDueToPeerDeps.test.ts
│ ├── getInstalledPackages.test.ts
│ ├── getPeerDependenciesFromRegistry.test.ts
│ ├── getPreferredWildcard.test.ts
│ ├── getRepoUrl.test.ts
│ ├── github-urls.test.ts
│ ├── global.test.ts
│ ├── group.test.ts
│ ├── helpers/
│ │ ├── chaiSetup.ts
│ │ ├── doctorHelpers.ts
│ │ ├── removeDir.ts
│ │ └── stubVersions.ts
│ ├── index.test.ts
│ ├── install.test.ts
│ ├── interactive.test.ts
│ ├── isUpgradeable.test.ts
│ ├── package-managers/
│ │ ├── deno/
│ │ │ └── index.test.ts
│ │ ├── npm/
│ │ │ ├── index.test.ts
│ │ │ └── package.json
│ │ └── yarn/
│ │ ├── default/
│ │ │ └── package.json
│ │ ├── index.test.ts
│ │ ├── nolockfile/
│ │ │ └── package.json
│ │ └── v4/
│ │ └── package.json
│ ├── parseJson.test.ts
│ ├── peer.test.ts
│ ├── queryVersions.test.ts
│ ├── rc-config.test.ts
│ ├── registryType.test.ts
│ ├── rejectVersion.ts
│ ├── target.test.ts
│ ├── test-data/
│ │ ├── basic/
│ │ │ └── package.json
│ │ ├── deep-ncurc/
│ │ │ ├── .ncurc.js
│ │ │ ├── package.json
│ │ │ └── pkg/
│ │ │ ├── sub1/
│ │ │ │ ├── .ncurc.js
│ │ │ │ └── package.json
│ │ │ ├── sub2/
│ │ │ │ ├── .ncurc.js
│ │ │ │ ├── package.json
│ │ │ │ ├── sub21/
│ │ │ │ │ ├── .ncurc.js
│ │ │ │ │ └── package.json
│ │ │ │ └── sub22/
│ │ │ │ └── package.json
│ │ │ └── sub3/
│ │ │ ├── package.json
│ │ │ ├── sub31/
│ │ │ │ ├── .ncurc.js
│ │ │ │ └── package.json
│ │ │ └── sub32/
│ │ │ └── package.json
│ │ ├── doctor/
│ │ │ ├── custominstall/
│ │ │ │ └── package.json
│ │ │ ├── customtest/
│ │ │ │ └── package.json
│ │ │ ├── customtest2/
│ │ │ │ ├── echo.js
│ │ │ │ └── package.json
│ │ │ ├── fail/
│ │ │ │ ├── README.md
│ │ │ │ ├── package.json
│ │ │ │ └── test.js
│ │ │ ├── nolockfile/
│ │ │ │ └── package.json
│ │ │ ├── nopackagefile/
│ │ │ │ └── nil
│ │ │ ├── notestscript/
│ │ │ │ └── package.json
│ │ │ ├── options/
│ │ │ │ ├── package.json
│ │ │ │ └── test.js
│ │ │ └── pass/
│ │ │ ├── README.md
│ │ │ └── package.json
│ │ ├── ncu/
│ │ │ ├── package-large.json
│ │ │ ├── package.json
│ │ │ └── package2.json
│ │ ├── peer-post-upgrade/
│ │ │ └── package.json
│ │ ├── peer-post-upgrade-no-upgrades/
│ │ │ └── package.json
│ │ ├── registry.json
│ │ ├── workspace-basic/
│ │ │ ├── package.json
│ │ │ └── pkg/
│ │ │ └── sub/
│ │ │ └── package.json
│ │ ├── workspace-no-sub-packages/
│ │ │ └── package.json
│ │ ├── workspace-sub-package-names/
│ │ │ ├── package.json
│ │ │ └── pkg/
│ │ │ ├── dirname-does-not-match-name/
│ │ │ │ └── package.json
│ │ │ ├── dirname-matches-name/
│ │ │ │ └── package.json
│ │ │ ├── dirname-will-become-name/
│ │ │ │ └── package.json
│ │ │ └── unlisted/
│ │ │ └── package.json
│ │ └── workspace-workspace-param-is-array/
│ │ └── package.json
│ ├── timeout.test.ts
│ ├── upgradeDependencies.test.ts
│ ├── upgradeYamlCatalogDependencies.test.ts
│ ├── version-util.test.ts
│ └── workspaces.test.ts
├── tsconfig.json
└── vite.config.mts
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{md,jade}]
trim_trailing_whitespace = false
================================================
FILE: .eslintrc.js
================================================
module.exports = {
env: {
es6: true,
mocha: true,
node: true,
},
extends: ['standard', 'eslint:recommended', 'plugin:import/typescript', 'raine', 'prettier'],
overrides: [
{
files: ['**/*.ts'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
project: './tsconfig.json',
},
extends: ['plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended'],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
},
plugins: ['@typescript-eslint'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-use-before-define': 'error',
'@typescript-eslint/no-unused-vars': [
'error',
{
caughtErrors: 'none',
// using destructuring to omit properties from objects
destructuredArrayIgnorePattern: '^_',
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'@typescript-eslint/array-type': [
'error',
{
default: 'array',
},
],
},
},
],
plugins: ['jsdoc'],
rules: {
'jsdoc/require-jsdoc': [
'error',
{
contexts: ['VariableDeclarator > ArrowFunctionExpression'],
require: {
ClassDeclaration: true,
ClassExpression: true,
},
},
],
},
}
================================================
FILE: .gitattributes
================================================
# Enforce Unix newlines
* text=auto eol=lf
================================================
FILE: .github/CONTRIBUTING.md
================================================
## Filing an issue
Make sure you read the list of [known issues](https://github.com/raineorshine/npm-check-updates#known-issues) and search for [similar issues](https://github.com/raineorshine/npm-check-updates/issues) before filing an issue.
## Known Issues
- If `ncu` prints output that does not seem related to this package, it may be conflicting with another executable such as `ncu-weather-cli` or Nvidia CUDA. Try using the long name instead: `npm-check-updates`.
- Windows: If npm-check-updates hangs, try setting the package file explicitly: `ncu --packageFile package.json`. You can run `ncu --loglevel verbose` to confirm that it was incorrectly waiting for stdin. See [#136](https://github.com/raineorshine/npm-check-updates/issues/136#issuecomment-155721102).
When filing an issue, please include:
- node version
- npm version
- npm-check-updates version
- the relevant package names and their specified versions from your package file
- ...or the output from `npm -g ls --depth=0` if using global mode
## Executable Stack Trace
The Vite Build uses SSR to bundle all dependencies for efficiency. There currently is no source map for `./build/cli.js`. To execute npm-check-updates with an accurate stack trace run the following
```sh
git clone https://github.com/raineorshine/npm-check-updates /MY_PROJECTS
npx tsx /MY_PROJECTS/npm-check-updates/src/bin/cli.ts
```
## Design Guidelines
The _raison d'être_ of npm-check-updates is to upgrade package.json dependencies to the latest versions, ignoring specified versions. Suggested features that do not fit within this objective will be considered out of scope.
npm-check-updates maintains a balance between minimalism and customizability. The default execution with no options will always produce simple, clean output. If you would like to add additional information to ncu's output, you may propose a new value for the `--format` option.
## Adding a new CLI or module option
All of ncu's options are generated from [/src/cli-options.ts](https://github.com/raineorshine/npm-check-updates/blob/main/src/cli-options.ts). You can add a new option to this file and then run `npm run build` to automatically generate README, CLI help text, and TypeScript definitions.
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
- [] I have searched for [similar issues](https://github.com/raineorshine/npm-check-updates/issues)
---
## Steps to Reproduce
.ncurc:
<!-- If you use a .ncurc config file, specify it here. ncu options have a dramatic effect on its behavior. -->
```js
```
Dependencies:
<!-- If the suggested upgrades are not what you expect, make sure to list your package.json dependencies here so the issue can be reproduced. -->
```json
```
Steps:
<!-- The exact steps taken to arrive at the unexpected behavior. -->
## Current Behavior
<!-- Describe the existing (incorrect) behavior. -->
## Expected Behavior
<!-- Describe the desired behavior. -->
================================================
FILE: .github/workflows/codeql.yml
================================================
name: 'CodeQL'
on:
push:
branches:
- main
- '!dependabot/**'
pull_request:
# The branches below must be a subset of the branches above
branches:
- main
schedule:
- cron: '0 2 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: 'javascript'
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
================================================
FILE: .github/workflows/lint.yml
================================================
name: Lint
on:
push:
branches:
- main
- '!dependabot/**'
pull_request:
branches:
- '**'
env:
FORCE_COLOR: 2
NODE: 24
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE }}
cache: npm
- name: Install npm dependencies
run: npm ci
- name: Run lint
run: npm run lint
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
================================================
FILE: .github/workflows/test.yml
================================================
name: Tests
on:
push:
branches:
- main
- '!dependabot/**'
pull_request:
branches:
- '**'
env:
FORCE_COLOR: 2
permissions:
contents: read
jobs:
run:
permissions:
contents: read # for actions/checkout to fetch code
name: Node ${{ matrix.node }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
node: [20, 22]
os: [ubuntu-latest, windows-latest]
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- name: Enable corepack
run: corepack enable
- name: Install npm dependencies
run: npm ci
- name: Build
run: npm run build
- name: Unit Tests
run: npm run test:unit
- name: Bun Tests
run: npm run test:bun
- name: E2E Tests
run: npm run test:e2e
if: startsWith(matrix.os, 'ubuntu') && matrix.node == 20
================================================
FILE: .gitignore
================================================
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
npm-debug.log
node_modules
build
.idea
*.iml
yarn.lock
.DS_store
# test files
/test/temp_package*.json
/test/.ncurc.json
# Agent files
.claude
================================================
FILE: .hooks/post-commit
================================================
#!/usr/bin/env bash
SHORT_SHA=$(git rev-parse HEAD)
LINT_LOG="$TMPDIR"/lint."$SHORT_SHA".log
# strip color
strip() {
sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2};?)?)?[mGK]//g"
}
# display a notification
notify() {
# if osascript is not supported, do nothing
if [ -f /usr/bin/osascript ]; then
# read back in the lint errors
ERRORS=$(sed 1,4d "$LINT_LOG")
# Trigger apple- or OSA-script on supported platforms
/usr/bin/osascript -e "display notification \"$ERRORS\" with title \"$*\""
fi
# clean up
rm "$LINT_LOG"
}
# ensure failed lint exit code passes through sed
set -o pipefail
# Do NOT run this when rebasing or we can't get the branch
branch=$(git branch --show-current)
if [ -z "$branch" ]; then
exit 0
fi
# Lint in the background, not blocking the terminal and piping all output to a file.
# If the lint fails, trigger a notification (on supported platforms) with at least the first error shown.
# We pipe output so that the terminal (tmux, vim, emacs etc.) isn't borked by stray output.
npm run lint:src | strip &>"$LINT_LOG" || notify "Lint Error" &
================================================
FILE: .hooks/pre-push
================================================
#!/bin/sh
fail=0
npm run lint || fail=1
npm run prettier -- --check || fail=1
if [ "$fail" -ne 0 ]; then
exit 1
fi
================================================
FILE: .markdownlint.js
================================================
module.exports = {
// use code indentation rather than code fencing so that extended help can be used for both the CLI and README
'code-block-style': 0,
'first-line-heading': 0,
'line-length': 0,
'no-bare-urls': 0,
'no-duplicate-heading': {
siblings_only: true,
},
// inline HTML used to create tables without headers
'no-inline-html': 0,
'commands-show-output': 0,
}
================================================
FILE: .ncurc.js
================================================
module.exports = {
format: 'group',
reject: [
// breaking
'eslint',
'eslint-plugin-n',
'eslint-plugin-promise',
// esm only modules
'@types/chai',
'@types/chai-as-promised',
'@types/remote-git-tags',
'camelcase',
'chai-as-promised',
'find-up',
'chai',
'p-map',
'remote-git-tags',
'untildify',
],
}
================================================
FILE: .npmrc
================================================
script-shell = bash
================================================
FILE: .prettierignore
================================================
build/
================================================
FILE: .prettierrc.json
================================================
{
"arrowParens": "avoid",
"importOrder": ["^\\.\\./", "^\\./"],
"importOrderSortSpecifiers": true,
"overrides": [{ "files": "*.ts", "options": { "parser": "typescript" } }],
"plugins": ["@trivago/prettier-plugin-sort-imports"],
"printWidth": 120,
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"useTabs": false
}
================================================
FILE: .vscode/settings.json
================================================
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
================================================
FILE: CHANGELOG.md
================================================
# Changelog
This file only documents **major version** releases. For smaller releases, you're stuck reading the [commit history](https://github.com/raineorshine/npm-check-updates/commits/main).
## [18.0.0] - 2025-04-21
### Breaking
The **only** breaking change in v18 is with the `-g/--global` flag.
`npm-check-updates -g` will now auto-detect your package manager based on the execution path. Previously, it defaulted to `npm`.
- `yarn dlx ncu -g --packageManager yarn` → `yarn dlx ncu -g`
- `pnpm dlx ncu --global --packageManager pnpm` → `pnpm dlx ncu -g`
- `bunx ncu -g--packageManager pnpm` → `bunx ncu -g`
If for some reason you were running `ncu -g` with an alternative package manager and relying on it checking the global `npm` packages, you will need to now explicitly specify npm:
- `ncu -g` → `ncu -g--packageManager npm`
Thanks to @LuisFerLCC for the improvement (#1514).
<https://github.com/raineorshine/npm-check-updates/compare/v17.1.18...v18.0.0>
## [17.0.0] - 2024-07-31
### Breaking
- Require node >= 18.18.0
- Deprecated versions are no longer excluded by default, as it requires fetching package info for every published version, significantly slowing down upgrades.
- You can opt in with `--no-deprecated` in the CLI or `deprecated: false` in your `ncurc` config.
- In workspaces mode, `--root` is now set by default (#1353)
- To **not** check the root package.json, use `--no-root`.
- If you have a [packageManager](https://nodejs.org/api/packages.html#packagemanager) field in your package.json, it is now upgraded by default (#1390)
- Use `--dep prod,dev,optional` for the old behavior.
<https://github.com/raineorshine/npm-check-updates/compare/v16.14.20...v17.0.0>
## [16.0.0] - 2022-07-23
### Breaking
- Automatic detection of package data on stdin has been removed. This feature was deprecated in `v14.0.0`. Add `--stdin` for old behavior.
- Wild card filters now apply to scoped packages. Previously, `ncu -f '*vite*'` would not include `@vitejs/plugin-react`. Now, filters will match any part of the package name, including the scope. Use a more specific glob or regex expression for old behavior.
<https://github.com/raineorshine/npm-check-updates/compare/v15.3.4...v16.0.0>
## [15.0.0] - 2022-06-30
### Breaking
- node >= 14.14 is now required (#1145)
- Needed to upgrade `update-notifier` with has a moderate severity vulnerability
- yarn autodetect has been improved (#1148)
- This is a patch, though _technically_ it is breaking. In the obscure case where `--packageManager` is not given, there is no `package-lock.json` in the current folder, and there is a `yarn.lock` in an ancestor directory, npm-check-updates will now use yarn.
- More practically, if you needed to specify `--packageManager yarn` explicitly before, you may not have to now
<https://github.com/raineorshine/npm-check-updates/compare/v14.1.1...v15.0.0>
## [14.0.0] - 2022-06-16
### Breaking
Prerelease versions are now "upgraded" to versions with a different [preid](https://docs.npmjs.com/cli/v8/commands/npm-version#preid).
For example, if you have a dependency at `1.3.3-next.1` and the version fetched by ncu is `1.2.3-dev.2`, ncu **will** suggest an "upgrade" to `1.2.3-dev.2`. This is because prerelease versions with different preids are incomparable. Since they are incomparable, ncu now assumes the fetched version is desired.
Since this change affects only prereleases, there is no impact on default `ncu` usage that fetches the `latest` version. With `--pre 1` or `--target newest` or `--target greatest`, this change could affect which version is suggested if versions with different preids are published. The change was made to support the new `--target @[tag]` feature.
If you have a use case where this change is not what is desired, please [report an issue](https://github.com/raineorshine/npm-check-updates/issues/new). The intention is for zero disruption to current usage.
### Features
- You can now upgrade to a specific tag, e.g. `--target @next`. Thanks to [IMalyugin](https://github.com/IMalyugin).
<https://github.com/raineorshine/npm-check-updates/compare/v13.1.5...v14.0.0>
## [13.0.0] - 2022-05-15
### Breaking
- node >= 14 is now required
- Several options which have long been deprecated have been removed:
- `--greatest` - Instead use `--target greatest`
- `--newest` - Instead use `--target newest`
- `--ownerChanged` - Instead use `--format ownerChanged`
- `--semverLevel` - Renamed to `--target`
<https://github.com/raineorshine/npm-check-updates/compare/v12.5.12...v13.0.0>
## [12.0.0] - 2021-11-01
### Breaking
- node >= 12 is required. Time to upgrade that old-ass server you never touch.
- `peerDependencies` are now excluded by default. Peer dependencies should use the **lowest** possible version that works. The old behavior encouraged a bad practice of upgrading peer dependencies. You can use `--dep prod,dev,optional,peer` for the old behavior ([#951](https://github.com/raineorshine/npm-check-updates/issues/951)).
- Dependencies with `>` will be converted to `>=`. The old behavior was causing upgrades to `> [latest]` which was impossible ([#957](https://github.com/raineorshine/npm-check-updates/issues/957)).
## Other
- TypeScript! There is a new build process, so if you have any issues with the executable or types, please report. It should be a non-breaking change if I did it correctly ([#888](https://github.com/raineorshine/npm-check-updates/issues/888)).
- WHen using `npm-check-updates` as a module, `vm` (versionmanager) is no longer exported. It was previously exposed for testing purposes, but was never part of the official API.
<https://github.com/raineorshine/npm-check-updates/compare/v11.8.5...v12.0.0>
## [11.0.0] - 2021-01-20
### Breaking
- `--packageFile` - Now interprets its argument as a glob pattern. It is possible that a previously supplied argument may be interpreted differently now (though I'm not aware of specific instances). Due to our conservative release policy we are releasing as a major version upgrade and allowing developers to assess for themselves.
### Features
- `--deep` - Run recursively in current working directory. Alias of `--packageFile '**/package.json'`.
See: [#785](https://github.com/raineorshine/npm-check-updates/issues/785)
<https://github.com/raineorshine/npm-check-updates/compare/v10.3.1...v11.0.0>
## [10.0.0] - 2020-11-08
### Breaking
- Specifying both the `--filter` option and argument filters will now throw an error. Use one or the other. Previously the arguments would override the `--filter` option, which made for a confusing result when accidentally not quoting the option in the shell. This change is only breaking for those who are relying on the incorrect behavior of argument filters overriding `--filter`.
See: [#759](https://github.com/raineorshine/npm-check-updates/issues/759#issuecomment-723587297)
<https://github.com/raineorshine/npm-check-updates/compare/v9.2.4...v10.0.0>
## [9.0.0] - 2020-09-10
### Breaking
- Versions marked as `deprecated` in npm are now ignored by default. If the latest version is deprecated, the next highest non-deprecated version will be suggested. Use `--deprecated` to include deprecated versions (old behavior).
<https://github.com/raineorshine/npm-check-updates/compare/v8.1.1...v9.0.0>
## [8.0.0] - 2020-08-29
### Breaking
- `--semverLevel major` is now `--target minor`. `--semverLevel minor` is now `--target patch`. This change was made to provide more intuitive semantics for `--semverLevel` (now `--target`). Most people assumed it meant the inclusive upper bound, so now it reflects that. [a2111f4c2](https://github.com/raineorshine/npm-check-updates/commits/a2111f4c2)
- Programmatic usage: `run` now defaults to `silent: true` instead of `loglevel: 'silent`, unless `loglevel` is explicitly specified. If you overrode `silent` or `loglevel`, this may affect the logging behavior. [423e024](https://github.com/raineorshine/npm-check-updates/commits/423e024)
### Deprecated
Options that controlled the target version (upper bound) of upgrades have been consolidated under `--target`. The old options are aliased with a deprecation warning and will be removed in the next major version. No functionality has been removed.
- `--greatest`: Renamed to `--target greatest`
- `--newest`: Renamed to `--target newest`
- `--semverLevel`: Renamed to `--target`
See: [7eca5bf3](https://github.com/raineorshine/npm-check-updates/commits/7eca5bf3)
### Features
#### Doctor Mode
[#722](https://github.com/raineorshine/npm-check-updates/pull/722)
Usage: `ncu --doctor [-u] [options]`
Iteratively installs upgrades and runs tests to identify breaking upgrades. Add `-u` to execute (modifies your package file, lock file, and node_modules).
To be more precise:
1. Runs `npm install` and `npm test` to ensure tests are currently passing.
2. Runs `ncu -u` to optimistically upgrade all dependencies.
3. If tests pass, hurray!
4. If tests fail, restores package file and lock file.
5. For each dependency, install upgrade and run tests.
6. When the breaking upgrade is found, saves partially upgraded package.json (not including the breaking upgrade) and exits.
Example:
```sh
$ ncu --doctor -u
npm install
npm run test
ncu -u
npm install
npm run test
Failing tests found:
/projects/myproject/test.js:13
throw new Error('Test failed!')
^
Now let’s identify the culprit, shall we?
Restoring package.json
Restoring package-lock.json
npm install
npm install --no-save react@16.0.0
npm run test
✓ react 15.0.0 → 16.0.0
npm install --no-save react-redux@7.0.0
npm run test
✗ react-redux 6.0.0 → 7.0.0
Saving partially upgraded package.json
```
#### GitHub URLs
Added support for GitHub URLs.
See: [f0aa792a4](https://github.com/raineorshine/npm-check-updates/commits/f0aa792a4)
Example:
```json
{
"dependencies": {
"chalk": "https://github.com/chalk/chalk#v2.0.0"
}
}
```
#### npm aliases
Added support for npm aliases.
See: [0f6f35c](https://github.com/raineorshine/npm-check-updates/commits/0f6f35c)
Example:
```json
{
"dependencies": {
"request": "npm:postman-request@2.88.1-postman.16"
}
}
```
#### Owner Changed
[#621](https://github.com/raineorshine/npm-check-updates/pull/621)
Usage: `ncu --ownerChanged`
Check if the npm user that published the package has changed between current and upgraded version.
Output values:
- Owner changed: `*owner changed*`
- Owner has not changed: _no output_
- Owner information not available: `*unknown*`
Example:
```sh
$ ncu --ownerChanged
Checking /tmp/package.json
[====================] 1/1 100%
mocha ^7.1.0 → ^8.1.3 *owner changed*
Run ncu -u to upgrade package.json
```
### Commits
<https://github.com/raineorshine/npm-check-updates/compare/v7.1.1...v8.0.0>
## [7.0.0] - 2020-06-09
### Breaking
- Removed bower support (4e4b47fd3bb567435b456906d0106ef442bf46fe)
### Patch
- Fix use of "<" with single digit versions (f04d00e550ce606893bee77b78ef2a0b2a50246a)
### Other
- Change eslint configuration
- Update dependencies
- Replace cint methods with native methods
- Add CI via GitHub Actions workflow
<https://github.com/raineorshine/npm-check-updates/compare/v6.0.2...v7.0.0>
## [6.0.0] - 2020-05-14
### Breaking
- `--semverLevel` now supports version ranges. This is a breaking change since version ranges are no longer ignored by `--semverLevel`, which may result in some dependencies having new suggested updates.
If you are not using `--semverLevel`, NO CHANGE! 😅
<https://github.com/raineorshine/npm-check-updates/compare/v5.0.0...v6.0.0>
## [5.0.0] - 2020-05-11
### Breaking
~node >= 8~
node >= 10.17
Bump minimum node version to `v10.17.0` due to `move-file` #651
If `ncu` was working for you on `v4.x`, then `v5.0.0` will still work. Just doing a major version bump since ncu's officially supported node version is changing. `v4` should be patched to be compatible with node `v8`, but I'll hold off unless someone requests it.
<https://github.com/raineorshine/npm-check-updates/compare/v4.1.2...v5.0.0>
## [4.0.0] - 2019-12-10
ncu v3 excluded prerelease versions (`-alpha`, `-beta`, etc) from the remote by default, as publishing prerelease versions to `latest` is unconventional and not recommended. Prereleases versions can be included by specifying `--pre` (and is implied in options `--greatest` and `--newest`).
However, when you are already specifying a prerelease version in your package.json dependencies, then clearly you want ncu to find newer prerelease versions. This is now default in v4, albeit with the conservative approach of sticking to the `latest` tag.
### Migration
No effect for most users.
If a prerelease version is published on the `latest` tag, and you specify a prerelease version in your package.json, ncu will now suggest upgrades for it.
If a prerelease version is published on a different tag, there is no change from ncu v3; you will still need `--pre`, `--greatest`, or `--newest` to get prerelease upgrades.
<https://github.com/raineorshine/npm-check-updates/compare/v3.2.2...v4.0.0>
## [3.0.0] - 2019-03-07
### Breaking
#### node < 8 deprecated
The required node version has been updated to allow the use of newer JavaScript features and reduce maintenance efforts for old versions.
#### System npm used
In ncu v2, an internally packaged npm was used for version lookups. When this became out-of-date and differed considerably from the system npm problems would occur. In ncu v3, the system-installed npm will be used for all lookups. This comes with the maintenance cost of needing to upgrade ncu whenever the output format of npm changes.
#### Installed modules ignored
In ncu v2, out-of-date dependencies in package.json that were installed up-to-date (e.g. `^1.0.0` specified and `1.0.1` installed) were ignored by ncu. Installed modules are now completely ignored and ncu only consider your package.json. This change was made to better match users’ expectations.
#### Existing version ranges that satisfy latest are ignored (-a by default)
In ncu v2, if you had `^1.0.0` in your package.json, a newly released `1.0.1` would be ignored by ncu. The logic was that `^1.0.0` is a range that includes `1.0.1`, so you don’t really need to change the version specified in your package.json, you just need to run `npm update`. While logical, that turned out to be quite confusing to users. In ncu v3, the package.json will always be upgraded if there is a newer version (same as `-a` in v2). The old default behavior is available via the `--minimal` option.
#### Prerelease versions ignored
In ncu v2, any version published to the `latest` tag was assumed to be a stable release version. In practice, occasional package authors would accidentally or unconventionally publish `-alpha`, `-beta`, and `-rc` versions to the `latest` tag. While I still consider this a bad practice, ncu v3 now ignores these prerelease versions by default to better match users’ expectations. The old behavior is available via the `--pre 1` option. (When `--newest` or `--greatest` are set, `--pre 1` is set by default, and can be disabled with `--pre 0`).
#### Options changed: `-m`, `--prod`, `--dev`, `--peer`
In order to only target one or more dependency sections, ncu now uses the `--dep` option instead of separate options for each section.
`--prod` is now `--dep prod`
`--dev` is now `--dep dev`
`--dev --peer` is now `--dep dev,peer` etc
The `--packageManager` alias has changed from `-m` to `-p` to make room for `--minimal` as `-m`.
<https://github.com/raineorshine/npm-check-updates/compare/v2.15.0...v3.0.0>
## [2.0.0] - 2005-08-14
v2 has a few important differences from v1:
- Newer published versions that satisfy the specified range are _not_ upgraded by default (e.g. `1.0.0` to `1.1.0`). This change was made because `npm update` handles upgrades within the satisfied range just fine, and npm-check-updates is primarily intended to provide functionality not otherwise provided by npm itself. These satisfied dependencies will still be shown when you run npm-check-updates, albeit with a short explanation. **For the old behavior, add the -ua/--upgradeAll option.**
- The command-line argument now specifies a package name filter (e.g. `ncu /^gulp-/`). For the old behavior (specifying an alternative package.json), pipe the package.json through stdin.
- Use the easier-to-type `ncu` instead of `npm-check-updates`. `npm-check-updates` is preserved for backwards-compatibility.
- Allow packageData to be specified as an option
- Colored table output
- Add -a/--upgradeAll
- Add -e/--error-level option
- Add -j/--json and --jsonFlat flags for json output
- Add -r/--registry option for specifying third-party npm registry
- Add -t/--greatest option to search for the highest versions instead of the default latest stable versions.
- Remove -f/--filter option and move to command-line argument
- Replace < and <= with ^
- Automatically look for the closest descendant package.json if not found in current directory
- Add ncu alias
- Export functionality to allow for programmatic use
- Bug fixes and refactoring
- Full unit test coverage!
<https://github.com/raineorshine/npm-check-updates/compare/v1.5.1...v2.0.0>
================================================
FILE: Dockerfile
================================================
FROM node:latest
RUN npm install -g npm-check-updates
WORKDIR /app
ENTRYPOINT ["npm-check-updates"]
================================================
FILE: LICENSE
================================================
Copyright 2025 Tomas Junnonen
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# npm-check-updates
[](https://www.npmjs.com/package/npm-check-updates)
[](https://github.com/raineorshine/npm-check-updates/actions?query=workflow%3ATests+branch%3Amain)
**npm-check-updates upgrades your package.json dependencies to the _latest_ versions, ignoring specified versions.**
- maintains existing semantic versioning _policies_, i.e. `"react": "^17.0.2"` to `"react": "^18.3.1"`.
- _only_ modifies package.json file. Run `npm install` to update your installed packages and package-lock.json.
- sensible defaults, but highly customizable
- compatible with npm, yarn, pnpm, deno, and bun
- CLI and module usage
<img width="500" alt="example output" src="https://github.com/user-attachments/assets/4808618b-ac20-4fc0-92e0-a777de70a2b6">
$${\color{red}Red}$$ major upgrade (and all [major version zero](https://semver.org/#spec-item-4))<br/>
$${\color{cyan}Cyan}$$ minor upgrade<br/>
$${\color{green}Green}$$ patch upgrade<br/>
## Installation
Install globally to use `npm-check-updates` or the shorter `ncu`:
```sh
npm install -g npm-check-updates
```
Or run with [npx](https://docs.npmjs.com/cli/v7/commands/npx) (only the long form is supported):
```sh
npx npm-check-updates
```
## Usage
Check the latest versions of all project dependencies:
```sh
$ ncu
Checking package.json
[====================] 5/5 100%
eslint 7.32.0 → 8.0.0
prettier ^2.7.1 → ^3.0.0
svelte ^3.48.0 → ^3.51.0
typescript >3.0.0 → >4.0.0
untildify <4.0.0 → ^4.0.0
webpack 4.x → 5.x
Run ncu -u to upgrade package.json
```
Upgrade a project's package file:
> **Make sure your package file is in version control and all changes have been committed. This _will_ overwrite your package file.**
```sh
$ ncu -u
Upgrading package.json
[====================] 1/1 100%
express 4.12.x → 4.13.x
Run npm install to install new versions.
$ npm install # update installed packages and package-lock.json
```
Check global packages:
```sh
ncu -g
```
## Interactive Mode
Choose which packages to update in interactive mode:
```sh
ncu --interactive
ncu -i
```

Combine with `--format group` for a truly _luxe_ experience:

### Keys
- <kbd>↑</kbd><kbd>↓</kbd> Select a package
- <kbd>Space</kbd> Toggle selection
- <kbd>a</kbd> Toggle all
- <kbd>Enter</kbd> Upgrade
## Filter packages
Filter packages using the `--filter` option or adding additional cli arguments:
```sh
# upgrade only mocha
ncu mocha
ncu -f mocha
ncu --filter mocha
# upgrade only chalk, mocha, and react
ncu chalk mocha react
ncu chalk, mocha, react
ncu -f "chalk mocha react"
```
Filter with wildcards or regex:
```sh
# upgrade packages that start with "react-"
ncu react-*
ncu "/^react-.*$/"
```
Exclude specific packages with the `--reject` option or prefixing a filter with `!`. Supports strings, wildcards, globs, comma-or-space-delimited lists, and regex:
```sh
# upgrade everything except nodemon
ncu \!nodemon
ncu -x nodemon
ncu --reject nodemon
# upgrade packages that do not start with "react-".
ncu \!react-*
ncu '/^(?!react-).*$/' # mac/linux
ncu "/^(?!react-).*$/" # windows
```
Advanced filters: [filter](https://github.com/raineorshine/npm-check-updates#filter), [filterResults](https://github.com/raineorshine/npm-check-updates#filterresults), [filterVersion](https://github.com/raineorshine/npm-check-updates#filterversion)
## How dependency updates are determined
- Direct dependencies are updated to the latest stable version:
- `2.0.1` → `2.2.0`
- `1.2` → `1.3`
- `0.1.0` → `1.0.1`
- Range operators are preserved and the version is updated:
- `^1.2.0` → `^2.0.0`
- `1.x` → `2.x`
- `>0.2.0` → `>0.3.0`
- "Less than" is replaced with a wildcard:
- `<2.0.0` → `^3.0.0`
- `1.0.0 < 2.0.0` → `^3.0.0`
- "Any version" is preserved:
- `*` → `*`
- Prerelease versions are ignored by default.
- Use `--pre` to include prerelease versions (e.g. `alpha`, `beta`, `build1235`)
- Choose what level to upgrade to:
- With `--target semver`, update according to your specified [semver](https://semver.org/) version ranges:
- `^1.1.0` → `^1.9.99`
- With `--target minor`, strictly update the patch and minor versions (including major version zero):
- `0.1.0` → `0.2.1`
- With `--target patch`, strictly update the patch version (including major version zero):
- `0.1.0` → `0.1.2`
- With `--target @next`, update to the version published on the `next` tag:
- `0.1.0` -> `0.1.1-next.1`
## Options
Options are merged with the following precedence:
1. Command line options
2. Local [Config File](#config-file) (current working directory)
3. Project Config File (next to package.json)
4. User Config File (`$HOME`)
Options that take no arguments can be negated by prefixing them with `--no-`, e.g. `--no-peer`.
<!-- BEGIN Options -->
<!-- Do not edit this section by hand. It is auto-generated in build-options.ts. Run "npm run build" or "npm run build:options" to build. -->
<table>
<tr>
<td>--cache</td>
<td>Cache versions to a local cache file. Default <code>--cacheFile</code> is ~/.ncu-cache.json and default <code>--cacheExpiration</code> is 10 minutes.</td>
</tr>
<tr>
<td>--cacheClear</td>
<td>Clear the default cache, or the cache file specified by <code>--cacheFile</code>.</td>
</tr>
<tr>
<td>--cacheExpiration <min></td>
<td>Cache expiration in minutes. Only works with <code>--cache</code>. (default: 10)</td>
</tr>
<tr>
<td>--cacheFile <path></td>
<td>Filepath for the cache file. Only works with <code>--cache</code>. (default: "~/.ncu-cache.json")</td>
</tr>
<tr>
<td>--color</td>
<td>Force color in terminal.</td>
</tr>
<tr>
<td>--concurrency <n></td>
<td>Max number of concurrent HTTP requests to registry. (default: 8)</td>
</tr>
<tr>
<td>--configFileName <s></td>
<td>Config file name. (default: .ncurc.{json,yml,js,cjs})</td>
</tr>
<tr>
<td>--configFilePath <path></td>
<td>Directory of .ncurc config file. (default: directory of <code>packageFile</code>)</td>
</tr>
<tr>
<td><a href="#cooldown">-c, --cooldown <period></a></td>
<td>Sets a minimum age for package versions to be considered for upgrade. Accepts a number (days) or a string with a unit: "7d" (days), "12h" (hours), "30m" (minutes). Reduces the risk of installing newly published, potentially compromised packages.</td>
</tr>
<tr>
<td>--cwd <path></td>
<td>Working directory in which npm will be executed.</td>
</tr>
<tr>
<td>--deep</td>
<td>Run recursively in current working directory. Alias of (<code>--packageFile '**/package.json'</code>).</td>
</tr>
<tr>
<td>--dep <value></td>
<td>Check one or more sections of dependencies only: dev, optional, peer, prod, or packageManager (comma-delimited). (default: ["prod","dev","optional","packageManager"])</td>
</tr>
<tr>
<td>--deprecated</td>
<td>Include deprecated packages. Use <code>--no-deprecated</code> to exclude deprecated packages (20–25% slower). (default: true)</td>
</tr>
<tr>
<td><a href="#doctor">-d, --doctor</a></td>
<td>Iteratively installs upgrades and runs tests to identify breaking upgrades. Requires <code>-u</code> to execute.</td>
</tr>
<tr>
<td>--doctorInstall <command></td>
<td>Specifies the install script to use in doctor mode. (default: <code>npm install</code> or the equivalent for your package manager)</td>
</tr>
<tr>
<td>--doctorTest <command></td>
<td>Specifies the test script to use in doctor mode. (default: <code>npm test</code>)</td>
</tr>
<tr>
<td>--enginesNode</td>
<td>Include only packages that satisfy engines.node as specified in the package file.</td>
</tr>
<tr>
<td>-e, --errorLevel <n></td>
<td>Set the error level. 1: exits with error code 0 if no errors occur. 2: exits with error code 0 if no packages need updating (useful for continuous integration). (default: 1)</td>
</tr>
<tr>
<td><a href="#filter">-f, --filter <p></a></td>
<td>Include only package names matching the given string, wildcard, glob, comma-or-space-delimited list, /regex/, or predicate function.</td>
</tr>
<tr>
<td><a href="#filterresults">filterResults <fn></a></td>
<td>Filters results based on a user provided predicate function after fetching new versions.</td>
</tr>
<tr>
<td><a href="#filterversion">--filterVersion <p></a></td>
<td>Filter on package version using comma-or-space-delimited list, /regex/, or predicate function.</td>
</tr>
<tr>
<td><a href="#format">--format <value></a></td>
<td>Modify the output formatting or show additional information. Specify one or more comma-delimited values: dep, group, ownerChanged, repo, time, lines, installedVersion. (default: [])</td>
</tr>
<tr>
<td>-g, --global</td>
<td>Check global packages instead of in the current project.</td>
</tr>
<tr>
<td><a href="#groupfunction">groupFunction <fn></a></td>
<td>Customize how packages are divided into groups when using <code>--format group</code>.</td>
</tr>
<tr>
<td><a href="#install">--install <value></a></td>
<td>Control the auto-install behavior: always, never, prompt. (default: "prompt")</td>
</tr>
<tr>
<td>-i, --interactive</td>
<td>Enable interactive prompts for each dependency; implies <code>-u</code> unless one of the json options are set.</td>
</tr>
<tr>
<td>-j, --jsonAll</td>
<td>Output new package file instead of human-readable message.</td>
</tr>
<tr>
<td>--jsonDeps</td>
<td>Like <code>jsonAll</code> but only lists <code>dependencies</code>, <code>devDependencies</code>, <code>optionalDependencies</code>, etc of the new package data.</td>
</tr>
<tr>
<td>--jsonUpgraded</td>
<td>Output upgraded dependencies in json.</td>
</tr>
<tr>
<td>-l, --loglevel <n></td>
<td>Amount to log: silent, error, minimal, warn, info, verbose, silly. (default: "warn")</td>
</tr>
<tr>
<td>--mergeConfig</td>
<td>Merges nested configs with the root config file for <code>--deep</code> or <code>--packageFile</code> options. (default: false)</td>
</tr>
<tr>
<td>-m, --minimal</td>
<td>Do not upgrade newer versions that are already satisfied by the version range according to semver.</td>
</tr>
<tr>
<td>--packageData <value></td>
<td>Package file data (you can also use stdin).</td>
</tr>
<tr>
<td>--packageFile <path|glob></td>
<td>Package file(s) location. (default: ./package.json)</td>
</tr>
<tr>
<td><a href="#packagemanager">-p, --packageManager <s></a></td>
<td>npm, yarn, pnpm, deno, bun, staticRegistry (default: npm).</td>
</tr>
<tr>
<td><a href="#peer">--peer</a></td>
<td>Check peer dependencies of installed packages and filter updates to compatible versions.</td>
</tr>
<tr>
<td>--pre <n></td>
<td>Include prerelease versions, e.g. -alpha.0, -beta.5, -rc.2. Automatically set to 1 when <code>--target</code> is newest or greatest, or when the current version is a prerelease. (default: 0)</td>
</tr>
<tr>
<td>--prefix <path></td>
<td>Current working directory of npm.</td>
</tr>
<tr>
<td>-r, --registry <uri></td>
<td>Specify the registry to use when looking up package versions.</td>
</tr>
<tr>
<td><a href="#registrytype">--registryType <type></a></td>
<td>Specify whether --registry refers to a full npm registry or a simple JSON file or url: npm, json. (default: npm)</td>
</tr>
<tr>
<td><a href="#reject">-x, --reject <p></a></td>
<td>Exclude packages matching the given string, wildcard, glob, comma-or-space-delimited list, /regex/, or predicate function.</td>
</tr>
<tr>
<td><a href="#rejectversion">--rejectVersion <p></a></td>
<td>Exclude package.json versions using comma-or-space-delimited list, /regex/, or predicate function.</td>
</tr>
<tr>
<td>--removeRange</td>
<td>Remove version ranges from the final package version.</td>
</tr>
<tr>
<td>--retry <n></td>
<td>Number of times to retry failed requests for package info. (default: 3)</td>
</tr>
<tr>
<td>--root</td>
<td>Runs updates on the root project in addition to specified workspaces. Only allowed with <code>--workspace</code> or <code>--workspaces</code>. (default: true)</td>
</tr>
<tr>
<td>-s, --silent</td>
<td>Don't output anything. Alias for <code>--loglevel</code> silent.</td>
</tr>
<tr>
<td>--stdin</td>
<td>Read package.json from stdin.</td>
</tr>
<tr>
<td><a href="#target">-t, --target <value></a></td>
<td>Determines the version to upgrade to: latest, newest, greatest, minor, patch, semver, <code>@[tag]</code>, or [function]. (default: latest)</td>
</tr>
<tr>
<td>--timeout <ms></td>
<td>Global timeout in milliseconds. (default: no global timeout and 30 seconds per npm-registry-fetch)</td>
</tr>
<tr>
<td>-u, --upgrade</td>
<td>Overwrite package file with upgraded versions instead of just outputting to console.</td>
</tr>
<tr>
<td>--verbose</td>
<td>Log additional information for debugging. Alias for <code>--loglevel</code> verbose.</td>
</tr>
<tr>
<td>--workspace <s></td>
<td>Run on one or more specified workspaces. Add <code>--no-root</code> to exclude the root project. (default: [])</td>
</tr>
<tr>
<td>-w, --workspaces</td>
<td>Run on all workspaces. Add <code>--no-root</code> to exclude the root project.</td>
</tr>
</table>
<!-- END Options -->
## Advanced Options
Some options have advanced usage, or allow per-package values by specifying a function in your .ncurc.js file.
Run `ncu --help [OPTION]` to view advanced help for a specific option, or see below:
<!-- BEGIN Advanced Options -->
<!-- Do not edit this section by hand. It is auto-generated in build-options.ts. Run "npm run build" or "npm run build:options" to build. -->
## cooldown
Usage:
ncu --cooldown [period]
ncu -c [period]
The cooldown option helps protect against supply chain attacks by requiring package versions to be published at least the given amount of time before considering them for upgrade.
The value can be a plain number (days) or a string with a unit suffix:
--cooldown 7 7 days
--cooldown 7d 7 days (same as above)
--cooldown 12h 12 hours
--cooldown 30m 30 minutes
Note that previous stable versions will not be suggested. The package will be completely ignored if its latest published version is within the cooldown period. This is due to a limitation of the npm registry, which does not provide a way to query previous stable versions.
Example:
Let's examine how cooldown works with a package that has these versions available:
1.0.0 Released 7 days ago (initial version)
1.1.0 Released 6 days ago (minor update)
1.1.1 Released 5 days ago (patch update)
1.2.0 Released 5 days ago (minor update)
2.0.0-beta.1 Released 5 days ago (beta release)
1.2.1 Released 4 days ago (patch update)
1.3.0 Released 4 days ago (minor update) [latest]
2.0.0-beta.2 Released 3 days ago (beta release)
2.0.0-beta.3 Released 2 days ago (beta release) [beta]
With default target (latest):
```js
$ ncu --cooldown 5
```
No update will be suggested because:
- Latest version (1.3.0) is only 4 days old.
- Cooldown requires versions to be at least 5 days old
- Use `--cooldown 4` or lower to allow this update
With `@beta`/`@tag` target:
```js
$ ncu --cooldown 3 --target @beta
```
No update will be suggested because:
- Current beta (2.0.0-beta.3) is only 2 days old
- Cooldown requires versions to be at least 3 days old
- Use `--cooldown 2` or lower to allow this update
With other targets:
```js
$ ncu --cooldown 5 --target greatest|newest|minor|patch|semver
```
Each target will select the best version that is at least 5 days old:
greatest → 1.2.0 (highest version number outside cooldown)
newest → 2.0.0-beta.1 (most recently published version outside cooldown)
minor → 1.2.0 (highest minor version outside cooldown)
patch → 1.1.1 (highest patch version outside cooldown)
Note for latest/tag targets:
> :warning: For packages that update frequently (e.g. daily releases), using a long cooldown period (7+ days) with the default `--target latest` or `--target @tag` may prevent all updates since new versions will be published before older ones meet the cooldown requirement. Please consider this when setting your cooldown period.
You can also provide a custom function in your .ncurc.js file or when importing npm-check-updates as a module.
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions.
```js
/** Set cooldown to 3 days but skip it for `@my-company` packages.
@param packageName The name of the dependency.
@returns Cooldown days restriction for given package.
*/
cooldown: packageName => (packageName.startsWith('@my-company') ? 0 : 3)
```
## doctor
Usage:
ncu --doctor -u
ncu --no-doctor
ncu -du
Iteratively installs upgrades and runs your project's tests to identify breaking upgrades. Reverts broken upgrades and updates package.json with working upgrades.
Requires `-u` to execute (modifies your package file, lock file, and node_modules)
To be more precise:
1. Runs `npm install` and `npm test` to ensure tests are currently passing.
2. Runs `ncu -u` to optimistically upgrade all dependencies.
3. If tests pass, hurray!
4. If tests fail, restores package file and lock file.
5. For each dependency, install upgrade and run tests.
6. Prints broken upgrades with test error.
7. Saves working upgrades to package.json.
Additional options:
<table>
<tr><td>--doctorInstall</td><td>specify a custom install script (default: `npm install` or `yarn`)</td></tr>
<tr><td>--doctorTest</td><td>specify a custom test script (default: `npm test`)</td></tr>
</table>
Example:
$ ncu --doctor -u
Running tests before upgrading
npm install
npm run test
Upgrading all dependencies and re-running tests
ncu -u
npm install
npm run test
Tests failed
Identifying broken dependencies
npm install
npm install --no-save react@16.0.0
npm run test
✓ react 15.0.0 → 16.0.0
npm install --no-save react-redux@7.0.0
npm run test
✗ react-redux 6.0.0 → 7.0.0
/projects/myproject/test.js:13
throw new Error('Test failed!')
^
npm install --no-save react-dnd@11.1.3
npm run test
✓ react-dnd 10.0.0 → 11.1.3
Saving partially upgraded package.json
## filter
Usage:
ncu --filter [p]
ncu -f [p]
Include only package names matching the given string, wildcard, glob, comma-or-space-delimited list, /regex/, or predicate function. Only included packages will be checked with `--peer`.
`--filter` runs _before_ new versions are fetched, in contrast to `--filterResults` which runs _after_.
You can also specify a custom function in your .ncurc.js file, or when importing npm-check-updates as a module.
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions.
```js
/**
@param name The name of the dependency.
@param semver A parsed Semver array of the current version.
(See: https://git.coolaj86.com/coolaj86/semver-utils.js#semverutils-parse-semverstring)
@returns True if the package should be included, false if it should be excluded.
*/
filter: (name, semver) => {
if (name.startsWith('@myorg/')) {
return false
}
return true
}
```
## filterResults
Filters results based on a user provided predicate function after fetching new versions.
`filterResults` runs _after_ new versions are fetched, in contrast to `filter`, `reject`, `filterVersion`, and `rejectVersion`, which run _before_. This allows you to exclude upgrades with `filterResults` based on how the version has changed (e.g. a major version change).
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions.
```js
/** Exclude major version updates. Note this could also be achieved with --target semver.
@param {string} packageName The name of the dependency.
@param {string} current Current version declaration (may be a range).
@param {SemVer[]} currentVersionSemver Current version declaration in semantic versioning format (may be a range).
@param {string} upgraded Upgraded version.
@param {SemVer} upgradedVersionSemver Upgraded version in semantic versioning format.
@returns {boolean} Return true if the upgrade should be kept; otherwise, it will be ignored.
*/
filterResults: (packageName, { current, currentVersionSemver, upgraded, upgradedVersionSemver }) => {
const currentMajor = parseInt(currentVersionSemver[0]?.major, 10)
const upgradedMajor = parseInt(upgradedVersionSemver?.major, 10)
if (currentMajor && upgradedMajor) {
return currentMajor >= upgradedMajor
}
return true
}
```
For the SemVer type definition, see: https://git.coolaj86.com/coolaj86/semver-utils.js#semverutils-parse-semverstring
## filterVersion
Usage:
ncu --filterVersion [p]
Include only versions matching the given string, wildcard, glob, comma-or-space-delimited list, /regex/, or predicate function.
`--filterVersion` runs _before_ new versions are fetched, in contrast to `--filterResults` which runs _after_.
You can also specify a custom function in your .ncurc.js file, or when importing npm-check-updates as a module.
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions. This function is an alias for the `filter` option function.
```js
/**
@param name The name of the dependency.
@param semver A parsed Semver array of the current version.
(See: https://git.coolaj86.com/coolaj86/semver-utils.js#semverutils-parse-semverstring)
@returns True if the package should be included, false if it should be excluded.
*/
filterVersion: (name, semver) => {
if (name.startsWith('@myorg/') && parseInt(semver[0]?.major) > 5) {
return false
}
return true
}
```
## format
Usage:
ncu --format [value]
Modify the output formatting or show additional information. Specify one or more comma-delimited values.
<table>
<tr><td>dep</td><td>Prints the dependency type (dev, peer, optional) of each package.</td></tr>
<tr><td>group</td><td>Groups packages by major, minor, patch, and major version zero updates.</td></tr>
<tr><td>homepage</td><td>Displays links to the package's homepage if specified in its package.json.</td></tr>
<tr><td>installedVersion</td><td>Prints the exact current version number instead of a range.</td></tr>
<tr><td>lines</td><td>Prints name@version on separate lines. Useful for piping to npm install.</td></tr>
<tr><td>ownerChanged</td><td>Shows if the package owner has changed.</td></tr>
<tr><td>repo</td><td>Infers and displays links to the package's source code repository. Requires packages to be installed.</td></tr>
<tr><td>diff</td><td>Display link to compare the changes between package versions.</td></tr>
<tr><td>time</td><td>Shows the publish time of each upgrade.</td></tr>
</table>
## groupFunction
Customize how packages are divided into groups when using `--format group`.
Only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions.
```js
/**
@param name The name of the dependency.
@param defaultGroup The predefined group name which will be used by default.
@param currentSpec The current version range in your package.json.
@param upgradedSpec The upgraded version range that will be written to your package.json.
@param upgradedVersion The upgraded version number returned by the registry.
@returns A predefined group name ('major' | 'minor' | 'patch' | 'majorVersionZero' | 'none') or a custom string to create your own group.
*/
groupFunction: (name, defaultGroup, currentSpec, upgradedSpec, upgradedVersion) => {
if (name === 'typescript' && defaultGroup === 'minor') {
return 'major'
}
if (name.startsWith('@myorg/')) {
return 'My Org'
}
return defaultGroup
}
```
## install
Usage:
ncu --install [value]
Default: prompt
Control the auto-install behavior.
<table>
<tr><td>always</td><td>Runs your package manager's install command automatically after upgrading.</td></tr>
<tr><td>never</td><td>Does not install and does not prompt.</td></tr>
<tr><td>prompt</td><td>Shows a message after upgrading that recommends an install, but does not install. In interactive mode, prompts for install. (default)</td></tr>
</table>
## packageManager
Usage:
ncu --packageManager [s]
ncu -p [s]
Specifies the package manager to use when looking up versions.
<table>
<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.lock or bun.lockb is present.</td></tr>
</table>
## peer
Usage:
ncu --peer
ncu --no-peer
Check peer dependencies of installed packages and filter updates to compatible versions.
Example:
The following example demonstrates how `--peer` works, and how it uses peer dependencies from upgraded modules.
The package ncu-test-peer-update has two versions published:
- 1.0.0 has peer dependency `"ncu-test-return-version": "1.0.x"`
- 1.1.0 has peer dependency `"ncu-test-return-version": "1.1.x"`
Our test app has the following dependencies:
"ncu-test-peer-update": "1.0.0",
"ncu-test-return-version": "1.0.0"
The latest versions of these packages are:
"ncu-test-peer-update": "1.1.0",
"ncu-test-return-version": "2.0.0"
With `--peer`:
ncu upgrades packages to the highest version that still adheres to the peer dependency constraints:
ncu-test-peer-update 1.0.0 → 1.1.0
ncu-test-return-version 1.0.0 → 1.1.0
Without `--peer`:
As a comparison: without using the `--peer` option, ncu will suggest the latest versions, ignoring peer dependencies:
ncu-test-peer-update 1.0.0 → 1.1.0
ncu-test-return-version 1.0.0 → 2.0.0
## registryType
Usage:
ncu --registryType [type]
Specify whether `--registry` refers to a full npm registry or a simple JSON file.
<table>
<tr><td>npm</td><td>Default npm registry</td></tr>
<tr><td>json</td><td>Checks versions from a file or url to a simple JSON registry. Must include the `--registry` option.
Example:
// local file
$ ncu --registryType json --registry ./registry.json
// url
$ ncu --registryType json --registry https://api.mydomain/registry.json
// you can omit --registryType when the registry ends in .json
$ ncu --registry ./registry.json
$ ncu --registry https://api.mydomain/registry.json
registry.json:
{
"prettier": "2.7.1",
"typescript": "4.7.4"
}
</td></tr>
</table>
## reject
Usage:
ncu --reject [p]
ncu -x [p]
The inverse of `--filter`. Exclude package names matching the given string, wildcard, glob, comma-or-space-delimited list, /regex/, or predicate function. This will also exclude them from the `--peer` check.
`--reject` runs _before_ new versions are fetched, in contrast to `--filterResults` which runs _after_.
You can also specify a custom function in your .ncurc.js file, or when importing npm-check-updates as a module.
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions.
```js
/**
@param name The name of the dependency.
@param semver A parsed Semver array of the current version.
(See: https://git.coolaj86.com/coolaj86/semver-utils.js#semverutils-parse-semverstring)
@returns True if the package should be excluded, false if it should be included.
*/
reject: (name, semver) => {
if (name.startsWith('@myorg/')) {
return true
}
return false
}
```
## rejectVersion
Usage:
ncu --rejectVersion [p]
The inverse of `--filterVersion`. Exclude versions matching the given string, wildcard, glob, comma-or-space-delimited list, /regex/, or predicate function.
`--rejectVersion` runs _before_ new versions are fetched, in contrast to `--filterResults` which runs _after_.
You can also specify a custom function in your .ncurc.js file, or when importing npm-check-updates as a module.
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions. This function is an alias for the reject option function.
```js
/**
@param name The name of the dependency.
@param semver A parsed Semver array of the current version.
(See: https://git.coolaj86.com/coolaj86/semver-utils.js#semverutils-parse-semverstring)
@returns True if the package should be excluded, false if it should be included.
*/
rejectVersion: (name, semver) => {
if (name.startsWith('@myorg/') && parseInt(semver[0]?.major) > 5) {
return true
}
return false
}
```
## target
Usage:
ncu --target [value]
ncu -t [value]
Determines the version to upgrade to. (default: "latest")
<table>
<tr><td>greatest</td><td>Upgrade to the highest version number published, regardless of release date or tag. Includes prereleases.</td></tr>
<tr><td>latest</td><td>Upgrade to whatever the package's "latest" git tag points to. Excludes prereleases unless --pre is specified.</td></tr>
<tr><td>minor</td><td>Upgrade to the highest minor version without bumping the major version.</td></tr>
<tr><td>newest</td><td>Upgrade to the version with the most recent publish date, even if there are other version numbers that are higher. Includes prereleases.</td></tr>
<tr><td>patch</td><td>Upgrade to the highest patch version without bumping the minor or major versions.</td></tr>
<tr><td>semver</td><td>Upgrade to the highest version within the semver range specified in your package.json.</td></tr>
<tr><td>@[tag]</td><td>Upgrade to the version published to a specific tag, e.g. 'next' or 'beta'.</td></tr>
</table>
e.g.
ncu --target semver
You can also specify a custom function in your .ncurc.js file, or when importing npm-check-updates as a module.
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions.
```js
/** Upgrade major version zero to the next minor version, and everything else to latest.
@param name The name of the dependency.
@param semver A parsed Semver object of the upgraded version.
(See: https://git.coolaj86.com/coolaj86/semver-utils.js#semverutils-parse-semverstring)
@returns One of the valid target values (specified in the table above).
*/
target: (name, semver) => {
if (parseInt(semver[0]?.major) === '0') return 'minor'
return 'latest'
}
```
<!-- END Advanced Options -->
## Config File
Add a `.ncurc.{json,yml,js,cjs}` file to your project directory to specify configuration information.
For example, `.ncurc.json`:
```json
{
"upgrade": true,
"filter": "svelte",
"reject": ["@types/estree", "ts-node"]
}
```
Options are merged with the following precedence:
1. Command line options
2. Local Config File (current working directory)
3. Project Config File (next to package.json)
4. User Config File (`$HOME`)
You can also specify a custom config file name or path using the `--configFileName` or `--configFilePath` command line options.
### Config Functions
Some options offer more advanced configuration using a function definition. These include [filter](https://github.com/raineorshine/npm-check-updates#filter), [filterVersion](https://github.com/raineorshine/npm-check-updates#filterversion), [filterResults](https://github.com/raineorshine/npm-check-updates#filterresults), [reject](https://github.com/raineorshine/npm-check-updates#reject), [rejectVersion](https://github.com/raineorshine/npm-check-updates#rejectversion), and [groupFunction](https://github.com/raineorshine/npm-check-updates#groupfunction). To define an options function, convert the config file to a JS file by adding the `.js` extension and setting module.exports:
For example, `.ncurc.js`:
```js
/** @type {import('npm-check-updates').RcOptions } */
module.exports = {
upgrade: true,
filter: name => name.startsWith('@myorg/'),
}
```
Alternatively, you can use the defineConfig helper which should provide intellisense without the need for jsdoc annotations:
```js
const { defineConfig } = require('npm-check-updates')
module.exports = defineConfig({
upgrade: true,
filter: name => name.startsWith('@myorg/'),
})
```
### JSON Schema
If you write `.ncurc` config files using json or yaml, you can add the JSON Schema to your IDE settings for completions.
e.g. for VS Code:
```json
"json.schemas": [
{
"fileMatch": [
".ncurc",
".ncurc.json",
],
"url": "https://raw.githubusercontent.com/raineorshine/npm-check-updates/main/src/types/RunOptions.json"
}
],
"yaml.schemas": {
"https://raw.githubusercontent.com/raineorshine/npm-check-updates/main/src/types/RunOptions.json": [
".ncurc.yml",
]
},
```
## Module/Programmatic Usage
npm-check-updates can be imported as a module:
```js
import ncu from 'npm-check-updates'
const upgraded = await ncu.run({
// Pass any cli option
packageFile: '../package.json',
upgrade: true,
// Defaults:
// jsonUpgraded: true,
// silent: true,
})
console.log(upgraded) // { "mypackage": "^2.0.0", ... }
```
## Contributing
Contributions are happily accepted. I respond to all PR's and can offer guidance on where to make changes. For contributing tips see [CONTRIBUTING.md](https://github.com/raineorshine/npm-check-updates/blob/main/.github/CONTRIBUTING.md).
## Problems?
[File an issue](https://github.com/raineorshine/npm-check-updates/issues). Please [search existing issues](https://github.com/raineorshine/npm-check-updates/issues?utf8=%E2%9C%93&q=is%3Aissue) first.
================================================
FILE: deploy.md
================================================
# Deployment Instructions
- Have you created tests?
- Have you updated the README?
```bash
npm version [minor]
git push && git push --tags
npm publish [--tag unstable]
```
- Update the release history
<https://github.com/raineorshine/npm-check-updates/releases>
================================================
FILE: package.json
================================================
{
"name": "npm-check-updates",
"version": "19.6.5",
"author": "Tomas Junnonen <tomas1@gmail.com>",
"license": "Apache-2.0",
"contributors": [
"Raine Revere (https://github.com/raineorshine)",
"Imamuzzaki Abu Salam <imamuzzaki@gmail.com>"
],
"description": "Find newer versions of dependencies than what your package.json allows",
"keywords": [
"dependencies",
"npm",
"package.json",
"update",
"upgrade",
"versions"
],
"engines": {
"node": ">=20.0.0",
"npm": ">=8.12.1"
},
"main": "build/index.js",
"types": "build/index.d.ts",
"scripts": {
"build": "rimraf build && npm run build:options && vite build",
"build:options": "vite-node src/scripts/build-options.ts",
"build:analyze": "rimraf build && npm run build:options && ANALYZER=true vite build",
"lint": "cross-env FORCE_COLOR=1 npm-run-all --parallel --aggregate-output lint:*",
"lint:lockfile": "lockfile-lint",
"lint:markdown": "markdownlint \"**/*.md\" --ignore \"**/node_modules/**/*.md\" --ignore build --config .markdownlint.js",
"lint:src": "eslint --cache --cache-location node_modules/.cache/.eslintcache --ignore-path .gitignore --report-unused-disable-directives .",
"prepare": "src/scripts/install-hooks",
"prepublishOnly": "npm run build",
"prettier": "prettier . --check",
"prettier:fix": "prettier . --write",
"test": "npm run test:unit && npm run test:e2e",
"test:bun": "test/bun-install.sh && mocha test/bun",
"test:unit": "mocha test test/package-managers/*",
"test:e2e": "./test/e2e.sh",
"ncu": "node build/cli.js"
},
"bin": {
"npm-check-updates": "build/cli.js",
"ncu": "build/cli.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/raineorshine/npm-check-updates.git"
},
"homepage": "https://github.com/raineorshine/npm-check-updates",
"bugs": {
"url": "https://github.com/raineorshine/npm-check-updates/issues"
},
"overrides": {
"ip": "2.0.1",
"jsonparse": "https://github.com/ARitz-Cracker/jsonparse/tree/patch-1",
"@yarnpkg/parsers": "2.6.0"
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/bun": "^1.2.23",
"@types/chai": "^4.3.19",
"@types/chai-as-promised": "^8.0.0",
"@types/chai-string": "^1.4.5",
"@types/cli-table": "^0.3.4",
"@types/hosted-git-info": "^3.0.5",
"@types/ini": "^4.1.1",
"@types/js-yaml": "^4.0.9",
"@types/jsonlines": "^0.1.5",
"@types/lodash": "^4.17.20",
"@types/mocha": "^10.0.10",
"@types/node": "^24.5.2",
"@types/npm-registry-fetch": "^8.0.8",
"@types/parse-github-url": "^1.0.3",
"@types/picomatch": "^4.0.2",
"@types/progress": "^2.0.7",
"@types/prompts": "^2.4.9",
"@types/semver": "^7.7.1",
"@types/semver-utils": "^1.1.3",
"@types/sinon": "^17.0.4",
"@types/update-notifier": "^6.0.8",
"@typescript-eslint/eslint-plugin": "^8.44.1",
"@typescript-eslint/parser": "^8.44.1",
"camelcase": "^6.3.0",
"chai": "^4.3.10",
"chai-as-promised": "^7.1.2",
"chai-string": "^1.6.0",
"chalk": "^5.6.2",
"cli-table3": "^0.6.5",
"commander": "^14.0.1",
"cross-env": "^10.0.0",
"dequal": "^2.0.3",
"eslint": "^8.57.0",
"eslint-config-prettier": "^10.1.8",
"eslint-config-raine": "^0.5.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsdoc": "^60.5.0",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-promise": "^6.6.0",
"fast-glob": "^3.3.3",
"fast-memoize": "^2.5.2",
"find-up": "5.0.0",
"fp-and-or": "^1.0.2",
"hosted-git-info": "^9.0.0",
"ini": "^5.0.0",
"js-yaml": "^4.1.0",
"jsonc-parser": "^3.3.1",
"jsonlines": "^0.1.1",
"lockfile-lint": "^4.14.1",
"lodash": "^4.17.21",
"markdownlint-cli": "^0.45.0",
"mocha": "^11.7.2",
"npm-registry-fetch": "^19.0.0",
"npm-run-all": "^4.1.5",
"p-map": "^4.0.0",
"parse-github-url": "^1.0.3",
"picomatch": "^4.0.3",
"prettier": "^3.6.2",
"progress": "^2.0.3",
"prompts-ncu": "^3.0.2",
"rc-config-loader": "^4.1.3",
"rfdc": "^1.4.1",
"rimraf": "^6.0.1",
"rollup-plugin-node-externals": "^8.1.1",
"semver": "^7.7.2",
"semver-utils": "^1.1.4",
"should": "^13.2.3",
"sinon": "^21.0.0",
"source-map-support": "^0.5.21",
"spawn-please": "^3.0.0",
"strip-ansi": "^7.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.9.2",
"typescript-json-schema": "^0.65.1",
"untildify": "^4.0.0",
"update-notifier": "^7.3.1",
"verdaccio": "^6.1.6",
"vite": "^7.1.7",
"vite-bundle-analyzer": "^1.2.3",
"vite-node": "^3.2.4",
"vite-plugin-dts": "^4.5.4",
"yaml": "^2.8.2",
"yarn": "^1.22.22",
"zod": "^4.3.5"
},
"files": [
"build",
"!**/test/**"
],
"lockfile-lint": {
"allowed-schemes": [
"https:",
"git+ssh:"
],
"allowed-hosts": [
"npm",
"github.com"
],
"empty-hostname": false,
"type": "npm ",
"path": "package-lock.json"
},
"mocha": {
"check-leaks": true,
"extension": [
"test.ts"
],
"require": [
"source-map-support/register",
"ts-node/register"
],
"timeout": 60000,
"trace-deprecation": true,
"trace-warnings": true,
"use_strict": true
}
}
================================================
FILE: src/bin/cli.ts
================================================
#!/usr/bin/env node
import { Help, Option, program } from 'commander'
import createCloneDeep from 'rfdc'
import semver from 'semver'
import pkg from '../../package.json'
import cliOptions, { renderExtendedHelp } from '../cli-options'
import ncu from '../index'
import { chalkInit } from '../lib/chalk'
// async global contexts are only available in esm modules -> function
import getNcuRc from '../lib/getNcuRc'
import { pickBy } from '../lib/pick'
const optionVersionDescription = 'Output the version number of npm-check-updates.'
/** Removes inline code ticks. */
const uncode = (s: string) => s.replace(/`/g, '')
const cloneDeep = createCloneDeep()
;(async () => {
// importing update-notifier dynamically as esm modules are only allowed to be dynamically imported inside of cjs modules
const { default: updateNotifier } = await import('update-notifier')
// check if a new version of ncu is available and print an update notification
//
// For testing from specific versions, use:
//
// updateNotifier({
// pkg: {
// name: 'npm-check-updates',
// version: x.y.z
// },
// updateCheckInterval: 0
// })
const notifier = updateNotifier({ pkg })
if (notifier.update && notifier.update.latest !== pkg.version) {
const { default: chalk } = await import('chalk')
// generate release urls for all the major versions from the current version up to the latest
const currentMajor = semver.parse(notifier.update.current)?.major
const latestMajor = semver.parse(notifier.update.latest)?.major
const majorVersions =
// Greater than or equal to (>=) will always return false if either operant is NaN or undefined.
// Without this condition, it can result in a RangeError: Invalid array length.
// See: https://github.com/raineorshine/npm-check-updates/issues/1200
currentMajor && latestMajor && latestMajor >= currentMajor
? new Array(latestMajor - currentMajor).fill(0).map((x, i) => currentMajor + i + 1)
: []
const releaseUrls = majorVersions.map(majorVersion => `${pkg.homepage ?? ''}/releases/tag/v${majorVersion}.0.0`)
// for non-major updates, generate a URL to view all commits since the current version
const compareUrl = `${pkg.homepage ?? ''}/compare/v${notifier.update.current}...v${notifier.update.latest}`
notifier.notify({
defer: false,
isGlobal: true,
message: `Update available ${chalk.dim('{currentVersion}')}${chalk.reset(' → ')}${
notifier.update.type === 'major'
? chalk.red('{latestVersion}')
: notifier.update.type === 'minor'
? chalk.yellow('{latestVersion}')
: chalk.green('{latestVersion}')
}
Run ${chalk.cyan('{updateCommand}')} to update
${chalk.dim.underline(
notifier.update.type === 'major' ? releaseUrls.map(url => chalk.dim.underline(url)).join('\n') : compareUrl,
)}`,
})
}
// manually detect option-specific help
// https://github.com/raineorshine/npm-check-updates/issues/787
const rawArgs = process.argv.slice(2)
const indexHelp = rawArgs.findIndex(arg => arg === '--help' || arg === '-h')
if (indexHelp !== -1 && rawArgs[indexHelp + 1]) {
const helpOption = rawArgs[indexHelp + 1].replace(/^-*/, '')
if (helpOption === 'help' || helpOption === 'h') {
console.info('Would you like some help with your help?')
} else {
await chalkInit()
const nonHelpArgs = [...rawArgs.slice(0, indexHelp), ...rawArgs.slice(indexHelp + 1)]
nonHelpArgs.forEach(arg => {
// match option by long or short
const query = arg.replace(/^-*/, '')
const option = cliOptions.find(
option =>
query === option.long ||
query === option.short ||
(query === `no-${option.long}` && option.type === 'boolean'),
)
if (option) {
console.info(renderExtendedHelp(option) + '\n')
} else if (query === 'version' || query === 'v' || query === 'V') {
console.info(
renderExtendedHelp({
long: 'version',
short: 'v',
description: optionVersionDescription,
// do not pass boolean or it will print --no-version
type: 'string',
}) + '\n',
)
} else {
console.info(`Unknown option: ${arg}`)
}
})
}
process.exit(0)
}
// a set of options that only work in an rc config file, not on the command line
const noCli = new Set(cliOptions.filter(option => option.cli === false).map(option => `--${option.long}`))
// start commander program
program
.description('[filter] is a list or regex of package names to check (all others will be ignored).')
.usage('[options] [filter]')
// See: boolean optional arg below
.configureHelp({
optionTerm: option =>
option.long && noCli.has(option.long)
? option.long.replace('--', '') + '*'
: option.long === '--version'
? // add -v to version help to cover the alias added below
'-v, -V, --version'
: option.flags.replace('[bool]', ''),
optionDescription: option =>
option.long === '--version'
? optionVersionDescription
: option.long === '--help'
? `You're lookin' at it. Run "ncu --help <option>" for a specific option.`
: Help.prototype.optionDescription(option),
})
// add hidden -v alias for --V/--version
.addOption(new Option('-v, --versionAlias').hideHelp())
.on('option:versionAlias', () => {
console.info(pkg.version)
process.exit(0)
})
// add cli options
cliOptions.forEach(({ long, short, arg, description, default: defaultValue, help, parse, type }) => {
const flags = `${short ? `-${short}, ` : ''}--${long}${arg ? ` <${arg}>` : ''}`
// format description for cli by removing inline code ticks
// point to help in description if extended help text is available
const descriptionFormatted = `${uncode(description)}${help ? ` Run "ncu --help ${long}" for details.` : ''}`
// handle 3rd/4th argument polymorphism
program.option(flags, descriptionFormatted, parse || defaultValue, parse ? defaultValue : undefined)
// add --no- prefixed boolean options
// necessary for overriding booleans set to true in the ncurc
if (type === 'boolean') {
program.addOption(new Option(`--no-${long}`).default(false).hideHelp())
}
})
// set version option at the end
program.version(pkg.version)
// commander mutates its optionValues with program.parse
// In order to call program.parse again and parse the rc file options, we need to clear commander's internal optionValues
// Otherwise array options will be duplicated
const defaultOptionValues = cloneDeep((program as any)._optionValues)
program.allowExcessArguments(true)
program.parse(process.argv)
const programOpts = program.opts()
const programArgs = process.argv.slice(2)
const { color, configFileName, configFilePath, global, packageFile, mergeConfig } = programOpts
// Force color on all chalk instances.
// See: /src/lib/chalk.ts
await chalkInit(color)
// load .ncurc
// Do not load when tests are running (can be overridden if configFilePath is set explicitly, or --mergeConfig option specified)
const rcResult =
!process.env.NCU_TESTS || configFilePath || mergeConfig
? await getNcuRc({
configFileName,
configFilePath,
global,
packageFile,
options: { ...programOpts, cli: true },
})
: null
// override rc args with program args
const rcArgs = (rcResult?.args || []).filter(
(arg, i, args) =>
(typeof arg !== 'string' || !arg.startsWith('-') || !programArgs.includes(arg)) &&
(typeof args[i - 1] !== 'string' || !args[i - 1].startsWith('-') || !programArgs.includes(args[i - 1])),
)
// insert config arguments into command line arguments so they can all be parsed by commander
const combinedArguments = [...process.argv.slice(0, 2), ...rcArgs, ...programArgs]
// See defaultOptionValues comment above
;(program as any)._optionValues = defaultOptionValues
program.parse(combinedArguments)
const combinedProgramOpts = program.opts()
// filter out undefined program options and combine cli options with config file options
const options = {
...(rcResult && Object.keys(rcResult.config).length > 0 ? { rcConfigPath: rcResult.filePath } : null),
...pickBy(program.opts(), (value: unknown) => value !== undefined),
args: program.args,
...(combinedProgramOpts.filter ? { filter: combinedProgramOpts.filter } : null),
...(combinedProgramOpts.reject ? { reject: combinedProgramOpts.reject } : null),
}
// NOTE: Options handling and defaults go in initOptions in index.js
ncu(options, { cli: true })
})()
================================================
FILE: src/cli-options.ts
================================================
import path from 'path'
import { defaultCacheFile } from './lib/cache'
import chalk from './lib/chalk'
import parseCooldown from './lib/parseCooldown'
import { sortBy } from './lib/sortBy'
import table from './lib/table'
import CLIOption from './types/CLIOption'
import ExtendedHelp from './types/ExtendedHelp'
import { Index } from './types/IndexType'
/** Valid strings for the --target option. Indicates the desired version to upgrade to. */
const supportedVersionTargets = ['latest', 'newest', 'greatest', 'minor', 'patch', 'semver']
/** Pads the left side of each line in a string. */
const padLeft = (s: string, n: number) =>
s
.split('\n')
.map(line => `${''.padStart(n, ' ')}${line}`)
.join('\n')
/** Formats a code block for CLI or markdown. */
const codeBlock = (code: string, { markdown }: { markdown?: boolean } = {}) =>
`${markdown ? '```js\n' : ''}${padLeft(code, markdown ? 0 : 4)}${markdown ? '\n```' : ''}`
/** Removes inline code ticks. */
const uncode = (s: string) => s.replace(/`/g, '')
/** Parses a number from a string or number input. Throws if the value is not a number. */
const parseNumberOption =
(optionName: string) =>
(value: unknown): number => {
if (typeof value === 'number') {
return value
} else if (typeof value === 'string') {
const parsed = parseInt(value, 10)
if (!isNaN(parsed)) {
return parsed
}
}
throw new Error(`${optionName} must be a number`)
}
/** Renders the extended help for an option with usage information. */
export const renderExtendedHelp = (option: CLIOption, { markdown }: { markdown?: boolean } = {}) => {
let output = ''
if (option.cli !== false) {
// add -u to doctor option
output = `Usage:
ncu --${option.long}${option.arg ? ` [${option.arg}]` : ''}${option.long === 'doctor' ? ' -u' : ''}\n`
}
if (option.type === 'boolean') {
output += ` ncu --no-${option.long}\n`
}
if (option.short) {
// add -u to doctor option
output += ` ncu -${option.short}${option.arg ? ` [${option.arg}]` : ''}${option.long === 'doctor' ? 'u' : ''}\n`
}
if (option.default !== undefined && !(Array.isArray(option.default) && option.default.length === 0)) {
output += `\nDefault: ${option.default}\n`
}
if (option.help) {
const helpText =
typeof option.help === 'function'
? markdown
? option.help({ markdown })
: uncode(option.help({ markdown }))
: option.help
output += `\n${helpText.trim()}\n\n`
} else if (option.description) {
const description = markdown ? option.description : uncode(option.description)
output += `\n${description.replace(/`/g, '')}\n`
}
return output.trim()
}
/** Extended help for the --doctor option. */
const extendedHelpDoctor: ExtendedHelp = ({
markdown,
}) => `Iteratively installs upgrades and runs your project's tests to identify breaking upgrades. Reverts broken upgrades and updates package.json with working upgrades.
${chalk.yellow('Requires `-u` to execute')} (modifies your package file, lock file, and node_modules)
To be more precise:
1. Runs \`npm install\` and \`npm test\` to ensure tests are currently passing.
2. Runs \`ncu -u\` to optimistically upgrade all dependencies.
3. If tests pass, hurray!
4. If tests fail, restores package file and lock file.
5. For each dependency, install upgrade and run tests.
6. Prints broken upgrades with test error.
7. Saves working upgrades to package.json.
Additional options:
${table({
markdown,
rows: [
[chalk.cyan('--doctorInstall'), 'specify a custom install script (default: `npm install` or `yarn`)'],
[chalk.cyan('--doctorTest'), 'specify a custom test script (default: `npm test`)'],
],
})}
Example:
$ ncu --doctor -u
Running tests before upgrading
npm install
npm run test
Upgrading all dependencies and re-running tests
ncu -u
npm install
npm run test
Tests failed
Identifying broken dependencies
npm install
npm install --no-save react@16.0.0
npm run test
✓ react 15.0.0 → 16.0.0
npm install --no-save react-redux@7.0.0
npm run test
✗ react-redux 6.0.0 → 7.0.0
/projects/myproject/test.js:13
throw new Error('Test failed!')
^
npm install --no-save react-dnd@11.1.3
npm run test
✓ react-dnd 10.0.0 → 11.1.3
Saving partially upgraded package.json
`
/** Extended help for the filterResults option. */
const extendedHelpFilterResults: ExtendedHelp = ({ markdown }) => {
/** If markdown, surround inline code with backticks. */
const codeInline = (code: string) => (markdown ? `\`${code}\`` : code)
return `Filters results based on a user provided predicate function after fetching new versions.
${codeInline('filterResults')} runs _after_ new versions are fetched, in contrast to ${codeInline(
'filter',
)}, ${codeInline('reject')}, ${codeInline('filterVersion')}, and ${codeInline(
'rejectVersion',
)}, which run _before_. This allows you to exclude upgrades with ${codeInline(
'filterResults',
)} based on how the version has changed (e.g. a major version change).
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions.
${codeBlock(
`${chalk.gray(`/** Exclude major version updates. Note this could also be achieved with --target semver.
@param {string} packageName The name of the dependency.
@param {string} current Current version declaration (may be a range).
@param {SemVer[]} currentVersionSemver Current version declaration in semantic versioning format (may be a range).
@param {string} upgraded Upgraded version.
@param {SemVer} upgradedVersionSemver Upgraded version in semantic versioning format.
@returns {boolean} Return true if the upgrade should be kept; otherwise, it will be ignored.
*/`)}
${chalk.green('filterResults')}: (packageName, { current, currentVersionSemver, upgraded, upgradedVersionSemver }) ${chalk.cyan(
'=>',
)} {
${chalk.cyan('const')} currentMajor ${chalk.red('=')} parseInt(currentVersionSemver[${chalk.cyan('0')}]?.major, ${chalk.cyan(
'10',
)})
${chalk.cyan('const')} upgradedMajor ${chalk.red('=')} parseInt(upgradedVersionSemver?.major, ${chalk.cyan('10')})
${chalk.red('if')} (currentMajor ${chalk.red('&&')} upgradedMajor) {
${chalk.red('return')} currentMajor ${chalk.red('>=')} upgradedMajor
}
${chalk.red('return')} ${chalk.cyan('true')}
}`,
{ markdown },
)}
For the SemVer type definition, see: https://git.coolaj86.com/coolaj86/semver-utils.js#semverutils-parse-semverstring
`
}
/** Extended help for the --format option. */
const extendedHelpFormat: ExtendedHelp = ({ markdown }) => {
const header =
'Modify the output formatting or show additional information. Specify one or more comma-delimited values.'
const tableString = table({
colAligns: ['right', 'left'],
markdown,
rows: [
['dep', `Prints the dependency type (dev, peer, optional) of each package.`],
['group', `Groups packages by major, minor, patch, and major version zero updates.`],
['homepage', `Displays links to the package's homepage if specified in its package.json.`],
['installedVersion', 'Prints the exact current version number instead of a range.'],
['lines', 'Prints name@version on separate lines. Useful for piping to npm install.'],
['ownerChanged', `Shows if the package owner has changed.`],
['repo', `Infers and displays links to the package's source code repository. Requires packages to be installed.`],
['diff', `Display link to compare the changes between package versions.`],
['time', 'Shows the publish time of each upgrade.'],
],
})
return `${header}\n\n${padLeft(tableString, markdown ? 0 : 4)}
`
}
/** Extended help for the --install option. */
const extendedHelpInstall: ExtendedHelp = ({ markdown }) => {
const header = 'Control the auto-install behavior.'
const tableString = table({
colAligns: ['right', 'left'],
markdown,
rows: [
['always', `Runs your package manager's install command automatically after upgrading.`],
['never', `Does not install and does not prompt.`],
[
'prompt',
`Shows a message after upgrading that recommends an install, but does not install. In interactive mode, prompts for install. (default)`,
],
],
})
return `${header}\n\n${padLeft(tableString, markdown ? 0 : 4)}
`
}
/** Extended help for the --filter option. */
const extendedHelpFilterFunction: ExtendedHelp = ({ markdown }) => {
/** If markdown, surround inline code with backticks. */
const codeInline = (code: string) => (markdown ? `\`${code}\`` : code)
return `Include only package names matching the given string, wildcard, glob, comma-or-space-delimited list, /regex/, or predicate function. Only included packages will be checked with ${codeInline(
'--peer',
)}.
${codeInline('--filter')} runs _before_ new versions are fetched, in contrast to ${codeInline(
'--filterResults',
)} which runs _after_.
You can also specify a custom function in your .ncurc.js file, or when importing npm-check-updates as a module.
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions.
${codeBlock(
`${chalk.gray(`/**
@param name The name of the dependency.
@param semver A parsed Semver array of the current version.
(See: https://git.coolaj86.com/coolaj86/semver-utils.js#semverutils-parse-semverstring)
@returns True if the package should be included, false if it should be excluded.
*/`)}
${chalk.green('filter')}: (name, semver) ${chalk.cyan('=>')} {
${chalk.red('if')} (name.startsWith(${chalk.yellow(`'@myorg/'`)})) {
${chalk.red('return')} ${chalk.cyan('false')}
}
${chalk.red('return')} ${chalk.cyan('true')}
}`,
{ markdown },
)}
`
}
/** Extended help for the --filterVersion option. */
const extendedHelpFilterVersionFunction: ExtendedHelp = ({ markdown }) => {
/** If markdown, surround inline code with backticks. */
const codeInline = (code: string) => (markdown ? `\`${code}\`` : code)
return `Include only versions matching the given string, wildcard, glob, comma-or-space-delimited list, /regex/, or predicate function.
${codeInline('--filterVersion')} runs _before_ new versions are fetched, in contrast to ${codeInline(
'--filterResults',
)} which runs _after_.
You can also specify a custom function in your .ncurc.js file, or when importing npm-check-updates as a module.
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions. This function is an alias for the ${codeInline('filter')} option function.
${codeBlock(
`${chalk.gray(`/**
@param name The name of the dependency.
@param semver A parsed Semver array of the current version.
(See: https://git.coolaj86.com/coolaj86/semver-utils.js#semverutils-parse-semverstring)
@returns True if the package should be included, false if it should be excluded.
*/`)}
${chalk.green('filterVersion')}: (name, semver) ${chalk.cyan('=>')} {
${chalk.red('if')} (name.startsWith(${chalk.yellow(`'@myorg/'`)}) ${chalk.red(
'&&',
)} parseInt(semver[0]?.major) ${chalk.cyan('>')} ${chalk.cyan(`5`)}) {
${chalk.red('return')} ${chalk.cyan('false')}
}
${chalk.red('return')} ${chalk.cyan('true')}
}`,
{ markdown },
)}
`
}
/** Extended help for the --reject option. */
const extendedHelpRejectFunction: ExtendedHelp = ({ markdown }) => {
/** If markdown, surround inline code with backticks. */
const codeInline = (code: string) => (markdown ? `\`${code}\`` : code)
return `The inverse of ${codeInline(
'--filter',
)}. Exclude package names matching the given string, wildcard, glob, comma-or-space-delimited list, /regex/, or predicate function. This will also exclude them from the ${codeInline(
'--peer',
)} check.
${codeInline('--reject')} runs _before_ new versions are fetched, in contrast to ${codeInline(
'--filterResults',
)} which runs _after_.
You can also specify a custom function in your .ncurc.js file, or when importing npm-check-updates as a module.
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions.
${codeBlock(
`${chalk.gray(`/**
@param name The name of the dependency.
@param semver A parsed Semver array of the current version.
(See: https://git.coolaj86.com/coolaj86/semver-utils.js#semverutils-parse-semverstring)
@returns True if the package should be excluded, false if it should be included.
*/`)}
${chalk.green('reject')}: (name, semver) ${chalk.cyan('=>')} {
${chalk.red('if')} (name.startsWith(${chalk.yellow(`'@myorg/'`)})) {
${chalk.red('return')} ${chalk.cyan('true')}
}
${chalk.red('return')} ${chalk.cyan('false')}
}`,
{ markdown },
)}
`
}
/** Extended help for the --rejectVersion option. */
const extendedHelpRejectVersionFunction: ExtendedHelp = ({ markdown }) => {
/** If markdown, surround inline code with backticks. */
const codeInline = (code: string) => (markdown ? `\`${code}\`` : code)
return `The inverse of ${codeInline(
'--filterVersion',
)}. Exclude versions matching the given string, wildcard, glob, comma-or-space-delimited list, /regex/, or predicate function.
${codeInline('--rejectVersion')} runs _before_ new versions are fetched, in contrast to ${codeInline(
'--filterResults',
)} which runs _after_.
You can also specify a custom function in your .ncurc.js file, or when importing npm-check-updates as a module.
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions. This function is an alias for the reject option function.
${codeBlock(
`${chalk.gray(`/**
@param name The name of the dependency.
@param semver A parsed Semver array of the current version.
(See: https://git.coolaj86.com/coolaj86/semver-utils.js#semverutils-parse-semverstring)
@returns True if the package should be excluded, false if it should be included.
*/`)}
${chalk.green('rejectVersion')}: (name, semver) ${chalk.cyan('=>')} {
${chalk.red('if')} (name.startsWith(${chalk.yellow(`'@myorg/'`)}) ${chalk.red(
'&&',
)} parseInt(semver[0]?.major) ${chalk.cyan('>')} ${chalk.cyan(`5`)}) {
${chalk.red('return')} ${chalk.cyan('true')}
}
${chalk.red('return')} ${chalk.cyan('false')}
}`,
{ markdown },
)}
`
}
/** Extended help for the --group option. */
const extendedHelpGroupFunction: ExtendedHelp = ({ markdown }) => {
return `Customize how packages are divided into groups when using \`--format group\`.
Only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions.
${codeBlock(
`${chalk.gray(`/**
@param name The name of the dependency.
@param defaultGroup The predefined group name which will be used by default.
@param currentSpec The current version range in your package.json.
@param upgradedSpec The upgraded version range that will be written to your package.json.
@param upgradedVersion The upgraded version number returned by the registry.
@returns A predefined group name ('major' | 'minor' | 'patch' | 'majorVersionZero' | 'none') or a custom string to create your own group.
*/`)}
${chalk.green('groupFunction')}: (name, defaultGroup, currentSpec, upgradedSpec, upgradedVersion) ${chalk.cyan('=>')} {
${chalk.red('if')} (name ${chalk.red('===')} ${chalk.yellow(`'typescript'`)} ${chalk.red(
'&&',
)} defaultGroup ${chalk.red('===')} ${chalk.yellow(`'minor'`)}) {
${chalk.red('return')} ${chalk.yellow(`'major'`)}
}
${chalk.red('if')} (name.startsWith(${chalk.yellow(`'@myorg/'`)})) {
${chalk.red('return')} ${chalk.yellow(`'My Org'`)}
}
${chalk.red('return')} defaultGroup
}`,
{ markdown },
)}
`
}
/** Extended help for the --target option. */
const extendedHelpTarget: ExtendedHelp = ({ markdown }) => {
const header = 'Determines the version to upgrade to. (default: "latest")'
const tableString = table({
colAligns: ['right', 'left'],
markdown,
rows: [
[
'greatest',
`Upgrade to the highest version number published, regardless of release date or tag. Includes prereleases.`,
],
[
'latest',
`Upgrade to whatever the package's "latest" git tag points to. Excludes prereleases unless --pre is specified.`,
],
['minor', 'Upgrade to the highest minor version without bumping the major version.'],
[
'newest',
`Upgrade to the version with the most recent publish date, even if there are other version numbers that are higher. Includes prereleases.`,
],
['patch', `Upgrade to the highest patch version without bumping the minor or major versions.`],
['semver', `Upgrade to the highest version within the semver range specified in your package.json.`],
['@[tag]', `Upgrade to the version published to a specific tag, e.g. 'next' or 'beta'.`],
],
})
return `${header}
${padLeft(tableString, markdown ? 0 : 4)}
e.g.
${codeBlock(`ncu --target semver`)}
You can also specify a custom function in your .ncurc.js file, or when importing npm-check-updates as a module.
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions.
${codeBlock(
`${chalk.gray(`/** Upgrade major version zero to the next minor version, and everything else to latest.
@param name The name of the dependency.
@param semver A parsed Semver object of the upgraded version.
(See: https://git.coolaj86.com/coolaj86/semver-utils.js#semverutils-parse-semverstring)
@returns One of the valid target values (specified in the table above).
*/`)}
${chalk.green('target')}: (name, semver) ${chalk.cyan('=>')} {
${chalk.red('if')} (parseInt(semver[0]?.major) ${chalk.red('===')} ${chalk.yellow("'0'")}) ${chalk.red(
'return',
)} ${chalk.yellow("'minor'")}
${chalk.red('return')} ${chalk.yellow("'latest'")}
}`,
{ markdown },
)}
`
}
/** Extended help for the --packageManager option. */
const extendedHelpPackageManager: ExtendedHelp = ({ markdown }) => {
const header = 'Specifies the package manager to use when looking up versions.'
const tableString = table({
colAligns: ['right', 'left'],
markdown,
rows: [
['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.lock or bun.lockb is present.`],
],
})
return `${header}\n\n${padLeft(tableString, markdown ? 0 : 4)}
`
}
/** Extended help for the --registryType option. */
const extendedHelpRegistryType: ExtendedHelp = ({ markdown }) => {
/** If markdown, surround inline code with backticks. */
const codeInline = (code: string) => (markdown ? `\`${code}\`` : code)
const header = `Specify whether ${codeInline('--registry')} refers to a full npm registry or a simple JSON file.`
const tableString = table({
colAligns: ['right', 'left'],
markdown,
rows: [
['npm', `Default npm registry`],
[
'json',
`Checks versions from a file or url to a simple JSON registry. Must include the ${chalk.cyan(
'`--registry`',
)} option.
Example:
${chalk.gray('// local file')}
${chalk.cyan('$')} ncu --registryType json --registry ./registry.json
${chalk.gray('// url')}
${chalk.cyan('$')} ncu --registryType json --registry https://api.mydomain/registry.json
${chalk.gray('// you can omit --registryType when the registry ends in .json')}
${chalk.cyan('$')} ncu --registry ./registry.json
${chalk.cyan('$')} ncu --registry https://api.mydomain/registry.json
registry.json:
{
"prettier": "2.7.1",
"typescript": "4.7.4"
}
`,
],
],
})
return `${header}\n\n${padLeft(tableString, markdown ? 0 : 4)}
`
}
/** Extended help for the --peer option. */
const extendedHelpPeer: ExtendedHelp = ({ markdown }) => {
/** If markdown, surround inline code with backticks. */
const codeInline = (code: string) => (markdown ? `\`${code}\`` : code)
return `Check peer dependencies of installed packages and filter updates to compatible versions.
${chalk.bold('Example')}:
The following example demonstrates how \`--peer\` works, and how it uses peer dependencies from upgraded modules.
The package ${chalk.bold('ncu-test-peer-update')} has two versions published:
- 1.0.0 has peer dependency ${codeInline('"ncu-test-return-version": "1.0.x"')}
- 1.1.0 has peer dependency ${codeInline('"ncu-test-return-version": "1.1.x"')}
Our test app has the following dependencies:
"ncu-test-peer-update": "1.0.0",
"ncu-test-return-version": "1.0.0"
The latest versions of these packages are:
"ncu-test-peer-update": "1.1.0",
"ncu-test-return-version": "2.0.0"
${chalk.bold('With `--peer`')}:
ncu upgrades packages to the highest version that still adheres to the peer dependency constraints:
ncu-test-peer-update 1.0.0 → 1.${chalk.cyan('1.0')}
ncu-test-return-version 1.0.0 → 1.${chalk.cyan('1.0')}
${chalk.bold('Without `--peer`')}:
As a comparison: without using the \`--peer\` option, ncu will suggest the latest versions, ignoring peer dependencies:
ncu-test-peer-update 1.0.0 → 1.${chalk.cyan('1.0')}
ncu-test-return-version 1.0.0 → ${chalk.red('2.0.0')}
`
}
/** Extended help for the --cooldown option. */
const extendedHelpCooldown: ExtendedHelp = ({ markdown }) => {
return `The cooldown option helps protect against supply chain attacks by requiring package versions to be published at least the given amount of time before considering them for upgrade.
The value can be a plain number (days) or a string with a unit suffix:
--cooldown 7 7 days
--cooldown 7d 7 days (same as above)
--cooldown 12h 12 hours
--cooldown 30m 30 minutes
Note that previous stable versions will ${chalk.bold('not')} be suggested. The package will be completely ignored if its latest published version is within the cooldown period. This is due to a limitation of the npm registry, which does not provide a way to query previous stable versions.
${chalk.bold('Example')}:
Let's examine how cooldown works with a package that has these versions available:
1.0.0 Released 7 days ago (initial version)
1.1.0 Released 6 days ago (minor update)
1.1.1 Released 5 days ago (patch update)
1.2.0 Released 5 days ago (minor update)
2.0.0-beta.1 Released 5 days ago (beta release)
1.2.1 Released 4 days ago (patch update)
1.3.0 Released 4 days ago (minor update) [latest]
2.0.0-beta.2 Released 3 days ago (beta release)
2.0.0-beta.3 Released 2 days ago (beta release) [beta]
${chalk.bold('With default target (latest)')}:
${codeBlock(`${chalk.cyan('$')} ncu --cooldown 5`, { markdown })}
No update will be suggested because:
- Latest version (1.3.0) is only 4 days old.
- Cooldown requires versions to be at least 5 days old
- Use \`--cooldown 4\` or lower to allow this update
${chalk.bold('With `@beta`/`@tag` target')}:
${codeBlock(`${chalk.cyan('$')} ncu --cooldown 3 --target @beta`, { markdown })}
No update will be suggested because:
- Current beta (2.0.0-beta.3) is only 2 days old
- Cooldown requires versions to be at least 3 days old
- Use \`--cooldown 2\` or lower to allow this update
${chalk.bold('With other targets')}:
${codeBlock(`${chalk.cyan('$')} ncu --cooldown 5 --target greatest|newest|minor|patch|semver`, { markdown })}
Each target will select the best version that is at least 5 days old:
greatest → 1.2.0 (highest version number outside cooldown)
newest → 2.0.0-beta.1 (most recently published version outside cooldown)
minor → 1.2.0 (highest minor version outside cooldown)
patch → 1.1.1 (highest patch version outside cooldown)
${chalk.bold('Note for latest/tag targets')}:
> :warning: For packages that update frequently (e.g. daily releases), using a long cooldown period (7+ days) with the default \`--target latest\` or \`--target @tag\` may prevent all updates since new versions will be published before older ones meet the cooldown requirement. Please consider this when setting your cooldown period.
You can also provide a custom function in your .ncurc.js file or when importing npm-check-updates as a module.
> :warning: The predicate function is only available in .ncurc.js or when importing npm-check-updates as a module, not on the command line. To convert a JSON config to a JS config, follow the instructions at https://github.com/raineorshine/npm-check-updates#config-functions.
${codeBlock(
`${chalk.gray(`/** Set cooldown to 3 days but skip it for \`@my-company\` packages.
@param packageName The name of the dependency.
@returns Cooldown days restriction for given package.
*/`)}
${chalk.green('cooldown')}: packageName ${chalk.cyan('=>')} (packageName.startsWith(${chalk.yellow("'@my-company'")}) ? ${chalk.cyan('0')} : ${chalk.cyan('3')})`,
{ markdown },
)}
`
}
// store CLI options separately from bin file so that they can be used to build type definitions
const cliOptions: CLIOption[] = [
{
long: 'cache',
description: `Cache versions to a local cache file. Default \`--cacheFile\` is ${defaultCacheFile} and default \`--cacheExpiration\` is 10 minutes.`,
type: 'boolean',
},
{
long: 'cacheClear',
description: 'Clear the default cache, or the cache file specified by `--cacheFile`.',
type: 'boolean',
},
{
long: 'cacheExpiration',
arg: 'min',
description: 'Cache expiration in minutes. Only works with `--cache`.',
parse: parseNumberOption('cacheExpiration'),
default: 10,
type: 'number',
},
{
long: 'cacheFile',
arg: 'path',
description: 'Filepath for the cache file. Only works with `--cache`.',
parse: value => {
if (typeof value !== 'string') {
throw new Error('cacheFile must be a string')
}
return path.isAbsolute(value) ? value : path.join(process.cwd(), value)
},
default: defaultCacheFile,
type: 'string',
},
{
long: 'color',
description: 'Force color in terminal.',
type: 'boolean',
},
{
long: 'concurrency',
arg: 'n',
description: 'Max number of concurrent HTTP requests to registry.',
parse: parseNumberOption('concurrency'),
default: 8,
type: 'number',
},
{
long: 'configFileName',
arg: 's',
description: 'Config file name. (default: .ncurc.{json,yml,js,cjs})',
type: 'string',
},
{
long: 'configFilePath',
arg: 'path',
description: 'Directory of .ncurc config file. (default: directory of `packageFile`)',
type: 'string',
},
{
long: 'cwd',
arg: 'path',
description: 'Working directory in which npm will be executed.',
type: 'string',
},
{
long: 'deep',
description: `Run recursively in current working directory. Alias of (\`--packageFile '**/package.json'\`).`,
type: 'boolean',
},
{
long: 'dep',
arg: 'value',
description:
'Check one or more sections of dependencies only: dev, optional, peer, prod, or packageManager (comma-delimited).',
default: ['prod', 'dev', 'optional', 'packageManager'],
parse: value => (value && typeof value === 'string' ? value.split(',') : value),
type: 'string | readonly string[]',
},
{
long: 'deprecated',
default: true,
description: 'Include deprecated packages. Use `--no-deprecated` to exclude deprecated packages (20–25% slower).',
type: 'boolean',
},
{
long: 'doctor',
short: 'd',
description:
'Iteratively installs upgrades and runs tests to identify breaking upgrades. Requires `-u` to execute.',
type: 'boolean',
help: extendedHelpDoctor,
},
{
long: 'doctorInstall',
arg: 'command',
description:
'Specifies the install script to use in doctor mode. (default: `npm install` or the equivalent for your package manager)',
type: 'string',
},
{
long: 'doctorTest',
arg: 'command',
description: 'Specifies the test script to use in doctor mode. (default: `npm test`)',
type: 'string',
},
{
long: 'enginesNode',
description: 'Include only packages that satisfy engines.node as specified in the package file.',
type: 'boolean',
},
{
long: 'errorLevel',
short: 'e',
arg: 'n',
description:
'Set the error level. 1: exits with error code 0 if no errors occur. 2: exits with error code 0 if no packages need updating (useful for continuous integration).',
parse: parseNumberOption('errorLevel'),
default: 1,
type: 'number',
},
{
long: 'filter',
short: 'f',
arg: 'p',
description:
'Include only package names matching the given string, wildcard, glob, comma-or-space-delimited list, /regex/, or predicate function.',
type: 'string | RegExp | readonly (string | RegExp)[] | FilterFunction',
parse: (value, accum) => [...(accum || []), value],
help: extendedHelpFilterFunction,
},
{
long: 'filterResults',
arg: 'fn',
cli: false,
description: `Filters results based on a user provided predicate function after fetching new versions.`,
type: 'FilterResultsFunction',
help: extendedHelpFilterResults,
},
{
long: 'filterVersion',
arg: 'p',
description: 'Filter on package version using comma-or-space-delimited list, /regex/, or predicate function.',
type: 'string | RegExp | readonly (string | RegExp)[] | FilterFunction',
parse: (value, accum) => [...(accum || []), value],
help: extendedHelpFilterVersionFunction,
},
{
long: 'format',
arg: 'value',
description:
'Modify the output formatting or show additional information. Specify one or more comma-delimited values: dep, group, ownerChanged, repo, time, lines, installedVersion.',
parse: value => (typeof value === 'string' ? value.split(',') : value),
default: [],
type: 'readonly string[]',
choices: ['dep', 'group', 'homepage', 'ownerChanged', 'repo', 'diff', 'time', 'lines', 'installedVersion'],
help: extendedHelpFormat,
},
{
long: 'global',
short: 'g',
description: 'Check global packages instead of in the current project.',
type: 'boolean',
},
{
long: 'groupFunction',
arg: 'fn',
cli: false,
description: `Customize how packages are divided into groups when using \`--format group\`.`,
type: 'GroupFunction',
help: extendedHelpGroupFunction,
},
{
long: 'install',
arg: 'value',
description: 'Control the auto-install behavior: always, never, prompt.',
help: extendedHelpInstall,
default: 'prompt',
choices: ['always', 'never', 'prompt'],
type: `'always' | 'never' | 'prompt'`,
},
{
long: 'interactive',
short: 'i',
description: 'Enable interactive prompts for each dependency; implies `-u` unless one of the json options are set.',
type: 'boolean',
},
{
// program.json is set to true in programInit if any options that begin with 'json' are true
long: 'jsonAll',
short: 'j',
description: 'Output new package file instead of human-readable message.',
type: 'boolean',
},
{
long: 'jsonDeps',
description:
'Like `jsonAll` but only lists `dependencies`, `devDependencies`, `optionalDependencies`, etc of the new package data.',
type: 'boolean',
},
{
long: 'jsonUpgraded',
description: 'Output upgraded dependencies in json.',
type: 'boolean',
},
{
long: 'loglevel',
short: 'l',
arg: 'n',
description: 'Amount to log: silent, error, minimal, warn, info, verbose, silly.',
default: 'warn',
type: 'string',
},
{
long: 'mergeConfig',
description: `Merges nested configs with the root config file for \`--deep\` or \`--packageFile\` options. (default: false)`,
type: 'boolean',
},
{
long: 'minimal',
short: 'm',
description: 'Do not upgrade newer versions that are already satisfied by the version range according to semver.',
type: 'boolean',
},
{
long: 'packageData',
arg: 'value',
description: 'Package file data (you can also use stdin).',
type: 'string | PackageFile',
},
{
long: 'packageFile',
arg: 'path|glob',
description: 'Package file(s) location. (default: ./package.json)',
type: 'string',
},
{
long: 'packageManager',
short: 'p',
arg: 's',
description: 'npm, yarn, pnpm, deno, bun, staticRegistry (default: npm).',
help: extendedHelpPackageManager,
type: `'npm' | 'yarn' | 'pnpm' | 'deno' | 'bun' | 'staticRegistry'`,
},
{
long: 'peer',
description: 'Check peer dependencies of installed packages and filter updates to compatible versions.',
type: 'boolean',
help: extendedHelpPeer,
},
{
long: 'pre',
arg: 'n',
description:
'Include prerelease versions, e.g. -alpha.0, -beta.5, -rc.2. Automatically set to 1 when `--target` is newest or greatest, or when the current version is a prerelease. (default: 0)',
parse: (value: unknown): boolean => {
if (typeof value === 'number') {
return !!value
} else if (typeof value === 'string') {
return !!parseInt(value, 10)
} else {
throw new Error('pre must be a number')
}
},
type: 'number',
},
{
long: 'prefix',
arg: 'path',
description: 'Current working directory of npm.',
type: 'string',
},
{
long: 'registry',
short: 'r',
arg: 'uri',
description: 'Specify the registry to use when looking up package versions.',
type: 'string',
},
{
long: 'registryType',
arg: 'type',
description:
'Specify whether --registry refers to a full npm registry or a simple JSON file or url: npm, json. (default: npm)',
help: extendedHelpRegistryType,
type: `'npm' | 'json'`,
},
{
long: 'reject',
short: 'x',
arg: 'p',
description:
'Exclude packages matching the given string, wildcard, glob, comma-or-space-delimited list, /regex/, or predicate function.',
type: 'string | RegExp | readonly (string | RegExp)[] | FilterFunction',
parse: (value, accum) => [...(accum || []), value],
help: extendedHelpRejectFunction,
},
{
long: 'rejectVersion',
arg: 'p',
description: 'Exclude package.json versions using comma-or-space-delimited list, /regex/, or predicate function.',
type: 'string | RegExp | readonly (string | RegExp)[] | FilterFunction',
parse: (value, accum) => [...(accum || []), value],
help: extendedHelpRejectVersionFunction,
},
{
long: 'removeRange',
description: 'Remove version ranges from the final package version.',
type: 'boolean',
},
{
long: 'root',
default: true,
description:
'Runs updates on the root project in addition to specified workspaces. Only allowed with `--workspace` or `--workspaces`.',
type: 'boolean',
},
{
long: 'retry',
arg: 'n',
description: 'Number of times to retry failed requests for package info.',
parse: parseNumberOption('retry'),
default: 3,
type: 'number',
},
{
long: 'silent',
short: 's',
description: "Don't output anything. Alias for `--loglevel` silent.",
type: 'boolean',
},
{
long: 'stdin',
description: 'Read package.json from stdin.',
type: 'string',
},
{
long: 'target',
short: 't',
arg: 'value',
description: `Determines the version to upgrade to: latest, newest, greatest, minor, patch, semver, \`@[tag]\`, or [function]. (default: latest)`,
help: extendedHelpTarget,
// eslint-disable-next-line no-template-curly-in-string
type: `${supportedVersionTargets.map(s => `'${s}'`).join(' | ')} | ${'`@${string}`'} | TargetFunction`,
},
{
long: 'timeout',
arg: 'ms',
description: 'Global timeout in milliseconds. (default: no global timeout and 30 seconds per npm-registry-fetch)',
parse: parseNumberOption('timeout'),
type: 'number',
},
{
long: 'upgrade',
short: 'u',
description: 'Overwrite package file with upgraded versions instead of just outputting to console.',
type: 'boolean',
},
{
long: 'verbose',
description: 'Log additional information for debugging. Alias for `--loglevel` verbose.',
type: 'boolean',
},
{
long: 'workspace',
arg: 's',
parse: (value, accum) => [...accum, value],
default: [],
description: 'Run on one or more specified workspaces. Add `--no-root` to exclude the root project.',
type: 'readonly string[]',
},
{
long: 'workspaces',
short: 'w',
description: 'Run on all workspaces. Add `--no-root` to exclude the root project.',
type: 'boolean',
},
{
long: 'cooldown',
short: 'c',
arg: 'period',
description:
'Sets a minimum age for package versions to be considered for upgrade. Accepts a number (days) or a string with a unit: "7d" (days), "12h" (hours), "30m" (minutes). Reduces the risk of installing newly published, potentially compromised packages.',
type: `number | string | CooldownFunction`,
help: extendedHelpCooldown,
parse: value => {
if (typeof value === 'number' || typeof value === 'function') {
return value
} else if (typeof value === 'string') {
const days = parseCooldown(value)
return days !== null ? days : parseInt(value, 10)
} else {
throw new Error('cooldown must be a number, string, or function')
}
},
},
]
// put cliOptions into an object for O(1) lookups
export const cliOptionsMap = cliOptions.reduce(
(accum, option) => ({
...accum,
...(option.short ? { [option.short]: option } : null),
...(option.long ? { [option.long]: option } : null),
}),
{} as Index<CLIOption>,
)
const cliOptionsSorted = sortBy(cliOptions, v => v.long)
export default cliOptionsSorted
================================================
FILE: src/index.ts
================================================
import path from 'path'
import prompts from 'prompts-ncu'
import pkg from '../package.json'
import { cliOptionsMap } from './cli-options'
import { cacheClear } from './lib/cache'
import chalk, { chalkInit } from './lib/chalk'
import determinePackageManager from './lib/determinePackageManager'
import doctor from './lib/doctor'
import findPackage from './lib/findPackage'
import getAllPackages from './lib/getAllPackages'
import getNcuRc from './lib/getNcuRc'
import initOptions from './lib/initOptions'
import { print, printJson } from './lib/logging'
import mergeOptions from './lib/mergeOptions'
import programError from './lib/programError'
import runGlobal from './lib/runGlobal'
import runLocal from './lib/runLocal'
import spawnCommand from './lib/spawnCommand'
import { Index } from './types/IndexType'
import { Options } from './types/Options'
import { PackageFile } from './types/PackageFile'
import { PackageInfo } from './types/PackageInfo'
import { RunOptions } from './types/RunOptions'
import { VersionSpec } from './types/VersionSpec'
export { default as defineConfig } from './lib/defineConfig'
export type { RcOptions } from './types/RcOptions'
// allow prompt injection from environment variable for testing purposes
if (process.env.INJECT_PROMPTS) {
prompts.inject(JSON.parse(process.env.INJECT_PROMPTS))
}
/** Tracks the (first) unhandled rejection so the process can exit with an error code at the end. This allows other errors to be logged before the process exits. */
let unhandledRejectionError = false
// Use `node --trace-uncaught ...` to show where the exception was thrown.
// See: https://nodejs.org/api/process.html#event-unhandledrejection
process.on('unhandledRejection', (reason: string | Error) => {
// do not rethrow, as there may be other errors to print out
console.error(reason)
// ensure the process exits with a non-zero code at the end
unhandledRejectionError = true
})
/**
* Volta is a tool for managing JavaScript tooling like Node and npm. Volta has
* its own system for installing global packages which circumvents npm, so
* commands like `npm ls -g` do not accurately reflect what is installed.
*
* The ability to use `npm ls -g` is tracked in this Volta issue: https://github.com/volta-cli/volta/issues/1012
*/
const noVolta = (options: Options) => {
// The first check is for macOS/Linux and the second check is for Windows
if (options.global && (!!process.env.VOLTA_HOME || process.env.PATH?.includes('\\Volta'))) {
const message =
'It appears you are using Volta. `npm-check-updates --global` ' +
'cannot be used with Volta because Volta has its own system for ' +
'managing global packages which circumvents npm.\n\n' +
'If you are still receiving this message after uninstalling Volta, ' +
'ensure your PATH does not contain an entry for Volta and your ' +
'shell profile does not define VOLTA_HOME. You may need to reboot ' +
'for changes to your shell profile to take effect.'
print(options, message, 'error')
process.exit(1)
}
}
/** Returns the package manager that should be used to install packages after running "ncu -u". Uses the same detection logic as the main package manager determination. */
const getPackageManagerForInstall = async (options: Options, packageFile: string) => {
// create options context for the package file location
const installOptions: Options = {
...options,
cwd: options.cwd || path.resolve(packageFile, '..'),
packageFile,
}
// when packageManager is set to staticRegistry, we need to infer the package manager from lock files
if (options.packageManager === 'staticRegistry') {
return await determinePackageManager({ ...installOptions, packageManager: undefined })
} else if (options.packageManager && options.packageManager !== 'npm') {
return options.packageManager
}
// use the same logic as the main package manager detection
return await determinePackageManager(installOptions)
}
/** Returns if analysis contains upgrades */
const someUpgraded = (pkgs: string[], analysis: Index<PackageFile> | PackageFile, options: Options) => {
// deep mode analysis is of type Index<PackageFile>
// non-deep mode analysis is of type <PackageFile>, so we normalize it to Index<PackageFile>
const analysisNormalized: Index<PackageFile> =
!options.deep && !options.workspaces && !options.workspace
? { [pkgs[0]]: analysis as PackageFile }
: (analysis as Index<PackageFile>)
return Object.values(analysisNormalized).some(upgrades => Object.keys(upgrades).length > 0)
}
/** Either suggest an install command based on the package manager, or in interactive mode, prompt to auto-install. */
const install = async (
pkgs: string[],
analysis: Index<PackageFile> | PackageFile,
options: Options,
): Promise<unknown> => {
if (options.install === 'never') {
print(options, '')
return
}
// if no packages were upgraded (i.e. all dependencies deselected in interactive mode), then bail without suggesting an install.
// normalize the analysis for one or many packages
if (!someUpgraded(pkgs, analysis, options)) return
// for the purpose of the install hint, just use the package manager used in the first sub-project
// if auto-installing, the actual package manager in each sub-project will be used
const packageManager = await getPackageManagerForInstall(options, pkgs[0])
// by default, show an install hint after upgrading
// this will be disabled in interactive mode if the user chooses to have npm-check-updates execute the install command
const installHint = `Run ${chalk.cyan(packageManager + ' install')}${
pkgs.length > 1 && !options.workspace && !options.workspaces ? ' in each project directory' : ''
} to install new versions`
// Disable interactive mode when running doctor EXCEPT when running tests.
// Otherwise running doctor mode on npm-check-updates itself will cause interactive.test.ts to fail.
const isInteractive = options.interactive && (process.env.NCU_TESTS || !process.env.NCU_DOCTOR)
// prompt the user if they want ncu to run "npm install"
let response
if (isInteractive && options.install === 'prompt') {
print(options, '')
response = await prompts({
type: 'confirm',
name: 'value',
message: `${installHint}?`,
initial: true,
// allow Ctrl+C to kill the process
onState: (state: any) => {
if (state.aborted) {
process.nextTick(() => process.exit(1))
}
},
})
}
// auto-install
if (options.install === 'always' || (isInteractive && response.value)) {
if (options.install === 'always') {
print(options, '')
}
print(options, 'Installing dependencies...')
// only run npm install once in the root when in workspace mode
// npm install will install packages for all workspaces
const isWorkspace = options.workspaces || !!options.workspace?.length
const pkgsNormalized = isWorkspace ? ['package.json'] : pkgs
for await (const pkgFile of pkgsNormalized) {
const packageManager = await getPackageManagerForInstall(options, pkgFile)
const cwd = options.cwd || path.resolve(pkgFile, '..')
let stdout = ''
try {
await spawnCommand(
packageManager,
['install'],
{
stdout: (data: string) => {
stdout += data
},
stderr: (data: string) => {
console.error(chalk.red(data.toString()))
},
},
{
cwd,
// spawnCommand takes the native SpawnOptions type, but the env property is missing some of the type definitions for environment variables that npm-check-updates uses. Cast to any to allow these extra environment variables.
// npm_config_strict_peer_dependencies is expected to be a string for some reason
env: {
...process.env,
...(options.color !== false ? { FORCE_COLOR: true } : null),
// With spawn, pnpm install will fail with ERR_PNPM_PEER_DEP_ISSUES Unmet peer dependencies.
// When pnpm install is run directly from the terminal, this error does not occur.
// When pnpm install is run from a simple spawn script, this error does not occur.
// The issue only seems to be when pnpm install is executed from npm-check-updates, but it's not clear what configuration or environmental factors are causing this.
// For now, turn off strict-peer-dependencies on pnpm auto-install.
// See: https://github.com/raineorshine/npm-check-updates/issues/1191
...(packageManager === 'pnpm' ? { npm_config_strict_peer_dependencies: false } : null),
} as any,
},
)
print(options, stdout)
print(options, 'Done')
} catch (err: any) {
// sometimes packages print errors to stdout instead of stderr
// if there is nothing on stderr, reject with stdout
console.error(err?.message || err || stdout)
// use a program error to exit with a non-zero code rather than throwing a new Error and allowing it to bubble up to the "this is a bug and should be reported message".
programError(
options,
'Install failed. This is not a bug in npm-check-updates. The most common causes are invalid peer dependencies, networking issues, or failing postinstall scripts. Consider using --peer to filter updates to compatible versions (takes longer) or --doctor to identify breaking upgrades.',
)
}
}
}
// show the install hint unless auto-install occurred
else if (!isInteractive) {
print(options, `\n${installHint}.`)
}
}
/** Runs the dependency upgrades. Loads the ncurc, finds the package file, and handles --deep. */
async function runUpgrades(options: Options, timeout?: NodeJS.Timeout): Promise<Index<string> | PackageFile | void> {
const [selectedPackageInfos, workspacePackages]: [PackageInfo[], string[]] = await getAllPackages(options)
const packageFilepaths: string[] = selectedPackageInfos.map((packageInfo: PackageInfo) => packageInfo.filepath)
// enable deep mode if --deep, --workspace, --workspaces, or if multiple package files are found
const isWorkspace = options.workspaces || !!options.workspace?.length
options.deep = options.deep || isWorkspace || selectedPackageInfos.length > 1
let analysis: Index<PackageFile> | PackageFile | void
if (options.global) {
const analysis = await runGlobal(options)
clearTimeout(timeout)
return analysis
} else if (options.deep) {
analysis = await selectedPackageInfos.reduce(
async (previousPromise, packageInfo: PackageInfo) => {
const packages = await previousPromise
// copy object to prevent share .ncurc options between different packageFile, to prevent unpredictable behavior
const rcResult = await getNcuRc({ packageFile: packageInfo.filepath, options })
let rcConfig = rcResult.config
if (options.mergeConfig && Object.keys(rcConfig).length) {
// Merge config options.
rcConfig = mergeOptions(options, rcConfig)
}
const pkgOptions: Options = {
...options,
...rcConfig,
packageFile: packageInfo.filepath,
workspacePackages,
}
// For virtual catalog files (like package.json#catalog), use the PackageInfo data directly
// since the virtual file doesn't exist on disk
let pkgData: string | null
let pkgFile: string
let indexKey: string
if (packageInfo.filepath.includes('#') || packageInfo.name === 'catalogs') {
// Virtual catalog file or catalog package - use PackageInfo data
pkgData = packageInfo.pkgFile
pkgFile = packageInfo.filepath
// For synthetic catalog files, use the actual underlying file path as the index key
indexKey = packageInfo.filepath.includes('#catalog')
? packageInfo.filepath.replace('#catalog', '')
: packageInfo.filepath
// Print the same message as findPackage for consistency
const relPathToPackage = path.resolve(indexKey)
print(pkgOptions, `${pkgOptions.upgrade ? 'Upgrading' : 'Checking'} ${relPathToPackage} catalog dependencies`)
} else {
// Regular file - read from disk
const result = await findPackage(pkgOptions)
pkgData = result.pkgData
pkgFile = result.pkgFile || packageInfo.filepath
indexKey = pkgFile
}
return {
...packages,
// index by relative path if cwd was specified
[pkgOptions.cwd
? path
.relative(path.resolve(pkgOptions.cwd), indexKey)
// convert Windows path to *nix path for consistency
.replace(/\\/g, '/')
: indexKey]: await runLocal(pkgOptions, pkgData, pkgFile),
}
},
Promise.resolve({} as Index<PackageFile> | PackageFile),
)
if (options.json) {
printJson(options, analysis)
}
} else {
// mutate packageFile when glob pattern finds only single package
if (
selectedPackageInfos.length === 1 &&
selectedPackageInfos[0].filepath !== (options.packageFile || 'package.json')
) {
options.packageFile = selectedPackageInfos[0].filepath
}
const { pkgData, pkgFile } = await findPackage(options)
analysis = await runLocal(options, pkgData, pkgFile)
}
clearTimeout(timeout)
if (options.errorLevel === 2 && someUpgraded(packageFilepaths, analysis, options)) {
programError(options, '\nDependencies not up-to-date')
}
// suggest install command or auto-install
if (options.upgrade) {
// deno does not have an install command
// The closest equivalent is deno cache, but it is optional.
// See: https://deno.land/manual@v1.30.3/references/cheatsheet#nodejs---deno-cheatsheet
if (options.packageManager === 'deno') {
print(options, '')
} else {
await install(packageFilepaths, analysis, options)
}
}
return analysis
}
/** Main entry point.
*
* @returns Promise<
* PackageFile Default returns upgraded package file.
* | Index<VersionSpec> --jsonUpgraded returns only upgraded dependencies.
* | void --global upgrade returns void.
* >
*/
export async function run(
runOptions: RunOptions = {},
{ cli }: { cli?: boolean } = {},
): Promise<PackageFile | Index<VersionSpec> | void> {
const options = await initOptions(runOptions, { cli })
// ensure that the process exits with an error code if there was an unhandled rejection
const bugsUrl = pkg.bugs.url
process.on('exit', () => {
if (unhandledRejectionError) {
programError(options, `Unhandled Rejection! This is a bug and should be reported: ${bugsUrl}`)
}
})
// chalk may already have been initialized in cli.ts, but when imported as a module
// chalkInit is idempotent
await chalkInit(options.color)
noVolta(options)
print(options, 'Initializing', 'verbose')
if (options.cacheClear) {
await cacheClear(options)
}
let timeout: NodeJS.Timeout | undefined
let timeoutPromise: Promise<void> = new Promise(() => null)
if (options.timeout) {
const timeoutMs = typeof options.timeout === 'string' ? Number.parseInt(options.timeout, 10) : options.timeout
timeoutPromise = new Promise((resolve, reject) => {
timeout = setTimeout(() => {
// must catch the error and reject explicitly since we are in a setTimeout
const error = `Exceeded global timeout of ${timeoutMs}ms`
reject(error)
try {
programError(options, error)
} catch (e) {
/* noop */
}
}, timeoutMs)
})
}
// doctor mode
if (options.doctor) {
// execute with -u
if (options.upgrade) {
// we have to pass run directly since it would be a circular require if doctor included this file
return Promise.race([timeoutPromise, doctor(run, options)])
}
// print help otherwise
else {
const help =
typeof cliOptionsMap.doctor.help === 'function' ? cliOptionsMap.doctor.help({}) : cliOptionsMap.doctor.help
print(options, `Usage: ncu --doctor\n\n${help}`, 'warn')
}
}
// normal mode
else {
return Promise.race([timeoutPromise, runUpgrades(options, timeout)])
}
}
export default run
export type { RunOptions }
================================================
FILE: src/lib/cache.ts
================================================
import fs from 'fs'
import os from 'os'
import path from 'path'
import { CacheData, Cacher } from '../types/Cacher'
import { Index } from '../types/IndexType'
import { Options } from '../types/Options'
import { Version } from '../types/Version'
import { print } from './logging'
export const CACHE_DELIMITER = '___'
/**
* Check if cache is expired if timestamp is set
*
* @param cacheData
* @param cacheExpiration
* @returns
*/
function checkCacheExpiration(cacheData: CacheData, cacheExpiration = 10) {
if (typeof cacheData.timestamp !== 'number') {
return false
}
const unixMinuteMS = 60 * 1000
const expirationLimit = cacheData.timestamp + cacheExpiration * unixMinuteMS
return expirationLimit < Date.now()
}
export const defaultCacheFilename = '.ncu-cache.json'
export const defaultCacheFile = `~/${defaultCacheFilename}`
export const resolvedDefaultCacheFile = path.join(os.homedir(), defaultCacheFilename)
/** Resolve the cache file path based on os/homedir. */
export function resolveCacheFile(optionsCacheFile: string) {
return optionsCacheFile === defaultCacheFile ? resolvedDefaultCacheFile : optionsCacheFile
}
/** Clear the default cache, or the cache file specified by --cacheFile. */
export async function cacheClear(options: Options) {
if (!options.cacheFile) {
return
}
await fs.promises.rm(resolveCacheFile(options.cacheFile), { force: true })
}
/**
* The cacher stores key (name + target) - value (new version) pairs
* for quick updates across `ncu` calls.
*
* @returns
*/
export default async function cacher(options: Omit<Options, 'cacher'>): Promise<Cacher | undefined> {
if (!options.cache || !options.cacheFile) {
return
}
const cacheFile = resolveCacheFile(options.cacheFile)
let cacheData: CacheData = {}
const cacheHits = new Set<string>()
try {
cacheData = JSON.parse(await fs.promises.readFile(cacheFile, 'utf-8'))
const expired = checkCacheExpiration(cacheData, options.cacheExpiration)
if (expired) {
// reset cache
fs.promises.rm(cacheFile, { force: true })
cacheData = {}
}
} catch (error) {
// ignore file read/parse/remove errors
}
if (typeof cacheData.timestamp !== 'number') {
cacheData.timestamp = Date.now()
}
if (!cacheData.packages) {
cacheData.packages = {}
}
if (!cacheData.peers) {
cacheData.peers = {}
}
return {
get: (name: string, target: string) => {
if (!cacheData.packages) return
const key = `${name}${CACHE_DELIMITER}${target}`
const cached = cacheData.packages[key]
if (cached && !key.includes(cached)) {
cacheHits.add(name)
}
return cached
},
set: (name: string, target: string, version: string) => {
if (!cacheData.packages) return
const key = `${name}${CACHE_DELIMITER}${target}`
cacheData.packages[key] = version
},
getPeers: (name: string, version: Version) => {
if (!cacheData.peers) return
const key = `${name}${CACHE_DELIMITER}${version}`
const cached = cacheData.peers[key]
if (cached) {
cacheHits.add(name)
}
return cached
},
setPeers: (name: string, version: Version, peers: Index<string>) => {
const key = `${name}${CACHE_DELIMITER}${version}`
if (!cacheData.peers) return
cacheData.peers[key] = peers
},
save: async () => {
await fs.promises.writeFile(cacheFile, JSON.stringify(cacheData))
},
log: (peers?: boolean) => {
const cacheCount = cacheHits.size
if (cacheCount === 0) return
print(
options,
`\nUsing ${cacheCount} cached package ${peers ? 'peer' : 'version'}${cacheCount > 1 ? 's' : ''}`,
'warn',
)
print(options, cacheHits, 'verbose')
cacheHits.clear()
},
} satisfies Cacher
}
================================================
FILE: src/lib/chalk.ts
================================================
/*
This chalk wrapper allows synchronous chalk.COLOR(...) syntax with special support for:
1) dynamic import as pure ESM module
2) force color on all instances
3) disable color on all instances
Call await chalkInit(color) at the beginning of execution and the chalk instance will be available everywhere.
It is a hacky solution, but it is the easiest way to import and pass the color option to all chalk instances without brutalizing the syntax.
*/
import keyValueBy from './keyValueBy'
type ChalkMethod = ((s: any) => string) & { bold: (s: any) => string }
const chalkMethods = {
blue: true,
bold: true,
cyan: true,
gray: true,
green: true,
magenta: true,
red: true,
yellow: true,
}
// A chalk instance that passes strings through as-is, without color. Used with color: null. */
const chalkNoop = keyValueBy(chalkMethods, name => ({ [name]: (s: any) => s.toString() })) as Record<
keyof typeof chalkMethods,
ChalkMethod
>
// a global Promise of a chalk instance that can optionally force or ignore color
let chalkInstance: Record<keyof typeof chalkMethods, ChalkMethod>
/** Initializes the global chalk instance with an optional flag for forced color. Idempotent. */
export const chalkInit = async (color?: boolean | null) => {
const chalkModule = await import('chalk')
const { default: chalkDefault, Chalk } = chalkModule
chalkInstance = color === true ? new Chalk({ level: 1 }) : color === null ? chalkNoop : chalkDefault
}
/** Asserts that chalk has been imported. */
const assertChalk = () => {
if (!chalkInstance) {
throw new Error(
`Chalk has not been imported yet. Chalk is a dynamic import and requires that you await { chalkInit } from './lib/chalk'.`,
)
}
}
// generate an async method for each chalk method that calls a chalk instance with global.color for forced color
const chalkGlobal = keyValueBy(chalkMethods, name => {
/** Chained bold method. */
const bold = (s: any) => {
assertChalk()
return chalkInstance[name as keyof typeof chalkInstance].bold(s)
}
/** Chalk method. */
const method = (s: any) => {
assertChalk()
return chalkInstance[name as keyof typeof chalkInstance](s)
}
method.bold = bold
return {
[name]: method,
}
}) as Record<keyof typeof chalkMethods, ChalkMethod>
export default chalkGlobal
================================================
FILE: src/lib/defineConfig.ts
================================================
import type { RcOptions } from '../types/RcOptions'
/**
* TypeScript helper for .npmrc config file. Similar to vite and eslint's
* defineConfig helper
*/
function defineConfig(config: RcOptions) {
return config
}
export default defineConfig
================================================
FILE: src/lib/determinePackageManager.ts
================================================
import fs from 'fs/promises'
import { Index } from '../types/IndexType'
import { Options } from '../types/Options'
import { PackageManagerName } from '../types/PackageManagerName'
import findLockfile from './findLockfile'
// map lockfiles to package managers
const packageManagerLockfileMap: Index<PackageManagerName> = {
'package-lock': 'npm',
yarn: 'yarn',
'pnpm-lock': 'pnpm',
deno: 'deno',
bun: 'bun',
}
/**
* Get the package manager being used to run the command.
* When checking global packages, we need to do it this way since there is no
* lockfile in the global directory.
*/
const getRunningPackageManager = (): PackageManagerName => {
const userAgent = process.env.npm_config_user_agent ?? ''
const execpath = process.env.npm_execpath ?? ''
if (
userAgent.startsWith('yarn/') ||
execpath.includes('yarn') ||
__dirname.includes('/yarn/') ||
__dirname.includes('\\Yarn\\')
)
return 'yarn'
if (
userAgent.startsWith('pnpm/') ||
execpath.includes('pnpm') ||
__dirname.includes('/pnpm/') ||
__dirname.includes('\\pnpm\\')
)
return 'pnpm'
if (
userAgent.startsWith('bun/') ||
typeof Bun !== 'undefined' ||
process.versions.bun ||
__dirname.includes('/.bun/') ||
__dirname.includes('\\.bun\\')
)
return 'bun'
return 'npm'
}
/**
* If the packageManager option was not provided, look at the lockfiles to
* determine which package manager is being used.
*/
const determinePackageManager = async (
options: Options,
// only for testing
readdir: (_path: string) => Promise<string[]> = fs.readdir,
): Promise<PackageManagerName> => {
if (options.packageManager) return options.packageManager
else if (options.global) return getRunningPackageManager()
const lockfileName = (await findLockfile(options, readdir))?.filename
return lockfileName ? packageManagerLockfileMap[lockfileName.split('.')[0]] : 'npm'
}
export default determinePackageManager
================================================
FILE: src/lib/doctor.ts
================================================
import fs from 'fs/promises'
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'
import { Index } from '../types/IndexType'
import { Options } from '../types/Options'
import { PackageFile } from '../types/PackageFile'
import { PackageInfo } from '../types/PackageInfo'
import { SpawnOptions } from '../types/SpawnOptions'
import { SpawnPleaseOptions } from '../types/SpawnPleaseOptions'
import { VersionSpec } from '../types/VersionSpec'
import chalk, { chalkInit } from './chalk'
import loadPackageInfoFromFile from './loadPackageInfoFromFile'
import upgradePackageData from './upgradePackageData'
type Run = (options?: Options) => Promise<PackageFile | Index<VersionSpec> | void>
/** Run npm, yarn, pnpm, or bun. */
const npm = (
args: string[],
options: Options,
print?: boolean,
{ spawnOptions, spawnPleaseOptions }: { spawnOptions?: SpawnOptions; spawnPleaseOptions?: SpawnPleaseOptions } = {},
): Promise<string> => {
if (print) {
console.log(chalk.blue([options.packageManager, ...args].join(' ')))
}
const spawnOptionsMerged = {
cwd: options.cwd || process.cwd(),
env: {
...process.env,
// TODO: Why does CI break pnpm install?
...(options.packageManager !== 'pnpm' ? { CI: '1' } : null),
FORCE_COLOR: '1',
...spawnOptions?.env,
},
...spawnOptions,
}
const npmOptions = {
...(options.global ? { global: true } : null),
...(options.prefix ? { prefix: options.prefix } : null),
}
return (
options.packageManager === 'pnpm'
? spawnPnpm
: options.packageManager === 'yarn'
? spawnYarn
: options.packageManager === 'bun'
? spawnBun
: spawnNpm
)(args, npmOptions, spawnPleaseOptions, spawnOptionsMerged)
}
/** Load and validate package file and tests. */
const loadPackageFileForDoctor = async (options: Options): Promise<PackageInfo> => {
// assert no --packageData or --packageFile
if (options.packageData || options.packageFile) {
console.error(
'--packageData and --packageFile are not allowed with --doctor. You must execute "ncu --doctor" in a directory with a package file so it can install dependencies and test them.',
)
process.exit(1)
}
let packageInfo: PackageInfo
// assert package.json
try {
packageInfo = await loadPackageInfoFromFile(options, 'package.json')
} catch (e) {
console.error('Missing or invalid package.json')
process.exit(1)
}
// assert npm script "test" (unless a custom test script is specified)
if (!options.doctorTest && !packageInfo.pkg.scripts?.test) {
console.error(
'No npm "test" script defined. You must define a "test" script in the "scripts" section of your package.json to use --doctor.',
)
process.exit(1)
}
return packageInfo
}
/** Iteratively installs upgrades and runs tests to identify breaking upgrades. */
// we have to pass run directly since it would be a circular require if doctor included this file
const doctor = async (run: Run, options: Options): Promise<void> => {
await chalkInit()
// bun lockFileName defaults to bun.lock but will be overwritten to bun.lockb if detected at the readFile step below
let lockFileName: 'package-lock.json' | 'yarn.lock' | 'pnpm-lock.yaml' | 'bun.lock' | 'bun.lockb' =
options.packageManager === 'yarn'
? 'yarn.lock'
: options.packageManager === 'pnpm'
? 'pnpm-lock.yaml'
: options.packageManager === 'bun'
? 'bun.lock'
: 'package-lock.json'
const { pkg, pkgFile }: PackageInfo = await loadPackageFileForDoctor(options)
// flatten all deps into one so we can iterate over them
const allDependencies: Index<VersionSpec> = {
...pkg.dependencies,
...pkg.devDependencies,
...pkg.optionalDependencies,
}
/** Install dependencies using "npm run install" or a custom script given by --doctorInstall. */
const runInstall = async (): Promise<void> => {
if (options.doctorInstall) {
const [installCommand, ...testArgs] = options.doctorInstall.split(' ')
console.log(chalk.blue(options.doctorInstall))
await spawn(installCommand, testArgs)
} else {
await npm(['install'], { packageManager: options.packageManager }, true)
}
}
/** Run the tests using "npm run test" or a custom script given by --doctorTest. */
const runTests = async (): Promise<void> => {
const spawnPleaseOptions = {
stderr: (data: string): void => {
console.error(chalk.red(data.toString()))
},
// Test runners typically write to stdout, so we need to print stdout.
// Otherwise test failures will be silenced.
stdout: (data: string): void => {
process.stdout.write(data.toString())
},
}
if (options.doctorTest) {
const regexp = /"(.+?)"|'(.+?)'|[^ ]+/g
const matches = options.doctorTest.matchAll(regexp)
let groups: string[] = []
for (const match of matches) {
groups = [...groups, match[2] || match[1] || match[0]]
}
const [testCommand, ...testArgs] = groups
console.log(chalk.blue(options.doctorTest))
await spawn(testCommand, testArgs, spawnPleaseOptions)
} else {
await npm(
['run', 'test'],
{
packageManager: options.packageManager,
},
true,
{ spawnPleaseOptions },
)
}
}
console.log(`Running tests before upgrading`)
// initial install
await runInstall()
// save lock file if there is one
let lockFile = ''
try {
lockFile = await fs.readFile(lockFileName, 'utf-8')
} catch (e) {
// try bun.lockb if bun.lock was not found
// set lockFileName so the rest of doctor mode uses bun.lockb for lock file updating and restoration
if (options.packageManager === 'bun') {
lockFileName = 'bun.lockb'
try {
lockFile = await fs.readFile(lockFileName, 'utf-8')
} catch (e) {}
}
}
// make sure current tests pass before we begin
try {
await runTests()
} catch (e) {
console.error('Tests failed before we even got started!')
process.exit(1)
}
if (!options.interactive) {
console.log(`Upgrading all dependencies and re-running tests`)
}
// upgrade all dependencies
// save upgrades for later in case we need to iterate
console.log(
chalk.blue(
'ncu ' +
process.argv
.slice(2)
.filter(arg => arg !== '--doctor')
.join(' '),
),
)
process.env.NCU_DOCTOR = '1'
const upgrades: Index<VersionSpec> = (await run({
...options,
silent: true,
// --doctor triggers the initial call to doctor, but the internal call needs to executes npm-check-updates normally in order to upgrade the dependencies
doctor: false,
})) as Index<VersionSpec>
if (Object.keys(upgrades || {}).length === 0) {
console.log('All dependencies are up-to-date ' + chalk.green.bold(':)'))
return
}
// track if installing dependencies was successful
// this allows us to skip re-installing when it fails and proceed straight to installing individual dependencies
let installAllSuccess = false
// run tests on all upgrades
try {
// install after all upgrades
await runInstall()
installAllSuccess = true
// run tests after all upgrades
await runTests()
console.log(`${chalk.green('✓')} Tests pass`)
await printUpgrades(options, {
current: allDependencies,
upgraded: upgrades,
total: Object.keys(upgrades || {}).length,
})
console.log(`\n${options.interactive ? 'Chosen' : 'All'} dependencies upgraded and installed ${chalk.green(':)')}`)
} catch {
console.error(chalk.red(installAllSuccess ? 'Tests failed' : 'Install failed'))
console.log(`Identifying broken dependencies`)
// restore package file, lockFile and re-install
await fs.writeFile('package.json', pkgFile)
if (lockFile) {
await fs.writeFile(lockFileName, lockFile)
} else {
await fs.rm(lockFileName, { recursive: true, force: true })
}
// save the last package file with passing tests
let lastPkgFile = pkgFile
// re-install after restoring package file and lock file
// only re-install if the tests failed, not if npm install failed
if (installAllSuccess) {
try {
await runInstall()
} catch (e) {
const installCommand = (options.packageManager || 'npm') + ' install'
throw new Error(
`Error: Doctor mode was about to test individual upgrades, but ${chalk.cyan(
installCommand,
)} failed after rolling back to your existing package and lock files. This is unexpected since the initial install before any upgrades succeeded. Either npm failed to revert a partial install, or failed anomalously on the second run. Please check your internet connection and retry. If doctor mode fails consistently, report a bug with your complete list of dependency versions at https://github.com/raineorshine/npm-check-updates/issues.`,
)
}
}
// iterate upgrades
let name: string, version: VersionSpec
for ([name, version] of Object.entries(upgrades)) {
try {
// install single dependency
await npm(
[
...(options.packageManager === 'yarn' ||
options.packageManager === 'pnpm' ||
options.packageManager === 'bun'
? ['add']
: ['install', '--no-save']),
`${name}@${version}`,
],
{ packageManager: options.packageManager },
true,
)
// if there is a prepare script, we need to run it manually since --no-save does not run prepare automatically
// https://github.com/raineorshine/npm-check-updates/issues/1170
if (pkg.scripts?.prepare) {
try {
await npm(['run', 'prepare'], { packageManager: options.packageManager }, true)
} catch (e) {
console.error(chalk.red('Prepare script failed'))
throw e
}
}
// run tests after individual upgrade
await runTests()
console.log(` ${chalk.green('✓')} ${name} ${allDependencies[name]} → ${version}`)
// save upgraded package data so that passing versions can still be saved even when there is a failure
lastPkgFile = await upgradePackageData(
lastPkgFile,
{ [name]: allDependencies[name] },
{ [name]: version },
options,
)
// save working lock file
lockFile = await fs.readFile(lockFileName, 'utf-8')
} catch (e) {
// print failing package
console.error(` ${chalk.red('✗')} ${name} ${allDependencies[name]} → ${version}\n`)
console.error(chalk.red(e))
// restore last good lock file
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' ||
options.packageManager === 'bun'
) {
await fs.writeFile('package.json', lastPkgFile)
}
}
}
// silently restore last passing package file and lock file
// only print message if package file is updated
if (lastPkgFile !== pkgFile) {
console.log('Saving partially upgraded package.json')
await fs.writeFile('package.json', lastPkgFile)
}
// re-install from restored package.json and lockfile
await runInstall()
}
}
export default doctor
================================================
FILE: src/lib/exists.ts
================================================
import fs from 'fs/promises'
/** Returns true if a file exists. */
const exists = (path: string) =>
fs.stat(path).then(
() => true,
() => false,
)
export default exists
================================================
FILE: src/lib/figgy-pudding/LICENSE.md
================================================
ISC License
Copyright (c) npm, Inc.
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE COPYRIGHT HOLDER DISCLAIMS
ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
USE OR PERFORMANCE OF THIS SOFTWARE.
================================================
FILE: src/lib/figgy-pudding/README.md
================================================
# Note: pending imminent deprecation
**This module will be deprecated once npm v7 is released. Please do not rely
on it more than absolutely necessary (ie, only if you are depending on
it for use with npm v6 internal dependencies).**
---
## figgy-pudding [](https://npm.im/figgy-pudding) [](https://npm.im/figgy-pudding) [](https://travis-ci.org/npm/figgy-pudding) [](https://coveralls.io/github/npm/figgy-pudding?branch=latest)
[`figgy-pudding`](https://github.com/npm/figgy-pudding) is a small JavaScript
library for managing and composing cascading options objects -- hiding what
needs to be hidden from each layer, without having to do a lot of manual munging
and passing of options.
### The God Object is Dead
### Now Bring Us Some Figgy Pudding
## Install
`$ npm install figgy-pudding`
### Example
```javascript
// print-package.js
const fetch = require('./fetch.js')
const puddin = require('figgy-pudding')
const PrintOpts = puddin({
json: { default: false },
})
async function printPkg(name, opts) {
// Expected pattern is to call this in every interface function. If `opts` is
// not passed in, it will automatically create an (empty) object for it.
opts = PrintOpts(opts)
const uri = `https://registry.npmjs.com/${name}`
const res = await fetch(
uri,
opts.concat({
// Add or override any passed-in configs and pass them down.
log: customLogger,
}),
)
// The following would throw an error, because it's not in PrintOpts:
// console.log(opts.log)
if (opts.json) {
return res.json()
} else {
return res.text()
}
}
console.log(
await printPkg('figgy', {
// Pass in *all* configs at the toplevel, as a regular object.
json: true,
cache: './tmp-cache',
}),
)
```
```javascript
// fetch.js
const puddin = require('figgy-pudding')
const FetchOpts = puddin({
log: { default: require('npmlog') },
cache: {}
})
module.exports = async function (..., opts) {
opts = FetchOpts(opts)
}
```
### Features
- hide options from layer that didn't ask for it
- shared multi-layer options
- make sure `opts` argument is available
- transparent key access like normal keys, through a Proxy. No need for`.get()`!
- default values
- key aliases
- arbitrary key filter functions
- key/value iteration
- serialization
- 100% test coverage using `tap --100`
### API
#### `figgyPudding({ key: { default: val } | String }, [opts]) -> PuddingFactory`
Defines an Options constructor that can be used to collect only the needed
options.
An optional `default` property for specs can be used to specify default values
if nothing was passed in.
If the value for a spec is a string, it will be treated as an alias to that
other key.
##### Example
```javascript
const MyAppOpts = figgyPudding({
lg: 'log',
log: {
default: () => require('npmlog'),
},
cache: {},
})
```
#### `> PuddingFactory(...providers) -> FiggyPudding{}`
Instantiates an options object defined by `figgyPudding()`, which uses
`providers`, in order, to find requested properties.
Each provider can be either a plain object, a `Map`-like object (that is, one
with a `.get()` method) or another figgyPudding `Opts` object.
When nesting `Opts` objects, their properties will not become available to the
new object, but any further nested `Opts` that reference that property _will_ be
able to read from their grandparent, as long as they define that key. Default
values for nested `Opts` parents will be used, if found.
##### Example
```javascript
const ReqOpts = figgyPudding({
follow: {},
})
const opts = ReqOpts({
follow: true,
log: require('npmlog'),
})
opts.follow // => true
opts.log // => Error: ReqOpts does not define `log`
const MoreOpts = figgyPudding({
log: {},
})
MoreOpts(opts).log // => npmlog object (passed in from original plain obj)
MoreOpts(opts).follow // => Error: MoreOpts does not define `follow`
```
#### `> opts.get(key) -> Value`
Gets a value from the options object.
##### Example
```js
const opts = MyOpts(config)
opts.get('foo') // value of `foo`
opts.foo // Proxy-based access through `.get()`
```
#### `> opts.concat(...moreProviders) -> FiggyPudding{}`
Creates a new opts object of the same type as `opts` with additional providers.
Providers further to the right shadow providers to the left, with properties in
the original `opts` being shadows by the new providers.
##### Example
```js
const opts = MyOpts({ x: 1 })
opts.get('x') // 1
opts.concat({ x: 2 }).get('x') // 2
opts.get('x') // 1 (original opts object left intact)
```
#### `> opts.toJSON() -> Value`
Converts `opts` to a plain, JSON-stringifiable JavaScript value. Used internally
by JavaScript to get `JSON.stringify()` working.
Only keys that are readable by the current pudding type will be serialized.
##### Example
```js
const opts = MyOpts({ x: 1 })
opts.toJSON() // {x: 1}
JSON.stringify(opts) // '{"x":1}'
```
#### `> opts.forEach((value, key, opts) => {}, thisArg) -> undefined`
Iterates over the values of `opts`, limited to the keys readable by the current
pudding type. `thisArg` will be used to set the `this` argument when calling the
`fn`.
##### Example
```js
const opts = MyOpts({ x: 1, y: 2 })
opts.forEach((value, key) => console.log(key, '=', value))
```
#### `> opts.entries() -> Iterator<[[key, value], ...]>`
Returns an iterator that iterates over the keys and values in `opts`, limited to
the keys readable by the current pudding type. Each iteration returns an array
of `[key, value]`.
##### Example
```js
const opts = MyOpts({x: 1, y: 2})
[...opts({x: 1, y: 2}).entries()] // [['x', 1], ['y', 2]]
```
#### `> opts[Symbol.iterator]() -> Iterator<[[key, value], ...]>`
Returns an iterator that iterates over the keys and values in `opts`, limited to
the keys readable by the current pudding type. Each iteration returns an array
of `[key, value]`. Makes puddings work natively with JS iteration mechanisms.
##### Example
```js
const opts = MyOpts({x: 1, y: 2})
[...opts({x: 1, y: 2})] // [['x', 1], ['y', 2]]
for (let [key, value] of opts({x: 1, y: 2})) {
console.log(key, '=', value)
}
```
#### `> opts.keys() -> Iterator<[key, ...]>`
Returns an iterator that iterates over the keys in `opts`, limited to the keys
readable by the current pudding type.
##### Example
```js
const opts = MyOpts({x: 1, y: 2})
[...opts({x: 1, y: 2}).keys()] // ['x', 'y']
```
#### `> opts.values() -> Iterator<[value, ...]>`
Returns an iterator that iterates over the values in `opts`, limited to the keys
readable by the current pudding type.
##### Example
```js
const opts = MyOpts({x: 1, y: 2})
[...opts({x: 1, y: 2}).values()] // [1, 2]
```
================================================
FILE: src/lib/figgy-pudding/index.js
================================================
/* eslint-disable */
/*
This is stripped down version of the deprecated figgy-pudding. It is used by libnpmconfig, which is also deprecated and has been brought into the codebase to avoid deprecation warnings.
https://github.com/npm/figgy-pudding
*/
class FiggyPudding {
constructor(specs, opts, providers) {
this.__specs = specs || {}
this.__opts = opts || {}
this.__providers = reverse(providers.filter(x => x != null && typeof x === 'object'))
this.__isFiggyPudding = true
}
get(key) {
return pudGet(this, key, true)
}
toJSON() {
const obj = {}
this.forEach((val, key) => {
obj[key] = val
})
return obj
}
forEach(fn, thisArg = this) {
for (let [key, value] of this.entries()) {
fn.call(thisArg, value, key, this)
}
}
*entries(_matcher) {
for (let key of Object.keys(this.__specs)) {
yield [key, this.get(key)]
}
const matcher = _matcher || this.__opts.other
if (matcher) {
const seen = new Set()
for (let p of this.__providers) {
const iter = p.entries ? p.entries(matcher) : entries(p)
for (let [key, val] of iter) {
if (matcher(key) && !seen.has(key)) {
seen.add(key)
yield [key, val]
}
}
}
}
}
concat(...moreConfig) {
return new Proxy(
new FiggyPudding(this.__specs, this.__opts, reverse(this.__providers).concat(moreConfig)),
proxyHandler,
)
}
}
function pudGet(pud, key, validate) {
let spec = pud.__specs[key]
if (!spec) {
spec = {}
}
let ret
for (let p of pud.__providers) {
ret = tryGet(key, p)
if (ret !== undefined) {
break
}
}
if (ret === undefined && spec.default !== undefined) {
if (typeof spec.default === 'function') {
return spec.default(pud)
} else {
return spec.default
}
} else {
return ret
}
}
function tryGet(key, p) {
let ret
if (p.__isFiggyPudding) {
ret = pudGet(p, key, false)
} else {
ret = p[key]
}
return ret
}
const proxyHandler = {
get(obj, prop) {
if (typeof prop === 'symbol' || prop.slice(0, 2) === '__' || prop in FiggyPudding.prototype) {
return obj[prop]
}
return obj.get(prop)
},
}
export default function figgyPudding(specs, opts) {
function factory(...providers) {
return new Proxy(new FiggyPudding(specs, opts, providers), proxyHandler)
}
return factory
}
function reverse(arr) {
const ret = []
arr.forEach(x => ret.unshift(x))
return ret
}
function entries(obj) {
return Object.keys(obj).map(k => [k, obj[k]])
}
================================================
FILE: src/lib/filterAndReject.ts
================================================
import { and, or } from 'fp-and-or'
import identity from 'lodash/identity'
import picomatch from 'picomatch'
import { parseRange } from 'semver-utils'
import { FilterPattern } from '../types/FilterPattern'
import { Maybe } from '../types/Maybe'
import { VersionSpec } from '../types/VersionSpec'
/**
* Creates a filter function from a given filter string.
* Supports strings, wildcards, comma-or-space-delimited lists, and regexes.
* The filter function *may* throw an exception if the filter pattern is invalid.
*
* @param [filterPattern]
* @returns
*/
function composeFilter(filterPattern: FilterPattern): (name: string, versionSpec?: string) => boolean {
let predicate: (name: string, versionSpec?: string) => boolean
// no filter
if (!filterPattern) {
predicate = identity
}
// string
else if (typeof filterPattern === 'string') {
// RegExp string
if (filterPattern[0] === '/' && filterPattern.at(-1) === '/') {
const regexp = new RegExp(filterPattern.slice(1, -1))
predicate = (dependencyName: string) => regexp.test(dependencyName)
}
// glob string
else {
const patterns = filterPattern.split(/[\s,]+/)
predicate = (dependencyName: string) => {
/** Returns true if the pattern matches an unscoped dependency name. */
const matchUnscoped = (pattern: string) => picomatch(pattern)(dependencyName)
/** Returns true if the pattern matches a scoped dependency name. */
const matchScoped = (pattern: string) =>
!pattern.includes('/') &&
dependencyName.includes('/') &&
picomatch(pattern)(dependencyName.replace(/\//g, '_'))
// return true if any of the provided patterns match the dependency name
return patterns.some(or(matchUnscoped, matchScoped))
}
}
}
// array
else if (Array.isArray(filterPattern)) {
predicate = (dependencyName: string, versionSpec?: string) =>
filterPattern.some(subpattern => composeFilter(subpattern)(dependencyName, versionSpec))
}
// raw RegExp
else if (filterPattern instanceof RegExp) {
predicate = (dependencyName: string) => filterPattern.test(dependencyName)
}
// function
else if (typeof filterPattern === 'function') {
predicate = (dependencyName: string, versionSpec?: string) =>
filterPattern(dependencyName, parseRange((versionSpec as string) ?? dependencyName))
} else {
throw new TypeError('Invalid filter. Must be a RegExp, array, or comma-or-space-delimited list.')
}
// limit the arity to 1 to avoid passing the value
return predicate
}
/**
* Composes a filter function from filter, reject, filterVersion, and rejectVersion patterns. The filter function *may* throw an exception if the filter pattern is invalid.
*
* @param [filter]
* @param [reject]
* @param [filterVersion]
* @param [rejectVersion]
*/
function filterAndReject(
filter: Maybe<FilterPattern>,
reject: Maybe<FilterPattern>,
filterVersion: Maybe<FilterPattern>,
rejectVersion: Maybe<FilterPattern>,
) {
return and(
// filter dep
(dependencyName: VersionSpec, version: string) =>
and(filter ? composeFilter(filter) : true, reject ? (...args) => !composeFilter(reject)(...args) : true)(
dependencyName,
version,
),
// filter version
(dependencyName: VersionSpec, version: string) =>
and(
filterVersion ? composeFilter(filterVersion) : true,
rejectVersion ? (...args) => !composeFilter(rejectVersion)(...args) : true,
)(version),
)
}
export default filterAndReject
================================================
FILE: src/lib/filterObject.ts
================================================
import { Index } from '../types/IndexType'
import keyValueBy from './keyValueBy'
/** Filters an object by a predicate. Does not catch exceptions thrown by the predicate. */
const filterObject = <T>(obj: Index<T>, predicate: (key: string, value: T) => boolean) =>
keyValueBy(obj, (key, value) => (predicate(key, value) ? { [key]: value } : null))
export default filterObject
================================================
FILE: src/lib/findLockfile.ts
================================================
import fs from 'fs/promises'
import os from 'os'
import path from 'path'
import { Options } from '../types/Options'
const lockFileNames = [
'package-lock.json',
'yarn.lock',
'pnpm-lock.yaml',
'deno.json',
'deno.jsonc',
'bun.lock',
'bun.lockb',
]
/**
* Goes up the filesystem tree until it finds a lock file. (e.g. "package-lock.json", "yarn.lock", etc.)
*
* @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
* filename of the lockfile.
*/
export default async function findLockfile(
options: Pick<Options, 'cwd' | 'packageFile'>,
readdir: (_path: string) => Promise<string[]> = fs.readdir,
): Promise<{ directoryPath: string; filename: string } | null> {
try {
// Get boundaries to stop searching.
const homeDir = os.homedir()
const tempDir = os.tmpdir()
// 1. explicit cwd
// 2. same directory as package file
// 3. current directory
let currentPath = options.cwd ? options.cwd : options.packageFile ? path.dirname(options.packageFile) : '.'
currentPath = path.resolve(currentPath)
while (true) {
const files = await readdir(currentPath)
for (const filename of lockFileNames) {
if (files.includes(filename)) {
return { directoryPath: currentPath, filename }
}
}
const pathParent = path.resolve(currentPath, '..')
if (
// Stop if we have reached the root of the file system.
pathParent === currentPath ||
// Stop if we have reached the root of a user's home directory.
pathParent === homeDir ||
// Stop if we have reached the root of the temporary directory.
pathParent === tempDir
) {
break
}
currentPath = pathParent
}
} catch (e) {
// if readdirSync fails, return null
}
return null
}
================================================
FILE: src/lib/findPackage.ts
================================================
import findUp from 'find-up'
import fs from 'fs/promises'
import { text } from 'node:stream/consumers'
import path from 'path'
import { print } from '../lib/logging'
import { Options } from '../types/Options'
import chalk from './chalk'
import programError from './programError'
/**
* Finds the package file and data.
*
* Searches as follows:
* --packageData flag
* --packageFile flag
* --stdin
* --findUp
*/
async function findPackage(options: Options): Promise<{
pkgData: string | null
pkgFile: string | null
pkgPath: string | null
}> {
let pkgData
let pkgFile = null
const pkgPath = options.packageFile || 'package.json'
/** Reads the contents of a package file. */
function getPackageDataFromFile(pkgFile: string | null | undefined, pkgFileName: string): Promise<string> {
// exit if no pkgFile to read from fs
if (pkgFile != null) {
const relPathToPackage = path.resolve(pkgFile)
print(options, `${options.upgrade ? 'Upgrading' : 'Checking'} ${relPathToPackage}`)
} else {
programError(
options,
`${chalk.red(
`No ${pkgFileName}`,
)}\n\nPlease add a ${pkgFileName} to the current directory, specify the ${chalk.cyan(
'--packageFile',
)} or ${chalk.cyan('--packageData')} options, or pipe a ${pkgFileName} to stdin and specify ${chalk.cyan(
'--stdin',
)}.`,
{ color: false },
)
}
return fs.readFile(pkgFile!, 'utf-8').catch(e => {
programError(options, e)
})
}
print(options, 'Running in local mode', 'verbose')
print(options, 'Finding package file data', 'verbose')
// get the package data from the various input possibilities
if (options.packageData) {
pkgFile = null
pkgData = Promise.resolve(options.packageData)
} else if (options.packageFile) {
pkgFile = options.packageFile
pkgData = getPackageDataFromFile(pkgFile, pkgPath)
} else if (options.stdin) {
print(options, 'Waiting for package data on stdin', 'verbose')
// get data from stdin
// trim stdin to account for \r\n
const stdinData = await text(process.stdin)
const data = stdinData.trim().length > 0 ? stdinData : null
// if no stdin content fall back to searching for package.json from pwd and up to root
pkgFile = data || !pkgPath ? null : await findUp(pkgPath)
pkgData = data || getPackageDataFromFile(await pkgFile, pkgPath)
} else {
// find the closest package starting from the current working directory and going up to the root
pkgFile = pkgPath
? await findUp(
!options.packageFile && options.packageManager === 'deno' ? ['deno.json', 'deno.jsonc'] : pkgPath,
{
cwd: options.cwd || process.cwd(),
},
)
: null
pkgData = getPackageDataFromFile(pkgFile, pkgPath)
}
const pkgDataResolved = await pkgData
return {
pkgData: pkgDataResolved,
pkgFile: pkgFile || null,
pkgPath,
}
}
export default findPackage
================================================
FILE: src/lib/getAllPackages.ts
================================================
import glob, { type Options as GlobOptions } from 'fast-glob'
import fs from 'fs/promises'
import yaml from 'js-yaml'
import path from 'path'
import untildify from 'untildify'
import { Index } from '../types/IndexType'
import { Options } from '../types/Options'
import { PackageFile } from '../types/PackageFile'
import { PackageInfo } from '../types/PackageInfo'
import { VersionSpec } from '../types/VersionSpec'
import findPackage from './findPackage'
import loadPackageInfoFromFile from './loadPackageInfoFromFile'
import programError from './programError'
type PnpmWorkspaces =
| string[]
| { packages: string[]; catalog?: Index<VersionSpec>; catalogs?: Index<Index<VersionSpec>> }
type YarnConfig = { catalog?: Index<VersionSpec>; catalogs?: Index<Index<VersionSpec>> }
const globOptions: GlobOptions = {
ignore: ['**/node_modules/**'],
}
/** Reads, parses, and resolves workspaces from a pnpm-workspace file at the same path as the package file. */
const readPnpmWorkspaces = async (pkgPath: string): Promise<PnpmWorkspaces | null> => {
const pnpmWorkspacesPath = path.join(path.dirname(pkgPath), 'pnpm-workspace.yaml')
let pnpmWorkspaceFile: string
try {
pnpmWorkspaceFile = await fs.readFile(pnpmWorkspacesPath, 'utf-8')
} catch {
return null
}
return yaml.load(pnpmWorkspaceFile) as PnpmWorkspaces
}
/** Reads, parses, and resolves catalog information from the yarn config file at the same path as the package file. */
const readYarnConfig = async (pkgPath: string): Promise<YarnConfig | null> => {
const yarnConfigPath = path.join(path.dirname(pkgPath), '.yarnrc.yml')
let yarnConfig: string
try {
yarnConfig = await fs.readFile(yarnConfigPath, 'utf-8')
} catch {
return null
}
return yaml.load(yarnConfig) as YarnConfig
}
/** Gets catalog dependencies from both pnpm-workspace.yaml and package.json files. */
const readCatalogDependencies = async (options: Options, pkgPath: string): Promise<Index<VersionSpec> | null> => {
const catalogDependencies: Index<VersionSpec> = {}
// Read from pnpm-workspace.yaml if the package manager is pnpm
if (options.packageManager === 'pnpm') {
const pnpmWorkspaces = await readPnpmWorkspaces(pkgPath)
if (pnpmWorkspaces && !Array.isArray(pnpmWorkspaces)) {
// Handle both singular 'catalog' and plural 'catalogs'
if (pnpmWorkspaces.catalog) {
Object.assign(catalogDependencies, pnpmWorkspaces.catalog)
}
if (pnpmWorkspaces.catalogs) {
Object.assign(catalogDependencies, ...Object.values(pnpmWorkspaces.catalogs))
}
}
}
if (options.packageManager === 'yarn') {
const yarnConfig = await readYarnConfig(pkgPath)
if (yarnConfig) {
if (yarnConfig.catalog) {
Object.assign(catalogDependencies, yarnConfig.catalog)
}
if (yarnConfig.catalogs) {
Object.assign(catalogDependencies, ...Object.values(yarnConfig.catalogs))
}
}
}
// Read from package.json (for Bun and modern pnpm)
const packageData: PackageFile & {
catalog?: Index<VersionSpec>
catalogs?: Index<Index<VersionSpec>>
workspaces?: string[] | { packages: string[]; catalog?: Index<VersionSpec>; catalogs?: Index<Index<VersionSpec>> }
} = JSON.parse(await fs.readFile(pkgPath, 'utf-8'))
Object.assign(catalogDependencies, packageData.catalog, ...Object.values(packageData.catalogs ?? {}))
// Workspaces catalogs (Bun format)
if (packageData.workspaces && !Array.isArray(packageData.workspaces)) {
Object.assign(
catalogDependencies,
packageData.workspaces.catalog,
...Object.values(packageData.workspaces.catalogs ?? {}),
)
}
return Object.keys(catalogDependencies).length > 0 ? catalogDependencies : null
}
/**
* Gets all workspace packages information.
*
* @param options the application options, used to determine which packages to return.
* @param defaultPackageFilename the default package filename
* @returns a list of PackageInfo objects, one for each workspace file
*/
async function getWorkspacePackageInfos(
options: Options,
defaultPackageFilename: string,
rootPackageFile: string,
cwd: string,
): Promise<[PackageInfo[], string[]]> {
// use silent; otherwise, there will be a duplicate "Checking" message
const { pkgData, pkgPath } = await findPackage({ ...options, packageFile: rootPackageFile, loglevel: 'silent' })
const rootPkg: PackageFile = typeof pkgData === 'string' ? JSON.parse(pkgData) : pkgData
const workspacesObject = rootPkg.workspaces || (await readPnpmWorkspaces(pkgPath || ''))
const workspaces = Array.isArray(workspacesObject) ? workspacesObject : workspacesObject?.packages
if (!workspaces) {
programError(
options,
`workspaces property missing from package.json. --workspace${
options.workspaces ? 's' : ''
} only works when you specify a "workspaces" property in your package.json.`,
)
}
// build a glob from the workspaces
// FIXME: the following workspaces check is redundant
const workspacePackageGlob: string[] = (workspaces || []).map(workspace =>
path
.join(cwd, workspace, 'package.json')
// convert Windows path to *nix path for globby
.replace(/\\/g, '/'),
)
// e.g. [packages/a/package.json, ...]
const allWorkspacePackageFilepaths: string[] = glob.sync(workspacePackageGlob, globOptions)
// Get the package names from the package files.
// If a package does not have a name, use the folder name.
// These will be used to filter out local workspace packages so they are not fetched from the registry.
const allWorkspacePackageInfos: PackageInfo[] = await Promise.all(
allWorkspacePackageFilepaths.map(async (filepath: string): Promise<PackageInfo> => {
const info: PackageInfo = await loadPackageInfoFromFile(options, filepath)
info.name = info.pkg.name || filepath.split('/').slice(-2)[0]
return info
}),
)
// Workspace package names
// These will be used to filter out local workspace packages so they are not fetched from the registry.
const allWorkspacePackageNames: string[] = allWorkspacePackageInfos.map(
(packageInfo: PackageInfo): string => packageInfo.name || '',
)
const filterWorkspaces = options.workspaces !== true
if (!filterWorkspaces) {
// --workspaces
return [allWorkspacePackageInfos, allWorkspacePackageNames]
}
// add workspace packages
// --workspace
const selectedWorkspacePackageInfos: PackageInfo[] = allWorkspacePackageInfos.filter((packageInfo: PackageInfo) =>
options.workspace?.some((workspace: string) =>
workspaces?.some(
(workspacePattern: string) =>
packageInfo.name === workspace ||
packageInfo.filepath ===
path.join(cwd, path.dirname(workspacePattern), workspace, defaultPackageFilename).replace(/\\/g, '/'),
),
),
)
return [selectedWorkspacePackageInfos, allWorkspacePackageNames]
}
/**
* Gets catalog package info from pnpm-workspace.yaml or package.json.
*
* @param options the application options
* @param pkgPath the package file path (already resolved)
* @returns PackageInfo for catalog dependencies or null if no catalogs exist
*/
async function getCatalogPackageInfo(options: Options, pkgPath: string): Promise<PackageInfo | null> {
if (!pkgPath) {
return null
}
const catalogDependencies = await readCatalogDependencies(options, pkgPath)
if (!catalogDependencies) {
return null
}
// Create a synthetic package info for catalog dependencies
const catalogPackageFile: PackageFile = {
name: 'catalog-dependencies',
version: '1.0.0',
dependencies: catalogDependencies,
}
// Determine the correct file path for catalogs. For pnpm, use pnpm-workspace.yaml.
// For Bun catalogs in package.json, use a virtual path to avoid conflicts with root package.
const catalogFilePath =
options.packageManager === 'pnpm'
? path.join(path.dirname(pkgPath), 'pnpm-workspace.yaml')
: options.packageManager === 'yarn'
? path.join(path.dirname(pkgPath), '.yarnrc.yml')
: `${pkgPath}#catalog`
// Create synthetic file content that matches the synthetic PackageFile
const syntheticFileContent = JSON.stringify(catalogPackageFile, null, 2)
const catalogPackageInfo: PackageInfo = {
filepath: catalogFilePath,
pkg: catalogPackageFile,
pkgFile: syntheticFileContent,
name: 'catalogs',
}
return catalogPackageInfo
}
/**
* Gets all local packages, including workspaces (depending on -w, -ws, and -root).
*
* @param options the application options, used to determine which packages to return.
* @returns PackageInfo[] an array of all package infos to be considered for updating
*/
async function getAllPackages(options: Options): Promise<[PackageInfo[], string[]]> {
const defaultPackageFilename = options.packageFile || 'package.json'
const cwd = options.cwd ? untildify(options.cwd) : './'
const rootPackageFile = options.packageFile || (options.cwd ? path.join(cwd, 'package.json') : 'package.json')
const useWorkspaces: boolean =
options.workspaces === true || (options.workspace !== undefined && options.workspace.length !== 0)
let packageInfos: PackageInfo[] = []
// Find the package file with globby.
// When in workspaces mode, only include the root project package file when --root is used.
const getBasePackageFile: boolean = !useWorkspaces || options.root === true
if (getBasePackageFile) {
// we are either:
// * NOT a workspace
// * a workspace and have requested an upgrade of the workspace-root
const globPattern = rootPackageFile.replace(/\\/g, '/')
const rootPackagePaths = glob.sync(globPattern, globOptions)
// realistically there should only be zero or one
const rootPackages = await Promise.all(
rootPackagePaths.map(
async (packagePath: string): Promise<PackageInfo> => await loadPackageInfoFromFile(options, packagePath),
),
)
packageInfos = [...packageInfos, ...rootPackages]
}
if (!useWorkspaces) {
return [packageInfos, []]
}
// Read catalog dependencies first so we can resolve references
let catalogPackageInfo: PackageInfo | null = null
if (useWorkspaces) {
const { pkgPath: workspacePkgPath } = await findPackage({
...options,
packageFile: rootPackageFile,
loglevel: 'silent',
})
if (workspacePkgPath) {
catalogPackageInfo = await getCatalogPackageInfo(options, workspacePkgPath)
}
}
// workspaces
const [workspacePackageInfos, workspaceNames]: [PackageInfo[], string[]] = await getWorkspacePackageInfos(
options,
defaultPackageFilename,
rootPackageFile,
cwd,
)
// Don't resolve catalog references in workspace packages - leave them as "catalog:*"
// Only the catalog definitions themselves should be updated
packageInfos = [...packageInfos, ...workspacePackageInfos]
// Add catalog package info for version checking (only if there are catalogs)
if (catalogPackageInfo) {
packageInfos = [...packageInfos, catalogPackageInfo]
}
return [packageInfos, workspaceNames]
}
export default getAllPackages
================================================
FILE: src/lib/getCurrentDependencies.ts
================================================
import * as semver from 'semver'
import { Index } from '../types/IndexType'
import { Options } from '../types/Options'
import { PackageFile } from '../types/PackageFile'
import { VersionSpec } from '../types/VersionSpec'
import filterAndReject from './filterAndReject'
import filterObject from './filterObject'
import { keyValueBy } from './keyValueBy'
import programError from './programError'
import resolveDepSections from './resolveDepSections'
/** Returns true if spec1 is greater than spec2, ignoring invalid version ranges. */
const isGreaterThanSafe = (spec1: VersionSpec, spec2: VersionSpec) =>
// not a valid range to compare (e.g. github url)
semver.validRange(spec1) &&
semver.validRange(spec2) &&
// otherwise return true if spec2 is smaller than spec1
semver.gt(semver.minVersion(spec1)!, semver.minVersion(spec2)!)
/** Parses the packageManager field into a { [name]: version } pair. */
const parsePackageManager = (pkgData: PackageFile) => {
if (!pkgData.packageManager) return {}
const [name, version] = pkgData.packageManager.split('@')
return { [name]: version }
}
/**
* Get the current dependencies from the package file.
*
* @param [pkgData={}] Object with dependencies, devDependencies, peerDependencies, and/or optionalDependencies properties.
* @param [options={}]
* @param options.dep
* @param options.filter
* @param options.reject
* @returns Promised {packageName: version} collection
*/
function getCurrentDependencies(pkgData: PackageFile = {}, options: Options = {}) {
const depSections = resolveDepSections(options.dep)
// get all dependencies from the selected sections
// if a dependency appears in more than one section, take the lowest version number
const allDependencies = depSections.reduce((accum, depSection) => {
return {
...accum,
...(depSection === 'packageManager'
? parsePackageManager(pkgData)
: filterObject(
(pkgData[depSection] as Index<string>) || {},
(dep, spec) => !isGreaterThanSafe(spec, accum[dep]),
)),
}
}, {} as Index<VersionSpec>)
// filter & reject dependencies and versions
const workspacePackageMap = keyValueBy(options.workspacePackages || [])
let filteredDependencies: Index<VersionSpec> = {}
try {
filteredDependencies = filterObject(
filterObject(allDependencies, name => !workspacePackageMap[name]),
filterAndReject(
options.filter || null,
options.reject || null,
options.filterVersion || null,
options.rejectVersion || null,
),
)
} catch (err: any) {
programError(options, 'Invalid filter: ' + err.message || err)
}
return filteredDependencies
}
export default getCurrentDependencies
================================================
FILE: src/lib/getEnginesNodeFromRegistry.ts
================================================
import ProgressBar from 'progress'
import { Index } from '../types/IndexType'
import { Options } from '../types/Options'
import { Version } from '../types/Version'
import { VersionSpec } from '../types/VersionSpec'
import getPackageManager from './getPackageManager'
/**
* Get the engines.node versions from the NPM repository based on the version target.
*
* @param packageMap An object whose keys are package name and values are version
* @param [options={}] Options.
* @returns Promised {packageName: engines.node} collection
*/
async function getEnginesNodeFromRegistry(packageMap: Index<Version>, options: Options) {
const packageManager = getPackageManager(options, options.packageManager)
if (!packageManager.getEngines) return {}
const numItems = Object.keys(packageMap).length
let bar: ProgressBar
if (!options.json && options.loglevel !== 'silent' && options.loglevel !== 'verbose' && numItems > 0) {
bar = new ProgressBar('[:bar] :current/:total :percent', { total: numItems, width: 20 })
bar.render()
}
return Object.entries(packageMap).reduce(async (accumPromise, [pkg, version]) => {
const enginesNode = (await packageManager.getEngines!(pkg, version, options)).node
if (bar) {
bar.tick()
}
const accum = await accumPromise
return { ...accum, [pkg]: enginesNode }
}, Promise.resolve<Index<VersionSpec | undefined>>({}))
}
export default getEnginesNodeFromRegistry
================================================
FILE: src/lib/getIgnoredUpgradesDueToEnginesNode.ts
================================================
import { minVersion, satisfies } from 'semver'
import { IgnoredUpgradeDueToEnginesNode } from '../types/IgnoredUpgradeDueToEnginesNode'
import { Index } from '../types/IndexType'
import { Maybe } from '../types/Maybe'
import { Options } from '../types/Options'
import { Version } from '../types/Version'
import { VersionSpec } from '../types/VersionSpec'
import getEnginesNodeFromRegistry from './getEnginesNodeFromRegistry'
import keyValueBy from './keyValueBy'
import upgradePackageDefinitions from './upgradePackageDefinitions'
/** Checks if package.json min node version satisfies given package engine.node spec */
const satisfiesNodeEngine = (enginesNode: Maybe<VersionSpec>, optionsEnginesNodeMinVersion: Version) =>
!enginesNode || satisfies(optionsEnginesNodeMinVersion, enginesNode)
/** Get all upgrades that are ignored due to incompatible engines.node. */
export async function getIgnoredUpgradesDueToEnginesNode(
current: Index<VersionSpec>,
upgraded: Index<VersionSpec>,
options: Options = {},
) {
if (!options.nodeEngineVersion) return {}
const optionsEnginesNodeMinVersion = minVersion(options.nodeEngineVersion)?.version
if (!optionsEnginesNodeMinVersion) return {}
const [upgradedLatestVersions, latestVersionResults] = await upgradePackageDefinitions(current, {
...options,
enginesNode: false,
nodeEngineVersion: undefined,
loglevel: 'silent',
})
// Use the latest versions since getEnginesNodeFromRegistry requires exact versions.
// Filter down to only the upgraded latest versions, as there is no point in checking the engines.node for packages that have been filtered out, e.g. by options.minimal or options.filterResults.
const latestVersions = keyValueBy(latestVersionResults, (dep, result) =>
upgradedLatestVersions[dep] && result?.version
? {
[dep]: result.version,
}
: null,
)
const enginesNodes = await getEnginesNodeFromRegistry(latestVersions, options)
return Object.entries(upgradedLatestVersions)
.filter(
([pkgName, newVersion]) =>
upgraded[pkgName] !== newVersion && !satisfiesNodeEngine(enginesNodes[pkgName], optionsEnginesNodeMinVersion),
)
.reduce(
(accum, [pkgName, newVersion]) => ({
...accum,
[pkgName]: {
from: current[pkgName],
to: newVersion,
enginesNode: enginesNodes[pkgName]!,
},
}),
{} as Index<IgnoredUpgradeDueToEnginesNode>,
)
}
export default getIgnoredUpgradesDueToEnginesNode
================================================
FILE: src/lib/getIgnoredUpgradesDueToPeerDeps.ts
================================================
import { intersects, minVersion, satisfies, validRange } from 'semver'
import { IgnoredUpgradeDueToPeerDeps } from '../types/IgnoredUpgradeDueToPeerDeps'
import { Index } from '../types/IndexType'
import { Options } from '../types/Options'
import { Version } from '../types/Version'
import { VersionSpec } from '../types/VersionSpec'
import getPeerDependenciesFromRegistry from './getPeerDependenciesFromRegistry'
import upgradePackageDefinitions from './upgradePackageDefinitions'
/** Get all upgrades that are ignored due to incompatible peer dependencies. */
export async function getIgnoredUpgradesDueToPeerDeps(
current: Index<VersionSpec>,
upgraded: Index<VersionSpec>,
upgradedPeerDependencies: Index<Index<Version>>,
options: Options = {},
) {
const upgradedPackagesWithPeerRestriction = {
...current,
...upgraded,
}
const [upgradedLatestVersions, latestVersionResults] = await upgradePackageDefinitions(current, {
...options,
peer: false,
peerDependencies: undefined,
loglevel: 'silent',
})
const upgradedPeerDependenciesLatest = await getPeerDependenciesFromRegistry(
Object.fromEntries(
Object.entries(upgradedLatestVersions).map(([packageName, versionSpec]) => {
return [
packageName,
// git urls and other non-semver versions are ignored.
// Make sure versionSpec is a valid semver range; otherwise, minVersion will throw.
validRange(versionSpec) ? (minVersion(versionSpec)?.version ?? versionSpec) : versionSpec,
]
}),
),
options,
)
return Object.entries(upgradedLatestVersions)
.filter(([pkgName, newVersion]) => upgraded[pkgName] !== newVersion)
.reduce((accum, [pkgName, newVersion]) => {
let reason = Object.entries(upgradedPeerDependencies)
.filter(
gitextract_0r1ho66r/ ├── .editorconfig ├── .eslintrc.js ├── .gitattributes ├── .github/ │ ├── CONTRIBUTING.md │ ├── ISSUE_TEMPLATE/ │ │ └── bug_report.md │ └── workflows/ │ ├── codeql.yml │ ├── lint.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .hooks/ │ ├── post-commit │ └── pre-push ├── .markdownlint.js ├── .ncurc.js ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── .vscode/ │ └── settings.json ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── deploy.md ├── package.json ├── src/ │ ├── bin/ │ │ └── cli.ts │ ├── cli-options.ts │ ├── index.ts │ ├── lib/ │ │ ├── cache.ts │ │ ├── chalk.ts │ │ ├── defineConfig.ts │ │ ├── determinePackageManager.ts │ │ ├── doctor.ts │ │ ├── exists.ts │ │ ├── figgy-pudding/ │ │ │ ├── LICENSE.md │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── filterAndReject.ts │ │ ├── filterObject.ts │ │ ├── findLockfile.ts │ │ ├── findPackage.ts │ │ ├── getAllPackages.ts │ │ ├── getCurrentDependencies.ts │ │ ├── getEnginesNodeFromRegistry.ts │ │ ├── getIgnoredUpgradesDueToEnginesNode.ts │ │ ├── getIgnoredUpgradesDueToPeerDeps.ts │ │ ├── getInstalledPackages.ts │ │ ├── getNcuRc.ts │ │ ├── getPackageJson.ts │ │ ├── getPackageManager.ts │ │ ├── getPackageVersion.ts │ │ ├── getPeerDependenciesFromRegistry.ts │ │ ├── getPreferredWildcard.ts │ │ ├── getRepoUrl.ts │ │ ├── initOptions.ts │ │ ├── isUpgradeable.ts │ │ ├── keyValueBy.ts │ │ ├── libnpmconfig/ │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── index.js │ │ ├── loadPackageInfoFromFile.ts │ │ ├── logging.ts │ │ ├── mergeOptions.ts │ │ ├── parseCooldown.ts │ │ ├── pick.ts │ │ ├── programError.ts │ │ ├── queryVersions.ts │ │ ├── resolveDepSections.ts │ │ ├── runGlobal.ts │ │ ├── runLocal.ts │ │ ├── sortBy.ts │ │ ├── spawnCommand.ts │ │ ├── table.ts │ │ ├── upgradeDependencies.ts │ │ ├── upgradeJsonCatalogDependencies.ts │ │ ├── upgradePackageData.ts │ │ ├── upgradePackageDefinitions.ts │ │ ├── upgradeYamlCatalogDependencies.ts │ │ ├── utils/ │ │ │ └── parseJson.ts │ │ ├── version-util.ts │ │ └── wrap.ts │ ├── package-managers/ │ │ ├── README.md │ │ ├── bun.ts │ │ ├── filters.ts │ │ ├── gitTags.ts │ │ ├── index.ts │ │ ├── npm.ts │ │ ├── pnpm.ts │ │ ├── staticRegistry.ts │ │ └── yarn.ts │ ├── scripts/ │ │ ├── build-options.ts │ │ └── install-hooks │ └── types/ │ ├── CLIOption.ts │ ├── Cacher.ts │ ├── CatalogConfig.ts │ ├── CooldownFunction.ts │ ├── DependencyGroup.ts │ ├── ExtendedHelp.ts │ ├── FilterFunction.ts │ ├── FilterPattern.ts │ ├── FilterResultsFunction.ts │ ├── GetVersion.ts │ ├── GroupFunction.ts │ ├── IgnoredUpgradeDueToEnginesNode.ts │ ├── IgnoredUpgradeDueToPeerDeps.ts │ ├── IndexType.ts │ ├── Maybe.ts │ ├── MockedVersions.ts │ ├── NpmConfig.ts │ ├── NpmOptions.ts │ ├── Options.ts │ ├── PackageFile.ts │ ├── PackageFileRepository.ts │ ├── PackageInfo.ts │ ├── PackageManager.ts │ ├── PackageManagerName.ts │ ├── Packument.ts │ ├── RcOptions.ts │ ├── RunOptions.json │ ├── RunOptions.ts │ ├── SpawnOptions.ts │ ├── SpawnPleaseOptions.ts │ ├── StaticRegistry.ts │ ├── Target.ts │ ├── TargetFunction.ts │ ├── UpgradeGroup.ts │ ├── Version.ts │ ├── VersionLevel.ts │ ├── VersionResult.ts │ ├── VersionSpec.ts │ ├── libnpmconfig.d.ts │ └── prompts-ncu.d.ts ├── tea.yaml ├── test/ │ ├── bin.test.ts │ ├── bun/ │ │ ├── bun.lockb │ │ ├── index.test.ts │ │ └── package.json │ ├── bun-install.sh │ ├── cache.test.ts │ ├── cli-options.test.ts │ ├── cooldown.test.ts │ ├── deep.test.ts │ ├── dep.test.ts │ ├── determinePackageManager.test.ts │ ├── doctor.test.ts │ ├── e2e/ │ │ ├── cjs/ │ │ │ ├── index.js │ │ │ └── package.json │ │ └── esm/ │ │ ├── index.js │ │ └── package.json │ ├── e2e.sh │ ├── enginesNode.test.ts │ ├── filter.test.ts │ ├── filterResults.test.ts │ ├── filterVersion.test.ts │ ├── format.test.ts │ ├── getAllPackages.test.ts │ ├── getCurrentDependencies.test.ts │ ├── getEnginesNodeFromRegistry.test.ts │ ├── getIgnoredUpgradesDueToEnginesNode.test.ts │ ├── getIgnoredUpgradesDueToPeerDeps.test.ts │ ├── getInstalledPackages.test.ts │ ├── getPeerDependenciesFromRegistry.test.ts │ ├── getPreferredWildcard.test.ts │ ├── getRepoUrl.test.ts │ ├── github-urls.test.ts │ ├── global.test.ts │ ├── group.test.ts │ ├── helpers/ │ │ ├── chaiSetup.ts │ │ ├── doctorHelpers.ts │ │ ├── removeDir.ts │ │ └── stubVersions.ts │ ├── index.test.ts │ ├── install.test.ts │ ├── interactive.test.ts │ ├── isUpgradeable.test.ts │ ├── package-managers/ │ │ ├── deno/ │ │ │ └── index.test.ts │ │ ├── npm/ │ │ │ ├── index.test.ts │ │ │ └── package.json │ │ └── yarn/ │ │ ├── default/ │ │ │ └── package.json │ │ ├── index.test.ts │ │ ├── nolockfile/ │ │ │ └── package.json │ │ └── v4/ │ │ └── package.json │ ├── parseJson.test.ts │ ├── peer.test.ts │ ├── queryVersions.test.ts │ ├── rc-config.test.ts │ ├── registryType.test.ts │ ├── rejectVersion.ts │ ├── target.test.ts │ ├── test-data/ │ │ ├── basic/ │ │ │ └── package.json │ │ ├── deep-ncurc/ │ │ │ ├── .ncurc.js │ │ │ ├── package.json │ │ │ └── pkg/ │ │ │ ├── sub1/ │ │ │ │ ├── .ncurc.js │ │ │ │ └── package.json │ │ │ ├── sub2/ │ │ │ │ ├── .ncurc.js │ │ │ │ ├── package.json │ │ │ │ ├── sub21/ │ │ │ │ │ ├── .ncurc.js │ │ │ │ │ └── package.json │ │ │ │ └── sub22/ │ │ │ │ └── package.json │ │ │ └── sub3/ │ │ │ ├── package.json │ │ │ ├── sub31/ │ │ │ │ ├── .ncurc.js │ │ │ │ └── package.json │ │ │ └── sub32/ │ │ │ └── package.json │ │ ├── doctor/ │ │ │ ├── custominstall/ │ │ │ │ └── package.json │ │ │ ├── customtest/ │ │ │ │ └── package.json │ │ │ ├── customtest2/ │ │ │ │ ├── echo.js │ │ │ │ └── package.json │ │ │ ├── fail/ │ │ │ │ ├── README.md │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── nolockfile/ │ │ │ │ └── package.json │ │ │ ├── nopackagefile/ │ │ │ │ └── nil │ │ │ ├── notestscript/ │ │ │ │ └── package.json │ │ │ ├── options/ │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ └── pass/ │ │ │ ├── README.md │ │ │ └── package.json │ │ ├── ncu/ │ │ │ ├── package-large.json │ │ │ ├── package.json │ │ │ └── package2.json │ │ ├── peer-post-upgrade/ │ │ │ └── package.json │ │ ├── peer-post-upgrade-no-upgrades/ │ │ │ └── package.json │ │ ├── registry.json │ │ ├── workspace-basic/ │ │ │ ├── package.json │ │ │ └── pkg/ │ │ │ └── sub/ │ │ │ └── package.json │ │ ├── workspace-no-sub-packages/ │ │ │ └── package.json │ │ ├── workspace-sub-package-names/ │ │ │ ├── package.json │ │ │ └── pkg/ │ │ │ ├── dirname-does-not-match-name/ │ │ │ │ └── package.json │ │ │ ├── dirname-matches-name/ │ │ │ │ └── package.json │ │ │ ├── dirname-will-become-name/ │ │ │ │ └── package.json │ │ │ └── unlisted/ │ │ │ └── package.json │ │ └── workspace-workspace-param-is-array/ │ │ └── package.json │ ├── timeout.test.ts │ ├── upgradeDependencies.test.ts │ ├── upgradeYamlCatalogDependencies.test.ts │ ├── version-util.test.ts │ └── workspaces.test.ts ├── tsconfig.json └── vite.config.mts
SYMBOL INDEX (218 symbols across 94 files)
FILE: src/index.ts
function runUpgrades (line 217) | async function runUpgrades(options: Options, timeout?: NodeJS.Timeout): ...
function run (line 329) | async function run(
FILE: src/lib/cache.ts
constant CACHE_DELIMITER (line 10) | const CACHE_DELIMITER = '___'
function checkCacheExpiration (line 19) | function checkCacheExpiration(cacheData: CacheData, cacheExpiration = 10) {
function resolveCacheFile (line 34) | function resolveCacheFile(optionsCacheFile: string) {
function cacheClear (line 39) | async function cacheClear(options: Options) {
function cacher (line 53) | async function cacher(options: Omit<Options, 'cacher'>): Promise<Cacher ...
FILE: src/lib/chalk.ts
type ChalkMethod (line 16) | type ChalkMethod = ((s: any) => string) & { bold: (s: any) => string }
FILE: src/lib/defineConfig.ts
function defineConfig (line 7) | function defineConfig(config: RcOptions) {
FILE: src/lib/doctor.ts
type Run (line 19) | type Run = (options?: Options) => Promise<PackageFile | Index<VersionSpe...
FILE: src/lib/figgy-pudding/index.js
class FiggyPudding (line 11) | class FiggyPudding {
method constructor (line 12) | constructor(specs, opts, providers) {
method get (line 18) | get(key) {
method toJSON (line 21) | toJSON() {
method forEach (line 28) | forEach(fn, thisArg = this) {
method entries (line 33) | *entries(_matcher) {
method concat (line 51) | concat(...moreConfig) {
function pudGet (line 59) | function pudGet(pud, key, validate) {
function tryGet (line 82) | function tryGet(key, p) {
method get (line 93) | get(obj, prop) {
function figgyPudding (line 101) | function figgyPudding(specs, opts) {
function reverse (line 108) | function reverse(arr) {
function entries (line 114) | function entries(obj) {
FILE: src/lib/filterAndReject.ts
function composeFilter (line 17) | function composeFilter(filterPattern: FilterPattern): (name: string, ver...
function filterAndReject (line 78) | function filterAndReject(
FILE: src/lib/findLockfile.ts
function findLockfile (line 23) | async function findLockfile(
FILE: src/lib/findPackage.ts
function findPackage (line 19) | async function findPackage(options: Options): Promise<{
FILE: src/lib/getAllPackages.ts
type PnpmWorkspaces (line 15) | type PnpmWorkspaces =
type YarnConfig (line 19) | type YarnConfig = { catalog?: Index<VersionSpec>; catalogs?: Index<Index...
function getWorkspacePackageInfos (line 107) | async function getWorkspacePackageInfos(
function getCatalogPackageInfo (line 186) | async function getCatalogPackageInfo(options: Options, pkgPath: string):...
function getAllPackages (line 231) | async function getAllPackages(options: Options): Promise<[PackageInfo[],...
FILE: src/lib/getCurrentDependencies.ts
function getCurrentDependencies (line 36) | function getCurrentDependencies(pkgData: PackageFile = {}, options: Opti...
FILE: src/lib/getEnginesNodeFromRegistry.ts
function getEnginesNodeFromRegistry (line 15) | async function getEnginesNodeFromRegistry(packageMap: Index<Version>, op...
FILE: src/lib/getIgnoredUpgradesDueToEnginesNode.ts
function getIgnoredUpgradesDueToEnginesNode (line 17) | async function getIgnoredUpgradesDueToEnginesNode(
FILE: src/lib/getIgnoredUpgradesDueToPeerDeps.ts
function getIgnoredUpgradesDueToPeerDeps (line 11) | async function getIgnoredUpgradesDueToPeerDeps(
FILE: src/lib/getInstalledPackages.ts
function getInstalledPackages (line 20) | async function getInstalledPackages(options: Options = {}) {
FILE: src/lib/getNcuRc.ts
function getNcuRc (line 10) | async function getNcuRc({
FILE: src/lib/getPackageJson.ts
function getPackageJson (line 7) | async function getPackageJson(
FILE: src/lib/getPackageManager.ts
function getPackageManager (line 15) | function getPackageManager(options: Options, name: Maybe<string>): Packa...
FILE: src/lib/getPackageVersion.ts
function getPackageVersion (line 9) | async function getPackageVersion(
FILE: src/lib/getPeerDependenciesFromRegistry.ts
type CircularData (line 8) | type CircularData =
function isCircularPeer (line 22) | function isCircularPeer(peerDependencies: Index<Index<string>>, packageN...
function getPeerDependenciesFromRegistry (line 52) | async function getPeerDependenciesFromRegistry(packageMap: Index<Version...
FILE: src/lib/getPreferredWildcard.ts
function getPreferredWildcard (line 12) | function getPreferredWildcard(dependencies: Index<string | null>) {
FILE: src/lib/getRepoUrl.ts
function getPackageRepo (line 8) | async function getPackageRepo(
function getRepoUrl (line 26) | async function getRepoUrl(
FILE: src/lib/initOptions.ts
function parseFilterExpression (line 20) | function parseFilterExpression(filterExpression: FilterPattern | undefin...
function isValidUrl (line 35) | function isValidUrl(url: string): boolean {
function initOptions (line 46) | async function initOptions(runOptions: RunOptions, { cli }: { cli?: bool...
FILE: src/lib/isUpgradeable.ts
function isUpgradeable (line 15) | function isUpgradeable(current: VersionSpec, latest: Version, { downgrad...
FILE: src/lib/keyValueBy.ts
type KeyValueGenerator (line 3) | type KeyValueGenerator<K, V, R> = (key: K, value: V, accum: Index<R>) =>...
type ArrayKeyValueGenerator (line 4) | type ArrayKeyValueGenerator<T, R> = KeyValueGenerator<T, number, R>
type ObjectKeyValueGenerator (line 5) | type ObjectKeyValueGenerator<T, R> = KeyValueGenerator<string, T, R>
function keyValueBy (line 16) | function keyValueBy<T, R = true>(
FILE: src/lib/libnpmconfig/index.js
method other (line 19) | other() {
function getNpmConfig (line 37) | function getNpmConfig(_opts, _builtin) {
function maybeReadIni (line 80) | function maybeReadIni(f) {
function getGlobalPrefix (line 95) | function getGlobalPrefix() {
FILE: src/lib/logging.ts
type LogLevel (line 26) | type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'verbose' | 'sill...
function print (line 55) | function print(
function printJson (line 75) | function printJson(options: Options, object: any) {
function printSimpleJoinedString (line 82) | function printSimpleJoinedString(object: any, join: string) {
function printSorted (line 91) | function printSorted<T extends { [key: string]: any }>(options: Options,...
function renderDependencyTable (line 101) | function renderDependencyTable(rows: string[][]) {
function getVersion (line 140) | function getVersion(dep: string): string {
function toDependencyTable (line 153) | async function toDependencyTable({
function printUpgradesTable (line 229) | async function printUpgradesTable(
function printErrors (line 283) | function printErrors(options: Options, errors?: Index<string>) {
function printUpgrades (line 320) | async function printUpgrades(
function printIgnoredUpdatesDueToPeerDeps (line 401) | function printIgnoredUpdatesDueToPeerDeps(options: Options, ignoredUpdat...
function printIgnoredUpdatesDueToEnginesNode (line 417) | function printIgnoredUpdatesDueToEnginesNode(
FILE: src/lib/mergeOptions.ts
type OptionKey (line 3) | type OptionKey = keyof Options
function mergeArrays (line 6) | function mergeArrays(arr1: any[], arr2: any[]) {
function mergeOptions (line 14) | function mergeOptions(rawOptions1: Options | null, rawOptions2: Options ...
FILE: src/lib/parseCooldown.ts
function parseCooldown (line 5) | function parseCooldown(s: string): number | null {
FILE: src/lib/pick.ts
function pick (line 2) | function pick<T extends object, U extends keyof T>(obj: T, props: U[]): ...
function pickBy (line 16) | function pickBy<R, K extends keyof R>(
FILE: src/lib/programError.ts
function programError (line 6) | function programError(
FILE: src/lib/queryVersions.ts
function queryVersions (line 23) | async function queryVersions(packageMap: Index<VersionSpec>, options: Op...
FILE: src/lib/runGlobal.ts
function runGlobal (line 11) | async function runGlobal(options: Options): Promise<Index<string> | void> {
FILE: src/lib/runLocal.ts
constant INTERACTIVE_HINT (line 36) | const INTERACTIVE_HINT = `
function getOptionsPerPage (line 48) | function getOptionsPerPage(showHint: boolean, groups?: DependencyGroup[]...
function getOwnerPerDependency (line 61) | async function getOwnerPerDependency(fromVersion: Index<Version>, toVers...
function runLocal (line 173) | async function runLocal(
FILE: src/lib/sortBy.ts
function sortBy (line 7) | function sortBy<T>(collection: T[] | null | undefined, selector: (item: ...
FILE: src/lib/spawnCommand.ts
function spawnCommand (line 9) | async function spawnCommand(
FILE: src/lib/upgradeDependencies.ts
type UpgradeSpec (line 13) | interface UpgradeSpec {
function upgradeDependencies (line 28) | function upgradeDependencies(
FILE: src/lib/upgradeJsonCatalogDependencies.ts
function escapeRegexp (line 8) | function escapeRegexp(s: string) {
function upgradeJsonCatalogDependencies (line 15) | async function upgradeJsonCatalogDependencies(
FILE: src/lib/upgradePackageData.ts
function escapeRegexp (line 16) | function escapeRegexp(s: string) {
function upgradePackageData (line 31) | async function upgradePackageData(
FILE: src/lib/upgradePackageDefinitions.ts
type CheckIfInPeerViolationResult (line 14) | type CheckIfInPeerViolationResult = {
type UpgradePackageDefinitionsResult (line 60) | type UpgradePackageDefinitionsResult = [
function upgradePackageDefinitions (line 73) | async function upgradePackageDefinitions(
FILE: src/lib/upgradeYamlCatalogDependencies.ts
type UpdateYamlCatalogDependenciesArgs (line 7) | type UpdateYamlCatalogDependenciesArgs = {
function throwYamlSyntaxError (line 18) | function throwYamlSyntaxError(error: unknown, { options, filePath }: { o...
function changeDependencyIn (line 38) | function changeDependencyIn(
function updateYamlCatalogDependencies (line 94) | function updateYamlCatalogDependencies({
FILE: src/lib/utils/parseJson.ts
function ensureLineDisplay (line 14) | function ensureLineDisplay(line: string): string {
function getMarker (line 24) | function getMarker(length: number): string {
function showSnippet (line 37) | function showSnippet(lines: string[], errorLine: number, columnNumber: n...
function parseJson (line 58) | function parseJson(jsonString: string) {
FILE: src/lib/version-util.ts
type VersionPart (line 16) | type VersionPart = keyof SemVer
constant VERSION_BASE_PARTS (line 18) | const VERSION_BASE_PARTS = ['major', 'minor', 'patch'] as VersionPart[]
constant VERSION_ADDED_PARTS (line 19) | const VERSION_ADDED_PARTS = ['release', 'build'] as VersionPart[]
constant VERSION_PARTS (line 20) | const VERSION_PARTS = [...VERSION_BASE_PARTS, ...VERSION_ADDED_PARTS] as...
constant VERSION_PART_DELIM (line 21) | const VERSION_PART_DELIM: SemVer = {
constant DEFAULT_WILDCARD (line 28) | const DEFAULT_WILDCARD = '^'
constant WILDCARDS (line 29) | const WILDCARDS = ['^', '~', '.*', '.x']
constant WILDCARDS_PURE (line 30) | const WILDCARDS_PURE = ['^', '~', '^*', '*', 'x', 'x.x', 'x.x.x']
constant WILDCARD_PURE_REGEX (line 31) | const WILDCARD_PURE_REGEX = new RegExp(`^(${WILDCARDS_PURE.join('|').rep...
constant NPM_ALIAS_REGEX (line 34) | const NPM_ALIAS_REGEX = /^npm:(.*)@(.*)/
type UpgradeOptions (line 36) | interface UpgradeOptions {
function numParts (line 45) | function numParts(version: string) {
function precisionAdd (line 67) | function precisionAdd(precision: VersionPart, n: number) {
function stringify (line 91) | function stringify(semver: SemVer, precision?: VersionPart) {
function getPrecision (line 109) | function getPrecision(version: string) {
function setPrecision (line 122) | function setPrecision(version: string, precision: VersionPart) {
function addWildCard (line 135) | function addWildCard(version: string, wildcard: string) {
function isWildCard (line 145) | function isWildCard(version: string) {
function isWildPart (line 155) | function isWildPart(versionPartValue: Maybe<string>) {
function partChanged (line 165) | function partChanged(from: string, to: string): UpgradeGroup {
function getDependencyGroups (line 191) | function getDependencyGroups(
function colorizeDiff (line 245) | function colorizeDiff(from: string, to: string) {
function isComparable (line 294) | function isComparable(a: string, b: string) {
function compareVersions (line 301) | function compareVersions(a: string, b: string) {
function findGreatestByLevel (line 315) | function findGreatestByLevel(versions: string[], current: string, level:...
function isPre (line 337) | function isPre(version: string) {
function v (line 365) | function v(str: Maybe<string>) {
function upgradeDependencyDeclaration (line 442) | function upgradeDependencyDeclaration(
FILE: src/package-managers/bun.ts
function spawnBun (line 10) | async function spawnBun(
FILE: src/package-managers/filters.ts
function allowDeprecatedOrIsNotDeprecated (line 16) | function allowDeprecatedOrIsNotDeprecated(versionResult: Partial<Packume...
function allowPreOrIsNotPre (line 25) | function allowPreOrIsNotPre(versionResult: Partial<Packument>, options: ...
function satisfiesNodeEngine (line 37) | function satisfiesNodeEngine(versionResult: Partial<Packument>, nodeEngi...
function satisfiesPeerDependencies (line 52) | function satisfiesPeerDependencies(versionResult: Partial<Packument>, pe...
function satisfiesCooldownPeriod (line 67) | function satisfiesCooldownPeriod(
function filterPredicate (line 89) | function filterPredicate(options: Options) {
FILE: src/package-managers/gitTags.ts
function getGitTags (line 23) | async function getGitTags(url: string): Promise<Index<string>> {
function getSortedVersions (line 34) | async function getSortedVersions(
FILE: src/package-managers/npm.ts
constant EXPLICIT_RANGE_OPS (line 33) | const EXPLICIT_RANGE_OPS = new Set(['-', '||', '&&', '<', '<=', '>', '>='])
function parseJson (line 324) | function parseJson<R>(result: string, data: { command?: string; packageN...
function packageAuthorChanged (line 347) | async function packageAuthorChanged(
function fetchUpgradedPackument (line 508) | async function fetchUpgradedPackument(
function spawnNpm (line 605) | async function spawnNpm(
function defaultPrefix (line 629) | async function defaultPrefix(options: Options): Promise<string | undefin...
FILE: src/package-managers/pnpm.ts
type PnpmList (line 20) | type PnpmList = {
function spawnPnpm (line 90) | async function spawnPnpm(
FILE: src/package-managers/yarn.ts
type ParsedDep (line 24) | interface ParsedDep {
type NpmScope (line 32) | interface NpmScope {
type YarnConfig (line 38) | interface YarnConfig {
function getPathToLookForYarnrc (line 86) | async function getPathToLookForYarnrc(
function parseJsonLines (line 150) | function parseJsonLines(result: string): Promise<{ dependencies: Index<P...
function extractFirstJsonLine (line 189) | function extractFirstJsonLine(result: string): Promise<string> {
function spawnYarn (line 216) | async function spawnYarn(
function defaultPrefix (line 246) | async function defaultPrefix(options: Options): Promise<string | null> {
FILE: src/scripts/build-options.ts
constant INJECT_HEADER (line 7) | const INJECT_HEADER =
FILE: src/types/CLIOption.ts
type CLIOption (line 3) | interface CLIOption<T = any> {
FILE: src/types/Cacher.ts
type CacheData (line 4) | interface CacheData {
type Cacher (line 10) | type Cacher = {
FILE: src/types/CatalogConfig.ts
type CatalogsConfig (line 8) | type CatalogsConfig = z.infer<typeof CatalogsConfig>
FILE: src/types/CooldownFunction.ts
type CooldownFunction (line 2) | type CooldownFunction = (packageName: string) => number | string | null
FILE: src/types/DependencyGroup.ts
type DependencyGroup (line 3) | interface DependencyGroup {
FILE: src/types/ExtendedHelp.ts
type ExtendedHelp (line 2) | type ExtendedHelp = string | ((options: { markdown?: boolean }) => string)
FILE: src/types/FilterFunction.ts
type FilterFunction (line 4) | type FilterFunction = (packageName: string, versionRange: SemVer[]) => b...
FILE: src/types/FilterPattern.ts
type FilterPattern (line 4) | type FilterPattern = string | RegExp | readonly (string | RegExp)[] | Fi...
FILE: src/types/FilterResultsFunction.ts
type FilterResultsFunction (line 5) | type FilterResultsFunction = (
FILE: src/types/GetVersion.ts
type GetVersion (line 7) | type GetVersion = (
FILE: src/types/GroupFunction.ts
type GroupFunction (line 5) | type GroupFunction = (
FILE: src/types/IgnoredUpgradeDueToEnginesNode.ts
type IgnoredUpgradeDueToEnginesNode (line 5) | interface IgnoredUpgradeDueToEnginesNode {
FILE: src/types/IgnoredUpgradeDueToPeerDeps.ts
type IgnoredUpgradeDueToPeerDeps (line 5) | interface IgnoredUpgradeDueToPeerDeps {
FILE: src/types/IndexType.ts
type Index (line 4) | type Index<T = any> = Record<string, T>
FILE: src/types/Maybe.ts
type Maybe (line 2) | type Maybe<T = any> = T | null | undefined
FILE: src/types/MockedVersions.ts
type MockedVersions (line 7) | type MockedVersions =
FILE: src/types/NpmConfig.ts
type NpmConfig (line 3) | type NpmConfig = Index<string | boolean | ((path: string) => string | { ...
FILE: src/types/NpmOptions.ts
type NpmOptions (line 2) | interface NpmOptions {
FILE: src/types/Options.ts
type Options (line 7) | type Options = RunOptions & {
FILE: src/types/PackageFile.ts
type NestedVersionSpecs (line 5) | type NestedVersionSpecs = {
type PackageFile (line 10) | interface PackageFile {
FILE: src/types/PackageFileRepository.ts
type PackageFileRepository (line 2) | interface PackageFileRepository {
FILE: src/types/PackageInfo.ts
type PackageInfo (line 4) | interface PackageInfo {
FILE: src/types/PackageManager.ts
type PackageManager (line 10) | interface PackageManager {
FILE: src/types/PackageManagerName.ts
type PackageManagerName (line 2) | type PackageManagerName = 'npm' | 'yarn' | 'pnpm' | 'deno' | 'bun' | 'st...
FILE: src/types/Packument.ts
type Packument (line 5) | interface Packument {
FILE: src/types/RcOptions.ts
type Nonsensical (line 4) | type Nonsensical = 'configFileName' | 'configFilePath' | 'cwd' | 'packag...
type RcOptions (line 7) | type RcOptions = Omit<RunOptions, Nonsensical> & {
FILE: src/types/RunOptions.ts
type RunOptions (line 10) | interface RunOptions {
FILE: src/types/SpawnOptions.ts
type SpawnOptions (line 4) | interface SpawnOptions {
FILE: src/types/SpawnPleaseOptions.ts
type SpawnPleaseOptions (line 1) | interface SpawnPleaseOptions {
FILE: src/types/StaticRegistry.ts
type StaticRegistry (line 3) | type StaticRegistry = {
FILE: src/types/Target.ts
type TargetString (line 7) | type TargetString = (typeof supportedVersionTargets)[number]
type TargetDistTag (line 10) | type TargetDistTag = `@${string}`
type Target (line 13) | type Target = TargetString | TargetDistTag | TargetFunction
FILE: src/types/TargetFunction.ts
type TargetFunction (line 4) | type TargetFunction = (packageName: string, versionRange: SemVer[]) => s...
FILE: src/types/UpgradeGroup.ts
type UpgradeGroup (line 1) | type UpgradeGroup = 'major' | 'minor' | 'patch' | 'majorVersionZero' | '...
FILE: src/types/Version.ts
type Version (line 2) | type Version = string
FILE: src/types/VersionLevel.ts
type VersionLevel (line 2) | type VersionLevel = 'major' | 'minor' | 'patch'
FILE: src/types/VersionResult.ts
type VersionResult (line 4) | interface VersionResult {
FILE: src/types/VersionSpec.ts
type VersionSpec (line 2) | type VersionSpec = string
FILE: test/cooldown.test.ts
constant DAY (line 11) | const DAY = 24 * 60 * 60 * 1000
constant NOW (line 12) | const NOW = Date.now()
type CreateMockParams (line 14) | interface CreateMockParams {
FILE: test/determinePackageManager.test.ts
function readdirMock (line 11) | function readdirMock(path: string): Promise<string[]> {
function readdirMock (line 32) | function readdirMock(path: string): Promise<string[]> {
function readdirMock (line 53) | function readdirMock(path: string): Promise<string[]> {
function readdirMock (line 80) | function readdirMock(path: string): Promise<string[]> {
function readdirMock (line 101) | function readdirMock(path: string): Promise<string[]> {
function readdirMock (line 128) | function readdirMock(path: string): Promise<string[]> {
function readdirMock (line 155) | function readdirMock(): Promise<string[]> {
FILE: test/getAllPackages.test.ts
function asPosixPath (line 10) | function asPosixPath(filepath: string): string {
function stripDir (line 15) | async function stripDir(dirPath: string, paths: [string[], string[]]): P...
function getAllPackagesForTest (line 24) | async function getAllPackagesForTest(testPath: string, options: Options)...
FILE: test/getIgnoredUpgradesDueToEnginesNode.test.ts
constant MOCK_ESLINT_VERSION (line 4) | const MOCK_ESLINT_VERSION = '999.0.0'
constant MOCK_DEL_VERSION (line 5) | const MOCK_DEL_VERSION = '999.0.1'
FILE: test/group.test.ts
function groupTestScaffold (line 17) | async function groupTestScaffold(
FILE: test/helpers/removeDir.ts
function removeDir (line 10) | async function removeDir(dirPath: string, maxRetries = 10, delayMs = 100) {
FILE: test/package-managers/yarn/index.test.ts
function readdirMock (line 127) | function readdirMock(path: string): Promise<string[]> {
Condensed preview — 238 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (815K chars).
[
{
"path": ".editorconfig",
"chars": 214,
"preview": "# editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_"
},
{
"path": ".eslintrc.js",
"chars": 1567,
"preview": "module.exports = {\n env: {\n es6: true,\n mocha: true,\n node: true,\n },\n extends: ['standard', 'eslint:recomme"
},
{
"path": ".gitattributes",
"chars": 43,
"preview": "# Enforce Unix newlines\n* text=auto eol=lf\n"
},
{
"path": ".github/CONTRIBUTING.md",
"chars": 2236,
"preview": "## Filing an issue\n\nMake sure you read the list of [known issues](https://github.com/raineorshine/npm-check-updates#know"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 756,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n---\n\n- [] I have searc"
},
{
"path": ".github/workflows/codeql.yml",
"chars": 662,
"preview": "name: 'CodeQL'\n\non:\n push:\n branches:\n - main\n - '!dependabot/**'\n pull_request:\n # The branches below"
},
{
"path": ".github/workflows/lint.yml",
"chars": 554,
"preview": "name: Lint\n\non:\n push:\n branches:\n - main\n - '!dependabot/**'\n pull_request:\n branches:\n - '**'\n\n"
},
{
"path": ".github/workflows/release.yml",
"chars": 303,
"preview": "name: Release\n\non:\n push:\n tags:\n - 'v*'\n\njobs:\n release:\n name: Release\n runs-on: ubuntu-latest\n\n st"
},
{
"path": ".github/workflows/test.yml",
"chars": 1088,
"preview": "name: Tests\n\non:\n push:\n branches:\n - main\n - '!dependabot/**'\n pull_request:\n branches:\n - '**'\n"
},
{
"path": ".gitignore",
"chars": 216,
"preview": "lib-cov\n*.seed\n*.log\n*.csv\n*.dat\n*.out\n*.pid\n*.gz\n\npids\nlogs\nresults\n\nnpm-debug.log\nnode_modules\nbuild\n\n.idea\n*.iml\nyarn"
},
{
"path": ".hooks/post-commit",
"chars": 1092,
"preview": "#!/usr/bin/env bash\nSHORT_SHA=$(git rev-parse HEAD)\nLINT_LOG=\"$TMPDIR\"/lint.\"$SHORT_SHA\".log\n\n# strip color\nstrip() {\n "
},
{
"path": ".hooks/pre-push",
"chars": 119,
"preview": "#!/bin/sh\nfail=0\n\nnpm run lint || fail=1\nnpm run prettier -- --check || fail=1\n\nif [ \"$fail\" -ne 0 ]; then\n exit 1\nfi\n"
},
{
"path": ".markdownlint.js",
"chars": 392,
"preview": "module.exports = {\n // use code indentation rather than code fencing so that extended help can be used for both the CLI"
},
{
"path": ".ncurc.js",
"chars": 364,
"preview": "module.exports = {\n format: 'group',\n reject: [\n // breaking\n 'eslint',\n 'eslint-plugin-n',\n 'eslint-plugi"
},
{
"path": ".npmrc",
"chars": 19,
"preview": "script-shell = bash"
},
{
"path": ".prettierignore",
"chars": 6,
"preview": "build/"
},
{
"path": ".prettierrc.json",
"chars": 363,
"preview": "{\n \"arrowParens\": \"avoid\",\n \"importOrder\": [\"^\\\\.\\\\./\", \"^\\\\./\"],\n \"importOrderSortSpecifiers\": true,\n \"overrides\": "
},
{
"path": ".vscode/settings.json",
"chars": 89,
"preview": "{\n \"editor.defaultFormatter\": \"esbenp.prettier-vscode\",\n \"editor.formatOnSave\": true\n}\n"
},
{
"path": "CHANGELOG.md",
"chars": 17237,
"preview": "# Changelog\n\nThis file only documents **major version** releases. For smaller releases, you're stuck reading the [commit"
},
{
"path": "Dockerfile",
"chars": 123,
"preview": "FROM node:latest\n\nRUN npm install -g npm-check-updates\n\nWORKDIR /app\n\nENTRYPOINT [\"npm-check-updates"
},
{
"path": "LICENSE",
"chars": 555,
"preview": "Copyright 2025 Tomas Junnonen\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "README.md",
"chars": 36382,
"preview": "# npm-check-updates\n\n[](https://www.npmjs.com/package/npm-"
},
{
"path": "deploy.md",
"chars": 267,
"preview": "# Deployment Instructions\n\n- Have you created tests?\n- Have you updated the README?\n\n```bash\nnpm version [minor]\ngit pus"
},
{
"path": "package.json",
"chars": 5404,
"preview": "{\n \"name\": \"npm-check-updates\",\n \"version\": \"19.6.5\",\n \"author\": \"Tomas Junnonen <tomas1@gmail.com>\",\n \"license\": \"A"
},
{
"path": "src/bin/cli.ts",
"chars": 8901,
"preview": "#!/usr/bin/env node\nimport { Help, Option, program } from 'commander'\nimport createCloneDeep from 'rfdc'\nimport semver f"
},
{
"path": "src/cli-options.ts",
"chars": 39567,
"preview": "import path from 'path'\nimport { defaultCacheFile } from './lib/cache'\nimport chalk from './lib/chalk'\nimport parseCoold"
},
{
"path": "src/index.ts",
"chars": 16581,
"preview": "import path from 'path'\nimport prompts from 'prompts-ncu'\nimport pkg from '../package.json'\nimport { cliOptionsMap } fro"
},
{
"path": "src/lib/cache.ts",
"chars": 3827,
"preview": "import fs from 'fs'\nimport os from 'os'\nimport path from 'path'\nimport { CacheData, Cacher } from '../types/Cacher'\nimpo"
},
{
"path": "src/lib/chalk.ts",
"chars": 2320,
"preview": "/*\n\nThis chalk wrapper allows synchronous chalk.COLOR(...) syntax with special support for:\n\n1) dynamic import as pure E"
},
{
"path": "src/lib/defineConfig.ts",
"chars": 248,
"preview": "import type { RcOptions } from '../types/RcOptions'\n\n/**\n * TypeScript helper for .npmrc config file. Similar to vite an"
},
{
"path": "src/lib/determinePackageManager.ts",
"chars": 1964,
"preview": "import fs from 'fs/promises'\nimport { Index } from '../types/IndexType'\nimport { Options } from '../types/Options'\nimpor"
},
{
"path": "src/lib/doctor.ts",
"chars": 11797,
"preview": "import fs from 'fs/promises'\nimport spawn from 'spawn-please'\nimport { printUpgrades } from '../lib/logging'\nimport spaw"
},
{
"path": "src/lib/exists.ts",
"chars": 183,
"preview": "import fs from 'fs/promises'\n\n/** Returns true if a file exists. */\nconst exists = (path: string) =>\n fs.stat(path).the"
},
{
"path": "src/lib/figgy-pudding/LICENSE.md",
"chars": 755,
"preview": "ISC License\n\nCopyright (c) npm, Inc.\n\nPermission to use, copy, modify, and/or distribute this software for\nany purpose w"
},
{
"path": "src/lib/figgy-pudding/README.md",
"chars": 6925,
"preview": "# Note: pending imminent deprecation\n\n**This module will be deprecated once npm v7 is released. Please do not rely\non it"
},
{
"path": "src/lib/figgy-pudding/index.js",
"chars": 2606,
"preview": "/* eslint-disable */\n\n/*\n\nThis is stripped down version of the deprecated figgy-pudding. It is used by libnpmconfig, whi"
},
{
"path": "src/lib/filterAndReject.ts",
"chars": 3577,
"preview": "import { and, or } from 'fp-and-or'\nimport identity from 'lodash/identity'\nimport picomatch from 'picomatch'\nimport { pa"
},
{
"path": "src/lib/filterObject.ts",
"chars": 378,
"preview": "import { Index } from '../types/IndexType'\nimport keyValueBy from './keyValueBy'\n\n/** Filters an object by a predicate. "
},
{
"path": "src/lib/findLockfile.ts",
"chars": 1897,
"preview": "import fs from 'fs/promises'\nimport os from 'os'\nimport path from 'path'\nimport { Options } from '../types/Options'\n\ncon"
},
{
"path": "src/lib/findPackage.ts",
"chars": 3002,
"preview": "import findUp from 'find-up'\nimport fs from 'fs/promises'\nimport { text } from 'node:stream/consumers'\nimport path from "
},
{
"path": "src/lib/getAllPackages.ts",
"chars": 11163,
"preview": "import glob, { type Options as GlobOptions } from 'fast-glob'\nimport fs from 'fs/promises'\nimport yaml from 'js-yaml'\nim"
},
{
"path": "src/lib/getCurrentDependencies.ts",
"chars": 2740,
"preview": "import * as semver from 'semver'\nimport { Index } from '../types/IndexType'\nimport { Options } from '../types/Options'\ni"
},
{
"path": "src/lib/getEnginesNodeFromRegistry.ts",
"chars": 1440,
"preview": "import ProgressBar from 'progress'\nimport { Index } from '../types/IndexType'\nimport { Options } from '../types/Options'"
},
{
"path": "src/lib/getIgnoredUpgradesDueToEnginesNode.ts",
"chars": 2516,
"preview": "import { minVersion, satisfies } from 'semver'\nimport { IgnoredUpgradeDueToEnginesNode } from '../types/IgnoredUpgradeDu"
},
{
"path": "src/lib/getIgnoredUpgradesDueToPeerDeps.ts",
"chars": 3431,
"preview": "import { intersects, minVersion, satisfies, validRange } from 'semver'\nimport { IgnoredUpgradeDueToPeerDeps } from '../t"
},
{
"path": "src/lib/getInstalledPackages.ts",
"chars": 1474,
"preview": "import { Index } from '../types/IndexType'\nimport { Options } from '../types/Options'\nimport { Version } from '../types/"
},
{
"path": "src/lib/getNcuRc.ts",
"chars": 2704,
"preview": "import os from 'os'\nimport path from 'path'\nimport { rcFile } from 'rc-config-loader'\nimport { cliOptionsMap } from '../"
},
{
"path": "src/lib/getPackageJson.ts",
"chars": 1140,
"preview": "import fs from 'fs/promises'\nimport path from 'path'\nimport { PackageFile } from '../types/PackageFile'\nimport exists fr"
},
{
"path": "src/lib/getPackageManager.ts",
"chars": 933,
"preview": "import packageManagers from '../package-managers'\nimport { Maybe } from '../types/Maybe'\nimport { Options } from '../typ"
},
{
"path": "src/lib/getPackageVersion.ts",
"chars": 874,
"preview": "import { PackageFile } from '../types/PackageFile'\nimport getPackageJson from './getPackageJson'\n\n/**\n * @param packageN"
},
{
"path": "src/lib/getPeerDependenciesFromRegistry.ts",
"chars": 3291,
"preview": "import pMap from 'p-map'\nimport ProgressBar from 'progress'\nimport { Index } from '../types/IndexType'\nimport { Options "
},
{
"path": "src/lib/getPreferredWildcard.ts",
"chars": 1270,
"preview": "import { Index } from '../types/IndexType'\nimport { sortBy } from './sortBy'\nimport { WILDCARDS } from './version-util'\n"
},
{
"path": "src/lib/getRepoUrl.ts",
"chars": 2535,
"preview": "import hostedGitInfo from 'hosted-git-info'\nimport { URL } from 'url'\nimport { PackageFile } from '../types/PackageFile'"
},
{
"path": "src/lib/initOptions.ts",
"chars": 10883,
"preview": "import { dequal } from 'dequal'\nimport propertyOf from 'lodash/propertyOf'\nimport cliOptions from '../cli-options'\nimpor"
},
{
"path": "src/lib/isUpgradeable.ts",
"chars": 2266,
"preview": "import * as semver from 'semver'\nimport semverutils from 'semver-utils'\nimport { Version } from '../types/Version'\nimpor"
},
{
"path": "src/lib/keyValueBy.ts",
"chars": 1666,
"preview": "import { Index } from '../types/IndexType'\n\ntype KeyValueGenerator<K, V, R> = (key: K, value: V, accum: Index<R>) => Ind"
},
{
"path": "src/lib/libnpmconfig/LICENSE",
"chars": 717,
"preview": "Copyright npm, Inc\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee"
},
{
"path": "src/lib/libnpmconfig/README.md",
"chars": 2187,
"preview": "# libnpmconfig\n\n[](https://npm.im/libnpmconfig)\n[![license]"
},
{
"path": "src/lib/libnpmconfig/index.js",
"chars": 3096,
"preview": "/*\n\nThis is a copy of the deprecated libnpmconfig library. It has been brought into the codebase to avoid deprecation wa"
},
{
"path": "src/lib/loadPackageInfoFromFile.ts",
"chars": 741,
"preview": "import fs from 'fs/promises'\nimport { Options } from '../types/Options'\nimport { PackageFile } from '../types/PackageFil"
},
{
"path": "src/lib/logging.ts",
"chars": 12792,
"preview": "/**\n * Loggin functions.\n */\nimport Table from 'cli-table3'\nimport fs from 'fs/promises'\nimport { IgnoredUpgradeDueToEng"
},
{
"path": "src/lib/mergeOptions.ts",
"chars": 859,
"preview": "import { Options } from '../types/Options'\n\ntype OptionKey = keyof Options\n\n/** Merges two arrays into one, removing dup"
},
{
"path": "src/lib/parseCooldown.ts",
"chars": 488,
"preview": "/**\n * Parses a cooldown string (e.g. \"6d\", \"12h\", \"30m\") into a number of days.\n * Returns `null` if the string does no"
},
{
"path": "src/lib/pick.ts",
"chars": 827,
"preview": "/** Creates an object composed of the picked `object` properties. */\nexport function pick<T extends object, U extends ke"
},
{
"path": "src/lib/programError.ts",
"chars": 611,
"preview": "import { print } from '../lib/logging'\nimport { Options } from '../types/Options'\nimport chalk from './chalk'\n\n/** Print"
},
{
"path": "src/lib/queryVersions.ts",
"chars": 6176,
"preview": "import pMap from 'p-map'\nimport ProgressBar from 'progress'\nimport { parseRange } from 'semver-utils'\nimport packageMana"
},
{
"path": "src/lib/resolveDepSections.ts",
"chars": 885,
"preview": "import { cliOptionsMap } from '../cli-options'\nimport { Index } from '../types/IndexType'\nimport { PackageFile } from '."
},
{
"path": "src/lib/runGlobal.ts",
"chars": 2766,
"preview": "import { print, printJson, printSorted, printUpgrades } from '../lib/logging'\nimport { Index } from '../types/IndexType'"
},
{
"path": "src/lib/runLocal.ts",
"chars": 11375,
"preview": "import fs from 'fs/promises'\nimport prompts from 'prompts-ncu'\nimport nodeSemver from 'semver'\nimport { parseDocument } "
},
{
"path": "src/lib/sortBy.ts",
"chars": 602,
"preview": "/**\n * Creates an array of elements, sorted in ascending order by the results of\n * running each element in a collection"
},
{
"path": "src/lib/spawnCommand.ts",
"chars": 850,
"preview": "import { SpawnOptions } from 'child_process'\nimport spawn from 'spawn-please'\nimport { SpawnPleaseOptions } from '../typ"
},
{
"path": "src/lib/table.ts",
"chars": 878,
"preview": "import Table from 'cli-table3'\nimport wrap from './wrap'\n\n/** Wraps the second column in a list of 2-column cli-table ro"
},
{
"path": "src/lib/upgradeDependencies.ts",
"chars": 4421,
"preview": "import flow from 'lodash/flow'\nimport { parseRange } from 'semver-utils'\nimport { Index } from '../types/IndexType'\nimpo"
},
{
"path": "src/lib/upgradeJsonCatalogDependencies.ts",
"chars": 1130,
"preview": "import fs from 'fs/promises'\nimport { Index } from '../types/IndexType'\nimport { VersionSpec } from '../types/VersionSpe"
},
{
"path": "src/lib/upgradePackageData.ts",
"chars": 5486,
"preview": "import fs from 'fs/promises'\nimport path from 'path'\nimport { parseDocument } from 'yaml'\nimport { CatalogsConfig } from"
},
{
"path": "src/lib/upgradePackageDefinitions.ts",
"chars": 6060,
"preview": "import { dequal } from 'dequal'\nimport { intersects, satisfies, validRange } from 'semver'\nimport { parse, parseRange } "
},
{
"path": "src/lib/upgradeYamlCatalogDependencies.ts",
"chars": 5131,
"preview": "import type { Document } from 'yaml'\nimport { CST, isCollection, isPair, isScalar, parseDocument } from 'yaml'\nimport { "
},
{
"path": "src/lib/utils/parseJson.ts",
"chars": 3305,
"preview": "import { ParseError, ParseErrorCode, parse, stripComments } from 'jsonc-parser'\n\nconst stdoutColumns = process.stdout.co"
},
{
"path": "src/lib/version-util.ts",
"chars": 18824,
"preview": "import propertyOf from 'lodash/propertyOf'\nimport parseGitHubUrl from 'parse-github-url'\nimport semver from 'semver'\nimp"
},
{
"path": "src/lib/wrap.ts",
"chars": 1386,
"preview": "/** Wraps a string by inserting newlines every n characters. Wraps on word break. Default: 92 chars. */\nconst wrap = (s:"
},
{
"path": "src/package-managers/README.md",
"chars": 734,
"preview": "# Package Managers\n\n## How to add a new package manager\n\nTo add support for another package manager, drop in a module wi"
},
{
"path": "src/package-managers/bun.ts",
"chars": 2781,
"preview": "import path from 'path'\nimport spawn from 'spawn-please'\nimport keyValueBy from '../lib/keyValueBy'\nimport { Index } fro"
},
{
"path": "src/package-managers/filters.ts",
"chars": 4634,
"preview": "import semver from 'semver'\nimport parseCooldown from '../lib/parseCooldown'\nimport * as versionUtil from '../lib/versio"
},
{
"path": "src/package-managers/gitTags.ts",
"chars": 4824,
"preview": "/** Fetches package metadata from GitHub tags. */\nimport childProcess from 'node:child_process'\nimport { promisify } fro"
},
{
"path": "src/package-managers/index.ts",
"chars": 412,
"preview": "import { Index } from '../types/IndexType'\nimport { PackageManager } from '../types/PackageManager'\nimport * as bun from"
},
{
"path": "src/package-managers/npm.ts",
"chars": 35253,
"preview": "import camelCase from 'camelcase'\nimport memoize from 'fast-memoize'\nimport fs from 'fs'\nimport ini from 'ini'\nimport np"
},
{
"path": "src/package-managers/pnpm.ts",
"chars": 3671,
"preview": "import memoize from 'fast-memoize'\nimport findUp from 'find-up'\nimport fs from 'fs/promises'\nimport ini from 'ini'\nimpor"
},
{
"path": "src/package-managers/staticRegistry.ts",
"chars": 1512,
"preview": "import memoize from 'fast-memoize'\nimport fs from 'fs/promises'\nimport programError from '../lib/programError'\nimport { "
},
{
"path": "src/package-managers/yarn.ts",
"chars": 14238,
"preview": "import memoize from 'fast-memoize'\nimport fs from 'fs/promises'\nimport yaml from 'js-yaml'\nimport jsonlines from 'jsonli"
},
{
"path": "src/scripts/build-options.ts",
"chars": 4866,
"preview": "import fs from 'fs/promises'\nimport spawn from 'spawn-please'\nimport cliOptions, { renderExtendedHelp } from '../cli-opt"
},
{
"path": "src/scripts/install-hooks",
"chars": 279,
"preview": "#!/usr/bin/env bash\n\n# Use a simple bash script to install git hooks.\n# husky was slow and did not play nice with nvm.\n#"
},
{
"path": "src/types/CLIOption.ts",
"chars": 563,
"preview": "import ExtendedHelp from './ExtendedHelp'\n\nexport interface CLIOption<T = any> {\n arg?: string\n choices?: T[]\n /** If"
},
{
"path": "src/types/Cacher.ts",
"chars": 560,
"preview": "import { Index } from './IndexType'\nimport { Version } from './Version'\n\nexport interface CacheData {\n timestamp?: numb"
},
{
"path": "src/types/CatalogConfig.ts",
"chars": 284,
"preview": "import { z } from 'zod'\n\nexport const CatalogsConfig = z.object({\n catalog: z.optional(z.record(z.string(), z.string())"
},
{
"path": "src/types/CooldownFunction.ts",
"chars": 174,
"preview": "/** A function that can be provided to the --cooldown option for custom cooldown predicate. */\nexport type CooldownFunct"
},
{
"path": "src/types/DependencyGroup.ts",
"chars": 138,
"preview": "import { Index } from './IndexType'\n\nexport interface DependencyGroup {\n heading: string\n groupName: string\n packages"
},
{
"path": "src/types/ExtendedHelp.ts",
"chars": 164,
"preview": "/** A function that renders extended help for an option. */\ntype ExtendedHelp = string | ((options: { markdown?: boolean"
},
{
"path": "src/types/FilterFunction.ts",
"chars": 190,
"preview": "import { SemVer } from 'semver-utils'\n\n/** Supported function for the --filter and --reject options. */\nexport type Filt"
},
{
"path": "src/types/FilterPattern.ts",
"chars": 208,
"preview": "import { FilterFunction } from './FilterFunction'\n\n/** Supported patterns for the --filter and --reject options. */\nexpo"
},
{
"path": "src/types/FilterResultsFunction.ts",
"chars": 352,
"preview": "import { SemVer } from 'semver-utils'\nimport { Version } from './Version'\nimport { VersionSpec } from './VersionSpec'\n\ne"
},
{
"path": "src/types/GetVersion.ts",
"chars": 406,
"preview": "import { NpmConfig } from './NpmConfig'\nimport { Options } from './Options'\nimport { Version } from './Version'\nimport {"
},
{
"path": "src/types/GroupFunction.ts",
"chars": 423,
"preview": "import { SemVer } from 'semver-utils'\nimport { UpgradeGroup } from './UpgradeGroup'\n\n/** Customize how packages are divi"
},
{
"path": "src/types/IgnoredUpgradeDueToEnginesNode.ts",
"chars": 283,
"preview": "import { Version } from './Version'\nimport { VersionSpec } from './VersionSpec'\n\n/** An object that represents an upgrad"
},
{
"path": "src/types/IgnoredUpgradeDueToPeerDeps.ts",
"chars": 286,
"preview": "import { Index } from './IndexType'\nimport { Version } from './Version'\n\n/** An object that represents an upgrade that w"
},
{
"path": "src/types/IndexType.ts",
"chars": 163,
"preview": "// This file cannot be named Index.ts as it conflicts with default directory import.\n\n/** A very generic object. */\nexpo"
},
{
"path": "src/types/Maybe.ts",
"chars": 96,
"preview": "/** A value that may be null or undefined. */\nexport type Maybe<T = any> = T | null | undefined\n"
},
{
"path": "src/types/MockedVersions.ts",
"chars": 380,
"preview": "import { Index } from './IndexType'\nimport { Options } from './Options'\nimport { Packument } from './Packument'\nimport {"
},
{
"path": "src/types/NpmConfig.ts",
"chars": 145,
"preview": "import { Index } from './IndexType'\n\nexport type NpmConfig = Index<string | boolean | ((path: string) => string | { ca: "
},
{
"path": "src/types/NpmOptions.ts",
"chars": 133,
"preview": "/** Options that can be provided to npm. */\nexport interface NpmOptions {\n global?: boolean\n prefix?: string\n registr"
},
{
"path": "src/types/Options.ts",
"chars": 784,
"preview": "import { Cacher } from './Cacher'\nimport { Index } from './IndexType'\nimport { RunOptions } from './RunOptions'\nimport {"
},
{
"path": "src/types/PackageFile.ts",
"chars": 844,
"preview": "import { Index } from './IndexType'\nimport { PackageFileRepository } from './PackageFileRepository'\nimport { VersionSpec"
},
{
"path": "src/types/PackageFileRepository.ts",
"chars": 134,
"preview": "/** Represents the repository field in package.json. */\nexport interface PackageFileRepository {\n url: string\n directo"
},
{
"path": "src/types/PackageInfo.ts",
"chars": 222,
"preview": "import { PackageFile } from './PackageFile'\n\n/** Describes package data plus it's filepath */\nexport interface PackageIn"
},
{
"path": "src/types/PackageManager.ts",
"chars": 1154,
"preview": "import { GetVersion } from './GetVersion'\nimport { Index } from './IndexType'\nimport { NpmConfig } from './NpmConfig'\nim"
},
{
"path": "src/types/PackageManagerName.ts",
"chars": 125,
"preview": "/** A valid package manager. */\nexport type PackageManagerName = 'npm' | 'yarn' | 'pnpm' | 'deno' | 'bun' | 'staticRegis"
},
{
"path": "src/types/Packument.ts",
"chars": 496,
"preview": "import { Index } from './IndexType'\nimport { Version } from './Version'\n\n/** A packument result object from npm-registry"
},
{
"path": "src/types/RcOptions.ts",
"chars": 478,
"preview": "import { RunOptions } from './RunOptions'\n\n/** Options that would make no sense in a .ncurc file */\ntype Nonsensical = '"
},
{
"path": "src/types/RunOptions.json",
"chars": 16219,
"preview": "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"definitions\": {\n \"Index<string>\": {\n \"description\":"
},
{
"path": "src/types/RunOptions.ts",
"chars": 8296,
"preview": "/** This file is generated automatically from the options specified in /src/cli-options.ts. Do not edit manually. Run \"n"
},
{
"path": "src/types/SpawnOptions.ts",
"chars": 151,
"preview": "import { Index } from './IndexType'\n\n/** Options to the spawn node built-in. */\nexport interface SpawnOptions {\n cwd?: "
},
{
"path": "src/types/SpawnPleaseOptions.ts",
"chars": 145,
"preview": "export interface SpawnPleaseOptions {\n rejectOnError?: boolean\n stdin?: string\n stdout?: (s: string) => void\n stderr"
},
{
"path": "src/types/StaticRegistry.ts",
"chars": 95,
"preview": "import { Version } from './Version'\n\nexport type StaticRegistry = {\n [key: string]: Version\n}\n"
},
{
"path": "src/types/Target.ts",
"chars": 697,
"preview": "import { TargetFunction } from './TargetFunction'\n\n/** Valid strings for the --target option. Indicates the desired vers"
},
{
"path": "src/types/TargetFunction.ts",
"chars": 208,
"preview": "import { SemVer } from 'semver-utils'\n\n/** A function that can be provided to the --target option for custom filtering. "
},
{
"path": "src/types/UpgradeGroup.ts",
"chars": 85,
"preview": "export type UpgradeGroup = 'major' | 'minor' | 'patch' | 'majorVersionZero' | 'none'\n"
},
{
"path": "src/types/Version.ts",
"chars": 133,
"preview": "/** An exact version number or value. Includes SemVer and some nonstandard variants for convenience. */\nexport type Vers"
},
{
"path": "src/types/VersionLevel.ts",
"chars": 104,
"preview": "/** The three main parts of a SemVer version. */\nexport type VersionLevel = 'major' | 'minor' | 'patch'\n"
},
{
"path": "src/types/VersionResult.ts",
"chars": 291,
"preview": "import { Version } from './Version'\n\n/** The result of fetching a version from the package manager, which may include an"
},
{
"path": "src/types/VersionSpec.ts",
"chars": 166,
"preview": "/** A version specification or range supported as a value in package.json dependencies. Includes SemVer, git urls, npm u"
},
{
"path": "src/types/libnpmconfig.d.ts",
"chars": 71,
"preview": "// add to tsconfig compilerOptions.paths\ndeclare module 'libnpmconfig'\n"
},
{
"path": "src/types/prompts-ncu.d.ts",
"chars": 70,
"preview": "// add to tsconfig compilerOptions.paths\ndeclare module 'prompts-ncu'\n"
},
{
"path": "tea.yaml",
"chars": 126,
"preview": "# https://tea.xyz/what-is-this-file\n---\nversion: 1.0.0\ncodeOwners:\n - '0x333377f49cBD5396E27f750C9413b5D758c705C2'\nquor"
},
{
"path": "test/bin.test.ts",
"chars": 16019,
"preview": "import fs from 'fs/promises'\nimport os from 'os'\nimport path from 'path'\nimport spawn from 'spawn-please'\nimport { Index"
},
{
"path": "test/bun/index.test.ts",
"chars": 988,
"preview": "import * as bun from '../../src/package-managers/bun'\nimport chaiSetup from '../helpers/chaiSetup'\nimport { testFail, te"
},
{
"path": "test/bun/package.json",
"chars": 76,
"preview": "{\n \"license\": \"MIT\",\n \"dependencies\": {\n \"ncu-test-v2\": \"^1.0.0\"\n }\n}\n"
},
{
"path": "test/bun-install.sh",
"chars": 353,
"preview": "#!/usr/bin/env bash\n# Install bun if not installed.\n# Cannot be added to devDependencies as bun currently does not work "
},
{
"path": "test/cache.test.ts",
"chars": 4274,
"preview": "import { expect } from 'chai'\nimport fs from 'fs/promises'\nimport ncu from '../src/'\nimport { CACHE_DELIMITER, resolvedD"
},
{
"path": "test/cli-options.test.ts",
"chars": 331,
"preview": "import cliOptions from '../src/cli-options'\nimport chaiSetup from './helpers/chaiSetup'\n\nchaiSetup()\n\ndescribe('cli-opti"
},
{
"path": "test/cooldown.test.ts",
"chars": 29253,
"preview": "import { expect } from 'chai'\nimport Sinon from 'sinon'\nimport ncu from '../src/'\nimport type { PackageFile } from '../s"
},
{
"path": "test/deep.test.ts",
"chars": 11128,
"preview": "import { expect } from 'chai'\nimport fs from 'fs/promises'\nimport os from 'os'\nimport path from 'path'\nimport spawn from"
},
{
"path": "test/dep.test.ts",
"chars": 11215,
"preview": "import fs from 'fs/promises'\nimport os from 'os'\nimport path from 'path'\nimport ncu from '../src/'\nimport chaiSetup from"
},
{
"path": "test/determinePackageManager.test.ts",
"chars": 7260,
"preview": "import determinePackageManager from '../src/lib/determinePackageManager'\nimport chaiSetup from './helpers/chaiSetup'\n\nch"
},
{
"path": "test/doctor.test.ts",
"chars": 12209,
"preview": "import fs from 'fs/promises'\nimport os from 'os'\nimport path from 'path'\nimport spawn from 'spawn-please'\nimport { cliOp"
},
{
"path": "test/e2e/cjs/index.js",
"chars": 934,
"preview": "/** NOTE: This script is copied into a temp directory by the e2e test and dependencies are installed from the local verd"
},
{
"path": "test/e2e/cjs/package.json",
"chars": 3,
"preview": "{}\n"
},
{
"path": "test/e2e/esm/index.js",
"chars": 924,
"preview": "/** NOTE: This script is copied into a temp directory by the e2e test and dependencies are installed from the local verd"
},
{
"path": "test/e2e/esm/package.json",
"chars": 23,
"preview": "{\n \"type\": \"module\"\n}\n"
},
{
"path": "test/e2e.sh",
"chars": 3007,
"preview": "#!/bin/bash\n\n# These can be set once the fnm permission errors are figured out\n# See: https://github.com/Schniz/fnm\n# se"
},
{
"path": "test/enginesNode.test.ts",
"chars": 1265,
"preview": "import ncu from '../src/'\nimport { Index } from '../src/types/IndexType'\nimport { VersionSpec } from '../src/types/Versi"
},
{
"path": "test/filter.test.ts",
"chars": 11800,
"preview": "import fs from 'fs/promises'\nimport path from 'path'\nimport spawn from 'spawn-please'\nimport ncu from '../src'\nimport { "
},
{
"path": "test/filterResults.test.ts",
"chars": 1832,
"preview": "import { expect } from 'chai'\nimport fs from 'fs/promises'\nimport os from 'os'\nimport path from 'path'\nimport ncu from '"
},
{
"path": "test/filterVersion.test.ts",
"chars": 4544,
"preview": "import path from 'path'\nimport spawn from 'spawn-please'\nimport ncu from '../src'\nimport chaiSetup from './helpers/chaiS"
},
{
"path": "test/format.test.ts",
"chars": 8323,
"preview": "import { expect } from 'chai'\nimport fs from 'fs/promises'\nimport os from 'os'\nimport path from 'path'\nimport spawn from"
},
{
"path": "test/getAllPackages.test.ts",
"chars": 9454,
"preview": "import path from 'path'\nimport getAllPackages from '../src/lib/getAllPackages'\nimport { Options } from '../src/types/Opt"
},
{
"path": "test/getCurrentDependencies.test.ts",
"chars": 9345,
"preview": "import { SemVer } from 'semver-utils'\nimport getCurrentDependencies from '../src/lib/getCurrentDependencies'\nimport { Pa"
},
{
"path": "test/getEnginesNodeFromRegistry.test.ts",
"chars": 1056,
"preview": "import { chalkInit } from '../src/lib/chalk'\nimport getEnginesNodeFromRegistry from '../src/lib/getEnginesNodeFromRegist"
},
{
"path": "test/getIgnoredUpgradesDueToEnginesNode.test.ts",
"chars": 1704,
"preview": "import getIgnoredUpgradesDueToEnginesNode from '../src/lib/getIgnoredUpgradesDueToEnginesNode'\nimport chaiSetup from './"
},
{
"path": "test/getIgnoredUpgradesDueToPeerDeps.test.ts",
"chars": 3438,
"preview": "import getIgnoredUpgradesDueToPeerDeps from '../src/lib/getIgnoredUpgradesDueToPeerDeps'\nimport { Packument } from '../s"
},
{
"path": "test/getInstalledPackages.test.ts",
"chars": 283,
"preview": "import getInstalledPackages from '../src/lib/getInstalledPackages'\n\n// test getInstalledPackages since we cannot test ru"
},
{
"path": "test/getPeerDependenciesFromRegistry.test.ts",
"chars": 1130,
"preview": "import { chalkInit } from '../src/lib/chalk'\nimport getPeerDependenciesFromRegistry from '../src/lib/getPeerDependencies"
},
{
"path": "test/getPreferredWildcard.test.ts",
"chars": 1882,
"preview": "import getPreferredWildcard from '../src/lib/getPreferredWildcard'\nimport chaiSetup from './helpers/chaiSetup'\n\nconst sh"
},
{
"path": "test/getRepoUrl.test.ts",
"chars": 2799,
"preview": "import getRepoUrl from '../src/lib/getRepoUrl'\nimport chaiSetup from './helpers/chaiSetup'\n\nconst should = chaiSetup()\n\n"
},
{
"path": "test/github-urls.test.ts",
"chars": 1953,
"preview": "import ncu from '../src'\nimport chaiSetup from './helpers/chaiSetup'\n\nchaiSetup()\n\ndescribe('github urls', () => {\n it("
},
{
"path": "test/global.test.ts",
"chars": 498,
"preview": "import { expect } from 'chai'\nimport path from 'path'\nimport spawn from 'spawn-please'\nimport chaiSetup from './helpers/"
},
{
"path": "test/group.test.ts",
"chars": 5510,
"preview": "import fs from 'fs/promises'\nimport os from 'os'\nimport path from 'path'\nimport spawn from 'spawn-please'\nimport { Group"
},
{
"path": "test/helpers/chaiSetup.ts",
"chars": 404,
"preview": "import chai from 'chai'\nimport chaiAsPromised from 'chai-as-promised'\nimport chaiString from 'chai-string'\n\n/** Global c"
},
{
"path": "test/helpers/doctorHelpers.ts",
"chars": 5762,
"preview": "import fs from 'fs/promises'\nimport path from 'path'\nimport spawn from 'spawn-please'\nimport { PackageManagerName } from"
},
{
"path": "test/helpers/removeDir.ts",
"chars": 992,
"preview": "import fs from 'fs/promises'\n\n/**\n * Helper function to remove a directory while avoiding errors like:\n * Error: EBUSY: "
},
{
"path": "test/helpers/stubVersions.ts",
"chars": 1022,
"preview": "import sinon from 'sinon'\nimport * as npmPackageManager from '../../src/package-managers/npm'\nimport { MockedVersions } "
},
{
"path": "test/index.test.ts",
"chars": 10881,
"preview": "import fs from 'fs/promises'\nimport os from 'os'\nimport path from 'path'\nimport ncu from '../src/'\nimport chaiSetup from"
},
{
"path": "test/install.test.ts",
"chars": 7981,
"preview": "/* eslint-disable @typescript-eslint/no-unused-expressions */\n// eslint doesn't like .to.be.false syntax\nimport { expect"
},
{
"path": "test/interactive.test.ts",
"chars": 5410,
"preview": "import fs from 'fs/promises'\nimport os from 'os'\nimport path from 'path'\nimport spawn from 'spawn-please'\nimport chaiSet"
},
{
"path": "test/isUpgradeable.test.ts",
"chars": 1125,
"preview": "import isUpgradeable from '../src/lib/isUpgradeable'\nimport chaiSetup from './helpers/chaiSetup'\n\nchaiSetup()\n\ndescribe("
},
{
"path": "test/package-managers/deno/index.test.ts",
"chars": 3640,
"preview": "import fs from 'node:fs/promises'\nimport os from 'node:os'\nimport path from 'node:path'\nimport spawn from 'spawn-please'"
},
{
"path": "test/package-managers/npm/index.test.ts",
"chars": 1746,
"preview": "import * as npm from '../../../src/package-managers/npm'\nimport chaiSetup from '../../helpers/chaiSetup'\n\nchaiSetup()\n\nd"
},
{
"path": "test/package-managers/npm/package.json",
"chars": 72,
"preview": "{\n \"license\": \"MIT\",\n \"dependencies\": {\n \"express\": \"^1.0.0\"\n }\n}\n"
},
{
"path": "test/package-managers/yarn/default/package.json",
"chars": 106,
"preview": "{\n \"license\": \"MIT\",\n \"packageManager\": \"yarn@1.22.22\",\n \"dependencies\": {\n \"chalk\": \"^3.0.0\"\n }\n}\n"
},
{
"path": "test/package-managers/yarn/index.test.ts",
"chars": 5743,
"preview": "import path from 'path'\nimport * as yarn from '../../../src/package-managers/yarn'\nimport { getPathToLookForYarnrc } fro"
},
{
"path": "test/package-managers/yarn/nolockfile/package.json",
"chars": 70,
"preview": "{\n \"license\": \"MIT\",\n \"dependencies\": {\n \"chalk\": \"^3.0.0\"\n }\n}\n"
},
{
"path": "test/package-managers/yarn/v4/package.json",
"chars": 104,
"preview": "{\n \"license\": \"MIT\",\n \"packageManager\": \"yarn@4.9.2\",\n \"dependencies\": {\n \"chalk\": \"^3.0.0\"\n }\n}\n"
},
{
"path": "test/parseJson.test.ts",
"chars": 2113,
"preview": "import parseJson from '../src/lib/utils/parseJson'\nimport chaiSetup from './helpers/chaiSetup'\n\nchaiSetup()\n\ndescribe('p"
},
{
"path": "test/peer.test.ts",
"chars": 4801,
"preview": "import path from 'path'\nimport ncu from '../src/'\nimport { Packument } from '../src/types/Packument'\nimport chaiSetup fr"
},
{
"path": "test/queryVersions.test.ts",
"chars": 10364,
"preview": "import queryVersions from '../src/lib/queryVersions'\nimport chaiSetup from './helpers/chaiSetup'\nimport stubVersions fro"
},
{
"path": "test/rc-config.test.ts",
"chars": 15199,
"preview": "import fs from 'fs/promises'\nimport os from 'os'\nimport path from 'path'\nimport spawn from 'spawn-please'\nimport chaiSet"
},
{
"path": "test/registryType.test.ts",
"chars": 2256,
"preview": "import ncu from '../src/index'\nimport chaiSetup from './helpers/chaiSetup'\n\nchaiSetup()\n\ndescribe('staticRegistry', func"
},
{
"path": "test/rejectVersion.ts",
"chars": 3379,
"preview": "import ncu from '../src'\nimport chaiSetup from './helpers/chaiSetup'\nimport stubVersions from './helpers/stubVersions'\n\n"
},
{
"path": "test/target.test.ts",
"chars": 16578,
"preview": "import ncu from '../src/'\nimport { FilterFunction } from '../src/types/FilterFunction'\nimport { Index } from '../src/typ"
},
{
"path": "test/test-data/basic/package.json",
"chars": 45,
"preview": "{\n \"license\": \"MIT\",\n \"dependencies\": {}\n}\n"
},
{
"path": "test/test-data/deep-ncurc/.ncurc.js",
"chars": 49,
"preview": "module.exports = {\n reject: ['cute-animals'],\n}\n"
},
{
"path": "test/test-data/deep-ncurc/package.json",
"chars": 104,
"preview": "{\n \"license\": \"MIT\",\n \"dependencies\": {\n \"cute-animals\": \"^0.1.0\",\n \"fp-and-or\": \"^0.1.0\"\n }\n}\n"
},
{
"path": "test/test-data/deep-ncurc/pkg/sub1/.ncurc.js",
"chars": 46,
"preview": "module.exports = {\n reject: ['fp-and-or'],\n}\n"
},
{
"path": "test/test-data/deep-ncurc/pkg/sub1/package.json",
"chars": 125,
"preview": "{\n \"dependencies\": {\n \"cute-animals\": \"^0.1.0\",\n \"fp-and-or\": \"^0.1.0\",\n \"ncu-test-return-version\": \"^0.1.0\"\n "
},
{
"path": "test/test-data/deep-ncurc/pkg/sub2/.ncurc.js",
"chars": 49,
"preview": "module.exports = {\n reject: ['cute-animals'],\n}\n"
},
{
"path": "test/test-data/deep-ncurc/pkg/sub2/package.json",
"chars": 113,
"preview": "{\n \"dependencies\": {\n \"cute-animals\": \"^0.1.0\",\n \"fp-and-or\": \"^0.1.0\",\n \"ncu-test-v2\": \"^0.1.0\"\n }\n}\n"
},
{
"path": "test/test-data/deep-ncurc/pkg/sub2/sub21/.ncurc.js",
"chars": 46,
"preview": "module.exports = {\n reject: ['fp-and-or'],\n}\n"
},
{
"path": "test/test-data/deep-ncurc/pkg/sub2/sub21/package.json",
"chars": 125,
"preview": "{\n \"dependencies\": {\n \"cute-animals\": \"^0.1.0\",\n \"fp-and-or\": \"^0.1.0\",\n \"ncu-test-return-version\": \"^0.1.0\"\n "
},
{
"path": "test/test-data/deep-ncurc/pkg/sub2/sub22/package.json",
"chars": 113,
"preview": "{\n \"dependencies\": {\n \"cute-animals\": \"^0.1.0\",\n \"fp-and-or\": \"^0.1.0\",\n \"ncu-test-v2\": \"^0.1.0\"\n }\n}\n"
},
{
"path": "test/test-data/deep-ncurc/pkg/sub3/package.json",
"chars": 113,
"preview": "{\n \"dependencies\": {\n \"cute-animals\": \"^0.1.0\",\n \"fp-and-or\": \"^0.1.0\",\n \"ncu-test-v2\": \"^0.1.0\"\n }\n}\n"
},
{
"path": "test/test-data/deep-ncurc/pkg/sub3/sub31/.ncurc.js",
"chars": 46,
"preview": "module.exports = {\n reject: ['fp-and-or'],\n}\n"
},
{
"path": "test/test-data/deep-ncurc/pkg/sub3/sub31/package.json",
"chars": 125,
"preview": "{\n \"dependencies\": {\n \"cute-animals\": \"^0.1.0\",\n \"fp-and-or\": \"^0.1.0\",\n \"ncu-test-return-version\": \"^0.1.0\"\n "
}
]
// ... and 38 more files (download for full content)
About this extraction
This page contains the full source code of the raineorshine/npm-check-updates GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 238 files (755.2 KB), approximately 205.9k tokens, and a symbol index with 218 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.