674

Is there a way to determine if you have packages in your package.json file that are no longer needed?

For instance, when trying out a package and later commenting or deleting code, but forgetting to uninstall it, I end up with a couple packages that could be deleted.

What would be an efficient way to determine if a package could safely be deleted?

0

14 Answers 14

839

You can use an npm module called depcheck (requires at least version 10 of Node).

  1. Install the module:

     npm install depcheck -g
    
     or
    
     yarn global add depcheck
    
  2. Run it and find the unused dependencies:

     depcheck
    

The good thing about this approach is that you don't have to remember the find or grep command.

To run without installing use npx:

npx depcheck 
22
  • 11
    depcheck-es6 is now merged into depcheck Commented Jan 4, 2016 at 0:27
  • 143
    doesnt look useful. I am using the standard angular2 cli setup and depcheck lists every package as unused which is just wrong
    – phil294
    Commented Feb 10, 2017 at 19:07
  • 14
    NB. depcheck doesn't take into account packages used in scripts specified in package.json Commented Oct 2, 2018 at 15:02
  • 40
    To run it just once (w/o installation) - use npx: npx depcheck
    – Kiril
    Commented Nov 7, 2018 at 17:59
  • 34
    Didn't work for me. It listed all the packages as unused.
    – dev27
    Commented May 20, 2019 at 22:20
201

There is also a package called npm-check:

npm-check

Check for outdated, incorrect, and unused dependencies.

enter image description here

It is quite powerful and actively developed. One of it's features it checking for unused dependencies - for this part it uses the depcheck module mentioned in the other answer.

4
  • 19
    Seems to give me the same results as depcheck. It looks like it even uses depcheck to find the unused dependencies.
    – Alex K
    Commented Sep 14, 2018 at 16:55
  • 6
    npm outdatedchecks and lists current, wanted and latest package versions. No list of unused packages though.
    – mgarde
    Commented Oct 25, 2018 at 11:34
  • 6
    doesnt look useful as well. I am using the standard angular setup and this also lists every package as unused which is just as wrong Commented Jun 27, 2019 at 17:00
  • 1
    Seems a bit outdated now. It includes high severity vulnerabilities right now...
    – jjmerelo
    Commented Jul 22, 2021 at 6:14
77

Check the unused dependencies

npm install depcheck -g
depcheck

enter image description here

Check the outdated library

npm outdated

enter image description here

1
  • 2
    depcheck doesn't seem to work for Vue3 single file components. If flags dependency as unused that is clearly used in the app. Do you know any such package that works with vue?
    – Fred
    Commented Nov 12, 2022 at 10:29
18

The script from gombosg is much better then npm-check.
I have modified a little bit, so devdependencies in node_modules will also be found.
example sass never used, but needed in sass-loader

#!/bin/bash
DIRNAME=${1:-.}
cd $DIRNAME

FILES=$(mktemp)
PACKAGES=$(mktemp)

# use fd
# https://github.com/sharkdp/fd

function check {
    cat package.json \
        | jq "{} + .$1 | keys" \
        | sed -n 's/.*"\(.*\)".*/\1/p' > $PACKAGES
    echo "--------------------------"
    echo "Checking $1..."
    fd '(js|ts|json)$' -t f > $FILES
    while read PACKAGE
    do
        if [ -d "node_modules/${PACKAGE}" ]; then
            fd  -t f '(js|ts|json)$' node_modules/${PACKAGE} >> $FILES
        fi
        RES=$(cat $FILES | xargs -I {} egrep -i "(import|require|loader|plugins|${PACKAGE}).*['\"](${PACKAGE}|.?\d+)[\"']" '{}' | wc -l)

        if [ $RES = 0 ]
        then
            echo -e "UNUSED\t\t $PACKAGE"
        else
            echo -e "USED ($RES)\t $PACKAGE"
        fi
    done < $PACKAGES
}

check "dependencies"
check "devDependencies"
check "peerDependencies"

Result with original script:

--------------------------
Checking dependencies...
UNUSED           jquery
--------------------------
Checking devDependencies...
UNUSED           @types/jquery
UNUSED           @types/jqueryui
USED (1)         autoprefixer
USED (1)         awesome-typescript-loader
USED (1)         cache-loader
USED (1)         css-loader
USED (1)         d3
USED (1)         mini-css-extract-plugin
USED (1)         postcss-loader
UNUSED           sass
USED (1)         sass-loader
USED (1)         terser-webpack-plugin
UNUSED           typescript
UNUSED           webpack
UNUSED           webpack-cli
USED (1)         webpack-fix-style-only-entries

