Over the holidays I’ve been updating F# unit conversion code so that I can regenerate the unit converters for different languages/technologies, hence I now have F# unit conversion, one for C# (removes the dependency on FSharp lib and written more in a C# idiomatic way), then I created SwiftUnits for Swift and finally (for the moment) TypeScript named unit-conversions (I might look to rename in it keeping with the other libs).
So next up I wanted to get this code packaged and available via the preferred package manager, hence F# and C# are now on NuGet, Swift isn’t on a package site but can be used as a package via it’s github location, so finally I wanted to get the TypeScript code packaged and deployed to NPM and that’s what this post discusses…
Getting Started
- First off you’ll need to have a NPM account, so if you’ve not got one, jump over to https://www.npmjs.com/ and create one.
- Next, create a folder for your package and then run yarn init (or the npm equivalent) and tsc –init
- Create an index.ts file in the same folder as your package.json – this will basically export all parts from our library to the user
- Create src folder off of the root folder – this will contain the TypeScript source for our package/lib
We’ve got the barebones in place, now we can start to code and configure our package. Add the source for your package into the src folder and then in the index.ts file just export the parts you want, so for example src for my unit-conversions package contains the TS files (my library code) and the index.ts has exports from my src files, like this
export { Angle } from './src/Angle'; export { Area } from './src/Area'; // etc.
We’re not going to waste time looking at the source for this package as it’s available on github from the previously mentioned link.
package.json requirements
Let’s get to the package.json configuration. We’ll need a name for the package, now I wanted to use a namespace, hence my package name is @putridparrot/conversion-units. Next up we’ll need a version, I’m classing my code as a beta release, hence using 0.1.x where x is the latest build. You’ll usually give your package a description, keywords etc. Importantly we’ll need a main and typings entry to allow the developer using the package an entry point as well as TypeScripts typings/definitions.
Here’s my current package.json as an example
{ "name": "@putridparrot/conversion-units", "version": "0.1.3", "description": "Units of measure converters package", "main": "dist/index.js", "typings": "dist/index.d.ts", "keywords": [ "units", "conversion", "convert", "unit of measure" ], "repository": { "type": "git", "url": "https://github.com/putridparrot/unit-conversions" }, "scripts": { "build": "tsc" }, "devDependencies": { "typescript": "^4.1.3" }, "author": "PutridParrot", "license": "MIT" }
In my case we’ll build the code to the output directory dist (so if you change this, often lib is used for example, you’ll need to change the folder in the main and typings entries of the package.json).
tsconfig.json
I’m just going to list the current tsconfig.json from my project below
{ "compilerOptions": { "target": "es5", "module": "commonjs", "declaration": true, "outDir": "dist", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "rootDir": ".", }, "exclude": ["node_modules", "dist"] }
The above is a clean up version of the tsconfig.json that is generated via tsc –init but the basics are pretty obvious, the main additions I made were to explicitly state the outDir. Without this the generated JavaScript files will go into the src folder and we don’t want that. Also I added the rootDir and the exclude section. All should be pretty self-explanatory but if not, have a read of Intro to the TSConfig Reference.
Packaging
Assuming your code builds/transpiles using tsc then once that’s run we should have a dist folder with the .d.ts and .js files. Now it’s time to push this package to NPM.
From bash or your preferred terminal the following just to ensure you’re logged into NPM
npm whoami
If you’re not logged in or are logged into the wrong account (if you’re running multiple accounts) then run
npm login
Note: Remember if you’ve setup 2FA in NPM then you’ll be prompted to use your authorizer app. to supply the “one-time” pass code.
Now if you have a paid account you might publish a private package in which case just run
npm publish
But if you do not have a private package option or simply want to make the package public then simply run
npm publish --access public
You will get asked for the passcode again (if 2FA is enabled), but once supplied the package will become available on the NPM site.
If you also have the same code committed to github then you’ll probably have a README.md and this will also be uploaded and available on the package page of NPM.
Publish only what you need to
One thing you may find is that the publish command will actually publish pretty much everything to NPM (it automatically excludes node_modules and some other files, see The .npmignore File for information on what’s ignored by default).
This means that when you install a package to your application you’re getting all the files that make up that published package – so for example my initial published package include my .github folder.
As you can probably deduce from the link above, to filter out files/folders that you do not want/need as part of your published package, you can add the file .npmignore which basically takes the same format entries as .gitignore. So from this we might have something like this
tests coverage .github src/
In this case I don’t want to publish the tests, coverage for github workflow. Also I remove the src as ultimately the package will simply use the dist/src anyway.