Update

We have updated the content of our program. To access the current Software Engineering curriculum visit curriculum.turing.edu.

NPM & Webpack

Configuring Your Projects

Adding dependencies, bundling your JavaScript files with Webpack, adding style loaders writing npm scripts, linting your code, preventing files from being added to github … This lesson will begin to draw back the veil on these mysteries!

In this lesson, we will hand-roll a boilerplate repo that can be used for a life-changing app - a Wombat Greeter.

Vocab

  • Dependency Something your program needs in order to do it’s job that is provided by an external source
  • NPM Node Package Manager is a repository of JavaScript libraries
  • Webpack Broadly, a thing that takes your many files and combines them into just a few files a web browser can understand

NPM

NPM documentation

What is NPM?

Node Package Manager

A package is a bit of reusable code!

  • usually solves a single problem
  • SOMEONE wrote it and shared it
  • makes your life easier

You can compare these to gems from your Rails experience.

You can write your own packages, bundle them up, and publish them (either on the NPM library or on GitHub) for other devs to use!

NPM is a manager; it includes a set of tools that let you use and control node packages. Using it, you can:

  • install packages into your project as dependencies (aka, code that your project depends on)
  • create custom scripts to run code
  • specify specific packages to use when deploying your app to production

You are so far pretty familiar with cloning down a repo, changing into the directory, and running npm install. Let’s unpack what’s happening when you do that!

package.json

package.json documentation

The package.json file is created when we initialize npm in a repo (the terminal command is npm init). It keeps track of our dependencies, lets us write npm scripts, and is the instruction manual NPM follows when we run npm install.

You can compare to you Gemfile and Rakefile combined in one.

Let’s take a look at an example package.json file:

{
  "name": "Shih-Tzus",
  "version": "1.0.0",
  "description": "It has very important shih-tzu information.",
  "main": "index.js",
  "dependencies": {
    "jquery": "^3.3.1",
    "node": "6.11.1"
  },
  "devDependencies": {
    "chai": "^4.0.2",
    "mocha": "^3.4.2"
  },
  "scripts": {
    "test": "./node_modules/mocha/bin/mocha --timeout 5000"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/ameseee/SuperRadApp.git"
  },
  "keywords": ["furry", "fun", "soft"],
  "author": "Sodie Holt",
  "license": "ISC"
}

Take a minute to look over this. What’s familiar? What’s confusing?

Let’s break this down. The package.json is just meta data about our installed packages. It can be helpful to think of the package.json as the recipe for our app.

dependencies (production):

We’re listing what ingredients we need for the app to run: we need jquery, and we need version 3.3.1 or any version compatible with 3.3.1. (You can read up on the what the symbols by the package version numbers mean here, and learn a bit about what the version numbers themselves even mean here.)

To install a package as a production dependency: npm install [PACKAGE NAME OR GITHUB URL]

When you install a package this way, a new key-value pair with that package’s information will be added to the "dependencies" object for you.

devDependencies (development):

The devDependencies are the tools that our chefs will need in order to work on our app - like our testing libraries, mocha and chai. The end user of an app will not need to have mocha and chai on their computers, since they will never be running the testing suite of the app. We designate those libraries as devDependencies because they are packages that only developers working on the app will use.

To install a package as a development dependency: npm install [PACKAGE NAME OR GITHUB URL] --save-dev Note: you must use the --save-dev flag. Otherwise, the dependency will be installed as a production dependency.

When you install a package this way, a new key-value pair with that package’s information will be added to the "devDependencies" object for you.

scripts:

Our package.json recipe also has instructions. The scripts object has a key of "test" whose value is "./node_modules/mocha/bin/mocha --timeout 5000".

We’re creating a shortcut; we can now run npm test in our terminal, and it is equivalent to running npm run ./node_modules/mocha/bin/mocha --timeout 5000 in the terminal, but obviously much shorter and easier for us.

