Over the past few days I have been learning how
apm, the Atom Package Manager
works under the hood.
apm is what you use when, in GitHub’s (relatively new)
“Atom” text editor, you go to the nice gui package installation interface under
Atom is a “hackable” text editor built on top of Chromium, using Node.js and Coffeescript. I believe they call it hackable because all the code is open source, and you can add plugins to do whatever you want. Your plugins can even be written in C++ if that’s more your style.
My goal was to figure out how
apm works, and I wasn’t sure how best to do
that. My knowledge of Node.js was minimal, and I was no expert in Coffeescript.
What I decided to do was fork the
apm GitHub repo, clone it onto my computer,
and set my local HEAD to the “initial commit”, and see if I could
understand that. The complete contents are as follows
1 2 3
At this stage I pretty confident that I understood everthing the author Kevin Sawicki was doing. Lucky for me, it seems Kevin is rather unique on GitHub’s Atom development team for having smaller commits. I can justify this by noting that on Atom-core’s list of developer contributions, he has 2x more commits than the next guy, but is not in the top 5 in terms of LOC added.
So with my head fully wrapped around the “initial commit” I moved my HEAD past the 2nd commit (a typo fix) into the first commit of any substance, “Add initial Gruntfile, binary, and ignores”. At this point there was some investigation to do.
I learned how he’s setting his default
grunt task to
compile his Coffeescript source files from the
src directory into a
directory to be generated at compile time. I learned the basics of what a
package.json is and what it’s basic fields do. And I learned how
require('path') command works by loading the
module.exports object of
either the file at the specified path, or in the dependencies, etc. as
specified here. At this point I was good to go on understanding
the first half-hour of development on this project by mister Sawicki.
Basically, I continued in this manner, covering test-driven-development and
jasmine-node, a primitive API with only two endpoints
express.js as part of a test-case, test fixtures, asynchronous vs
synchronous I/O APIs in various Node modules, and so on, and in the process
apm originally worked.
How APM originally worked
It basically just wraps the normal
npm command to pull from a different
registry set up presumably by Mr. Sawicki. An
npm registry is a CouchDB
instance, where module names, versions, and other metadata are mapped to the
At first, there was a bit of complicated code where
apm was downloading and
node-gyp itself, but eventually, these last two
were just added as dependencies. This involved an
async.waterfall which to my naive judgement about such a simple
script seemed to be a bit of overkill.
One thing I noticed was that a lot of the more complicated bits tended to
disapear over time, getting replaced by packages (e.g.
etc.) or replaced by finding a simpler way to do the same thing (like passing
the appropriate command line option to the underlying
It has felt, watching the first stage of this project come together, like I have been able to peer over the shoulder of a far superior coder as he writes what eventually has come to be one of the main selling points of the Atom text editor itself. I have learned countless lessons. It is like that tutorial that he never had the chance to write. When someone writes a tutorial they generally are nervous and keep trying to explain the same thing in different ways and it never quite makes sense. When someone is writing a significant piece of a public work, they are strictly getting down to business. Kevin may not have expected anyone to come along and piece through his thought process, but he left it out on the table anyway and I just grabbed it.
I will continue to go through this git history to find out what happens in the next chapter. I will also keep this in mind for the future as a way to find out how something was made. I’ve got my eye in particular on watching Linus write git itself.