5 things to know when writing a TypeScript NPM module
https://unsplash.com/photos/GopRYASfsOc
Photo by https://unsplash.com/photos/GopRYASfsOc
(Outdated) tips on building a npm module from scratch
Disclaimer: This article was written in 2016, most information are outdated.

 

 

I recently started a library named spotify-graphql in TypeScript.
I wanted the library to be “importable” in Node.js(v6) and TypeScript.
Here are all the things to know that will save you hours.

 

 

Proposed Package structure

1- package.json
2- gulpfile.js
3- lib/myFile.ts 
4- spec/myFile.spec.ts
5- index.ts
6- tsconfig.json

 

1. package.json

1{  
2  "name": "my-package",  
3  "version": "0.0.0",  
4  //...  
5  "main": "dist/index.js", // for import from nodejs environments  
6  "typings": "definitions/index", // TypeScript definitions  
7  "typescript": {  
8    "definition": "definitions/index" // TypeScript definitions  
9  },  
10  //...  
11  "devDependencies": {  
12    "@types/node": "~6.0.46", // needed for Node.js targeting  
13    //...  
14  },  
15  "scripts": {  
16    "test": "gulp test",  
17    "release": "standard-version" // amazing package, seriously !  
18  },  
19  //...  
20  "dependencies": {  
21    "@types/jasmine": "^2.5.38" // need for using jasmine in TS !  
22  }  
23}

 

2. tsconfig.json

1{  
2  "compilerOptions": {  
3    "lib": ["es6"], // we target Node.js(v6)  
4    "module": "commonjs", // export compatibility  
5    "target": "es5", // we target Node.js(v6)  
6    "moduleResolution": "node", // we target Node.js(v6)  
7    "declaration": true, // generate TypeScript definitions  
8    "rootDir": ".",  
9    "outDir": "dist", // transpile JS in this directory  
10    "types" : ["node", "jasmine"] // only use needed types  
11  },  
12  "include": [  
13    "index.ts",  
14    "lib/**/*",  
15    "spec/**/*"  
16  ]  
17}

 

3. gulpfile.js

The gulpfile is dead-simple.
It just uses:
  • gulp-typescript
  • gulp-jasmine
1gulp.task('build', function() {  
2    const merge = require('merge2');  
3    const tsProject = ts.createProject('tsconfig.json');
4
5var tsResult = tsProject.src()  
6        .pipe(tsProject());
7
8return merge(\[  
9        tsResult.dts.pipe(gulp.dest('./definitions')),  
10        tsResult.js.pipe(  
11          gulp.dest(tsProject.config.compilerOptions.outDir)  
12        )  
13    \]);  
14});
the build task fetches the tsconfig.json compilerOptions and generate 2 things :
  • transpiled JS in outDir
  • TypeScript definitions in ./definitions dir
    (only needed if tsconfig.json specify definitions: true)

 

4. the “index.ts story”

When doing the following :
1import * from 'my-package'
tsc will start to search for a index.ts in node_modules and all possible directories.
I found that by running tsc with — traceResolution option.
This option print all the path looked by tsc during module resolution, this is very helpful when a NPM module is “missing”.
So, when creating a TypeScript NPM module, remember to place a index.ts file a the root of your structure

 

5. Testing in TypeScript

In order to use Jasmine with TypeScript, you’ll need 2 things :
  • import Jasmine typings
  • transpile code to JS before running your specs
For the typing, see the “package.json” section.
Transpiling code before running the specs is done in the gulpfile, let’s take a glance :
1gulp.task('test:run', function() {  
2   return gulp.src('dist/spec/\*\*').pipe(jasmine())  
3});  
4// ...  
5gulp.task('test', \[\], function(cb) {  
6   runSequence('clean', 'build', 'test:run', cb);  
7});
As seen in “gulpfile.js” section, the build task transpile all JS (as specified in tsconfig.json) and store it in dist/ folder.
All we have to do is to send file located in dist/spec/ to jasmine, and “tada !”
I’ve made a public repo for bootstraping a TypeScript package :
https://github.com/charlypoly/typescript-npm-module-bootstrap/