We’re able to create any scripts. One thing to be aware of: scripts named start or test are so ubiquitous that they can be run in the terminal by simply typing in npm start and npm test. For any other script, however, you must add the keyword run into the terminal command.

For example, if you create a script called deploy, to run that script in the terminal, you must type npm run deploy.

Note: you can always edit the package.json file manually! Just go in there and create key value pairs to your heart’s content!

Another note: Additional reading about the package-lock.json file here. It is a log of snapshots of your dependency tree. It ensures that a team or production build is using the exact same dependencies, and is also a log that allows you to “time travel” into earlier versions of the dependency tree.

Common NPM Commands

- npm init and optionally npm init --yes

Initializes npm and creates the package.json file. If run without the --yes flag, it will run you through a dialogue designed to help customize the package.json file. The file can always be edited and updated at any point.

- npm install

Install package dependencies and devDependencies listed in package.json

- npm install [package-name]

Install package locally in folder location node_modules

- npm install -g [package-name]

Install package globally, usually to /usr/local/lib/node_modules

- npm install --save [package-name]

Install package locally in folder location node_modules and update package.json dependencies

- npm install --save-dev [package-name]

Install package locally in folder location node_modules and update package.json developer dependencies. Shortcut: npm install -D.

- npm start

Run start script located in package.json

- npm test

Run test script located in package.json

- npm run [custom script]

Run custom script located in package.json

  • NOTE: Notice that the –save-dev flags can be typed before or after the package name.

In your wombat repo:

  1. Run npm init to create the package.json file. You will need to answer a series of questions before you file is created. Shortcut: npm init --yes, but you might want to go through the process for your first time.

  2. Edit the scripts portion of the package.json file:

     "test": "./node_modules/mocha/bin/mocha --require babel-core/register test",
     "start": "webpack"
    
  3. Install the following dev dependencies:
    • mocha
    • chai
     npm install mocha -D
     npm install chai -D
    
  4. Install the following dependencies:
    • webpack
    • webpack-dev-server
    • webpack-cli
    • babel-loader
    • babel-cli
    • babel-preset-env
    • babel-preset-es2015
    • babel-core@7
    • style-loader
    • css-loader
     npm install webpack webpack-cli babel-loader babel-cli babel-preset-env style-loader css-loader
    

Webpack Basics

Webpack ‘Getting Started’ documentation

Webpack intro blog post

What is Webpack?

Webpack is a build tool that takes multiple JavaScript modules and bundles them up into a single, unified file.

You can compare webpack to the Asset Pipeline.

Why Do We Like It?

While webpack can be difficult to understand at first, it makes our lives much easier.

  • We can organize our code into separate files. This makes it easier to find specific pieces of code and improves maintainability.

  • Webpack creates a unique scope for each our files, helping to prevent adding things to the global and naming collisions.

  • By bundling all of our JS into a single file, the browser only needs to request, wait for, and process through one file.

    • Otherwise, it would have to request every JS file and individual dependency!

    • Network requests are expensive (they take a long time), which slows down the app and makes for a poor user experience.

  • We can even bundle up our HTML and CSS files by using loaders, further reducing the number of files the browser has to request and process.

Configuring Webpack (and Babel)

Core Webpack Concepts

Configuring Webpack

Here is the webpack.config.js file from the gametime repo:

const path = require('path');

module.exports = {
  devtool: 'inline-source-map',
  entry: {
    main: "./lib/index.js",
    test: "mocha-loader!./test/index.js"
  },
  mode: 'development',
  output: {
    path: __dirname,
    filename: "[name].bundle.js"
  },
  module: {
    rules: [
      { test: /\.css$/, exclude: /node_modules/, use: [ 'style-loader', 'css-loader' ]},
      { test: /\.js$/, exclude: /node_modules/, use: ['babel-loader'] }
    ]
  },
  resolve: {
    extensions: ['.js', '.json', '.css']
  }
};

devtool

