clone-stats
clone-stats is a one-function CommonJS package for copying Node.js fs.Stats objects while keeping methods such as isFile(), isDirectory(), and isSymbolicLink(). It creates a new fs.Stats instance and assigns every enumerable property from the source. The package exists because spreading a Stats object into a plain object copies its data but loses its prototype methods. It has no runtime dependencies, but its implementation now rests on a deprecated Node constructor and does not correctly support BigInt stats.
Keep it only where an old dependency already expects its exact behavior. New code should avoid it because the implementation invokes a deprecated Node constructor and fails on BigInt stats, the main modern variation of the object it claims to clone.
Use it if
- You maintain an older CommonJS dependency that already uses clone-stats and only passes ordinary number-based fs.Stats values
- You need a mutable copy of a classic fs.stat result while preserving the familiar fs.Stats predicate methods
- You need bug-compatible behavior with vinyl or another older build-pipeline package that historically depended on this exact module
- You are adding new Node.js code: version 1.0.0 constructs fs.Stats directly, and current Node emits DEP0180 because that constructor is deprecated
- You call fs.stat or fs.statSync with bigint: true: the source becomes BigIntStats, but clone-stats creates ordinary Stats, and predicate calls can throw because its numeric mode no longer matches the copied BigInt fields
- You expect an immutable snapshot: this is a shallow enumerable-property copy, so any extra nested object you attach to the source remains shared
- You need ESM, TypeScript declarations, or a Promise API: the published package is a single CommonJS export with no types and no asynchronous behavior of its own
- You want active maintenance or modern-runtime testing: npm 1.0.0 was published in 2016, the README labels the project experimental, and its test dependency targets tape 2.x
Setup reality
Installation is only npm install clone-stats, and there are no runtime dependencies, peer dependencies, native builds, configuration files, credentials, or environment variables. The export is CommonJS: require('clone-stats') returns the cloning function. An ESM project can usually default-import the CommonJS package through Node interoperability, but there is no exports map and no native named export. The important surprise is runtime compatibility, not installation. The implementation calls new fs.Stats(), which produces a DEP0180 deprecation warning on Node 22. It then copies Object.keys(stats), so non-enumerable properties, symbol properties, a custom subclass prototype, and property descriptors are not preserved. Do not feed it results created with { bigint: true }; those are BigIntStats objects, while the replacement is ordinary Stats. The copied BigInt mode fields can make isFile() and related methods throw a mixed-number-type error. There are no bundled TypeScript declarations, so typed projects must add a local declaration or keep the call behind a small typed wrapper. The package performs no validation: null, undefined, or a plain lookalike either throws or produces a Stats instance with incomplete fields. Treat it as a compatibility shim for ordinary results from fs.stat, fs.lstat, or their synchronous forms, not as a general clone utility and not as a future-facing filesystem abstraction.
Patterns
Clone a synchronous file statclone-sync-stat
const fs = require('node:fs')
const cloneStats = require('clone-stats')
const original = fs.statSync('report.pdf')
const copy = cloneStats(original)
console.log(copy.size, copy.isFile())Use only ordinary number-based stats. On current Node, the clone operation may emit DEP0180 because the package constructs fs.Stats directly.
Clone a callback-based stat resultclone-async-stat
const fs = require('node:fs')
const cloneStats = require('clone-stats')
fs.stat('uploads/photo.jpg', (error, stats) => {
if (error) throw error
const snapshot = cloneStats(stats)
console.log(snapshot.mtime)
})clone-stats itself is synchronous. It only copies the result passed by fs.stat and does not change filesystem error handling.
Clone a promise-based stat resultclone-promise-stat
import { stat } from 'node:fs/promises'
import cloneStats from 'clone-stats'
const original = await stat('package.json')
const copy = cloneStats(original)
console.log(copy.isFile())The package is CommonJS. Node ESM default-import interoperability normally supplies the exported function, but the package has no exports map or native ESM build.
Keep Stats predicate methodspreserve-predicates
const plain = { ...original }
const cloned = cloneStats(original)
console.log(typeof plain.isDirectory) // undefined
console.log(cloned.isDirectory()) // booleanThis prototype preservation is the package's entire reason to exist. Object spread and Object.assign({}, stats) produce plain objects without Stats methods.
Change copied scalar fields without changing the sourcemutate-copy
const original = fs.statSync('asset.css')
const copy = cloneStats(original)
copy.mtime = new Date(0)
copy.size = 0
console.log(original.size !== copy.size)Built-in Stats fields are scalar values or Date objects. Reassigning a top-level field is independent, but mutating a shared nested object or Date in place can still affect both objects.
Clone symlink-aware lstat metadatacopy-lstat
const original = fs.lstatSync('current')
const copy = cloneStats(original)
if (copy.isSymbolicLink()) {
console.log('current is a symlink')
}Use lstat when the link itself matters. fs.stat follows a symbolic link before clone-stats sees the result.
Reject non-Stats inputs before cloningguard-input
const fs = require('node:fs')
function safeCloneStats(value) {
if (!(value instanceof fs.Stats)) {
throw new TypeError('Expected fs.Stats')
}
return cloneStats(value)
}The package does no input validation. Object.keys(null) throws, while a plain object can silently become an incomplete Stats instance.
Keep BigInt stats out of clone-statsavoid-bigint
const precise = fs.statSync('archive.tar', { bigint: true })
// Do not pass precise to cloneStats. Keep the original, or copy
// the fields into an application-owned data shape instead.
const snapshot = { size: precise.size, mtimeNs: precise.mtimeNs }BigIntStats uses different internal mode arithmetic. clone-stats replaces its prototype with ordinary Stats, so predicates can throw Cannot mix BigInt and other types.
Declare the untyped CommonJS module locallyadd-types
// types/clone-stats.d.ts
declare module 'clone-stats' {
import type { Stats } from 'node:fs'
function cloneStats(stats: Stats): Stats
export = cloneStats
}The package ships no TypeScript declarations. This narrow declaration deliberately excludes BigIntStats because the implementation does not handle it correctly.
Prefer an application-owned metadata snapshotsnapshot-fields
const stats = await fs.promises.stat('video.mp4')
const snapshot = {
size: stats.size,
mode: stats.mode,
modifiedAt: stats.mtime.toISOString(),
kind: stats.isFile() ? 'file' : 'other',
}For serialization, caching, or worker messages, a plain explicit shape is safer than cloning a runtime-specific Stats instance and makes Date conversion intentional.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| clone | npm | Use for general object graphs when prototype preservation and circular-reference handling matter more than a tiny Stats-only helper |
| lodash.clonedeep | npm | Use when the real requirement is a familiar general-purpose deep copy, after verifying how your special object types behave |
| rfdc | npm | Use for fast deep cloning of ordinary data objects, with its proto option only when inherited enumerable properties are intentionally needed |