UNPKG

sync-npm-packages

Version:

Sync released npm packages to mirror registries automatically.

423 lines (311 loc) 14.6 kB
# sync-npm-packages [![CI](https://github.com/ntnyq/sync-npm-packages/workflows/CI/badge.svg)](https://github.com/ntnyq/sync-npm-packages/actions) [![NPM VERSION](https://img.shields.io/npm/v/sync-npm-packages.svg)](https://www.npmjs.com/package/sync-npm-packages) [![NPM DOWNLOADS](https://img.shields.io/npm/dy/sync-npm-packages.svg)](https://www.npmjs.com/package/sync-npm-packages) [![LICENSE](https://img.shields.io/github/license/ntnyq/sync-npm-packages.svg)](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)