and the modified:

Checking dependencies...
USED (5)         jquery
--------------------------
Checking devDependencies...
UNUSED           @types/jquery
UNUSED           @types/jqueryui
USED (1)         autoprefixer
USED (1)         awesome-typescript-loader
USED (1)         cache-loader
USED (1)         css-loader
USED (2)         d3
USED (1)         mini-css-extract-plugin
USED (1)         postcss-loader
USED (3)         sass
USED (1)         sass-loader
USED (1)         terser-webpack-plugin
USED (16)        typescript
USED (16)        webpack
USED (2)         webpack-cli
USED (2)         webpack-fix-style-only-entries
5
  • 4
    Adding -P 32 switch to your xargs will result in a huge speedup. Commented Aug 17, 2021 at 20:54
  • 1
    Best solution compared to depcheck and derivatives. Adding --max-procs|-P 32 greatly improves the speed. Commented Sep 2, 2021 at 5:53
  • Great script that nicely extended the orginal one, but it got unusable slow (even xargs -P options) on a large react app. Re-organized file searches and shared a version that should produce same output, but not necessary in the same order.
    – Manwe
    Commented Oct 25, 2021 at 12:44
  • I had import { Request, Response } from 'express'; in some files and in package.json I had "@types/express": "^4.17.13" This script reported "@types/express": "^4.17.13" as unused
    – ontherocks
    Commented Jul 3, 2023 at 15:16
  • Link to @Manwe's modified script for future readers: stackoverflow.com/a/69708249/3554391
    – MHebes
    Commented Aug 25, 2023 at 19:24
18

Many of the answer here are how to only find unused items. What if... I wanted to AUTOmatically -- a) find + b) Remove the unused items?

Option 2, below seems to be the newer way.


Option 1:

  1. Install this node project.
 $ npm install -g typescript tslint tslint-etc

  1. At the root dir, add a new file tslint-imports.json
{
  "extends": [
    "tslint-etc"
  ],
  "rules": {
    "no-unused-declaration": true
  }
}

  1. Run this at your own risk, make a backup :)
$ tslint --config tslint-imports.json --fix --project .

Option 2 (per @Alex & @JacopKane suggestions, thanks):

// newer one line option
npx depcheck --json | jq '.dependencies[]' | xargs -L1 npm rm

// or
npm uninstall $(npx depcheck --oneline)
5
  • But this is going to remove from the js files only. But ya still good. Commented May 27, 2020 at 22:55
  • 4
    how about npx depcheck --json | jq '.dependencies[]' | xargs -L1 npm rm
    – alex
    Commented May 29, 2020 at 13:19
  • 4
    tslint is deprecated as of 2019
    – AndyW
    Commented Jan 7, 2022 at 14:53
  • 2
    This one-liner command will remove all unused packages by the depcheck module: npm uninstall $(npx depcheck --oneline)
    – JacopKane
    Commented Nov 7, 2022 at 15:56
  • depcheck is highly unreliable and just generally a very poor package - it seems it's using a basic string/regex search for potential packages preceded by a colon. Some missing dependencies it thinks I need on my Windows system are D: and public:. Commented Sep 6, 2023 at 1:42
17

For checking unused dependencies, libraries and unimported files

 npx unimported 

you will get result something like that enter image description here

5
  • 2
    Unable to locate entry points for this node project. Please declare them in package.json or .unimportedrc.json
    – PirateApp
    Commented Jun 10, 2022 at 5:59
  • 1
    Worked for me, gives a lot of false-positives for 'unimported files' but the list of modules seemd fine.
    – sommmen
    Commented Sep 14, 2022 at 10:25
  • "Unable to locate entry points" error in Node 18. Is "main": "./dist/index.js", the issue here, since the dist folder isn't checked in?
    – jcollum
    Commented Jul 25, 2023 at 17:07
  • this will create false positives for dynamic imports e.g. in Next.js: const NoSSRForceGraph = dynamic(() => import("./NoSSRForceGraph"), { ssr: false, });
    – Jon Willis
    Commented Aug 21, 2023 at 18:21
  • unimported is no longer maintained as of 10.03.2024.
    – ddsultan
    Commented Jun 19 at 7:19
14

