Let's Build a Thing: NPM Dependency Checker - Part 5
Series start along with dev notes: Let’s Build: NPM Dependency Checker
Previous part: Let’s Build: NPM Dependency Checker - Part 3
GitHub: NPM Dependency Checker
As of this moment, NPM Dependency Checker has some serious problems. Take a look:
- It’s parsing a repo url that may or may not be there.
- It’s then transforming that url (if there), which may be in several formats, which means we need all sorts of String.replace calls to be somewhat effective.
- It’s trying to follow a HTTP path to get more info (again, if the url is there and if we transformed it ok).
- It’s grabbing devDependencies, which aren’t going to be recursively installed. Instead it should just report the main dependencies.
The good news is that we don’t need to do any of this to get the dependencies. When we call npm view, we’re actually given the dependencies for that repo, so we can just pull our data from there and not do any web scraping.
Our new code is going to operate more simply and cleanly. Here’s how we’re going to have it operate:
- Start by using npm view for a repo.
- Grab the dependencies from that repo and put it into a pending dependencies list.
- Add the repo we just checked to a completed dependencies list.
- Recursively call a function that each time takes the first dependency off the pending list, runs npm view, puts that dependency on the completed list, scrubs the new incoming dependencies against the pending list and adds whatever is new to it.
- This process will continue until the pending list is empty and at that time we’ll report the number of dependencies a package has and write a txt file as well.
We’re going to add two configuration items in our Mix config so that our application knows where to store the final txt file.
Open up /config/config.exs/ and add the following two config statements under Use.Mix.Config:
To see all the updated code, go to this application’s GitHub and select the branch for this step.
Here are some of the highlights of the changes:
decode_body: It’s possible that the package has no dependencies or that a package is calling a bogus repo. To prevent our application from quitting, I’ve created two functions that guard against these situations.
npm_view: Because versions matter for dependencies, I’ve updated the npm_view call to include a version for the package.
display_package_information: We want to use recursion and Elixir’s pattern matching to provide a clean solution (or at least cleaner than it would have been).
I’ve created a function (get_package_information) that has three versions. It could be called with /2, /1 or /1 with a specific atom. Let’s go over each one as this is the meat of the application.
This function takes two arguments: a pending dependency list and a completed one. It’s a recursive function, so it will keep calling itself.
When this function is executed it will take the first element in the pending_dependency_list and run a pipe of functions on it. The result is repo, which ends up being a list of dependencies.
We then do a bit of scrubbing to see what incoming dependencies are actually new and then we add them to the pending_dependency_list.
We also add the used package for this call to the complete_dependency_list.
At this point update_pending is a list of what’s left to do. If that list has more to do, the function calls itself with /2 again. If not, it calls itself with just the complete_dependency_list.
It’s possible that a user starts the application with a package that doesn’t exist. Rather than creating a differently named function, I’m just using pattern matching.
When the atom :error404 is passed to get_package_information, the user is informed that nothing was found.
Finally, if only one argument is passed into get_package_information and it isn’t the error Atom, it means we’re all done processing and we need to display information.
When this function is called, it outputs all the dependencies to the screen along with notices displaying the dependency count and location of the saved file.
There was a lot of refactor work on this application so let’s do a test and make sure it’s working before we proceed.
Sweet! We have it working and really we’re about done. There’s some cleanup to do and maybe we add a feature or two. For now, let’s call it a day and regroup for the final part.
I encourage you to look up the code on GitHub to make sure you have the latest.
Expect the final part by the end of September 2016.