Fix broken node modules with no fuss - create and apply patches to npm dependencies instantly
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Supporting utilities for path handling, file operations, state management, and type definitions used throughout the patch-package ecosystem. These utilities provide cross-platform compatibility, type safety, and robust error handling.
Fundamental data structures used throughout patch-package.
/**
* Core package details interface
*/
interface PackageDetails {
/** Human-readable package path specifier */
humanReadablePathSpecifier: string;
/** Package path specifier */
pathSpecifier: string;
/** File system path to package */
path: string;
/** Package name */
name: string;
/** Whether package is nested */
isNested: boolean;
/** Array of package names in hierarchy */
packageNames: string[];
}
/**
* Extended package details with patch information
*/
interface PatchedPackageDetails extends PackageDetails {
/** Package version */
version: string;
/** Patch file name */
patchFilename: string;
/** Development dependency only */
isDevOnly: boolean;
/** Optional sequence name */
sequenceName?: string;
/** Optional sequence number */
sequenceNumber?: number;
}Functions for parsing and extracting package information from various string formats.
/**
* Parse package name and version from string
* @param str - String containing package info
* @returns Parsed name and version info or null if invalid
*/
function parseNameAndVersion(str: string): ParsedNameVersion | null;
interface ParsedNameVersion {
packageName: string;
version: string;
sequenceName?: string;
sequenceNumber?: number;
}
/**
* Extract package details from patch filename
* @param patchFilename - Patch file name
* @returns Package details or null if invalid filename
*/
function getPackageDetailsFromPatchFilename(patchFilename: string): PatchedPackageDetails | null;
/**
* Parse package details from CLI string
* @param specifier - Package specifier string
* @returns Package details or null if invalid
*/
function getPatchDetailsFromCliString(specifier: string): PackageDetails | null;Usage Examples:
import {
parseNameAndVersion,
getPackageDetailsFromPatchFilename,
getPatchDetailsFromCliString
} from "patch-package/dist/PackageDetails";
// Parse version info
const nameVersion = parseNameAndVersion("lodash+4.17.21++001_security-fix");
// Returns: { packageName: "lodash", version: "4.17.21", sequenceName: "security-fix", sequenceNumber: 1 }
// Extract from patch filename
const patchDetails = getPackageDetailsFromPatchFilename("react+17.0.2.patch");
// Parse CLI input
const cliDetails = getPatchDetailsFromCliString("@types/node");Cross-platform path handling utilities that work consistently across Windows, macOS, and Linux.
/**
* Cross-platform path joining
* @param args - Path segments to join
* @returns Joined path with correct separators
*/
function join(...args: string[]): string;
/**
* Cross-platform path resolution
* @param args - Path segments to resolve
* @returns Absolute resolved path
*/
function resolve(...args: string[]): string;
/**
* Cross-platform relative path calculation
* @param from - Source path
* @param to - Target path
* @returns Relative path from source to target
*/
function relative(from: string, to: string): string;
/**
* Get directory name from path
* @param path - File or directory path
* @returns Directory name
*/
function dirname(path: string): string;Usage Examples:
import { join, resolve, relative, dirname } from "patch-package/dist/path";
// Cross-platform path joining
const patchPath = join("patches", "lodash+4.17.21.patch");
// Windows: "patches\\lodash+4.17.21.patch"
// Unix: "patches/lodash+4.17.21.patch"
// Resolve absolute path
const absolutePath = resolve("./patches", "../node_modules", "lodash");
// Calculate relative path
const relPath = relative("/app", "/app/node_modules/lodash");
// Returns: "node_modules/lodash"
// Get directory
const dir = dirname("/path/to/file.patch");
// Returns: "/path/to"Content hashing for patch validation and integrity checking.
/**
* Generate SHA-256 hash of file content
* @param filePath - Path to file to hash
* @returns SHA-256 hash string
*/
function hashFile(filePath: string): string;
/**
* Coerce version string to valid semver format
* @param version - Version string to coerce
* @returns Valid semver string or null if invalid
*/
function coerceSemVer(version: string): string | null;Usage Examples:
import { hashFile } from "patch-package/dist/hash";
import { coerceSemVer } from "patch-package/dist/coerceSemVer";
// Generate file hash for validation
const patchHash = hashFile("patches/lodash+4.17.21.patch");
console.log(`Patch hash: ${patchHash}`);
// Coerce version to valid semver
const validVersion = coerceSemVer("1.0.0-beta.1+build.123");
// Returns: "1.0.0-beta.1"
const invalidVersion = coerceSemVer("not-a-version");
// Returns: null
// Use in state management
import { savePatchApplicationState } from "patch-package/dist/stateFile";
savePatchApplicationState({
packageDetails,
patches: [{
patchFilename: "lodash+4.17.21.patch",
patchContentHash: hashFile("patches/lodash+4.17.21.patch"),
didApply: true
}],
isRebasing: false
});Secure process spawning for executing shell commands.
/**
* Safely execute shell commands
* @param command - Command to execute
* @param args - Command arguments array
* @param options - Spawn options
* @returns Spawn result with status and output
*/
function spawnSafeSync(
command: string,
args?: string[],
options?: SpawnSafeOptions
): SpawnResult;
interface SpawnSafeOptions {
/** Current working directory */
cwd?: string;
/** Environment variables */
env?: Record<string, string>;
/** Input to pass to command */
input?: string | Buffer;
/** Stdio configuration */
stdio?: "pipe" | "inherit" | "ignore";
/** Timeout in milliseconds */
timeout?: number;
/** Kill signal */
killSignal?: string;
}
interface SpawnResult {
/** Exit status code */
status: number | null;
/** Kill signal if terminated */
signal: string | null;
/** Standard output */
stdout: Buffer;
/** Standard error */
stderr: Buffer;
}Usage Examples:
import { spawnSafeSync } from "patch-package/dist/spawnSafe";
// Execute git command safely
const result = spawnSafeSync("git", ["diff", "--no-index", "original", "modified"], {
cwd: "/path/to/package",
timeout: 30000
});
if (result.status === 0) {
const diff = result.stdout.toString();
console.log("Git diff output:", diff);
} else {
console.error("Git command failed:", result.stderr.toString());
}Utility for creating regular expressions with error handling and validation.
/**
* Create RegExp with error handling and validation
* @param reString - Regular expression string
* @param name - Name for error reporting
* @param defaultValue - Default regex if parsing fails
* @param caseSensitive - Case sensitivity flag
* @returns Compiled regular expression
*/
function makeRegExp(
reString: string,
name: string,
defaultValue: RegExp,
caseSensitive: boolean
): RegExp;Usage Examples:
import { makeRegExp } from "patch-package/dist/makeRegExp";
// Create include pattern with error handling
const includePaths = makeRegExp(
"src/.*\\.(js|ts)$",
"include paths",
/.*/,
false
);
// Create exclude pattern
const excludePaths = makeRegExp(
"(test|spec|__tests__)/.*",
"exclude paths",
/^package\.json$/,
true // case sensitive
);
// Use in patch creation
import { makePatch } from "patch-package/dist/makePatch";
makePatch({
packagePathSpecifier: "my-package",
appPath: process.cwd(),
packageManager: "npm",
includePaths,
excludePaths,
// ... other options
});TypeScript utility for exhaustive type checking.
/**
* TypeScript exhaustiveness checking utility
* @param x - Value that should never be reached
* @throws Error if called (indicates missing case)
*/
function assertNever(x: never): never;Usage Examples:
import { assertNever } from "patch-package/dist/assertNever";
type PackageManager = "npm" | "yarn" | "npm-shrinkwrap";
function handlePackageManager(pm: PackageManager) {
switch (pm) {
case "npm":
return "Using npm";
case "yarn":
return "Using yarn";
case "npm-shrinkwrap":
return "Using npm-shrinkwrap";
default:
// TypeScript will ensure this is never reached
return assertNever(pm);
}
}Utilities for managing patch application state persistence.
/** State file name constant */
const STATE_FILE_NAME: ".patch-package.json";
/**
* Get current patch application state
* @param packageDetails - Package details
* @returns Current state or null if none exists
*/
function getPatchApplicationState(packageDetails: PackageDetails): PatchApplicationState | null;
/**
* Save patch application state
* @param options - State save options
*/
function savePatchApplicationState(options: SaveStateOptions): void;
/**
* Clear patch application state
* @param packageDetails - Package details
*/
function clearPatchApplicationState(packageDetails: PackageDetails): void;
interface SaveStateOptions {
packageDetails: PackageDetails;
patches: PatchState[];
isRebasing: boolean;
}
interface PatchState {
patchFilename: string;
patchContentHash: string;
didApply: boolean;
}
interface PatchApplicationState {
version: number;
patches: PatchState[];
isRebasing: boolean;
}import {
getPatchDetailsFromCliString,
parseNameAndVersion,
join,
resolve,
hashFile,
makeRegExp,
spawnSafeSync,
getPatchApplicationState,
savePatchApplicationState
} from "patch-package/dist";
// Complete workflow using utilities
async function processPackagePatch(packageName: string) {
// Parse package details
const packageDetails = getPatchDetailsFromCliString(packageName);
if (!packageDetails) {
throw new Error(`Invalid package: ${packageName}`);
}
// Build paths using cross-platform utilities
const patchDir = resolve("patches");
const patchPath = join(patchDir, `${packageName}+1.0.0.patch`);
// Create regex patterns with error handling
const includePaths = makeRegExp("src/.*\\.(js|ts)$", "include", /.*/, false);
const excludePaths = makeRegExp("test/.*", "exclude", /^package\.json$/, false);
// Check current state
const currentState = getPatchApplicationState(packageDetails);
// Generate file hash for validation
const patchHash = hashFile(patchPath);
// Execute git command safely
const gitResult = spawnSafeSync("git", ["status", "--porcelain"], {
cwd: packageDetails.path,
timeout: 10000
});
// Update state
savePatchApplicationState({
packageDetails,
patches: [{
patchFilename: `${packageName}+1.0.0.patch`,
patchContentHash: patchHash,
didApply: true
}],
isRebasing: false
});
return {
packageDetails,
patchPath,
currentState,
gitStatus: gitResult.stdout.toString()
};
}These utilities form the foundation that enables patch-package to work reliably across different platforms, package managers, and project structures while maintaining type safety and robust error handling.
Install with Tessl CLI
npx tessl i tessl/npm-patch-package