fiskeben wrote:

The downside is that it's not fully automatic, i.e. it doesn't extract package names from package.json and check them. You need to do this for each package yourself.

Let's make Fiskeben's answer automated if for whatever reason depcheck is not working properly! (E.g. I tried it with Typescript and it gave unnecessary parsing errors)

For parsing package.json we can use the software jq. The below shell script requires a directory name where to start.

#!/bin/bash
DIRNAME=${1:-.}
cd $DIRNAME

FILES=$(mktemp)
PACKAGES=$(mktemp)

find . \
    -path ./node_modules -prune -or \
    -path ./build -prune -or \
    \( -name "*.ts" -or -name "*.js" -or -name "*.json" \) -print > $FILES

function check {
    cat package.json \
        | jq "{} + .$1 | keys" \
        | sed -n 's/.*"\(.*\)".*/\1/p' > $PACKAGES

    echo "--------------------------"
    echo "Checking $1..."
    while read PACKAGE
    do
        RES=$(cat $FILES | xargs -I {} egrep -i "(import|require).*['\"]$PACKAGE[\"']" '{}' | wc -l)
        if [ $RES = 0 ]
        then
            echo -e "UNUSED\t\t $PACKAGE"
        else
            echo -e "USED ($RES)\t $PACKAGE"
        fi
    done < $PACKAGES
}

check "dependencies"
check "devDependencies"
check "peerDependencies"

First it creates two temporary files where we can cache package names and files.

It starts with the find command. The first and second line make it ignore the node_modules and build folders (or whatever you want). The third line contains allowed extensions, you can add more here e.g. JSX or JSON files.

A function will read dependendy types.

