Chrome Apps deliver an experience as capable as a native app, but as safe as a web page. Just like web apps, Chrome Apps are written in HTML5, JavaScript, and CSS. But Chrome Apps look and behave like native apps, and they have native-like capabilities that are much more powerful than those available to web apps.
The Grunt ecosystem is huge and it’s growing every day. With literally hundreds of plugins to choose from, you can use Grunt to automate just about anything with a minimum of effort. Here is what we used to automate our tasks for developing, building and publishing the chrome app all with grunt.
<!-- bower:js -->
<!-- endbower -->
Here’s what our configuration for the task looks like:
wiredep: {
app: {
src: ['<%= config.app %>/index.html'],
ignorePath: '<%= config.app %>/'
}
};
build
block and get another file with generated scripts/html optimized for deployment.<!-- build:js js/app.js -->
<script src="js/app.js"></script>
<script src="js/controllers/thing-controller.js"></script>
<script src="js/models/thing-model.js"></script>
<script src="js/views/thing-view.js"></script>
<!-- endbuild -->
Here’s how our usemin
configuration looks like:
usemin: {
options: {
assetsDirs: ['<%= config.dist %>', '<%= config.dist %>/images']
},
html: ['<%= config.dist %>/{,*/}*.html'],
css: ['<%= config.dist %>/styles/{,*/}*.css']
},
useminPrepare: {
options: {
dest: '<%= config.dist %>'
},
html: ['<%= config.processed %>/index.html', '<%= config.processed %>/player.html']
}
grunt-env, grunt-preprocess and grunt-contrib-copy: Use grunt-env to specify different environment variables for different targets. It works great when coupled with grunt-preprocess which can trim out the code based on the environment. For example, to have different app names for staging and production, you could do the following in `manifest.json:
{
"name": "<!-- @echo APP_NAME -->",
...
}
Here’s the configuration:
// Apply environment variables
env: {
staging: {
APP_NAME: 'App Dev'
},
prod: {
APP_NAME: 'App'
}
},
// Copy files from `app` directory to `processed` directory
copy: {
preprocess: {
expand: true,
cwd: '<%= config.app %>',
dest: '<%= config.processed %>',
src: ['**']
}
},
// Preprocess all js, html and json files inside the `processed` directory
preprocess: {
inline: {
src: ['<%= config.processed %>/js/{,*/}*.js', '<%= config.processed %>/*.html', '<%= config.processed %>/*.json'],
options: {
inline: true
}
}
}
// Make sure code styles are up to par and there are no obvious mistakes
jshint: {
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish')
},
all: [
'Gruntfile.js',
'<%= config.processed %>/js/{,*/}*.js',
'!<%= config.processed %>/js/vendor/*',
'test/spec/{,*/}*.js'
]
}
grunt-contrib-watch: Run predefined tasks whenever watched file patterns are added, changed or deleted.
watch: {
// Run the wiredep task whenever bower.json changes
bower: {
files: ['bower.json'],
tasks: ['wiredep', 'copy:preprocess', 'preprocess']
},
// Run preprocess and jshint whenever any js file changes
js: {
files: ['<%= config.app %>/js/**/*.js'],
tasks: ['copy:preprocess', 'preprocess', 'jshint'],
options: {
livereload: 45729
}
},
// Run preprocess whenever any css file changes
styles: {
files: ['<%= config.app %>/styles/{,*/}*.css'],
tasks: ['copy:preprocess', 'preprocess'],
options: {
livereload: 45729
}
},
// Reload the app on any significant changes
livereload: {
tasks: ['copy:preprocess', 'preprocess'],
options: {
livereload: 45729
},
files: [
'.tmp/styles/{,*/}*.css',
'<%= config.app %>/*.html',
'<%= config.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
'<%= config.app %>/manifest.json',
'<%= config.app %>/_locales/{,*/}*.json'
]
}
}
// Merge event page, update build number, exclude the debug script
chromeManifest: {
dist: {
options: {
// Don't increase the build number automatically
buildnumber: false,
background: {
target: 'js/background.js',
// Exclude files used for development
exclude: [
'js/chromereload.js'
]
}
},
src: '<%= config.processed %>',
dest: '<%= config.dist %>'
}
}
// Increments the version
bump: {
options: {
// Update the version in package.json, manifest.json and commit the changes
files: ['package.json', '<%= config.app %>/manifest.json'],
updateConfigs: [],
commit: true,
commitMessage: 'Release v%VERSION%',
commitFiles: ['package.json', '<%= config.app %>/manifest.json'],
createTag: false,
push: false,
pushTo: 'upstream',
gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d'
}
}
// Copmress files inside processed directory and create a new package ready for upload
compress: function (grunt) {
return {
dist: {
options: {
archive: function () {
var manifest = grunt.file.readJSON(grunt.template.process('<%= config.processed %>') + '/manifest.json');
return grunt.template.process('<%= config.package %>') + '/App-' + manifest.version + '.zip';
}
},
files: [
{
expand: true,
cwd: 'dist/',
src: ['**'],
dest: ''
}
]
}
}
},
// Uploads latest build from package to production/staging
webstore: {
options: {
publish: true,
client_id: "XXXX",
client_secret: "XXXX"
},
"staging": {
options: {
appID: "XXXX",
zip: "<%= config.package %>" // Uploads the most recent zip file from this directory
}
}
}
Putting everything together: Lets put all the tasks together into more manageable defaults for our development and deployment.
// Configurable paths
var config = {
app: 'app',
dist: 'dist',
processed: 'processed',
package: 'package',
tasks: grunt.cli.tasks
};
// Loads task options from `tasks/options/` and loads tasks defined in `package.json`
// Read more here: https://www.thomasboyt.com/2013/09/01/maintainable-grunt.html
require('load-grunt-config')(grunt, {
configPath: path.join(process.cwd(), 'tasks/options'),
init: true,
config: {
pkg: grunt.file.readJSON('package.json'),
manifest: 'manifest',
config: config
}
});
var target = grunt.option('target') || 'dev';
// `grunt debug [--target=dev|test|staging|prod]` - Default, run Chrome App on Chrome app container
grunt.registerTask('debug', 'Task for development of the app. Enables live reload', [
'clean',
'env:' + target,
'copy:preprocess',
'preprocess',
'watch'
]);
// Creates a production/staging build and archives to zip file
// grunt build [--target=dev|test|staging|prod]
grunt.registerTask('build', 'Creates a production/staging build and archives to zip file', [
'env:' + target,
'clean:dist',
'copy:preprocess',
'preprocess',
'chromeManifest:dist',
'useminPrepare',
'concat',
'cssmin',
'uglify',
'copy:dist',
'copy:styles',
'usemin',
'compress'
]);
// Publish the application on the web store. Automatically increases the build version, uploads the build and commits the updated files.
// grunt publish [--release=major|minor|patch] [--target=staging|prod] [--setversion=x.x.x]
grunt.registerTask('publish', 'Publish the application on the web store', [
'bump-only:' + release,
'build',
'webstore_upload:' + target,
'bump-commit'
]);
By the way, if you enjoy movies and tv shows, check out our MovieTabs extension for Chrome to discover new movies and tv shows with every tab.