Graphic representing a folder in a GitHub repo being sent to a node_modules folder on a computer

Using a GitHub repo directory as an npm package

Yesterday I was working on my personal site and wanted to incorporate some CSS from an npm package. The problem is that the package was huge and I only needed a very small part of it. The following is a somewhat brief explaination of how I solved it.

The problem

I think I'll have a better time explaining this if I actually explain my own problem, in the hope you have a better time matching it to your situation.

Within Ghost there are a bunch of cards that you can drop into your content, for example this callout card:

💌
Have you signed up to my newsletter? No? Here's a direct link if you're interested

Pretty cool right? However my site isn't using Ghost, well not all of it. My site is built using Eleventy and the content is sourced from Ghost, which means my site doesn't contain the CSS needed for these nice looking cards. I believe Ghost throws that CSS in for free if you're using their theming layer.

I could head on over to the Ghost GitHub repo, locate the CSS, download it and drop it into my project. But that's very manual, prone to error and overall hard to maintain. At the other end of the spectrum I could npm install the entirety of Ghost into my tiny project and extract the CSS from the package within node_modules, which is effectively like asking for a map of the moon in order to have a photo Neil Armstrong's footprint. I only want the CSS folder, why can't I only have that?

Which leads me to the question that prompts the title of this article: How do I treat a folder inside a GitHub repo like a npm dependency?

The solution

After a fair bit of searching I stumbled upon this DEV article from Naman Gupta:

Clone just the sub-folder in GIT 🔥
Background Hi there, Its Naman. I am sure that if you work with Git then you must have stu...

Here Naman is using an npm package called degit by Rich Harris which lets you pull down entire GitHub repos or directories within them. Awesome, exactly what I want to do! I'll need to use degit as part of the project itself rather than a CLI command though.

The implementation

Prerequisites: My implementation is within a project managed with npm and runs mostly in Node, so the following steps are for that.

Firstly I'm going to need degit installed in the project. Running the follwing command within my project will install it:

npm install degit

I'm also going to need the path to the directory I want within the Ghost GitHub git repo:

tryGhost/Ghost/core/frontend/src/cards/css

Effectively I want this step to behave like any other dependency in my project; be downloaded into the node_modules directory when the command npm install is used. Conveniently npm provides the hook postinstall which I can use to add a script to execute right after all the other packages installed. Here's what my package.json looks like:

{
  "scripts": {
    "build": "eleventy",
    "postinstall": "degit tryGhost/Ghost/core/frontend/src/cards/css node_modules/ghost-cards"
  },
  "dependencies": {
    "@11ty/eleventy": "^1.0.0",
    "degit": "^2.8.4"
  }
}
I've ommited most of the other npm packages I'm using for brevity

Once saved I can then run npm install and all the files within that /css directory I'm pointing to on GitHub will be downloaded into a direcotory called ghost-cards within node_modules.

So now in my main styles file (for which I'm using Sass to compile my CSS) I can write the following and pull that CSS straight in:

@import "../node_modules/ghost-cards/callout";
@import "../node_modules/ghost-cards/file";
@import "../node_modules/ghost-cards/button";
Again, I've abbreviated the code here for demonstration purposes

I get that some might see this as a somewhat fragile approach, but it works for my use case. In an ideal world someone would make that directory inside Ghost a package itself and publish it on npm. But then that would entail maintenance and overall more moving parts, and who's got time for that?

Thanks for reading! ✌🏻