It seems that every few years there is a new coalescing of disparate technologies that create a learning cliff slowing down progress. At the moment, for me personally, it is creating reusable typescript packages. I'm new to typescript and also I wanted to give the newly launched github packages a whirl. Here is my experience and some pitfalls I ran into. This was made all the more confusing by working across Github Organizations.
Note: If you are just looking to figure out how to publish Github packages, read this great post by Dale Nguyen on the topic.
Update: I started a template repository. You can use it as a base for your package to follow along.
This step is required to publish your package. Log in to Github and navigate to your Personal Access Tokens (Settings > Developer Settings > Personal Access Tokens)
Create a New Personal Access Token. I tend to name my tokens things like "Package Tests 2020" so I know what to revoke/rotate later. You will need the following scopes:
Once you are presented with your key, put it somewhere. You'll need it in a second and it cannot be retrieved again.
In your terminal run:
npm login --registry=https://npm.pkg.github.com
You will then be presented with prompts to enter:
If you authenticate correctly, you will see:
Logged in as <username> on https://npm.pkg.github.com/.
In the package.json file for your project you want to publish, ensure you have the following top level field (sibling to name, scripts, dependencies, etc).
"publishConfig": { "registry": "https://npm.pkg.github.com/@github-username-or-organization" }
Note: It is important here to set @github-username-or-organization to the destination the package will be published to. This should surely be the account under which your project lives. If for example, you project lives under the digibodies Organization at https://github.com/digibodies/scrapertools, then your config should be:
"publishConfig": { "registry": "https://npm.pkg.github.com/@digibodies" }
Next lets run the TypeScript compiler to populate the dist folder with our non-TypeScript transpiled code.
npm run build
or if you do not have a build directive setup
tcs
Check the contents of the dist folder and make sure they look generally alright. This is what will get published.
Finally, let's publish:
npm publish
If all goes well, you should be able to navigate to your project in github and see your new package under the packages tab. eg. https://github.com/digibodies/scrapertools/packages/
Pitfall: As you are experimenting and doing subsequent publishes, be sure to bump the version # in your package.json. Otherwise, you'll get a EPUBLISHCONFLICT error Cannot publish over existing version.
Before we get too excited, we will want to ensure that we can consume our package. For me, this was a project within a different organization, which caused some confusion. Either way, you will need to put an .npmrc file within the root of your consuming project. This informs node to also look at github packages when resolving.
This file should have one line (for now):
registry=https://npm.pkg.github.com/@github-username-or-organization
Replace @github-username-or-organization with the what you used in step 2 above. In my case, this was @digibodies
Finally, install the package into your project:
npm install --save @github-username-or-organization/package-name
If all goes well, your package should be installed! Celebrate momentarily.
Note: If you receive the error: 401 Unauthorized:@github-username-or-organization/packagename you likely need to login with your Personal Access Token again from step 1. I ran into this when working across multiple machines. you need to login on each.
Note: If you receive the error: 404 Not Found:@github-username-or-organization/packagename ensure you spelled the package name correctly and that it is indeed published.
In your consuming project, import your package:
const packagename = require('@github-username-or-organization/package-name');
or using import syntax
import packagename from '@github-username-or-organization/package-name'
In my case:
import scrapertools from '@digibodies/scrapertools';
This is one of the steps that bit me. Originally, my project contained a helper func that was exported only for unit testing purposes. The function returned a type that was part of a 3rd party package that I didn't want to have to expose as a peer dependency. However, when I went to consume my package, the TypeScript compiler blew up on the type being undefined in my index.d.ts since I didn't install the 3rd party package as part of the consuming repo. As I learn more about TypeScript nuance, I hope to understand this issue better. Either/or, ensure your stuff works before calling it a day.
I have some lingering questions I need to resolve in this post or in a followup post: