This github repository explains how to create a node cli tool with typescript and publish it as a npm (yarn) package.
mkdir hello-ts-cli
cd hello-ts-cli
yarn init -y
To use typescript, let's install typescript
.
yarn add typescript --dev
https://www.typescriptlang.org/download
Make src
folder and cli.ts
inside it.
src/cli.ts
#!/usr/bin/env node
console.log("Hello World");
Let's check basic typescript command.
https://www.typescriptlang.org/docs/handbook/compiler-options.html
tsc
is the command to compile the Typescript code.
yarn tsc src/cli.ts
This command creates src/cli.js
.
Automatically compile ts
files when you make a change.
tsc src/index.ts --watch
https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
- Specify compile options
- Indicates that the directory is the root of a TypeScript project
- You can specify which files to compile and which files to exclude (you don't need to specify each files to compile).
yarn tsc --init
to create the tsconfig.json
file.
📝In the beginning, you may not know what settings to use, so let's extend the recommended settings.
This time, we will use the recommended settings for node14
.
https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#tsconfig-bases
terminal
yarn add -D @tsconfig/node14
Then,
tsconfig.json
{
"extends": "@tsconfig/node14/tsconfig.json",
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
Set the rootDir
to src
since ts
files are only stored under the src
directory.
tsconfig.json
{
"extends": "@tsconfig/node14/tsconfig.json",
"compilerOptions": {
"rootDir": "src",
},
"include": ["src/**/*"],
"exclude": ["node_modules"],
}
Ref: https://www.typescriptlang.org/tsconfig#rootDir
- Change the output destination of the compiled
.ts
file tolib
. - Also, add exclude
lib
intsconfig.json
.
{
"extends": "@tsconfig/node14/tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib",
},
"include": ["src/**/*"],
"exclude": ["node_modules", "lib"],
}
Ref: Naming Conventions of lib
If you compile the file in this state, you will get the following error.
terminal
$ yarn tsc # thanks to `tsconfig.json`, you don't need to specify target ts files now.
Cannot find name 'console'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
1 console.log("Hello World");
~~~~~~~
This is caused by the lack of node
types. So, you can add
yarn add -D @types/node
Ref: microsoft/TypeScript#9545
package.json
{
"name": "hello-ts-cli",
"version": "1.0.0",
"main": "lib/cli.js",
"bin": {
"hello-ts-cli": "lib/cli.js"
},
"license": "MIT",
"devDependencies": {
"@tsconfig/node14": "^1.0.0",
"@types/node": "^15.6.1",
"typescript": "^4.3.2"
},
"dependencies": {
"chalk": "^4.1.1",
"commander": "^7.2.0"
}
}
main
: https://docs.npmjs.com/cli/v7/configuring-npm/package-json#main
bin
: https://docs.npmjs.com/cli/v7/configuring-npm/package-json#bin
Now we are ready to run the cli command. Let's install the current folder into npm
global.
terminal
npm i -g .
Now you can use the hello-ts-cli
command specified in package.json
.
terminal
$ hello-ts-cli
Hello World
❗Don't forget to uninstall after finishing this tutorial.
npm uninstall -g .
https://github.com/chalk/chalk
yarn add chalk
src/cli.ts
#!/usr/bin/env node
import chalk from "chalk";
console.log(chalk.blue("Hello World"));
yarn tsc
hello-ts-cli
https://github.com/tj/commander.js/
yarn add commander
I will use this example
https://github.com/tj/commander.js/#action-handler
#!/usr/bin/env node
import { Command } from "commander";
import chalk from "chalk";
const program = new Command();
program
.arguments("<name>")
.option("-t, --title <honorific>", "title to use before name")
.option("-d, --debug", "display some debugging")
.action((name, options, command) => {
if (options.debug) {
console.error("Called %s with options %o", command.name(), options);
}
const title = options.title ? `${options.title} ` : "";
console.log(chalk.blue(`Hello ${title}${name}`));
});
program.parse();
Then compile and run command with options
$ yarn tsc
$ hello-ts-cli World --title="Super"
Hello Super World
Then,
# git tagging before publish
$ git tag v1.0.0
$ git tag -l
v1.0.0
$ git push origin v1.0.0
# publish package
$ yarn publish
Change name in package.json
{
"name": "@username/hello-ts-cli",
...
}
Then
yarn publish --access public
Ref: https://docs.npmjs.com/creating-and-publishing-scoped-public-packages
You can check what files will be included in your package, by running npx npm-packlist
.
$npx npm-packlist
lib/cli.js
package.json
tsconfig.json
README.md
src/cli.ts
Ref: https://docs.npmjs.com/cli/v7/commands/npm-publish#files-included-in-package
If you put the following in package.json
, it will automatically build the typescript file when you type yarn publish
.
In package.json
...
"scripts": {
"prepare": "tsc"
},
...
Ref: https://docs.npmjs.com/cli/v7/using-npm/scripts#life-cycle-scripts
$ yarn version
...
# Type new version here
question New version: 1.0.1
yarn version
automatically creates new git tag.
$ git tag -l
v1.0.0
v1.0.1
# So, just push new version to remote
git push origin v1.0.1
# publish updated version
yarn publish
- https://developer.okta.com/blog/2019/06/18/command-line-app-with-nodejs
- https://medium.com/netscape/a-guide-to-create-a-nodejs-command-line-package-c2166ad0452e
- https://www.digitalocean.com/community/tutorials/typescript-new-project
- https://itnext.io/how-to-create-your-own-typescript-cli-with-node-js-1faf7095ef89