Overview
In Part I, we setup basic project and prepared output package for mobile device. This got us some benefits coming from statically typed system provided by Typescript, dependency loading and gave us some automation in building and possible integration with Continuous Integration systems.
This part will cover additional tools that can be used to provide better cooperation with other developers and template engines. We will cover the following topics:
- Templates of view
- Templates of CSS styles
- Maintenance
Jade - View Templates
Jade is a powerful template engine for HTML pages or snippets that can be controlled via models passed. Traditionally, it's used more for web servers to provide base templates, however with our setup we can easily reuse this component to prepare mobile web applications and take away some of the redundancy coming to play with.
To start with, we install Jade task for Grunt:
npm install grunt-contrib-jade --save-dev
Now, we can update our existing views/index.html
file to views/index.jade
file:
html
head
script(data-main='js/config', src='js/require.js')
body
#corpus Hello, world!
And finally, update our Gruntfile.js
to execute a new task:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
typescript: {
base: {
src: ['src/**/*.ts'],
dest: './dist/www/js',
options: {
module: 'amd',
target: 'es5',
base_path: 'src'
}
}
},
copy: {
libs: {
files: [
{ flatten: true, expand: true, src: ['lib/require.js'], dest: 'dist/www/js/'}
]
},
tizen: {
files: [
{ flatten: true, expand: true, src: ['platform/tizen/**'], dest: 'dist/tizen'},
{ flatten: true, expand: true, src: ['dist/www/index.html'], dest: 'dist/tizen'},
{ flatten: true, expand: true, src: ['dist/www/js/*'], dest: 'dist/tizen/js'}
]
}
},
zip: {
tizen: {
src: 'dist/tizen/*',
cwd: 'dist/tizen',
dest: 'dist/helloWorld.wgt'
}
},
jade: {
compile: {
options: {
data: {
debug: true
}
},
files: {
"dist/www/index.html": ["views/*.jade"]
}
}
}
});
grunt.loadNpmTasks('grunt-typescript');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-zip');
grunt.loadNpmTasks('grunt-contrib-jade');
grunt.registerTask('default', ['copy:libs', 'typescript', 'jade']);
grunt.registerTask('tizen', ['default', 'copy:tizen', 'zip:tizen']);
};
This approach has the added benefit of a simpler structure (no need to use closing tags), better readability and ability to use inheritance, blocks and includes.
Stylus - CSS Templates
Similar approach as for views can be used for CSS by using Stylus; in this case benefit is less obvious but with the help of in-line conditioning and functions which can take away some of the maintenance problems. Approach to integrate with our solution is straightforward:
npm install grunt-contrib-stylus --save-dev
Put the initial style definition into styles/index.styl
:
body
font: 62.5% "Trebuchet MS", sans-serif
#canvas
margin: 8px
Update views/index.jade
:
html
head
link(rel='stylesheet', href='css/index.css')
script(data-main='js/config', src='js/require.js')
body
#corpus Hello, world!
And finally update Gruntfile.js:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
typescript: {
base: {
src: ['src/**/*.ts'],
dest: './dist/www/js',
options: {
module: 'amd',
target: 'es5',
base_path: 'src'
}
}
},
copy: {
libs: {
files: [
{ flatten: true, expand: true, src: ['lib/require.js'], dest: 'dist/www/js/'}
]
},
tizen: {
files: [
{ flatten: true, expand: true, src: ['platform/tizen/**'], dest: 'dist/tizen'},
{ flatten: true, expand: true, src: ['dist/www/index.html'], dest: 'dist/tizen'},
{ flatten: true, expand: true, src: ['dist/www/js/*'], dest: 'dist/tizen/js'},
{ flatten: true, expand: true, src: ['dist/www/css/*'], dest: 'dist/tizen/css'}
]
}
},
zip: {
tizen: {
src: 'dist/tizen/*',
cwd: 'dist/tizen',
dest: 'dist/helloWorld.wgt'
}
},
jade: {
compile: {
options: {
data: {
debug: true
}
},
files: {
"dist/www/index.html": ["views/*.jade"]
}
}
},
stylus: {
compile: {
files: {
'dist/www/css/index.css': 'styles/index.styl'
}
}
}
});
grunt.loadNpmTasks('grunt-typescript');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-zip');
grunt.loadNpmTasks('grunt-contrib-jade');
grunt.loadNpmTasks('grunt-contrib-stylus');
grunt.registerTask('default', ['copy:libs', 'typescript', 'jade', 'stylus']);
grunt.registerTask('tizen', ['default', 'copy:tizen', 'zip:tizen']);
};
Maintenance
An important part of the software development process is making sure that everyone in the team starts from the same spot and follows the same rules and avoids common mistakes. First part of this process can be automated by linting (CSSLint, JSLint, JSHint etc.), unit testing (QUnit), adding clean task and the second part can be done by code peer reviews. Let's start by adding some more tasks:
npm install grunt-contrib-csslint grunt-contrib-jshint grunt-contrib-qunit grunt-contrib-clean --save-dev
Now let's make clean & lint part of our daily workflow by updating Gruntfile.js
:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
typescript: {
base: {
src: ['src/**/*.ts'],
dest: './dist/www/js',
options: {
module: 'amd',
target: 'es5',
base_path: 'src'
}
}
},
copy: {
libs: {
files: [
{ flatten: true, expand: true, src: ['lib/require.js'], dest: 'dist/www/js/'}
]
},
tizen: {
files: [
{ flatten: true, expand: true, src: ['platform/tizen/**'], dest: 'dist/tizen'},
{ flatten: true, expand: true, src: ['dist/www/index.html'], dest: 'dist/tizen'},
{ flatten: true, expand: true, src: ['dist/www/js/*'], dest: 'dist/tizen/js'},
{ flatten: true, expand: true, src: ['dist/www/css/*'], dest: 'dist/tizen/css'}
]
}
},
zip: {
tizen: {
src: 'dist/tizen/*',
cwd: 'dist/tizen',
dest: 'dist/helloWorld.wgt'
}
},
jade: {
compile: {
options: {
data: {
debug: true
}
},
files: {
"dist/www/index.html": ["views/*.jade"]
}
}
},
stylus: {
compile: {
files: {
'dist/www/css/index.css': 'styles/index.styl'
}
}
},
csslint: {
strict: {
options: {
import: 2
},
src: ['dist/www/css/*.css']
}
},
jshint: {
files: [
'dist/www/js/main.js',
'dist/www/js/config.js'
],
options: {
force: true,
browser: true,
devel: true,
globals: {define: true},
}
},
clean: ['dist']
});
grunt.loadNpmTasks('grunt-typescript');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-zip');
grunt.loadNpmTasks('grunt-contrib-jade');
grunt.loadNpmTasks('grunt-contrib-stylus');
grunt.loadNpmTasks('grunt-contrib-csslint');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.registerTask('default', ['copy:libs', 'typescript', 'jade', 'stylus', 'csslint', 'jshint']);
grunt.registerTask('tizen', ['clean', 'default', 'copy:tizen', 'zip:tizen']);
};
By running grunt command, we will run into a couple of issues in both CSS and JS files (bad, bad developer!). Most of those can be easily resolved except for W033 (missing semicolon) which is not generated by Typescript compiler in the current version, so we turn off hard fail for JS validation.
With clean task integrated into tizen target, we can ensure that everybody will always receive the same build from the same source files; clean task can be run also directly to force cleaning of dist directory.
Complete project is distributed without node_modules
folder so it is necessary to run the following command from project directory to load dependencies:
npm install .
Continue to Part III or back to Part I.