CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-np

A better npm publish tool with automated workflows, version bumping, testing, and git integration

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

package-manager.mddocs/

Package Manager Support

Multi-package-manager abstraction supporting npm, Yarn (Classic/Berry), pnpm, and Bun with unified configuration and command generation.

Capabilities

Package Manager Detection

Functions for detecting and configuring package managers.

/**
 * Get package manager configuration for a project
 * @param rootDirectory - Project root directory
 * @param package_ - Package.json object
 * @returns Package manager configuration object
 */
function getPackageManagerConfig(
  rootDirectory: string, 
  package_: {packageManager?: string}
): PackageManagerConfig;

/**
 * Find lockfile for a specific package manager
 * @param rootDirectory - Project root directory
 * @param config - Package manager configuration
 * @returns Lockfile path or undefined if not found
 */
function findLockfile(
  rootDirectory: string, 
  config: PackageManagerConfig
): string | undefined;

/**
 * Format command for display purposes
 * @param command - Command tuple [cli, arguments]
 * @returns Formatted command string
 */
function printCommand([cli, arguments_]: [string, string[]]): string;

Usage Examples:

import { getPackageManagerConfig, findLockfile, printCommand } from "np/source/package-manager/index.js";

// Detect package manager
const packageManager = getPackageManagerConfig(process.cwd(), packageJson);
console.log(`Using package manager: ${packageManager.id}`);

// Find lockfile
const lockfile = findLockfile(process.cwd(), packageManager);
if (lockfile) {
  console.log(`Found lockfile: ${lockfile}`);
}

// Format command for display
const installCmd = packageManager.installCommand;
console.log(`Install command: ${printCommand(installCmd)}`);

Package Manager Configuration Interface

Unified configuration interface for all supported package managers.

interface PackageManagerConfig {
  /** Package manager identifier */
  id: 'npm' | 'yarn' | 'pnpm' | 'bun';
  
  /** CLI command name */
  cli: string;
  
  /** Install command with lockfile */
  installCommand: [string, string[]];
  
  /** Install command without lockfile */
  installCommandNoLockfile: [string, string[]];
  
  /** Generate version bump command */
  versionCommand(input: string): [string, string[]];
  
  /** Custom publish command (optional) */
  publishCommand?: (args: string[]) => [string, string[]];
  
  /** Whether to throw error on external registries */
  throwOnExternalRegistry?: boolean;
}

Supported Package Managers

Configuration objects for each supported package manager.

/**
 * NPM package manager configuration
 */
const npmConfig: PackageManagerConfig = {
  id: 'npm',
  cli: 'npm',
  installCommand: ['npm', ['ci']],
  installCommandNoLockfile: ['npm', ['install']],
  versionCommand: (input) => ['npm', ['version', input]],
  throwOnExternalRegistry: false
};

/**
 * PNPM package manager configuration
 */
const pnpmConfig: PackageManagerConfig = {
  id: 'pnpm',
  cli: 'pnpm',
  installCommand: ['pnpm', ['install', '--frozen-lockfile']],
  installCommandNoLockfile: ['pnpm', ['install', '--no-lockfile']],
  versionCommand: (input) => ['pnpm', ['version', input]],
  throwOnExternalRegistry: true
};

/**
 * Yarn Classic package manager configuration
 */
const yarnConfig: PackageManagerConfig = {
  id: 'yarn',
  cli: 'yarn',
  installCommand: ['yarn', ['install', '--frozen-lockfile']],
  installCommandNoLockfile: ['yarn', ['install', '--no-lockfile']],
  versionCommand: (input) => ['yarn', ['version', '--new-version', input]],
  throwOnExternalRegistry: false
};

/**
 * Yarn Berry package manager configuration
 */
const yarnBerryConfig: PackageManagerConfig = {
  id: 'yarn',
  cli: 'yarn',
  installCommand: ['yarn', ['install', '--immutable']],
  installCommandNoLockfile: ['yarn', ['install']],
  versionCommand: (input) => ['yarn', ['version', input]],
  publishCommand: (args) => ['yarn', ['npm', 'publish', ...args]],
  throwOnExternalRegistry: false
};

/**
 * Bun package manager configuration
 */
const bunConfig: PackageManagerConfig = {
  id: 'bun',
  cli: 'bun',
  installCommand: ['bun', ['install', '--frozen-lockfile']],
  installCommandNoLockfile: ['bun', ['install', '--no-save']],
  versionCommand: (input) => ['npm', ['version', input]], // Bun uses npm for versioning
  throwOnExternalRegistry: false
};

Usage Examples:

import { npmConfig, pnpmConfig, yarnConfig } from "np/source/package-manager/configs.js";

// Use specific package manager
const config = pnpmConfig;
const [cli, args] = config.versionCommand('patch');
console.log(`Version command: ${cli} ${args.join(' ')}`);

// Check external registry support
if (config.throwOnExternalRegistry && isExternalRegistry(packageJson)) {
  throw new Error(`External registry not supported with ${config.id}`);
}

Package Manager Detection

Detection Priority

Package manager detection follows this priority:

  1. CLI flag: --package-manager option
  2. Package.json field: packageManager field specification
  3. Lockfile detection: Based on existing lockfiles
  4. Default: Falls back to npm

PackageManager Field

Support for package.json packageManager field:

{
  "packageManager": "pnpm@8.5.0",
  "packageManager": "yarn@3.4.1",
  "packageManager": "npm@9.0.0"
}