This provides a map of our bundled code, so that when we run into errors it will tell us in the console where the error is in our pre-bundled code.

entry

This is where webpack will start bundling up our app. It should be the entry point to your application.

output

This defines where where webpack will create your bundled code and what it will name the file.

loaders

Let’s focus on the rules key-value pair inside the module object.

Loaders transform different non-JS code into valid JS modules so they can be included when webpack bundles everything up into a single file for the browser.

Loaders have two parts: a npm module, and a configuration object which is added to the webpack.config.js file.

Let’s walk through how the css-loader was added to the react starter kit repo.

  1. Install the style and css loaders npm install style-loader css-loader --save-dev

  2. In the webpack.config.js file, we add our configuration object

{
  test: /\.css$/,
  exclude: /node_modules/,
  use: 'style-loader!css-loader'
}

That’s it! But what is that object doing?

The regular expression inside the object looks for all files whose extension is .css and identifies them as code to be transformed into a JS, using the style-loader and css-loader npm packages.

When we run webpack, it transforms the css files into JS modules, which are then bundled together with the rest of our JS into a single file, bundle.js (as specified in the output section of the webpack.config.js file!).

You can read more about loaders here.

Using Our Bundled File

In the HTML of the project, we point our <script> tag to "bundle.js", so it references the all-neatly-bundled-up JS file that webpack made for us!

In your wombat repo:

  1. Create a new file in the root of your repo: webpack.config.js
  2. Create a new file in the root of your repo: .babelrc

     touch webpack.config.js .babelrc
    
  3. Paste the following code into the .babelrc file. preset-env is a smart preset that allows us to use the latest JavaScript without needing to micromanage syntax and browser polyfills.

     {
       "presets": ["env"]
     }
    
  4. Paste the following code to the webpack.config.js file:

     const path = require('path');
    
     module.exports = {
       devtool: 'inline-source-map',
       entry: {
         main: "./lib/index.js"
       },
       output: {
         path: __dirname,
         filename: "dist/[name].bundle.js"
       },
       mode: 'development',
       module: {
         rules: [
           {
             test: /\.css$/,
             exclude: /node_modules/,
             loader: "style-loader!css-loader"
           },
           {
             test: /\.js$/,
             loader: 'babel-loader',
             query: {
               presets: ['es2015']
             }
           }
         ]
       },
       resolve: {
         extensions: ['.js', '.json', '.css']
       }
     };
    
  5. In the test/index-test.js file, let’s require chai and write one test to see that mocha and chai are properly hooked up:

     import { assert } from 'chai';
    
     describe('test', function() {
       it('should return true', function() {
         assert.equal(true, true);
       });
     });
    
  6. In your terminal, run npm test

  7. Before this will work, we need to change the file we are providing to index.html. The script tag should point to ./dist/main.bundle.js.

  8. In your terminal, run npm start. Visit localhost:8080 to view in your browser.

.gitignore Basics

gitignore documentation

github documentation on ignoring files

.gitignore is a file we can add to any git repo. This file contains filepaths and types of files we do not want to add to our git repo.

Common things to add to your .gitignore file are:

  • dependency directories
  • log files
  • files generated by compilers
  • API keys, other sensitive data (note: there are better ways to obscure and protect data that will be covered in Mod 4)

In your wombat repo:

  1. Create a new file in the root of your repo: .gitignore
  2. In that file, add the filepaths of the directories or files we don’t want added to github.

That’s it!

An example .gitignore file might look like:

# dependencies
/node_modules

# production
/build

# misc
.DS_Store
.env
APIkey.js

# logs
*.log

The lines beginning with # are simply comments that make it easier to navigate and maintain the .gitignore file. Notice that the contents of the file are filepaths. Any file with /node_modules in its filepath will not be added, committed, or pushed to github. If we want to ignore an entire type of file we can use the * to indicate all, and then follow it with the file extension we want to ignore.

Lesson Search Results

Showing top 10 results