Exploring Directories in Node.js: Retrieve All Files and Folders
Add to your RSS feed3 November 20244 min readTable of Contents
In this tutorial, we’ll look at how to read all .mdx
files in a directory structure like this:
1 content2 └── posts3 ├── post1/post.mdx4 └── post2/post.mdx
Managing markdown files, especially in .mdx format, can be essential for content-driven applications. This guide shows how to retrieve and process .mdx files in a nested directory structure using Node.js, fs, and gray-matter.
Check out this collection of code snippets for handling various folder and file operations efficiently.
1. Retrieve Directories:
1 function getDirectories(srcpath) {2 return fs.readdirSync(srcpath)3 .map((file) => path.join(srcpath, file))4 .filter((path) => fs.statSync(path).isDirectory());5 }
This function lists all directories within a specified path by filtering out non-directory items.
2. Flattening Arrays:
1 function flatten(lists) {2 return lists.reduce((a, b) => a.concat(b), []);3 }
The flatten
function merges nested arrays into a single-level array, simplifying handling directory structures.
3. Recursive Directory Search:
1 function getDirectoriesRecursive(dir) {2 return [dir, ...flatten(getDirectories(dir).map(getDirectoriesRecursive))];3 }
Recursively collects all directories and subdirectories under a given path.
4. Retrieving MDX Files:
1 function getMDXFiles(dir) {2 return fs.readdirSync(dir).filter((file) => path.extname(file) === ".mdx");3 }
This function searches for .mdx
files in a specified directory.
5. Retrieve MDX Files from Multiple Folders:
1 function getMDXFilesInFolders(pathArr) {2 const files = [];3 pathArr.forEach((pathSrc) => {4 const file = fs.readdirSync(pathSrc).filter((file) => path.extname(file) === ".mdx");5 if (file.length > 0) {6 file.forEach((f) => {7 files.push(path.join(pathSrc, f));8 });9 }10 });11 return files;12 }
This function accepts an array of folder paths and retrieves .mdx
files from each.
6. Reading MDX File Content and Metadata:
1 function readMDXFile(filePath) {2 const rawContent = fs.readFileSync(filePath, "utf-8");3 return matter(rawContent);4 }
Here, gray-matter
is used to parse front matter (metadata) from MDX files.
7. Compiling MDX Data:
1 function getMDXData(dir) {2 const dirs = getDirectoriesRecursive(dir);3 const filesInDir = getMDXFilesInFolders(dirs);45 return filesInDir.map((file) => {6 const { data: metadata, content } = readMDXFile(file);7 const slug = path.basename(file, path.extname(file));89 return { metadata, slug, content };10 });11 }
8. Specific Data Retrieval for Blog
1 export function getBlogPosts() {2 return getMDXData(path.join(process.cwd(), "content", "blog"));3 }
These functions streamline the retrieval of data for specific types of content.
9. Formatting Dates:
1 export function formatDate(date, includeRelative = false) {2 const targetDate = new Date(date.includes("T") ? date : `${date}T00:00:00`);3 const fullDate = targetDate.toLocaleString("en-us", { month: "long", day: "numeric", year: "numeric" });45 if (!includeRelative) return fullDate;67 const currentDate = new Date();8 const difference = currentDate - targetDate;910 const formattedDate = difference > 0 ? `${Math.floor(difference / (1000 * 60 * 60 * 24))} days ago` : "Today";11 return `${fullDate} (${formattedDate})`;12 }
This function formats dates for blog posts, optionally including relative dates.
Full code
1 import fs from "fs";2 import path from "path";3 import matter from "gray-matter";45 function flatten(lists) {6 return lists.reduce((a, b) => a.concat(b), []);7 }89 function getDirectories(srcpath) {10 return fs11 .readdirSync(srcpath)12 .map((file) => path.join(srcpath, file))13 .filter((path) => fs.statSync(path).isDirectory());14 }1516 function getDirectoriesRecursive(dir) {17 return [dir, ...flatten(getDirectories(dir).map(getDirectoriesRecursive))];18 }1920 // get all the mdx files from the dir21 function getMDXFiles(dir: string) {22 return fs.readdirSync(dir).filter((file) => path.extname(file) === ".mdx");23 }24 function getMDXFilesInFolders(pathArr: string[]): string[] {25 const files = [];26 pathArr.forEach((pathSrc) => {27 const file = fs28 .readdirSync(pathSrc)29 .filter((file) => path.extname(file) === ".mdx");30 if (file.length > 0) {31 file.forEach((f) => {32 files.push(path.join(pathSrc, f));33 });34 }35 });36 return files;37 }38 // Read data from those files39 function readMDXFile(filePath: fs.PathOrFileDescriptor) {40 const rawContent = fs.readFileSync(filePath, "utf-8");41 return matter(rawContent);42 }43 // present the mdx data and metadata44 function getMDXData(dir: string) {45 const dirs = getDirectoriesRecursive(dir);46 const filesInDir = getMDXFilesInFolders(dirs);4748 return filesInDir.map((file) => {49 const { data: metadata, content } = readMDXFile(file);50 const slug = path.basename(file, path.extname(file));5152 return {53 metadata,54 slug,55 content,56 };57 });58 }5960 export function getBlogPosts() {61 return getMDXData(path.join(process.cwd(), "content", "blog"));62 }6364 export function formatDate(date: string, includeRelative = false) {65 const currentDate = new Date();66 if (!date.includes("T")) {67 date = `${date}T00:00:00`;68 }6970 const targetDate = new Date(date);7172 const yearsAgo = currentDate.getFullYear() - targetDate.getFullYear();73 const monthsAgo = currentDate.getMonth() - targetDate.getMonth();74 const daysAgo = currentDate.getDate() - targetDate.getDate();7576 let formattedDate = "";7778 if (yearsAgo > 0) {79 formattedDate = `${yearsAgo}y ago`;80 } else if (monthsAgo > 0) {81 formattedDate = `${monthsAgo}mo ago`;82 } else if (daysAgo > 0) {83 formattedDate = `${daysAgo}d ago`;84 } else {85 formattedDate = "Today";86 }8788 const fullDate = targetDate.toLocaleString("en-us", {89 month: "long",90 day: "numeric",91 year: "numeric",92 });9394 if (!includeRelative) {95 return fullDate;96 }9798 return `${fullDate} (${formattedDate})`;99 }
Conclusion
This setup allows you to organize and process .mdx
files in various directories, ideal for a blog or CMS system. Using fs
and gray-matter
, you can dynamically read and parse metadata and content, building a powerful, file-driven content management layer in Node.js.