Lockfile Detection

Automatic detection based on lockfiles:

// Lockfile to package manager mapping
const lockfileMap = {
  'package-lock.json': 'npm',
  'yarn.lock': 'yarn',
  'pnpm-lock.yaml': 'pnpm',
  'bun.lockb': 'bun'
};

// Detection logic
function detectFromLockfile(rootDirectory) {
  for (const [lockfile, manager] of Object.entries(lockfileMap)) {
    if (fs.existsSync(path.join(rootDirectory, lockfile))) {
      return manager;
    }
  }
  return 'npm'; // Default fallback
}

Command Generation

Install Commands

Different install behavior based on lockfile presence:

// With lockfile (exact dependencies)
config.installCommand     // ['pnpm', ['install', '--frozen-lockfile']]

// Without lockfile (resolve latest)
config.installCommandNoLockfile  // ['pnpm', ['install', '--no-lockfile']]

Version Commands

Version bump commands vary by package manager:

// NPM
npmConfig.versionCommand('patch')    // ['npm', ['version', 'patch']]

// Yarn Classic
yarnConfig.versionCommand('patch')   // ['yarn', ['version', '--new-version', 'patch']]

// PNPM
pnpmConfig.versionCommand('patch')   // ['pnpm', ['version', 'patch']]

// Bun (uses npm for versioning)
bunConfig.versionCommand('patch')    // ['npm', ['version', 'patch']]

Publish Commands

Publishing commands with custom handling:

// Standard (npm, pnpm, yarn classic)
const args = ['publish', '--tag', 'beta'];
// Uses: npm publish --tag beta

// Yarn Berry (custom publish command)
yarnBerryConfig.publishCommand(args)  // ['yarn', ['npm', 'publish', '--tag', 'beta']]

Package Manager Specific Features

NPM Features

  • Registry support: Full external registry support
  • 2FA integration: Native 2FA support
  • Scoped packages: Built-in scoped package handling
  • Dist-tags: Full dist-tag management

Yarn Features

Yarn Classic

  • Workspace support: Monorepo workspace detection
  • Registry config: Custom registry via .yarnrc
  • Version handling: Different version command syntax
  • Publish workflow: Standard npm publish compatibility

Yarn Berry

  • Plugin system: Modern plugin architecture
  • Workspace protocols: Advanced workspace linking
  • Custom publish: Uses yarn npm publish command
  • Configuration: .yarnrc.yml configuration format

PNPM Features

  • External registry restriction: Throws on external registries
  • Workspace support: Built-in monorepo support
  • Strict lockfile: Immutable lockfile enforcement
  • Space efficiency: Content-addressable storage

Bun Features

  • Fast installs: Ultra-fast package installation
  • Version delegation: Uses npm for version bumping
  • Binary lockfile: Compact binary lockfile format
  • Runtime integration: Built-in JavaScript runtime

Workspace Support

Monorepo Detection

Package managers handle monorepos differently:

// Yarn workspaces
{
  "workspaces": [
    "packages/*",
    "apps/*"
  ]
}

// PNPM workspaces (pnpm-workspace.yaml)
packages:
  - 'packages/*'
  - 'apps/*'

// NPM workspaces
{
  "workspaces": [
    "packages/*"
  ]
}

Workspace Publishing

Publishing from workspace packages:

// Detect if in workspace
const isWorkspace = packageJson.workspaces || 
                   fs.existsSync('pnpm-workspace.yaml');

// Adjust commands for workspace context
if (isWorkspace) {
  // Use workspace-aware commands
  const publishCmd = config.publishCommand || defaultPublishCommand;
}

Configuration Integration

CLI Integration

Package manager selection in CLI:

# Force specific package manager
np --package-manager pnpm

# Auto-detect from lockfile
np  # Uses detected package manager

# Override packageManager field
np --package-manager yarn

Programmatic Usage

// Force specific package manager
const options = {
  packageManager: 'pnpm'
};

// Let np auto-detect
const packageManager = getPackageManagerConfig(rootDirectory, packageJson);

// Use in publishing
await np('patch', {...options, packageManager}, context);

Error Handling

Package Manager Errors

Detection Errors:

  • Multiple lockfiles present
  • Unknown package manager specified
  • Package manager not installed

Command Errors:

  • Invalid command syntax
  • Package manager version incompatibility
  • Missing dependencies

Registry Errors:

  • External registry not supported (pnpm)
  • Authentication failures
  • Network connectivity issues

Error Recovery

Package manager error handling strategies:

// Package manager not found
if (error.code === 'ENOENT') {
  throw new Error(`Package manager '${config.id}' not found. Please install it first.`);
}

// External registry restriction
if (config.throwOnExternalRegistry && isExternalRegistry(package_)) {
  throw new Error(`External registry not supported with ${config.id}`);
}

// Version mismatch
if (error.message.includes('version')) {
  console.warn(`${config.id} version may be incompatible. Consider upgrading.`);
}

Compatibility Matrix

Package manager compatibility with np features:

FeaturenpmYarn ClassicYarn BerrypnpmBun
External Registry
2FA Setup
Workspaces
Custom Publish
Version Bumping➡️ npm

✅ Full support, ❌ Not supported, ➡️ Delegates to another tool

Install with Tessl CLI

npx tessl i tessl/npm-np

docs

configuration.md

core-publishing.md

git-operations.md

index.md

npm-operations.md

package-manager.md

utilities.md

version-management.md

tile.json