Create your own modules in NodeJS
If you haven't already set up your Node sample project, you can quickly do so by following these instructions
In the previous step of this project, we used NPM to download a few modules from the NPM website. You can also create your own modules. Organizing your code into modules is an important strategy for developers. Modules are also called 'components'. Component based design is the practice of organizing your code into components/modules. This makes your code more flexible, maintainable, and easier to test.
This article is a good overview of component based design.
Create a 'grading helper' module
Create a folder named modules inside the project folder.
Inside the modules folder, create a file named grading-helpers.js and put this code into it:
/**
Takes a test score (a number) as a parameter, and returns a letter grade.
The letter grade should be determined by the score like so:
90 - 100 should return "A"
80 - 89 should return "B"
70 - 79 should return "C"
60 - 69 should return "D"
A score lower than 60 should return "F"
@param {number} score The score to be converted into a letter grade
@return {string} The letter grade for the score
*/
function calculateLetterGrade(score){
if(score >= 90){
return "A";
}else if(score >= 80){
return "B";
}else if(score > 70){
return "C";
}else if(score > 60){
return "D"
}else{
return "F"
}
}
/**
Takes an array of test scores (numbers) and returns the average for them.
@param {array<number>} scores The array of scores to be used in
calculating the average
@return {number} The average of the scores
*/
function calculateAverageScore(scores){
let total = 0;
scores.forEach(s => total += s);
return total/scores.length;
}
exports.calculateLetterGrade = calculateLetterGrade
exports.calculateAverageScore = calculateAverageScore
This module/component consists of two functions. The last two lines 'export' each function, which allows other files to 'import' the functions. If we did not export the functions, then they would be 'private', could only be invoked from inside the grading-helpers.js file. We want other files/modules to be able to use these functions, which is why we 'exported' them.
A NodeJS module implicitly has an 'exports' object, and any property that you add to it will be exported. So the last two lines in the module add properties to the exports object.
If you look closely at the code, you may spot a bug or two. Hopefully we'll address these issues when we talk about testing our code.
I included proper documentation for each function in the comments. In a real-world project, you should add comments that give a brief description of the each function, as well as a description the parameters and return value.
Now that we've created a module, and exported the parts that can be shared with other files in the project, let's create an example that uses it.
In the samples folder, create a file named grading-example.js, and put this code in it:
const {calculateLetterGrade, calculateAverageScore} = require('../modules/grading-helpers');
console.log(calculateLetterGrade(93));
const testScores = [90, 80];
console.log(calculateAverageScore(testScores));
The first line imports both functions from the module. To import modules (or parts of modules) you call the require() function, and pass in the path to the file that contains whatever you are importing. Remember from the previous activity (on the gray-matter and markdown-it modules), that if you use NPM to install a package into your node_modules folder, then you don't include a path to the module when you call require(). You just pass in the name of the module.
Here's a summary of the parameter that you pass into require():
- if the parameter that you pass into require() is a path (starts with a dot, or two dots) then NodeJS will follow the path
- if the parameter is just the name of a module (not a path), NodeJS will look for the module in the node_modules folder.
There is another way to import and export things in NodeJS, which is becoming more common, and it's known as ES6 modules. We will learn more about ES6 modules in the Web 3 class.
To run the example program, open the terminal and run this command (make sure the terminal is in the project folder):
node samples/grading-example.js
You should see the return values of our function calls in the console log.
We started our journey into NodeJS by using two modules that are 'built-in' to it. The fs and http modules come with NodeJS when you install it. Then we used NPM to install modules that help us work with markdown files (the gray-matter and markdown-it packages). Finally, we created our own custom module, and used it in the grading-example program.
Create a markdown-helpers module
Now we'll create another module, which will be very important because we'll use it in our final project. One of the great things about modules, is that they can be re-used in multiple projects.
Add a file named markdown-helpers.js to the modules folder, and put this code into it:
const matter = require('gray-matter'); // converts md file (with gray matter) into an object
const md = require("markdown-it")({html:true});// html:true allows you to put HTML tags in the markdown files
/**
* Converts a markdown file into an object that includes properties
* for the file's front matter, a 'content' property which consists
* of the files' markdown code, and an 'html' content which consists
* of the markdown code that has been converted to HTML
*
* @param {string} filePath The path to the markdown file
* @returns {object}
*/
function convertMarkdown(filePath){
// TODO: Put code here
}
/**
* Takes a path to a markdown file, reads the front-maktter (metadata)
* and returns an object that that includes the title, author, published
* date of the file.
* @param {string} filePath The path to the markdown file
* @returns {object} An object that has a title, author, and published property
*/
function getMarkdownMetaData(filePath){
// TODO: Put code here
}
exports.convertMarkdown = convertMarkdown;
exports.getMarkdownMetaData = getMarkdownMetaData;
This module imports the following NPM packages/modules:
- gray-matter for converting the contents and metadata of a markdown file into a JavaScript object (we used the variable name matter to store the 'gray-matter' object)
- markdown-it for converting markdown code into HTML code ( we used the variable md to store the markdown-it object)
The module then defines two functions, and we'll add the code for those functions next.
Finally, the module exports the two functions so that they can be 'imported' and used in other JavaScript files within the project.
Now, update the convertMarkdown() function to look like this:
function convertMarkdown(filePath){
const obj = matter.read(filePath);
if(obj){
obj.html = md.render(obj.content);
}
return obj;
}
Now we'll create a sample/test program so that we can try out the function.
Create a file named markdown-helper-tests.js in the samples, and put this code into it:
const {convertMarkdown, getMarkdownMetaData} = require("../modules/markdown-helpers");
const filePath = __dirname + "/sample.md"
console.log(convertMarkdown(filePath));
Run the test program by entering this in the terminal (make sure you run the command from the project folder):
node samples\markdown-helper-tests.js
In the console, you should see the object that is returned by calling the read() method of the gray-matter module, which includes the content, and metadata from the markdown file (among other things) The object should also include an html property which displays the markdown code that has been converted to HTML. We used the markdown-it module's render() method to add this property to the object that is returned by our function.
This function will be the cornerstone of our final project! So make sure you understand it, by stepping through the above sample code with the debugger.
There will be cases in the final project when we only need certain metadata from a markdown file (the convertMarkdown() function would be overkill because we may not need the HTML). The getMarkdownMetaData() function will do just that - get the metadata that we are interested from an .md file.
Update the getMarkdownMetaData() function (in markdown-helpers.js) to look like this:
function getMarkdownMetaData(filePath){
const obj = matter.read(filePath);
const metaData = {
title: obj.data.title || "No Title",
author: obj.data.author || "No Author",
description: obj.data.description || "No Description",
published: obj.data.published || false
}
return metaData;
}
Now add this code to the markdown-helper-tests program:
console.log(getMarkdownMetaData(filePath));
Now, when you run the test program (node samples\markdown-helper-tests.js), you should see an object in the console log that includes properties for the metadata in the markdown file. Note that if the .md file does not contain one of the metadata properties that we're interested in, we provide a default value (such as 'No Title').
Make sure to step through code we've just written with the debugger. Understanding the mechanics of these functions will really help you to understand the final project.
Commit your changes to your Git repository.
Then create a separate branch, so that we can easily return our project to the current state if we need to. Run this command:
git branch 5-modules-complete
Finally, make sure to push this branch, and your main/master branch to GitHub.
In the next lesson, you'll Get Started with Express