How I wrote a package manager in JavaScript
Tags: javascript, linux, packages
Published: 2024-04-18 13:00:00

---
title: How I wrote a package manager in JavaScript
published: true
description: In this post, I will show you, how I wrote a decent package manager in JavaScript
tags: #javascript #linux #packages
cover_image: https://images.unsplash.com/photo-1616401784845-180882ba9ba8?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&dl=petrebels-JwMGy1h-JsY-unsplash.jpg
published_at: 2024-04-18 13:00
---

Firstly, you will probably be asking yourself: Why did he do this?
I have been working on a project called Zentrox for a few weeks by now. Zentrox is a server administration tool, that gives you a dashboard in the web browser.
The dashboard should include information about the storage, the users, file sharing protocols and the installed packages.

The first step to creating this package manager was figuring out, how I was going to do this. So, I wrote some boilerplate HTML code and some CSS for the front-end. At this time, I already had an Express.js server and some front-end code.
Still, I didn't know, how to manage the packages and what I actually wanted to do in detail. So I chose to write the following functions:
- listing all packages in the repositories
- listing all packages on the system
- installing a package
- removing a package
- getting an icon for an installed package

Then, I went on Google and looked for things like “JavaScript package manager”, “Package manager for JavaScript” or “DNF API Python”. The only one that really lead to a result (that is not NPM, …) was the DNF API for Python. I tried to write a small python script, but it didn't work. Also, I didn't find an API for other package managers like DNF or pacman.
So, I started by writing a JavaScript, that uses `exec` to run some commands. One command was (and still is): `dnf list`. The problem with this was, that I didn't know the result and also did not want to write some complicated async or callback code.

While browsing the Node documentation, I found a function named `execSync`. This function does exactly what I want. It executes the command in a synchronous order.

Now, the only thing stopping me from doing the first four tasks was figuring out the commands to install, remove and list packages under apt, DNF and pacman.
Those were pretty easy to figure out. Then I wrote a condition that detects which distro the manager is running on. 

After I tested it on my one system, it turned out to be working just fine.

So, the next step was adding support for icons of installed packages.

I did some research and figured out, that packages that have an icon, probably also have a `.desktop` file in `/usr/share/applications`. This file will then contain a name, an icon, and the path to the binary.

With this new knowledge, I wrote a script that reads the name and icon path of an application from its desktop file.

Some packages do not include an icon in the corresponding  desktop file, though. Nonetheless, every desktop file will contain a name and execution path. These can be used to get an icon from the installed icon pack on the system.

To get to these icons, I first need to know where they all are. Most of the time, the location for icons on Linux is `/home/username/.icons` or `/local/share/icons`.

Now, I just had to write a script that reads these folders, selects a pack, looks the name up and then returns the icon to me.

The problem was, that there may be an icon pack, that does not contain the icon at all. So, I also wrote some code, that detects that.

At this point, I had all the planned functions done and published it on GitHub. I also wrote about it on Reddit.

Very soon, there was one thing, that was (rightfully) criticized by several Redditors. I did not sanitize the sudo password required to install and remove packages.

I added a few `.replaceAll` to the code. Then it was just fine. After connecting it with the front-end and testing it on a Manjaro and Debian distro, I wanted to call it a day. 

The issue was, that it was terribly slow. So, instead of running the command used to list all available packages every time, when the app is opened, I made a background worker, that caches it to another file in an interval of 1h.

So, every time the package manager is loaded, the cached data is used. Now, the only this that is slowing it down is the command used to list installed packages.

The last thing to do was integrating it into my project.
I added a section called “Packages” in my Zentrox dashboard.
You can find it here: [wervice/zentrox](https://www.github.com/wervice/zentrox)

The final product looks like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/urbyve67rj56i5tpqnuc.png)

If you want to, you can install Zentrox on your system and test the package manager. 

Please tell me, if you spotted a problem.