First it cats the package.json. Then, jq gets the required dependency group. ({} + is there so that it won't throw an error if e.g. there are no peer dependencies in the file.)

After that, sed extracts the parts between the quotes, the package name. -n and .../p tells it to print the matching parts and nothing else from jq's JSON output. Then we read this list of package names into a while loop.

RES is the number of occurrences of the package name in quotes. Right now it's import/require ... 'package'/"package". It does the job for most cases.

Then we simply count the number of result lines then print the result.

Caveats:

  • Won't find files in different imports e.g. tsconfig.json files (lib option)
  • You have to grep manually for only ^USED and UNUSED files.
  • It's slow for large projects - shell scripts often don't scale well. But hopefully you won't be running this many times.
1
  • 1
    Editors sometimes cause imports to wrap into multiple lines. Would this script catch statements where ‘import’ or ‘require’ would be on a different line than the ‘from “PACKAGE_NAME”’? In other words, does it ignore whitespace in import or require statements?
    – vdiaz1130
    Commented Mar 11, 2020 at 13:31
6

If you're using a Unix like OS (Linux, OSX, etc) then you can use a combination of find and egrep to search for require statements containing your package name:

find . -path ./node_modules -prune -o -name "*.js" -exec egrep -ni 'name-of-package' {} \;

If you search for the entire require('name-of-package') statement, remember to use the correct type of quotation marks:

find . -path ./node_modules -prune -o -name "*.js" -exec egrep -ni 'require("name-of-package")' {} \;

or

find . -path ./node_modules -prune -o -name "*.js" -exec egrep -ni "require('name-of-package')" {} \;

The downside is that it's not fully automatic, i.e. it doesn't extract package names from package.json and check them. You need to do this for each package yourself. Since package.json is just JSON this could be remedied by writing a small script that uses child_process.exec to run this command for each dependency. And make it a module. And add it to the NPM repo...

2
  • 2
    What about .jsx files and .ts files etc :D
    – OZZIE
    Commented Oct 18, 2019 at 11:02
  • 4
    Apparently using this approach we are not using react module in our React app :D
    – OZZIE
    Commented Oct 18, 2019 at 12:22
4

In Yarn 2.x and above, use:

yarn dlx depcheck

yarn dlx is designed to execute one off scripts that may have been installed as global packages with yarn 1.x. Managing system-wide packages is outside of the scope of yarn. To reflect this, yarn global has been removed.

Source: https://yarnpkg.com/getting-started/migration#use-yarn-dlx-instead-of-yarn-global

4

Unless I've misunderstood something about the scripts by gombosg and nMo. Here's a faster version of nMo script-extensions with defaulting to 'find', but can be easily modified to use 'fd' for find functionality.

Changes are that it first finds all relevant files and then grep packages from all relevant files on one go and not a file-by-file bases.

Concurrency can be controlled and defaults to 8.

#!/bin/bash
DIRNAME=${1:-.}
cd "$DIRNAME"

FILES=$(mktemp)
PACKAGES=$(mktemp)

export NUMCONCURRENT=8

function findCmd {
  startPath=${1:-.}
  find "$startPath" \
    -path ./node_modules -prune -or \
    -path ./build -prune -or \
    \( -name "*.ts" -or -name "*.js" -or -name "*.json" \) -print
}

# use fd
# https://github.com/sharkdp/fd
function findCmd_fd {
  startPath=${1:-.}
  fd  -t f '(js|ts|json)$' "$startPath"
}



function check {
    cat package.json \
        | jq "{} + .$1 | keys" \
        | sed -n 's/.*"\(.*\)".*/\1/p' > "$PACKAGES"
    echo "--------------------------"
    echo "Checking $1..."

    findCmd > "$FILES"
    while read PACKAGE
    do
        #echo "node_modules/${PACKAGE}"
        if [ -d "node_modules/${PACKAGE}" ]; then
                findCmd node_modules/${PACKAGE} >> $FILES
        fi
    done < $PACKAGES
    export FILES
    export SQ="'"
    xargs -P ${NUMCONCURRENT:-1} -r -a  "$PACKAGES" -I[] bash -c '
        PACKAGE="[]"

        RES=$(cat "$FILES" | xargs -r egrep -i "(import|require|loader|plugins|${PACKAGE}).*[\"${SQ}](${PACKAGE}|.?\d+)[\"${SQ}]" | wc -l)

        if [ $RES = 0 ]
        then
            echo -e "UNUSED\t\t $PACKAGE"
        else
            echo -e "USED ($RES)\t $PACKAGE"
        fi
    '
    [ -f  "$PACKAGES" ] && rm "$PACKAGES"
    [ -f  "$FILES" ] && rm "$FILES"
}

check "dependencies"
check "devDependencies"
check "peerDependencies"

6
  • This worked like a charm, thank you! I tried the other scripts (by gombosg and nMo) and they gave me some used package as unused, by yours worked well. Cheers
    – Vivi
    Commented Nov 18, 2022 at 6:29
  • Doesn't work on macos: xargs: illegal option -- a Commented Sep 21, 2023 at 19:58
  • @justFatLard Try replacint this line xargs -P ${NUMCONCURRENT:-1} -r -a "$PACKAGES" -I[] bash -c ' With: cat "$PACKAGES" | xargs -P ${NUMCONCURRENT:-1} -r -I[] bash -c '
    – Manwe
    Commented Sep 26, 2023 at 7:06
  • 1
    @Manwe - After using your replacement line it worked on macOS but now it says: xargs: command line cannot be assembled, too long
    – akxer
    Commented Nov 17, 2023 at 7:57
  • I found I need to modify the "egrep -i" to be "egrep -ia" to suppress many grep: ...: Is a directory warnings
    – MrDaniel
    Commented Feb 29 at 16:30
2

I decided for knip after doing some research and PoCs because it is a tool-set to keep the code clean and also dependencies up-to-date. Even though the tool is new, the community and fundamental direction in the architecture is going pretty well.

1

We can use the below npm module for this purpose:

https://www.npmjs.com/package/npm-check-unused

1
  • it revealed some not used ones but also used ones, still helpful I guess :-) It doesn't understand webpack loaders ;-)
    – OZZIE
    Commented Sep 2, 2019 at 13:07
1

if you want to choose upon which giant's shoulders you will stand

here is a link to generate a short list of options available to npm; it filters on the keywords unused packages

https://www.npmjs.com/search?q=unused%20packages

Why is my answer just a link?

Typically I wouldn't provide just a link. This question deserves a less time-sensitive answer. The solution relies on up-to-date software. Recommending a specific piece of software that may have stopped being maintained (the case with some of the recommendations here) is of little use. Helping people find something current seems appropriate.

0
1

I've created a package unused-package (https://www.npmjs.com/package/unused-package) which returns any unused packages or if package is devDependency but installed as normal dependecy

create a file and add this code to to your file

const check = require("unused-package");

check({ entries: ['entry path to your code'] }).then((res) => {
  console.log(res) // list of packages returned by library
});

one ADVANTAGE of usused-package over depcheck package is unused-package supports multiple entry path.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.