350 lines
13 KiB
Markdown
350 lines
13 KiB
Markdown
# @git.zone/tspublish 🚀
|
||
|
||
> **Effortlessly publish multiple TypeScript packages from your monorepo**
|
||
|
||
[](https://www.npmjs.com/package/@git.zone/tspublish)
|
||
[](https://opensource.org/licenses/MIT)
|
||
|
||
## 🌟 What is tspublish?
|
||
|
||
`@git.zone/tspublish` is a powerful CLI tool and library for managing and publishing multiple TypeScript packages from a monorepo. It automates the tedious parts of package publishing — discovery, dependency resolution, building, version validation, and multi-registry publishing — while giving you full control over the process. Whether you're maintaining a suite of microservices, a component library, or any collection of related packages, tspublish makes your life dramatically easier.
|
||
|
||
## Issue Reporting and Security
|
||
|
||
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
|
||
|
||
### ✨ Key Features
|
||
|
||
- 📦 **Automatic Package Discovery** — Scans your monorepo for publishable `ts*` directories
|
||
- 🎨 **Beautiful CLI Output** — Color-coded logging with progress bars and status indicators
|
||
- 🔍 **Version Collision Detection** — Prevents accidental overwrites by checking the registry first
|
||
- 🏗️ **Build Integration** — Automatically compiles TypeScript before publishing via `@git.zone/tsbuild`
|
||
- 🎯 **Smart Dependency Management** — Inherits dependency versions from your monorepo's `package.json`
|
||
- 🌐 **Multi-Registry Support** — Publish to npm, GitHub Packages, Gitea, or private registries
|
||
- 🔗 **Base Registry Inheritance** — Use `useBase` / `extendBase` to inherit registries from `.smartconfig.json`
|
||
- ⚡ **CLI Binary Support** — Automatically generates `cli.js` wrappers for publishable CLI packages
|
||
- 🧹 **Clean Builds** — Creates isolated `dist_publish_*` directories for each package
|
||
|
||
## 📥 Installation
|
||
|
||
```bash
|
||
# Using pnpm (recommended)
|
||
pnpm add -D @git.zone/tspublish
|
||
|
||
# Global installation for CLI usage
|
||
pnpm add -g @git.zone/tspublish
|
||
```
|
||
|
||
## 🚀 Quick Start
|
||
|
||
### 1️⃣ Structure Your Monorepo
|
||
|
||
Organize your packages using directories that start with `ts`:
|
||
|
||
```
|
||
my-awesome-monorepo/
|
||
├── package.json # Main monorepo package.json (version is inherited)
|
||
├── tsconfig.json # Shared TypeScript config
|
||
├── .smartconfig.json # Optional: base registry configuration
|
||
├── ts_core/ # Core package
|
||
│ ├── index.ts # Entry point
|
||
│ ├── readme.md # Package-specific documentation
|
||
│ └── tspublish.json # Publishing configuration
|
||
├── ts_utils/ # Utilities package
|
||
│ ├── index.ts
|
||
│ ├── readme.md
|
||
│ └── tspublish.json
|
||
└── ts_cli/ # CLI package
|
||
├── index.ts
|
||
├── readme.md
|
||
└── tspublish.json
|
||
```
|
||
|
||
### 2️⃣ Configure Each Package
|
||
|
||
Create a `tspublish.json` in each publishable directory:
|
||
|
||
```json
|
||
{
|
||
"name": "@myorg/core",
|
||
"order": 1,
|
||
"dependencies": [
|
||
"@push.rocks/smartpromise",
|
||
"@push.rocks/smartfile"
|
||
],
|
||
"registries": [
|
||
"registry.npmjs.org:public"
|
||
],
|
||
"bin": []
|
||
}
|
||
```
|
||
|
||
#### Configuration Options
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `name` | `string` | The published package name (e.g., `@myorg/core`) |
|
||
| `order` | `number` | Build order for interdependent packages (lower builds first) |
|
||
| `dependencies` | `string[]` | Dependencies to include — versions are resolved from the monorepo's `package.json` |
|
||
| `registries` | `string[]` | Target registries with access level (format: `"url:accessLevel"`) |
|
||
| `bin` | `string[]` | CLI executable names (generates a `cli.js` wrapper automatically) |
|
||
|
||
### 3️⃣ Publish
|
||
|
||
```bash
|
||
# From your monorepo root
|
||
npx tspublish
|
||
```
|
||
|
||
That's it! tspublish will discover all `ts*` directories containing `tspublish.json`, build them in order, validate versions against the registry, and publish.
|
||
|
||
## 🎯 Advanced Usage
|
||
|
||
### Registry Configuration
|
||
|
||
TSPublish offers three approaches for configuring target registries:
|
||
|
||
#### 1. Explicit Registries
|
||
|
||
Define specific registries directly in `tspublish.json`:
|
||
|
||
```json
|
||
{
|
||
"registries": [
|
||
"registry.npmjs.org:public",
|
||
"npm.pkg.github.com:private"
|
||
]
|
||
}
|
||
```
|
||
|
||
The format is `"registryUrl:accessLevel"` where `accessLevel` is `public` or `private`.
|
||
|
||
#### 2. Use Base Configuration (`useBase`)
|
||
|
||
Inherit registries from your project's `.smartconfig.json` (managed by `@git.zone/cli`):
|
||
|
||
```json
|
||
{
|
||
"registries": ["useBase"]
|
||
}
|
||
```
|
||
|
||
This reads from `.smartconfig.json` at the key `@git.zone/cli.release.registries`.
|
||
|
||
#### 3. Extend Base Configuration (`extendBase`)
|
||
|
||
Start with base registries and add or remove specific ones:
|
||
|
||
```json
|
||
{
|
||
"registries": [
|
||
"extendBase",
|
||
"custom-registry.example.com:public",
|
||
"-https://registry.npmjs.org"
|
||
]
|
||
}
|
||
```
|
||
|
||
The `-` prefix excludes a registry from the base configuration. All other entries (besides `extendBase`) are added.
|
||
|
||
#### Empty Registries
|
||
|
||
If `registries` is an empty array `[]`, the package will be built but **not published** — useful for internal-only packages that other packages depend on.
|
||
|
||
### Build Order Management
|
||
|
||
When packages depend on each other, use the `order` field to control build sequence:
|
||
|
||
```json
|
||
// ts_core/tspublish.json — builds first
|
||
{
|
||
"name": "@myorg/core",
|
||
"order": 1,
|
||
"dependencies": [],
|
||
"registries": ["useBase"],
|
||
"bin": []
|
||
}
|
||
|
||
// ts_utils/tspublish.json — builds second, depends on core
|
||
{
|
||
"name": "@myorg/utils",
|
||
"order": 2,
|
||
"dependencies": ["@myorg/core"],
|
||
"registries": ["useBase"],
|
||
"bin": []
|
||
}
|
||
```
|
||
|
||
### CLI Binary Packages
|
||
|
||
For packages that ship CLI tools, specify the binary names in the `bin` array:
|
||
|
||
```json
|
||
{
|
||
"name": "@myorg/cli",
|
||
"order": 3,
|
||
"dependencies": ["commander", "@myorg/core"],
|
||
"registries": ["registry.npmjs.org:public"],
|
||
"bin": ["my-cli", "my-tool"]
|
||
}
|
||
```
|
||
|
||
TSPublish will:
|
||
1. Fetch the standard `cli.js` template from the `@git.zone/cli` assets repository
|
||
2. Adjust the import path to point to the correct `dist_*` folder
|
||
3. Configure the `bin` field in the generated `package.json`
|
||
|
||
### Programmatic Usage
|
||
|
||
```typescript
|
||
import { TsPublish } from '@git.zone/tspublish';
|
||
|
||
const publisher = new TsPublish();
|
||
await publisher.publish(process.cwd());
|
||
```
|
||
|
||
#### Custom Publishing Pipeline
|
||
|
||
```typescript
|
||
import { TsPublish, PublishModule } from '@git.zone/tspublish';
|
||
|
||
const publisher = new TsPublish();
|
||
const modules = await publisher.getModuleSubDirs('./my-monorepo');
|
||
|
||
for (const [name, config] of Object.entries(modules)) {
|
||
const mod = new PublishModule(publisher, {
|
||
monoRepoDir: './my-monorepo',
|
||
packageSubFolder: name,
|
||
});
|
||
|
||
await mod.init(); // Resolve deps, validate version
|
||
await mod.createPublishModuleDir(); // Scaffold dist_publish_* directory
|
||
await mod.build(); // Compile TypeScript
|
||
await mod.publish(); // Publish to registries
|
||
}
|
||
```
|
||
|
||
## 🔧 How It Works
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────┐
|
||
│ tspublish pipeline │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ 1. 🔍 Discovery │
|
||
│ Scan for ts* directories containing tspublish.json │
|
||
│ │
|
||
│ 2. 📋 Preparation │
|
||
│ Create dist_publish_* with generated package.json, │
|
||
│ tsconfig.json, source files, readme, and license │
|
||
│ │
|
||
│ 3. 🔨 Build │
|
||
│ Run `tsbuild tsfolders` in the publish directory │
|
||
│ │
|
||
│ 4. ✅ Validation │
|
||
│ Check npm registry — abort if version already exists │
|
||
│ │
|
||
│ 5. 🚀 Publish │
|
||
│ pnpm publish to each configured registry │
|
||
│ │
|
||
└──────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
For each discovered module, tspublish:
|
||
|
||
1. **Discovers** all directories starting with `ts` that contain a `tspublish.json`
|
||
2. **Resolves dependencies** from the monorepo's `package.json`, using the monorepo version for packages not found in `dependencies`
|
||
3. **Creates an isolated publish directory** (`dist_publish_<folder>`) with a generated `package.json`, `tsconfig.json`, source code copy, readme, and license
|
||
4. **Builds** the package using `pnpm run build` (which calls `tsbuild tsfolders`)
|
||
5. **Validates** against the target registry — if the name+version already exists, it exits with an error
|
||
6. **Publishes** to each configured registry via `pnpm publish`
|
||
|
||
## 📚 API Reference
|
||
|
||
### TsPublish
|
||
|
||
```typescript
|
||
class TsPublish {
|
||
/** Publish all discovered modules in a monorepo directory */
|
||
async publish(monorepoDirPath: string): Promise<void>;
|
||
|
||
/** Discover and return all publishable modules with their tspublish.json configs */
|
||
async getModuleSubDirs(dirPath: string): Promise<Record<string, ITsPublishJson>>;
|
||
}
|
||
```
|
||
|
||
### PublishModule
|
||
|
||
```typescript
|
||
class PublishModule {
|
||
/** Initialize: resolve dependencies, validate version against registry */
|
||
async init(): Promise<void>;
|
||
|
||
/** Create the dist_publish_* directory with all necessary files */
|
||
async createPublishModuleDir(): Promise<void>;
|
||
|
||
/** Build the TypeScript package */
|
||
async build(): Promise<void>;
|
||
|
||
/** Publish to all configured registries */
|
||
async publish(): Promise<void>;
|
||
}
|
||
```
|
||
|
||
### ITsPublishJson
|
||
|
||
```typescript
|
||
interface ITsPublishJson {
|
||
name: string; // Published package name
|
||
order: number; // Build sequence (lower = earlier)
|
||
dependencies: string[]; // Dependencies to include from monorepo
|
||
registries: string[]; // Target registries ("url:accessLevel", "useBase", or "extendBase")
|
||
bin: string[]; // CLI binary names
|
||
}
|
||
```
|
||
|
||
### GiteaAssets
|
||
|
||
```typescript
|
||
class GiteaAssets {
|
||
constructor(options: { giteaBaseUrl: string; token?: string });
|
||
|
||
/** Fetch files from a Gitea repository directory */
|
||
async getFiles(owner: string, repo: string, directory: string, branch?: string): Promise<IRepoFile[]>;
|
||
|
||
/** Get the standard cli.js entry file template */
|
||
async getBinCliEntryFile(): Promise<IRepoFile>;
|
||
}
|
||
```
|
||
|
||
## 🐛 Troubleshooting
|
||
|
||
| Problem | Solution |
|
||
|---------|----------|
|
||
| **"Package X already exists with version Y"** | Bump the version in your monorepo's `package.json` |
|
||
| **No publish modules found** | Ensure directories start with `ts` and contain a valid `tspublish.json` |
|
||
| **Build failures** | Check TypeScript errors — tspublish runs `tsbuild tsfolders` in the generated directory |
|
||
| **useBase/extendBase error** | Ensure `.smartconfig.json` has registries at `@git.zone/cli.release.registries` |
|
||
| **Missing dependency versions** | Add the dependency to your monorepo's `package.json` `dependencies` field |
|
||
|
||
## License and Legal Information
|
||
|
||
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license) file.
|
||
|
||
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
||
|
||
### Trademarks
|
||
|
||
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
|
||
|
||
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
|
||
|
||
### Company Information
|
||
|
||
Task Venture Capital GmbH
|
||
Registered at District Court Bremen HRB 35230 HB, Germany
|
||
|
||
For any legal inquiries or further information, please contact us via email at hello@task.vc.
|
||
|
||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|