monocrate 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +75 -148
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,193 +1,120 @@
1
1
  # Monocrate
2
2
 
3
- > From monorepo to npm in one command
3
+ [![npm version](https://img.shields.io/npm/v/monocrate.svg)](https://www.npmjs.com/package/monocrate)
4
+ [![CI](https://github.com/imaman/monocrate/actions/workflows/ci.yml/badge.svg)](https://github.com/imaman/monocrate/actions/workflows/ci.yml)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
6
 
5
- Monocrate bundles monorepo packages for npm publishing by automatically resolving in-repo dependencies, copying compiled code, and generating a unified package.json with all third-party dependencies merged.
7
+ From monorepo to npm in one command.
6
8
 
7
- ## The Problem
9
+ ## Why
8
10
 
9
- Publishing a package from a monorepo to npm is surprisingly painful. Your package might depend on other internal packages (like `@myorg/utils` or `@myorg/core`), but npm doesn't understand workspace dependencies. You end up with one of these bad options:
11
+ Publishing from a monorepo breaks when your package depends on internal packages. npm doesn't understand workspace references like `@myorg/utils`. You're forced to either publish every internal package separately, manually copy and merge files, or bundle everything into a single file (losing module structure and type declarations).
10
12
 
11
- 1. **Publish everything** - Every internal dependency must be published separately, even if they're implementation details
12
- 2. **Manual bundling** - Copy files, manually merge package.json dependencies, hope you didn't miss anything
13
- 3. **Complex build pipelines** - Esbuild/Rollup/Webpack configurations that bundle everything into a single file, losing the module structure
13
+ Monocrate gives you:
14
14
 
15
- ## The Solution
15
+ - **One command** — point at your package, done
16
+ - **Self-contained output** — internal dependencies are included, nothing else to publish
17
+ - **Preserved module structure** — no flattening into a single file, tree-shaking works
18
+ - **Type declarations included** — `.d.ts` files just work
19
+ - **Open-source mirroring** — `--mirror-to` copies sources to a public repo alongside publishing
16
20
 
17
- Monocrate takes a different approach: it bundles your package and all its in-repo dependencies into a single publishable directory, preserving the original module structure. Third-party dependencies are collected from all packages and merged into a single package.json.
18
-
19
- ```
20
- monorepo/
21
- packages/
22
- app/ <-- You want to publish this
23
- utils/ <-- app depends on this
24
- core/ <-- utils depends on this
25
-
26
- $ monocrate bundle @myorg/app --output ./publish
27
-
28
- publish/
29
- index.js <-- app's compiled code
30
- _deps/
31
- _myorg_utils/ <-- utils' compiled code
32
- _myorg_core/ <-- core's compiled code
33
- package.json <-- Merged dependencies from all packages
34
- ```
35
-
36
- ## Installation
21
+ ## Install
37
22
 
38
23
  ```bash
39
24
  npm install -g monocrate
40
25
  ```
41
26
 
42
- Or use without installing:
27
+ ## Usage
43
28
 
44
29
  ```bash
45
- npx monocrate bundle my-package --output ./publish
30
+ # Prepare for inspection without publishing
31
+ monocrate prepare packages/my-app
32
+
33
+ # Publish directly to npm
34
+ monocrate publish packages/my-app --bump patch
46
35
  ```
47
36
 
48
- ## Quick Start
37
+ ### Commands
49
38
 
50
- **1. Build your packages**
39
+ **`prepare <packages...>`** — Assemble packages but don't publish. Useful for inspecting output or manual publishing.
51
40
 
52
- ```bash
53
- # Ensure all packages have been compiled to their dist/ directories
54
- npm run build --workspaces
55
- ```
41
+ **`publish <packages...>`** — Assemble and publish to npm.
56
42
 
57
- **2. Bundle for publishing**
43
+ ### Options
58
44
 
59
- ```bash
60
- monocrate bundle @myorg/app --output ./publish
61
- ```
45
+ | Flag | Description |
46
+ |------|-------------|
47
+ | `-b, --bump` | Version bump: `patch`, `minor`, `major`, or explicit version like `1.2.3`. Defaults to `minor`. |
48
+ | `-o, --output` | Output directory. Defaults to a temp directory. |
49
+ | `-r, --root` | Monorepo root. Auto-detected if omitted. |
50
+ | `-m, --mirror-to` | Mirror source files to another directory (for open-source mirrors). |
51
+ | `--report` | Write the resolved version to a file instead of stdout. |
62
52
 
63
- **3. Publish to npm**
53
+ ### Examples
64
54
 
65
55
  ```bash
66
- cd publish && npm publish
67
- ```
56
+ # Bump patch version and publish
57
+ monocrate publish packages/cli --bump patch
68
58
 
69
- ## Programmatic API
70
-
71
- Monocrate can be used as a library in your build scripts:
72
-
73
- ```typescript
74
- import { bundle } from 'monocrate';
59
+ # Publish multiple packages with synchronized versions
60
+ monocrate publish packages/core packages/cli
75
61
 
76
- const result = await bundle({
77
- packagePath: '/path/to/monorepo/packages/my-app',
78
- monorepoRoot: '/path/to/monorepo',
79
- outputDir: '/tmp/my-app-bundle',
80
- });
62
+ # Prepare to a specific directory for inspection
63
+ monocrate prepare packages/app --output ./publish-staging
81
64
 
82
- if (result.success) {
83
- console.log('Bundle created at:', result.outputPath);
84
- console.log('Included packages:', result.includedPackages);
85
- console.log('Dependencies:', result.mergedDependencies);
86
- } else {
87
- console.error('Bundle failed:', result.error);
88
- console.error('Details:', result.details);
89
- }
65
+ # Mirror sources to a public repo after publishing
66
+ monocrate publish packages/sdk --mirror-to ../public-repo/packages
90
67
  ```
91
68
 
92
- ### Convenience Function
69
+ ## How It Works
93
70
 
94
- For simpler use cases when running from the monorepo root:
71
+ 1. Discovers all packages in the monorepo via workspace configuration
72
+ 2. Builds a dependency graph starting from your target package
73
+ 3. Copies each package's publishable files (determined by `npm pack`) to the output
74
+ 4. Rewrites import statements from package names to relative paths (using `exports` or `main` fields)
75
+ 5. Generates a `package.json` with merged third-party dependencies
95
76
 
96
- ```typescript
97
- import { bundlePackage } from 'monocrate';
77
+ ### Output Structure
98
78
 
99
- // Automatically discovers packages from workspace configuration
100
- const result = await bundlePackage('@myorg/app', '/tmp/bundle');
101
79
  ```
102
-
103
- ## Configuration Options
104
-
105
- | Option | Type | Default | Description |
106
- |--------|------|---------|-------------|
107
- | `packagePath` | `string` | required | Absolute path to the package to bundle |
108
- | `monorepoRoot` | `string` | required | Absolute path to the monorepo root |
109
- | `outputDir` | `string` | required | Absolute path to the output directory |
110
- | `includeSourceMaps` | `boolean` | `true` | Include `.map` files in the bundle |
111
- | `includeDeclarations` | `boolean` | `true` | Include `.d.ts` files in the bundle |
112
- | `versionConflictStrategy` | `'highest' \| 'error' \| 'warn'` | `'warn'` | How to handle version conflicts |
113
- | `cleanOutputDir` | `boolean` | `true` | Remove existing output directory before bundling |
114
- | `distDirName` | `string` | `'dist'` | Name of the compiled output directory |
115
-
116
- ### Version Conflict Strategies
117
-
118
- When multiple packages depend on the same third-party package with different versions:
119
-
120
- - **`'highest'`** - Silently use the highest semver-compatible version
121
- - **`'warn'`** - Use highest version, but log a warning
122
- - **`'error'`** - Fail the bundle operation
123
-
124
- ```typescript
125
- const result = await bundle({
126
- // ...
127
- versionConflictStrategy: 'error', // Strict mode
128
- });
80
+ monorepo/
81
+ packages/
82
+ app/ ← you want to publish this
83
+ utils/ ← app depends on this
84
+ core/ ← utils depends on this
85
+
86
+ output/
87
+ package.json ← merged deps, in-repo refs removed
88
+ <app files> ← app's publishable files
89
+ deps/
90
+ packages/
91
+ utils/ ← utils' publishable files (imports rewritten)
92
+ core/ ← core's publishable files (imports rewritten)
129
93
  ```
130
94
 
131
- ## How It Works
132
-
133
- 1. **Discover packages** - Reads workspace configuration (npm, yarn, or pnpm) to find all packages in the monorepo
134
-
135
- 2. **Build dependency graph** - Starting from your target package, recursively finds all in-repo dependencies and builds a topologically-sorted graph
136
-
137
- 3. **Validate builds** - Ensures all packages have been compiled (have a `dist/` directory)
138
-
139
- 4. **Assemble bundle** - Copies compiled code:
140
- - Root package's `dist/` goes to output root
141
- - Dependencies' `dist/` go to `_deps/<package-name>/`
142
-
143
- 5. **Transform package.json** - Generates a publish-ready package.json:
144
- - Removes workspace-specific fields (`workspaces`, `private`, `devDependencies`)
145
- - Removes in-repo dependencies
146
- - Merges third-party dependencies from all included packages
147
-
148
- ## Bundle Result
149
-
150
- The `bundle()` function returns a discriminated union:
151
-
152
- ```typescript
153
- // Success
154
- {
155
- success: true,
156
- outputPath: string, // Path to the bundle
157
- packageJson: PackageJson, // Generated package.json
158
- mergedDependencies: object, // All third-party deps
159
- includedPackages: string[], // List of bundled packages
160
- versionConflicts: VersionConflict[], // Any conflicts found
161
- }
162
-
163
- // Failure
164
- {
165
- success: false,
166
- error: string, // Error message
167
- details?: string, // Additional context
168
- cause?: Error, // Underlying error
169
- }
170
- ```
95
+ Entry points (`main`, `types`, `exports`) work unchanged because each package's file structure stays in the same relative position.
171
96
 
172
97
  ## Requirements
173
98
 
174
- - **Node.js 20+**
175
- - **Built packages** - All packages must have a `dist/` directory with compiled code
176
- - **Workspace configuration** - npm workspaces, yarn workspaces, or pnpm-workspace.yaml
99
+ - Node.js 20+
100
+ - Packages must have valid entry points (`exports` or `main` field in package.json)
101
+ - Monorepo must use npm, yarn, or pnpm workspaces
177
102
 
178
- ## Supported Package Managers
179
-
180
- Monocrate automatically detects your package manager from:
103
+ ## Programmatic API
181
104
 
182
- - `pnpm-workspace.yaml`
183
- - `workspaces` field in root `package.json`
184
- - Lock files (`pnpm-lock.yaml`, `yarn.lock`, `package-lock.json`)
105
+ ```typescript
106
+ import { monocrate } from 'monocrate';
185
107
 
186
- ## Documentation
108
+ const result = await monocrate({
109
+ pathToSubjectPackages: ['packages/my-app'],
110
+ cwd: process.cwd(),
111
+ publish: false,
112
+ bump: 'minor',
113
+ });
187
114
 
188
- - [API Reference](./docs/api.md) - Complete programmatic API documentation
189
- - [How It Works](./docs/how-it-works.md) - Deep dive into the bundling process
190
- - [Troubleshooting](./docs/troubleshooting.md) - Common errors and solutions
115
+ console.log(result.resolvedVersion); // "1.3.0"
116
+ console.log(result.outputDir); // "/tmp/monocrate-xyz/my-app"
117
+ ```
191
118
 
192
119
  ## License
193
120
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monocrate",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "From monorepo to npm in one command",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",