Getting started
Install
npm i entry-scriptpnpm add entry-scriptyarn add entry-scriptTIP
entry-script requires Node.js 24 or later, and is ESM only.
Write a script
Extend EntryScript, implement main, and export an instance as the module's default. Take dependencies through the constructor, with the real ones as defaults:
// count-users.ts
import { EntryScript } from 'entry-script';
export class CountUsers extends EntryScript {
readonly #db: Database;
public constructor(db: Database = database) {
super();
this.#db = db;
}
public override async main([format = 'text']: string[]): Promise<void> {
await this.#db.connect();
const count = await this.#db.count();
console.log(format === 'json' ? JSON.stringify({ count }) : `${count} users`);
}
}
export default new CountUsers();Run it with node count-users.js json: main receives ['json'], the arguments after the script path.
When main runs
When Node starts, entry-script imports the script Node was started with (process.argv[1]), and calls main on its default export, if that's an EntryScript instance or subclass. Importing the module from anywhere else does nothing.
- The check is on the entry point's default export, wherever it was defined. A
bin.mjsthat only doesexport { default } from './dist/cli.js';runs the script it re-exports. - An error thrown by
mainis an unhandled rejection: Node prints it and exits with code 1. - Exactly one of
mainandstatic mainmust be implemented. Calling the other throwsMainNotImplementedError.
Test it
Import the class, pass test doubles to the constructor, and call main with the arguments to test. Nothing ran when the module was imported:
const fakeDb: Database = { connect: async () => {}, count: async () => 3 };
// Prints {"count":3}
await new CountUsers(fakeDb).main(['json']);Exporting the class
A script that needs no instance can export the class, and implement static main:
import { EntryScript } from 'entry-script';
export default class Version extends EntryScript {
public static override async main(): Promise<void> {
console.log('1.0.0');
}
}Next steps
- haywire-launcher: build the script from a dependency injection container, only when it runs.
- API reference.