npx allows to run a single executable file without installing a full npm package locally.
I recently implemented a static website generator that runs a single npx command to generate a full html website from markdown:
In this post we learn how to:
- Create an
npxexecutable script using Typescript - How to configure
package.jsonto publish annpxcommand - How to bundle Typescript code to a single javascript executable file
- How to publish the
npxcode to npm
Implement code to execute
The implementation is up to you 💁🏼♂️
The only requirement is adding a shebang in the first line of the executable code:
shebang: number sign and exclamation mark (
#!) at the beginning of a script.It tells the operating system (Unix) that this file is a node script.
#!/usr/bin/env node
import { Effect, Layer, LogLevel, Logger, ReadonlyArray, pipe } from "effect";
import * as Converter from "./Converter.js";
import * as Css from "./Css.js";
import * as FileSystem from "./FileSystem.js";
import * as LinkCheck from "./LinkCheck.js";
import { ChalkLogger } from "./Logger.js";
import * as SiteConfig from "./SiteConfig.js";
import * as Template from "./Template.js";When running npx the code in this entry file will be executed (bin.ts/js in this example 👆).
In the code below the final line executes the full program as a Promise and logs unexpected errors (using Effect):
#!/usr/bin/env node
const program = Effect.gen(function* (_) {
/// ...
});
const MainLive = Layer.mergeAll(
/// ...
);
const runnable = program.pipe(
Effect.provide(
/// ...
)
);
const main: Effect.Effect<never, never, void> = runnable.pipe(
Effect.catchTags({
/// ...
})
);
Effect.runPromise(main).catch(console.error); package.json configuration (bin)
Inside package.json we define the npx command to run using bin.
bin can be either:
- A single string with the name of the file to execute
- An object where the key is the name of the command and the value is the file to execute
In the example below we define a menimal command that executes the code inside ./dist/bin.js.
This will create a npx menimal command on npm:
{
"name": "menimal",
"version": "0.0.11",
"author": "Sandro Maglione",
"description": "Generate a static html-only website from markdown and css",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/SandroMaglione/menimal.git"
},
"keywords": [
"effect"
],
"main": "./dist/bin.js",
"bin": {
"menimal": "./dist/bin.js"
},
"files": [
"dist"
],
"scripts": {
"tsc": "tsc -p tsconfig.json",
"dev": "tsx src/bin.ts",
"bundle": "tsup && tsx scripts/copy-templates.ts",
"upload": "pnpm bundle && npm publish"
},
"dependencies": {
"@effect/platform": "^0.43.7",
"@effect/platform-node": "^0.42.7",
"@effect/schema": "^0.61.5",
"chalk": "^4.1.2",
"effect": "^2.2.3",
"gray-matter": "^4.0.3",
"html-minifier": "^4.0.0",
"lightningcss": "^1.23.0",
"mustache": "^4.2.0",
"node-html-parser": "^6.1.12",
"showdown": "^2.1.0"
},
"devDependencies": {
"@types/html-minifier": "^4.0.5",
"@types/mustache": "^4.2.5",
"@types/node": "^20.11.16",
"@types/showdown": "^2.0.6",
"tsup": "^8.0.1",
"tsx": "^4.7.0",
"typescript": "^5.3.3"
}
}There is more 🤩
Every week I dive headfirst into a topic, uncovering every hidden nook and shadow, to deliver you the most interesting insights
Not convinced? Well, let me tell you more about it
tsup: Bundle Typescript library
Since our script is written using Typescript, we need to bundle it to a single javascript file.
In this example I used tsup:
"devDependencies": {
"@types/html-minifier": "^4.0.5",
"@types/mustache": "^4.2.5",
"@types/node": "^20.11.16",
"@types/showdown": "^2.0.6",
"tsup": "^8.0.1",
"tsx": "^4.7.0",
"typescript": "^5.3.3"
}Inside tsup.config.ts we define the bundling configuration:
- Specify entry file (
src/bin.ts) - Specify the output format (
cjsfor Node)
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/bin.ts"],
publicDir: false,
clean: true,
minify: true,
format: ["cjs"], // 👈 Node
});Now we can simply run the tsup command:
"scripts": {
"tsc": "tsc -p tsconfig.json",
"dev": "tsx src/bin.ts",
"bundle": "tsup && tsx scripts/copy-templates.ts",
"upload": "pnpm bundle && npm publish"
},I added also a custom script
copy-templates.tsthat is run usingtsx.
This will generate a dist folder containing the bundled code (a single bin.js file):
Publish library on npm
The final step is publishing the library on npm.
You need to have a valid account on npm to be allowed to publish a package ☝️
We define an upload command that bundles the code and executes npm publish:
You will be prompted to login to your npm account. You can also run the
npm logincommand yourself.
"scripts": {
"tsc": "tsc -p tsconfig.json",
"dev": "tsx src/bin.ts",
"bundle": "tsup && tsx scripts/copy-templates.ts",
"upload": "pnpm bundle && npm publish"
},This is it!
You can now publish your own npx commands on npm. This comes handy when you want to execute some code without installing the package locally.
Check out the
menimalpackage on npm.
If you are interested to learn more, every week I publish a new open source project and share notes and lessons learned in my newsletter. You can subscribe here below 👇
Thanks for reading.
