Amaro wraps the SWC TypeScript parser for Node's type stripping
Node.js TypeScript wrapper
At a glance
- What is it?
- Amaro is the npm package around the WebAssembly build of the SWC TypeScript parser, and it exists so that the type stripper Node.js ships internally can be updated independently of Node itself. It also works as a standalone loader, which is the more interesting way to use it.
- Who is it for?
- Amaro suits a Node project that wants to run TypeScript directly without a build step, or a team that needs a newer type stripper than the one inside its Node binary, since the external loader overrides the built in version. It is the wrong tool if you need type checking, since stripping types is not checking them, and it is not a general purpose transpiler for older targets.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A wrapper around a WebAssembly parser
Amaro is a wrapper around @swc/wasm-typescript, which is a WebAssembly port of the SWC TypeScript parser. The primary language of the repository is JavaScript, the licence is MIT, and the package description is Node.js TypeScript wrapper.
The reason the package exists at all is a version independence problem. Amaro is used internally in Node.js for type stripping, but it can also be used as a standalone package, and the stated purpose of the wrapper is to provide a stable API for the TypeScript parser and to let users upgrade to the latest version of the TypeScript transpiler independently from the one Node uses internally.
That is a real concern for anyone on an LTS release line. Node bundles a parser for its own type stripping feature, and the version it bundles is fixed when the Node release is cut. Amaro gives you a way to install a newer one next to your application and have it take precedence.
The name is explained in the README as well: Amaro means bitter in Italian, a reference to Mount Amaro, on whose slopes the package was conceived.
transformSync strips types and keeps the stack trace aligned
By default Amaro exports a transformSync function that performs type stripping. The mechanism has one detail worth understanding, because it explains the output you get:
const amaro = require('amaro');
const { code } = amaro.transformSync("const foo: string = 'bar';", { mode: "strip-only" });
console.log(code); // "const foo = 'bar';"Stack traces are preserved by replacing removed types with white spaces. The type annotation is not deleted from the source, it is replaced by spaces of the same length, so every character after the removed type keeps its original position. The output in the example shows the string annotation turned into blank padding between the identifier and the equals sign.
That is the whole trick behind line-preserving type stripping. If the type were simply deleted, every character after it would shift left by a few columns and the line and column numbers in a stack trace would point at the wrong place in the original file. Padding keeps the mapping exact.
The mode option in the example is strip-only, which is the safe mode: it removes type syntax and leaves everything else alone, which is why the transform mode is a separate loader.
The two loaders, and when the flag is no longer needed
Amaro can be used as an external loader to execute TypeScript files, and the point of doing so is that the installed Amaro overrides the Amaro version used by Node.js. Type stripping has to be enabled to use it as a loader:
node --import="amaro/strip" file.tsThe README notes that in Node v22.18.0 and later the --experimental-strip-types flag can be omitted, because it is enabled by default. That version boundary matters, since the package manifest requires Node >=22.
Enabling TypeScript feature transformation, which is the other mode, uses a different loader:
node --enable-source-maps --import="amaro/transform" file.tsThe note attached to that command is not optional guidance: the amaro/transform loader should be used with --enable-source-maps to preserve accurate source-mapped stack traces. Transform mode rewrites the code rather than blanking it out, so without source maps the positions in a stack trace no longer correspond to the original TypeScript file.
module.register lets you avoid the flag entirely
Passing --import on every invocation is fine for a script, and it is the wrong answer for an existing codebase that you do not control the start command of. Amaro supports programmatic registration for that case.
You create a small bootstrap file and register Amaro once before loading the TypeScript entrypoint:
// bootstrap.mjs
import { register } from "node:module";
register("amaro/strip", import.meta.url);
await import("./src/index.ts");and then start the app through that file:
node --watch ./bootstrap.mjsFor transform mode, you swap amaro/strip for amaro/transform and run Node with --enable-source-maps.
The mechanism is the standard module customisation hook in Node, so nothing here is Amaro specific beyond which loader name you register. The practical effect is that the flag disappears from every npm script and from every process manager configuration, and the TypeScript entrypoint becomes an ordinary import in the bootstrap file.
Monorepo use removes the rebuild step
The README's monorepo section describes what the package does for workspace setups: it makes working in monorepos smoother by removing the need to rebuild internal packages during development.
The trick is the --conditions flag. You point the exports map at the TypeScript source rather than the built output:
"exports": {
".": {
"typescript": "./src/index.ts",
"types": "./dist/index.d.ts",
"require": "./dist/index.js",
"import": "./dist/index.js"
}
}and run with the condition enabled:
node --watch --import="amaro/strip" --conditions=typescript ./src/index.tsBecause the typescript condition is only present when you ask for it, the same package resolves to the built JavaScript for consumers and to the TypeScript source for your local run. Changes to any package are picked up immediately, which is the point.
The types condition still points at the declaration file in dist, so type information for editors and for a tsc run comes from the build output rather than from the source, which is the one loose end in this arrangement.
The package surface is three exports and a wasm blob
The package manifest shows a deliberately small surface. The main entry is ./dist/index.js with type declarations at ./dist/index.d.ts, and there are two subpath exports, ./strip for dist/register-strip.mjs and ./transform for dist/register-transform.mjs.
The files that ship are dist, lib/wasm.js, the TypeScript declarations under lib and the licence. That lib/wasm.js entry is the WebAssembly bundle of the parser, and the separate build script for it is build:wasm, which runs node tools/build-wasm.js. So the WebAssembly parser is not fetched at runtime by a CDN, it is bundled into the published package.
The scripts show the rest of the toolchain. The build runs esbuild and then tsc --noCheck, so the TypeScript compiler is used to emit declarations without a full type check, and lint and format are handled by biome at version 1.8.3, with a ci target running biome ci. Tests run under the Node test runner with node --test, and there is a separate test:regenerate script that updates snapshots.
The dev dependencies include typescript at ^5.5.3, and the README states that the supported TypeScript version is 5.8. The repository also has a deny.toml, which is the license allow-list used by cargo-deny style tooling, and a .nvmrc for the Node version.
Stripping is not checking, and that is the boundary
The most important limitation is not a bug but a property of the approach. Type stripping removes type annotations from the source and leaves the code to run. It does not verify that the types are correct, that the code type checks, or that a type error is absent.
So the tool answers a different question from tsc. It answers whether the program runs, not whether it is well typed. A CI pipeline that swaps tsc for Amaro has swapped a correctness check for an execution convenience, and the two are not substitutes.
The second limitation is the supported TypeScript version. The README states it is 5.8, which means syntax introduced after that release is outside the parser's range. A project tracking the newest TypeScript release will occasionally hit a construct Amaro cannot handle, and the parser being a WebAssembly build of SWC means the failure surfaces as a parse error at load time rather than as a helpful diagnostic.
The third is Node's own type stripping constraints, which apply equally to the built in version: the language feature requires a recent Node, the manifest requires >=22, and the transform mode needs source maps enabled to keep stack traces meaningful.
On maintenance, the repository is not archived and the last push was on 2026-10-19, with v1.2.1 tagged on 2026-09-21 and v1.2.0 on 2026-09-14. The releases are frequent because the package has a single job and tracks upstream parser releases closely.
Editorial conclusion
Amaro suits a Node project that wants to run TypeScript directly without a build step, or a team that needs a newer type stripper than the one inside its Node binary, since the external loader overrides the built in version. It is the wrong tool if you need type checking, since stripping types is not checking them, and it is not a general purpose transpiler for older targets. Before depending on it, run npm install amaro, try the transformSync example on your own source, and confirm your Node version satisfies the >=22 engine requirement.
Frequently asked questions
How do I install Amaro?
The README gives the install as npm install amaro. The package requires Node >=22 according to the engines field, and the manifest version is 1.2.1.
What does Amaro do with TypeScript types?
It strips them, and it replaces removed types with white spaces so that stack traces stay aligned with the original file. The exported transformSync function performs the stripping, with a strip-only mode available.
Can I use Amaro to run TypeScript files in Node without a build step?
Yes. You can use the amaro/strip or amaro/transform loader with node --import, or register the loader once through module.register in a bootstrap file. In Node v22.18.0 and later the --experimental-strip-types flag is no longer needed because type stripping is enabled by default.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nodejs-amaro)Was this useful?