Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/Filters/GetParentDirectory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Returns the parent directory of a given path.
*
* For directory paths (ending with /): returns the parent directory
* For file paths: returns the directory containing the file
*
* Examples:
* /mydir/ -> /
* /mydir/slug.html -> /mydir/
* /mydir/index.md -> /mydir/
* /a/b/c/ -> /a/b/
* /file.html -> /
*
* @param {string} path - The path to get the parent directory of
* @returns {string} The parent directory path
*/
export default function getParentDirectory(path) {
if (!path || typeof path !== "string") {
return "";
}

// If path ends with /, it's a directory - get its parent
if (path.endsWith("/") && path.length > 1) {
// Remove trailing slash, then find parent
let withoutTrailing = path.slice(0, -1);
let lastSlash = withoutTrailing.lastIndexOf("/");
if (lastSlash === -1) {
return "/";
}
return withoutTrailing.slice(0, lastSlash + 1) || "/";
}

// Handle root directory
if (path === "/") {
return "/";
}

// Otherwise it's a file path - get the directory containing it
let lastSlash = path.lastIndexOf("/");
if (lastSlash === -1) {
// No slash found, no parent directory
return "";
}
if (lastSlash === 0) {
// File is at root level
return "/";
}
return path.slice(0, lastSlash + 1);
}
6 changes: 6 additions & 0 deletions src/defaultConfigExtended.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import MemoizeUtil from "./Util/MemoizeFunction.js";
import urlFilter from "./Filters/Url.js";
import getLocaleCollectionItem from "./Filters/GetLocaleCollectionItem.js";
import getCollectionItemIndex from "./Filters/GetCollectionItemIndex.js";
import getParentDirectory from "./Filters/GetParentDirectory.js";
import { FilterPlugin as InputPathToUrlFilterPlugin } from "./Plugins/InputPathToUrl.js";

/**
Expand Down Expand Up @@ -98,4 +99,9 @@ export default function (config) {

return urlFilter.call(this, url, pathPrefix);
});

// Returns the parent directory of a path
// For directories (/mydir/): returns parent (/mydir/ -> /)
// For files (/mydir/file.html): returns containing directory (/mydir/file.html -> /mydir/)
config.addFilter("getParentDirectory", getParentDirectory);
}
55 changes: 55 additions & 0 deletions test/GetParentDirectoryTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import test from "ava";
import getParentDirectory from "../src/Filters/GetParentDirectory.js";

test("Directory paths - returns parent directory", (t) => {
// Examples from issue #3794
t.is(getParentDirectory("/mydir/"), "/");

// Nested directories
t.is(getParentDirectory("/a/b/"), "/a/");
t.is(getParentDirectory("/a/b/c/"), "/a/b/");
t.is(getParentDirectory("/a/b/c/d/"), "/a/b/c/");
});

test("File paths - returns containing directory", (t) => {
// Examples from issue #3794
t.is(getParentDirectory("/mydir/slug.html"), "/mydir/");
t.is(getParentDirectory("/mydir/index.md"), "/mydir/");

// Nested file paths
t.is(getParentDirectory("/a/b/file.html"), "/a/b/");
t.is(getParentDirectory("/a/b/c/page.md"), "/a/b/c/");
});

test("Root level paths", (t) => {
// Root directory
t.is(getParentDirectory("/"), "/");

// File at root
t.is(getParentDirectory("/file.html"), "/");
t.is(getParentDirectory("/index.md"), "/");
});

test("Edge cases", (t) => {
// Empty and invalid inputs
t.is(getParentDirectory(""), "");
t.is(getParentDirectory(null), "");
t.is(getParentDirectory(undefined), "");

// Non-string inputs
t.is(getParentDirectory(123), "");
t.is(getParentDirectory({}), "");

// Relative paths (no leading slash)
t.is(getParentDirectory("file.html"), "");
t.is(getParentDirectory("dir/file.html"), "dir/");
t.is(getParentDirectory("a/b/c/"), "a/b/");
});

test("Single segment paths", (t) => {
// Directory with single segment
t.is(getParentDirectory("/foo/"), "/");

// File with single segment at root
t.is(getParentDirectory("/foo"), "/");
});