New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

monocrate

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

monocrate

From monorepo to npm in one command

latest
Source
npmnpm
Version
0.14.8
Version published
Maintainers
1
Created
Source

monocrate

npm version CI License: MIT

Monorepos? Great. Publishing from a monorepo? Comically hard.

The Problem

Consider @acme/my-awesome-package, which imports @acme/internal-utils, a workspace dependency.

Now you want to publish it.

  • Naive approach: npm publish produces an uninstallable package - @acme/internal-utils was never published.
  • Publish-all approach (e.g., Lerna): Publishes every internal dependency. Now your internal @acme/internal-utils is a permanent public API - rename a function there, break consumers you never intended to have.
  • Bundler approach (e.g., esbuild): Produces a self-contained blob, but types and sourcemaps break, and consumers can't tree-shake.

The Solution

monocrate is a publishing CLI that gets monorepos. It produces a single publishable directory containing everything needed from your package and its in-repo dependencies.

  • 📦 Consumers get a single package with exactly what they need
  • 🔒 Internal packages remain unpublished
  • ✅ Tree-shaking, sourcemaps, and types all work

The result: a standard npm package that looks like you had hand-crafted it for publishing.

Quickstart

⚠️ ESM only — monocrate only supports ES modules. If your monorepo uses CommonJS, you need to migrate to ESM first.

# Install
pnpm add --save-dev monocrate
# Or: yarn add --dev monocrate
# Or: npm install --save-dev monocrate

# Build first (monocrate publishes; it doesn't build)
npm run build

# Publish
npx monocrate publish packages/my-awesome-package --bump patch

# Or: assemble without publishing
npx monocrate pack packages/my-awesome-package --pack-destination /tmp/inspect --bump patch

What Gets Published

Given this monorepo structure:

/path/to/my-monorepo/
└── packages/
    ├── my-awesome-package/
    │   ├── package.json      # name: @acme/my-awesome-package
    │   └── src/
    │       └── index.ts      # import ... from '@acme/internal-utils'
    └── internal-utils/
        ├── package.json      # name: @acme/internal-utils (private)
        └── src/
            └── index.ts

Running npx monocrate pack packages/my-awesome-package produces:

/tmp/monocrate-xxxxxx/
└── packages/
    └── my-awesome-package/      # preserves the package's original path
        ├── package.json         # name: "@acme/my-awesome-package", version: "1.3.0" (the new resolved version)
        ├── dist/
        │   └── index.js         # rewritten:
        │                        # import ... from '../deps/__acme__internal-utils/dist/index.js'
        └── deps/
            └── __acme__internal-utils/  # mangled package name, the exact notation may vary.
                └── dist/
                    └── index.js

The deps/ directory is where in-repo dependencies are embedded. Each dependency is placed under a mangled form of its package name, avoiding collisions regardless of where packages live in the monorepo.

Note: The actual directory name includes a randomized suffix (e.g., deps-a1b2c3d4/) to prevent conflicts with existing directories in your package.

For details on how monocrate assembles packages, see The Assembly Process.

Supported Scope

⚠️ Important:

  • ESM only — CommonJS require() calls are not rewritten, which will cause runtime failures for consumers. Only use monocrate on ESM-compliant monorepos.
  • peerDependencies and optionalDependencies — preserved in the output package.json, not embedded. It's your responsibility to ensure these are published and available to consumers.

monocrate validates (and rejects with a clear error):

  • Dynamic imports must use string literalsawait import('@pkg/lib') works; await import(variable) does not.
  • Consistent third-party versions — two in-repo packages cannot require different versions of the same dependency.
  • Symlinks must stay within the monorepo — packages symlinked from outside the monorepo root are not supported.

Version Resolution

The --bump flag determines the published version. There are three approaches:

