Initializing Node.js Projects
Initializing a Node.js Project
Typically, for each Node.js project, a directory is created to store all the files and packages related to the project. A directory does not necessarily need to be empty to initialize it, so we will continue working with the greeting
project.
npm can store all the metadata and manage the required packages, along with their respective versions, through a manifest file. One way to create this manifest file is with the npm init
command, which will display a wizard that guides you through questions to gather the necessary information for the project until the process is completed. However, a quicker way to create the manifest file with all the default options is by using the following command:
npm init -y
The result is a text file named package.json
, which contains all the basic information generated by default. These values can be changed simply by editing and saving the changes in the file. It is best for this file to be located at the root of the project directory.
Optionally, to avoid having to change the manifest file information every time a new project is created, it is possible to set default values for when the npm init
command is executed. For example:
npm set init.author.name "Gustavo Morales"
npm set init.author.email "gustavo.morales@gmail.com"
npm set init.author.url "https://gmoralesc.me"
The other option is to save these values in a file named .npmrc
in the root of the user’s directory or in the project’s directory, to be more specific. For more information about the .npmrc
file, refer to the npmrc documentation.
For more information:
Managing Packages and Configuration with the package.json File
Installing Dependencies
The package.json
file is very important as it will be the application’s manifest. From now on, to install a package as a dependency in the project, we add the -S
or, if we want to be more explicit, the --save
flag.
Before installing a package, you can get all the information on it with the following command:
npm info colors
Install the dependency in the project with the following command:
npm install colors
In the package.json
file, the following section has been added:
"dependencies": {
"colors": "^1.4.0"
}
This lists the project’s dependencies and the installed version of each package. If someone else copied this project and installed the dependencies (with the npm install
command), they would get the minimum version of the colors
package equivalent to 1.4.0
. The ^
character at the beginning of the package version indicates that any upgrade or patch version 1 can be installed, but no version with the major release 2 will be installed; in other words, it can install 1.4.0 <= x < 2.0.0
. This nomenclature is called semver and allows you to set versioning rules for the project’s packages.
To list all the project’s dependencies, use the following command:
npm list
Or for a particular dependency, add the name of the dependency with the following command:
npm list colors
There are packages that are not dependencies but are needed in the local development environment; for example, the ESLint package allows you to check the syntax of JavaScript files based on a set of rules. This can be very useful for standardizing code by selecting or creating a style guide. To include it in the manifest as a development package, use the -D
or --save-dev
flag.
npm install --save-dev eslint
Many npm subcommands have shortcuts; in the previous example, you can replace
install
with the abbreviationi
, i.e.,npm i -D eslint
.
Now, in the package.json
file, a new section is included for packages to be used in the development environment. This is how npm distinguishes the dependencies needed to be installed in production environments.
"devDependencies": {
"eslint": "^8.56.0"
},
Configuring git
It is very important to note that if you are using a version control system, the node_modules
directory should be ignored, as it is not part of the project’s source code and may vary depending on the versions of the packages installed each time.
If you are using git, initialize the repository if it is not already initialized:
git init
In the root of the project directory, create a file named .gitignore
and add the following lines:
node_modules/
.DS_Store
Thumbs.db
With this configuration, files or directories matching this pattern will not be included in the git repository. You should also include files like .DS_Store
for Mac OS or Thumbs.db
for Windows, which are temporary files used for generating directory previews (thumbnails).
If the project is going to be hosted on a public repository like GitHub, GitLab, or Bitbucket, it is highly recommended to create a file named README.md
with the project description. Once published, the repository will display preliminary information about the project. The syntax for writing this file is Markdown.
For more information:
Configuring npm Scripts
When working on a project, we often repeat the same commands in the terminal over and over again. These commands can be created to streamline the workflow, and npm does this through scripts. In the package.json
file, you will see the following section:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
In this section, you can specify the commands you want to use in the terminal. The previous text is configured so that the test
script prints a string to the screen, indicating that tests have not been set up yet. This script can be executed as follows:
npm test
Built-in Scripts
npm has several built-in script aliases for common tasks, such as test
, which you saw in the previous section. As the name suggests, it is used to run the project’s tests. Another highly recommended script is start
, which has become a convention for starting Node.js projects and will be configured later.
Before adding the script, note that the main
key in the package.json
file declares the default entry file for the project to run. This file is named index.js
.
By convention, the main file is named
index.js
, but it can be any name and even in a different location, not necessarily in the project’s root directory.
Modify the scripts
section of the package.json
file:
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\"&& exit 1"
},
From now on, to start the Node.js application, run the following command:
npm start
Custom Scripts
It is possible to create custom scripts to perform different types of tasks. In the previous section, we installed and used the global ESLint package. Now, let’s create a custom script to check the project’s files:
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\"&& exit 1",
"lint": "eslint --no-eslintrc *.js"
},
To execute this custom task, add the run
prefix this time, as it is not a built-in script:
npm run lint
To list all the tasks that can be executed with npm, you can run the following command:
npm run
Conventions for a Project Using npm
Typically, all projects using npm as a package and dependency manager follow this convention whenever the project is downloaded or cloned:
npm install
npm start
And if you want to run tests, then npm test
. It is recommended to include these instructions in the README.md
file.
Using Dependencies and Scripts
Edit the index.js
file with the following content:
const colors = require('colors/safe');
const args = process.argv.slice(2);
const [name = 'Friend'] = args;
const hour = new Date().getHours();
// Ask for hours range
if (hour >= 6 && hour < 12) {
console.log(colors.yellow(`Good morning ${name}`));
} else if (hour >= 12 && hour < 18) {
console.log(colors.green(`Good afternoon ${name}`));
} else if (hour >= 18 && hour < 23) {
console.log(colors.cyan(`Good evening ${name}`));
} else {
console.log(colors.blue(`Good night ${name}`));
}
To run the program, use the following command. This time, to pass arguments, use a special parameter (--
) so that it takes the command-line arguments:
npm start -- Gustavo
Pre and Post Scripts
It is possible to take advantage of the npm script conventions to run other scripts before and/or after another script. For example:
"scripts": {
"postinstall": "npm test",
"prestart": "npm run lint",
"start": "node index.js",
"test": "echo \"Error: no test specified\"&& exit 1",
"lint": "eslint **.js"
},
In the previous example, after running the npm install
script, the commands specified in the postinstall
script will automatically run (this is not limited to just executing other scripts). Additionally, before running the npm start
script, the commands specified in the prestart
script will automatically run. The order in which they are placed in the package.json
file within the scripts section does not matter; what matters is the nomenclature.
Standardizing Command-Line Arguments
Every time you run a Node.js application, you are sending at least one argument to the Node.js executable (node
). This argument is the application’s entry file (index.js
).
node index Gustavo
node index Gustavo Morales
node index "Gustavo Morales"
node index name=Gustavo
node index -n "Gustavo Morales"
As you can see, Node.js separates the arguments by space and stores them in the same order they were input. Therefore, for the second example, it would be incorrect to send the name separated by space, as it would be interpreted as two arguments. The correct way would be as shown in the third example.
One way to create named arguments can be seen in the fourth example, where you would then need to separate the key from the value. Lastly, the traditional way of using named arguments exists, but as you can see, it would be quite a bit of work to interpret what is stored in process.argv
. There are many useful libraries for this case; in the following example, you will use command-line-args
. Install it as a project dependency with the following command:
npm i command-line-args
Add the following content to the index.js
file:
const colors = require('colors/safe');
const commandLineArgs = require('command-line-args');
const params = [{ name: 'name', alias: 'n', type: String }];
const options = commandLineArgs(params);
const name = options.name || 'Friend';
const hour = new Date().getHours();
...
In the params
variable, the expected arguments and their types are specified for the library to process and finally store the result in the options
variable, which is a key/value object with each of the declared arguments.
Run the application again with the following arguments:
npm start -- -n Gustavo
Finally, you will get the same result.
It is very important to validate that the arguments are correct. In this exercise, and in the following ones of this type, it is assumed that the user inputs the arguments correctly. To practice and go deeper, you can perform your own validations.
For more information: