Javascript Development on the Command Line
From:
andrew cooke <andrew@...>
Date:
Fri, 17 Feb 2012 20:19:21 -0300
I am trying to get started with Javascript on the command line. I want to
write a "significant" amount of code, and so thought maybe I should be trying
to develop using nodejs to run tests etc (even though the code will finally
run in a browser).
But I am having a hard time finding good, introductory information on how to
do this, so decided to write some notes here in case they might help others.
This is where I am: I can define modules and run tests. In addition, I am
using requirejs to load modules, which I believe will carry across to working
in the browser - http://requirejs.org/
This is what I did:
- Add the nodejs plugin to Intellij Idea (most of what is described here is
independent of using Intellij Idea, but if you use it, you need this).
- Install nodejs and npm (for OpenSuse the nodejs package installs both).
- npm is the node package manager and works something like easy_install for
Python. You can install files globally, but it seems more sensible to run
it locally in the project directory). If you do that (as I do here) you
need to add the file node_modules/.bin to your path (where node_modules is
a directory that will appear when you run npm below).
- Create you project directory.
- In that directoryUsing npm install the libraries you want:
npm install underscore
npm install requirejs
npm install benchmark
npm install nodeunit
npm install amdefine
npm install assert
- Also create src and test subdirectories.
- In the src directory create a file called utils.js (for example). This is
your first module. The contents are (for example):
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
define(['underscore'], function () {
"use strict";
if ('function' !== typeof Object.beget) {
Object.beget = function (o) {
var f = function () {
};
f.prototype = o;
return new f();
};
}
});
where the first three lines are described at
http://requirejs.org/docs/node.html#nodeModules and the next block defines
your module (which unusually doesn't have any contents, but does make
global changes to Javascript).
- In the test directory create file called utils.js (for example) with your
tests:
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
define(['underscore'], function () {
"use strict";
return {
test1: function(test) {
test.equals(1,1);
test.done();
}
}
});
where I am using nodeunit because it is supported by Intellij.
Alternatively, you could use mocha. For that case see my question here
http://stackoverflow.com/questions/9303945
- With that you can now test from the command line (don't forget to cmodify
PATH as described above:
> nodeunit test
utils.js
/ test1
OK: 1 assertions (14ms)
where I've replace a cute tick mark with a "/" that is more likely to
display on my antique blog.
- Alternatively, you can run the tests in Intellij. Go to "Edit
configurations" and add a nodeunit config. Note that there is a strange
bug - if you select the files from the file selector then you need to make
the path in the last textbox relative (delete the leftmost part of the
path). See http://youtrack.jetbrains.com/issue/IDEA-81566
OK, that's as far as I have got.
Andrew
Better Test Example
From:
andrew cooke <andrew@...>
Date:
Fri, 17 Feb 2012 20:31:25 -0300
This actually imports the module that was defined and tests some
functionality:
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
define(['underscore', '../src/utils'], function (underscore, utils) {
"use strict";
return {
test1:function (test) {
test.equals(1, 1);
test.done();
},
test2:function (test) {
var a = {a:1};
var b = Object.beget(a);
b.b = 2;
test.equals(a.beget, undefined);
test.equals(typeof(Object.beget), 'function');
test.equals(a.a, 1);
test.equals(b.a, 1);
test.equals(a.b, undefined);
test.equals(b.b, 2);
test.done();
}
}
});
Andrew
Building for the Web
From:
andrew cooke <andrew@...>
Date:
Sun, 19 Feb 2012 10:52:28 -0300
All the above describes a project sitting on the server or development
machine. When deploying to a web page you need to somehow remove the node
related stuff and have a single, compact file. This is done by using r.js
(the requirejs optimizer).
In the utilities directory I have the file build.js:
> cat build.js
({
baseUrl:"../src",
out:"../built.js",
paths: {'underscore': '../node_modules/underscore/underscore'},
name: "utils"
})
and running this command in that directory:
rm ../built.js; r.js -o ./build.js
builds the file "built.js" which appears to contain both underscore and utils
munged into a single largely unreadable (by humans) file. I've looked for
some way to make it readable, but can't find anything.
I don't think you can simply load the utils.js file "as is" because the
includion of amdefine will break things in the browser (one of the things that
j.rs does is strip that out).
Andrew
Final Javascript Example (including browser)
From:
andrew cooke <andrew@...>
Date:
Sun, 19 Feb 2012 14:55:00 -0300
This is the script to install various packages (not all used below)
> cat setup.sh
#!/bin/bash
npm install underscore
npm install requirejs
npm install benchmark
#npm install mocha
npm install nodeunit
npm install amdefine
npm install assert
Here are the source files
> ls src
trench.js utils.js
> cat src/trench.js
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
define(['utils', 'underscore'], function (utils, _) {
"use strict";
var a = {a: 1};
var b = Object.beget(a);
b.b = 2;
alert(JSON.stringify(b));
});
> cat src/utils.js
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
define(['underscore'], function (_) {
"use strict";
if ('function' !== typeof Object.beget) {
Object.beget = function (o) {
var f = function () {
};
f.prototype = o;
return new f();
};
}
});
And the test file:
> ls test
utils.js
> cat test/utils.js
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
define(['underscore', '../src/utils'], function (underscore, utils) {
"use strict";
return {
test1:function (test) {
test.equals(1, 1);
test.done();
},
test2:function (test) {
var a = {a:1};
var b = Object.beget(a);
b.b = 2;
test.equals(a.beget, undefined);
test.equals(typeof(Object.beget), 'function');
test.equals(a.a, 1);
test.equals(b.a, 1);
test.equals(a.b, undefined);
test.equals(b.b, 2);
test.equals(JSON.stringify(b), '{"b":2}');
test.done();
}
}
});
Which runs:
> nodeunit test
utils
/ test1
/ test2
OK: 8 assertions (15ms)
And here is the build step:
> ls utilities/
build.js build.sh
> cd utilities/
> ./build.sh
Tracing dependencies for: trench
Uglifying file: /home/andrew/projects/personal/webgl/trench/built.js
/home/andrew/projects/personal/webgl/trench/built.js
----------------
/home/andrew/projects/personal/webgl/trench/node_modules/underscore/underscore.js
/home/andrew/projects/personal/webgl/trench/src/utils.js
/home/andrew/projects/personal/webgl/trench/src/trench.js
And here is the web application:
> cd ..
> cat index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<script data-main="built.js"
src="node_modules/requirejs/require.js"></script>
</head>
<body>
</body>
</html>
And, finally, loading that into a browser displays the alert '{"b":2}'.
Andrew
Comment on this post