How to bulk convert file names using Node.js
By Flavio Copes
Learn how to bulk convert file names with a Node.js script that uses glob to find files and fs.copyFile to rename index.md files into slug-based names.
You can bulk convert file names with a short Node.js script: find the files with a glob pattern, compute the new name for each one, then copy or rename them with the fs module.
Here’s the real case where I needed this. I had to convert my folders structure from something like this:
posts/test/index.mdposts/hey-cool-post/index.md
to this:
posts/test.mdposts/hey-cool-post.md
removing the folder that contains an index.md file, and instead have the markdown file itself have the post slug (the part that’s used as the post URL).
I had hundreds of posts, so renaming by hand was not an option.
I used a Node.js script to do this. The script relies on a glob pattern to find the files — you can test patterns like this in my glob pattern tester.
Here it is:
const fs = require('fs')
const glob = require('glob')
const root_folder = '.' //search in the current folder
glob(root_folder + '/**/index.md', (err, files) => {
if (err) {
console.log('Error', err)
} else {
for (const file_path of files) {
const match = file_path.match(/\/(.*?)\//)
const folder_name = match[1]
fs.copyFile(file_path, './' + folder_name + '.md', (err) => {
if (err) {
console.log('Error Found:', err)
} else {
console.log('File moved!')
}
})
}
}
})
How the script works
The './**/index.md' pattern matches every index.md file, at any depth, starting from the current folder. glob returns the list of matching paths.
For each path, the regular expression /\/(.*?)\// captures the text between the first two slashes. For ./hey-cool-post/index.md that’s hey-cool-post, the folder name I want to use as the file name.
Then fs.copyFile() copies index.md to a new file named after its folder, in the current directory.
Two things to watch out for
First, fs.copyFile() copies, it doesn’t move. The original index.md files and their folders are still there after the script runs. I liked that, because I could check the result before deleting anything. Once you’ve verified the new files, remove the old folders. And run this kind of script on a folder tracked by Git, so a wrong result is one checkout away from being undone.
Second, this script uses the callback API of the glob package, which worked up to version 8. Newer versions of glob removed it, and glob() now returns a promise. If you get an error about the callback, either install the older version with npm install glob@8, or await the result:
const { glob } = require('glob')
const files = await glob('./**/index.md')
The rest of the script stays the same.
Related posts about node: