microsoft/rushstack: a build toolchain for large TypeScript monorepos
Monorepo for tools developed by the Rush Stack community
At a glance
- What is it?
- Rush Stack is a collection of npm-published tools for scaling TypeScript monorepos, from the Rush build orchestrator to API Extractor and Heft. This review covers what each piece does, how the packages are published, and where the toolchain stops being the right answer.
- Who is it for?
- Adopt Rush Stack if your repository is a TypeScript monorepo large enough that build orchestration, API surface tracking or lockfile conflicts have become recurring work. Skip it for a single-package project or a small polyrepo; the tooling expects a monorepo with a rush.json and adds ceremony that a plain tsc plus npm scripts does not.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Rush Stack is, and the monorepo problem it targets
Rush Stack is the home for projects maintained by the Rush Stack community, and the README states the mission plainly: develop reusable tooling for large scale TypeScript monorepos. That word large carries the design intent. A repository with three packages does not need a build orchestrator, a project graph, or a lockfile investigator. A repository with eighty packages, each with its own tsconfig, its own test setup and its own published API, does.
The problem is not compilation. It is coordination. When package A depends on package B, a change in B must rebuild A, and only A, in the right order. When forty packages each declare eslint as a devDependency, the install tree can end up with several copies of the same version range. When a package publishes types, a removed export is a breaking change that TypeScript will not flag for you at the package boundary. Rush Stack is a set of answers to those three problems, shipped as separate npm packages rather than one monolith.
The audience is therefore narrow and specific: teams running a TypeScript monorepo where build time, dependency drift and public API stability are already costing engineering hours. The README points to a minimal example repository, rush-example, described as demonstrating the fundamentals of Rush without relying on any other Rush Stack tooling, which is a reasonable first stop before adopting the full stack.
Rush, Heft, API Extractor: how the pieces fit together
The repository is organized as a monorepo itself, with apps/, libraries/, heft-plugins/, rush-plugins/, eslint/ and rigs/ at the top level. The published packages listed in the README map onto distinct jobs.
Rush, published as @microsoft/rush, is the build orchestrator. The README describes it as a build orchestrator for large scale TypeScript monorepos and links to rushjs.io for its documentation, which is separate from the main rushstack.io site, a sign that Rush has its own lifecycle and configuration surface. The repository root contains rush.json, the file the orchestrator reads.
Heft, published as @rushstack/heft, is described in the README as the recommended tool that integrates with Rush. Where Rush coordinates many projects, Heft runs the build inside one project. The heft-plugins/ directory in the repository suggests the plugin model is a first-class extension point rather than an afterthought.
API Extractor, published as @microsoft/api-extractor, is described as creating .d.ts rollups and tracking TypeScript API signatures. That second verb matters more than the first. A rollup bundles scattered declaration files into one entry point; signature tracking compares the exported surface against a stored report so that a removed or narrowed export shows up as a diff rather than as a runtime surprise in a consumer.
Around those three sit supporting tools: API Documenter for generating documentation sites from TSDoc comments, Lockfile Explorer for investigating and solving version conflicts for PNPM lockfiles, TSDoc as the doc-comment standard, and eslint-config and eslint-patch under eslint/. The data flow is file-driven throughout. rush.json defines the project graph, each project's config files define its build, and the tools read and write files in the repository rather than maintaining server-side state.
Getting the tools: what the README says about installation
The README does not contain install commands. It is an index: a set of documentation links, a table of published packages with their npm names, and pointers to related repositories. Anyone looking for a copy-paste install line will not find one here, and that is worth knowing before you start.
What the README does give is the npm package name for each tool, in the Published Packages table. Rush is @microsoft/rush. Heft is @rushstack/heft. API Extractor is @microsoft/api-extractor. API Documenter is @microsoft/api-documenter. Lockfile Explorer is @rushstack/lockfile-explorer. The ESLint packages are @rushstack/eslint-bulk, @rushstack/eslint-config and @rushstack/eslint-patch. Each row links to its own CHANGELOG.md in the repository and to its npm page.
That table is the starting point. The package name is exact and citable; the version and the install instructions are not in the README, so the npm page or the linked documentation site is where you go next. For Rush specifically, the README links to rushjs.io rather than rushstack.io, and for Heft it links to heft.rushstack.io. Those two sites are the ones that carry setup instructions.
The README also offers two ways to try the repository without a local install: an Open in GitHub Codespaces badge that launches a devcontainer from .devcontainer/devcontainer.json, and an Open in VS Code web view link. Neither is an installation of the tools into your own project, but both let you read the source and the config files in a browser.
The lockfile and install model is the main constraint
The repository root contains a common/ directory alongside rush.json. In a Rush-managed repository, common/ holds the shared lockfile and configuration, and Rush owns dependency installation rather than delegating it to npm or yarn workspaces. The README does not spell this out; the directory layout is the evidence.
The consequence is that a Rush repository does not behave like a plain npm workspace. Dependency changes go through Rush's own commands rather than a bare package-manager install inside a project folder, and a node_modules layout produced by another tool can diverge from what Rush expects. The failure often surfaces later as a module resolution error in an unrelated package. The README does not document a rollback procedure for a bad lockfile update, and common/ is not a directory to edit by hand.
Version conflicts are common enough in this model that the project ships Lockfile Explorer, published as @rushstack/lockfile-explorer, specifically to investigate and solve version conflicts for PNPM lockfiles. The existence of a dedicated tool is itself evidence that lockfile conflicts are an expected operational cost, not an edge case. Teams that want each package to resolve its own dependency tree independently will find this model restrictive. Teams that want one reproducible install across the whole repository will find it is the point.
eslint-patch and the failure mode people search for
The eslint/ directory contains three published packages: @rushstack/eslint-bulk, @rushstack/eslint-config and @rushstack/eslint-patch. Of these, eslint-patch is the one with a well-known failure mode, and the related search data includes the exact error text people hit: "Failed to patch ESLint because the calling module was not recognized."
The patch exists because ESLint's resolution of plugins and configs does not always match how a monorepo installs them. It works by intercepting module resolution at require time, which means it depends on recognizing the caller. When ESLint is invoked through a wrapper, a different entry point, or a version whose internals have shifted, the patch cannot identify the calling module and refuses to apply. The error is a safety check, not a crash: the patch would rather do nothing than patch the wrong thing.
This is a real limitation, and it is worth stating plainly. eslint-patch is coupled to ESLint's internal module structure, so an ESLint upgrade can break it before any of your own code changes. The README does not document a compatibility matrix for eslint-patch against ESLint versions. Teams that pin ESLint and upgrade it deliberately will have a smoother time than teams that float the version. The alternative is to avoid the patch entirely by configuring ESLint with explicit paths, at the cost of more configuration per package.
@rushstack/eslint-config is a separate concern: it is a shared rule set, and the related searches for the Rush Stack ESLint config suggest it is often adopted on its own, without Rush. That is a supported-looking use, since the table in the README lists every package as independently published on npm.
Where Rush Stack is the wrong tool
The clearest boundary is repository size. If your project is a single npm package, or a handful of packages that build in seconds, Rush adds a rush.json file, a shared lockfile, a common/ directory and a build orchestrator to maintain. The README's own framing, tooling for large scale TypeScript monorepos, is not marketing language; it is a scope statement. Below that scale, the overhead is real and the benefit is not.
The second boundary is language. Rush Stack is TypeScript and Node.js tooling. The related searches include "Turborepo for python" and "Turborepo SWC," which suggests people arrive at monorepo build tools looking for cross-language support. Rush and Heft do not offer that. A polyglot repository with Go, Python and TypeScript packages will get orchestration for the TypeScript portion only.
The third boundary is the install model, covered above. If your organization has standardized on npm workspaces or yarn, adopting Rush means changing how every developer installs dependencies, and the shared lockfile under common/ becomes a file that must be reviewed in pull requests. That is a workflow decision, not a tooling one, and it should be made before the repository is restructured around rush.json rather than after.
Finally, API Extractor's signature tracking is only useful if you publish packages that other teams consume. For an application repository that never publishes types, the .d.ts rollup and the API report are work with no corresponding payoff.
Alternatives: Turborepo and Lage take different approaches
Turborepo appears repeatedly in the related search data, and the comparison is instructive. Turborepo is a task runner layered on top of an existing package manager's workspace support. It reads a pipeline configuration, hashes task inputs, and caches outputs so that unchanged tasks are skipped. It does not take over dependency installation; npm, yarn or pnpm continues to own the lockfile and the node_modules layout.
Rush inverts that. It owns installation through the common/ directory and its own update command, and the build orchestration is one part of a larger managed repository. The practical difference: with Turborepo, an existing npm workspace can adopt the tool by adding a config file and changing the build command. With Rush, adoption means restructuring the repository around rush.json and moving dependency management into Rush's model.
Lage is another name that shows up in searches. It is a task runner in the same family as Turborepo: it schedules and caches npm scripts across a workspace without replacing the package manager. The trade-off between Rush and these two is roughly the trade between control and intrusion. Rush gives you a single reproducible install and a project graph it fully understands, at the cost of owning the install path. Task runners give you faster incremental builds with far less disruption, at the cost of leaving dependency resolution to the package manager, which is where many monorepo version conflicts originate.
Heft also has a narrower comparison point: it is Rush Stack's own build tool for a single project, and the README points to rushstack-samples for sample projects that illustrate various project setups, including how to use Heft with other popular JavaScript frameworks. That is the escape hatch if you want the build stages without the orchestrator.
Editorial conclusion
Adopt Rush Stack if your repository is a TypeScript monorepo large enough that build orchestration, API surface tracking or lockfile conflicts have become recurring work. Skip it for a single-package project or a small polyrepo; the tooling expects a monorepo with a rush.json and adds ceremony that a plain tsc plus npm scripts does not. Before committing, verify the Node.js version your environment provides against the requirements listed on the npm page for @microsoft/rush, and confirm that your team will accept a repo-owned lockfile in the common/ directory instead of npm or yarn workspaces.
Frequently asked questions
What is Rush Stack used for?
It is a collection of reusable tooling for large scale TypeScript monorepos, including the Rush build orchestrator, the Heft build tool, API Extractor for .d.ts rollups and API signature tracking, and shared ESLint packages. The packages are published independently on npm, so they can be adopted separately.
How do I install Rush Stack?
There is no single Rush Stack package. The README does not carry install commands; it lists each tool's npm package name in its Published Packages table and links to separate documentation sites, such as rushjs.io for Rush and heft.rushstack.io for Heft, where setup instructions live.
Why does eslint-patch fail with "Failed to patch ESLint because the calling module was not recognized"?
The patch intercepts module resolution at require time and needs to recognize the calling module before it applies. When ESLint is invoked through a wrapper or an entry point the patch does not recognize, it refuses to patch rather than patching the wrong target. The README does not document a compatibility matrix against ESLint versions.
Does Rush Stack work outside a monorepo?
The README states the mission as developing reusable tooling for large scale TypeScript monorepos, and Rush is driven by a rush.json file at the repository root that defines the project graph. Individual packages such as @rushstack/eslint-config can be used on their own, but the orchestrator assumes a monorepo.
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/microsoft-rushstack)