sync-npm-packages
Version:
Sync released npm packages to mirror registries automatically.
423 lines (311 loc) • 14.6 kB
Markdown
# sync-npm-packages
[](https://github.com/ntnyq/sync-npm-packages/actions)
[](https://www.npmjs.com/package/sync-npm-packages)
[](https://www.npmjs.com/package/sync-npm-packages)
[](https://github.com/ntnyq/sync-npm-packages/blob/main/LICENSE)
Sync released npm packages to mirror registries automatically. e.g: [npmmirror](https://npmmirror.com/)
## Features
- 🚀 **Zero Config** - Works out of the box with sensible defaults
- 📦 **Auto Detection** - Automatically discovers packages in monorepos
- 🔧 **Flexible Config** - Supports 8+ config file formats via `unconfig`
- 🎯 **CLI & API** - Use as a CLI tool or Node.js library
- 🔄 **Retry Mechanism** - Automatic retry with exponential backoff
- ⚡ **Concurrency Control** - Prevent rate limiting with configurable limits
- 📊 **Progress Display** - Real-time sync progress with detailed logging
- 🪝 **Hooks** - `beforeSync` and `afterSync` callbacks for custom logic
- 💾 **Smart Caching** - Cache synced packages to avoid redundant operations
- 🔐 **Custom Mirrors** - Support for custom registry URLs
- 🧩 **Target Adaptors** - Built-in `npmmirror` and configurable `custom` target
- 🔄 **GitHub Actions** - Easy integration with CI/CD workflows
- 🌐 **Mirror Support** - Currently supports [npmmirror](https://npmmirror.com/)
## Quick Start
Run without installation:
```bash
# npm
npx sync-npm-packages --target npmmirror
```
```bash
# yarn
yarn dlx sync-npm-packages --target npmmirror
```
```bash
# pnpm
pnpm dlx sync-npm-packages --target npmmirror
```
## Installation
```bash
npm install sync-npm-packages -D
```
```bash
yarn add sync-npm-packages -D
```
```bash
pnpm add sync-npm-packages -D
```
## Usage
Check [docs - examples](./docs/EXAMPLES.md) for more examples.
### As a Library
```ts
import { syncNpmPackages, syncNpmPackagesAuto } from 'sync-npm-packages'
// Sync a single package
await syncNpmPackages('package-foobar', { target: 'npmmirror' })
// Sync multiple packages
await syncNpmPackages(['package-foo', 'package-bar'], { target: 'npmmirror' })
// Auto-detect and sync all packages in workspace
await syncNpmPackagesAuto({ target: 'npmmirror' })
// Auto-sync with custom options
await syncNpmPackagesAuto({
cwd: './packages',
ignore: ['**/themes/**', '**/tools/**'],
target: 'npmmirror',
})
// With retry and concurrency control
await syncNpmPackagesAuto({
target: 'npmmirror',
retry: 5,
retryDelay: 2000,
concurrency: 10,
timeout: 15000,
verbose: true,
})
// Use custom target adaptor with user-defined sync path
await syncNpmPackagesAuto({
target: 'custom',
registry: 'registry.example.com',
syncMethod: 'POST',
syncPathTemplate: '/api/sync/{packageName}',
})
```
### As a CLI
```bash
# Sync all detected packages
sync-npm-packages --target npmmirror
# Specify working directory
sync-npm-packages --target npmmirror --cwd ./packages
# With custom ignore patterns
sync-npm-packages --target npmmirror --ignore "**/private/**"
# Disable default ignore patterns
sync-npm-packages --target npmmirror --no-default-ignore
# Enable verbose output
sync-npm-packages --target npmmirror --verbose
# With retry and concurrency control
sync-npm-packages --target npmmirror --retry 5 --concurrency 10
# Custom timeout
sync-npm-packages --target npmmirror --timeout 15000
# Enable debug mode
sync-npm-packages --target npmmirror --debug
# Use custom target adaptor with custom path template
sync-npm-packages --target custom --registry registry.example.com --sync-method POST --sync-path-template "/api/sync/{packageName}"
```
#### CLI Options
| Option | Type | Default | Description |
| ---------------------- | --------- | ------- | ----------------------------------------------------- |
| `--target` | `string` | - | Target mirror registry (required) |
| `--sync-method` | `string` | `PUT` | HTTP method for sync request (`PUT`, `POST`, `PATCH`) |
| `--sync-path-template` | `string` | - | Custom sync path template with `{packageName}` |
| `--cwd` | `string` | - | Working directory |
| `--ignore` | `string` | - | Ignore glob pattern |
| `--include` | `string` | - | Additional packages to sync |
| `--exclude` | `string` | - | Exclude packages from sync |
| `--with-optional` | `boolean` | `false` | Include optionalDependencies |
| `--no-default-ignore` | `boolean` | - | Disable default ignore patterns |
| `--retry` | `number` | `3` | Number of retry attempts |
| `--retry-delay` | `number` | `1000` | Delay between retries (ms) |
| `--concurrency` | `number` | `5` | Max concurrent requests |
| `--timeout` | `number` | `10000` | Request timeout (ms) |
| `--verbose` | `boolean` | `false` | Enable detailed output |
| `--silent` | `boolean` | `false` | Suppress all output |
| `--debug` | `boolean` | `false` | Enable debug mode |
| `--dry` | `boolean` | `false` | Dry run without actual sync |
| `--version` | - | - | Show version |
| `--help` | - | - | Show help |
### In npm Scripts
Add to your [package.json](package.json):
```json
{
"scripts": {
"release": "bumpp && npm publish && sync-npm-packages --target npmmirror"
}
}
```
## Configuration
### Config File Resolution
`sync-npm-packages` uses [unconfig](https://github.com/antfu-collective/unconfig) for config loading. It searches for config files in the following order:
- `sync.config.{mts,cts,ts,mjs,cjs,js,json}`
- `.syncrc.json`
### TypeScript Config
Use the `defineConfig` helper for type safety:
```ts
// sync.config.ts
import { defineConfig } from 'sync-npm-packages'
export default defineConfig({
target: 'npmmirror',
cwd: './packages',
ignore: ['**/private/**'],
defaultIgnore: true,
// Performance options
retry: 5,
retryDelay: 2000,
concurrency: 10,
timeout: 15000,
// Output options
verbose: true,
})
```
### JSON Config
JSON configs support IntelliSense via JSON schema:
```json
// .syncrc.json
{
"$schema": "https://unpkg.com/sync-npm-packages/schemas/syncrc.json",
"target": "npmmirror",
"defaultIgnore": false
}
```
### Priority
> [!NOTE]
> CLI options override config file settings.
## GitHub Actions Integration
Automatically sync packages after publishing to npm:
```yaml
name: Release
permissions:
contents: write
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version: lts/*
registry-url: https://registry.npmjs.org/
# Build and publish your packages
- run: npm publish
# Sync to mirror
- run: npx sync-npm-packages --target npmmirror
```
## API Reference
### npmmirror Sync Endpoint
This project uses the official npmmirror single-package sync endpoint:
- `PUT https://registry.npmmirror.com/-/package/<package-name>/syncs`
- Package names are URL-encoded automatically (including scoped packages like `@vue/compiler-sfc`)
- A successful response means the package has been queued for sync (not necessarily completed immediately)
### Custom Target Adaptor
Use `target: 'custom'` when your registry has a different sync trigger path.
- `syncPathTemplate` is required for `custom` target
- Use `{packageName}` placeholder, it will be URL-encoded
- `syncMethod` defaults to `PUT`
Example:
```ts
await syncNpmPackages('lodash', {
target: 'custom',
registry: 'registry.example.com',
syncMethod: 'POST',
syncPathTemplate: '/api/sync/{packageName}',
})
```
### `syncNpmPackages(input, options)`
Sync specified packages to a mirror registry.
```ts
function syncNpmPackages(
input: string | string[],
options: SyncOptions,
): Promise<void>
```
#### Parameters
- **`input`** (`string | string[]`) - Package name(s) to sync
- **`options`** (`SyncOptions`) - Sync configuration
- `target` (`'npmmirror' | 'custom'`) - Target mirror registry (required)
- `debug` (`boolean`) - Enable debug logging (default: `false`)
#### Example
```ts
import { syncNpmPackages } from 'sync-npm-packages'
// Single package
await syncNpmPackages('my-package', { target: 'npmmirror' })
// Multiple packages
await syncNpmPackages(['pkg-a', 'pkg-b'], { target: 'npmmirror' })
```
---
### `syncNpmPackagesAuto(options)`
Auto-detect and sync packages from workspace.
```ts
function syncNpmPackagesAuto(
options?: SyncOptions & DetectOptions,
): Promise<void>
```
#### Parameters
- **`options`** (`SyncOptions & DetectOptions`) - Combined sync and detection options
- All `SyncOptions` properties
- All `DetectOptions` properties (see below)
#### Example
```ts
import { syncNpmPackagesAuto } from 'sync-npm-packages'
await syncNpmPackagesAuto({
target: 'npmmirror',
cwd: './packages',
ignore: ['**/test/**'],
exclude: ['private-pkg'],
})
```
---
## Type Definitions
### `DetectOptions`
Options for package detection in workspace.
| Property | Type | Default | Description |
| --------------- | -------------------- | --------------- | ---------------------------------- |
| `cwd` | `string` | `process.cwd()` | Working directory for glob search |
| `defaultIgnore` | `boolean` | `true` | Use built-in ignore patterns |
| `exclude` | `string \| string[]` | `[]` | Package names to exclude from sync |
| `ignore` | `string \| string[]` | `[]` | Glob patterns to ignore |
| `include` | `string \| string[]` | `[]` | Additional packages to sync |
| `withOptional` | `boolean` | `false` | Include `optionalDependencies` |
### `SyncOptions`
Options for syncing packages to mirrors.
| Property | Type | Default | Description |
| ------------------ | -------------------------------------------------------- | ------------- | ---------------------------------------------------------- |
| `target` | `'npmmirror' \| 'custom'` | - | Target mirror registry (required) |
| `debug` | `boolean` | `false` | Enable debug logging |
| `retry` | `number` | `3` | Number of retry attempts on failure |
| `retryDelay` | `number` | `1000` | Delay between retries in milliseconds |
| `concurrency` | `number` | `5` | Maximum number of concurrent requests |
| `timeout` | `number` | `10000` | Request timeout in milliseconds |
| `verbose` | `boolean` | `false` | Enable verbose output with progress |
| `silent` | `boolean` | `false` | Silent mode, suppress all output |
| `registry` | `string` | - | Custom public registry host or HTTPS URL (optional) |
| `syncMethod` | `'PUT' \| 'POST' \| 'PATCH'` | `PUT` | HTTP method for sync request |
| `syncPathTemplate` | `string` | - | Required when target is `custom`; supports `{packageName}` |
| `cache` | `boolean` | `false` | Enable caching of synced packages |
| `cacheDir` | `string` | `.sync-cache` | Cache directory path |
| `beforeSync` | `(name: string) => void \| Promise<void>` | - | Hook before each sync |
| `afterSync` | `(name: string, error?: Error) => void \| Promise<void>` | - | Hook after each sync |
### Advanced Features
For detailed information about advanced features, see [ADVANCED.md](ADVANCED.md):
- **Hooks** (`beforeSync`, `afterSync`) - Execute custom logic before/after sync
- **Caching** - Automatically skip already synced packages
- **Custom Registries** - Sync to non-default mirror endpoints
### Default Ignore Patterns
When `defaultIgnore: true` (default), these patterns are automatically excluded:
- `**/node_modules/**`
- `**/.git/**`
- `**/docs/**`
- `**/tests/**`
- `**/examples/**`
- `**/fixtures/**`
- `**/playground/**`
> [!TIP]
> Use `--no-default-ignore` or `defaultIgnore: false` to disable all defaults except `**/node_modules/**`.
---
## Requirements
- Node.js `^22.13.0 || >=24.0`
## Related Projects
- [vuepress/ecosystem](https://github.com/vuepress/ecosystem/blob/main/scripts/syncNpmmirror.ts) - Original inspiration
- [npmmirror](https://npmmirror.com/) - The npm mirror service
## License
[MIT](./LICENSE) License © 2025-PRESENT [ntnyq](https://github.com/ntnyq)