├── .npmignore
├── .gitattributes
├── .gitignore
├── app
├── templates
│ ├── gitattributes
│ ├── styles
│ │ ├── main.css
│ │ └── main.scss
│ ├── bowerrc
│ ├── coffees
│ │ ├── popup.coffee
│ │ ├── options.coffee
│ │ ├── contentscript.coffee
│ │ ├── background.coffee
│ │ ├── background.browseraction.coffee
│ │ ├── background.pageaction.coffee
│ │ └── chromereload.coffee
│ ├── scripts
│ │ ├── options.js
│ │ ├── popup.js
│ │ ├── contentscript.js
│ │ ├── background.js
│ │ ├── background.browseraction.js
│ │ ├── background.pageaction.js
│ │ └── chromereload.js
│ ├── images
│ │ ├── icon-128.png
│ │ ├── icon-16.png
│ │ ├── icon-19.png
│ │ └── icon-38.png
│ ├── gitignore
│ ├── _bower.json
│ ├── _locales
│ │ └── en
│ │ │ └── messages.json
│ ├── manifest.json
│ ├── editorconfig
│ ├── jshintrc
│ ├── options.html
│ ├── popup.html
│ ├── _package.json
│ └── Gruntfile.js
├── USAGE
└── index.js
├── .travis.yml
├── contributing.md
├── .editorconfig
├── .jshintrc
├── package.json
├── readme.md
└── test
└── test.js
/.npmignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | temp/
3 |
--------------------------------------------------------------------------------
/app/templates/gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/app/templates/styles/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 20px;
3 | }
4 |
--------------------------------------------------------------------------------
/app/templates/bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "app/bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/app/templates/styles/main.scss:
--------------------------------------------------------------------------------
1 | $padding: 20px;
2 | body {
3 | padding: $padding;
4 | }
5 |
--------------------------------------------------------------------------------
/app/templates/coffees/popup.coffee:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | console.log('\'Allo \'Allo! Popup')
4 |
--------------------------------------------------------------------------------
/app/templates/scripts/options.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | console.log('\'Allo \'Allo! Option');
4 |
--------------------------------------------------------------------------------
/app/templates/scripts/popup.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | console.log('\'Allo \'Allo! Popup');
4 |
--------------------------------------------------------------------------------
/app/templates/coffees/options.coffee:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | console.log('\'Allo \'Allo! Option')
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - 'iojs'
5 | - '0.12'
6 | - '0.10'
7 |
--------------------------------------------------------------------------------
/app/templates/scripts/contentscript.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | console.log('\'Allo \'Allo! Content script');
4 |
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | See the [contributing docs](https://github.com/yeoman/yeoman/blob/master/contributing.md)
2 |
--------------------------------------------------------------------------------
/app/templates/coffees/contentscript.coffee:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | console.log('\'Allo \'Allo! Content script')
4 |
--------------------------------------------------------------------------------
/app/templates/images/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stefanbuck/generator-chrome-extension/master/app/templates/images/icon-128.png
--------------------------------------------------------------------------------
/app/templates/images/icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stefanbuck/generator-chrome-extension/master/app/templates/images/icon-16.png
--------------------------------------------------------------------------------
/app/templates/images/icon-19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stefanbuck/generator-chrome-extension/master/app/templates/images/icon-19.png
--------------------------------------------------------------------------------
/app/templates/images/icon-38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stefanbuck/generator-chrome-extension/master/app/templates/images/icon-38.png
--------------------------------------------------------------------------------
/app/templates/gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | temp
3 | .tmp
4 | dist
5 | .sass-cache
6 | app/bower_components
7 | test/bower_components
8 | package
9 |
--------------------------------------------------------------------------------
/app/templates/_bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= _.slugify(appname) %>",
3 | "version": "0.0.0",
4 | "dependencies": {},
5 | "devDependencies": {}
6 | }
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
--------------------------------------------------------------------------------
/app/templates/coffees/background.coffee:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | chrome.runtime.onInstalled.addListener (details) ->
4 | console.log('previousVersion', details.previousVersion)
5 |
6 | console.log('\'Allo \'Allo! Event Page')
7 |
--------------------------------------------------------------------------------
/app/templates/scripts/background.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | chrome.runtime.onInstalled.addListener(function (details) {
4 | console.log('previousVersion', details.previousVersion);
5 | });
6 |
7 | console.log('\'Allo \'Allo! Event Page');
8 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": false,
5 | "curly": false,
6 | "eqeqeq": true,
7 | "eqnull": true,
8 | "immed": true,
9 | "latedef": true,
10 | "newcap": true,
11 | "noarg": true,
12 | "undef": true,
13 | "strict": false
14 | }
15 |
--------------------------------------------------------------------------------
/app/templates/coffees/background.browseraction.coffee:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | chrome.runtime.onInstalled.addListener (details) ->
4 | console.log('previousVersion', details.previousVersion)
5 |
6 | chrome.browserAction.setBadgeText({text: '\'Allo'})
7 |
8 | console.log('\'Allo \'Allo! Event Page for Browser Action')
9 |
--------------------------------------------------------------------------------
/app/templates/_locales/en/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": {
3 | "message": "<%= manifest.name %>",
4 | "description": "The name of the application"
5 | },
6 | "appDescription": {
7 | "message": "<%= manifest.description %>",
8 | "description": "The description of the application"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/templates/scripts/background.browseraction.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | chrome.runtime.onInstalled.addListener(function (details) {
4 | console.log('previousVersion', details.previousVersion);
5 | });
6 |
7 | chrome.browserAction.setBadgeText({text: '\'Allo'});
8 |
9 | console.log('\'Allo \'Allo! Event Page for Browser Action');
10 |
--------------------------------------------------------------------------------
/app/templates/coffees/background.pageaction.coffee:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | chrome.runtime.onInstalled.addListener (details) ->
4 | console.log('previousVersion', details.previousVersion)
5 |
6 | chrome.tabs.onUpdated.addListener (tabId) ->
7 | chrome.pageAction.show(tabId)
8 |
9 | console.log('\'Allo \'Allo! Event Page for Page Action')
10 |
--------------------------------------------------------------------------------
/app/templates/scripts/background.pageaction.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | chrome.runtime.onInstalled.addListener(function (details) {
4 | console.log('previousVersion', details.previousVersion);
5 | });
6 |
7 | chrome.tabs.onUpdated.addListener(function (tabId) {
8 | chrome.pageAction.show(tabId);
9 | });
10 |
11 | console.log('\'Allo \'Allo! Event Page for Page Action');
12 |
--------------------------------------------------------------------------------
/app/templates/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "__MSG_appName__",
3 | "version": "0.0.1",
4 | "manifest_version": 2,
5 | "description": "__MSG_appDescription__",
6 | "icons": {
7 | "16": "images/icon-16.png",
8 | "128": "images/icon-128.png"
9 | },
10 | "default_locale": "en",
11 | "background": {
12 | "scripts": [
13 | "scripts/chromereload.js",
14 | "scripts/background.js"
15 | ]
16 | }<%= manifest.items %>
17 | }
18 |
--------------------------------------------------------------------------------
/app/USAGE:
--------------------------------------------------------------------------------
1 | Description:
2 | Create a chrome extension boilerplate generator that creates everything for you. have fun.
3 |
4 | Example:
5 | yeoman init chrome-extension $NAME[OPTIONAL]
6 |
7 | This will create:
8 | Gruntfile.js: Configuration for the task runner.
9 | bower.json: Front-end packages installed by bower.
10 | package.json: Development packages installed by npm.
11 |
12 | app/: Your application files.
13 | test/: Unit tests for your application.
14 |
--------------------------------------------------------------------------------
/app/templates/editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | [*.json]
15 | indent_size = 2
16 |
17 | # We recommend you to keep these unchanged
18 | end_of_line = lf
19 | charset = utf-8
20 | trim_trailing_whitespace = true
21 | insert_final_newline = true
22 |
23 | [*.md]
24 | trim_trailing_whitespace = false
25 |
--------------------------------------------------------------------------------
/app/templates/jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,<%if (coffee) { %>
9 | "eqnull": true,<% } %>
10 | "immed": true,
11 | "indent": 2,
12 | "latedef": true,
13 | "newcap": true,
14 | "noarg": true,
15 | "quotmark": <%if (coffee) { %>false<% } else { %>"single"<% } %>,
16 | "regexp": true,
17 | "undef": true,
18 | "unused": true,
19 | "strict": true,
20 | "trailing": true,
21 | "smarttabs": true,
22 | "globals" : {
23 | "chrome": true,
24 | "crypto": true
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/templates/coffees/chromereload.coffee:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | ###
4 | Reload client for Chrome Apps & Extensions.
5 | The reload client has a compatibility with livereload.
6 | WARNING: only supports reload command.
7 | ###
8 |
9 | LIVERELOAD_HOST = 'localhost:'
10 | LIVERELOAD_PORT = 35729
11 | connection = new WebSocket('ws://' + LIVERELOAD_HOST + LIVERELOAD_PORT + '/livereload')
12 |
13 | connection.onerror = (e) -> console.log('reload connection got error' + JSON.stringify(e))
14 |
15 | connection.onmessage = (e) ->
16 | if e.data
17 | data = JSON.parse(e.data)
18 | if data and data.command == 'reload' then chrome.runtime.reload()
19 |
--------------------------------------------------------------------------------
/app/templates/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 'Allo, 'Allo!
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/templates/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | 'Allo, 'Allo!
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/templates/scripts/chromereload.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Reload client for Chrome Apps & Extensions.
4 | // The reload client has a compatibility with livereload.
5 | // WARNING: only supports reload command.
6 |
7 | var LIVERELOAD_HOST = 'localhost:';
8 | var LIVERELOAD_PORT = 35729;
9 | var connection = new WebSocket('ws://' + LIVERELOAD_HOST + LIVERELOAD_PORT + '/livereload');
10 |
11 | connection.onerror = function (error) {
12 | console.log('reload connection got error:', error);
13 | };
14 |
15 | connection.onmessage = function (e) {
16 | if (e.data) {
17 | var data = JSON.parse(e.data);
18 | if (data && data.command === 'reload') {
19 | chrome.runtime.reload();
20 | }
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generator-chrome-extension",
3 | "version": "0.3.0",
4 | "description": "Yeoman generator for Chrome Extensions",
5 | "keywords": [
6 | "yeoman-generator",
7 | "chrome",
8 | "extension",
9 | "addon"
10 | ],
11 | "author": "The Yeoman Team",
12 | "main": "app/index.js",
13 | "repository": {
14 | "type": "git",
15 | "url": "git://github.com/yeoman/generator-chrome-extension.git"
16 | },
17 | "scripts": {
18 | "test": "mocha --reporter spec"
19 | },
20 | "dependencies": {
21 | "yeoman-generator": "^0.17.0"
22 | },
23 | "peerDependencies": {
24 | "generator-mocha": ">=0.1.6",
25 | "yo": ">=1.0.0"
26 | },
27 | "devDependencies": {
28 | "mocha": "*",
29 | "underscore": "^1.6.0"
30 | },
31 | "engines": {
32 | "node": ">=0.10.0",
33 | "npm": ">=1.2.10"
34 | },
35 | "licenses": [
36 | {
37 | "type": "BSD"
38 | }
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/app/templates/_package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= _.slugify(appname) %>",
3 | "version": "0.0.0",
4 | "dependencies": {},
5 | "devDependencies": {
6 | "grunt": "~0.4.1",
7 | "grunt-contrib-copy": "~0.5.0",
8 | "grunt-contrib-concat": "~0.3.0",<% if (coffee) { %>
9 | "grunt-contrib-coffee": "~0.10.1",<% } %>
10 | "grunt-contrib-uglify": "~0.4.0",<% if (compass) { %>
11 | "grunt-contrib-compass": "~0.7.0",<% } %>
12 | "grunt-contrib-jshint": "~0.9.2",
13 | "grunt-contrib-cssmin": "~0.9.0",
14 | "grunt-contrib-connect": "~0.7.1",
15 | "grunt-contrib-clean": "~0.5.0",
16 | "grunt-contrib-htmlmin": "~0.2.0",
17 | "grunt-bower-install": "~1.0.0",
18 | "grunt-contrib-imagemin": "~0.7.1",
19 | "grunt-contrib-watch": "~0.6.1",<% if (testFramework === 'jasmine') { %>
20 | "grunt-contrib-jasmine": "~0.6.2",<% } %>
21 | "grunt-usemin": "~2.1.0",<% if (testFramework === 'mocha') { %>
22 | "grunt-mocha": "~0.4.10",<% } %>
23 | "grunt-svgmin": "~0.4.0",
24 | "grunt-concurrent": "~0.5.0",
25 | "load-grunt-tasks": "~0.4.0",
26 | "time-grunt": "~0.3.1",
27 | "jshint-stylish": "~0.1.5",
28 | "grunt-chrome-manifest": "~0.2.0",
29 | "grunt-contrib-compress": "~0.9.1"
30 | },
31 | "engines": {
32 | "node": ">=0.8.0"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Chrome Extension generator [](http://travis-ci.org/yeoman/generator-chrome-extension)
2 |
3 | Maintainer: [Jimmy Moon](https://github.com/ragingwind)
4 |
5 | > Chrome Extension generator that creates everything you need to get started with extension development. You can choose Browser UI(Browser,Page Action, Omnibox) type and select into permissions what you need.
6 |
7 | ## Getting Started
8 |
9 | - First make a new directory, and `cd` into it: mkdir my-new-chrome-extension && cd $_
10 | - Install the generator: `npm install -g generator-chrome-extension`
11 | - Run: `yo chrome-extension`, optionally passing an extension name: yo chrome-extension [extension-name]
12 |
13 | Need more information about Chrome Extension? Please visit [Google Chrome Extension Develpment](http://developer.chrome.com/extensions/devguide.html)
14 |
15 | 
16 |
17 | ## Generators
18 |
19 | Available generators:
20 |
21 | * [chrome-extension](#app) (aka [chrome-extension:app](#app))
22 |
23 | ### App
24 |
25 | Sets up a new Chrome Extension, generating all the boilerplate you need to get started.
26 |
27 | ```bash
28 | yo chrome-extension
29 | ```
30 |
31 | ## Test Chrome Extension
32 |
33 | To test, go to: chrome://extensions, enable Developer mode and load app as an unpacked extension.
34 |
35 | ## Grunt tasks
36 |
37 | ### Debug
38 |
39 | Debug task helps reduce your effors during development extensions. If the task detects your changes of source files, Livereload([chromereload.js](https://github.com/yeoman/generator-chrome-extension/blob/master/app/templates/scripts/chromereload.js)) reloads your extension. If you would like to know more about Livereload and preview of Yeoman? Please see [Getting started with Yeoman and generator-webapp](http://youtu.be/zBt2g9ekiug?t=3m51s) for your understanding.
40 |
41 | ```bash
42 | grunt debug
43 | ```
44 |
45 | ### Build
46 |
47 | By default, generators compress the file that was created by building a js/css/html/resource file. You can distribute the compressed file using the Chrome Developer Dashboard to publish to the Chrome Web Store.
48 |
49 | Run this command to build your Chrome Extension project.
50 |
51 | ```bash
52 | grunt build
53 | ```
54 |
55 | ## Options
56 |
57 | * `--skip-install`
58 |
59 | Skips the automatic execution of `bower` and `npm` after
60 | scaffolding has finished.
61 |
62 | * `--test-framework=[framework]`
63 |
64 | Defaults to `mocha`. Can be switched for
65 | another supported testing framework like `jasmine`.
66 |
67 | * `--coffee`
68 |
69 | Add support for [CoffeeScript](http://coffeescript.org/).
70 |
71 | * `--compass`
72 |
73 | Add support for [Compass](http://compass-style.org/).
74 |
75 | > WARN, Compiled files that generated by coffee or compass will be remained if your Chrome App is running on Chrome App container (with `grunt debug`). You should remove or ignore that files if you don't want to commit to repo.
76 |
77 | ## Contribute
78 |
79 | See the [contributing docs](https://github.com/yeoman/yeoman/blob/master/contributing.md)
80 |
81 | ## License
82 |
83 | [BSD license](http://opensource.org/licenses/bsd-license.php)
84 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | /*global describe, it */
2 | 'use strict';
3 |
4 | var path = require('path');
5 | var helpers = require('yeoman-generator').test;
6 | var assert = require('assert');
7 | var _ = require('underscore');
8 |
9 | describe('Chrome Extension generator', function () {
10 | if ('the generator can be required without throwing', function () {
11 | this.app = require('../app');
12 | });
13 |
14 | var options = {
15 | 'skip-install': true
16 | };
17 |
18 | var prompts = {
19 | uifeatures: [],
20 | permissions: []
21 | };
22 |
23 | var runGen;
24 |
25 | beforeEach(function (done) {
26 | helpers.testDirectory(path.join(__dirname, 'temp'), function (err) {
27 | if (err) {
28 | return done(err);
29 | }
30 |
31 | runGen = helpers
32 | .run(path.join(__dirname, '../app'))
33 | .withGenerators([
34 | [helpers.createDummyGenerator(), 'chrome-extension:app'],
35 | [helpers.createDummyGenerator(), 'mocha:app']
36 | ]);
37 | done();
38 | });
39 | });
40 |
41 | it('creates expected files in no UI Action', function (done) {
42 | var expected = [
43 | 'app/bower_components',
44 | 'Gruntfile.js',
45 | 'app/manifest.json',
46 | 'app/_locales/en/messages.json',
47 | 'app/images/icon-128.png',
48 | 'app/images/icon-16.png'
49 | ];
50 |
51 | runGen.withOptions(options).withPrompt(
52 | _.extend(prompts, {
53 | 'name': 'temp',
54 | 'description': 'description',
55 | 'action': 'No'
56 | })
57 | ).on('end', function () {
58 | assert.file(expected);
59 | assert.fileContent([
60 | ['bower.json', /"name": "temp"/],
61 | ['package.json', /"name": "temp"/],
62 | ['Gruntfile.js', /\/\/ No UI feature selected, cssmin task will be commented\n\s+\/\/ 'cssmin'/]
63 | ]);
64 | done();
65 | });
66 | });
67 |
68 | it('creates expected files in Browser Action', function (done) {
69 | var expected = [
70 | 'app/bower_components',
71 | 'Gruntfile.js',
72 | 'app/_locales/en/messages.json',
73 | 'app/images/icon-128.png',
74 | 'app/images/icon-16.png',
75 | 'app/images/icon-19.png',
76 | 'app/images/icon-38.png',
77 | 'app/scripts/background.js',
78 | 'app/scripts/popup.js',
79 | 'app/popup.html',
80 | 'app/styles/main.css'
81 | ];
82 |
83 | runGen.withOptions(options).withPrompt(
84 | _.extend(prompts, {
85 | 'name': 'temp',
86 | 'description': 'description',
87 | 'action': 'Browser',
88 | })
89 | ).on('end', function () {
90 | assert.file(expected);
91 | assert.fileContent([
92 | ['app/manifest.json', /"browser_action": {\s+"default_icon": {\s+"19": "images\/icon-19.png",\s+"38": "images\/icon-38.png"\s+},\s+"default_title": "temp",\s+"default_popup": "popup.html"\s+}/]
93 | ]);
94 | done();
95 | });
96 | });
97 |
98 | it('creates expected files in Page Action', function (done) {
99 | var expected = [
100 | 'app/bower_components',
101 | 'Gruntfile.js',
102 | 'app/_locales/en/messages.json',
103 | 'app/images/icon-128.png',
104 | 'app/images/icon-16.png',
105 | 'app/images/icon-19.png',
106 | 'app/images/icon-38.png',
107 | 'app/scripts/background.js',
108 | 'app/scripts/popup.js',
109 | 'app/popup.html',
110 | 'app/styles/main.css'
111 | ];
112 |
113 | runGen.withOptions(options).withPrompt(
114 | _.extend(prompts, {
115 | 'name': 'temp',
116 | 'description': 'description',
117 | 'action': 'Page',
118 | })
119 | ).on('end', function () {
120 | assert.file(expected);
121 | assert.fileContent([
122 | ['app/manifest.json', /"page_action": {\s+"default_icon": {\s+"19": "images\/icon-19.png",\s+"38": "images\/icon-38.png"\s+},\s+"default_title": "temp",\s+"default_popup": "popup.html"\s+}/]
123 | ]);
124 | done();
125 | });
126 | });
127 |
128 | it('creates expected files with Options Page', function (done) {
129 | var expected = [
130 | 'app/bower_components',
131 | 'Gruntfile.js',
132 | 'app/_locales/en/messages.json',
133 | 'app/images/icon-128.png',
134 | 'app/images/icon-16.png',
135 | 'app/scripts/background.js',
136 | 'app/scripts/options.js',
137 | 'app/options.html',
138 | 'app/styles/main.css'
139 | ];
140 |
141 | prompts['uifeatures'].push('options');
142 |
143 | runGen.withOptions(options).withPrompt(
144 | _.extend(prompts, {
145 | 'name': 'temp',
146 | 'description': 'description'
147 | })
148 | ).on('end', function () {
149 | assert.file(expected);
150 | assert.fileContent([
151 | ['bower.json', /"name": "temp"/],
152 | ['package.json', /"name": "temp"/],
153 | ['app/manifest.json', /"options_page": "options.html"/]
154 | ]);
155 | done();
156 | });
157 | });
158 |
159 | it('creates manifest.json with Omnibox option', function (done) {
160 | prompts['uifeatures'].push('omnibox');
161 |
162 | runGen.withOptions(options).withPrompt(
163 | _.extend(prompts, {
164 | 'name': 'temp',
165 | 'description': 'description'
166 | })
167 | ).on('end', function () {
168 | assert.fileContent([
169 | ['app/manifest.json', /"omnibox": {\s+"keyword": "temp"\s+}/]
170 | ]);
171 | done();
172 | });
173 | });
174 |
175 | it('creates expected files with Content-script option', function (done) {
176 | prompts['uifeatures'].push('contentscript');
177 |
178 | runGen.withOptions(options).withPrompt(
179 | _.extend(prompts, {
180 | 'name': 'temp',
181 | 'description': 'description'
182 | })
183 | ).on('end', function () {
184 | assert.fileContent([
185 | ['app/manifest.json', /"content_scripts": \[\s+{\s+"matches": \[\s+"http:\/\/\*\/\*",\s+"https:\/\/\*\/\*"\s+\],\s+"js": \[\s+"scripts\/contentscript.js"\s+\],\s+"run_at": "document_end",\s+"all_frames": false/],
186 | ]);
187 | done();
188 | });
189 | });
190 |
191 | it('creates expected manifest permission properties', function (done) {
192 | prompts['permissions'] = [
193 | 'permission',
194 | 'tabs',
195 | 'bookmarks',
196 | 'cookies',
197 | 'history',
198 | 'http://*/*',
199 | 'https://*/*'
200 | ];
201 |
202 | runGen.withOptions(options).withPrompt(
203 | _.extend(prompts, {
204 | 'name': 'temp',
205 | 'description': 'description'
206 | })
207 | ).on('end', function () {
208 | assert.fileContent([
209 | ['app/manifest.json', /"permissions"/],
210 | ['app/manifest.json', /"tabs"/],
211 | ['app/manifest.json', /"bookmarks"/],
212 | ['app/manifest.json', /"cookies"/],
213 | ['app/manifest.json', /"history"/],
214 | ['app/manifest.json', /\s+"http:\/\/\*\/\*",\s+"https:\/\/\*\/\*"/],
215 | ]);
216 | done();
217 | });
218 | });
219 | });
220 |
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var path = require('path');
3 | var util = require('util');
4 | var spawn = require('child_process').spawn;
5 | var yeoman = require('yeoman-generator');
6 |
7 | module.exports = yeoman.generators.Base.extend({
8 | constructor: function (args, options, config) {
9 | yeoman.generators.Base.apply(this, arguments);
10 |
11 | // setup the test-framework property, Gruntfile template will need this
12 | this.option('test-framework', {
13 | desc: 'Test framework to be invoked',
14 | type: String,
15 | defaults: 'mocha'
16 | });
17 | this.testFramework = this.options['test-framework'];
18 |
19 | // setup the coffee property
20 | this.option('coffee', {
21 | desc: 'Use CoffeeScript',
22 | type: Boolean,
23 | defaults: false
24 | });
25 | this.coffee = this.options.coffee;
26 |
27 | // setup the coffee property
28 | this.option('compass', {
29 | desc: 'Use Compass',
30 | type: Boolean,
31 | defaults: false
32 | });
33 | this.compass = this.options.compass;
34 |
35 | // load package
36 | this.pkg = require('../package.json');
37 |
38 | // set source root path to templates
39 | this.sourceRoot(path.join(__dirname, 'templates'));
40 |
41 | // init extension manifest data
42 | this.manifest = {
43 | permissions:{}
44 | };
45 |
46 | // copy script with js or coffee extension
47 | this.copyjs = function copyjs(src, dest) {
48 | var ext = this.coffee ? '.coffee' : '.js';
49 |
50 | src = src + ext;
51 | dest = dest ? dest + ext : src;
52 | this.copy((this.coffee ? 'coffees/' : 'scripts/') + src, 'app/scripts/' + dest);
53 | };
54 | },
55 |
56 | askFor: function (argument) {
57 | var cb = this.async();
58 |
59 | var prompts = [
60 | {
61 | name: 'name',
62 | message: 'What would you like to call this extension?',
63 | default: (this.appname) ? this.appname : 'myChromeApp'
64 | },
65 | {
66 | name: 'description',
67 | message: 'How would you like to describe this extension?',
68 | default: 'My Chrome Extension'
69 | },
70 | {
71 | type: 'list',
72 | name: 'action',
73 | message: 'Would you like to use UI Action?',
74 | choices:[
75 | 'No',
76 | 'Browser',
77 | 'Page'
78 | ]
79 | },
80 | {
81 | type: 'checkbox',
82 | name: 'uifeatures',
83 | message: 'Would you like more UI Features?',
84 | choices: [{
85 | value: 'options',
86 | name: 'Options Page',
87 | checked: false
88 | }, {
89 | value: 'contentscript',
90 | name: 'Content Scripts',
91 | checked: false
92 | }, {
93 | value: 'omnibox',
94 | name: 'Omnibox',
95 | checked: false
96 | }]
97 | },
98 | {
99 | type: 'checkbox',
100 | name: 'permissions',
101 | message: 'Would you like to use permissions?',
102 | choices: [{
103 | value: 'tabs',
104 | name: 'Tabs',
105 | checked: false
106 | }, {
107 | value: 'bookmark',
108 | name: 'Bookmarks',
109 | checked: false
110 | }, {
111 | value: 'cookie',
112 | name: 'Cookies',
113 | checked: false
114 | }, {
115 | value: 'history',
116 | name: 'History',
117 | checked: false
118 | }, {
119 | value: 'management',
120 | name: 'Management',
121 | checked: false
122 | }]
123 | }
124 | ];
125 |
126 | this.prompt( prompts , function(answers) {
127 | var isChecked = function (choices, value) { return choices.indexOf(value) > -1; };
128 |
129 | this.appname = this.manifest.name = answers.name.replace(/\"/g, '\\"');
130 | this.manifest.description = answers.description.replace(/\"/g, '\\"');
131 | this.manifest.action = (answers.action === 'No') ? 0 : (answers.action === 'Browser') ? 1 : 2;
132 | this.manifest.options = isChecked(answers.uifeatures, 'options');
133 | this.manifest.omnibox = isChecked(answers.uifeatures, 'omnibox');
134 | this.manifest.contentscript = isChecked(answers.uifeatures, 'contentscript');
135 | this.manifest.permissions.tabs = isChecked(answers.permissions, 'tabs');
136 | this.manifest.permissions.bookmarks = isChecked(answers.permissions, 'bookmarks');
137 | this.manifest.permissions.cookies = isChecked(answers.permissions, 'cookies');
138 | this.manifest.permissions.history = isChecked(answers.permissions, 'history');
139 | this.manifest.permissions.management = isChecked(answers.permissions, 'management');
140 |
141 | cb();
142 | }.bind(this));
143 | },
144 |
145 | app: function () {
146 | this.mkdir('app');
147 | this.mkdir('app/bower_components');
148 | },
149 |
150 | gruntfile: function () {
151 | this.template('Gruntfile.js');
152 | },
153 |
154 | packageJSON: function () {
155 | this.template('_package.json', 'package.json');
156 | },
157 |
158 | git: function () {
159 | this.copy('gitignore', '.gitignore');
160 | this.copy('gitattributes', '.gitattributes');
161 | },
162 |
163 | bower: function () {
164 | this.copy('bowerrc', '.bowerrc');
165 | this.copy('_bower.json', 'bower.json');
166 | },
167 |
168 | jshint: function () {
169 | this.copy('jshintrc', '.jshintrc');
170 | },
171 |
172 | editorConfig: function () {
173 | this.copy('editorconfig', '.editorconfig');
174 | },
175 |
176 | manifest: function () {
177 | var manifest = {};
178 | var permissions = [];
179 | var items = [];
180 |
181 | // add browser / page action field
182 | if (this.manifest.action > 0) {
183 | var action = {
184 | default_icon: { 19: 'images/icon-19.png', 38: 'images/icon-38.png' },
185 | default_title: this.manifest.name,
186 | default_popup: 'popup.html'
187 | };
188 | var title = (this.manifest.action === 1) ? 'browser_action' : 'page_action';
189 | manifest[title] = JSON.stringify(action, null, 2).replace(/\n/g, '\n ');
190 | }
191 |
192 | // add options page field.
193 | if (this.manifest.options) {
194 | manifest.options_page = '"options.html"';
195 | }
196 |
197 | // add omnibox keyword field.
198 | if (this.manifest.omnibox) {
199 | manifest.omnibox = JSON.stringify({ keyword: this.manifest.name }, null, 2).replace(/\n/g, '\n ');
200 | }
201 |
202 | // add contentscript field.
203 | if (this.manifest.contentscript) {
204 | var contentscript = [{
205 | matches: ['http://*/*', 'https://*/*'],
206 | js: ['scripts/contentscript.js'],
207 | run_at: 'document_end',
208 | all_frames: false
209 | }];
210 |
211 | manifest.content_scripts = JSON.stringify(contentscript, null, 2).replace(/\n/g, '\n ');
212 | }
213 |
214 | // add generate permission field.
215 | for (var p in this.manifest.permissions) {
216 | if (this.manifest.permissions[p]) {
217 | permissions.push(p);
218 | }
219 | }
220 |
221 | // add generic match pattern field.
222 | if (this.manifest.permissions.tabs) {
223 | permissions.push('http://*/*');
224 | permissions.push('https://*/*');
225 | }
226 |
227 | if (permissions.length > 0) {
228 | manifest.permissions = JSON.stringify(permissions, null, 2).replace(/\n/g, '\n ');
229 | }
230 |
231 | for (var i in manifest) {
232 | items.push([' "', i, '": ', manifest[i]].join(''));
233 | }
234 |
235 | this.manifest.items = (items.length > 0) ? ',\n' + items.join(',\n') : '';
236 |
237 | this.template('manifest.json', 'app/manifest.json');
238 | },
239 |
240 | actions: function () {
241 | if (this.manifest.action === 0) {
242 | return;
243 | }
244 |
245 | this.copy('popup.html', 'app/popup.html');
246 | this.copyjs('popup');
247 | this.copy('images/icon-19.png', 'app/images/icon-19.png');
248 | this.copy('images/icon-38.png', 'app/images/icon-38.png');
249 | },
250 |
251 | eventpage: function () {
252 | var backgroundjs = 'background';
253 |
254 | if (this.manifest.action === 2) {
255 | backgroundjs = 'background.pageaction';
256 | } else if (this.manifest.action === 1) {
257 | backgroundjs = 'background.browseraction';
258 | }
259 |
260 | this.copyjs(backgroundjs, 'background');
261 | this.copyjs('chromereload');
262 | },
263 |
264 | options: function () {
265 | if (!this.manifest.options) {
266 | return;
267 | }
268 |
269 | this.copy('options.html', 'app/options.html');
270 | this.copyjs('options');
271 | },
272 |
273 | contentscript: function () {
274 | if (!this.manifest.contentscript) {
275 | return;
276 | }
277 |
278 | this.copyjs('contentscript');
279 | },
280 |
281 | mainStylesheet: function () {
282 | if (this.manifest.action === 0 && !this.manifest.options) {
283 | return;
284 | }
285 |
286 | var css = 'styles/main.' + (this.compass ? 's' : '') + 'css';
287 | this.copy(css, 'app/' + css);
288 | },
289 |
290 | assets: function () {
291 | this.template('_locales/en/messages.json', 'app/_locales/en/messages.json');
292 | this.copy('images/icon-16.png', 'app/images/icon-16.png');
293 | this.copy('images/icon-128.png', 'app/images/icon-128.png');
294 | },
295 |
296 | install: function () {
297 | this.on('end', function () {
298 | this.invoke(this.options['test-framework'], {
299 | options: {
300 | 'skip-message': this.options['skip-install-message'],
301 | 'skip-install': this.options['skip-install'],
302 | 'coffee': this.options.coffee
303 | }
304 | });
305 |
306 | if (!this.options['skip-install']) {
307 | this.installDependencies({
308 | skipMessage: this.options['skip-install-message'],
309 | skipInstall: this.options['skip-install']
310 | });
311 | }
312 | });
313 | }
314 | });
315 |
--------------------------------------------------------------------------------
/app/templates/Gruntfile.js:
--------------------------------------------------------------------------------
1 | // Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %>
2 | 'use strict';
3 |
4 | // # Globbing
5 | // for performance reasons we're only matching one level down:
6 | // 'test/spec/{,*/}*.js'
7 | // use this if you want to recursively match all subfolders:
8 | // 'test/spec/**/*.js'
9 |
10 | module.exports = function (grunt) {
11 |
12 | // Load grunt tasks automatically
13 | require('load-grunt-tasks')(grunt);
14 |
15 | // Time how long tasks take. Can help when optimizing build times
16 | require('time-grunt')(grunt);
17 |
18 | // Configurable paths
19 | var config = {
20 | app: 'app',
21 | dist: 'dist'
22 | };
23 |
24 | grunt.initConfig({
25 |
26 | // Project settings
27 | config: config,
28 |
29 | // Watches files for changes and runs tasks based on the changed files
30 | watch: {
31 | bower: {
32 | files: ['bower.json'],
33 | tasks: ['bowerInstall']
34 | },<%if (coffee) { %>
35 | coffee: {
36 | files: ['<%%= config.app %>/scripts/{,*/}*.{coffee,litcoffee,coffee.md}'],
37 | tasks: ['coffee:chrome'],
38 | options: {
39 | livereload: '<%%= connect.options.livereload %>'
40 | }
41 | },<%} else { %>
42 | js: {
43 | files: ['<%%= config.app %>/scripts/{,*/}*.js'],
44 | tasks: ['jshint'],
45 | options: {
46 | livereload: '<%%= connect.options.livereload %>'
47 | }
48 | },<% } %><% if (compass) { %>
49 | compass: {
50 | files: ['<%%= config.app %>/styles/{,*/}*.{scss,sass}'],
51 | tasks: ['compass:chrome']
52 | },<% } %>
53 | gruntfile: {
54 | files: ['Gruntfile.js']
55 | },
56 | styles: {
57 | files: ['<%%= config.app %>/styles/{,*/}*.css'],
58 | tasks: [],
59 | options: {
60 | livereload: '<%%= connect.options.livereload %>'
61 | }
62 | },
63 | livereload: {
64 | options: {
65 | livereload: '<%%= connect.options.livereload %>'
66 | },
67 | files: [
68 | '<%%= config.app %>/*.html',
69 | '<%%= config.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
70 | '<%%= config.app %>/manifest.json',
71 | '<%%= config.app %>/_locales/{,*/}*.json'
72 | ]
73 | }
74 | },
75 |
76 | // Grunt server and debug server setting
77 | connect: {
78 | options: {
79 | port: 9000,
80 | livereload: 35729,
81 | // change this to '0.0.0.0' to access the server from outside
82 | hostname: 'localhost'
83 | },
84 | chrome: {
85 | options: {
86 | open: false,
87 | base: [
88 | '<%%= config.app %>'
89 | ]
90 | }
91 | },
92 | test: {
93 | options: {
94 | open: false,
95 | base: [
96 | 'test',
97 | '<%%= config.app %>'
98 | ]
99 | }
100 | }
101 | },
102 |
103 | // Empties folders to start fresh
104 | clean: {
105 | chrome: {
106 | },
107 | dist: {
108 | files: [{
109 | dot: true,
110 | src: [
111 | '<%%= config.dist %>/*',
112 | '!<%%= config.dist %>/.git*'
113 | ]
114 | }]
115 | }
116 | },
117 |
118 | // Make sure code styles are up to par and there are no obvious mistakes
119 | jshint: {
120 | options: {
121 | jshintrc: '.jshintrc',
122 | reporter: require('jshint-stylish')
123 | },
124 | all: [
125 | 'Gruntfile.js',
126 | '<%%= config.app %>/scripts/{,*/}*.js',
127 | '!<%%= config.app %>/scripts/vendor/*',
128 | 'test/spec/{,*/}*.js'
129 | ]
130 | },<% if (testFramework === 'mocha') { %>
131 | mocha: {
132 | all: {
133 | options: {
134 | run: true,
135 | urls: ['http://localhost:<%%= connect.options.port %>/index.html']
136 | }
137 | }
138 | },<% } else if (testFramework === 'jasmine') { %>
139 | jasmine: {
140 | all: {
141 | options: {
142 | specs: 'test/spec/{,*/}*.js'
143 | }
144 | }
145 | },<% } %><% if (coffee) { %>
146 |
147 | // Compiles CoffeeScript to JavaScript
148 | coffee: {
149 | chrome: {
150 | files: [{
151 | expand: true,
152 | cwd: '<%%= config.app %>/scripts',
153 | src: '{,*/}*.{coffee,litcoffee,coffee.md}',
154 | dest: '<%%= config.app %>/scripts',
155 | ext: '.js'
156 | }]
157 | },
158 | dist: {
159 | files: [{
160 | expand: true,
161 | cwd: '<%%= config.app %>/scripts',
162 | src: '{,*/}*.{coffee,litcoffee,coffee.md}',
163 | dest: '<%%= config.app %>/scripts',
164 | ext: '.js'
165 | }]
166 | },
167 | test: {
168 | files: [{
169 | expand: true,
170 | cwd: 'test/spec',
171 | src: '{,*/}*.coffee',
172 | dest: './spec',
173 | ext: '.js'
174 | }]
175 | }
176 | },<% } %><% if (compass) { %>
177 |
178 | // Compiles Sass to CSS and generates necessary files if requested
179 | compass: {
180 | options: {
181 | sassDir: '<%%= config.app %>/styles',
182 | cssDir: '<%%= config.dist %>/styles',
183 | generatedImagesDir: '<%%= config.dist %>/images/generated',
184 | imagesDir: '<%%= config.app %>/images',
185 | javascriptsDir: '<%%= config.app %>/scripts',
186 | fontsDir: '<%%= config.app %>/styles/fonts',
187 | importPath: '<%%= config.app %>/bower_components',
188 | httpImagesPath: '/images',
189 | httpGeneratedImagesPath: '/images/generated',
190 | httpFontsPath: '/styles/fonts',
191 | relativeAssets: false,
192 | assetCacheBuster: false
193 | },
194 | chrome: {
195 | options: {
196 | cssDir: '<%%= config.app %>/styles',
197 | generatedImagesDir: '<%%= config.app %>/images/generated',
198 | debugInfo: true
199 | }
200 | },
201 | dist: {
202 | },
203 | test: {
204 | }
205 | },<% } %>
206 |
207 | // Automatically inject Bower components into the HTML file
208 | bowerInstall: {
209 | app: {
210 | src: [
211 | '<%%= config.app %>/*.html'
212 | ]
213 | }<% if (compass) { %>,
214 | sass: {
215 | src: ['<%%= config.app %>/styles/{,*/}*.{scss,sass}'],
216 | ignorePath: '<%%= config.app %>/bower_components/'
217 | }<% } %>
218 | },
219 |
220 | // Reads HTML for usemin blocks to enable smart builds that automatically
221 | // concat, minify and revision files. Creates configurations in memory so
222 | // additional tasks can operate on them
223 | useminPrepare: {
224 | options: {
225 | dest: '<%%= config.dist %>'
226 | },
227 | html: [
228 | '<%%= config.app %>/popup.html',
229 | '<%%= config.app %>/options.html'
230 | ]
231 | },
232 |
233 | // Performs rewrites based on rev and the useminPrepare configuration
234 | usemin: {
235 | options: {
236 | assetsDirs: ['<%%= config.dist %>', '<%%= config.dist %>/images']
237 | },
238 | html: ['<%%= config.dist %>/{,*/}*.html'],
239 | css: ['<%%= config.dist %>/styles/{,*/}*.css']
240 | },
241 |
242 | // The following *-min tasks produce minifies files in the dist folder
243 | imagemin: {
244 | dist: {
245 | files: [{
246 | expand: true,
247 | cwd: '<%%= config.app %>/images',
248 | src: '{,*/}*.{gif,jpeg,jpg,png}',
249 | dest: '<%%= config.dist %>/images'
250 | }]
251 | }
252 | },
253 |
254 | svgmin: {
255 | dist: {
256 | files: [{
257 | expand: true,
258 | cwd: '<%%= config.app %>/images',
259 | src: '{,*/}*.svg',
260 | dest: '<%%= config.dist %>/images'
261 | }]
262 | }
263 | },
264 |
265 | htmlmin: {
266 | dist: {
267 | options: {
268 | // removeCommentsFromCDATA: true,
269 | // collapseWhitespace: true,
270 | // collapseBooleanAttributes: true,
271 | // removeAttributeQuotes: true,
272 | // removeRedundantAttributes: true,
273 | // useShortDoctype: true,
274 | // removeEmptyAttributes: true,
275 | // removeOptionalTags: true
276 | },
277 | files: [{
278 | expand: true,
279 | cwd: '<%%= config.app %>',
280 | src: '*.html',
281 | dest: '<%%= config.dist %>'
282 | }]
283 | }
284 | },
285 |
286 | // By default, your `index.html`'s will take care of
287 | // minification. These next options are pre-configured if you do not wish
288 | // to use the Usemin blocks.
289 | // cssmin: {
290 | // dist: {
291 | // files: {
292 | // '<%%= config.dist %>/styles/main.css': [
293 | // '<%%= config.app %>/styles/{,*/}*.css'
294 | // ]
295 | // }
296 | // }
297 | // },
298 | // uglify: {
299 | // dist: {
300 | // files: {
301 | // '<%%= config.dist %>/scripts/scripts.js': [
302 | // '<%%= config.dist %>/scripts/scripts.js'
303 | // ]
304 | // }
305 | // }
306 | // },
307 | // concat: {
308 | // dist: {}
309 | // },
310 |
311 | // Copies remaining files to places other tasks can use
312 | copy: {
313 | dist: {
314 | files: [{
315 | expand: true,
316 | dot: true,
317 | cwd: '<%%= config.app %>',
318 | dest: '<%%= config.dist %>',
319 | src: [
320 | '*.{ico,png,txt}',
321 | 'images/{,*/}*.{webp,gif}',
322 | '{,*/}*.html',
323 | 'styles/{,*/}*.css',
324 | 'styles/fonts/{,*/}*.*',
325 | '_locales/{,*/}*.json',
326 | ]
327 | }]
328 | }
329 | },
330 |
331 | // Run some tasks in parallel to speed up build process
332 | concurrent: {
333 | chrome: [<%if (coffee) { %>
334 | 'coffee:chrome',<% } if (compass) { %>
335 | 'compass:chrome',<% } %>
336 | ],
337 | dist: [<% if (coffee) { %>
338 | 'coffee:dist',<% } if (compass) { %>
339 | 'compass:dist',<% } %>
340 | 'imagemin',
341 | 'svgmin'
342 | ],
343 | test: [<%if (coffee) { %>
344 | 'coffee:test',<% } if (compass) { %>
345 | 'compass:test',<% } %>
346 | ]
347 | },
348 |
349 | // Auto buildnumber, exclude debug files. smart builds that event pages
350 | chromeManifest: {
351 | dist: {
352 | options: {
353 | buildnumber: true,
354 | indentSize: 2,
355 | background: {
356 | target: 'scripts/background.js',
357 | exclude: [
358 | 'scripts/chromereload.js'
359 | ]
360 | }
361 | },
362 | src: '<%%= config.app %>',
363 | dest: '<%%= config.dist %>'
364 | }
365 | },
366 |
367 | // Compres dist files to package
368 | compress: {
369 | dist: {
370 | options: {
371 | archive: function() {
372 | var manifest = grunt.file.readJSON('app/manifest.json');
373 | return 'package/<%= appname %>-' + manifest.version + '.zip';
374 | }
375 | },
376 | files: [{
377 | expand: true,
378 | cwd: 'dist/',
379 | src: ['**'],
380 | dest: ''
381 | }]
382 | }
383 | }
384 | });
385 |
386 | grunt.registerTask('debug', function () {
387 | grunt.task.run([
388 | 'jshint',
389 | 'concurrent:chrome',
390 | 'connect:chrome',
391 | 'watch'
392 | ]);
393 | });
394 |
395 | grunt.registerTask('test', [
396 | 'connect:test',<% if (testFramework === 'mocha') { %>
397 | 'mocha'<% } else if (testFramework === 'jasmine') { %>
398 | 'jasmine'<% } %>
399 | ]);
400 |
401 | grunt.registerTask('build', [
402 | 'clean:dist',
403 | 'chromeManifest:dist',
404 | 'useminPrepare',
405 | 'concurrent:dist',
406 | <% if (manifest.action === 0) { %>// No UI feature selected, cssmin task will be commented
407 | // <% } %>'cssmin',
408 | 'concat',
409 | 'uglify',
410 | 'copy',
411 | 'usemin',
412 | 'compress'
413 | ]);
414 |
415 | grunt.registerTask('default', [
416 | 'jshint',
417 | 'test',
418 | 'build'
419 | ]);
420 | };
421 |
--------------------------------------------------------------------------------