# Node & Node Modules

{% hint style="info" %}
Follow along with code examples [here](https://github.com/The-Marcy-Lab-School/1-0-0-intro-to-node)!
{% endhint %}

**Table of Contents:**

* [Slides](#slides)
* [Overview](#overview)
  * [Key Terms](#key-terms)
* [What is Node?](#what-is-node)
* [Exporting and Importing Node Modules](#exporting-and-importing-node-modules)
  * [Exporting with `module.exports` (CommonJS)](#exporting-with-moduleexports-commonjs)
  * [Importing with `require()` (CommonJS)](#importing-with-require-commonjs)
  * [Destructuring](#destructuring)
* [Node Package Manager (NPM)](#node-package-manager-npm)
  * [Installing and Using Dependencies from NPM](#installing-and-using-dependencies-from-npm)
  * [`package.json` and `node_modules`](#packagejson-and-node_modules)
  * [Developer Dependencies](#developer-dependencies)
  * [`package.json` Scripts and `nodemon`](#packagejson-scripts-and-nodemon)
  * [`npm init -y`](#npm-init--y)
* [Madlib Challenge](#madlib-challenge)

## Slides

{% embed url="<https://docs.google.com/presentation/d/10Op4ZnmbUbm1Zv48vcIR5h2vkr9wpZULwAg6NYbzd9w/embed?start=false&loop=false&delayms=3000>" %}

## Overview

In this lesson we'll learn the history of Node and the fundamentals of using Node Modules to build JavaScript scripts.

### Key Terms

* **Node** is a "JavaScript runtime environment" which is just a fancy way to say it is a program for running JavaScript.
* A **module** is an exported chunk of code (typically a function or a set of functions) that can be used across our project.
* In a Node project, a file exports a module by assigning a value to `module.exports`
* A file can export a module in two ways:
  * It can export one value/function (a single **"default export"**)
  * It can export many values/functions (a group of **"named exports"**)
* A module can be imported with the `require(filename)` function.
* Modules can be downloaded from the **Node Package Manager (NPM)** online registry
* When a module is downloaded, it is called a **dependency**
* `package.json` is a file with meta-data about a Node project including dependencies and configuration.
* **JavaScript Object Notation (JSON)** is a file format for representing data in an JavaScript-Object-like notation with key:value pairs.
* **Developer dependencies** are dependencies used by the developer(s) who created the package but aren't needed by the users of the package. They are not added to the `node_modules` folder of the user.

## What is Node?

* JavaScript started out as a language that could only be executed by a browser.
* In 2009, Node was invented to allow programmers to run JavaScript on their own computers without a browser, opening the door for fullstack JavaScript development.
* **Node** is a "JavaScript runtime environment" which is just a fancy way to say it is a program for running JavaScript.
* To run a JavaScript program, we use the command `node <file_name>`

![Node is a program for running JavaScript files](/files/HrczSMbpNDgdT06Dyb7e)

* If you use the `node` command on its own, you will open the Node Read Evaluate Print Loop (REPL) program.

![The Node REPL is useful for testing out expressions.](/files/XqJeyvMvNxb3PuPbGTd2)

## Exporting and Importing Node Modules

Consider the simple JavaScript program below. It declares a few functions for calculating data about a circle with a given radius and then prints them out.

{% code title="index.js" overflow="wrap" lineNumbers="true" %}

```js
// functions for circle stuff
const LAZY_PI = 3.14;

const getArea = (radius) => {
  return LAZY_PI * radius * radius;
}

const getDiameter = (radius) => {
  return radius * 2;
}

const getCircumference = (radius) => {
  return LAZY_PI * radius * 2;
}

// function for printing stuff. It is a "wrapper" for the console.log function
const print = (input) => {
  console.log(input);
}

// The main function just runs all of the other functions
const main = () => {
  const radius = 5;
  const area = getArea(radius);
  print(`the area of a circle with radius ${radius} is ${area}`);

  const diameter = getDiameter(radius);
  print(`the diameter of a circle with radius ${radius} is ${diameter}`);

  const circumference = getCircumference(radius);
  print(`the circumference of a circle with radius ${radius} is ${circumference}`);
}

main();
```

{% endcode %}

* Notice that the `main` function just uses the other functions.
* JavaScript projects are rarely built entirely in one file like this. Instead, code is separated into multiple files that share code with each other.
* These shared pieces of code are called **modules**. A **module** is an exported chunk of code (typically a function or a set of functions) that can be used across our project.
  * For example, a file can create a function and then **export** it.
  * Meanwhile, another file can **import** that function and use it.
* Modules help with organization and **separation of concerns** but require more files!

{% hint style="info" %}
Separation of Concerns is a fundamental principle of software engineering. It emphasizes the importance of organizing our code into distinct functions and modules that each serve a singular and specific purpose. However, when put together, those individual pieces work in harmony.
{% endhint %}

### Exporting with `module.exports` (CommonJS)

* In a Node project, a file exports a module by assigning a value/function to `module.exports`.

{% code title="print.js" overflow="wrap" lineNumbers="true" %}

```javascript
const print = (input) => {
  console.log(input)
}
module.exports = print;
```

{% endcode %}

* When `module.exports` is assigned a single value/function, we call that a **default export**.
* You will also commonly see `module.exports` be assigned to an object containing multiple values/functions. These are called **named exports**:

{% code title="circle-helpers.js" overflow="wrap" lineNumbers="true" %}

```javascript
const LAZY_PI = 3.14;

const getArea = (radius) => {
  return LAZY_PI * radius * radius;
}
const getDiameter = (radius) => {
  return radius * 2;
}
const getCircumference = (radius) => {
  return LAZY_PI * radius * 2;
}

module.exports = {
  LAZY_PI,
  getArea,
  getDiameter,
  getCircumference
};
```

{% endcode %}

### Importing with `require()` (CommonJS)

* To import a value/function exported from another file, use the `require(filepath)` function and provide a `filepath` argument.

{% code title="index.js" overflow="wrap" lineNumbers="true" %}

```javascript
// The variable name here is up to you since only the value is exported, not the variable.
const print = require('./print.js');
  
// circleHelpers is what we'll call the object that is exported
const circleHelpers = require('./circle-helpers.js');

const main = () => {
  const radius = 5;
  
  // the getArea method is INSIDE of the circleHelpers object so we use dot notation
  const area = circleHelpers.getArea(radius);

  // print was the default export of print.js so we can just invoke it.
  print(`the area of a circle with radius ${radius} is ${area}`);

  //... the rest of the code ...
}

main();
```

{% endcode %}

### Destructuring

* If the exported value is an object, we typically will **destructure** the object immediately upon importing it:

{% code title="index.js" overflow="wrap" lineNumbers="true" %}

```javascript
// With destructuring, we create a variable for each named export.
const { getArea, getDiameter, getCircumference } = require('./circle-helpers.js');

const main = () => {
  const radius = 5;
  
  // We can just invoke getArea now without digging through an object.
  const area = getArea(radius);
  print(`the area of a circle with radius ${radius} is ${area}`);

  //... the rest of the code ...
}

main();
```

{% endcode %}

## Node Package Manager (NPM)

* Modules can be downloaded from the **Node Package Manager (NPM)** online registry.
* When you download a package, it is called a **dependency**.
* Visit <https://www.npmjs.com/> to explore available packages. Start by searching up the "prompt-sync" package.

![The prompt sync page on npmjs.com shows the synopsis, basic usage, and an installation command](/files/dYwzyt7Rlf3uHHNOlauD)

### Installing and Using Dependencies from NPM

* To install any module from npmjs, use the `npm install` (or `npm i`) command in your Terminal:

```sh
npm i prompt-sync # installs the prompt-sync package
```

* Now, you should see a `node_modules/` folder with a `prompt-sync/` folder inside. Open up the `prompt-sync/index.js` file to see the module that is exported!
* To use the package in our own program, use `require()` again, this time with just the name of the module.

{% code title="5-prompt-sync/index.js" overflow="wrap" lineNumbers="true" %}

```javascript
// with modules in node_modules, we only need to provide the name:
const prompt = require('prompt-sync')();

// we don't need to provide the full relative path (although we can)
const prompt = require('./node_modules/prompt-sync')();
```

{% endcode %}

{% hint style="info" %}
For `prompt-sync`, the exported module is not the `prompt` function itself, but instead a `create` function which lets the programmer configure how they want `prompt` to work (see `node_modules/prompt-sync/index.js`)

This is a common pattern for modules.
{% endhint %}

### `package.json` and `node_modules`

* Every dependency of a project, and its version number, will be listed in the file `package.json` (if the file doesn't exist, the `npm i` command will create it). The existence of this file turns our project into a **package**.
* **JavaScript Object Notation (JSON)** is a file format for representing data in an JavaScript-Object-like notation with key:value pairs.

![Installing a module using npm adds the module name and version number to a list of dependencies in the package.json file](/files/c4t5L4RbSEXm97GeZl5H)

* The downloaded module will be placed in a folder called `node_modules/` along with any **sub-dependencies** that the module itself may require.
* You can see the sub-dependencies of a module by opening its own `package.json` file. All modules listed under `"dependencies"` will also be installed in `node_modules/`.
  * In `prompt-sync/package.json`, we can see it has `strip-ansi` as a dependency.
  * In `strip-ansi/package.json`, we can see it has `ansi-regex` as a dependency.

![For prompt-sync, we can see that it has two of its own dependencies that were also added to node\_modules: ansi-regex and strip-ansi:](/files/h9U3kVubj8X5uw7Agn87)

### Developer Dependencies

* In the `prompt-sync/package.json` file, you will notice that `prompt-sync-history` is listed under `"devDependencies"`.
* **Developer dependencies** are dependencies used by the developer(s) who created the package but aren't needed by the users of the package. They are not added to the `node_modules` folder of the user.
* Try installing the `nodemon` module as a developer dependency using the `npm i -D` command:

```sh
npm i -D nodemon
```

* You should see `nodemon` added to the `"devDependencies"` section of `package.json` (version numbers may vary):

```json
{
  "dependencies": {
    "prompt-sync": "^4.2.0"
  },
  "devDependencies": {
    "nodemon": "^3.1.7"
  }
}
```

* The `nodemon` module installs a new command `nodemon` that can be used to run a JavaScript file in "hot reload" mode. This means that any time you save a file in the project, it will re-run the file.

```sh
# runs index.js once
node index.js 

# re-runs index.js each time the file changes
nodemon index.js
```

<details>

<summary><strong>Q: Why is <code>nodemon</code> installed as a developer dependency and not a required dependency of the project?</strong></summary>

`nodemon` makes it easier to test your code but the users of the package do not strictly need its functionality. It is just a convenience.

</details>

### `package.json` Scripts and `nodemon`

* You can add a `"scripts"` section to the `package.json` file to make it easier to run commonly used Terminal commands.
* Two common script commands to add are `"start"` which runs `node index.js` and `"dev"` which runs `nodemon index.js`

```json
{
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js"
  },
  "dependencies": {
    "prompt-sync": "^4.2.0"
  },
  "devDependencies": {
    "nodemon": "^3.1.7"
  }
}
```

* To use these commands, we can type `npm run script_name`. For example, `npm run start` or `npm run dev`

### `npm init -y`

* When working on a new Node project, it is common to set up the `package.json` file prior to installing any dependencies.
* This can be done using the command `npm init` which will ask you some questions to generate a `package.json` file.

```json
{
  "name": "project-name",
  "version": "0.0.1",
  "description": "Project Description",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "the repositories url"
  },
  "author": "your name",
  "license": "N/A"
}
```

* You can also run `npm init -y` to skip the questions and build a `package.json` file using default values.

## Madlib Challenge

A program is considered **hard-coded** if the program code must be modified in order to produce a new result.

The `prompt-sync` function is really useful for creating programs that will produce new results depending on the user's input.

{% code title="5-prompt-sync/index.js" overflow="wrap" lineNumbers="true" %}

```javascript
const prompt = require('prompt-sync')();

const name = prompt(`Hello there! What's your name? `);
console.log(`hi ${name}. My name is HAL`);
```

{% endcode %}

In the `6-madlib-challenge/index.js` file you will find the following hard-coded program:

{% code title="6-madlib-challenge/index.js" overflow="wrap" lineNumbers="true" %}

```javascript
const madlib = (name, verb, quantity, item, newItem, isHappy) => {
  console.log(`There once was a man named ${name}.`);
  console.log(`Every day he would ${verb} with his ${quantity} ${item}s`);

  if (isHappy) {
    console.log(`But then, he found a ${newItem} and everything changed!`);
  } else {
    console.log(`But then, a ${newItem} took over his life and he couldn't ${verb} again!`);
  }

  console.log("The end.")
}

const main = () => {
  const name = 'Ben';
  const verb = 'run';
  const quantity = 50;
  const item = 'dog';
  const newItem = 'new car';
  const isHappy = false;

  madlib(name, verb, quantity, item, newItem, isHappy);
}
```

{% endcode %}

Your goal is to do the following in the `6-madlib-challenge` folder:

1. Download the `prompt-sync` module using `npm i prompt-sync`
2. Import and configure the `prompt` function in `index.js`
3. Replace the hard-coded variables defined in the `main` function with values retrieved from the user via the `prompt` function.
4. Re-organize the code such that the `madlib` function is in its own file called `madlib.js` that exports `madlib` as the default export.

If you get stuck, you can view the solution below:

<details>

<summary><strong>Q: Solution</strong></summary>

{% code title="index.js" overflow="wrap" lineNumbers="true" %}

```javascript
const madlib = require('./madlib.js');
const prompt = require('prompt-sync')();

const main = () => {
  const name = prompt('Choose a name: ')
  const verb = prompt('Choose a verb: ')
  const quantity = prompt('Choose a quantity: ')
  const item = prompt('Choose a item: ')
  const newItem = prompt('Choose a newItem: ')
  const isHappyResponse = prompt('Choose whether the story is happy. Y or N: ')
  const isHappy = isHappyResponse.toUpperCase() === "Y"

  madlib(name, verb, quantity, item, newItem, isHappy);
}

main();
```

{% endcode %}

{% code title="madlib.js" overflow="wrap" lineNumbers="true" %}

```javascript
const madlib = (name, verb, quantity, item, newItem, isHappy) => {
  console.log(`There once was a man named ${name}.`);
  console.log(`Every day he would ${verb} with his ${quantity} ${item}s`);

  if (isHappy) {
    console.log(`But then, he found a ${newItem} and everything changed!`);
  } else {
    console.log(`But then, a ${newItem} took over his life and he couldn't ${verb} again!`);
  }

  console.log("The end.")
}

module.exports = madlib;
```

{% endcode %}

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://raffycastlee.gitbook.io/marcyannotes/fullstack-curriculum/mod-1-javascript-fundamentals/2-node-and-modules.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
