Determine if a path is subdirectory of another in Node.js
Let Node itself do the work.
const path = require('path');
const relative = path.relative(parent, dir);
return relative && !relative.startsWith('..') && !path.isAbsolute(relative);
It does normalisation for you as well.
const path = require('path');
const tests = [
['/foo', '/foo'],
['/foo', '/bar'],
['/foo', '/foobar'],
['/foo', '/foo/bar'],
['/foo', '/foo/../bar'],
['/foo', '/foo/./bar'],
['/bar/../foo', '/foo/bar'],
['/foo', './bar'],
['C:\\Foo', 'C:\\Foo\\Bar'],
['C:\\Foo', 'C:\\Bar'],
['C:\\Foo', 'D:\\Foo\\Bar'],
];
tests.forEach(([parent, dir]) => {
const relative = path.relative(parent, dir);
const isSubdir = relative && !relative.startsWith('..') && !path.isAbsolute(relative);
console.log(`[${parent}, ${dir}] => ${isSubdir} (${relative})`);
});
Works on Windows across drives too.
[/foo, /foo] => false ()
[/foo, /bar] => false (..\bar)
[/foo, /foobar] => false (..\foobar)
[/foo, /foo/bar] => true (bar)
[/foo, /foo/../bar] => false (..\bar)
[/foo, /foo/./bar] => true (bar)
[/bar/../foo, /foo/bar] => true (bar)
[/foo, ./bar] => false (..\Users\kozhevnikov\Desktop\bar)
[C:\Foo, C:\Foo\Bar] => true (Bar)
[C:\Foo, C:\Bar] => false (..\Bar)
[C:\Foo, D:\Foo\Bar] => false (D:\Foo\Bar)
For those who care about performances, which seem to pass unnoticed to people who have already answered, checking whether the sub path starts with its parent path should be enough.
const path = require('path');
function isSubPathOf(subPath, parentPath) {
parentPath = normalize(parentPath);
if (subPath.length <= parentPath.length)
return false;
function normalize(p) {
p = path.normalize(p);
if (!p.endsWith(path.sep))
p += path.sep;
return p;
}
subPath = normalize(subPath);
return subPath.startsWith(parentPath);
}
console.log(isSubPathOf('/a/b/c/d/e', '/a/b/c'));
console.log(isSubPathOf('/a/b/c/de', '/a/b/c'));
console.log(isSubPathOf('/a/b/c', '/a/y/c'));
console.log(isSubPathOf('/a/y/c/k', '/a/y/c'));
2021 Answer
Use @Ilya's solution.
2017 Answer
In ES6.
const isChildOf = (child, parent) => {
if (child === parent) return false
let parentTokens = parent.split('/').filter(i => i.length)
let childTokens = child.split('/').filter(i => i.length)
return parentTokens.every((t, i) => childTokens[i] === t)
}
If you're working in node.js and you want to make it cross-platform, include the path
module and replace split('/')
with split(path.sep)
.
How it works:
So, you want to find out if a directory (like home/etc/subdirectory
) is a subdirectory of another directory (like home/etc
).
It takes both the hypothesised child
and parent
paths and convert them into arrays using split
:
['home', 'etc', 'subdirectory'], ['home', 'etc']
It then iterates through all of the tokens in the parent
array and checks them one-by-one against their relative position in the child
array using ES6's .every()
.
If everything in parent matches up to everything in child, knowing that we've ruled out they are exactly the same directory (using child !== parent
), we will have our answer.
This is a really old question, but I came up with a dead simple solution for this using Node's built–in path.relative
: if the child is inside the parent, the relative path from the former to the latter will always start with '..'
.
import { relative } from 'path';
function isSubDirectory(parent, child) {
return relative(child, parent).startsWith('..');
}