is-subdir

Cross-platform check for whether one directory path is a subdirectory of another.

Library
npm
v2.0.0
104stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
53/100Fair
Development Activity60
Maintenance8
Community64
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
52/100Fair
Architecture60
Code Quality78
Innovation25
Learning Curve45

is-subdir is a tiny npm utility that answers one narrow but easy-to-get-wrong question: is this directory located inside that one? It resolves both paths to a canonical, platform-normalized form before comparing them, so it behaves correctly on Windows where drive letters can appear in different casings (c:\foo vs C:\foo\bar) and where path separators differ from POSIX systems.

The package is part of Zoltan Kochan’s zkochan/packages monorepo, alongside sibling utilities used throughout the pnpm ecosystem (better-path-resolve, which-pm, preferred-pm, read-yaml-file). It exposes two functions — isSubdir for a normal containment check and isSubdir.strict for the case where the two directories must not be identical — with no configuration or options beyond that.

What You Get

  • A single-purpose isSubdir(parentDir, subdir) function returning a boolean
  • A strict() variant that excludes the identical-path case
  • Cross-platform path normalization via better-path-resolve, including Windows drive-letter casing
  • Bundled TypeScript type declarations (index.d.ts) alongside the JS

Common Use Cases

  • Verifying a resolved import, config, or lockfile path stays within a project’s workspace root
  • Guarding destructive file operations (delete/move/copy) against escaping an expected directory
  • Deciding whether a changed file belongs to a watched directory tree in build or dev-server tooling
  • Package-manager internals that need to confirm one resolved path is nested under another

Under The Hood

Architecture is-subdir is a single 15-line ES module (index.js) exporting two functions, isSubdir and strict, with no internal layering beyond that: both delegate immediately to better-path-resolve — a sibling package inside the same zkochan/packages pnpm workspace — to canonicalize each input path, append a trailing path.sep with Node’s built-in path module, then compare the two strings with startsWith. strict differs only by an additional inequality guard before the same startsWith check, so the entire behavioral surface reduces to one normalize-then-compare code path duplicated across two entry points. There is no configuration, class hierarchy, or state; the only thing that could change its behavior is a change to better-path-resolve’s normalization rules, which is exactly the point — it exists to centralize the one piece of cross-platform path logic that’s easy to get wrong.

Tech Stack The package targets modern Node.js only (engines: >=22.13), ships as type: module with no build step (index.js is hand-written ESM, not transpiled), and declares a single runtime dependency, better-path-resolve, resolved from its own monorepo via pnpm’s workspace protocol rather than a published npm range. Its only devDependency, is-windows, is pulled through pnpm’s catalog mechanism (a workspace-wide version pin) and used solely to gate a Windows-specific assertion in the test suite. There’s no bundler, transpiler, or framework involved; publishing is handled by the monorepo’s shared tooling rather than anything package-specific.

Code Quality Testing uses Node’s built-in test runner (node —test) with node:assert, covering both isSubdir and strict across relative/absolute paths, the identical-directory edge case, sibling-directory false positives (node_modules/foo vs node_modules/foo-bar), and — conditionally, gated by an is-windows check — Windows drive-letter comparisons. CI (.github/workflows/ci.yml) runs this suite across three Node major versions (22, 24, 25) and three operating systems (Ubuntu, Windows, macOS) in a matrix build, which is a more meaningful quality signal for a path-handling utility than line coverage alone would be. There’s no dedicated linter config visible for this package specifically, but it ships hand-written index.d.ts type declarations alongside the JS, and the monorepo-wide renovate.json keeps dependencies current on a weekly schedule.

API Design There’s nothing technically novel here — it’s a well-known problem (compare two resolved paths with a boundary-safe prefix check) solved the standard way, and its value is in having already handled the fiddly cross-platform edge cases so callers don’t have to. The public API is about as low-friction as it gets: two named exports, no options object, and a plain boolean return — a consumer can go from install to correct usage by reading the four-line README example. The tradeoff is an opinionated, non-configurable notion of “subdirectory” (byte-wise prefix match after resolution), which is a reasonable simplicity/flexibility tradeoff given its narrow scope.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search