--bump valueSource of truthExample result
patch/minor/majornpm registry1.2.3 → 1.2.4
1.8.9CLI argument1.8.9
packagepackage.json(whatever's in the file)

When publishing multiple packages, see Multiple Packages for unified versioning with --max.

In all cases, your source package.json is never modified—the resolved version only appears in the assembled output.

# Registry-based: query npm, bump from latest (default is minor)
npx monocrate publish packages/my-awesome-package              # bumps minor
npx monocrate publish packages/my-awesome-package --bump patch

# Explicit: use exact version
npx monocrate publish packages/my-awesome-package --bump 2.3.0

# Package-based: read from package.json
cd packages/my-awesome-package
npm version minor --no-git-tag-version
npx monocrate publish . --bump package

Programmatic API

For custom build steps, or integration with other tooling, you can use monocrate as a library instead of invoking the CLI:

import { monocrate } from 'monocrate'

const result = await monocrate({
  pathToSubjectPackages: ['packages/my-awesome-package'],
  publish: true,
  bump: 'minor',
  cwd: process.cwd(),
})

console.log(result.summaries[0].version) // '1.3.0'

The above snippet is the programmatic equivalent of npx monocrate publish packages/my-awesome-package --bump minor.

Advanced Features

Custom Publish Name

Sometimes your internal package name doesn't match the name you want on npm. Add a monocrate.publishName field to your package.json to publish under a different name without renaming the package across your monorepo:

{
  "name": "@acme/my-awesome-package",
  "monocrate": {
    "publishName": "best-package-ever"
  }
}

Mirroring to a Public Repo

Want to open-source your package while keeping your monorepo private? Use --mirror-to to copy the package and its in-repo dependencies to a separate public repository:

npx monocrate publish packages/my-awesome-package --mirror-to ../public-repo

This way, your public repo stays in sync with what you publish—all necessary packages included. Contributors can clone and work on your package.

Requires a clean working tree. Only committed files (from git HEAD) are mirrored.

Multiple Packages

If you have several public packages in your monorepo, publish them in one go by listing multiple directories:

npx monocrate publish packages/lib-a packages/lib-b --bump patch

By default, each package will be published with its own version (individual versioning). If lib-a is at 1.0.0 and lib-b is at 2.0.0, a patch bump publishes them at 1.0.1 and 2.0.1 respectively.

You can also publish all specified packages at the same version (unified versioning, à la AWS SDK v3), by using the --max flag. This applies the bump to the maximum version and publishes all packages at that version.

# Now both will be published at 2.0.1 (the max)
npx monocrate publish packages/lib-a packages/lib-b --bump patch --max

This is purely a stylistic choice; it doesn't affect correctness since in-repo dependencies are always embedded.

Reference

CLI

monocrate <command> <packages...> [options]

Arguments

ArgumentDescription
commandpack (create tarballs only) or publish (publish to npm)
packagesOne or more package directories to process (required)

Options

OptionAliasTypeDefaultDescription
--bump-bstringminorVersion bump strategy: patch, minor, major, package, or explicit semver (e.g., 2.3.0). Use package to read version from package.json.
--maxbooleanfalseUse max version across all packages (unified versioning). When false, each package uses its own version.
--pack-destination-dstring(temp dir)Directory to write tarballs to (pack command only)
--root-rstring(auto)Monorepo root directory (auto-detected if omitted)
--mirror-to-mstringMirror source files to a directory (for public repos)
--result-filestringWrite full result as JSON to file
--helpShow help
--versionShow version number

API

monocrate(options): Promise<MonocrateResult>

Assembles one or more monorepo packages and their in-repo dependencies, and optionally publishes to npm.

MonocrateOptions

PropertyTypeRequiredDefaultDescription
pathToSubjectPackagesstring | string[]YesPackage directories to assemble. Relative paths resolved from cwd.
publishbooleanYesWhether to publish to npm after assembly.
cwdstringYesBase directory for resolving relative paths.
bumpstringNo"minor"Version specifier: "patch", "minor", "major", "package", or explicit semver.
maxbooleanNofalseUse max version across all packages (unified versioning).
packDestinationstringNo(temp dir)Output directory for the assembled package.
monorepoRootstringNo(auto)Monorepo root directory; auto-detected if omitted.
mirrorTostringNoMirror source files to this directory.
npmrcPathstringNoPath to .npmrc file for npm authentication.

MonocrateResult

PropertyTypeDescription
resolvedVersionstring | undefinedThe unified resolved version (only set when max: true).
summariesArray<{ packageName: string; version: string; tarballPath: string }>Details for each assembled package, including version and path to generated tarball.

Keywords

monorepo

FAQs

Package last updated on 15 Feb 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts