- Up to this point we've covered basic JS
- Lanuage syntax
- Object orientation
- DOM / Browser / jQuery
- API concepts
- The apps we've created are enhanced HTML pages
- Today we talk about what it means to create a JS app
- Node lets you run JavaScript in your terminal.
- Based on Chrome's V8 JS engine
- It's the basis of the JS ecosystem. All commandline tools for creating and managing JS applications require node.
NPM is the Node Package Manager. It is installed for you with Node.
- Define an application
- Install open source packages (dependencies)
- Define how to run your application.
https://docs.npmjs.com/getting-started/what-is-npm
Lets you create an application, give it a name, version, etc.
Run npm init in a directory and it will walk you through a wizard to define all the metadata for your app.
NPM init creates a package.json file. Take a look at it.
It's JSON file that defines metadata about the application like Name, version, author, etc. It also includes information about running the app.
{
"name": "myapp",
"version": "1.0.0",
"description": "My first app",
"main": "index.js",
"scripts": {
"test": "test",
"start" : "node index.js"
},
"author": "",
"license": "ISC"
}Semantic versioning is a guideline for how you should set the version numbers of your application. This is especially important if you're going to share it with others.
- If a project is going to be shared with others, it should start at 1.0.0
- Bug fixes and other minor changes: Patch release, increment the last number, e.g. 1.0.1
- New features which don't break existing features: Minor release, increment the middle number, e.g. 1.1.0
- Changes which break backwards compatibility: Major release, increment the first number, e.g. 2.0.0
Defines how to compile, test, and run your application. Let's focus on start for now.
-
Create a simple index.js file
console.log('my application just ran');
-
Tell npm to run the index.js file with the start command.
"scripts": { "test": "test", "start" : "node index.js" }
-
Run your app by typing npm start in the commandline
Let's make sure your environment works. On your own computer:
- Make sure Node is installed
- Create an app with NPM install
- Add an 'npm start' script
- Run your app and check the output
Developers create NPM packages, just like the one you just created and publish them to the NPM registry for others to use.
These packages solve many common problems for you. Think of them as scripts you'd include on a webpage (like jQuery).
You can browse a listing of those packages here: https://www.npmjs.com/
Keep in mind, not all open source packages are created equal. When selecting a package to include in your project, look at the github repo for:
- Recent commits to prove it is being maintained.
- High number of stars as social proof that others like this package.
From the commandline, use the npm install some-cool-package command to download and save a package. This will download the some-cool-package package and save it in the node_modules directory.
Most of the time, you want to use npm install --save some-cool-package. This adds the package name to package.json. for example:
npm install moment --saveAdds a section called "dependencies" to you package.json
"dependencies": {
"moment": "^2.10.6"
}Rubyists: This is analagous to Ruby 'gems'. Think of the package.json file is your gemfile.
You can now get access to the installed dependency by using a require statement.
var Moment = require('moment');
var a = new Moment();
console.log(a.format('h:mm:ss a')); //logs the current timeNotice that your dependencies specify a version number. Using compatible versions of dependencies is important. There are a number of ways to specify dependency versions:
- You can specify a specific version e.g. "2.10.6"
- Minimum version number ">=2.10.6"
- Tilde version ("~2.10.6") includes this version and future patches. (up to but not including 2.11.0)
- Caret version ("^2.10.6") includes all updates in the major version (up to but not including 3.0.0)
Contains all the modules you've downloaded from open source repositories.
- Is not checked in to source control
- Should be delete-able
When someone (or something) downloads your package, they will need to run npm install. This will create a node_modules directory and download all the packages you've specified into it.
Rubyists: This is analagous to 'bundle install'.
In this way, the Package.json file has the following benefits:
- It serves as documentation for what packages your project depends on
- It allows you to specify the versions of a package that your project can use using semantic versioning rules
- Makes your build reproducable which means that its way easier to share with other developers.
Some packages, like a web server are required for your app to run. Other packages, like linting or test tools are only required for developers. To save a package that doesn't need to be installed on the server, run --save-dev
npm install lodash --saveAdds a section called "devDependencies" to you package.json
"devDependencies": {
"lodash": "^3.10.1"
}You can always manually edit your package.json and delete folders from node modules, but npm uninstall is more convenient:
npm uninstall moment --save- Install the 'request-promise' package
- Install the 'moment' package
- Examine the directory structures and effects on package.json
- Delete node_modules folder and run npm install again
- Create an index.js file and update package.json so that
npm startwill run it - Require the 'request' and 'moment' modules in your application.
- Use 'request-promise' to get data from http://jsonplaceholder.typicode.com/posts/1. Log the response
- Use 'moment' to output a timestamp for when response arrives using the format ("h:mm:ss a").
- Have your application fetch data every 3 seconds.
var Moment = require('moment');
var Request = require('request-promise');
function logResponse(resp){
console.log(resp, new Moment().format("h:mm:ss a"));
}
setInterval(function(){
Request('http://jsonplaceholder.typicode.com/posts/1')
.then(logResponse)
},3000)When writing an application, you'll want to keep your code organized in modules.
- Modules allow for encapsulation (privacy)
- Makes your code easier to maintain
- Makes your code re-usable and composable.
- Modules should do one thing (single responsibility)
To define a module, create a file and assign a value to the keyword module.exports. e.g.
var myModule = {
sayHi: function(){
console.log('hi');
}
}
module.exports = myModule;Import your module in much the same way you do your node_modules
var myMod = require('./myModule');
myMod.sayHi();For a module, being able to hide variables and state and only exposing a deliberate API to its consumers is important. Modules let you achieve this.
var myGreeting = "hi"; //Private!
var myModule = {
sayHi: function(name){
console.log(myGreeting + ' ' + name);
}
}
module.exports = myModule;Take the previous exercise and factor out some reusable functionality.
- Create a
DataAccessmodule- Export an object with a function
getPost(postId)
- Export an object with a function
- Create a
TimeStampmodule- Export an object with a function
getTime()that returns a timestamp string in the format 'h:mm:ss a'
- Export an object with a function
- Update your index.js file to use
DataAccessandLogger.
Real applications need automated tests, and JS apps are no different. There are many open source assertion frameworks and test runners that let you test your code.
An assertion framework simply lets you test if something is true, false, equal, etc and throw an error if its not. Let's use the 'assert' package.
npm install assert --save-dev
Create a directory called 'test' with a test.js file. In that file create a test suite
var assert = require('assert');
assert(true);
assert(false);
});Remember the scripts section of on package.json file? It specifies how to run your tests.
"scripts": {
"test": "node test/test.js",
"start": "node index.js"
},
Run npm test and your script runs
A test runner manages all the work around running your tests like defining your test suites, and performing setup tasks. Let's use 'Mocha' for now.
Install Mocha
npm install mocha --save-dev
Update test.js to use a Mocha test suite
var assert = require('assert');
describe('Array', function() {
describe('#indexOf()', function () {
it('should return -1 when the value is not present', function () {
assert.equal(-1, [1,2,3].indexOf(5));
assert.equal(-1, [1,2,3].indexOf(0));
});
});
});
Update the npm test script to run mocha. (Note, mocha knows to look for your tests in /test/test.js by default, but you can configure it otherwise).
"scripts": {
"test": "mocha",
"start": "node index.js"
},
Run the test
npm test
Ka-ching!
A lot of our code in JS is going to be asynchronous. Luckily, promises make testing async code easy. Simply return the promise.
var dataModule = require('../dataModule.js');
var assert = require('assert');
describe('DataModule', function() {
it('should return the right data', function () {
return dataModule.getTodos().then(function(todo){
assert(post);
})
});
});
Let's write some tests for our modules!
- Install 'assert' as a dev dependency
- Install 'mocha' as a dev dependency
- Create a simple Mocha test suite that runs
assert(true) - Update the
npm testscript to run your mocha tests - Create a test 'should return current time' and test the logger to make sure it returns the current time in the right format
- Create a test 'should return a post' and test your dataModule's getPost method. Make sure your post has the properties you expect.
- Complete the how-to-npm module and post a screenshot to slack
Read about popular packages on https://www.npmjs.com/