JavaScript Project Setup Guidelines¶
Note
We support the current version of node.js and npm available in the LTS releases of Ubuntu. As of this writing, these are Node v0.10.29 and npm v1.4.21. Using newer versions may create unexpected results.
Quickstart¶
These are generic project setup instructions for an OpenStack JavaScript project. Individual projects may extend or override these instructions, so please check for additional documentation.
Install Dependencies¶
On OSX (using homebrew):
brew install nodejs
Note
Homebrew does not provide an easy way to install our supported version of node.js and npm. The above appears to be compatible, however unexpected results may still occur.
On Debian:
# Using Ubuntu
sudo curl -sL https://deb.nodesource.com/setup_0.10 | sudo -E bash -
sudo apt-get install -y nodejs
# Using Debian
sudo curl -sL https://deb.nodesource.com/setup_0.10 | sudo bash -
sudo apt-get install -y nodejs
On CentOS:
sudo curl -sL https://rpm.nodesource.com/setup | bash -
sudo yum install -y nodejs
Check out and initialize the project¶
All project initialization, and virtual environment creation, is handled via npm. This process is fairly straightforward, and will ensure that all dependencies, tools, and other resources are available.:
# Check out the project
git checkout https://git.openstack.org/openstack/<PROJECT NAME>.git
# Initialize the project.
cd <PROJECT NAME>
npm install
Exercise the codebase¶
After initialization, you should be able to run some basic tests.
npm run lint
This will run our codestyle checks.
npm test
This will run tests and report on test coverage.
npm start
For web-based projects, this command should launch a server.
Common Tools¶
The following section addresses each command prescribed by the Consistent Testing Interface, including tools and setup instructions for the same.
Codestyle Checks¶
- Commands:
npm run lint
OpenStack requires the custom npm script ‘lint’ to execute our codestyle checks. The tool we use is called ESLint, and our rules are published to npm as eslint-config-openstack. To initialize your project with these rules, perform the following steps in your project root.
First, add eslint and eslint-config-openstack to your project, and initialize a basic .eslintrc file.:
npm install --save-dev eslint eslint-config-openstack
echo "extends: openstack" > .eslintrc
Second, add the lint script to your project.json
file:
{
...
'scripts' : {
'lint': 'eslint ./'
},
...
}
For more information on how to exclude files, override specific rules, or on the rules themselves, please visit the ESLint Project. If you’d like to contribute to OpenStack’s ESLint rules, you may do so at eslint-config-openstack.
Executing Tests and Coverage¶
- Commands:
npm test
OpenStack requires a sane testing and code coverage strategy for each
project, though we do not prescribe the tools and coverage threshold, as
these may differ based on circumstance and project type. Generated test
reports should be placed in ./reports
in your projects’ root directory.
Generated coverage output should similarly be placed in ./cover
.
Example setup for browser projects, using Karma, Jasmine, and Istanbul.:
// ./package.json
{
...
"scripts": {
...
"test": "karma start ./karma.conf.js",
...
},
"devDependencies": {
...
"jasmine": "2.3.2",
"karma": "0.13.9",
"karma-chrome-launcher": "0.2.0",
"karma-cli": "0.1.0",
"karma-coverage": "0.5.0",
"karma-firefox-launcher": "0.1.6",
"karma-jasmine": "0.3.6",
"karma-phantomjs-launcher": "0.2.1",
"karma-threshold-reporter": "0.1.15",
...
}
}
// ./karma.conf.js
module.exports = function (config) {
config.set({
'frameworks': ['jasmine'],
'browsers': ['PhantomJS', 'Chrome', 'Firefox'],
'reporters': ['progress', 'coverage', 'threshold'],
'plugins': [
'karma-jasmine',
'karma-coverage',
'karma-threshold-reporter',
'karma-phantomjs-launcher',
'karma-chrome-launcher',
'karma-firefox-launcher'
],
'preprocessors': {
'www/js/{!lib/**/*.js,*.js}': ['coverage']
},
'files': [
// Library files, with some ordering.
'www/js/lib/angular.js',
'node_modules/angular-mocks/angular-mocks.js',
'www/js/lib/*.js',
// Application files
'www/js/**/*.js',
// Tests
'test/js/**/*.js'
],
'coverageReporter': {
type: 'html',
dir: 'cover',
instrumenterOptions: {
istanbul: {noCompact: true}
}
},
// Coverage threshold values.
thresholdReporter: {
statements: 100,
branches: 100,
functions: 100,
lines: 100
},
'singleRun': true
});
};
Example setup for node.js projects, using Jasmine and Istanbul:
# /package.json
{
...
"scripts": {
"test": "istanbul cover --print=detail --include-all-sources jasmine",
...
},
...
"devDependencies": {
...
"istanbul": "0.3.17",
"jasmine": "2.3.1",
...
}
}
# /spec/support/jasmine.json
{
"spec_dir": "spec",
"spec_files": [
"**/*.js",
"!helpers/**/*.js"
],
"helpers": [
"helpers/**/*.js"
]
}
Package Tarball Generation¶
- Commands:
npm pack
OpenStack uses npm pack
to generate a release tarball, which will
compile all files listed in package.json
. If your project requires
concatenation, minification, or any other preprocessing to create a valid
tarball, you may use the npm prepublish
hook to trigger these steps.
An example package.json file may look as follows.:
{
...
'scripts': {
'prepublish': 'uglify -s ./src/**/*.js -o ./lib/generated.min.js'
},
files: [
'LICENSE',
'README.md',
'index.js',
'lib/*.js'
],
...
}
All packages should include:
A README
A LICENSE file
All source code
Generate Documentation¶
- Commands:
npm run document
No canonical way of generating documentation for JavaScript projects has been discussed yet. If you would like to contribute, please join the conversation on OFTC in #openstack-docs or #openstack-javascript.
Validate Dependency Licences¶
- Commands:
npm run document
No common way of validating dependency licenses has been discussed yet. If you would like to contribute, please join the conversation on OFTC in #openstack-javascript.
Import Translation Strings¶
- Commands:
npm run translate
No canonical way of importing translations for JavaScript projects has been discussed yet. If you would like to contribute, please join the conversation on OFTC in #openstack-i18n and #openstack-javascript.
Best Practices¶
All best practices outlined in the general project setup guide apply to JavaScript projects. What follows are additional suggestions.
Use the tool, not the adapter¶
This section in particular addresses gulp, grunt, and similar build tools that aim to be the collect all build steps under one roof. While useful, they often achieve this by wrapping a tool that already exists, while adding dependencies of their own. In order to avoid this bloat - and potential cross-dependency version mismatch - it is far easier to use the tool directly, than maintaining the additional abstraction layer.
Examples:
* Use eslint
instead of gulp-eslint
* Use karma
instead of grunt-karma
* Use webpack
instead of gulp-webpack
Do Not Minify¶
Minification is a frequently used optimization step that reduces JavaScript file size at the cost of legibility. While this step often provides significant load reduction, especially when combined with HTTP GZip compression and HTTP Cache headers, we prefer to leave this decision in the hands of the operator.
Do Not Use Fuzzy Versions¶
Dependencies declared in package.json or bower.json use a fuzzy version
markup that allows installed dependencies to be flexible at the cost of
deterministic builds. This is especially dangerous if one of your project’s
dependencies also uses fuzzy versioning, when an incompatibility is introduced
in a transient dependency. Please use fixed versions for all your
dependencies, and (if possible) lock them using npm shrinkwrap
.
Naming conventions¶
Naming a project is tricky, as each project must avoid name reservation conflict, while clearly communicating its purpose and intent. Names should consider the following:
How does it distinguish itself - amongst openstack projects - as a javascript project rather than a python project.
How does it distinguish itself - among npm or bower packages - that it is an openstack project.
How does it clearly communicate which team it is associated with?
How does it communicate its purpose and content?
How does it acommodate our existing naming conventions in gerrit?
How does it communiate peer library requirements, such as angular.js?
- Suggested names patterns:
openstack/oslo-jslib
A javascript library of common utilities used in other OpenStack projects, published to bower as
openstack-oslo-jslib
openstack/ironic-jslib
A javascript library that integrates with the Ironic API, published to bower as
openstack-ironic-jslib
openstack/ironic-jsclient
A commandline client for ironic, written in JavaScript, published to npm as
openstack-ironic-jsclient
openstack/ironic-webclient
A web client that provides a human interface to the Ironic API.
Exceptions may occur where a tool prescribes an existing naming convention.
- eslint-config-openstack
A list of JavaScript style rules for eslint.
Publish multiple artifacts¶
If your JavaScript project can be consumed by more than one style of application, it’s often helpful to publish separate artifacts for each indented use.
Example artifact names might include:
openstack-oslo-jslib.ng.js
openstack-oslo-jslib.react.js