DEV Community

Cover image for Building “.ignore” mechanism with 30 lines of code
Ahmed Abdalla Elbilal
Ahmed Abdalla Elbilal

Posted on

Building “.ignore” mechanism with 30 lines of code

Recently we have seen .Ignore files a lot in many places, GitHub has .gitignore to ignore file that will not be committed also prettier .prettierignore to ignore files that will not be formatted and more others. So how can I build such a mechanism ?

How it will work ?

We will load the file and change all of it's content to regex so that we can make the comparing functionality more efficient.

fs module

We will be using fs module which is one of NodeJS core modules, fs have promises object which contains a promise version of core fs functions like raedFile and writeFile to help us work with async/await to make the code cleaner.

Loading the file

const fs = require("fs");
const loadIgnoreFile = async (file) => {
  let loadedFile = await fs.promises.readFile(path, { encoding: "utf8" });
};
Enter fullscreen mode Exit fullscreen mode

Change * to .* for pattern ignore

In .ignore files, a person could want to ignore a pattern of files, for example all *.json files, but regex could understand * with a character before because * means that the preceding character can be repeater more than one time.

  //change all * to .*
  loadedFile = loadedFile.replace(/\*/g, ".*");
Enter fullscreen mode Exit fullscreen mode

File content to array

  //change file to array
  loadedFile = loadedFile.split("\n");
Enter fullscreen mode Exit fullscreen mode

Resolve array items to paths

For example, if a person wanted to ignore dist folder he could enter dist, ./dest or /dist which can make the comparison operation more difficult. So we change what ever he entered to a full file path, so the three options of dist folder will be for example /home/user/project/dist.

  //resolve all files
  loadedFile = loadedFile.map((item) => join(resolve("."), item));
Enter fullscreen mode Exit fullscreen mode

Change paths to groups of regex

A group in regex in something between two () for example (ABC) is a group. And then separate each group from other by or operator | and then to make the regex only restricted to that path we will add ^ $ to each regex path. So in our work if we have two paths, let us say /home/user/project/dist and /home/user/project/node_mdules they are at the end after converting them to regex will be (^/home/user/project/dist&)|(^/home/user/project/node_mdules$)

  //add (^filename$) to all content
  loadedFile = "(^" + loadedFile.join("$)|(^") + "$)";
Enter fullscreen mode Exit fullscreen mode

Returning the result in RegExp object

  //change loaded file content to regex
  return new RegExp(loadedFile);
Enter fullscreen mode Exit fullscreen mode

We could also add some checks to see if the file exits and return a default regex if not before we go to all these steps.

Final code

const fs = require("fs");
const { resolve, join } = require("path");

//load ignore file
const loadIgnoreFile = async (  const path = ".ignore") => {
  const dfReg = "(.git$)|(node_modules$)";

  //check if .ignore exits
  const ptIgnoreExits = fs.existsSync(path);
  if (!ptIgnoreExits) return new RegExp(dfReg);

  //load .ignore
  let loadedFile = await fs.promises.readFile(path, { encoding: "utf8" });

  // add .git and node_modules to loaded file
  loadedFile += "\n.git\nnode_modules";

  //change all * to .*
  loadedFile = loadedFile.replace(/\*/g, ".*");

  //change .ignore to array
  loadedFile = loadedFile.split("\n");

  //resolve all files
  loadedFile = loadedFile.map((item) => join(resolve("."), item));

  //add (^filename$) to all content
  loadedFile = "(^" + loadedFile.join("$)|(^") + "$)";

  //change loaded file content to regex
  return new RegExp(loadedFile);
};

module.exports = loadIgnoreFile;
Enter fullscreen mode Exit fullscreen mode

Test it

This is a simple test and will not contain any test runners or any frameworks like jest or others

Create a file say .ignore and add to it some dummy names say dist1 and dist2 and put the .ignore along with this code in the same folder. Create a new file called ignore.test.js and import the loadIgnore function and run a function in loadIgnore called test since loadIgnore is actually a regex object as we see above, the test function will return a boolean value.

const loadIgnore = require('./loadIgnore.js');
const filePath = join(resolve("."), "dist1"))
console.log(loadIgnore.test(filePath));
Enter fullscreen mode Exit fullscreen mode

Top comments (0)