JavaScript Development Space

Exploring Directories in Node.js: Retrieve All Files and Folders

Add to your RSS feed3 November 20244 min read
Exploring Directories in Node.js: Retrieve All Files and Folders

In this tutorial, we’ll look at how to read all .mdx files in a directory structure like this:

bash
1 content
2 └── posts
3 ├── post1/post.mdx
4 └── 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:

js
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:

js
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.

js
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:

js
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:

js
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:

js
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:

js
1 function getMDXData(dir) {
2 const dirs = getDirectoriesRecursive(dir);
3 const filesInDir = getMDXFilesInFolders(dirs);
4
5 return filesInDir.map((file) => {
6 const { data: metadata, content } = readMDXFile(file);
7 const slug = path.basename(file, path.extname(file));
8
9 return { metadata, slug, content };
10 });
11 }

8. Specific Data Retrieval for Blog

js
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:

js
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" });
4
5 if (!includeRelative) return fullDate;
6
7 const currentDate = new Date();
8 const difference = currentDate - targetDate;
9
10 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

js
1 import fs from "fs";
2 import path from "path";
3 import matter from "gray-matter";
4
5 function flatten(lists) {
6 return lists.reduce((a, b) => a.concat(b), []);
7 }
8
9 function getDirectories(srcpath) {
10 return fs
11 .readdirSync(srcpath)
12 .map((file) => path.join(srcpath, file))
13 .filter((path) => fs.statSync(path).isDirectory());
14 }
15
16 function getDirectoriesRecursive(dir) {
17 return [dir, ...flatten(getDirectories(dir).map(getDirectoriesRecursive))];
18 }
19
20 // get all the mdx files from the dir
21 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 = fs
28 .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 files
39 function readMDXFile(filePath: fs.PathOrFileDescriptor) {
40 const rawContent = fs.readFileSync(filePath, "utf-8");
41 return matter(rawContent);
42 }
43 // present the mdx data and metadata
44 function getMDXData(dir: string) {
45 const dirs = getDirectoriesRecursive(dir);
46 const filesInDir = getMDXFilesInFolders(dirs);
47
48 return filesInDir.map((file) => {
49 const { data: metadata, content } = readMDXFile(file);
50 const slug = path.basename(file, path.extname(file));
51
52 return {
53 metadata,
54 slug,
55 content,
56 };
57 });
58 }
59
60 export function getBlogPosts() {
61 return getMDXData(path.join(process.cwd(), "content", "blog"));
62 }
63
64 export function formatDate(date: string, includeRelative = false) {
65 const currentDate = new Date();
66 if (!date.includes("T")) {
67 date = `${date}T00:00:00`;
68 }
69
70 const targetDate = new Date(date);
71
72 const yearsAgo = currentDate.getFullYear() - targetDate.getFullYear();
73 const monthsAgo = currentDate.getMonth() - targetDate.getMonth();
74 const daysAgo = currentDate.getDate() - targetDate.getDate();
75
76 let formattedDate = "";
77
78 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 }
87
88 const fullDate = targetDate.toLocaleString("en-us", {
89 month: "long",
90 day: "numeric",
91 year: "numeric",
92 });
93
94 if (!includeRelative) {
95 return fullDate;
96 }
97
98 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.

JavaScript Development Space

© 2024 JavaScript Development Space - Master JS and NodeJS. All rights reserved.