@k13engineering/releasetool
v0.0.7
Published
Build and release tooling for TypeScript npm packages: position-preserving builds with per-file declarations, package.json merging and versioning
Maintainers
Readme
releasetool
Tooling for building and releasing TypeScript packages to npm.
- build: builds a TypeScript project to JavaScript and declaration files, keeping every line and column of the code where it was
- merge: merges
package.jsonwithpackage.npm.jsonfor publishing - patch-version: sets the version in
package.jsonfrom a git tag
Installation
npm install --save-dev @k13engineering/releasetoolRequires Node.js 20 or newer.
Building
releasetool build --root . --out dist/ --entry lib/index.ts --entry bin/cli.ts| Option | Description |
| ------------ | ----------------------------------------------------------------------------------- |
| --entry | Entry file, can be given multiple times. Required. |
| --out | Output directory. Required. |
| --root | Root directory, the structure below it is kept in the output. Defaults to .. |
| --tsconfig | TypeScript configuration for declarations. Defaults to tsconfig.json in the root. |
Paths are relative to the working directory. The options are the same as
for deno-node-build, so releasetool build can be used as a drop-in
replacement.
What the build does
All files reachable through relative imports from the entry files are built into the output directory:
| Source | Output |
| -------------------------------- | --------------------------------------------------------- |
| .ts, .mts | .js, .mjs with types stripped, plus .d.ts, .d.mts |
| .js, .mjs, .d.ts, .d.mts | copied, relative imports of TypeScript files rewritten |
| .json | copied unchanged |
- Positions are preserved. Types are replaced by whitespace using
ts-blank-space, and imports
such as
"./foo.ts"are rewritten to"./foo.js", which has the same length. Stack traces of the built code therefore point to the same line and column as in the TypeScript source, without source maps. - One declaration file per TypeScript file. Declarations are generated
by the TypeScript compiler for all files as one program, so types inferred
from other modules or packages resolve correctly. The project's
tsconfig.jsonis honored, except for options that control what and where to emit. - Type-only imports are followed, so declarations of files that are only imported for their types are part of the output.
- Nothing is written if the build fails.
The build does not type-check, use tsc --noEmit for that.
Limitations
- Only erasable TypeScript syntax is supported, the same subset Node.js runs
natively. Enums, namespaces with values, parameter properties,
import x = require()and<T>valueassertions are rejected with their location. Enabling theerasableSyntaxOnlycompiler option surfaces most of these in your editor already. - Only static specifiers are followed: imports, re-exports and
import()with a string literal.import.meta.resolve(),require()and computedimport()specifiers are left alone. - Imports must not leave the root directory and must include the file extension, as required by Node.js for ES modules.
Publishing
releasetool merge --local-package-json package.json --npm-package-json package.npm.json --output package.json
releasetool patch-version --package-json package.json --package-version v1.2.3merge combines both files, with fields of package.npm.json taking
precedence. patch-version expects the version with a v prefix, as in git
tags.
This separates development from publishing: package.json points main and
bin to the TypeScript sources, so the package can be run and tested
directly with Node.js, while package.npm.json holds the package metadata and
points to the built files in dist/.
A GitHub workflow publishing on every pushed tag:
- run: npm ci
- run: npm run build # e.g. releasetool build --out dist/ --entry lib/index.ts
- run: node node_modules/.bin/releasetool merge --local-package-json package.json --npm-package-json package.npm.json --output package.json
- run: node node_modules/.bin/releasetool patch-version --package-json package.json --package-version ${{ github.ref_name }}
- run: npm publishUsage from code
import { build, stripTypes, mergePackageJson, patchPackageJsonVersion, runCli } from "@k13engineering/releasetool";
// paths of entry points and tsconfig are relative to the root directory
const { files } = await build({
rootDirectory: ".",
outputDirectory: "dist",
entryPoints: ["lib/index.ts"],
});
const javaScript = stripTypes({ code: "const a: number = 1;", fileName: "a.ts" });
// "const a = 1;"
const merged = mergePackageJson({ localPackageJson, npmPackageJson });
const patched = patchPackageJsonVersion({ packageJson: merged, versionTag: "v1.2.3" });
// runs the CLI without touching the process, e.g. for wrapping it
const { exitCode } = await runCli({
args: ["build", "--out", "dist", "--entry", "lib/index.ts"],
stdout: (text) => console.log(text),
stderr: (text) => console.error(text),
});All functions throw on errors.
Development
npm ci
npm run build # builds releasetool with itself
npm run type-check
npm test # requires 100% coverage
npm run lintThe project follows node-boilerplate.
